Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-01-28 18:09:27 +00:00
parent c6ee7ef0f5
commit e1b9b92a49
42 changed files with 317 additions and 139 deletions

View file

@ -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)
);
},

View file

@ -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() {

View file

@ -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) => {

View file

@ -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

View file

@ -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

View file

@ -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`

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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' }

View file

@ -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?

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 } }

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,5 @@
---
title: Expose user availablility data on issuable pages
merge_request: 52333
author:
type: other

View file

@ -0,0 +1,5 @@
---
title: Add name validation to Terraform state
merge_request: 52102
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Apply new GitLab UI class for U2F try again button
merge_request: 52759
author: Yogi (@yo)
type: other

View 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

View file

@ -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. |

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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:

View file

@ -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)
```

View file

@ -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 ""

View file

@ -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

View file

@ -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

View file

@ -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"],

View file

@ -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"],

View file

@ -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 },
});
};

View file

@ -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,

View file

@ -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 };

View file

@ -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);
});
});

View file

@ -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) }

View file

@ -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

View file

@ -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)) }

View file

@ -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