Add latest changes from gitlab-org/gitlab@master
|
@ -138,6 +138,7 @@ export default {
|
|||
snippetsAccessLevel: featureAccessLevel.EVERYONE,
|
||||
pagesAccessLevel: featureAccessLevel.EVERYONE,
|
||||
metricsDashboardAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
|
||||
analyticsAccessLevel: featureAccessLevel.EVERYONE,
|
||||
requirementsAccessLevel: featureAccessLevel.EVERYONE,
|
||||
containerRegistryEnabled: true,
|
||||
lfsEnabled: true,
|
||||
|
@ -241,6 +242,10 @@ export default {
|
|||
featureAccessLevel.PROJECT_MEMBERS,
|
||||
this.metricsDashboardAccessLevel,
|
||||
);
|
||||
this.analyticsAccessLevel = Math.min(
|
||||
featureAccessLevel.PROJECT_MEMBERS,
|
||||
this.analyticsAccessLevel,
|
||||
);
|
||||
this.requirementsAccessLevel = Math.min(
|
||||
featureAccessLevel.PROJECT_MEMBERS,
|
||||
this.requirementsAccessLevel,
|
||||
|
@ -266,6 +271,8 @@ export default {
|
|||
this.snippetsAccessLevel = featureAccessLevel.EVERYONE;
|
||||
if (this.pagesAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
|
||||
this.pagesAccessLevel = featureAccessLevel.EVERYONE;
|
||||
if (this.analyticsAccessLevel > featureAccessLevel.NOT_ENABLED)
|
||||
this.analyticsAccessLevel = featureAccessLevel.EVERYONE;
|
||||
if (this.metricsDashboardAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
|
||||
this.metricsDashboardAccessLevel = featureAccessLevel.EVERYONE;
|
||||
if (this.requirementsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
|
||||
|
@ -494,6 +501,17 @@ export default {
|
|||
/>
|
||||
</project-setting-row>
|
||||
</div>
|
||||
<project-setting-row
|
||||
ref="analytics-settings"
|
||||
:label="s__('ProjectSettings|Analytics')"
|
||||
:help-text="s__('ProjectSettings|View project analytics')"
|
||||
>
|
||||
<project-feature-setting
|
||||
v-model="analyticsAccessLevel"
|
||||
:options="featureAccessLevelOptions"
|
||||
name="project[project_feature_attributes][analytics_access_level]"
|
||||
/>
|
||||
</project-setting-row>
|
||||
<project-setting-row
|
||||
v-if="requirementsAvailable"
|
||||
ref="requirements-settings"
|
||||
|
|
|
@ -49,6 +49,7 @@ function UsersSelect(currentUser, els, options = {}) {
|
|||
options.todoStateFilter = $dropdown.data('todoStateFilter');
|
||||
options.iid = $dropdown.data('iid');
|
||||
options.issuableType = $dropdown.data('issuableType');
|
||||
options.targetBranch = $dropdown.data('targetBranch');
|
||||
const showNullUser = $dropdown.data('nullUser');
|
||||
const defaultNullUser = $dropdown.data('nullUserDefault');
|
||||
const showMenuAbove = $dropdown.data('showMenuAbove');
|
||||
|
@ -582,7 +583,14 @@ function UsersSelect(currentUser, els, options = {}) {
|
|||
img = `<img src='${avatar}' class='avatar avatar-inline m-0' width='32' />`;
|
||||
}
|
||||
|
||||
return userSelect.renderRow(options.issuableType, user, selected, username, img);
|
||||
return userSelect.renderRow(
|
||||
options.issuableType,
|
||||
user,
|
||||
selected,
|
||||
username,
|
||||
img,
|
||||
elsClassName,
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -746,8 +754,17 @@ UsersSelect.prototype.users = function(query, options, callback) {
|
|||
...getAjaxUsersSelectParams(options, AJAX_USERS_SELECT_PARAMS_MAP),
|
||||
};
|
||||
|
||||
if (options.issuableType === 'merge_request') {
|
||||
const isMergeRequest = options.issuableType === 'merge_request';
|
||||
const isEditMergeRequest = !options.issuableType && (options.iid && options.targetBranch);
|
||||
const isNewMergeRequest = !options.issuableType && (!options.iid && options.targetBranch);
|
||||
|
||||
if (isMergeRequest || isEditMergeRequest || isNewMergeRequest) {
|
||||
params.merge_request_iid = options.iid || null;
|
||||
params.approval_rules = true;
|
||||
}
|
||||
|
||||
if (isNewMergeRequest) {
|
||||
params.target_branch = options.targetBranch || null;
|
||||
}
|
||||
|
||||
return axios.get(url, { params }).then(({ data }) => {
|
||||
|
@ -762,7 +779,14 @@ UsersSelect.prototype.buildUrl = function(url) {
|
|||
return url;
|
||||
};
|
||||
|
||||
UsersSelect.prototype.renderRow = function(issuableType, user, selected, username, img) {
|
||||
UsersSelect.prototype.renderRow = function(
|
||||
issuableType,
|
||||
user,
|
||||
selected,
|
||||
username,
|
||||
img,
|
||||
elsClassName,
|
||||
) {
|
||||
const tooltip = issuableType === 'merge_request' && !user.can_merge ? __('Cannot merge') : '';
|
||||
const tooltipClass = tooltip ? `has-tooltip` : '';
|
||||
const selectedClass = selected === true ? 'is-active' : '';
|
||||
|
@ -776,10 +800,15 @@ UsersSelect.prototype.renderRow = function(issuableType, user, selected, usernam
|
|||
<a href="#" class="dropdown-menu-user-link d-flex align-items-center ${linkClasses}" ${tooltipAttributes}>
|
||||
${this.renderRowAvatar(issuableType, user, img)}
|
||||
<span class="d-flex flex-column overflow-hidden">
|
||||
<strong class="dropdown-menu-user-full-name">
|
||||
<strong class="dropdown-menu-user-full-name gl-font-weight-bold">
|
||||
${escape(user.name)}
|
||||
</strong>
|
||||
${username ? `<span class="dropdown-menu-user-username">${username}</span>` : ''}
|
||||
${
|
||||
username
|
||||
? `<span class="dropdown-menu-user-username gl-text-gray-400">${username}</span>`
|
||||
: ''
|
||||
}
|
||||
${this.renderApprovalRules(elsClassName, user.applicable_approval_rules)}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -802,4 +831,22 @@ UsersSelect.prototype.renderRowAvatar = function(issuableType, user, img) {
|
|||
</span>`;
|
||||
};
|
||||
|
||||
UsersSelect.prototype.renderApprovalRules = function(elsClassName, approvalRules = []) {
|
||||
if (!gon.features?.reviewerApprovalRules || !elsClassName?.includes('reviewer')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const count = approvalRules.length;
|
||||
const [rule] = approvalRules;
|
||||
const countText = sprintf(__('(+%{count} rules)'), { count });
|
||||
const renderApprovalRulesCount = count > 1 ? `<span class="ml-1">${countText}</span>` : '';
|
||||
|
||||
return count
|
||||
? `<div class="gl-display-flex gl-font-sm">
|
||||
<span class="gl-text-truncate" title="${rule.name}">${rule.name}</span>
|
||||
${renderApprovalRulesCount}
|
||||
</div>`
|
||||
: '';
|
||||
};
|
||||
|
||||
export default UsersSelect;
|
||||
|
|
|
@ -14,6 +14,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
|
|||
before_action do
|
||||
push_frontend_feature_flag(:merge_request_reviewers, @project, default_enabled: true)
|
||||
push_frontend_feature_flag(:mr_collapsed_approval_rules, @project)
|
||||
push_frontend_feature_flag(:reviewer_approval_rules, @project)
|
||||
end
|
||||
|
||||
def new
|
||||
|
|
|
@ -53,6 +53,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
push_frontend_feature_flag(:vue_issuable_sidebar, @project.group)
|
||||
push_frontend_feature_flag(:merge_request_reviewers, @project, default_enabled: true)
|
||||
push_frontend_feature_flag(:mr_collapsed_approval_rules, @project)
|
||||
push_frontend_feature_flag(:reviewer_approval_rules, @project)
|
||||
end
|
||||
|
||||
around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :discussions]
|
||||
|
|
|
@ -387,6 +387,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
wiki_access_level
|
||||
pages_access_level
|
||||
metrics_dashboard_access_level
|
||||
analytics_access_level
|
||||
operations_access_level
|
||||
]
|
||||
end
|
||||
|
|
|
@ -55,7 +55,7 @@ module FormHelper
|
|||
dropdown_data
|
||||
end
|
||||
|
||||
def reviewers_dropdown_options(issuable_type)
|
||||
def reviewers_dropdown_options(issuable_type, iid = nil, target_branch = nil)
|
||||
dropdown_data = {
|
||||
toggle_class: 'js-reviewer-search js-multiselect js-save-user-data',
|
||||
title: 'Request review from',
|
||||
|
@ -78,6 +78,14 @@ module FormHelper
|
|||
}
|
||||
}
|
||||
|
||||
if iid
|
||||
dropdown_data[:data][:iid] = iid
|
||||
end
|
||||
|
||||
if target_branch
|
||||
dropdown_data[:data][:target_branch] = target_branch
|
||||
end
|
||||
|
||||
if merge_request_supports_multiple_reviewers?
|
||||
dropdown_data = multiple_reviewers_dropdown_options(dropdown_data)
|
||||
end
|
||||
|
|
|
@ -463,7 +463,8 @@ module ProjectsHelper
|
|||
issues: :read_issue,
|
||||
project_members: :read_project_member,
|
||||
wiki: :read_wiki,
|
||||
feature_flags: :read_feature_flag
|
||||
feature_flags: :read_feature_flag,
|
||||
analytics: :read_analytics
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -625,6 +626,7 @@ module ProjectsHelper
|
|||
wikiAccessLevel: feature.wiki_access_level,
|
||||
snippetsAccessLevel: feature.snippets_access_level,
|
||||
pagesAccessLevel: feature.pages_access_level,
|
||||
analyticsAccessLevel: feature.analytics_access_level,
|
||||
containerRegistryEnabled: !!project.container_registry_enabled,
|
||||
lfsEnabled: !!project.lfs_enabled,
|
||||
emailsDisabled: project.emails_disabled?,
|
||||
|
|
|
@ -70,6 +70,10 @@ module ProjectFeaturesCompatibility
|
|||
write_feature_attribute_string(:metrics_dashboard_access_level, value)
|
||||
end
|
||||
|
||||
def analytics_access_level=(value)
|
||||
write_feature_attribute_string(:analytics_access_level, value)
|
||||
end
|
||||
|
||||
def operations_access_level=(value)
|
||||
write_feature_attribute_string(:operations_access_level, value)
|
||||
end
|
||||
|
|
|
@ -385,10 +385,10 @@ class Project < ApplicationRecord
|
|||
|
||||
delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
|
||||
:merge_requests_enabled?, :forking_enabled?, :issues_enabled?,
|
||||
:pages_enabled?, :snippets_enabled?, :public_pages?, :private_pages?,
|
||||
:pages_enabled?, :analytics_enabled?, :snippets_enabled?, :public_pages?, :private_pages?,
|
||||
:merge_requests_access_level, :forking_access_level, :issues_access_level,
|
||||
:wiki_access_level, :snippets_access_level, :builds_access_level,
|
||||
:repository_access_level, :pages_access_level, :metrics_dashboard_access_level,
|
||||
:repository_access_level, :pages_access_level, :metrics_dashboard_access_level, :analytics_access_level,
|
||||
:operations_enabled?, :operations_access_level, to: :project_feature, allow_nil: true
|
||||
delegate :show_default_award_emojis, :show_default_award_emojis=,
|
||||
:show_default_award_emojis?,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class ProjectFeature < ApplicationRecord
|
||||
include Featurable
|
||||
|
||||
FEATURES = %i(issues forking merge_requests wiki snippets builds repository pages metrics_dashboard operations).freeze
|
||||
FEATURES = %i(issues forking merge_requests wiki snippets builds repository pages metrics_dashboard analytics operations).freeze
|
||||
|
||||
set_available_features(FEATURES)
|
||||
|
||||
|
@ -44,6 +44,7 @@ class ProjectFeature < ApplicationRecord
|
|||
default_value_for :snippets_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :analytics_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :metrics_dashboard_access_level, value: PRIVATE, allows_nil: false
|
||||
default_value_for :operations_access_level, value: ENABLED, allows_nil: false
|
||||
|
||||
|
|
|
@ -73,8 +73,6 @@ class ProjectStatistics < ApplicationRecord
|
|||
end
|
||||
|
||||
def update_uploads_size
|
||||
return uploads_size unless Feature.enabled?(:count_uploads_size_in_storage_stats, project)
|
||||
|
||||
self.uploads_size = project.uploads.sum(:size)
|
||||
end
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ class Snippet < ApplicationRecord
|
|||
has_many :notes, as: :noteable, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :user_mentions, class_name: "SnippetUserMention", dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_one :snippet_repository, inverse_of: :snippet
|
||||
has_many :repository_storage_moves, class_name: 'SnippetRepositoryStorageMove', inverse_of: :container
|
||||
|
||||
# We need to add the `dependent` in order to call the after_destroy callback
|
||||
has_one :statistics, class_name: 'SnippetStatistics', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# SnippetRepositoryStorageMove are details of repository storage moves for a
|
||||
# snippet. For example, moving a snippet to another gitaly node to help
|
||||
# balance storage capacity.
|
||||
class SnippetRepositoryStorageMove < ApplicationRecord
|
||||
extend ::Gitlab::Utils::Override
|
||||
include RepositoryStorageMovable
|
||||
|
||||
belongs_to :container, class_name: 'Snippet', inverse_of: :repository_storage_moves, foreign_key: :snippet_id
|
||||
alias_attribute :snippet, :container
|
||||
|
||||
override :schedule_repository_storage_update_worker
|
||||
def schedule_repository_storage_update_worker
|
||||
# TODO https://gitlab.com/gitlab-org/gitlab/-/issues/218991
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
override :error_key
|
||||
def error_key
|
||||
:snippet
|
||||
end
|
||||
end
|
|
@ -151,6 +151,7 @@ class ProjectPolicy < BasePolicy
|
|||
builds
|
||||
pages
|
||||
metrics_dashboard
|
||||
analytics
|
||||
operations
|
||||
]
|
||||
|
||||
|
@ -216,6 +217,7 @@ class ProjectPolicy < BasePolicy
|
|||
enable :award_emoji
|
||||
enable :read_pages_content
|
||||
enable :read_release
|
||||
enable :read_analytics
|
||||
end
|
||||
|
||||
# These abilities are not allowed to admins that are not members of the project,
|
||||
|
@ -442,6 +444,10 @@ class ProjectPolicy < BasePolicy
|
|||
prevent(*create_read_update_admin_destroy(:snippet))
|
||||
end
|
||||
|
||||
rule { analytics_disabled }.policy do
|
||||
prevent(:read_analytics)
|
||||
end
|
||||
|
||||
rule { wiki_disabled }.policy do
|
||||
prevent(*create_read_update_admin_destroy(:wiki))
|
||||
prevent(:download_wiki_code)
|
||||
|
@ -512,6 +518,7 @@ class ProjectPolicy < BasePolicy
|
|||
enable :download_wiki_code
|
||||
enable :read_cycle_analytics
|
||||
enable :read_pages_content
|
||||
enable :read_analytics
|
||||
|
||||
# NOTE: may be overridden by IssuePolicy
|
||||
enable :read_issue
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
- if navbar_links.any?
|
||||
= nav_link(path: all_paths) do
|
||||
= link_to analytics_link.link, { data: { qa_selector: 'analytics_anchor' } } do
|
||||
= link_to analytics_link.link, {class: 'shortcuts-analytics', data: { qa_selector: 'analytics_anchor' } } do
|
||||
.nav-icon-container
|
||||
= sprite_icon('chart')
|
||||
%span.nav-item-name{ data: { qa_selector: 'analytics_link' } }
|
||||
|
|
|
@ -324,7 +324,8 @@
|
|||
|
||||
= render_if_exists 'layouts/nav/sidebar/project_packages_link'
|
||||
|
||||
= render 'layouts/nav/sidebar/analytics_links', links: project_analytics_navbar_links(@project, current_user)
|
||||
- if project_nav_tab? :analytics
|
||||
= render 'layouts/nav/sidebar/analytics_links', links: project_analytics_navbar_links(@project, current_user)
|
||||
|
||||
- if project_nav_tab?(:confluence)
|
||||
- confluence_url = project_wikis_confluence_path(@project)
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
- if issuable.reviewers.empty?
|
||||
= hidden_field_tag "#{issuable.to_ability_name}[reviewer_ids][]", 0, id: nil, data: { meta: '' }
|
||||
|
||||
= dropdown_tag(users_dropdown_label(issuable.reviewers), options: reviewers_dropdown_options(issuable.to_ability_name))
|
||||
= dropdown_tag(users_dropdown_label(issuable.reviewers), options: reviewers_dropdown_options(issuable.to_ability_name, issuable.iid, issuable.target_branch))
|
||||
- if Feature.enabled?(:mr_collapsed_approval_rules, @project)
|
||||
= render_if_exists 'shared/issuable/approver_suggestion', issuable: issuable, presenter: presenter
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add toggle to remove Analytics left nav item
|
||||
merge_request: 46011
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Removed count_uploads_size_in_storage_stats feature flag
|
||||
merge_request: 49998
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: reviewer_approval_rules
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46738
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/293742
|
||||
milestone: '13.7'
|
||||
type: development
|
||||
group: group::code review
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: security_dast_site_profiles_additional_fields
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46848
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292897
|
||||
milestone: '13.7'
|
||||
type: development
|
||||
group: group::dynamic analysis
|
||||
default_enabled: false
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class AddAnalyticsAccessLevelToProjectFeatures < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
# Set this constant to true if this migration requires downtime.
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
add_column :project_features, :analytics_access_level, :integer, default: 20, null: false
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_column :project_features, :analytics_access_level, :integer
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
d9151c8cafe7a62be9904cb05cc2a6f6e28c2910e69744df1ddd4ad587c83335
|
|
@ -15439,7 +15439,8 @@ CREATE TABLE project_features (
|
|||
forking_access_level integer,
|
||||
metrics_dashboard_access_level integer,
|
||||
requirements_access_level integer DEFAULT 20 NOT NULL,
|
||||
operations_access_level integer DEFAULT 20 NOT NULL
|
||||
operations_access_level integer DEFAULT 20 NOT NULL,
|
||||
analytics_access_level integer DEFAULT 20 NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE project_features_id_seq
|
||||
|
|
|
@ -13,7 +13,7 @@ to deploy features in an early stage of development so that they can be
|
|||
incrementally rolled out.
|
||||
|
||||
Before making them permanently available, features can be deployed behind
|
||||
flags for a [number of reasons](../development/feature_flags/process.md#when-to-use-feature-flags), such as:
|
||||
flags for a [number of reasons](../development/feature_flags/index.md#when-to-use-feature-flags), such as:
|
||||
|
||||
- To test the feature.
|
||||
- To get feedback from users and customers while in an early stage of the development of the feature.
|
||||
|
|
|
@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Getting started with GitLab GraphQL API
|
||||
|
||||
This guide demonstrates basic usage of GitLab's GraphQL API.
|
||||
This guide demonstrates basic usage of the GitLab GraphQL API.
|
||||
|
||||
See the [GraphQL API style guide](../../development/api_graphql_styleguide.md) for implementation details
|
||||
aimed at developers who wish to work on developing the API itself.
|
||||
|
@ -69,7 +69,7 @@ In the GitLab GraphQL API, `id` refers to a
|
|||
[Global ID](https://graphql.org/learn/global-object-identification/),
|
||||
which is an object identifier in the format of `"gid://gitlab/Issue/123"`.
|
||||
|
||||
[GitLab's GraphQL Schema](reference/index.md) outlines which objects and fields are
|
||||
[GitLab GraphQL Schema](reference/index.md) outlines which objects and fields are
|
||||
available for clients to query and their corresponding data types.
|
||||
|
||||
Example: Get only the names of all the projects the currently logged in user can access (up to a limit, more on that later)
|
||||
|
@ -289,7 +289,7 @@ More about introspection:
|
|||
|
||||
## Sorting
|
||||
|
||||
Some of GitLab's GraphQL endpoints allow you to specify how you'd like a collection of
|
||||
Some of the GitLab GraphQL endpoints allow you to specify how you'd like a collection of
|
||||
objects to be sorted. You can only sort by what the schema allows you to.
|
||||
|
||||
Example: Issues can be sorted by creation date:
|
||||
|
@ -314,7 +314,7 @@ Pagination is a way of only asking for a subset of the records (say, the first 1
|
|||
If we want more of them, we can make another request for the next 10 from the server
|
||||
(in the form of something like "please give me the next 10 records").
|
||||
|
||||
By default, GitLab's GraphQL API returns only the first 100 records of any collection.
|
||||
By default, the GitLab GraphQL API returns only the first 100 records of any collection.
|
||||
This can be changed by using `first` or `last` arguments. Both arguments take a value,
|
||||
so `first: 10` returns the first 10 records, and `last: 10` the last 10 records.
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ For those new to the GitLab GraphQL API, see
|
|||
|
||||
### Quick Reference
|
||||
|
||||
- GitLab's GraphQL API endpoint is located at `/api/graphql`.
|
||||
- The GitLab GraphQL API endpoint is located at `/api/graphql`.
|
||||
- Get an [introduction to GraphQL from graphql.org](https://graphql.org/).
|
||||
- GitLab supports a wide range of resources, listed in the [GraphQL API Reference](reference/index.md).
|
||||
|
||||
|
@ -115,9 +115,9 @@ library GitLab uses on the backend.
|
|||
|
||||
## Reference
|
||||
|
||||
GitLab's GraphQL reference [is available](reference/index.md).
|
||||
The GitLab GraphQL reference [is available](reference/index.md).
|
||||
|
||||
It is automatically generated from GitLab's GraphQL schema and embedded in a Markdown file.
|
||||
It is automatically generated from the GitLab GraphQL schema and embedded in a Markdown file.
|
||||
|
||||
Machine-readable versions are also available:
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ in [Removed Items](../removed_items.md).
|
|||
|
||||
## Object types
|
||||
|
||||
Object types represent the resources that GitLab's GraphQL API can return.
|
||||
Object types represent the resources that the GitLab GraphQL API can return.
|
||||
They contain _fields_. Each field has its own type, which will either be one of the
|
||||
basic GraphQL [scalar types](https://graphql.org/learn/schema/#scalar-types)
|
||||
(e.g.: `String` or `Boolean`) or other object types.
|
||||
|
|
|
@ -1045,6 +1045,7 @@ POST /projects
|
|||
| Attribute | Type | Required | Description |
|
||||
|-------------------------------------------------------------|---------|------------------------|-------------|
|
||||
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
|
||||
| `analytics_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
||||
| `approvals_before_merge` **(STARTER)** | integer | **{dotted-circle}** No | How many approvers should approve merge requests by default. |
|
||||
| `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. |
|
||||
| `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`). |
|
||||
|
@ -1118,6 +1119,7 @@ POST /projects/user/:user_id
|
|||
| Attribute | Type | Required | Description |
|
||||
|-------------------------------------------------------------|---------|------------------------|-------------|
|
||||
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
|
||||
| `analytics_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
||||
| `approvals_before_merge` **(STARTER)** | integer | **{dotted-circle}** No | How many approvers should approve merge requests by default. |
|
||||
| `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. |
|
||||
| `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`). |
|
||||
|
@ -1190,6 +1192,7 @@ PUT /projects/:id
|
|||
| Attribute | Type | Required | Description |
|
||||
|-------------------------------------------------------------|----------------|------------------------|-------------|
|
||||
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
|
||||
| `analytics_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
||||
| `approvals_before_merge` **(STARTER)** | integer | **{dotted-circle}** No | How many approvers should approve merge request by default. |
|
||||
| `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. |
|
||||
| `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual`, or `timed_incremental`). |
|
||||
|
@ -2374,6 +2377,7 @@ Example response:
|
|||
"repository_access_level": "enabled",
|
||||
"merge_requests_access_level": "enabled",
|
||||
"forking_access_level": "enabled",
|
||||
"analytics_access_level": "enabled",
|
||||
"wiki_access_level": "enabled",
|
||||
"builds_access_level": "enabled",
|
||||
"snippets_access_level": "enabled",
|
||||
|
|
|
@ -53,7 +53,6 @@ problems, such as outages.
|
|||
|
||||
Please also read the [development guide for feature flags](development.md).
|
||||
|
||||
|
||||
### Including a feature behind feature flag in the final release
|
||||
|
||||
In order to build a final release and present the feature for self-managed
|
||||
|
|
|
@ -88,6 +88,9 @@ in multiple ways:
|
|||
|
||||
## Features
|
||||
|
||||
NOTE:
|
||||
Depending on your target platform, some features might not be available to you.
|
||||
|
||||
Comprised of a set of [stages](stages.md), Auto DevOps brings these best practices to your
|
||||
project in a simple and automatic way:
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@ Read them carefully to understand how each one works.
|
|||
|
||||
## Auto Build
|
||||
|
||||
NOTE:
|
||||
Auto Build is not supported if Docker in Docker is not available for your GitLab Runners, like in OpenShift clusters. GitLab's OpenShift support is tracked [in a dedicated epic](https://gitlab.com/groups/gitlab-org/-/epics/2068).
|
||||
|
||||
Auto Build creates a build of the application using an existing `Dockerfile` or
|
||||
Heroku buildpacks. The resulting Docker image is pushed to the
|
||||
[Container Registry](../../user/packages/container_registry/index.md), and tagged
|
||||
|
|
|
@ -183,7 +183,7 @@ The following variables are used for configuring specific analyzers (used for a
|
|||
| `PIP_REQUIREMENTS_FILE` | `gemnasium-python` | | Pip requirements file to be scanned. |
|
||||
| `DS_PIP_VERSION` | `gemnasium-python` | | Force the install of a specific pip version (example: `"19.3"`), otherwise the pip installed in the Docker image is used. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12811) in GitLab 12.7) |
|
||||
| `DS_PIP_DEPENDENCY_PATH` | `gemnasium-python` | | Path to load Python pip dependencies from. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12412) in GitLab 12.2) |
|
||||
| `DS_PYTHON_VERSION` | `retire.js` | | Version of Python. If set to 2, dependencies are installed using Python 2.7 instead of Python 3.6. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12296) in GitLab 12.1)|
|
||||
| `DS_PYTHON_VERSION` | `retire.js` | | Version of Python. If set to 2, dependencies are installed using Python 2.7 instead of Python 3.6. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12296) in GitLab 12.1, [removed](https://www.python.org/doc/sunset-python-2/) in GitLab 13.7)|
|
||||
| `DS_JAVA_VERSION` | `gemnasium-maven` | `11` | Version of Java. Available versions: `8`, `11`, `13`, `14`. Maven and Gradle use the Java version specified by this value. |
|
||||
| `MAVEN_CLI_OPTS` | `gemnasium-maven` | `"-DskipTests --batch-mode"` | List of command line arguments that are passed to `maven` by the analyzer. See an example for [using private repositories](../index.md#using-private-maven-repos). |
|
||||
| `GRADLE_CLI_OPTS` | `gemnasium-maven` | | List of command line arguments that are passed to `gradle` by the analyzer. |
|
||||
|
@ -520,3 +520,8 @@ uses the [`rules:exists`](../../../ci/yaml/README.md#rulesexists)
|
|||
syntax. This directive is limited to 10000 checks and always returns `true` after reaching this
|
||||
number. Because of this, and depending on the number of files in your repository, a dependency
|
||||
scanning job might be triggered even if the scanner doesn't support your project.
|
||||
|
||||
### Issues building projects with npm or yarn packages relying on Python 2
|
||||
|
||||
Python 2 was removed (see: [Python 2 sunsetting](https://www.python.org/doc/sunset-python-2/)) from the `retire.js` analyzer in GitLab 13.7 (analyzer version 2.10.1). Projects using packages
|
||||
with a dependency on this version of Python should use `retire.js` version 2.10.0 or lower (for example, `registry.gitlab.com/gitlab-org/security-products/analyzers/retire.js:2.10.0`).
|
||||
|
|
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 41 KiB |
|
@ -211,6 +211,88 @@ To remove a member from a group:
|
|||
1. (Optional) Select the **Also unassign this user from related issues and merge requests** checkbox.
|
||||
1. Click **Remove member**.
|
||||
|
||||
## Filter and sort members in a group
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21727) in GitLab 12.6.
|
||||
> - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/228675) in GitLab 13.7.
|
||||
> - Improvements are [deployed behind a feature flag](../feature_flags.md), enabled by default.
|
||||
> - Improvements are enabled on GitLab.com.
|
||||
> - Improvements are recommended for production use.
|
||||
> - For GitLab self-managed instances, GitLab administrators can opt to [disable improvements](#enable-or-disable-improvements-to-the-ability-to-filter-and-sort-group-members). **(CORE ONLY)**
|
||||
|
||||
The following sections illustrate how you can filter and sort members in a group. To view these options,
|
||||
navigate to your desired group, go to **Members**, and include the noted search terms.
|
||||
|
||||
### Membership filter
|
||||
|
||||
By default, inherited and direct members are displayed. The [membership](subgroups/index.md#membership) filter can be used to display only inherited or only direct members.
|
||||
|
||||
#### Only display inherited members
|
||||
|
||||
Include `Membership` `=` `Inherited` in the search text box.
|
||||
|
||||
![Group members filter inherited](img/group_members_filter_inherited_13_7.png)
|
||||
|
||||
#### Only display direct members
|
||||
|
||||
Include `Membership` `=` `Direct` in the search text box.
|
||||
|
||||
![Group members filter direct](img/group_members_filter_direct_13_7.png)
|
||||
|
||||
### 2FA filter
|
||||
|
||||
[Owner](../permissions.md#group-members-permissions) permissions required.
|
||||
|
||||
By default, members with 2FA enabled and disabled are displayed. The 2FA filter can be used to display only members with 2FA enabled or only members with 2FA disabled.
|
||||
|
||||
#### Only display members with 2FA enabled
|
||||
|
||||
Include `2FA` `=` `Enabled` in the search text box.
|
||||
|
||||
![Group members filter 2FA enabled](img/group_members_filter_2fa_enabled_13_7.png)
|
||||
|
||||
#### Only display members with 2FA disabled
|
||||
|
||||
Include `2FA` `=` `Disabled` in the search text box.
|
||||
|
||||
![Group members filter 2FA disabled](img/group_members_filter_2fa_disabled_13_7.png)
|
||||
|
||||
### Search
|
||||
|
||||
You can search for members by name, username, or email.
|
||||
|
||||
![Group members search](img/group_members_search_13_7.png)
|
||||
|
||||
### Sort
|
||||
|
||||
You can sort members by **Account**, **Access granted**, **Max role**, or **Last sign-in** in ascending or descending order.
|
||||
|
||||
![Group members sort](img/group_members_sort_13_7.png)
|
||||
|
||||
### Enable or disable improvements to the ability to filter and sort group members **(CORE ONLY)**
|
||||
|
||||
Group member filtering and sorting improvements are deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
|
||||
can opt to disable the improvements.
|
||||
|
||||
To disable them:
|
||||
|
||||
```ruby
|
||||
# For the instance
|
||||
Feature.disable(:group_members_filtered_search)
|
||||
# For a single group
|
||||
Feature.disable(:group_members_filtered_search, Group.find(<group id>))
|
||||
```
|
||||
|
||||
To enable them:
|
||||
|
||||
```ruby
|
||||
# For the instance
|
||||
Feature.enable(:group_members_filtered_search)
|
||||
# For a single group
|
||||
Feature.enable(:group_members_filtered_search, Group.find(<group id>))
|
||||
```
|
||||
|
||||
## Changing the default branch protection of a group
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7583) in GitLab 12.9.
|
||||
|
|
|
@ -192,6 +192,8 @@ If the information you need isn't listed above you may wish to check our [troubl
|
|||
|
||||
## User access and management
|
||||
|
||||
> [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/268142) in GitLab 13.7.
|
||||
|
||||
Once Group SSO is configured and enabled, users can access the GitLab.com group through the identity provider's dashboard. If [SCIM](scim_setup.md) is configured, please see the [user access and linking setup section on the SCIM page](scim_setup.md#user-access-and-linking-setup).
|
||||
|
||||
When a user tries to sign in with Group SSO, GitLab attempts to find or create a user based on the following:
|
||||
|
|
Before Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 58 KiB |
|
@ -116,9 +116,9 @@ Follow the same process to create any subsequent groups.
|
|||
|
||||
## Membership
|
||||
|
||||
When you add a member to a subgroup, they inherit the membership and permission
|
||||
level from the parent group(s). This model allows access to nested groups if you
|
||||
have membership in one of its parents.
|
||||
When you add a member to a group, that member is also added to all subgroups.
|
||||
Permission level is inherited from the group’s parent. This model allows access to
|
||||
subgroups if you have membership in one of its parents.
|
||||
|
||||
Jobs for pipelines in subgroups can use [runners](../../../ci/runners/README.md) registered to the parent group(s).
|
||||
This means secrets configured for the parent group are available to subgroup jobs.
|
||||
|
@ -131,31 +131,23 @@ the **Members** page of the group the member was added.
|
|||
You can tell if a member has inherited the permissions from a parent group by
|
||||
looking at the group's **Members** page.
|
||||
|
||||
![Group members page](img/group_members.png)
|
||||
![Group members page](img/group_members_13_7.png)
|
||||
|
||||
From the image above, we can deduce the following things:
|
||||
|
||||
- There are 5 members that have access to the group `four`.
|
||||
- User0 is a Reporter and has inherited their permissions from group `one`
|
||||
- User 0 is a Reporter and has inherited their permissions from group `one`
|
||||
which is above the hierarchy of group `four`.
|
||||
- User1 is a Developer and has inherited their permissions from group
|
||||
- User 1 is a Developer and has inherited their permissions from group
|
||||
`one/two` which is above the hierarchy of group `four`.
|
||||
- User2 is a Developer and has inherited their permissions from group
|
||||
- User 2 is a Developer and has inherited their permissions from group
|
||||
`one/two/three` which is above the hierarchy of group `four`.
|
||||
- For User3 there is no indication of a parent group, therefore they belong to
|
||||
- For User 3 the **Source** column indicates **Direct member**, therefore they belong to
|
||||
group `four`, the one we're inspecting.
|
||||
- Administrator is the Owner and member of **all** subgroups and for that reason,
|
||||
as with User3, there is no indication of an ancestor group.
|
||||
as with User 3, the **Source** column indicates **Direct member**.
|
||||
|
||||
[From](https://gitlab.com/gitlab-org/gitlab/-/issues/21727) GitLab 12.6, you can filter
|
||||
this list using dropdown on the right side:
|
||||
|
||||
![Group members filter](img/group_members_filter_v12_6.png)
|
||||
|
||||
- **Show only direct members** displays only Administrator and User3, since these are
|
||||
the only users that belong to group `four`, which is the one we're inspecting.
|
||||
- **Show only inherited members** displays User0, User1 and User2, no matter which group
|
||||
above the hierarchy is the source of inherited permissions.
|
||||
Members can be [filtered by inherited or direct membership](../index.md#membership-filter).
|
||||
|
||||
### Overriding the ancestor group membership
|
||||
|
||||
|
@ -169,9 +161,9 @@ Therefore, you cannot reduce a user's permissions in a subgroup with respect to
|
|||
To override a user's membership of an ancestor group (the first group they were
|
||||
added to), add the user to the new subgroup again with a higher set of permissions.
|
||||
|
||||
For example, if User0 was first added to group `group-1/group-1-1` with Developer
|
||||
For example, if User 1 was first added to group `one/two` with Developer
|
||||
permissions, then they inherit those permissions in every other subgroup
|
||||
of `group-1/group-1-1`. To give them Maintainer access to `group-1/group-1-1/group1-1-1`,
|
||||
of `one/two`. To give them Maintainer access to group `one/two/three/four`,
|
||||
you would add them again in that group as Maintainer. Removing them from that group,
|
||||
the permissions fall back to those of the ancestor group.
|
||||
|
||||
|
|
|
@ -289,6 +289,7 @@ group.
|
|||
| View Value Stream analytics | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| View Billing **(FREE ONLY)** | | | | | ✓ (4) |
|
||||
| View Usage Quotas **(FREE ONLY)** | | | | | ✓ (4) |
|
||||
| Filter members by 2FA status | | | | | ✓ |
|
||||
|
||||
1. Groups can be set to [allow either Owners or Owners and
|
||||
Maintainers to create subgroups](group/subgroups/index.md#creating-a-subgroup)
|
||||
|
|
|
@ -35,7 +35,7 @@ you can also view all the issues collectively at the group level.
|
|||
See also [Always start a discussion with an issue](https://about.gitlab.com/blog/2016/03/03/start-with-an-issue/).
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
To learn how the GitLab Strategic Marketing department uses GitLab issues with [labels](../labels.md) and
|
||||
To learn how our Strategic Marketing department uses GitLab issues with [labels](../labels.md) and
|
||||
[issue boards](../issue_board.md), see the video on
|
||||
[Managing Commitments with Issues](https://www.youtube.com/watch?v=cuIHNintg1o&t=3).
|
||||
|
||||
|
|
|
@ -68,6 +68,7 @@ Use the switches to enable or disable the following features:
|
|||
| **Container Registry** | | Activates a [registry](../../packages/container_registry/) for your Docker images |
|
||||
| **Git Large File Storage** | | Enables the use of [large files](../../../topics/git/lfs/index.md#git-large-file-storage-lfs) |
|
||||
| **Packages** | | Supports configuration of a [package registry](../../../administration/packages/index.md#gitlab-package-registry-administration) functionality |
|
||||
| **Analytics** | ✓ | Enables [analytics](../../analytics/) |
|
||||
| **Wiki** | ✓ | Enables a separate system for [documentation](../wiki/) |
|
||||
| **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md) |
|
||||
| **Pages** | ✓ | Allows you to [publish static websites](../pages/) |
|
||||
|
|
|
@ -68,6 +68,7 @@ module API
|
|||
expose(:snippets_access_level) { |project, options| project.project_feature.string_access_level(:snippets) }
|
||||
expose(:pages_access_level) { |project, options| project.project_feature.string_access_level(:pages) }
|
||||
expose(:operations_access_level) { |project, options| project.project_feature.string_access_level(:operations) }
|
||||
expose(:analytics_access_level) { |project, options| project.project_feature.string_access_level(:analytics) }
|
||||
|
||||
expose :emails_disabled
|
||||
expose :shared_runners_enabled
|
||||
|
|
|
@ -33,6 +33,7 @@ module API
|
|||
optional :snippets_access_level, type: String, values: %w(disabled private enabled), desc: 'Snippets access level. One of `disabled`, `private` or `enabled`'
|
||||
optional :pages_access_level, type: String, values: %w(disabled private enabled public), desc: 'Pages access level. One of `disabled`, `private`, `enabled` or `public`'
|
||||
optional :operations_access_level, type: String, values: %w(disabled private enabled), desc: 'Operations access level. One of `disabled`, `private` or `enabled`'
|
||||
optional :analytics_access_level, type: String, values: %w(disabled private enabled), desc: 'Analytics access level. One of `disabled`, `private` or `enabled`'
|
||||
|
||||
optional :emails_disabled, type: Boolean, desc: 'Disable email notifications'
|
||||
optional :show_default_award_emojis, type: Boolean, desc: 'Show default award emojis'
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
:plain
|
||||
## Object types
|
||||
|
||||
Object types represent the resources that GitLab's GraphQL API can return.
|
||||
Object types represent the resources that the GitLab GraphQL API can return.
|
||||
They contain _fields_. Each field has its own type, which will either be one of the
|
||||
basic GraphQL [scalar types](https://graphql.org/learn/schema/#scalar-types)
|
||||
(e.g.: `String` or `Boolean`) or other object types.
|
||||
|
|
|
@ -962,6 +962,9 @@ msgstr ""
|
|||
msgid "(%{value}) has already been taken"
|
||||
msgstr ""
|
||||
|
||||
msgid "(+%{count} rules)"
|
||||
msgstr ""
|
||||
|
||||
msgid "(No changes)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -8680,6 +8683,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Error Details"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Excluded URLs"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Hide debug messages"
|
||||
msgstr ""
|
||||
|
||||
|
@ -8728,6 +8734,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Profile name"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Request headers"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Run the AJAX spider, in addition to the traditional spider, to crawl the target site."
|
||||
msgstr ""
|
||||
|
||||
|
@ -21754,6 +21763,9 @@ msgstr ""
|
|||
msgid "ProjectSettings|Allow users to request access"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|Analytics"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|Automatically resolve merge request diff discussions when they become outdated"
|
||||
msgstr ""
|
||||
|
||||
|
@ -21997,6 +22009,9 @@ msgstr ""
|
|||
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|View project analytics"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :snippet_repository_storage_move, class: 'SnippetRepositoryStorageMove' do
|
||||
container { association(:snippet) }
|
||||
|
||||
source_storage_name { 'default' }
|
||||
|
||||
trait :scheduled do
|
||||
state { SnippetRepositoryStorageMove.state_machines[:state].states[:scheduled].value }
|
||||
end
|
||||
|
||||
trait :started do
|
||||
state { SnippetRepositoryStorageMove.state_machines[:state].states[:started].value }
|
||||
end
|
||||
|
||||
trait :replicated do
|
||||
state { SnippetRepositoryStorageMove.state_machines[:state].states[:replicated].value }
|
||||
end
|
||||
|
||||
trait :finished do
|
||||
state { SnippetRepositoryStorageMove.state_machines[:state].states[:finished].value }
|
||||
end
|
||||
|
||||
trait :failed do
|
||||
state { SnippetRepositoryStorageMove.state_machines[:state].states[:failed].value }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,7 +14,7 @@ RSpec.describe 'Edit Project Settings' do
|
|||
sign_in(member)
|
||||
end
|
||||
|
||||
tools = { builds: "pipelines", issues: "issues", wiki: "wiki", snippets: "snippets", merge_requests: "merge_requests" }
|
||||
tools = { builds: "pipelines", issues: "issues", wiki: "wiki", snippets: "snippets", merge_requests: "merge_requests", analytics: "analytics" }
|
||||
|
||||
tools.each do |tool_name, shortcut_name|
|
||||
describe "feature #{tool_name}" do
|
||||
|
|
|
@ -21,6 +21,7 @@ const defaultProps = {
|
|||
wikiAccessLevel: 20,
|
||||
snippetsAccessLevel: 20,
|
||||
pagesAccessLevel: 10,
|
||||
analyticsAccessLevel: 20,
|
||||
containerRegistryEnabled: true,
|
||||
lfsEnabled: true,
|
||||
emailsDisabled: false,
|
||||
|
@ -79,6 +80,8 @@ describe('Settings Panel', () => {
|
|||
const findRepositoryFeatureSetting = () =>
|
||||
findRepositoryFeatureProjectRow().find(projectFeatureSetting);
|
||||
|
||||
const findAnalyticsRow = () => wrapper.find({ ref: 'analytics-settings' });
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mountComponent();
|
||||
});
|
||||
|
@ -557,4 +560,12 @@ describe('Settings Panel', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Analytics', () => {
|
||||
it('should show the analytics toggle', async () => {
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(findAnalyticsRow().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -107,6 +107,7 @@ snippets:
|
|||
- user_mentions
|
||||
- snippet_repository
|
||||
- statistics
|
||||
- repository_storage_moves
|
||||
releases:
|
||||
- author
|
||||
- project
|
||||
|
|
|
@ -578,6 +578,7 @@ ProjectFeature:
|
|||
- pages_access_level
|
||||
- metrics_dashboard_access_level
|
||||
- requirements_access_level
|
||||
- analytics_access_level
|
||||
- operations_access_level
|
||||
- created_at
|
||||
- updated_at
|
||||
|
|
|
@ -3,11 +3,33 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ProjectRepositoryStorageMove, type: :model do
|
||||
it_behaves_like 'handles repository moves' do
|
||||
let_it_be_with_refind(:container) { create(:project) }
|
||||
let_it_be_with_refind(:project) { create(:project) }
|
||||
|
||||
it_behaves_like 'handles repository moves' do
|
||||
let(:container) { project }
|
||||
let(:repository_storage_factory_key) { :project_repository_storage_move }
|
||||
let(:error_key) { :project }
|
||||
let(:repository_storage_worker) { ProjectUpdateRepositoryStorageWorker }
|
||||
end
|
||||
|
||||
describe 'state transitions' do
|
||||
let(:storage) { 'test_second_storage' }
|
||||
|
||||
before do
|
||||
stub_storage_settings(storage => { 'path' => 'tmp/tests/extra_storage' })
|
||||
end
|
||||
|
||||
context 'when started' do
|
||||
subject(:storage_move) { create(:project_repository_storage_move, :started, container: project, destination_storage_name: storage) }
|
||||
|
||||
context 'and transits to replicated' do
|
||||
it 'sets the repository storage and marks the container as writable' do
|
||||
storage_move.finish_replication!
|
||||
|
||||
expect(project.repository_storage).to eq(storage)
|
||||
expect(project).not_to be_repository_read_only
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -313,17 +313,6 @@ RSpec.describe ProjectStatistics do
|
|||
it 'stores the size of related uploaded files' do
|
||||
expect(statistics.update_uploads_size).to eq(3.megabytes)
|
||||
end
|
||||
|
||||
context 'with feature flag disabled' do
|
||||
before do
|
||||
statistics.update_columns(uploads_size: 0)
|
||||
stub_feature_flags(count_uploads_size_in_storage_stats: false)
|
||||
end
|
||||
|
||||
it 'does not store the size of related uploaded files' do
|
||||
expect(statistics.update_uploads_size).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update_storage_size' do
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe SnippetRepositoryStorageMove, type: :model do
|
||||
it_behaves_like 'handles repository moves' do
|
||||
let_it_be_with_refind(:container) { create(:snippet) }
|
||||
|
||||
let(:repository_storage_factory_key) { :snippet_repository_storage_move }
|
||||
let(:error_key) { :snippet }
|
||||
let(:repository_storage_worker) { nil } # TODO set to SnippetUpdateRepositoryStorageWorker after https://gitlab.com/gitlab-org/gitlab/-/issues/218991 is implemented
|
||||
end
|
||||
end
|
|
@ -21,6 +21,7 @@ RSpec.describe Snippet do
|
|||
it { is_expected.to have_many(:user_mentions).class_name("SnippetUserMention") }
|
||||
it { is_expected.to have_one(:snippet_repository) }
|
||||
it { is_expected.to have_one(:statistics).class_name('SnippetStatistics').dependent(:destroy) }
|
||||
it { is_expected.to have_many(:repository_storage_moves).class_name('SnippetRepositoryStorageMove').inverse_of(:container) }
|
||||
end
|
||||
|
||||
describe 'validation' do
|
||||
|
|
|
@ -977,6 +977,34 @@ RSpec.describe ProjectPolicy do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'read_analytics' do
|
||||
context 'anonymous user' do
|
||||
let(:current_user) { anonymous }
|
||||
|
||||
it { is_expected.to be_allowed(:read_analytics) }
|
||||
end
|
||||
|
||||
context 'project member' do
|
||||
let(:project) { private_project }
|
||||
|
||||
%w(guest reporter developer maintainer).each do |role|
|
||||
context role do
|
||||
let(:current_user) { send(role) }
|
||||
|
||||
it { is_expected.to be_allowed(:read_analytics) }
|
||||
|
||||
context "without access to Analytics" do
|
||||
before do
|
||||
project.project_feature.update!(analytics_access_level: ProjectFeature::DISABLED)
|
||||
end
|
||||
|
||||
it { is_expected.to be_disallowed(:read_analytics) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'Self-managed Core resource access tokens'
|
||||
|
||||
describe 'operations feature' do
|
||||
|
|
|
@ -873,6 +873,7 @@ RSpec.describe API::Projects do
|
|||
jobs_enabled: false,
|
||||
merge_requests_enabled: false,
|
||||
forking_access_level: 'disabled',
|
||||
analytics_access_level: 'disabled',
|
||||
wiki_enabled: false,
|
||||
resolve_outdated_diff_discussions: false,
|
||||
remove_source_branch_after_merge: true,
|
||||
|
@ -903,6 +904,7 @@ RSpec.describe API::Projects do
|
|||
expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::DISABLED)
|
||||
expect(project.project_feature.wiki_access_level).to eq(ProjectFeature::DISABLED)
|
||||
expect(project.operations_access_level).to eq(ProjectFeature::DISABLED)
|
||||
expect(project.project_feature.analytics_access_level).to eq(ProjectFeature::DISABLED)
|
||||
end
|
||||
|
||||
it 'creates a project using a template' do
|
||||
|
@ -1623,6 +1625,7 @@ RSpec.describe API::Projects do
|
|||
expect(json_response['issues_access_level']).to be_present
|
||||
expect(json_response['merge_requests_access_level']).to be_present
|
||||
expect(json_response['forking_access_level']).to be_present
|
||||
expect(json_response['analytics_access_level']).to be_present
|
||||
expect(json_response['wiki_access_level']).to be_present
|
||||
expect(json_response['builds_access_level']).to be_present
|
||||
expect(json_response['operations_access_level']).to be_present
|
||||
|
|
|
@ -63,6 +63,7 @@ RSpec.shared_examples 'handles repository moves' do
|
|||
|
||||
context 'and transits to scheduled' do
|
||||
it 'triggers the corresponding repository storage worker' do
|
||||
skip unless repository_storage_worker # TODO remove after https://gitlab.com/gitlab-org/gitlab/-/issues/218991 is implemented
|
||||
expect(repository_storage_worker).to receive(:perform_async).with(container.id, 'test_second_storage', storage_move.id)
|
||||
|
||||
storage_move.schedule!
|
||||
|
@ -72,6 +73,7 @@ RSpec.shared_examples 'handles repository moves' do
|
|||
|
||||
context 'when the transition fails' do
|
||||
it 'does not trigger ProjectUpdateRepositoryStorageWorker and adds an error' do
|
||||
skip unless repository_storage_worker # TODO remove after https://gitlab.com/gitlab-org/gitlab/-/issues/218991 is implemented
|
||||
allow(storage_move.container).to receive(:set_repository_read_only!).and_raise(StandardError, 'foobar')
|
||||
expect(repository_storage_worker).not_to receive(:perform_async)
|
||||
|
||||
|
@ -94,10 +96,9 @@ RSpec.shared_examples 'handles repository moves' do
|
|||
subject(:storage_move) { create(repository_storage_factory_key, :started, container: container, destination_storage_name: 'test_second_storage') }
|
||||
|
||||
context 'and transits to replicated' do
|
||||
it 'sets the repository storage and marks the container as writable' do
|
||||
it 'marks the container as writable' do
|
||||
storage_move.finish_replication!
|
||||
|
||||
expect(container.repository_storage).to eq('test_second_storage')
|
||||
expect(container).not_to be_repository_read_only
|
||||
end
|
||||
end
|
||||
|
|