Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-07-22 21:09:40 +00:00
parent d81dc2a54e
commit 014b832720
66 changed files with 767 additions and 202 deletions

View file

@ -55,41 +55,6 @@ Graphql/OldTypes:
- 'app/graphql/mutations/design_management/base.rb' - 'app/graphql/mutations/design_management/base.rb'
- 'app/graphql/mutations/discussions/toggle_resolve.rb' - 'app/graphql/mutations/discussions/toggle_resolve.rb'
- 'app/graphql/mutations/environments/canary_ingress/update.rb' - 'app/graphql/mutations/environments/canary_ingress/update.rb'
- 'app/graphql/mutations/issues/base.rb'
- 'app/graphql/mutations/issues/common_mutation_arguments.rb'
- 'app/graphql/mutations/issues/create.rb'
- 'app/graphql/mutations/issues/move.rb'
- 'app/graphql/mutations/issues/set_confidential.rb'
- 'app/graphql/mutations/issues/set_locked.rb'
- 'app/graphql/mutations/issues/set_subscription.rb'
- 'app/graphql/mutations/issues/update.rb'
- 'app/graphql/mutations/jira_import/import_users.rb'
- 'app/graphql/mutations/jira_import/start.rb'
- 'app/graphql/mutations/labels/create.rb'
- 'app/graphql/mutations/merge_requests/base.rb'
- 'app/graphql/mutations/merge_requests/create.rb'
- 'app/graphql/mutations/merge_requests/set_draft.rb'
- 'app/graphql/mutations/merge_requests/set_locked.rb'
- 'app/graphql/mutations/merge_requests/set_subscription.rb'
- 'app/graphql/mutations/merge_requests/set_wip.rb'
- 'app/graphql/mutations/merge_requests/update.rb'
- 'app/graphql/mutations/metrics/dashboard/annotations/create.rb'
- 'app/graphql/mutations/namespace/package_settings/update.rb'
- 'app/graphql/mutations/notes/create/base.rb'
- 'app/graphql/mutations/notes/update/image_diff_note.rb'
- 'app/graphql/mutations/notes/update/note.rb'
- 'app/graphql/mutations/release_asset_links/create.rb'
- 'app/graphql/mutations/release_asset_links/update.rb'
- 'app/graphql/mutations/releases/base.rb'
- 'app/graphql/mutations/releases/create.rb'
- 'app/graphql/mutations/releases/delete.rb'
- 'app/graphql/mutations/releases/update.rb'
- 'app/graphql/mutations/security/ci_configuration/base_security_analyzer.rb'
- 'app/graphql/mutations/security/ci_configuration/configure_sast.rb'
- 'app/graphql/mutations/security/ci_configuration/configure_secret_detection.rb'
- 'app/graphql/mutations/snippets/create.rb'
- 'app/graphql/mutations/snippets/update.rb'
- 'app/graphql/mutations/user_callouts/create.rb'
- 'app/graphql/types/access_level_type.rb' - 'app/graphql/types/access_level_type.rb'
- 'app/graphql/types/admin/analytics/usage_trends/measurement_type.rb' - 'app/graphql/types/admin/analytics/usage_trends/measurement_type.rb'
- 'app/graphql/types/admin/sidekiq_queues/delete_jobs_response_type.rb' - 'app/graphql/types/admin/sidekiq_queues/delete_jobs_response_type.rb'

View file

@ -55,7 +55,7 @@ export default {
label: s__('Pipeline|Stages'), label: s__('Pipeline|Stages'),
thClass: DEFAULT_TH_CLASSES, thClass: DEFAULT_TH_CLASSES,
tdClass: DEFAULT_TD_CLASS, tdClass: DEFAULT_TD_CLASS,
columnClass: 'gl-w-15p', columnClass: 'gl-w-quarter',
thAttr: { 'data-testid': 'stages-th' }, thAttr: { 'data-testid': 'stages-th' },
}, },
{ {
@ -70,12 +70,14 @@ export default {
key: 'actions', key: 'actions',
thClass: DEFAULT_TH_CLASSES, thClass: DEFAULT_TH_CLASSES,
tdClass: DEFAULT_TD_CLASS, tdClass: DEFAULT_TD_CLASS,
columnClass: 'gl-w-20p', columnClass: 'gl-w-15p',
thAttr: { 'data-testid': 'actions-th' }, thAttr: { 'data-testid': 'actions-th' },
}, },
], ],
components: { components: {
GlTable, GlTable,
LinkedPipelinesMiniList: () =>
import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'),
PipelinesCommit, PipelinesCommit,
PipelineMiniGraph, PipelineMiniGraph,
PipelineOperations, PipelineOperations,
@ -182,12 +184,23 @@ export default {
<div class="stage-cell"> <div class="stage-cell">
<!-- This empty div should be removed, see https://gitlab.com/gitlab-org/gitlab/-/issues/323488 --> <!-- This empty div should be removed, see https://gitlab.com/gitlab-org/gitlab/-/issues/323488 -->
<div></div> <div></div>
<linked-pipelines-mini-list
v-if="item.triggered_by"
:triggered-by="[item.triggered_by]"
data-testid="mini-graph-upstream"
/>
<pipeline-mini-graph <pipeline-mini-graph
v-if="item.details && item.details.stages && item.details.stages.length > 0" v-if="item.details && item.details.stages && item.details.stages.length > 0"
class="gl-display-inline"
:stages="item.details.stages" :stages="item.details.stages"
:update-dropdown="updateGraphDropdown" :update-dropdown="updateGraphDropdown"
@pipelineActionRequestComplete="onPipelineActionRequestComplete" @pipelineActionRequestComplete="onPipelineActionRequestComplete"
/> />
<linked-pipelines-mini-list
v-if="item.triggered.length"
:triggered="item.triggered"
data-testid="mini-graph-downstream"
/>
</div> </div>
</template> </template>

View file

@ -154,16 +154,9 @@ class MergeRequestsFinder < IssuableFinder
# WIP is deprecated in favor of Draft. Currently both options are supported # WIP is deprecated in favor of Draft. Currently both options are supported
def wip_match(table) def wip_match(table)
items = table[:title].matches('WIP:%')
table[:title].matches('WIP:%') .or(table[:title].matches('WIP %'))
.or(table[:title].matches('WIP %')) .or(table[:title].matches('[WIP]%'))
.or(table[:title].matches('[WIP]%'))
# Let's keep this FF around until https://gitlab.com/gitlab-org/gitlab/-/issues/232999
# is implemented
return items unless Feature.enabled?(:merge_request_draft_filter, default_enabled: true)
items
.or(table[:title].matches('Draft - %')) .or(table[:title].matches('Draft - %'))
.or(table[:title].matches('Draft:%')) .or(table[:title].matches('Draft:%'))
.or(table[:title].matches('[Draft]%')) .or(table[:title].matches('[Draft]%'))

View file

@ -5,11 +5,11 @@ module Mutations
class Base < BaseMutation class Base < BaseMutation
include Mutations::ResolvesIssuable include Mutations::ResolvesIssuable
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: true, required: true,
description: "The project the issue to mutate is in." description: "The project the issue to mutate is in."
argument :iid, GraphQL::STRING_TYPE, argument :iid, GraphQL::Types::String,
required: true, required: true,
description: "The IID of the issue to mutate." description: "The IID of the issue to mutate."

View file

@ -6,7 +6,7 @@ module Mutations
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
argument :description, GraphQL::STRING_TYPE, argument :description, GraphQL::Types::String,
required: false, required: false,
description: copy_field_description(Types::IssueType, :description) description: copy_field_description(Types::IssueType, :description)
@ -14,11 +14,11 @@ module Mutations
required: false, required: false,
description: copy_field_description(Types::IssueType, :due_date) description: copy_field_description(Types::IssueType, :due_date)
argument :confidential, GraphQL::BOOLEAN_TYPE, argument :confidential, GraphQL::Types::Boolean,
required: false, required: false,
description: copy_field_description(Types::IssueType, :confidential) description: copy_field_description(Types::IssueType, :confidential)
argument :locked, GraphQL::BOOLEAN_TYPE, argument :locked, GraphQL::Types::Boolean,
as: :discussion_locked, as: :discussion_locked,
required: false, required: false,
description: copy_field_description(Types::IssueType, :discussion_locked) description: copy_field_description(Types::IssueType, :discussion_locked)

View file

@ -10,15 +10,15 @@ module Mutations
include CommonMutationArguments include CommonMutationArguments
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: true, required: true,
description: 'Project full path the issue is associated with.' description: 'Project full path the issue is associated with.'
argument :iid, GraphQL::INT_TYPE, argument :iid, GraphQL::Types::Int,
required: false, required: false,
description: 'The IID (internal ID) of a project issue. Only admins and project owners can modify.' description: 'The IID (internal ID) of a project issue. Only admins and project owners can modify.'
argument :title, GraphQL::STRING_TYPE, argument :title, GraphQL::Types::String,
required: true, required: true,
description: copy_field_description(Types::IssueType, :title) description: copy_field_description(Types::IssueType, :title)
@ -26,7 +26,7 @@ module Mutations
required: false, required: false,
description: 'The ID of the milestone to assign to the issue. On update milestone will be removed if set to null.' description: 'The ID of the milestone to assign to the issue. On update milestone will be removed if set to null.'
argument :labels, [GraphQL::STRING_TYPE], argument :labels, [GraphQL::Types::String],
required: false, required: false,
description: copy_field_description(Types::IssueType, :labels) description: copy_field_description(Types::IssueType, :labels)
@ -42,7 +42,7 @@ module Mutations
required: false, required: false,
description: 'The IID of a merge request for which to resolve discussions.' description: 'The IID of a merge request for which to resolve discussions.'
argument :discussion_to_resolve, GraphQL::STRING_TYPE, argument :discussion_to_resolve, GraphQL::Types::String,
required: false, required: false,
description: 'The ID of a discussion to resolve. Also pass `merge_request_to_resolve_discussions_of`.' description: 'The ID of a discussion to resolve. Also pass `merge_request_to_resolve_discussions_of`.'

View file

@ -6,7 +6,7 @@ module Mutations
graphql_name 'IssueMove' graphql_name 'IssueMove'
argument :target_project_path, argument :target_project_path,
GraphQL::ID_TYPE, GraphQL::Types::ID,
required: true, required: true,
description: 'The project to move the issue to.' description: 'The project to move the issue to.'

View file

@ -8,7 +8,7 @@ module Mutations
graphql_name 'IssueSetConfidential' graphql_name 'IssueSetConfidential'
argument :confidential, argument :confidential,
GraphQL::BOOLEAN_TYPE, GraphQL::Types::Boolean,
required: true, required: true,
description: 'Whether or not to set the issue as a confidential.' description: 'Whether or not to set the issue as a confidential.'

View file

@ -6,7 +6,7 @@ module Mutations
graphql_name 'IssueSetLocked' graphql_name 'IssueSetLocked'
argument :locked, argument :locked,
GraphQL::BOOLEAN_TYPE, GraphQL::Types::Boolean,
required: true, required: true,
description: 'Whether or not to lock discussion on the issue.' description: 'Whether or not to lock discussion on the issue.'

View file

@ -8,11 +8,11 @@ module Mutations
include ResolvesSubscription include ResolvesSubscription
include Mutations::ResolvesIssuable include Mutations::ResolvesIssuable
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: true, required: true,
description: "The project the issue to mutate is in." description: "The project the issue to mutate is in."
argument :iid, GraphQL::STRING_TYPE, argument :iid, GraphQL::Types::String,
required: true, required: true,
description: "The IID of the issue to mutate." description: "The IID of the issue to mutate."

View file

@ -7,19 +7,19 @@ module Mutations
include CommonMutationArguments include CommonMutationArguments
argument :title, GraphQL::STRING_TYPE, argument :title, GraphQL::Types::String,
required: false, required: false,
description: copy_field_description(Types::IssueType, :title) description: copy_field_description(Types::IssueType, :title)
argument :milestone_id, GraphQL::ID_TYPE, # rubocop: disable Graphql/IDType argument :milestone_id, GraphQL::Types::ID, # rubocop: disable Graphql/IDType
required: false, required: false,
description: 'The ID of the milestone to assign to the issue. On update milestone will be removed if set to null.' description: 'The ID of the milestone to assign to the issue. On update milestone will be removed if set to null.'
argument :add_label_ids, [GraphQL::ID_TYPE], argument :add_label_ids, [GraphQL::Types::ID],
required: false, required: false,
description: 'The IDs of labels to be added to the issue.' description: 'The IDs of labels to be added to the issue.'
argument :remove_label_ids, [GraphQL::ID_TYPE], argument :remove_label_ids, [GraphQL::Types::ID],
required: false, required: false,
description: 'The IDs of labels to be removed from the issue.' description: 'The IDs of labels to be removed from the issue.'

View file

@ -14,10 +14,10 @@ module Mutations
null: true, null: true,
description: 'Users returned from Jira, matched by email and name if possible.' description: 'Users returned from Jira, matched by email and name if possible.'
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: true, required: true,
description: 'The project to import the Jira users into.' description: 'The project to import the Jira users into.'
argument :start_at, GraphQL::INT_TYPE, argument :start_at, GraphQL::Types::Int,
required: false, required: false,
description: 'The index of the record the import should started at, default 0 (50 records returned).' description: 'The index of the record the import should started at, default 0 (50 records returned).'

View file

@ -14,13 +14,13 @@ module Mutations
null: true, null: true,
description: 'The Jira import data after mutation.' description: 'The Jira import data after mutation.'
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: true, required: true,
description: 'The project to import the Jira project into.' description: 'The project to import the Jira project into.'
argument :jira_project_key, GraphQL::STRING_TYPE, argument :jira_project_key, GraphQL::Types::String,
required: true, required: true,
description: 'Project key of the importer Jira project.' description: 'Project key of the importer Jira project.'
argument :jira_project_name, GraphQL::STRING_TYPE, argument :jira_project_name, GraphQL::Types::String,
required: false, required: false,
description: 'Project name of the importer Jira project.' description: 'Project name of the importer Jira project.'
argument :users_mapping, argument :users_mapping,

View file

@ -12,15 +12,15 @@ module Mutations
null: true, null: true,
description: 'The label after mutation.' description: 'The label after mutation.'
argument :title, GraphQL::STRING_TYPE, argument :title, GraphQL::Types::String,
required: true, required: true,
description: 'Title of the label.' description: 'Title of the label.'
argument :description, GraphQL::STRING_TYPE, argument :description, GraphQL::Types::String,
required: false, required: false,
description: 'Description of the label.' description: 'Description of the label.'
argument :color, GraphQL::STRING_TYPE, argument :color, GraphQL::Types::String,
required: false, required: false,
default_value: Label::DEFAULT_COLOR, default_value: Label::DEFAULT_COLOR,
see: { see: {

View file

@ -23,20 +23,20 @@ module Mutations
as: :auto_merge_strategy, as: :auto_merge_strategy,
description: 'How to merge this merge request.' description: 'How to merge this merge request.'
argument :commit_message, ::GraphQL::STRING_TYPE, argument :commit_message, ::GraphQL::Types::String,
required: false, required: false,
description: 'Custom merge commit message.' description: 'Custom merge commit message.'
argument :squash_commit_message, ::GraphQL::STRING_TYPE, argument :squash_commit_message, ::GraphQL::Types::String,
required: false, required: false,
description: 'Custom squash commit message (if squash is true).' description: 'Custom squash commit message (if squash is true).'
argument :sha, ::GraphQL::STRING_TYPE, argument :sha, ::GraphQL::Types::String,
required: true, required: true,
description: 'The HEAD SHA at the time when this merge was requested.' description: 'The HEAD SHA at the time when this merge was requested.'
argument :should_remove_source_branch, ::GraphQL::BOOLEAN_TYPE, argument :should_remove_source_branch, ::GraphQL::Types::Boolean,
required: false, required: false,
description: 'Should the source branch be removed.' description: 'Should the source branch be removed.'
argument :squash, ::GraphQL::BOOLEAN_TYPE, argument :squash, ::GraphQL::Types::Boolean,
required: false, required: false,
default_value: false, default_value: false,
description: 'Squash commits on the source branch before merge.' description: 'Squash commits on the source branch before merge.'

View file

@ -5,11 +5,11 @@ module Mutations
class Base < BaseMutation class Base < BaseMutation
include Mutations::ResolvesIssuable include Mutations::ResolvesIssuable
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: true, required: true,
description: "The project the merge request to mutate is in." description: "The project the merge request to mutate is in."
argument :iid, GraphQL::STRING_TYPE, argument :iid, GraphQL::Types::String,
required: true, required: true,
description: "The IID of the merge request to mutate." description: "The IID of the merge request to mutate."

View file

@ -7,27 +7,27 @@ module Mutations
graphql_name 'MergeRequestCreate' graphql_name 'MergeRequestCreate'
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: true, required: true,
description: 'Project full path the merge request is associated with.' description: 'Project full path the merge request is associated with.'
argument :title, GraphQL::STRING_TYPE, argument :title, GraphQL::Types::String,
required: true, required: true,
description: copy_field_description(Types::MergeRequestType, :title) description: copy_field_description(Types::MergeRequestType, :title)
argument :source_branch, GraphQL::STRING_TYPE, argument :source_branch, GraphQL::Types::String,
required: true, required: true,
description: copy_field_description(Types::MergeRequestType, :source_branch) description: copy_field_description(Types::MergeRequestType, :source_branch)
argument :target_branch, GraphQL::STRING_TYPE, argument :target_branch, GraphQL::Types::String,
required: true, required: true,
description: copy_field_description(Types::MergeRequestType, :target_branch) description: copy_field_description(Types::MergeRequestType, :target_branch)
argument :description, GraphQL::STRING_TYPE, argument :description, GraphQL::Types::String,
required: false, required: false,
description: copy_field_description(Types::MergeRequestType, :description) description: copy_field_description(Types::MergeRequestType, :description)
argument :labels, [GraphQL::STRING_TYPE], argument :labels, [GraphQL::Types::String],
required: false, required: false,
description: copy_field_description(Types::MergeRequestType, :labels) description: copy_field_description(Types::MergeRequestType, :labels)

View file

@ -6,7 +6,7 @@ module Mutations
graphql_name 'MergeRequestSetDraft' graphql_name 'MergeRequestSetDraft'
argument :draft, argument :draft,
GraphQL::BOOLEAN_TYPE, GraphQL::Types::Boolean,
required: true, required: true,
description: <<~DESC description: <<~DESC
Whether or not to set the merge request as a draft. Whether or not to set the merge request as a draft.

View file

@ -6,7 +6,7 @@ module Mutations
graphql_name 'MergeRequestSetLocked' graphql_name 'MergeRequestSetLocked'
argument :locked, argument :locked,
GraphQL::BOOLEAN_TYPE, GraphQL::Types::Boolean,
required: true, required: true,
description: <<~DESC description: <<~DESC
Whether or not to lock the merge request. Whether or not to lock the merge request.

View file

@ -8,11 +8,11 @@ module Mutations
include ResolvesSubscription include ResolvesSubscription
include Mutations::ResolvesIssuable include Mutations::ResolvesIssuable
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: true, required: true,
description: "The project the merge request to mutate is in." description: "The project the merge request to mutate is in."
argument :iid, GraphQL::STRING_TYPE, argument :iid, GraphQL::Types::String,
required: true, required: true,
description: "The IID of the merge request to mutate." description: "The IID of the merge request to mutate."

View file

@ -6,7 +6,7 @@ module Mutations
graphql_name 'MergeRequestSetWip' graphql_name 'MergeRequestSetWip'
argument :wip, argument :wip,
GraphQL::BOOLEAN_TYPE, GraphQL::Types::Boolean,
required: true, required: true,
description: <<~DESC description: <<~DESC
Whether or not to set the merge request as a draft. Whether or not to set the merge request as a draft.

View file

@ -7,15 +7,15 @@ module Mutations
description 'Update attributes of a merge request' description 'Update attributes of a merge request'
argument :title, GraphQL::STRING_TYPE, argument :title, GraphQL::Types::String,
required: false, required: false,
description: copy_field_description(Types::MergeRequestType, :title) description: copy_field_description(Types::MergeRequestType, :title)
argument :target_branch, GraphQL::STRING_TYPE, argument :target_branch, GraphQL::Types::String,
required: false, required: false,
description: copy_field_description(Types::MergeRequestType, :target_branch) description: copy_field_description(Types::MergeRequestType, :target_branch)
argument :description, GraphQL::STRING_TYPE, argument :description, GraphQL::Types::String,
required: false, required: false,
description: copy_field_description(Types::MergeRequestType, :description) description: copy_field_description(Types::MergeRequestType, :description)

View file

@ -36,12 +36,12 @@ module Mutations
description: 'Timestamp indicating ending moment to which the annotation relates.' description: 'Timestamp indicating ending moment to which the annotation relates.'
argument :dashboard_path, argument :dashboard_path,
GraphQL::STRING_TYPE, GraphQL::Types::String,
required: true, required: true,
description: 'The path to a file defining the dashboard on which the annotation should be added.' description: 'The path to a file defining the dashboard on which the annotation should be added.'
argument :description, argument :description,
GraphQL::STRING_TYPE, GraphQL::Types::String,
required: true, required: true,
description: 'The description of the annotation.' description: 'The description of the annotation.'

View file

@ -11,12 +11,12 @@ module Mutations
authorize :create_package_settings authorize :create_package_settings
argument :namespace_path, argument :namespace_path,
GraphQL::ID_TYPE, GraphQL::Types::ID,
required: true, required: true,
description: 'The namespace path where the namespace package setting is located.' description: 'The namespace path where the namespace package setting is located.'
argument :maven_duplicates_allowed, argument :maven_duplicates_allowed,
GraphQL::BOOLEAN_TYPE, GraphQL::Types::Boolean,
required: false, required: false,
description: copy_field_description(Types::Namespace::PackageSettingsType, :maven_duplicates_allowed) description: copy_field_description(Types::Namespace::PackageSettingsType, :maven_duplicates_allowed)
@ -26,7 +26,7 @@ module Mutations
description: copy_field_description(Types::Namespace::PackageSettingsType, :maven_duplicate_exception_regex) description: copy_field_description(Types::Namespace::PackageSettingsType, :maven_duplicate_exception_regex)
argument :generic_duplicates_allowed, argument :generic_duplicates_allowed,
GraphQL::BOOLEAN_TYPE, GraphQL::Types::Boolean,
required: false, required: false,
description: copy_field_description(Types::Namespace::PackageSettingsType, :generic_duplicates_allowed) description: copy_field_description(Types::Namespace::PackageSettingsType, :generic_duplicates_allowed)

View file

@ -14,12 +14,12 @@ module Mutations
description: 'The global ID of the resource to add a note to.' description: 'The global ID of the resource to add a note to.'
argument :body, argument :body,
GraphQL::STRING_TYPE, GraphQL::Types::String,
required: true, required: true,
description: copy_field_description(Types::Notes::NoteType, :body) description: copy_field_description(Types::Notes::NoteType, :body)
argument :confidential, argument :confidential,
GraphQL::BOOLEAN_TYPE, GraphQL::Types::Boolean,
required: false, required: false,
description: 'The confidentiality flag of a note. Default is false.' description: 'The confidentiality flag of a note. Default is false.'

View file

@ -11,7 +11,7 @@ module Mutations
DESC DESC
argument :body, argument :body,
GraphQL::STRING_TYPE, GraphQL::Types::String,
required: false, required: false,
description: copy_field_description(Types::Notes::NoteType, :body) description: copy_field_description(Types::Notes::NoteType, :body)

View file

@ -8,12 +8,12 @@ module Mutations
description "Updates a Note.\n#{QUICK_ACTION_ONLY_WARNING}" description "Updates a Note.\n#{QUICK_ACTION_ONLY_WARNING}"
argument :body, argument :body,
GraphQL::STRING_TYPE, GraphQL::Types::String,
required: false, required: false,
description: copy_field_description(Types::Notes::NoteType, :body) description: copy_field_description(Types::Notes::NoteType, :body)
argument :confidential, argument :confidential,
GraphQL::BOOLEAN_TYPE, GraphQL::Types::Boolean,
required: false, required: false,
description: 'The confidentiality flag of a note. Default is false.' description: 'The confidentiality flag of a note. Default is false.'

View file

@ -11,11 +11,11 @@ module Mutations
include Types::ReleaseAssetLinkSharedInputArguments include Types::ReleaseAssetLinkSharedInputArguments
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: true, required: true,
description: 'Full path of the project the asset link is associated with.' description: 'Full path of the project the asset link is associated with.'
argument :tag_name, GraphQL::STRING_TYPE, argument :tag_name, GraphQL::Types::String,
required: true, as: :tag, required: true, as: :tag,
description: "Name of the associated release's tag." description: "Name of the associated release's tag."

View file

@ -13,15 +13,15 @@ module Mutations
required: true, required: true,
description: 'ID of the release asset link to update.' description: 'ID of the release asset link to update.'
argument :name, GraphQL::STRING_TYPE, argument :name, GraphQL::Types::String,
required: false, required: false,
description: 'Name of the asset link.' description: 'Name of the asset link.'
argument :url, GraphQL::STRING_TYPE, argument :url, GraphQL::Types::String,
required: false, required: false,
description: 'URL of the asset link.' description: 'URL of the asset link.'
argument :direct_asset_path, GraphQL::STRING_TYPE, argument :direct_asset_path, GraphQL::Types::String,
required: false, as: :filepath, required: false, as: :filepath,
description: 'Relative path for a direct asset link.' description: 'Relative path for a direct asset link.'

View file

@ -5,7 +5,7 @@ module Mutations
class Base < BaseMutation class Base < BaseMutation
include FindsProject include FindsProject
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: true, required: true,
description: 'Full path of the project the release is associated with.' description: 'Full path of the project the release is associated with.'
end end

View file

@ -10,19 +10,19 @@ module Mutations
null: true, null: true,
description: 'The release after mutation.' description: 'The release after mutation.'
argument :tag_name, GraphQL::STRING_TYPE, argument :tag_name, GraphQL::Types::String,
required: true, as: :tag, required: true, as: :tag,
description: 'Name of the tag to associate with the release.' description: 'Name of the tag to associate with the release.'
argument :ref, GraphQL::STRING_TYPE, argument :ref, GraphQL::Types::String,
required: false, required: false,
description: 'The commit SHA or branch name to use if creating a new tag.' description: 'The commit SHA or branch name to use if creating a new tag.'
argument :name, GraphQL::STRING_TYPE, argument :name, GraphQL::Types::String,
required: false, required: false,
description: 'Name of the release.' description: 'Name of the release.'
argument :description, GraphQL::STRING_TYPE, argument :description, GraphQL::Types::String,
required: false, required: false,
description: 'Description (also known as "release notes") of the release.' description: 'Description (also known as "release notes") of the release.'
@ -30,7 +30,7 @@ module Mutations
required: false, required: false,
description: 'The date when the release will be/was ready. Defaults to the current time.' description: 'The date when the release will be/was ready. Defaults to the current time.'
argument :milestones, [GraphQL::STRING_TYPE], argument :milestones, [GraphQL::Types::String],
required: false, required: false,
description: 'The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones.' description: 'The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones.'

View file

@ -10,7 +10,7 @@ module Mutations
null: true, null: true,
description: 'The deleted release.' description: 'The deleted release.'
argument :tag_name, GraphQL::STRING_TYPE, argument :tag_name, GraphQL::Types::String,
required: true, as: :tag, required: true, as: :tag,
description: 'Name of the tag associated with the release to delete.' description: 'Name of the tag associated with the release to delete.'

View file

@ -10,15 +10,15 @@ module Mutations
null: true, null: true,
description: 'The release after mutation.' description: 'The release after mutation.'
argument :tag_name, GraphQL::STRING_TYPE, argument :tag_name, GraphQL::Types::String,
required: true, as: :tag, required: true, as: :tag,
description: 'Name of the tag associated with the release.' description: 'Name of the tag associated with the release.'
argument :name, GraphQL::STRING_TYPE, argument :name, GraphQL::Types::String,
required: false, required: false,
description: 'Name of the release.' description: 'Name of the release.'
argument :description, GraphQL::STRING_TYPE, argument :description, GraphQL::Types::String,
required: false, required: false,
description: 'Description (release notes) of the release.' description: 'Description (release notes) of the release.'
@ -26,7 +26,7 @@ module Mutations
required: false, required: false,
description: 'The release date.' description: 'The release date.'
argument :milestones, [GraphQL::STRING_TYPE], argument :milestones, [GraphQL::Types::String],
required: false, required: false,
description: 'The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones.' description: 'The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones.'

View file

@ -6,14 +6,14 @@ module Mutations
class BaseSecurityAnalyzer < BaseMutation class BaseSecurityAnalyzer < BaseMutation
include FindsProject include FindsProject
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: true, required: true,
description: 'Full path of the project.' description: 'Full path of the project.'
field :success_path, GraphQL::STRING_TYPE, null: true, field :success_path, GraphQL::Types::String, null: true,
description: 'Redirect path to use when the response is successful.' description: 'Redirect path to use when the response is successful.'
field :branch, GraphQL::STRING_TYPE, null: true, field :branch, GraphQL::Types::String, null: true,
description: 'Branch that has the new/modified `.gitlab-ci.yml` file.' description: 'Branch that has the new/modified `.gitlab-ci.yml` file.'
authorize :push_code authorize :push_code

View file

@ -16,11 +16,11 @@ module Mutations
null: true, null: true,
description: 'The snippet after mutation.' description: 'The snippet after mutation.'
argument :title, GraphQL::STRING_TYPE, argument :title, GraphQL::Types::String,
required: true, required: true,
description: 'Title of the snippet.' description: 'Title of the snippet.'
argument :description, GraphQL::STRING_TYPE, argument :description, GraphQL::Types::String,
required: false, required: false,
description: 'Description of the snippet.' description: 'Description of the snippet.'
@ -28,11 +28,11 @@ module Mutations
description: 'The visibility level of the snippet.', description: 'The visibility level of the snippet.',
required: true required: true
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: false, required: false,
description: 'The project full path the snippet is associated with.' description: 'The project full path the snippet is associated with.'
argument :uploaded_files, [GraphQL::STRING_TYPE], argument :uploaded_files, [GraphQL::Types::String],
required: false, required: false,
description: 'The paths to files uploaded in the snippet description.' description: 'The paths to files uploaded in the snippet description.'

View file

@ -13,11 +13,11 @@ module Mutations
required: true, required: true,
description: 'The global ID of the snippet to update.' description: 'The global ID of the snippet to update.'
argument :title, GraphQL::STRING_TYPE, argument :title, GraphQL::Types::String,
required: false, required: false,
description: 'Title of the snippet.' description: 'Title of the snippet.'
argument :description, GraphQL::STRING_TYPE, argument :description, GraphQL::Types::String,
required: false, required: false,
description: 'Description of the snippet.' description: 'Description of the snippet.'

View file

@ -6,7 +6,7 @@ module Mutations
graphql_name 'UserCalloutCreate' graphql_name 'UserCalloutCreate'
argument :feature_name, argument :feature_name,
GraphQL::STRING_TYPE, GraphQL::Types::String,
required: true, required: true,
description: "The feature name you want to dismiss the callout for." description: "The feature name you want to dismiss the callout for."

View file

@ -61,13 +61,7 @@ module Ci
scope :paused, -> { where(active: false) } scope :paused, -> { where(active: false) }
scope :online, -> { where('contacted_at > ?', online_contact_time_deadline) } scope :online, -> { where('contacted_at > ?', online_contact_time_deadline) }
scope :recent, -> { where('ci_runners.created_at > :date OR ci_runners.contacted_at > :date', date: 3.months.ago) } scope :recent, -> { where('ci_runners.created_at > :date OR ci_runners.contacted_at > :date', date: 3.months.ago) }
# The following query using negation is cheaper than using `contacted_at <= ?` scope :offline, -> { where(arel_table[:contacted_at].lteq(online_contact_time_deadline)) }
# because there are less runners online than have been created. The
# resulting query is quickly finding online ones and then uses the regular
# indexed search and rejects the ones that are in the previous set. If we
# did `contacted_at <= ?` the query would effectively have to do a seq
# scan.
scope :offline, -> { where.not(id: online) }
scope :not_connected, -> { where(contacted_at: nil) } scope :not_connected, -> { where(contacted_at: nil) }
scope :ordered, -> { order(id: :desc) } scope :ordered, -> { order(id: :desc) }

View file

@ -80,6 +80,11 @@ module Members
def after_execute(member:) def after_execute(member:)
super super
track_invite_source(member)
track_areas_of_focus(member)
end
def track_invite_source(member)
Gitlab::Tracking.event(self.class.name, 'create_member', label: invite_source, property: tracking_property(member), user: current_user) Gitlab::Tracking.event(self.class.name, 'create_member', label: invite_source, property: tracking_property(member), user: current_user)
end end
@ -94,6 +99,16 @@ module Members
member.invite? ? 'net_new_user' : 'existing_user' member.invite? ? 'net_new_user' : 'existing_user'
end end
def track_areas_of_focus(member)
areas_of_focus.each do |area_of_focus|
Gitlab::Tracking.event(self.class.name, 'area_of_focus', label: area_of_focus, property: member.id.to_s)
end
end
def areas_of_focus
params[:areas_of_focus] || []
end
def user_limit def user_limit
limit = params.fetch(:limit, DEFAULT_INVITE_LIMIT) limit = params.fetch(:limit, DEFAULT_INVITE_LIMIT)

View file

@ -1,8 +0,0 @@
---
name: merge_request_draft_filter
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35942
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/298776
milestone: '13.3'
type: development
group: group::code review
default_enabled: true

View file

@ -0,0 +1,139 @@
- title: Track progress on overall DevOps adoption
body: |
See the total number of key DevOps features adopted across your organization using the new progress bars in DevOps Adoption. Progress bars help you understand the value that teams are getting from GitLab and evaluate the state of your DevOps transformation.
stage: Manage
self-managed: true
gitlab-com: true
packages: [Ultimate]
url: https://docs.gitlab.com/ee/user/group/devops_adoption/
image_url: https://about.gitlab.com/images/14_1/progressbar.png
published_at: 2021-07-22
release: 14.1
- title: Track use of security scanning across multiple teams
body: |
Track which groups across your organization have enabled SAST and DAST scanning. This is helpful for verifying compliance with organizational requirements, responding to audit requests, and tracking progress on company initiatives to make applications more secure. To track adoption, go to the **Sec** tab in DevOps Adoption either at the group level or instance level.
To see groups that have enabled fuzz testing and dependency scanning, use [the DevOps API](https://docs.gitlab.com/ee/api/graphql/reference/index.html#devopsadoptionsnapshot). Fuzz testing and dependency scanning will be added to the DevOps Adoption UI in an upcoming release.
stage: Manage
self-managed: true
gitlab-com: true
packages: [Ultimate]
url: https://docs.gitlab.com/ee/user/group/devops_adoption
image_url: https://about.gitlab.com/images/14_1/scanadoption.png
published_at: 2021-07-22
release: 14.1
- title: Create and apply patches in VS Code
body: |
When reviewing a merge request (MR) it can be helpful to make suggestions to many of the changed files. This is often done by creating a patch file with the suggestions and sharing it with others. The problem is that this requires several manual steps like running Git commands and uploading the patch file somewhere others can download it.
With [GitLab Workflow](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow) [v3.26.0](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/blob/main/CHANGELOG.md#3260-2021-07-13) for VS Code you can now create and apply patches directly in your editor. The new `GitLab: Create snippet patch` command creates a patch with the changes in your editor and uploads that patch as a [GitLab snippet](https://docs.gitlab.com/ee/user/snippets.html).
Anyone can search for patches in the project's snippets and apply them directly in VS Code with the `GitLab: Apply snippet patch` command. The applied changes can then be committed to the MR.
Sharing and collaborating around patches is a great way to propose more complex suggestions and provide clear improvements. Patches created in VS Code can also be linked to others through snippets and downloaded and applied outside of VS Code for users with different editing tools.
stage: Create
self-managed: true
gitlab-com: true
packages: [Free, Premium, Ultimate]
url: 'https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/blob/main/README.md#create-and-apply-snippet-patch'
image_url: https://img.youtube.com/vi/QQxpLoKJULQ/hqdefault.jpg
published_at: 2021-07-22
release: 14.1
- title: Code coverage merge request approval rule
body: |
To keep code test coverage high, you need to ensure that merge requests to your codebase never decrease test coverage. Previously, the only way to enforce this was to [require approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/#required-approvals) from users who would check for test coverage decreases as part of their reviews.
Now you can enforce this organizational policy with the new Coverage check approval rule. This is a simple way to ensure merge requests that would decrease test coverage cannot be merged.
stage: Verify
self-managed: true
gitlab-com: true
packages: [Premium, Ultimate]
url: 'https://docs.gitlab.com/ee/ci/pipelines/settings.html#coverage-check-approval-rule'
image_url: https://about.gitlab.com/images/14_1/coverage-mr-approval-rule.png
published_at: 2021-07-22
release: 14.1
- title: Registration Features
body: |
[Registration Features](https://docs.gitlab.com/ee/development/service_ping/index.html#registration-features-program) introduces the ability for free, self-managed users running GitLab EE to access paid features by registering with GitLab and sharing activity data via [Service Ping](https://docs.gitlab.com/ee/development/service_ping/index.html#what-is-service-ping). The first feature introduced is [email from GitLab](https://docs.gitlab.com/ee/tools/email.html), enabling instance administrators to email users within their instance.
stage: Growth
self-managed: true
gitlab-com: false
packages: [Free]
url: 'https://docs.gitlab.com/ee/development/service_ping/index.html#registration-features-program'
image_url: https://about.gitlab.com/images/14_1/registration-features.png
published_at: 2021-07-22
release: 14.1
- title: Build, publish, and share Helm charts
body: |
Helm defines a [chart](https://helm.sh/docs/intro/using_helm/#three-big-concepts) as a Helm package that contains all of the resource definitions necessary to run an application, tool, or service inside of a Kubernetes cluster. For organizations that create and manage their own Helm charts, it's important to have a central repository to collect and share them.
GitLab already supports a variety of other [package manager formats](https://docs.gitlab.com/ee/user/packages/). Why not also support Helm? That's what community member and [MVP from the 14.0 milestone](https://about.gitlab.com/releases/2021/06/22/gitlab-14-0-released/#mvp) [Mathieu Parent](https://gitlab.com/sathieu) asked several months ago before breaking ground on the new GitLab Helm chart registry. The collaboration between the community and GitLab is part of our [dual flywheel strategy](https://about.gitlab.com/company/strategy/#dual-flywheels) and one of the reasons we love working at GitLab. Chapeau Mathieu!
Now you can use your GitLab project to publish and share packaged Helm charts. Simply add your project as a remote, authenticating with a personal access, deploy, or CI/CD job token. Once that's done you can use the Helm client or GitLab CI/CD to manage your Helm charts. You can also download the charts using the [API](https://docs.gitlab.com/ee/api/packages.html#get-a-project-package) or the [user interface](https://docs.gitlab.com/ee/user/packages/package_registry/#download-a-package).
stage: Package
self-managed: true
gitlab-com: true
packages: [Free, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/packages/helm_repository/
image_url: https://img.youtube.com/vi/B6K373-pAgw/hqdefault.jpg
published_at: 2021-07-22
release: 14.1
- title: Escalation Policies
body: |
Being on-call is a stressful, 24/7 job. It's possible to miss a notification despite your best efforts and intentions. Teams that maintain critical systems can't afford to miss alerts for outages or service disruptions. Escalation policies are a safety net for these situations. Escalation policies contain time-boxed steps that automatically page a responder in the next escalation step if the responder in the step before didn't respond. To protect your company from missed critical alerts, create an escalation policy in the GitLab project where you manage on-call schedules.
In GitLab 14.1, users can create, view, or delete escalation policies.
stage: Monitor
self-managed: true
gitlab-com: true
packages: [Premium, Ultimate]
url: https://docs.gitlab.com/ee/operations/incident_management/escalation_policies.html
image_url: https://img.youtube.com/vi/-1MuKzWJXKQ/hqdefault.jpg
published_at: 2021-07-22
release: 14.1
- title: CI/CD Tunnel for Kubernetes clusters
body: |
Until now, connecting Kubernetes clusters to GitLab CI/CD required users to open up their clusters towards GitLab. Some organizations do not encourage opening up their firewall externally due to security concerns.
GitLab now ships with a CI/CD Tunnel that connects GitLab Runners with your Kubernetes cluster using the [GitLab Kubernetes Agent](https://docs.gitlab.com/ee/user/clusters/agent/). This enables versatile GitOps workflows where the deployment logic can be coded in the pipeline.
You and your team can safely use your preferred tool to run the deployment itself using `kubectl`, `helm`, `kpt`, `tanka`, or anything else without security concerns.
To use the tunnel, define the `kubecontext` in your CI/CD pipeline to connect with your agent. To simplify this process, we plan to [automatically inject the `kubecontext`](https://gitlab.com/gitlab-org/gitlab/-/issues/324275) into the CI/CD environment in a future iteration.
The CI/CD tunnel is currently supported only from the project where the agent was configured but we are working on [adding group-level support](https://gitlab.com/groups/gitlab-org/-/epics/5784). You can safely start using the tunnel on GitLab SaaS and self-managed instances.
stage: Configure
self-managed: true
gitlab-com: true
packages: [Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/clusters/agent/ci_cd_tunnel.html
image_url: https://img.youtube.com/vi/eXxM4ScqiJs/hqdefault.jpg
published_at: 2021-07-22
release: 14.1
- title: External status checks for merge requests
body: |
You can now contact an external API to perform a status check in a merge request. This is a great way to integrate GitLab with third-party systems that:
- Run in an external system and do not have specific pipeline jobs.
- Require manual approval in another system.
In the project, APIs for the status checks can be configured (using either the GitLab UI or the GitLab API) and then when a change is made to a merge request, that API is called with various details about the merge request. The external API can then respond with a return code to indicate if the check has passed. This result is then shown in the merge request.
This allows teams to easily stay in sync and makes it easy to see that merge requests have met external requirements before being merged, adding an extra method to ensure compliance requirements are met.
stage: Manage
self-managed: true
gitlab-com: true
packages: [Ultimate]
url: https://docs.gitlab.com/ee/user/project/merge_requests/status_checks.html
image_url: https://about.gitlab.com/images/14_1/status-checks-pending.png
published_at: 2021-07-22
release: 14.1
- title: Pronouns viewable in user profile snapshot
body: |
You can now see pronouns on the snapshot view of a user profile when you hover over someone's name on an issue or merge request. This helps users better respond to comments using the correct pronouns without needing to navigate to the user's profile.
stage: Manage
self-managed: true
gitlab-com: true
packages: [Free, Premium, Ultimate]
url: 'https://docs.gitlab.com/ee/user/profile/#add-your-gender-pronouns'
image_url: https://about.gitlab.com/images/14_1/pronouns.png
published_at: 2021-07-22
release: 14.1

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
class AddUpdatedAtIndexOnMergeRequests < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
INDEX_NAME = 'index_merge_requests_on_target_project_id_and_updated_at_and_id'
disable_ddl_transaction!
def up
add_concurrent_index :merge_requests, [:target_project_id, :updated_at, :id], name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :merge_requests, INDEX_NAME
end
end

View file

@ -0,0 +1 @@
bd934c20443d5a044caa9e92389018291ffb2bf60b8ca54d9baca4a0e70caf28

View file

@ -24179,6 +24179,8 @@ CREATE INDEX index_merge_requests_on_target_project_id_and_squash_commit_sha ON
CREATE INDEX index_merge_requests_on_target_project_id_and_target_branch ON merge_requests USING btree (target_project_id, target_branch) WHERE ((state_id = 1) AND (merge_when_pipeline_succeeds = true)); CREATE INDEX index_merge_requests_on_target_project_id_and_target_branch ON merge_requests USING btree (target_project_id, target_branch) WHERE ((state_id = 1) AND (merge_when_pipeline_succeeds = true));
CREATE INDEX index_merge_requests_on_target_project_id_and_updated_at_and_id ON merge_requests USING btree (target_project_id, updated_at, id);
CREATE INDEX index_merge_requests_on_target_project_id_iid_jira_description ON merge_requests USING btree (target_project_id, iid) WHERE (description ~ '[A-Z][A-Z_0-9]+-\d+'::text); CREATE INDEX index_merge_requests_on_target_project_id_iid_jira_description ON merge_requests USING btree (target_project_id, iid) WHERE (description ~ '[A-Z][A-Z_0-9]+-\d+'::text);
CREATE INDEX index_merge_requests_on_title ON merge_requests USING btree (title); CREATE INDEX index_merge_requests_on_title ON merge_requests USING btree (title);

View file

@ -11,6 +11,47 @@ info: To determine the technical writer assigned to the Stage/Group associated w
With the GitLab Migrations API, you can view the progress of migrations initiated with With the GitLab Migrations API, you can view the progress of migrations initiated with
[GitLab Group Migration](../user/group/import/index.md). [GitLab Group Migration](../user/group/import/index.md).
## Start a new GitLab migration
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66353) in GitLab 14.2.
```plaintext
POST /bulk_imports
```
| Attribute | Type | Required | Description |
| --------------------------------- | ------ | -------- | ----------- |
| `configuration` | Hash | yes | The source GitLab instance configuration. |
| `configuration[url]` | String | yes | Source GitLab instance URL. |
| `configuration[access_token]` | String | yes | Access token to the source GitLab instance. |
| `entities` | Array | yes | List of entities to import. |
| `entities[source_type]` | String | yes | Source entity type (only `group_entity` is supported). |
| `entities[source_full_path]` | String | yes | Source full path of the entity to import. |
| `entities[destination_name]` | String | yes | Destination name for the entity. |
| `entities[destination_namespace]` | String | no | Destination namespace for the entity. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/bulk_imports" \
--data '{
"configuration": {
"url": "http://gitlab.example/",
"access_token": "access_token"
},
"entities": [
{
"source_full_path": "source/full/path",
"source_type": "group_entity",
"destination_name": "destination_name",
"destination_namespace": "destination/namespace/path"
}
]
}'
```
```json
{ "id": 1, "status": "created", "source_type": "gitlab", "created_at": "2021-06-18T09:45:55.358Z", "updated_at": "2021-06-18T09:46:27.003Z" }
```
## List all GitLab migrations ## List all GitLab migrations
```plaintext ```plaintext

View file

@ -42,6 +42,7 @@ POST /projects/:id/invitations
| `access_level` | integer | yes | A valid access level | | `access_level` | integer | yes | A valid access level |
| `expires_at` | string | no | A date string in the format YEAR-MONTH-DAY | | `expires_at` | string | no | A date string in the format YEAR-MONTH-DAY |
| `invite_source` | string | no | The source of the invitation that starts the member creation process. See [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/327120). | | `invite_source` | string | no | The source of the invitation that starts the member creation process. See [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/327120). |
| `areas_of_focus` | string | no | Areas the inviter wants the member to focus upon. |
```shell ```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \

View file

@ -418,6 +418,7 @@ POST /projects/:id/members
| `access_level` | integer | yes | A valid access level | | `access_level` | integer | yes | A valid access level |
| `expires_at` | string | no | A date string in the format `YEAR-MONTH-DAY` | | `expires_at` | string | no | A date string in the format `YEAR-MONTH-DAY` |
| `invite_source` | string | no | The source of the invitation that starts the member creation process. See [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/327120). | | `invite_source` | string | no | The source of the invitation that starts the member creation process. See [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/327120). |
| `areas_of_focus` | string | no | Areas the inviter wants the member to focus upon. |
```shell ```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \

View file

@ -83,7 +83,7 @@ The following table lists project permissions available for each role:
| Manage labels | | ✓ | ✓ | ✓ | ✓ | | Manage labels | | ✓ | ✓ | ✓ | ✓ |
| Manage linked issues | | ✓ | ✓ | ✓ | ✓ | | Manage linked issues | | ✓ | ✓ | ✓ | ✓ |
| Move [test case](../ci/test_cases/index.md) | | ✓ | ✓ | ✓ | ✓ | | Move [test case](../ci/test_cases/index.md) | | ✓ | ✓ | ✓ | ✓ |
| Pull [packages](packages/index.md) | | ✓ | ✓ | ✓ | ✓ | | Pull [packages](packages/index.md) | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| Reopen [test case](../ci/test_cases/index.md) | | ✓ | ✓ | ✓ | ✓ | | Reopen [test case](../ci/test_cases/index.md) | | ✓ | ✓ | ✓ | ✓ |
| See [DORA metrics](analytics/ci_cd_analytics.md) | | ✓ | ✓ | ✓ | ✓ | | See [DORA metrics](analytics/ci_cd_analytics.md) | | ✓ | ✓ | ✓ | ✓ |
| See a commit status | | ✓ | ✓ | ✓ | ✓ | | See a commit status | | ✓ | ✓ | ✓ | ✓ |

View file

@ -8,7 +8,10 @@ module API
helpers do helpers do
def bulk_imports def bulk_imports
@bulk_imports ||= ::BulkImports::ImportsFinder.new(user: current_user, status: params[:status]).execute @bulk_imports ||= ::BulkImports::ImportsFinder.new(
user: current_user,
status: params[:status]
).execute
end end
def bulk_import def bulk_import
@ -16,7 +19,11 @@ module API
end end
def bulk_import_entities def bulk_import_entities
@bulk_import_entities ||= ::BulkImports::EntitiesFinder.new(user: current_user, bulk_import: bulk_import, status: params[:status]).execute @bulk_import_entities ||= ::BulkImports::EntitiesFinder.new(
user: current_user,
bulk_import: bulk_import,
status: params[:status]
).execute
end end
def bulk_import_entity def bulk_import_entity
@ -27,13 +34,44 @@ module API
before { authenticate! } before { authenticate! }
resource :bulk_imports do resource :bulk_imports do
desc 'Start a new GitLab Migration' do
detail 'This feature was introduced in GitLab 14.2.'
end
params do
requires :configuration, type: Hash, desc: 'The source GitLab instance configuration' do
requires :url, type: String, desc: 'Source GitLab instance URL'
requires :access_token, type: String, desc: 'Access token to the source GitLab instance'
end
requires :entities, type: Array, desc: 'List of entities to import' do
requires :source_type, type: String, desc: 'Source entity type (only `group_entity` is supported)',
values: %w[group_entity]
requires :source_full_path, type: String, desc: 'Source full path of the entity to import'
requires :destination_name, type: String, desc: 'Destination name for the entity'
requires :destination_namespace, type: String, desc: 'Destination namespace for the entity'
end
end
post do
response = BulkImportService.new(
current_user,
params[:entities],
url: params[:configuration][:url],
access_token: params[:configuration][:access_token]
).execute
if response.success?
present response.payload, with: Entities::BulkImport
else
render_api_error!(response.message, response.http_status)
end
end
desc 'List all GitLab Migrations' do desc 'List all GitLab Migrations' do
detail 'This feature was introduced in GitLab 14.1.' detail 'This feature was introduced in GitLab 14.1.'
end end
params do params do
use :pagination use :pagination
optional :status, type: String, values: BulkImport.all_human_statuses, optional :status, type: String, values: BulkImport.all_human_statuses,
desc: 'Return GitLab Migrations with specified status' desc: 'Return GitLab Migrations with specified status'
end end
get do get do
present paginate(bulk_imports), with: Entities::BulkImport present paginate(bulk_imports), with: Entities::BulkImport
@ -45,10 +83,13 @@ module API
params do params do
use :pagination use :pagination
optional :status, type: String, values: ::BulkImports::Entity.all_human_statuses, optional :status, type: String, values: ::BulkImports::Entity.all_human_statuses,
desc: "Return all GitLab Migrations' entities with specified status" desc: "Return all GitLab Migrations' entities with specified status"
end end
get :entities do get :entities do
entities = ::BulkImports::EntitiesFinder.new(user: current_user, status: params[:status]).execute entities = ::BulkImports::EntitiesFinder.new(
user: current_user,
status: params[:status]
).execute
present paginate(entities), with: Entities::BulkImports::Entity present paginate(entities), with: Entities::BulkImports::Entity
end end
@ -69,7 +110,7 @@ module API
params do params do
requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration" requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration"
optional :status, type: String, values: ::BulkImports::Entity.all_human_statuses, optional :status, type: String, values: ::BulkImports::Entity.all_human_statuses,
desc: 'Return import entities with specified status' desc: 'Return import entities with specified status'
use :pagination use :pagination
end end
get ':import_id/entities' do get ':import_id/entities' do

View file

@ -54,6 +54,14 @@ module API
source.add_user(user, params[:access_level], current_user: current_user, expires_at: params[:expires_at]) source.add_user(user, params[:access_level], current_user: current_user, expires_at: params[:expires_at])
end end
def track_areas_of_focus(member, areas_of_focus)
return unless areas_of_focus
areas_of_focus.each do |area_of_focus|
Gitlab::Tracking.event(::Members::CreateService.name, 'area_of_focus', label: area_of_focus, property: member.id.to_s)
end
end
def present_members(members) def present_members(members)
present members, with: Entities::Member, current_user: current_user, show_seat_info: params[:show_seat_info] present members, with: Entities::Member, current_user: current_user, show_seat_info: params[:show_seat_info]
end end

View file

@ -24,6 +24,7 @@ module API
requires :access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'A valid access level (defaults: `30`, developer access level)' requires :access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'A valid access level (defaults: `30`, developer access level)'
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY' optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'invitations-api' optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'invitations-api'
optional :areas_of_focus, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Areas the inviter wants the member to focus upon'
end end
post ":id/invitations" do post ":id/invitations" do
params[:source] = find_source(source_type, params[:id]) params[:source] = find_source(source_type, params[:id])
@ -54,9 +55,9 @@ module API
success Entities::Member success Entities::Member
end end
params do params do
requires :email, type: String, desc: 'The email address of the invitation.' requires :email, type: String, desc: 'The email address of the invitation'
optional :access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'A valid access level (defaults: `30`, developer access level).' optional :access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'A valid access level (defaults: `30`, developer access level)'
optional :expires_at, type: DateTime, desc: 'Date string in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`).' optional :expires_at, type: DateTime, desc: 'Date string in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`)'
end end
put ":id/invitations/:email", requirements: { email: /[^\/]+/ } do put ":id/invitations/:email", requirements: { email: /[^\/]+/ } do
source = find_source(source_type, params.delete(:id)) source = find_source(source_type, params.delete(:id))

View file

@ -94,6 +94,7 @@ module API
requires :user_id, types: [Integer, String], desc: 'The user ID of the new member or multiple IDs separated by commas.' requires :user_id, types: [Integer, String], desc: 'The user ID of the new member or multiple IDs separated by commas.'
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY' optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'members-api' optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'members-api'
optional :areas_of_focus, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Areas the inviter wants the member to focus upon'
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
post ":id/members" do post ":id/members" do
@ -119,7 +120,12 @@ module API
not_allowed! # This currently can only be reached in EE not_allowed! # This currently can only be reached in EE
elsif member.valid? && member.persisted? elsif member.valid? && member.persisted?
present_members(member) present_members(member)
Gitlab::Tracking.event(::Members::CreateService.name, 'create_member', label: params[:invite_source], property: 'existing_user', user: current_user) Gitlab::Tracking.event(::Members::CreateService.name,
'create_member',
label: params[:invite_source],
property: 'existing_user',
user: current_user)
track_areas_of_focus(member, params[:areas_of_focus])
else else
render_validation_error!(member) render_validation_error!(member)
end end

View file

@ -8,6 +8,10 @@ require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/string/inflections'
# Explicitly load Redis::Store::Factory so we can read Redis configuration in
# TestEnv
require 'redis/store/factory'
module Gitlab module Gitlab
module Redis module Redis
class Wrapper class Wrapper

View file

@ -32,7 +32,7 @@ module Gitlab
extend Gitlab::SetupHelper extend Gitlab::SetupHelper
class << self class << self
def configuration_toml(dir, _, _) def configuration_toml(dir, _, _)
config = { redis: { URL: redis_url } } config = { redis: { URL: redis_url, DB: redis_db } }
TomlRB.dump(config) TomlRB.dump(config)
end end
@ -41,6 +41,10 @@ module Gitlab
Gitlab::Redis::SharedState.url Gitlab::Redis::SharedState.url
end end
def redis_db
Gitlab::Redis::SharedState.params.fetch(:db, 0)
end
def get_config_path(dir, _) def get_config_path(dir, _)
File.join(dir, 'config_path') File.join(dir, 'config_path')
end end

View file

@ -4160,6 +4160,30 @@ msgstr ""
msgid "ApprovalRule|Target branch" msgid "ApprovalRule|Target branch"
msgstr "" msgstr ""
msgid "ApprovalSettings|Merge request approval settings have been updated."
msgstr ""
msgid "ApprovalSettings|Prevent MR approvals by the author."
msgstr ""
msgid "ApprovalSettings|Prevent approval of merge requests by merge request committers."
msgstr ""
msgid "ApprovalSettings|Prevent users from modifying MR approval rules."
msgstr ""
msgid "ApprovalSettings|Remove all approvals in a merge request when new commits are pushed to its source branch."
msgstr ""
msgid "ApprovalSettings|Require user password for approvals."
msgstr ""
msgid "ApprovalSettings|There was an error loading merge request approval settings."
msgstr ""
msgid "ApprovalSettings|There was an error updating merge request approval settings."
msgstr ""
msgid "ApprovalStatusTooltip|Adheres to separation of duties" msgid "ApprovalStatusTooltip|Adheres to separation of duties"
msgstr "" msgstr ""
@ -20489,9 +20513,6 @@ msgstr ""
msgid "Merge request analytics" msgid "Merge request analytics"
msgstr "" msgstr ""
msgid "Merge request approval settings have been updated."
msgstr ""
msgid "Merge request approvals" msgid "Merge request approvals"
msgstr "" msgstr ""
@ -24767,9 +24788,6 @@ msgstr ""
msgid "Prevent adding new members to project membership within this group" msgid "Prevent adding new members to project membership within this group"
msgstr "" msgstr ""
msgid "Prevent approval of merge requests by merge request committers."
msgstr ""
msgid "Prevent environment from auto-stopping" msgid "Prevent environment from auto-stopping"
msgstr "" msgstr ""
@ -24785,9 +24803,6 @@ msgstr ""
msgid "Prevent users from modifying MR approval rules in projects and merge requests." msgid "Prevent users from modifying MR approval rules in projects and merge requests."
msgstr "" msgstr ""
msgid "Prevent users from modifying MR approval rules."
msgstr ""
msgid "Prevent users from performing write operations on GitLab while performing maintenance." msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr "" msgstr ""
@ -27126,9 +27141,6 @@ msgstr ""
msgid "Remove access" msgid "Remove access"
msgstr "" msgstr ""
msgid "Remove all approvals in a merge request when new commits are pushed to its source branch."
msgstr ""
msgid "Remove all or specific assignee(s)" msgid "Remove all or specific assignee(s)"
msgstr "" msgstr ""
@ -33126,9 +33138,6 @@ msgstr ""
msgid "There was an error importing the Jira project." msgid "There was an error importing the Jira project."
msgstr "" msgstr ""
msgid "There was an error loading merge request approval settings."
msgstr ""
msgid "There was an error loading related feature flags" msgid "There was an error loading related feature flags"
msgstr "" msgstr ""
@ -33168,9 +33177,6 @@ msgstr ""
msgid "There was an error trying to validate your query" msgid "There was an error trying to validate your query"
msgstr "" msgstr ""
msgid "There was an error updating merge request approval settings."
msgstr ""
msgid "There was an error updating the Geo Settings" msgid "There was an error updating the Geo Settings"
msgstr "" msgstr ""

View file

@ -59,7 +59,7 @@
"@gitlab/favicon-overlay": "2.0.0", "@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.202.0", "@gitlab/svgs": "1.202.0",
"@gitlab/tributejs": "1.0.0", "@gitlab/tributejs": "1.0.0",
"@gitlab/ui": "31.5.0", "@gitlab/ui": "31.6.0",
"@gitlab/visual-review-tools": "1.6.1", "@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "6.1.3-2", "@rails/actioncable": "6.1.3-2",
"@rails/ujs": "6.1.3-2", "@rails/ujs": "6.1.3-2",

View file

@ -317,18 +317,6 @@ RSpec.describe MergeRequestsFinder do
) )
end end
context 'when merge_request_draft_filter is disabled' do
it 'does not include draft merge requests' do
stub_feature_flags(merge_request_draft_filter: false)
merge_requests = described_class.new(user, { draft_param_key => 'yes' }).execute
expect(merge_requests).to contain_exactly(
merge_request4, merge_request5, wip_merge_request1, wip_merge_request2, wip_merge_request3, wip_merge_request4
)
end
end
it "filters by not #{draft_param_key}" do it "filters by not #{draft_param_key}" do
params = { draft_param_key => 'no' } params = { draft_param_key => 'no' }

View file

@ -471,3 +471,84 @@ export const mockSearch = [
export const mockBranchesAfterMap = ['branch-1', 'branch-10', 'branch-11']; export const mockBranchesAfterMap = ['branch-1', 'branch-10', 'branch-11'];
export const mockTagsAfterMap = ['tag-3', 'tag-2', 'tag-1', 'main-tag']; export const mockTagsAfterMap = ['tag-3', 'tag-2', 'tag-1', 'main-tag'];
export const triggered = [
{
id: 602,
user: {
id: 1,
name: 'Administrator',
username: 'root',
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
web_url: 'http://gdk.test:3000/root',
show_status: false,
path: '/root',
},
active: false,
coverage: null,
source: 'pipeline',
source_job: { name: 'trigger_job_on_mr' },
path: '/root/job-log-sections/-/pipelines/602',
details: {
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/root/job-log-sections/-/pipelines/602',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
},
project: {
id: 36,
name: 'job-log-sections',
full_path: '/root/job-log-sections',
full_name: 'Administrator / job-log-sections',
},
},
];
export const triggeredBy = {
id: 614,
user: {
id: 1,
name: 'Administrator',
username: 'root',
state: 'active',
avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
web_url: 'http://gdk.test:3000/root',
show_status: false,
path: '/root',
},
active: false,
coverage: null,
source: 'web',
source_job: { name: null },
path: '/root/trigger-downstream/-/pipelines/614',
details: {
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/root/trigger-downstream/-/pipelines/614',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
},
project: {
id: 42,
name: 'trigger-downstream',
full_path: '/root/trigger-downstream',
full_name: 'Administrator / trigger-downstream',
},
};

View file

@ -12,6 +12,7 @@ import PipelinesTimeago from '~/pipelines/components/pipelines_list/time_ago.vue
import eventHub from '~/pipelines/event_hub'; import eventHub from '~/pipelines/event_hub';
import CiBadge from '~/vue_shared/components/ci_badge_link.vue'; import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
import CommitComponent from '~/vue_shared/components/commit.vue'; import CommitComponent from '~/vue_shared/components/commit.vue';
import { triggeredBy, triggered } from './mock_data';
jest.mock('~/pipelines/event_hub'); jest.mock('~/pipelines/event_hub');
@ -59,6 +60,8 @@ describe('Pipelines Table', () => {
const findStagesTh = () => wrapper.findByTestId('stages-th'); const findStagesTh = () => wrapper.findByTestId('stages-th');
const findTimeAgoTh = () => wrapper.findByTestId('timeago-th'); const findTimeAgoTh = () => wrapper.findByTestId('timeago-th');
const findActionsTh = () => wrapper.findByTestId('actions-th'); const findActionsTh = () => wrapper.findByTestId('actions-th');
const findUpstream = () => wrapper.findByTestId('mini-graph-upstream');
const findDownstream = () => wrapper.findByTestId('mini-graph-downstream');
beforeEach(() => { beforeEach(() => {
pipeline = createMockPipeline(); pipeline = createMockPipeline();
@ -136,6 +139,8 @@ describe('Pipelines Table', () => {
describe('stages cell', () => { describe('stages cell', () => {
it('should render a pipeline mini graph', () => { it('should render a pipeline mini graph', () => {
expect(findPipelineMiniGraph().exists()).toBe(true); expect(findPipelineMiniGraph().exists()).toBe(true);
expect(findUpstream().exists()).toBe(false);
expect(findDownstream().exists()).toBe(false);
}); });
it('should render the right number of stages', () => { it('should render the right number of stages', () => {
@ -173,6 +178,57 @@ describe('Pipelines Table', () => {
expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable'); expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable');
}); });
describe('upstream linked pipelines', () => {
beforeEach(() => {
pipeline = createMockPipeline();
pipeline.triggered_by = triggeredBy;
createComponent({ pipelines: [pipeline] });
});
it('should render only a upstream pipeline', () => {
expect(findUpstream().exists()).toBe(true);
expect(findDownstream().exists()).toBe(false);
});
it('should pass an array of the correct data to the linked pipeline component', () => {
const triggeredByProps = findUpstream().props('triggeredBy');
expect(triggeredByProps).toEqual(expect.any(Array));
expect(triggeredByProps).toHaveLength(1);
expect(triggeredByProps[0]).toBe(triggeredBy);
});
});
describe('downstream linked pipelines', () => {
beforeEach(() => {
pipeline = createMockPipeline();
pipeline.triggered = triggered;
createComponent({ pipelines: [pipeline] });
});
it('should render only a downstream pipeline', () => {
expect(findDownstream().exists()).toBe(true);
expect(findUpstream().exists()).toBe(false);
});
});
describe('upstream and downstream linked pipelines', () => {
beforeEach(() => {
pipeline = createMockPipeline();
pipeline.triggered = triggered;
pipeline.triggered_by = triggeredBy;
createComponent({ pipelines: [pipeline] });
});
it('should render both downstream and upstream pipelines', () => {
expect(findDownstream().exists()).toBe(true);
expect(findUpstream().exists()).toBe(true);
});
});
}); });
describe('duration cell', () => { describe('duration cell', () => {

View file

@ -22,4 +22,28 @@ RSpec.describe Gitlab::SetupHelper::Workhorse do
end end
end end
end end
describe '.redis_url' do
it 'matches the SharedState URL' do
expect(Gitlab::Redis::SharedState).to receive(:url).and_return('foo')
expect(described_class.redis_url).to eq('foo')
end
end
describe '.redis_db' do
subject { described_class.redis_db }
it 'matches the SharedState DB' do
expect(Gitlab::Redis::SharedState).to receive(:params).and_return(db: 1)
is_expected.to eq(1)
end
it 'defaults to 0 if unspecified' do
expect(Gitlab::Redis::SharedState).to receive(:params).and_return({})
is_expected.to eq(0)
end
end
end end

View file

@ -20,6 +20,48 @@ RSpec.describe API::BulkImports do
end end
end end
describe 'POST /bulk_imports' do
it 'starts a new migration' do
post api('/bulk_imports', user), params: {
configuration: {
url: 'http://gitlab.example',
access_token: 'access_token'
},
entities: [
source_type: 'group_entity',
source_full_path: 'full_path',
destination_name: 'destination_name',
destination_namespace: 'destination_namespace'
]
}
expect(response).to have_gitlab_http_status(:created)
expect(json_response['status']).to eq('created')
end
context 'when provided url is blocked' do
it 'returns blocked url error' do
post api('/bulk_imports', user), params: {
configuration: {
url: 'url',
access_token: 'access_token'
},
entities: [
source_type: 'group_entity',
source_full_path: 'full_path',
destination_name: 'destination_name',
destination_namespace: 'destination_namespace'
]
}
expect(response).to have_gitlab_http_status(:unprocessable_entity)
expect(json_response['message']).to eq('Validation failed: Url is blocked: Only allowed schemes are http, https')
end
end
end
describe 'GET /bulk_imports/entities' do describe 'GET /bulk_imports/entities' do
it 'returns a list of all import entities authored by the user' do it 'returns a list of all import entities authored by the user' do
get api('/bulk_imports/entities', user) get api('/bulk_imports/entities', user)

View file

@ -152,6 +152,20 @@ RSpec.describe API::Invitations do
end end
end end
context 'with areas_of_focus', :snowplow do
it 'tracks the areas_of_focus from params' do
post invitations_url(source, maintainer),
params: { email: email, access_level: Member::DEVELOPER, areas_of_focus: 'Other' }
expect_snowplow_event(
category: 'Members::InviteService',
action: 'area_of_focus',
label: 'Other',
property: source.members.last.id.to_s
)
end
end
context 'with invite_source considerations', :snowplow do context 'with invite_source considerations', :snowplow do
let(:params) { { email: email, access_level: Member::DEVELOPER } } let(:params) { { email: email, access_level: Member::DEVELOPER } }

View file

@ -409,6 +409,53 @@ RSpec.describe API::Members do
end end
end end
context 'with areas_of_focus considerations', :snowplow do
context 'when there is 1 user to add' do
let(:user_id) { stranger.id }
context 'when areas_of_focus is present in params' do
it 'tracks the areas_of_focus' do
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
params: { user_id: user_id, access_level: Member::DEVELOPER, areas_of_focus: 'Other' }
expect_snowplow_event(
category: 'Members::CreateService',
action: 'area_of_focus',
label: 'Other',
property: source.members.last.id.to_s
)
end
end
context 'when areas_of_focus is not present in params' do
it 'does not track the areas_of_focus' do
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
params: { user_id: user_id, access_level: Member::DEVELOPER }
expect_no_snowplow_event(category: 'Members::CreateService', action: 'area_of_focus')
end
end
end
context 'when there are multiple users to add' do
let(:user_id) { [developer.id, stranger.id].join(',') }
context 'when areas_of_focus is present in params' do
it 'tracks the areas_of_focus' do
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
params: { user_id: user_id, access_level: Member::DEVELOPER, areas_of_focus: 'Other' }
expect_snowplow_event(
category: 'Members::CreateService',
action: 'area_of_focus',
label: 'Other',
property: source.members.last.id.to_s
)
end
end
end
end
it "returns 409 if member already exists" do it "returns 409 if member already exists" do
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer), post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
params: { user_id: maintainer.id, access_level: Member::MAINTAINER } params: { user_id: maintainer.id, access_level: Member::MAINTAINER }

View file

@ -3,7 +3,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_cache, :clean_gitlab_redis_shared_state, :sidekiq_inline do RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_cache, :clean_gitlab_redis_shared_state, :sidekiq_inline do
let_it_be(:source) { create(:project) } let_it_be(:source, reload: true) { create(:project) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:member) { create(:user) } let_it_be(:member) { create(:user) }
let_it_be(:user_ids) { member.id.to_s } let_it_be(:user_ids) { member.id.to_s }
@ -89,7 +89,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
context 'when invite_source is not passed' do context 'when invite_source is not passed' do
let(:additional_params) { {} } let(:additional_params) { {} }
it 'tracks the invite source as unknown' do it 'raises an error' do
expect { execute_service }.to raise_error(ArgumentError, 'No invite source provided.') expect { execute_service }.to raise_error(ArgumentError, 'No invite source provided.')
expect_no_snowplow_event expect_no_snowplow_event
@ -126,4 +126,74 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
end end
end end
end end
context 'when tracking the areas of focus', :snowplow do
context 'when areas_of_focus is not passed' do
it 'does not track' do
execute_service
expect_no_snowplow_event(category: described_class.name, action: 'area_of_focus')
end
end
context 'when 1 areas_of_focus is passed' do
let(:additional_params) { { invite_source: '_invite_source_', areas_of_focus: ['no_selection'] } }
it 'tracks the areas_of_focus from params' do
execute_service
expect_snowplow_event(
category: described_class.name,
action: 'area_of_focus',
label: 'no_selection',
property: source.members.last.id.to_s
)
end
context 'when passing many user ids' do
let(:another_user) { create(:user) }
let(:user_ids) { [member.id, another_user.id].join(',') }
it 'tracks the areas_of_focus from params' do
execute_service
members = source.members.last(2)
expect_snowplow_event(
category: described_class.name,
action: 'area_of_focus',
label: 'no_selection',
property: members.first.id.to_s
)
expect_snowplow_event(
category: described_class.name,
action: 'area_of_focus',
label: 'no_selection',
property: members.last.id.to_s
)
end
end
end
context 'when multiple areas_of_focus are passed' do
let(:additional_params) { { invite_source: '_invite_source_', areas_of_focus: %w[no_selection Other] } }
it 'tracks the areas_of_focus from params' do
execute_service
expect_snowplow_event(
category: described_class.name,
action: 'area_of_focus',
label: 'no_selection',
property: source.members.last.id.to_s
)
expect_snowplow_event(
category: described_class.name,
action: 'area_of_focus',
label: 'Other',
property: source.members.last.id.to_s
)
end
end
end
end end

View file

@ -263,8 +263,13 @@ module TestEnv
# Feature specs are run through Workhorse # Feature specs are run through Workhorse
def setup_workhorse def setup_workhorse
# Always rebuild the config file
if skip_compile_workhorse?
Gitlab::SetupHelper::Workhorse.create_configuration(workhorse_dir, nil)
return
end
start = Time.now start = Time.now
return if skip_compile_workhorse?
FileUtils.rm_rf(workhorse_dir) FileUtils.rm_rf(workhorse_dir)
Gitlab::SetupHelper::Workhorse.compile_into(workhorse_dir) Gitlab::SetupHelper::Workhorse.compile_into(workhorse_dir)
@ -305,12 +310,6 @@ module TestEnv
config_path = Gitlab::SetupHelper::Workhorse.get_config_path(workhorse_dir, {}) config_path = Gitlab::SetupHelper::Workhorse.get_config_path(workhorse_dir, {})
# This should be set up in setup_workhorse, but since
# component_needs_update? only checks that versions are consistent,
# we need to ensure the config file exists. This line can be removed
# later after a new Workhorse version is updated.
Gitlab::SetupHelper::Workhorse.create_configuration(workhorse_dir, nil) unless File.exist?(config_path)
workhorse_pid = spawn( workhorse_pid = spawn(
{ 'PATH' => "#{ENV['PATH']}:#{workhorse_dir}" }, { 'PATH' => "#{ENV['PATH']}:#{workhorse_dir}" },
File.join(workhorse_dir, 'gitlab-workhorse'), File.join(workhorse_dir, 'gitlab-workhorse'),

View file

@ -908,10 +908,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8" resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw== integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
"@gitlab/ui@31.5.0": "@gitlab/ui@31.6.0":
version "31.5.0" version "31.6.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-31.5.0.tgz#45b7866b790e7d5a1b67b39000c047991036b437" resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-31.6.0.tgz#70aeb2b497aea15ea0a0a223b7332bc608587ff1"
integrity sha512-pIJXbO0zfpgD0CmZTjKMMCu6l1AMLWiGPdPqDhqgGskuKGOU1UxX7ApDLesgRsSCievMTD/zsVvRHrG6AH7LiQ== integrity sha512-HABk7zwF7h5jaNaRiGKnWEkuQiPIm/bDRUzAtV3d/E/OgIzAU9S/fX3SHOrbj44g4Kq3mXifa4omMhEORx+mQg==
dependencies: dependencies:
"@babel/standalone" "^7.0.0" "@babel/standalone" "^7.0.0"
bootstrap-vue "2.18.1" bootstrap-vue "2.18.1"