From 427451410d11193ef3b2638fc37a70f259839f6d Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 7 Jul 2022 15:08:37 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../ide/components/new_dropdown/modal.vue | 24 ++- ...l.vue => import_project_members_modal.vue} | 122 +++++++------ .../import_project_members_trigger.vue | 34 ++++ .../init_import_a_project_modal.js | 23 --- .../init_import_project_members_modal.js | 23 +++ .../init_import_project_members_trigger.js | 20 +++ .../pages/projects/project_members/index.js | 6 +- .../components/source_viewer/constants.js | 2 +- .../mutations/work_items/update_arguments.rb | 3 + app/graphql/mutations/work_items/update.rb | 6 +- .../widgets/hierarchy_update_input_type.rb | 21 +++ app/services/audit_event_service.rb | 7 +- app/services/issuable_base_service.rb | 15 +- app/services/issuable_links/create_service.rb | 2 +- .../work_items/parent_links/create_service.rb | 65 +++++++ app/services/work_items/update_service.rb | 36 +++- .../work_items/widgets/base_service.rb | 2 + .../widgets/hierarchy_service/base_service.rb | 33 ++++ .../hierarchy_service/update_service.rb | 51 ++++++ .../projects/project_members/index.html.haml | 3 +- ...3_product_analytics_test_metrics_union.yml | 4 +- ...ct_analytics_test_metrics_intersection.yml | 4 +- ...40_testing_total_unique_counts_monthly.yml | 4 +- .../20210916080405_promoted_issues.yml | 4 +- ...3_product_analytics_test_metrics_union.yml | 4 +- ...ct_analytics_test_metrics_intersection.yml | 4 +- ...02_service_usage_data_download_payload.yml | 6 +- .../license/20210201124932_recorded_at.yml | 4 +- .../metrics/license/20210201124933_uuid.yml | 4 +- .../license/20210204124827_hostname.yml | 4 +- .../20210204124829_active_user_count.yml | 4 +- ...0210204124938_recording_ce_finished_at.yml | 4 +- ...210702140138_collected_data_categories.yml | 4 +- ...15152326_service_ping_features_enabled.yml | 4 +- .../20211124061450_snowplow_enabled.yml | 4 +- ...onfigured_to_gitlab_collector_hostname.yml | 4 +- .../reference_architectures/10k_users.md | 3 + .../reference_architectures/25k_users.md | 3 + .../reference_architectures/2k_users.md | 3 + .../reference_architectures/3k_users.md | 3 + .../reference_architectures/50k_users.md | 3 + .../reference_architectures/5k_users.md | 3 + doc/api/graphql/reference/index.md | 11 ++ doc/ci/migration/circleci.md | 4 +- .../dependency_scanning/index.md | 4 +- doc/user/clusters/agent/index.md | 8 +- .../Jobs/Dependency-Scanning.gitlab-ci.yml | 1 + .../background_migration/batched_job.rb | 1 + .../background_migration/batched_migration.rb | 6 + .../batched_migration_runner.rb | 2 + .../service_ping/instrumented_payload.rb | 7 + locale/gitlab.pot | 15 ++ .../1_manage/import_large_github_repo_spec.rb | 2 +- .../gitlab_migration_large_project_spec.rb | 6 +- .../ide/components/new_dropdown/modal_spec.js | 94 +++++++--- ...s => import_project_members_modal_spec.js} | 44 ++--- .../import_project_members_trigger_spec.js | 49 ++++++ .../hierarchy_update_input_type_spec.rb | 9 + .../background_migration/batched_job_spec.rb | 6 + .../batched_migration_runner_spec.rb | 6 +- .../batched_migration_spec.rb | 21 +++ .../service_ping/instrumented_payload_spec.rb | 50 ++++++ .../mutations/work_items/update_spec.rb | 132 ++++++++++++++ .../parent_links/create_service_spec.rb | 161 ++++++++++++++++++ .../work_items/update_service_spec.rb | 60 ++++++- .../hierarchy_service/update_service_spec.rb | 148 ++++++++++++++++ .../project_members/index.html.haml_spec.rb | 6 +- 67 files changed, 1218 insertions(+), 217 deletions(-) rename app/assets/javascripts/invite_members/components/{import_a_project_modal.vue => import_project_members_modal.vue} (54%) create mode 100644 app/assets/javascripts/invite_members/components/import_project_members_trigger.vue delete mode 100644 app/assets/javascripts/invite_members/init_import_a_project_modal.js create mode 100644 app/assets/javascripts/invite_members/init_import_project_members_modal.js create mode 100644 app/assets/javascripts/invite_members/init_import_project_members_trigger.js create mode 100644 app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb create mode 100644 app/services/work_items/parent_links/create_service.rb create mode 100644 app/services/work_items/widgets/hierarchy_service/base_service.rb create mode 100644 app/services/work_items/widgets/hierarchy_service/update_service.rb rename spec/frontend/invite_members/components/{import_a_project_modal_spec.js => import_project_members_modal_spec.js} (77%) create mode 100644 spec/frontend/invite_members/components/import_project_members_trigger_spec.js create mode 100644 spec/graphql/types/work_items/widgets/hierarchy_update_input_type_spec.rb create mode 100644 spec/services/work_items/parent_links/create_service_spec.rb create mode 100644 spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue index e3c230f7660..d6207d4a557 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue @@ -68,6 +68,10 @@ export default { }, methods: { ...mapActions(['createTempEntry', 'renameEntry']), + submitAndClose() { + this.submitForm(); + this.close(); + }, submitForm() { this.entryName = trimPathComponents(this.entryName); @@ -161,15 +165,17 @@ export default {
- +
+ +
  • -import { GlButton, GlFormGroup, GlModal, GlModalDirective, GlSprintf } from '@gitlab/ui'; +import { GlFormGroup, GlModal, GlSprintf } from '@gitlab/ui'; import { uniqueId } from 'lodash'; import { importProjectMembers } from '~/api/projects_api'; +import { BV_SHOW_MODAL } from '~/lib/utils/constants'; import { s__, __, sprintf } from '~/locale'; +import eventHub from '../event_hub'; import ProjectSelect from './project_select.vue'; export default { + name: 'ImportProjectMembersModal', components: { - GlButton, GlFormGroup, GlModal, GlSprintf, ProjectSelect, }, - directives: { - GlModal: GlModalDirective, - }, props: { projectId: { type: String, @@ -45,8 +44,33 @@ export default { validationState() { return this.invalidFeedbackMessage === '' ? null : false; }, + actionPrimary() { + return { + text: this.$options.i18n.modalPrimaryButton, + attributes: { + variant: 'confirm', + disabled: this.importDisabled, + loading: this.isLoading, + }, + }; + }, + actionCancel() { + return { text: this.$options.i18n.modalCancelButton }; + }, + }, + mounted() { + eventHub.$on('openProjectMembersModal', () => { + this.openModal(); + }); }, methods: { + openModal() { + this.$root.$emit(BV_SHOW_MODAL, this.$options.modalId); + }, + resetFields() { + this.invalidFeedbackMessage = ''; + this.projectToBeImported = {}; + }, submitImport() { this.isLoading = true; return importProjectMembers(this.projectId, this.projectToBeImported.id) @@ -57,11 +81,6 @@ export default { this.projectToBeImported = {}; }); }, - closeModal() { - this.invalidFeedbackMessage = ''; - - this.$refs.modal.hide(); - }, showToastMessage() { this.$toast.show(this.$options.i18n.successMessage, this.$options.toastOptions); @@ -79,7 +98,6 @@ export default { }; }, i18n: { - buttonText: s__('ImportAProjectModal|Import from a project'), projectLabel: __('Project'), modalTitle: s__('ImportAProjectModal|Import members from another project'), modalIntro: s__( @@ -95,63 +113,37 @@ export default { }, projectSelectLabelId: 'project-select', modalId: uniqueId('import-a-project-modal-'), - formClasses: 'gl-md-w-auto gl-w-full', - buttonClasses: 'gl-w-full', }; diff --git a/app/assets/javascripts/invite_members/components/import_project_members_trigger.vue b/app/assets/javascripts/invite_members/components/import_project_members_trigger.vue new file mode 100644 index 00000000000..5781abb41b7 --- /dev/null +++ b/app/assets/javascripts/invite_members/components/import_project_members_trigger.vue @@ -0,0 +1,34 @@ + + + diff --git a/app/assets/javascripts/invite_members/init_import_a_project_modal.js b/app/assets/javascripts/invite_members/init_import_a_project_modal.js deleted file mode 100644 index 954347467de..00000000000 --- a/app/assets/javascripts/invite_members/init_import_a_project_modal.js +++ /dev/null @@ -1,23 +0,0 @@ -import Vue from 'vue'; -import ImportAProjectModal from '~/invite_members/components/import_a_project_modal.vue'; - -export default function initImportAProjectModal() { - const el = document.querySelector('.js-import-a-project-modal'); - - if (!el) { - return false; - } - - const { projectId, projectName } = el.dataset; - - return new Vue({ - el, - render: (createElement) => - createElement(ImportAProjectModal, { - props: { - projectId, - projectName, - }, - }), - }); -} diff --git a/app/assets/javascripts/invite_members/init_import_project_members_modal.js b/app/assets/javascripts/invite_members/init_import_project_members_modal.js new file mode 100644 index 00000000000..daaa1315884 --- /dev/null +++ b/app/assets/javascripts/invite_members/init_import_project_members_modal.js @@ -0,0 +1,23 @@ +import Vue from 'vue'; +import ImportProjectMembersModal from '~/invite_members/components/import_project_members_modal.vue'; + +export default function initImportProjectMembersModal() { + const el = document.querySelector('.js-import-project-members-modal'); + + if (!el) { + return false; + } + + const { projectId, projectName } = el.dataset; + + return new Vue({ + el, + render: (createElement) => + createElement(ImportProjectMembersModal, { + props: { + projectId, + projectName, + }, + }), + }); +} diff --git a/app/assets/javascripts/invite_members/init_import_project_members_trigger.js b/app/assets/javascripts/invite_members/init_import_project_members_trigger.js new file mode 100644 index 00000000000..66a9bf118d2 --- /dev/null +++ b/app/assets/javascripts/invite_members/init_import_project_members_trigger.js @@ -0,0 +1,20 @@ +import Vue from 'vue'; +import ImportProjectMembersTrigger from '~/invite_members/components/import_project_members_trigger.vue'; + +export default function initImportProjectMembersTrigger() { + const el = document.querySelector('.js-import-project-members-trigger'); + + if (!el) { + return false; + } + + return new Vue({ + el, + render: (createElement) => + createElement(ImportProjectMembersTrigger, { + props: { + ...el.dataset, + }, + }), + }); +} diff --git a/app/assets/javascripts/pages/projects/project_members/index.js b/app/assets/javascripts/pages/projects/project_members/index.js index 595a285032c..9a7fd74fd8c 100644 --- a/app/assets/javascripts/pages/projects/project_members/index.js +++ b/app/assets/javascripts/pages/projects/project_members/index.js @@ -1,4 +1,5 @@ -import initImportAProjectModal from '~/invite_members/init_import_a_project_modal'; +import initImportProjectMembersTrigger from '~/invite_members/init_import_project_members_trigger'; +import initImportProjectMembersModal from '~/invite_members/init_import_project_members_modal'; import initInviteGroupTrigger from '~/invite_members/init_invite_group_trigger'; import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; import initInviteGroupsModal from '~/invite_members/init_invite_groups_modal'; @@ -9,11 +10,12 @@ import { MEMBER_TYPES } from '~/members/constants'; import { groupLinkRequestFormatter } from '~/members/utils'; import { projectMemberRequestFormatter } from '~/projects/members/utils'; -initImportAProjectModal(); +initImportProjectMembersModal(); initInviteMembersModal(); initInviteGroupsModal(); initInviteMembersTrigger(); initInviteGroupTrigger(); +initImportProjectMembersTrigger(); const SHARED_FIELDS = ['account', 'maxRole', 'expiration', 'actions']; initMembersApp(document.querySelector('.js-project-members-list-app'), { diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/constants.js b/app/assets/javascripts/vue_shared/components/source_viewer/constants.js index 1ce077e81d4..94ebbccb014 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/constants.js +++ b/app/assets/javascripts/vue_shared/components/source_viewer/constants.js @@ -91,7 +91,7 @@ export const ROUGE_TO_HLJS_LANGUAGE_MAP = { scala: 'scala', scheme: 'scheme', scss: 'scss', - shell: 'shell', + shell: 'sh', smalltalk: 'smalltalk', sml: 'sml', sqf: 'sqf', diff --git a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb index cc15bff0916..cbe1cfb4099 100644 --- a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb +++ b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb @@ -21,6 +21,9 @@ module Mutations argument :weight_widget, ::Types::WorkItems::Widgets::WeightInputType, required: false, description: 'Input for weight widget.' + argument :hierarchy_widget, ::Types::WorkItems::Widgets::HierarchyUpdateInputType, + required: false, + description: 'Input for hierarchy widget.' end end end diff --git a/app/graphql/mutations/work_items/update.rb b/app/graphql/mutations/work_items/update.rb index ff4aba4830f..8b1968dbad2 100644 --- a/app/graphql/mutations/work_items/update.rb +++ b/app/graphql/mutations/work_items/update.rb @@ -26,7 +26,7 @@ module Mutations spam_params = ::Spam::SpamParams.new_from_request(request: context[:request]) widget_params = extract_widget_params(work_item, attributes) - ::WorkItems::UpdateService.new( + update_result = ::WorkItems::UpdateService.new( project: work_item.project, current_user: current_user, params: attributes, @@ -37,8 +37,8 @@ module Mutations check_spam_action_response!(work_item) { - work_item: work_item.valid? ? work_item : nil, - errors: errors_on_object(work_item) + work_item: (update_result[:work_item] if update_result[:status] == :success), + errors: Array.wrap(update_result[:message]) } end diff --git a/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb b/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb new file mode 100644 index 00000000000..8870d27ddce --- /dev/null +++ b/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Types + module WorkItems + module Widgets + class HierarchyUpdateInputType < BaseInputObject + graphql_name 'WorkItemWidgetHierarchyUpdateInput' + + argument :parent_id, ::Types::GlobalIDType[::WorkItem], + required: false, + description: 'Global ID of the parent work item.', + prepare: ->(id, _) { id&.model_id } + + argument :children_ids, [::Types::GlobalIDType[::WorkItem]], + required: false, + description: 'Global IDs of children work items.', + prepare: ->(ids, _) { ids.map(&:model_id) } + end + end + end +end diff --git a/app/services/audit_event_service.rb b/app/services/audit_event_service.rb index 97debccfb18..26244a8bcc5 100644 --- a/app/services/audit_event_service.rb +++ b/app/services/audit_event_service.rb @@ -121,12 +121,15 @@ class AuditEventService def log_security_event_to_database return if Gitlab::Database.read_only? - event = AuditEvent.new(base_payload.merge(details: @details)) + event = build_event save_or_track event - event end + def build_event + AuditEvent.new(base_payload.merge(details: @details)) + end + def stream_event_to_external_destinations(_event) # Defined in EE end diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 03115416607..a00a9a2021b 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -282,8 +282,9 @@ class IssuableBaseService < ::BaseProjectService assign_requested_labels(issuable) assign_requested_assignees(issuable) assign_requested_crm_contacts(issuable) + widget_params = filter_widget_params - if issuable.changed? || params.present? + if issuable.changed? || params.present? || widget_params.present? issuable.assign_attributes(allowed_update_params(params)) if has_title_or_description_changed?(issuable) @@ -303,7 +304,7 @@ class IssuableBaseService < ::BaseProjectService ensure_milestone_available(issuable) issuable_saved = issuable.with_transaction_returning_status do - issuable.save(touch: should_touch) + transaction_update(issuable, { save_with_touch: should_touch }) end if issuable_saved @@ -332,6 +333,12 @@ class IssuableBaseService < ::BaseProjectService issuable end + def transaction_update(issuable, opts = {}) + touch = opts[:save_with_touch] || false + + issuable.save(touch: touch) + end + def update_task(issuable) filter_params(issuable) @@ -590,6 +597,10 @@ class IssuableBaseService < ::BaseProjectService issuable_sla.update(issuable_closed: issuable.closed?) end + + def filter_widget_params + params.delete(:widget_params) + end end IssuableBaseService.prepend_mod_with('IssuableBaseService') diff --git a/app/services/issuable_links/create_service.rb b/app/services/issuable_links/create_service.rb index 0887f04760c..aca98596a02 100644 --- a/app/services/issuable_links/create_service.rb +++ b/app/services/issuable_links/create_service.rb @@ -8,6 +8,7 @@ module IssuableLinks @issuable = issuable @current_user = user @params = params.dup + @errors = [] end def execute @@ -22,7 +23,6 @@ module IssuableLinks return error(issuables_not_found_message, 404) end - @errors = [] references = create_links if @errors.present? diff --git a/app/services/work_items/parent_links/create_service.rb b/app/services/work_items/parent_links/create_service.rb new file mode 100644 index 00000000000..87995cc6550 --- /dev/null +++ b/app/services/work_items/parent_links/create_service.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module WorkItems + module ParentLinks + class CreateService < IssuableLinks::CreateService + private + + # rubocop: disable CodeReuse/ActiveRecord + def relate_issuables(work_item) + link = WorkItems::ParentLink.find_or_initialize_by(work_item: work_item) + link.work_item_parent = issuable + + if link.changed? && link.save + create_notes(work_item) + end + + link + end + # rubocop: enable CodeReuse/ActiveRecord + + def linkable_issuables(work_items) + @linkable_issuables ||= begin + return [] unless can?(current_user, :read_work_item, issuable.project) + + work_items.select do |work_item| + linkable?(work_item) + end + end + end + + def linkable?(work_item) + can?(current_user, :update_work_item, work_item) && + !previous_related_issuables.include?(work_item) + end + + def previous_related_issuables + @related_issues ||= issuable.work_item_children.to_a + end + + def extract_references + params[:issuable_references].map do |id| + ::WorkItem.find(id) + rescue ActiveRecord::RecordNotFound + @errors << _("Task with ID: %{id} could not be found.") % { id: id } + nil + end + end + + # TODO: Create system notes when work item's parent or children are updated + # See https://gitlab.com/gitlab-org/gitlab/-/issues/362213 + def create_notes(work_item) + # no-op + end + + def target_issuable_type + issuable.issue_type == 'issue' ? 'task' : issuable.issue_type + end + + def issuables_not_found_message + _('No matching %{issuable} found. Make sure that you are adding a valid %{issuable} ID.' % + { issuable: target_issuable_type }) + end + end + end +end diff --git a/app/services/work_items/update_service.rb b/app/services/work_items/update_service.rb index 7b50040a716..4d34b32096c 100644 --- a/app/services/work_items/update_service.rb +++ b/app/services/work_items/update_service.rb @@ -3,12 +3,26 @@ module WorkItems class UpdateService < ::Issues::UpdateService def initialize(project:, current_user: nil, params: {}, spam_params: nil, widget_params: {}) + params[:widget_params] = true if widget_params.present? + super(project: project, current_user: current_user, params: params, spam_params: nil) @widget_params = widget_params @widget_services = {} end + def execute(work_item) + updated_work_item = super + + if updated_work_item.valid? + success(payload(work_item)) + else + error(updated_work_item.errors.full_messages, :unprocessable_entity, pass_back: payload(updated_work_item)) + end + rescue ::WorkItems::Widgets::BaseService::WidgetError => e + error(e.message, :unprocessable_entity) + end + private def update(work_item) @@ -17,6 +31,12 @@ module WorkItems super end + def transaction_update(work_item, opts = {}) + execute_widgets(work_item: work_item, callback: :before_update_in_transaction) + + super + end + def after_update(work_item) super @@ -30,15 +50,17 @@ module WorkItems end def widget_service(widget) - service_class = begin - "WorkItems::Widgets::#{widget.type.capitalize}Service::UpdateService".constantize - rescue NameError - nil - end + @widget_services[widget] ||= widget_service_class(widget)&.new(widget: widget, current_user: current_user) + end - return unless service_class + def widget_service_class(widget) + "WorkItems::Widgets::#{widget.type.capitalize}Service::UpdateService".constantize + rescue NameError + nil + end - @widget_services[widget] ||= service_class.new(widget: widget, current_user: current_user) + def payload(work_item) + { work_item: work_item } end end end diff --git a/app/services/work_items/widgets/base_service.rb b/app/services/work_items/widgets/base_service.rb index 72debc272bd..037733bbed5 100644 --- a/app/services/work_items/widgets/base_service.rb +++ b/app/services/work_items/widgets/base_service.rb @@ -3,6 +3,8 @@ module WorkItems module Widgets class BaseService < ::BaseService + WidgetError = Class.new(StandardError) + attr_reader :widget, :current_user def initialize(widget:, current_user:) diff --git a/app/services/work_items/widgets/hierarchy_service/base_service.rb b/app/services/work_items/widgets/hierarchy_service/base_service.rb new file mode 100644 index 00000000000..d385074a9a6 --- /dev/null +++ b/app/services/work_items/widgets/hierarchy_service/base_service.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module WorkItems + module Widgets + module HierarchyService + class BaseService < WorkItems::Widgets::BaseService + private + + def update_work_item_parent(parent_id) + begin + parent = ::WorkItem.find(parent_id) + rescue ActiveRecord::RecordNotFound + return parent_not_found_error(parent_id) + end + + ::WorkItems::ParentLinks::CreateService + .new(parent, current_user, { target_issuable: widget.work_item }) + .execute + end + + def update_work_item_children(children_ids) + ::WorkItems::ParentLinks::CreateService + .new(widget.work_item, current_user, { issuable_references: children_ids }) + .execute + end + + def parent_not_found_error(id) + error(_('No Work Item found with ID: %{id}.' % { id: id })) + end + end + end + end +end diff --git a/app/services/work_items/widgets/hierarchy_service/update_service.rb b/app/services/work_items/widgets/hierarchy_service/update_service.rb new file mode 100644 index 00000000000..5b6fea6aa1e --- /dev/null +++ b/app/services/work_items/widgets/hierarchy_service/update_service.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module WorkItems + module Widgets + module HierarchyService + class UpdateService < WorkItems::Widgets::HierarchyService::BaseService + def before_update_in_transaction(params:) + return unless params.present? + + result = handle_hierarchy_changes(params) + + raise WidgetError, result[:message] if result[:status] == :error + end + + private + + def handle_hierarchy_changes(params) + return feature_flag_error unless feature_flag_enabled? + return incompatible_args_error if incompatible_args?(params) + + update_hierarchy(params) + end + + def update_hierarchy(params) + parent_id = params.delete(:parent_id) + children_ids = params.delete(:children_ids) + + return update_work_item_parent(parent_id) if parent_id + + update_work_item_children(children_ids) if children_ids + end + + def feature_flag_enabled? + Feature.enabled?(:work_items_hierarchy, widget.work_item&.project) + end + + def incompatible_args?(params) + params[:parent_id] && params[:children_ids] + end + + def feature_flag_error + error(_('`work_items_hierarchy` feature flag disabled for this project')) + end + + def incompatible_args_error + error(_('A Work Item can be a parent or a child, but not both.')) + end + end + end + end +end diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index fd91786e9f9..7e2dfc94bb4 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -15,7 +15,8 @@ .gl-display-flex.gl-flex-wrap.gl-align-items-flex-start.gl-ml-auto.gl-md-w-auto.gl-w-full.gl-mt-3 - invite_group_top_margin = '' - if can_admin_project_member?(@project) - .js-import-a-project-modal{ data: { project_id: @project.id, project_name: @project.name } } + .js-import-project-members-trigger{ data: { classes: 'gl-md-w-auto gl-w-full' } } + .js-import-project-members-modal{ data: { project_id: @project.id, project_name: @project.name } } - invite_group_top_margin = 'gl-md-mt-0 gl-mt-3' - if @project.allowed_to_share_with_group? .js-invite-group-trigger{ data: { classes: "gl-md-w-auto gl-w-full gl-md-ml-3 #{invite_group_top_margin}", display_text: _('Invite a group') } } diff --git a/config/metrics/counts_28d/20210216183203_product_analytics_test_metrics_union.yml b/config/metrics/counts_28d/20210216183203_product_analytics_test_metrics_union.yml index e51ac640710..d617f3dcebd 100644 --- a/config/metrics/counts_28d/20210216183203_product_analytics_test_metrics_union.yml +++ b/config/metrics/counts_28d/20210216183203_product_analytics_test_metrics_union.yml @@ -2,8 +2,8 @@ data_category: optional key_path: counts_monthly.aggregated_metrics.product_analytics_test_metrics_union description: This was test metric used for purpose of assuring correct implementation of aggregated metrics feature -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: number diff --git a/config/metrics/counts_28d/20210216183205_product_analytics_test_metrics_intersection.yml b/config/metrics/counts_28d/20210216183205_product_analytics_test_metrics_intersection.yml index 195f14de62f..b5c1114ddbb 100644 --- a/config/metrics/counts_28d/20210216183205_product_analytics_test_metrics_intersection.yml +++ b/config/metrics/counts_28d/20210216183205_product_analytics_test_metrics_intersection.yml @@ -2,8 +2,8 @@ data_category: optional key_path: counts_monthly.aggregated_metrics.product_analytics_test_metrics_intersection description: This was test metric used for purpose of assuring correct implementation of aggregated metrics feature -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: number diff --git a/config/metrics/counts_28d/20210216184140_testing_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184140_testing_total_unique_counts_monthly.yml index 5603a8f0885..9c8ce1a1f06 100644 --- a/config/metrics/counts_28d/20210216184140_testing_total_unique_counts_monthly.yml +++ b/config/metrics/counts_28d/20210216184140_testing_total_unique_counts_monthly.yml @@ -2,8 +2,8 @@ data_category: optional key_path: redis_hll_counters.testing.testing_total_unique_counts_monthly description: Total users for events under testing category -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence value_type: number status: removed diff --git a/config/metrics/counts_28d/20210916080405_promoted_issues.yml b/config/metrics/counts_28d/20210916080405_promoted_issues.yml index feef83f3e04..5c1ad6446f0 100644 --- a/config/metrics/counts_28d/20210916080405_promoted_issues.yml +++ b/config/metrics/counts_28d/20210916080405_promoted_issues.yml @@ -2,8 +2,8 @@ key_path: counts_monthly.promoted_issues name: count_promoted_issues description: Count of issues promoted to epics -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: number diff --git a/config/metrics/counts_7d/20210216183213_product_analytics_test_metrics_union.yml b/config/metrics/counts_7d/20210216183213_product_analytics_test_metrics_union.yml index d268d1260d5..6d7b16b9f3d 100644 --- a/config/metrics/counts_7d/20210216183213_product_analytics_test_metrics_union.yml +++ b/config/metrics/counts_7d/20210216183213_product_analytics_test_metrics_union.yml @@ -2,8 +2,8 @@ data_category: optional key_path: counts_weekly.aggregated_metrics.product_analytics_test_metrics_union description: This was test metric used for purpose of assuring correct implementation of aggregated metrics feature -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: number diff --git a/config/metrics/counts_7d/20210216183215_product_analytics_test_metrics_intersection.yml b/config/metrics/counts_7d/20210216183215_product_analytics_test_metrics_intersection.yml index 3a8d6c7e74e..46295dcdb00 100644 --- a/config/metrics/counts_7d/20210216183215_product_analytics_test_metrics_intersection.yml +++ b/config/metrics/counts_7d/20210216183215_product_analytics_test_metrics_intersection.yml @@ -2,8 +2,8 @@ data_category: optional key_path: counts_weekly.aggregated_metrics.product_analytics_test_metrics_intersection description: This was test metric used for purpose of assuring correct implementation of aggregated metrics feature -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: number diff --git a/config/metrics/counts_all/20220314362302_service_usage_data_download_payload.yml b/config/metrics/counts_all/20220314362302_service_usage_data_download_payload.yml index 69303f9e016..1b26202c5ef 100644 --- a/config/metrics/counts_all/20220314362302_service_usage_data_download_payload.yml +++ b/config/metrics/counts_all/20220314362302_service_usage_data_download_payload.yml @@ -2,9 +2,9 @@ key_path: counts.service_usage_data_download_payload_click description: Count Download Payload button clicks data_category: optional -name: count_promoted_issues -product_section: growth -product_stage: growth +name: service_usage_data_download_payload_click +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: number diff --git a/config/metrics/license/20210201124932_recorded_at.yml b/config/metrics/license/20210201124932_recorded_at.yml index d98bc41aa75..c9d98a96727 100644 --- a/config/metrics/license/20210201124932_recorded_at.yml +++ b/config/metrics/license/20210201124932_recorded_at.yml @@ -2,8 +2,8 @@ data_category: standard key_path: recorded_at description: When the Usage Ping computation was started -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: string diff --git a/config/metrics/license/20210201124933_uuid.yml b/config/metrics/license/20210201124933_uuid.yml index f1fb9ac8005..f864c2be37a 100644 --- a/config/metrics/license/20210201124933_uuid.yml +++ b/config/metrics/license/20210201124933_uuid.yml @@ -1,8 +1,8 @@ --- key_path: uuid description: GitLab instance unique identifier -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: string diff --git a/config/metrics/license/20210204124827_hostname.yml b/config/metrics/license/20210204124827_hostname.yml index 5959c7d1496..f545ae0c061 100644 --- a/config/metrics/license/20210204124827_hostname.yml +++ b/config/metrics/license/20210204124827_hostname.yml @@ -1,8 +1,8 @@ --- key_path: hostname description: Host name of GitLab instance -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: string diff --git a/config/metrics/license/20210204124829_active_user_count.yml b/config/metrics/license/20210204124829_active_user_count.yml index 38bc756b9a7..037ac7b5a03 100644 --- a/config/metrics/license/20210204124829_active_user_count.yml +++ b/config/metrics/license/20210204124829_active_user_count.yml @@ -1,8 +1,8 @@ --- key_path: active_user_count description: The number of active users existing in the instance. This is named the instance_user_count in the Versions application. -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: number diff --git a/config/metrics/license/20210204124938_recording_ce_finished_at.yml b/config/metrics/license/20210204124938_recording_ce_finished_at.yml index 8ef26361838..54728ff8206 100644 --- a/config/metrics/license/20210204124938_recording_ce_finished_at.yml +++ b/config/metrics/license/20210204124938_recording_ce_finished_at.yml @@ -2,8 +2,8 @@ data_category: standard key_path: recording_ce_finished_at description: When the core features were computed -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: string diff --git a/config/metrics/settings/20210702140138_collected_data_categories.yml b/config/metrics/settings/20210702140138_collected_data_categories.yml index 6fed28feff7..006a77f8b29 100644 --- a/config/metrics/settings/20210702140138_collected_data_categories.yml +++ b/config/metrics/settings/20210702140138_collected_data_categories.yml @@ -2,8 +2,8 @@ key_path: settings.collected_data_categories name: collected_data_categories description: List of collected data categories corresponding to instance settings -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: object diff --git a/config/metrics/settings/20210915152326_service_ping_features_enabled.yml b/config/metrics/settings/20210915152326_service_ping_features_enabled.yml index 6412e97d8a8..d7e1a7f68bf 100644 --- a/config/metrics/settings/20210915152326_service_ping_features_enabled.yml +++ b/config/metrics/settings/20210915152326_service_ping_features_enabled.yml @@ -2,8 +2,8 @@ key_path: settings.service_ping_features_enabled name: "service_ping_features_enabled" description: Whether Service Ping features are enabled -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: collection value_type: boolean diff --git a/config/metrics/settings/20211124061450_snowplow_enabled.yml b/config/metrics/settings/20211124061450_snowplow_enabled.yml index dc75a4a6e1e..14662665cf7 100644 --- a/config/metrics/settings/20211124061450_snowplow_enabled.yml +++ b/config/metrics/settings/20211124061450_snowplow_enabled.yml @@ -2,8 +2,8 @@ key_path: settings.snowplow_enabled name: snowplow_enabled_gitlab_instance description: Whether snowplow is enabled for the GitLab instance -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: product intelligence value_type: boolean diff --git a/config/metrics/settings/20211124085521_snowplow_configured_to_gitlab_collector_hostname.yml b/config/metrics/settings/20211124085521_snowplow_configured_to_gitlab_collector_hostname.yml index 49c1a2ae448..c634facdd92 100644 --- a/config/metrics/settings/20211124085521_snowplow_configured_to_gitlab_collector_hostname.yml +++ b/config/metrics/settings/20211124085521_snowplow_configured_to_gitlab_collector_hostname.yml @@ -2,8 +2,8 @@ key_path: settings.snowplow_configured_to_gitlab_collector name: snowplow_configured_to_gitlab_collector description: Metric informs if currently configured Snowplow collector hostname points towards Gitlab Snowplow collection pipeline. -product_section: growth -product_stage: growth +product_section: analytics +product_stage: analytics product_group: product_intelligence product_category: product intelligence value_type: boolean diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index 7901fcaf78d..6e92c9a3c2b 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -1351,6 +1351,9 @@ To configure the Praefect nodes, on each one: on the page. 1. Edit the `/etc/gitlab/gitlab.rb` file to configure Praefect: + NOTE: + You can't remove the `default` entry from `virtual_storages` because [GitLab requires it](../gitaly/configure_gitaly.md#gitlab-requires-a-default-repository-storage). +