Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
c6ee7ef0f5
commit
e1b9b92a49
42 changed files with 317 additions and 139 deletions
|
@ -32,7 +32,7 @@ export default {
|
|||
import('ee_component/members/components/ldap/ldap_override_confirmation_modal.vue'),
|
||||
},
|
||||
computed: {
|
||||
...mapState(['members', 'tableFields', 'tableAttrs', 'currentUserId', 'sourceId']),
|
||||
...mapState(['members', 'tableFields', 'tableAttrs', 'currentUserId']),
|
||||
filteredFields() {
|
||||
return FIELDS.filter(
|
||||
(field) => this.tableFields.includes(field.key) && this.showField(field),
|
||||
|
@ -55,9 +55,9 @@ export default {
|
|||
methods: {
|
||||
hasActionButtons(member) {
|
||||
return (
|
||||
canRemove(member, this.sourceId) ||
|
||||
canRemove(member) ||
|
||||
canResend(member) ||
|
||||
canUpdate(member, this.currentUserId, this.sourceId) ||
|
||||
canUpdate(member, this.currentUserId) ||
|
||||
canOverride(member)
|
||||
);
|
||||
},
|
||||
|
|
|
@ -19,7 +19,7 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['sourceId', 'currentUserId']),
|
||||
...mapState(['currentUserId']),
|
||||
isGroup() {
|
||||
return isGroup(this.member);
|
||||
},
|
||||
|
@ -41,19 +41,19 @@ export default {
|
|||
return MEMBER_TYPES.user;
|
||||
},
|
||||
isDirectMember() {
|
||||
return isDirectMember(this.member, this.sourceId);
|
||||
return isDirectMember(this.member);
|
||||
},
|
||||
isCurrentUser() {
|
||||
return isCurrentUser(this.member, this.currentUserId);
|
||||
},
|
||||
canRemove() {
|
||||
return canRemove(this.member, this.sourceId);
|
||||
return canRemove(this.member);
|
||||
},
|
||||
canResend() {
|
||||
return canResend(this.member);
|
||||
},
|
||||
canUpdate() {
|
||||
return canUpdate(this.member, this.currentUserId, this.sourceId);
|
||||
return canUpdate(this.member, this.currentUserId);
|
||||
},
|
||||
},
|
||||
render() {
|
||||
|
|
|
@ -35,26 +35,24 @@ export const isGroup = (member) => {
|
|||
return Boolean(member.sharedWithGroup);
|
||||
};
|
||||
|
||||
export const isDirectMember = (member, sourceId) => {
|
||||
return isGroup(member) || member.source?.id === sourceId;
|
||||
export const isDirectMember = (member) => {
|
||||
return isGroup(member) || member.isDirectMember;
|
||||
};
|
||||
|
||||
export const isCurrentUser = (member, currentUserId) => {
|
||||
return member.user?.id === currentUserId;
|
||||
};
|
||||
|
||||
export const canRemove = (member, sourceId) => {
|
||||
return isDirectMember(member, sourceId) && member.canRemove;
|
||||
export const canRemove = (member) => {
|
||||
return isDirectMember(member) && member.canRemove;
|
||||
};
|
||||
|
||||
export const canResend = (member) => {
|
||||
return Boolean(member.invite?.canResend);
|
||||
};
|
||||
|
||||
export const canUpdate = (member, currentUserId, sourceId) => {
|
||||
return (
|
||||
!isCurrentUser(member, currentUserId) && isDirectMember(member, sourceId) && member.canUpdate
|
||||
);
|
||||
export const canUpdate = (member, currentUserId) => {
|
||||
return !isCurrentUser(member, currentUserId) && isDirectMember(member) && member.canUpdate;
|
||||
};
|
||||
|
||||
export const parseSortParam = (sortableFields) => {
|
||||
|
|
|
@ -20,7 +20,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
|
|||
end
|
||||
|
||||
def preloadable_mr_relations
|
||||
[:metrics, :assignees, { author: :status }]
|
||||
[:metrics, { assignees: :status }, { author: :status }]
|
||||
end
|
||||
|
||||
def merge_request_params
|
||||
|
|
|
@ -38,7 +38,9 @@ module Autocomplete
|
|||
end
|
||||
end
|
||||
|
||||
items.uniq
|
||||
items.uniq.tap do |unique_items|
|
||||
preload_associations(unique_items)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -91,6 +93,12 @@ module Autocomplete
|
|||
User.none
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def preload_associations(items)
|
||||
ActiveRecord::Associations::Preloader.new.preload(items, :status)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ module Groups::GroupMembersHelper
|
|||
end
|
||||
|
||||
def members_data_json(group, members)
|
||||
MemberSerializer.new.represent(members, { current_user: current_user, group: group }).to_json
|
||||
MemberSerializer.new.represent(members, { current_user: current_user, group: group, source: group }).to_json
|
||||
end
|
||||
|
||||
# Overridden in `ee/app/helpers/ee/groups/group_members_helper.rb`
|
||||
|
|
|
@ -346,6 +346,7 @@ module IssuablesHelper
|
|||
def assignee_sidebar_data(assignee, merge_request: nil)
|
||||
{ avatar_url: assignee.avatar_url, name: assignee.name, username: assignee.username }.tap do |data|
|
||||
data[:can_merge] = merge_request.can_be_merged_by?(assignee) if merge_request
|
||||
data[:availability] = assignee.status.availability if assignee.association(:status).loaded? && assignee.status&.availability
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ module Projects::ProjectMembersHelper
|
|||
end
|
||||
|
||||
def project_members_data_json(project, members)
|
||||
MemberSerializer.new.represent(members, { current_user: current_user, group: project.group }).to_json
|
||||
MemberSerializer.new.represent(members, { current_user: current_user, group: project.group, source: project }).to_json
|
||||
end
|
||||
|
||||
def project_members_list_data_attributes(project, members)
|
||||
|
|
|
@ -132,7 +132,7 @@ class Issue < ApplicationRecord
|
|||
scope :counts_by_state, -> { reorder(nil).group(:state_id).count }
|
||||
|
||||
scope :service_desk, -> { where(author: ::User.support_bot) }
|
||||
scope :inc_relations_for_view, -> { includes(author: :status) }
|
||||
scope :inc_relations_for_view, -> { includes(author: :status, assignees: :status) }
|
||||
|
||||
# An issue can be uniquely identified by project_id and iid
|
||||
# Takes one or more sets of composite IDs, expressed as hash-like records of
|
||||
|
|
|
@ -24,6 +24,7 @@ module Terraform
|
|||
scope :ordered_by_name, -> { order(:name) }
|
||||
scope :with_name, -> (name) { where(name: name) }
|
||||
|
||||
validates :name, presence: true, uniqueness: { scope: :project_id }
|
||||
validates :project_id, presence: true
|
||||
validates :uuid, presence: true, uniqueness: true, length: { is: UUID_LENGTH },
|
||||
format: { with: HEX_REGEXP, message: 'only allows hex characters' }
|
||||
|
|
|
@ -16,6 +16,10 @@ module UserStatusTooltip
|
|||
status_loaded? && show_status_emoji?(user.status)
|
||||
end
|
||||
|
||||
expose :availability, if: -> (*) { status_loaded? } do |user|
|
||||
user.status&.availability
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def status_loaded?
|
||||
|
|
|
@ -23,6 +23,10 @@ class MemberEntity < Grape::Entity
|
|||
member.can_remove?
|
||||
end
|
||||
|
||||
expose :is_direct_member do |member, options|
|
||||
member.source == options[:source]
|
||||
end
|
||||
|
||||
expose :access_level do
|
||||
expose :human_access, as: :string_value
|
||||
expose :access_level, as: :integer_value
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class MergeRequestUserEntity < ::API::Entities::UserBasic
|
||||
include UserStatusTooltip
|
||||
|
||||
expose :can_merge do |reviewer, options|
|
||||
options[:merge_request]&.can_be_merged_by?(reviewer)
|
||||
end
|
||||
|
|
|
@ -68,12 +68,14 @@ module Terraform
|
|||
|
||||
find_params = { project: project, name: params[:name] }
|
||||
|
||||
if find_only
|
||||
Terraform::State.find_by(find_params) || # rubocop: disable CodeReuse/ActiveRecord
|
||||
raise(ActiveRecord::RecordNotFound.new("Couldn't find state"))
|
||||
else
|
||||
Terraform::State.create_or_find_by(find_params)
|
||||
end
|
||||
return find_state!(find_params) if find_only
|
||||
|
||||
state = Terraform::State.create_or_find_by(find_params)
|
||||
|
||||
# https://github.com/rails/rails/issues/36027
|
||||
return state unless state.errors.of_kind? :name, :taken
|
||||
|
||||
find_state(find_params)
|
||||
end
|
||||
|
||||
def lock_matches?(state)
|
||||
|
@ -86,5 +88,13 @@ module Terraform
|
|||
def can_modify_state?
|
||||
current_user.can?(:admin_terraform_state, project)
|
||||
end
|
||||
|
||||
def find_state(find_params)
|
||||
Terraform::State.find_by(find_params) # rubocop: disable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
||||
def find_state!(find_params)
|
||||
find_state(find_params) || raise(ActiveRecord::RecordNotFound.new("Couldn't find state"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
.filtered-search-box
|
||||
= dropdown_tag(_('Recent searches'),
|
||||
options: { wrapper_class: 'filtered-search-history-dropdown-wrapper',
|
||||
toggle_class: 'gl-button btn filtered-search-history-dropdown-toggle-button',
|
||||
toggle_class: 'btn filtered-search-history-dropdown-toggle-button',
|
||||
dropdown_class: 'filtered-search-history-dropdown',
|
||||
content_class: 'filtered-search-history-dropdown-content' }) do
|
||||
.js-filtered-search-history-dropdown{ data: { full_path: admin_runners_path } }
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
%script#js-authenticate-token-2fa-error{ type: "text/template" }
|
||||
%div
|
||||
%p <%= error_message %> (<%= error_name %>)
|
||||
%a.btn.btn-block.btn-warning#js-token-2fa-try-again= _("Try again?")
|
||||
%a.btn.gl-button.btn-block.btn-warning#js-token-2fa-try-again= _("Try again?")
|
||||
|
||||
%script#js-authenticate-token-2fa-authenticated{ type: "text/template" }
|
||||
%div
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
%div
|
||||
%p
|
||||
%span <%= error_message %> (<%= error_name %>)
|
||||
%a.btn.btn-warning#js-token-2fa-try-again= _("Try again?")
|
||||
%a.btn.gl-button.btn-warning#js-token-2fa-try-again= _("Try again?")
|
||||
|
||||
%script#js-register-token-2fa-registered{ type: "text/template" }
|
||||
.row.gl-mb-3
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
= form_for 'email', url: profile_emails_path do |f|
|
||||
.form-group
|
||||
= f.label :email, _('Email'), class: 'label-bold'
|
||||
= f.text_field :email, class: 'form-control', data: { qa_selector: 'email_address_field' }
|
||||
= f.text_field :email, class: 'form-control gl-form-input', data: { qa_selector: 'email_address_field' }
|
||||
.gl-mt-3
|
||||
= f.submit _('Add email address'), class: 'gl-button btn btn-success', data: { qa_selector: 'add_email_address_button' }
|
||||
%hr
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Expose user availablility data on issuable pages
|
||||
merge_request: 52333
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add name validation to Terraform state
|
||||
merge_request: 52102
|
||||
author:
|
||||
type: changed
|
5
changelogs/unreleased/gl-button-utf-try-again.yml
Normal file
5
changelogs/unreleased/gl-button-utf-try-again.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Apply new GitLab UI class for U2F try again button
|
||||
merge_request: 52759
|
||||
author: Yogi (@yo)
|
||||
type: other
|
5
changelogs/unreleased/gl-form-input-user-email.yml
Normal file
5
changelogs/unreleased/gl-form-input-user-email.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Apply new GitLab UI for input field in user email settings
|
||||
merge_request: 52427
|
||||
author: Yogi (@yo)
|
||||
type: other
|
|
@ -23,32 +23,32 @@ Here you can access the complete documentation for GitLab, the single applicatio
|
|||
|
||||
No matter how you use GitLab, we have documentation for you.
|
||||
|
||||
| Essential documentation | Essential documentation |
|
||||
|:---------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------|
|
||||
| Essential documentation | Essential documentation |
|
||||
|:------------------------|:------------------------|
|
||||
| [**User documentation**](user/index.md)<br>Discover features and concepts for GitLab users. | [**Administrator documentation**](administration/index.md)<br/>Everything GitLab self-managed administrators need to know. |
|
||||
| [**Contributing to GitLab**](#contributing-to-gitlab)<br/>At GitLab, everyone can contribute! | [**New to Git and GitLab?**](#new-to-git-and-gitlab)<br/>We have the resources to get you started. |
|
||||
| [**Build an integration with GitLab**](#build-an-integration-with-gitlab)<br/>Consult our integration documentation. | [**Coming to GitLab from another platform?**](#coming-to-gitlab-from-another-platform)<br/>Consult our guides. |
|
||||
| [**Install GitLab**](https://about.gitlab.com/install/)<br/>Installation options for different platforms. | [**Customers**](subscriptions/index.md)<br/>Information for new and existing customers. |
|
||||
| [**Update GitLab**](update/README.md)<br/>Update your GitLab self-managed instance to the latest version. | [**Reference Architectures**](administration/reference_architectures/index.md)<br/>GitLab reference architectures |
|
||||
| [**GitLab releases**](https://about.gitlab.com/releases/)<br/>What's new in GitLab. | |
|
||||
| [**Contributing to GitLab**](#contributing-to-gitlab)<br/>At GitLab, everyone can contribute! | [**New to Git and GitLab?**](#new-to-git-and-gitlab)<br/>We have the resources to get you started. |
|
||||
| [**Build an integration with GitLab**](#build-an-integration-with-gitlab)<br/>Consult our integration documentation. | [**Coming to GitLab from another platform?**](#coming-to-gitlab-from-another-platform)<br/>Consult our guides. |
|
||||
| [**Install GitLab**](https://about.gitlab.com/install/)<br/>Installation options for different platforms. | [**Customers**](subscriptions/index.md)<br/>Information for new and existing customers. |
|
||||
| [**Update GitLab**](update/README.md)<br/>Update your GitLab self-managed instance to the latest version. | [**Reference Architectures**](administration/reference_architectures/index.md)<br/>GitLab reference architectures. |
|
||||
| [**GitLab releases**](https://about.gitlab.com/releases/)<br/>What's new in GitLab. | |
|
||||
|
||||
## Popular topics
|
||||
|
||||
Have a look at some of our most popular topics:
|
||||
|
||||
| Popular topic | Description |
|
||||
|:-----------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------|
|
||||
| [Two-factor authentication](user/profile/account/two_factor_authentication.md) | Improve the security of your GitLab account. |
|
||||
| [GitLab groups](user/group/index.md) | Manage projects together. |
|
||||
| [GitLab CI/CD pipeline configuration reference](ci/yaml/README.md) | Available configuration options for `.gitlab-ci.yml` files. |
|
||||
| [Activate GitLab EE with a license](user/admin_area/license.md) **(STARTER ONLY)** | Activate GitLab Enterprise Edition functionality with a license. |
|
||||
| [Back up and restore GitLab](raketasks/backup_restore.md) **(FREE SELF)** | Rake tasks for backing up and restoring GitLab self-managed instances. |
|
||||
| [GitLab release and maintenance policy](policy/maintenance.md) | Policies for version naming and cadence, and also upgrade recommendations. |
|
||||
| [Elasticsearch integration](integration/elasticsearch.md) **(STARTER ONLY)** | Integrate Elasticsearch with GitLab to enable advanced searching. |
|
||||
| [Omnibus GitLab database settings](https://docs.gitlab.com/omnibus/settings/database.html) **(FREE SELF)** | Database settings for Omnibus GitLab self-managed instances. |
|
||||
| [Omnibus GitLab NGINX settings](https://docs.gitlab.com/omnibus/settings/nginx.html) **(FREE SELF)** | NGINX settings for Omnibus GitLab self-managed instances. |
|
||||
| [Omnibus GitLab SSL configuration](https://docs.gitlab.com/omnibus/settings/ssl.html) **(FREE SELF)** | SSL settings for Omnibus GitLab self-managed instances. |
|
||||
| [GitLab.com settings](user/gitlab_com/index.md) | Settings used for GitLab.com. |
|
||||
| Popular topic | Description |
|
||||
|:-------------------------------------------------------------------------------------------|:------------|
|
||||
| [Two-factor authentication](user/profile/account/two_factor_authentication.md) | Improve the security of your GitLab account. |
|
||||
| [GitLab groups](user/group/index.md) | Manage projects together. |
|
||||
| [GitLab CI/CD pipeline configuration reference](ci/yaml/README.md) | Available configuration options for `.gitlab-ci.yml` files. |
|
||||
| [Activate GitLab EE with a license](user/admin_area/license.md) | Activate GitLab Enterprise Edition functionality with a license. |
|
||||
| [Back up and restore GitLab](raketasks/backup_restore.md) | Rake tasks for backing up and restoring GitLab self-managed instances. |
|
||||
| [GitLab release and maintenance policy](policy/maintenance.md) | Policies for version naming and cadence, and also upgrade recommendations. |
|
||||
| [Elasticsearch integration](integration/elasticsearch.md) | Integrate Elasticsearch with GitLab to enable advanced searching. |
|
||||
| [Omnibus GitLab database settings](https://docs.gitlab.com/omnibus/settings/database.html) | Database settings for Omnibus GitLab self-managed instances. |
|
||||
| [Omnibus GitLab NGINX settings](https://docs.gitlab.com/omnibus/settings/nginx.html) | NGINX settings for Omnibus GitLab self-managed instances. |
|
||||
| [Omnibus GitLab SSL configuration](https://docs.gitlab.com/omnibus/settings/ssl.html) | SSL settings for Omnibus GitLab self-managed instances. |
|
||||
| [GitLab.com settings](user/gitlab_com/index.md) | Settings used for GitLab.com. |
|
||||
|
||||
## The entire DevOps lifecycle
|
||||
|
||||
|
@ -64,53 +64,53 @@ Working with new systems can be daunting.
|
|||
|
||||
We have the following documentation to rapidly uplift your GitLab knowledge:
|
||||
|
||||
| Topic | Description |
|
||||
|:--------------------------------------------------------------------------------------------------|:---------------------------------------------------------------|
|
||||
| [GitLab basics guides](gitlab-basics/README.md) | Start working on the command line and with GitLab. |
|
||||
| [GitLab workflow overview](https://about.gitlab.com/blog/2016/10/25/gitlab-workflow-an-overview/) | Enhance your workflow with the best of GitLab Workflow. |
|
||||
| [Get started with GitLab CI/CD](ci/quick_start/README.md) | Quickly implement GitLab CI/CD. |
|
||||
| [Auto DevOps](topics/autodevops/index.md) | Learn more about Auto DevOps in GitLab. |
|
||||
| [GitLab Markdown](user/markdown.md) | Advanced formatting system (GitLab Flavored Markdown) |
|
||||
| Topic | Description |
|
||||
|:--------------------------------------------------------------------------------------------------|:------------|
|
||||
| [GitLab basics guides](gitlab-basics/README.md) | Start working on the command line and with GitLab. |
|
||||
| [GitLab workflow overview](https://about.gitlab.com/blog/2016/10/25/gitlab-workflow-an-overview/) | Enhance your workflow with the best of GitLab Workflow. |
|
||||
| [Get started with GitLab CI/CD](ci/quick_start/README.md) | Quickly implement GitLab CI/CD. |
|
||||
| [Auto DevOps](topics/autodevops/index.md) | Learn more about Auto DevOps in GitLab. |
|
||||
| [GitLab Markdown](user/markdown.md) | Advanced formatting system (GitLab Flavored Markdown). |
|
||||
|
||||
### User account
|
||||
|
||||
Learn more about GitLab account management:
|
||||
|
||||
| Topic | Description |
|
||||
|:-----------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------|
|
||||
| [User account](user/profile/index.md) | Manage your account. |
|
||||
| Topic | Description |
|
||||
|:-----------------------------------------------------------|:------------|
|
||||
| [User account](user/profile/index.md) | Manage your account. |
|
||||
| [Authentication](topics/authentication/index.md) | Account security with two-factor authentication, set up your SSH keys, and deploy keys for secure access to your projects. |
|
||||
| [Profile settings](user/profile/index.md#profile-settings) | Manage your profile settings, two factor authentication, and more. |
|
||||
| [User permissions](user/permissions.md) | Learn what each role in a project can do. |
|
||||
| [Profile settings](user/profile/index.md#profile-settings) | Manage your profile settings, two factor authentication, and more. |
|
||||
| [User permissions](user/permissions.md) | Learn what each role in a project can do. |
|
||||
|
||||
### Git and GitLab
|
||||
|
||||
Learn more about using Git, and using Git with GitLab:
|
||||
|
||||
| Topic | Description |
|
||||
|:-----------------------------------------------------------------------------|:---------------------------------------------------------------------------|
|
||||
| Topic | Description |
|
||||
|:-----------------------------------------------------------------------------|:------------|
|
||||
| [Git](topics/git/index.md) | Getting started with Git, branching strategies, Git LFS, and advanced use. |
|
||||
| [Git cheat sheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf) | Download a PDF describing the most used Git operations. |
|
||||
| [GitLab Flow](topics/gitlab_flow.md) | Explore the best of Git with the GitLab Flow strategy. |
|
||||
| [Git cheat sheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf) | Download a PDF describing the most used Git operations. |
|
||||
| [GitLab Flow](topics/gitlab_flow.md) | Explore the best of Git with the GitLab Flow strategy. |
|
||||
|
||||
## Coming to GitLab from another platform
|
||||
|
||||
If you are coming to GitLab from another platform, the following information is useful:
|
||||
|
||||
| Topic | Description |
|
||||
|:----------------------------------------------------|:---------------------------------------------------------------------------------------|
|
||||
| Topic | Description |
|
||||
|:----------------------------------------------------|:------------|
|
||||
| [Importing to GitLab](user/project/import/index.md) | Import your projects from GitHub, Bitbucket, GitLab.com, FogBugz, and SVN into GitLab. |
|
||||
| [Migrating from SVN](user/project/import/svn.md) | Convert a SVN repository to Git and GitLab. |
|
||||
| [Migrating from SVN](user/project/import/svn.md) | Convert a SVN repository to Git and GitLab. |
|
||||
|
||||
## Build an integration with GitLab
|
||||
|
||||
There are many ways to integrate with GitLab, including:
|
||||
|
||||
| Topic | Description |
|
||||
|:-------------------------------------------|:---------------------------------------------|
|
||||
| [GitLab REST API](api/README.md) | Integrate with GitLab using our REST API. |
|
||||
| Topic | Description |
|
||||
|:-------------------------------------------|:------------|
|
||||
| [GitLab REST API](api/README.md) | Integrate with GitLab using our REST API. |
|
||||
| [GitLab GraphQL API](api/graphql/index.md) | Integrate with GitLab using our GraphQL API. |
|
||||
| [Integrations](integration/README.md) | Integrations with third-party products. |
|
||||
| [Integrations](integration/README.md) | Integrations with third-party products. |
|
||||
|
||||
## Contributing to GitLab
|
||||
|
||||
|
@ -119,8 +119,8 @@ and GitLab Enterprise Edition is [open-core](https://gitlab.com/gitlab-org/gitla
|
|||
|
||||
Learn how to contribute to GitLab with the following resources:
|
||||
|
||||
| Topic | Description |
|
||||
|:------------------------------------------------------------|:-----------------------------------------|
|
||||
| Topic | Description |
|
||||
|:------------------------------------------------------------|:------------|
|
||||
| [Development](development/README.md) | How to contribute to GitLab development. |
|
||||
| [Legal](legal/README.md) | Contributor license agreements. |
|
||||
| [Writing documentation](development/documentation/index.md) | How to contribute to GitLab Docs. |
|
||||
| [Legal](legal/README.md) | Contributor license agreements. |
|
||||
| [Writing documentation](development/documentation/index.md) | How to contribute to GitLab Docs. |
|
||||
|
|
|
@ -28,8 +28,8 @@ From the pipeline editor page you can:
|
|||
- [Commit](#commit-changes-to-ci-configuration) the changes to a specific branch.
|
||||
|
||||
NOTE:
|
||||
You must have already [created a CI/CD configuration file](../quick_start/README.md#create-a-gitlab-ciyml-file)
|
||||
to use the editor.
|
||||
You must already have [a `.gitlab-ci.yml` file](../quick_start/README.md#create-a-gitlab-ciyml-file)
|
||||
on the default branch (usually "master") of your project to use the editor.
|
||||
|
||||
## Validate CI configuration
|
||||
|
||||
|
|
|
@ -145,4 +145,8 @@ stageGroupDashboards.dashboard('source_code')
|
|||
|
||||
![Stage Group Dashboard Customization](img/stage_group_dashboards_time_customization.png)
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
If you want to see the workflow in action, we've recorded a pairing session on customizing a dashboard,
|
||||
available on [GitLab Unfiltered](https://youtu.be/shEd_eiUjdI).
|
||||
|
||||
For deeper customization and more complicated metrics, visit the [Grafonnet lib](https://github.com/grafana/grafonnet-lib) project and the [GitLab Prometheus Metrics](../administration/monitoring/prometheus/gitlab_metrics.md#gitlab-prometheus-metrics) documentation.
|
||||
|
|
|
@ -7,7 +7,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
# Feature Flags **(FREE)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7433) in GitLab 11.4.
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to [GitLab Starter](https://about.gitlab.com/pricing/) in 13.4.
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.5.
|
||||
|
||||
With Feature Flags, you can deploy your application's new features to production in smaller batches.
|
||||
|
@ -61,14 +60,13 @@ next to any feature flag in the list.
|
|||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/254379) in GitLab 13.5.
|
||||
|
||||
The maximum number of feature flags per project on self-managed GitLab instances
|
||||
is 200. On GitLab.com, the maximum number is determined by [GitLab.com tier](https://about.gitlab.com/pricing/):
|
||||
is 200. For GitLab SaaS, the maximum number is determined by [tier](https://about.gitlab.com/pricing/):
|
||||
|
||||
| Tier | Number of feature flags per project |
|
||||
|----------|-------------------------------------|
|
||||
| Free | 50 |
|
||||
| Bronze | 100 |
|
||||
| Silver | 150 |
|
||||
| Gold | 200 |
|
||||
| Premium | 150 |
|
||||
| Ultimate | 200 |
|
||||
|
||||
## Feature flag strategies
|
||||
|
||||
|
|
|
@ -6,19 +6,20 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Operations Dashboard **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5781) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.5. [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/9218) to [GitLab Premium](https://about.gitlab.com/pricing/) in 11.10.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5781) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.5.
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/9218) to [GitLab Premium](https://about.gitlab.com/pricing/) in 11.10.
|
||||
|
||||
The Operations Dashboard provides a summary of each project's operational health,
|
||||
including pipeline and alert status.
|
||||
|
||||
The dashboard can be accessed via the top bar, by clicking **More > Operations**.
|
||||
The dashboard can be accessed from the top bar, by clicking **More > Operations**.
|
||||
|
||||
## Adding a project to the dashboard
|
||||
|
||||
NOTE:
|
||||
For GitLab.com, you can add your project to the Operations Dashboard for free if
|
||||
your project is public. If your project is private, the group it belongs to must
|
||||
have a [Silver](https://about.gitlab.com/pricing/) plan.
|
||||
have a [GitLab Premium](https://about.gitlab.com/pricing/) plan.
|
||||
|
||||
To add a project to the dashboard:
|
||||
|
||||
|
|
|
@ -40,6 +40,26 @@ You can select a framework label to identify that your project has certain compl
|
|||
NOTE:
|
||||
Compliance framework labels do not affect your project settings.
|
||||
|
||||
#### Custom compliance frameworks
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276221) in GitLab 13.9.
|
||||
> - It's [deployed behind a feature flag](../../feature_flags.md), disabled by default.
|
||||
> - It's disabled on GitLab.com.
|
||||
> - It's not recommended for production use.
|
||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-custom-compliance-frameworks). **(PREMIUM ONLY)**
|
||||
|
||||
WARNING:
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
|
||||
GitLab 13.8 introduces custom compliance frameworks at the group-level. A group owner can create a compliance framework label
|
||||
and assign it to any number of projects within that group or sub-groups. When this feature is enabled, projects can only
|
||||
be assigned compliance framework labels that already exist within that group.
|
||||
|
||||
If existing [Compliance frameworks](#compliance-framework) are not sufficient, you can now create
|
||||
your own.
|
||||
|
||||
New compliance framework labels can be created and updated using GraphQL.
|
||||
|
||||
### Sharing and permissions
|
||||
|
||||
For your repository, you can set up features such as public access, repository features,
|
||||
|
@ -299,3 +319,22 @@ Add the URL of a Jaeger server to allow your users to [easily access the Jaeger
|
|||
|
||||
[Add Storage credentials](../../../operations/incident_management/status_page.md#sync-incidents-to-the-status-page)
|
||||
to enable the syncing of public Issues to a [deployed status page](../../../operations/incident_management/status_page.md#create-a-status-page-project).
|
||||
|
||||
### Enable or disable custom compliance frameworks **(PREMIUM ONLY)**
|
||||
|
||||
Enabling or disabling custom compliance frameworks is under development and not ready for production use. It is
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||
can enable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:ff_custom_compliance_frameworks)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:ff_custom_compliance_frameworks)
|
||||
```
|
||||
|
|
|
@ -1253,6 +1253,9 @@ msgstr ""
|
|||
msgid "A deleted user"
|
||||
msgstr ""
|
||||
|
||||
msgid "A description is required"
|
||||
msgstr ""
|
||||
|
||||
msgid "A file has been changed."
|
||||
msgstr ""
|
||||
|
||||
|
@ -1346,6 +1349,9 @@ msgstr ""
|
|||
msgid "A string appended to the project path to form the Service Desk email address."
|
||||
msgstr ""
|
||||
|
||||
msgid "A title is required"
|
||||
msgstr ""
|
||||
|
||||
msgid "A user can only participate in a rotation once"
|
||||
msgstr ""
|
||||
|
||||
|
@ -7326,6 +7332,9 @@ msgstr ""
|
|||
msgid "ComplianceFrameworks|There are no compliance frameworks set up yet"
|
||||
msgstr ""
|
||||
|
||||
msgid "ComplianceFrameworks|Use %{codeStart}::%{codeEnd} to create a %{linkStart}scoped set%{linkEnd} (eg. %{codeStart}SOX::AWS%{codeEnd})"
|
||||
msgstr ""
|
||||
|
||||
msgid "ComplianceFramework|GDPR"
|
||||
msgstr ""
|
||||
|
||||
|
@ -19318,6 +19327,9 @@ msgstr ""
|
|||
msgid "No commits present here"
|
||||
msgstr ""
|
||||
|
||||
msgid "No compliance frameworks are in use. Create one using the GraphQL API."
|
||||
msgstr ""
|
||||
|
||||
msgid "No connection could be made to a Gitaly Server, please check your logs!"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -107,5 +107,21 @@ RSpec.describe 'User views an open merge request' do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the assignee\'s availability set' do
|
||||
before do
|
||||
merge_request.author.create_status(availability: 'busy')
|
||||
merge_request.assignees << merge_request.author
|
||||
|
||||
visit(merge_request_path(merge_request))
|
||||
end
|
||||
|
||||
it 'exposes the availability in the data-availability attribute' do
|
||||
assignees_data = find_all("input[name='merge_request[assignee_ids][]']", visible: false)
|
||||
|
||||
expect(assignees_data.size).to eq(1)
|
||||
expect(assignees_data.first['data-availability']).to eq('busy')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -118,5 +118,10 @@ RSpec.describe Autocomplete::UsersFinder do
|
|||
|
||||
it { is_expected.to match_array([user1, external_user, omniauth_user, current_user]) }
|
||||
end
|
||||
|
||||
it 'preloads the status association' do
|
||||
associations = subject.map { |user| user.association(:status) }
|
||||
expect(associations).to all(be_loaded)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"source",
|
||||
"valid_roles",
|
||||
"can_update",
|
||||
"can_remove"
|
||||
"can_remove",
|
||||
"is_direct_member"
|
||||
],
|
||||
"properties": {
|
||||
"id": { "type": "integer" },
|
||||
|
@ -18,6 +19,7 @@
|
|||
"requested_at": { "type": ["date-time", "null"] },
|
||||
"can_update": { "type": "boolean" },
|
||||
"can_remove": { "type": "boolean" },
|
||||
"is_direct_member": { "type": "boolean" },
|
||||
"access_level": {
|
||||
"type": "object",
|
||||
"required": ["integer_value", "string_value"],
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"web_url": { "type": "string" },
|
||||
"blocked": { "type": "boolean" },
|
||||
"two_factor_enabled": { "type": "boolean" },
|
||||
"availability": { "type": ["string", "null"] },
|
||||
"status": {
|
||||
"type": "object",
|
||||
"required": ["emoji"],
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import { mount, createLocalVue } from '@vue/test-utils';
|
||||
import Vuex from 'vuex';
|
||||
import { MEMBER_TYPES } from '~/members/constants';
|
||||
import { member as memberMock, group, invite, accessRequest } from '../../mock_data';
|
||||
import {
|
||||
member as memberMock,
|
||||
directMember,
|
||||
inheritedMember,
|
||||
group,
|
||||
invite,
|
||||
accessRequest,
|
||||
} from '../../mock_data';
|
||||
import MembersTableCell from '~/members/components/table/members_table_cell.vue';
|
||||
|
||||
describe('MembersTableCell', () => {
|
||||
|
@ -75,19 +82,12 @@ describe('MembersTableCell', () => {
|
|||
|
||||
const createComponentWithDirectMember = (member = {}) => {
|
||||
createComponent({
|
||||
member: {
|
||||
...memberMock,
|
||||
source: {
|
||||
...memberMock.source,
|
||||
id: 1,
|
||||
},
|
||||
...member,
|
||||
},
|
||||
member: { ...directMember, ...member },
|
||||
});
|
||||
};
|
||||
const createComponentWithInheritedMember = (member = {}) => {
|
||||
createComponent({
|
||||
member: { ...memberMock, ...member },
|
||||
member: { ...inheritedMember, ...member },
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import RoleDropdown from '~/members/components/table/role_dropdown.vue';
|
|||
import ExpirationDatepicker from '~/members/components/table/expiration_datepicker.vue';
|
||||
import MemberActionButtons from '~/members/components/table/member_action_buttons.vue';
|
||||
import * as initUserPopovers from '~/user_popovers';
|
||||
import { member as memberMock, invite, accessRequest } from '../../mock_data';
|
||||
import { member as memberMock, directMember, invite, accessRequest } from '../../mock_data';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
|
@ -74,11 +74,6 @@ describe('MembersTable', () => {
|
|||
});
|
||||
|
||||
describe('fields', () => {
|
||||
const directMember = {
|
||||
...memberMock,
|
||||
source: { ...memberMock.source, id: 1 },
|
||||
};
|
||||
|
||||
const memberCanUpdate = {
|
||||
...directMember,
|
||||
canUpdate: true,
|
||||
|
|
|
@ -4,6 +4,7 @@ export const member = {
|
|||
canRemove: false,
|
||||
canOverride: false,
|
||||
isOverridden: false,
|
||||
isDirectMember: false,
|
||||
accessLevel: { integerValue: 50, stringValue: 'Owner' },
|
||||
source: {
|
||||
id: 178,
|
||||
|
@ -71,3 +72,6 @@ export const accessRequest = {
|
|||
export const members = [member];
|
||||
|
||||
export const membersJsonString = JSON.stringify(members);
|
||||
|
||||
export const directMember = { ...member, isDirectMember: true };
|
||||
export const inheritedMember = { ...member, isDirectMember: false };
|
||||
|
|
|
@ -13,10 +13,16 @@ import {
|
|||
groupLinkRequestFormatter,
|
||||
} from '~/members/utils';
|
||||
import { DEFAULT_SORT } from '~/members/constants';
|
||||
import { member as memberMock, group, invite, membersJsonString, members } from './mock_data';
|
||||
import {
|
||||
member as memberMock,
|
||||
directMember,
|
||||
inheritedMember,
|
||||
group,
|
||||
invite,
|
||||
membersJsonString,
|
||||
members,
|
||||
} from './mock_data';
|
||||
|
||||
const DIRECT_MEMBER_ID = 178;
|
||||
const INHERITED_MEMBER_ID = 179;
|
||||
const IS_CURRENT_USER_ID = 123;
|
||||
const IS_NOT_CURRENT_USER_ID = 124;
|
||||
const URL_HOST = 'https://localhost/';
|
||||
|
@ -59,11 +65,11 @@ describe('Members Utils', () => {
|
|||
|
||||
describe('isDirectMember', () => {
|
||||
test.each`
|
||||
sourceId | expected
|
||||
${DIRECT_MEMBER_ID} | ${true}
|
||||
${INHERITED_MEMBER_ID} | ${false}
|
||||
`('returns $expected', ({ sourceId, expected }) => {
|
||||
expect(isDirectMember(memberMock, sourceId)).toBe(expected);
|
||||
member | expected
|
||||
${directMember} | ${true}
|
||||
${inheritedMember} | ${false}
|
||||
`('returns $expected', ({ member, expected }) => {
|
||||
expect(isDirectMember(member)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -78,18 +84,13 @@ describe('Members Utils', () => {
|
|||
});
|
||||
|
||||
describe('canRemove', () => {
|
||||
const memberCanRemove = {
|
||||
...memberMock,
|
||||
canRemove: true,
|
||||
};
|
||||
|
||||
test.each`
|
||||
member | sourceId | expected
|
||||
${memberCanRemove} | ${DIRECT_MEMBER_ID} | ${true}
|
||||
${memberCanRemove} | ${INHERITED_MEMBER_ID} | ${false}
|
||||
${memberMock} | ${INHERITED_MEMBER_ID} | ${false}
|
||||
`('returns $expected', ({ member, sourceId, expected }) => {
|
||||
expect(canRemove(member, sourceId)).toBe(expected);
|
||||
member | expected
|
||||
${{ ...directMember, canRemove: true }} | ${true}
|
||||
${{ ...inheritedMember, canRemove: true }} | ${false}
|
||||
${{ ...memberMock, canRemove: false }} | ${false}
|
||||
`('returns $expected', ({ member, expected }) => {
|
||||
expect(canRemove(member)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -98,25 +99,20 @@ describe('Members Utils', () => {
|
|||
member | expected
|
||||
${invite} | ${true}
|
||||
${{ ...invite, invite: { ...invite.invite, canResend: false } }} | ${false}
|
||||
`('returns $expected', ({ member, sourceId, expected }) => {
|
||||
expect(canResend(member, sourceId)).toBe(expected);
|
||||
`('returns $expected', ({ member, expected }) => {
|
||||
expect(canResend(member)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('canUpdate', () => {
|
||||
const memberCanUpdate = {
|
||||
...memberMock,
|
||||
canUpdate: true,
|
||||
};
|
||||
|
||||
test.each`
|
||||
member | currentUserId | sourceId | expected
|
||||
${memberCanUpdate} | ${IS_NOT_CURRENT_USER_ID} | ${DIRECT_MEMBER_ID} | ${true}
|
||||
${memberCanUpdate} | ${IS_CURRENT_USER_ID} | ${DIRECT_MEMBER_ID} | ${false}
|
||||
${memberCanUpdate} | ${IS_CURRENT_USER_ID} | ${INHERITED_MEMBER_ID} | ${false}
|
||||
${memberMock} | ${IS_NOT_CURRENT_USER_ID} | ${DIRECT_MEMBER_ID} | ${false}
|
||||
`('returns $expected', ({ member, currentUserId, sourceId, expected }) => {
|
||||
expect(canUpdate(member, currentUserId, sourceId)).toBe(expected);
|
||||
member | currentUserId | expected
|
||||
${{ ...directMember, canUpdate: true }} | ${IS_NOT_CURRENT_USER_ID} | ${true}
|
||||
${{ ...directMember, canUpdate: true }} | ${IS_CURRENT_USER_ID} | ${false}
|
||||
${{ ...inheritedMember, canUpdate: true }} | ${IS_CURRENT_USER_ID} | ${false}
|
||||
${{ ...directMember, canUpdate: false }} | ${IS_NOT_CURRENT_USER_ID} | ${false}
|
||||
`('returns $expected', ({ member, currentUserId, expected }) => {
|
||||
expect(canUpdate(member, currentUserId)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -8,8 +8,11 @@ RSpec.describe Terraform::State do
|
|||
it { is_expected.to belong_to(:project) }
|
||||
it { is_expected.to belong_to(:locked_by_user).class_name('User') }
|
||||
|
||||
it { is_expected.to validate_presence_of(:name) }
|
||||
it { is_expected.to validate_presence_of(:project_id) }
|
||||
|
||||
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
|
||||
|
||||
describe 'scopes' do
|
||||
describe '.ordered_by_name' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe MemberEntity do
|
||||
let_it_be(:current_user) { create(:user) }
|
||||
let(:entity) { described_class.new(member, { current_user: current_user, group: group }) }
|
||||
let(:entity) { described_class.new(member, { current_user: current_user, group: group, source: source }) }
|
||||
let(:entity_hash) { entity.as_json }
|
||||
|
||||
shared_examples 'member.json' do
|
||||
|
@ -40,8 +40,27 @@ RSpec.describe MemberEntity do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples 'is_direct_member' do
|
||||
context 'when `source` is the same as `member.source`' do
|
||||
let(:source) { direct_member_source }
|
||||
|
||||
it 'exposes `is_direct_member` as `true`' do
|
||||
expect(entity_hash[:is_direct_member]).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when `source` is not the same as `member.source`' do
|
||||
let(:source) { inherited_member_source }
|
||||
|
||||
it 'exposes `is_direct_member` as `false`' do
|
||||
expect(entity_hash[:is_direct_member]).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'group member' do
|
||||
let(:group) { create(:group) }
|
||||
let(:source) { group }
|
||||
let(:member) { GroupMemberPresenter.new(create(:group_member, group: group), current_user: current_user) }
|
||||
|
||||
it_behaves_like 'member.json'
|
||||
|
@ -52,11 +71,19 @@ RSpec.describe MemberEntity do
|
|||
it_behaves_like 'member.json'
|
||||
it_behaves_like 'invite'
|
||||
end
|
||||
|
||||
context 'is_direct_member' do
|
||||
let(:direct_member_source) { group }
|
||||
let(:inherited_member_source) { create(:group) }
|
||||
|
||||
it_behaves_like 'is_direct_member'
|
||||
end
|
||||
end
|
||||
|
||||
context 'project member' do
|
||||
let(:project) { create(:project) }
|
||||
let(:group) { project.group }
|
||||
let(:source) { project }
|
||||
let(:member) { ProjectMemberPresenter.new(create(:project_member, project: project), current_user: current_user) }
|
||||
|
||||
it_behaves_like 'member.json'
|
||||
|
@ -67,5 +94,12 @@ RSpec.describe MemberEntity do
|
|||
it_behaves_like 'member.json'
|
||||
it_behaves_like 'invite'
|
||||
end
|
||||
|
||||
context 'is_direct_member' do
|
||||
let(:direct_member_source) { project }
|
||||
let(:inherited_member_source) { group }
|
||||
|
||||
it_behaves_like 'is_direct_member'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ RSpec.describe MemberSerializer do
|
|||
|
||||
let_it_be(:current_user) { create(:user) }
|
||||
|
||||
subject { described_class.new.represent(members, { current_user: current_user, group: group }) }
|
||||
subject { described_class.new.represent(members, { current_user: current_user, group: group, source: source }) }
|
||||
|
||||
shared_examples 'members.json' do
|
||||
it 'matches json schema' do
|
||||
|
@ -17,6 +17,7 @@ RSpec.describe MemberSerializer do
|
|||
|
||||
context 'group member' do
|
||||
let(:group) { create(:group) }
|
||||
let(:source) { group }
|
||||
let(:members) { present_members(create_list(:group_member, 1, group: group)) }
|
||||
|
||||
it_behaves_like 'members.json'
|
||||
|
@ -24,6 +25,7 @@ RSpec.describe MemberSerializer do
|
|||
|
||||
context 'project member' do
|
||||
let(:project) { create(:project) }
|
||||
let(:source) { project }
|
||||
let(:group) { project.group }
|
||||
let(:members) { present_members(create_list(:project_member, 1, project: project)) }
|
||||
|
||||
|
|
|
@ -17,5 +17,23 @@ RSpec.describe MergeRequestUserEntity do
|
|||
it 'exposes needed attributes' do
|
||||
expect(subject).to include(:id, :name, :username, :state, :avatar_url, :web_url, :can_merge)
|
||||
end
|
||||
|
||||
context 'when `status` is not preloaded' do
|
||||
it 'does not expose the availability attribute' do
|
||||
expect(subject).not_to include(:availability)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when `status` is preloaded' do
|
||||
before do
|
||||
user.create_status!(availability: :busy)
|
||||
|
||||
user.status # make sure `status` is loaded
|
||||
end
|
||||
|
||||
it 'exposes the availibility attribute' do
|
||||
expect(subject[:availability]).to eq('busy')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue