diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-16 00:09:52 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-16 00:09:52 +0300 |
commit | a2fd863d3be23fee66ec7036b8febb2605c17767 (patch) | |
tree | 27eb2c778723397d6326c72cd13e953bf9cb2564 /app | |
parent | efbd20fd1ecd091f1270f73d617822a32c94376f (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
7 files changed, 133 insertions, 35 deletions
diff --git a/app/assets/javascripts/invite_members/components/invite_members_modal.vue b/app/assets/javascripts/invite_members/components/invite_members_modal.vue index 674058bcaf0..87f1ed31a7f 100644 --- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue +++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue @@ -6,6 +6,9 @@ import { GlLink, GlSprintf, GlFormCheckboxGroup, + GlButton, + GlCollapse, + GlIcon, } from '@gitlab/ui'; import { partition, isString, uniqueId, isEmpty } from 'lodash'; import InviteModalBase from 'ee_else_ce/invite_members/components/invite_modal_base.vue'; @@ -13,7 +16,7 @@ import Api from '~/api'; import ExperimentTracking from '~/experimentation/experiment_tracking'; import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants'; import { getParameterValues } from '~/lib/utils/url_utility'; -import { n__ } from '~/locale'; +import { n__, sprintf } from '~/locale'; import { CLOSE_TO_LIMIT_COUNT, USERS_FILTER_ALL, @@ -38,6 +41,9 @@ export default { GlDropdownItem, GlSprintf, GlFormCheckboxGroup, + GlButton, + GlCollapse, + GlIcon, InviteModalBase, MembersTokenSelect, ModalConfetti, @@ -110,6 +116,8 @@ export default { mode: 'default', // Kept in sync with "base" selectedAccessLevel: undefined, + errorsLimit: 2, + isErrorsSectionExpanded: false, }; }, computed: { @@ -135,7 +143,7 @@ export default { return n__( "InviteMembersModal|The following member couldn't be invited", "InviteMembersModal|The following %d members couldn't be invited", - Object.keys(this.invalidMembers).length, + this.errorList.length, ); }, tasksToBeDoneEnabled() { @@ -187,6 +195,29 @@ export default { ? this.$options.labels.placeHolderDisabled : this.$options.labels.placeHolder; }, + errorList() { + return Object.entries(this.invalidMembers).map(([member, error]) => { + return { member, displayedMemberName: this.tokenName(member), message: error }; + }); + }, + errorsLimited() { + return this.errorList.slice(0, this.errorsLimit); + }, + errorsExpanded() { + return this.errorList.slice(this.errorsLimit); + }, + shouldErrorsSectionExpand() { + return Boolean(this.errorsExpanded.length); + }, + errorCollapseText() { + if (this.isErrorsSectionExpanded) { + return this.$options.labels.expandedErrors; + } + + return sprintf(this.$options.labels.collapsedErrors, { + count: this.errorsExpanded.length, + }); + }, }, mounted() { eventHub.$on('openModal', (options) => { @@ -311,6 +342,9 @@ export default { delete this.invalidMembers[memberName(token)]; this.invalidMembers = { ...this.invalidMembers }; }, + toggleErrorExpansion() { + this.isErrorsSectionExpanded = !this.isErrorsSectionExpanded; + }, }, labels: MEMBER_MODAL_LABELS, }; @@ -357,10 +391,36 @@ export default { > {{ $options.labels.memberErrorListText }} <ul class="gl-pl-5 gl-mb-0"> - <li v-for="(error, member) in invalidMembers" :key="member"> - <strong>{{ tokenName(member) }}:</strong> {{ error }} + <li v-for="error in errorsLimited" :key="error.member" data-testid="errors-limited-item"> + <strong>{{ error.displayedMemberName }}:</strong> {{ error.message }} </li> </ul> + <template v-if="shouldErrorsSectionExpand"> + <gl-collapse v-model="isErrorsSectionExpanded"> + <ul class="gl-pl-5 gl-mb-0"> + <li + v-for="error in errorsExpanded" + :key="error.member" + data-testid="errors-expanded-item" + > + <strong>{{ error.displayedMemberName }}:</strong> {{ error.message }} + </li> + </ul> + </gl-collapse> + <gl-button + class="gl-text-decoration-none! gl-shadow-none! gl-mt-3" + data-testid="accordion-button" + variant="link" + @click="toggleErrorExpansion" + > + {{ errorCollapseText }} + <gl-icon + name="chevron-down" + class="gl-transition-medium" + :class="{ 'gl-rotate-180': isErrorsSectionExpanded }" + /> + </gl-button> + </template> </gl-alert> <user-limit-notification v-else diff --git a/app/assets/javascripts/invite_members/constants.js b/app/assets/javascripts/invite_members/constants.js index 6a0be3fc75b..1ceb63e2146 100644 --- a/app/assets/javascripts/invite_members/constants.js +++ b/app/assets/javascripts/invite_members/constants.js @@ -79,6 +79,8 @@ export const HEADER_CLOSE_LABEL = s__('InviteMembersModal|Close invite team memb export const MEMBER_ERROR_LIST_TEXT = s__( 'InviteMembersModal|Review the invite errors and try again:', ); +export const COLLAPSED_ERRORS = s__('InviteMembersModal|Show more (%{count})'); +export const EXPANDED_ERRORS = s__('InviteMembersModal|Show less'); export const MEMBER_MODAL_LABELS = { modal: { @@ -115,6 +117,8 @@ export const MEMBER_MODAL_LABELS = { }, toastMessageSuccessful: TOAST_MESSAGE_SUCCESSFUL, memberErrorListText: MEMBER_ERROR_LIST_TEXT, + collapsedErrors: COLLAPSED_ERRORS, + expandedErrors: EXPANDED_ERRORS, }; export const GROUP_MODAL_LABELS = { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue index d25d1ade7eb..6c5fc916799 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue @@ -51,9 +51,6 @@ export default { isLoading() { return this.glFeatures.mergeRequestWidgetGraphql && this.$apollo.queries.state.loading; }, - showRebaseWithoutCi() { - return this.glFeatures?.rebaseWithoutCiUi; - }, rebaseInProgress() { if (this.glFeatures.mergeRequestWidgetGraphql) { return this.state.rebaseInProgress; @@ -195,7 +192,6 @@ export default { </template> <template v-if="!isLoading" #actions> <gl-button - v-if="showRebaseWithoutCi" :loading="isMakingRequest" variant="confirm" size="small" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue index 3af4023d8e4..9c8819327e6 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue @@ -3,11 +3,15 @@ import * as Sentry from '@sentry/browser'; import { normalizeHeaders } from '~/lib/utils/common_utils'; import { __ } from '~/locale'; import Poll from '~/lib/utils/poll'; +import StatusIcon from '../extensions/status_icon.vue'; +import { EXTENSION_ICON_NAMES } from '../../constants'; const FETCH_TYPE_COLLAPSED = 'collapsed'; -// const FETCH_TYPE_EXPANDED = 'expanded'; export default { + components: { + StatusIcon, + }, props: { /** * @param {value.collapsed} Object @@ -19,7 +23,8 @@ export default { }, loadingText: { type: String, - required: true, + required: false, + default: __('Loading'), }, errorText: { type: String, @@ -52,16 +57,30 @@ export default { required: false, default: false, }, + statusIconName: { + type: String, + default: 'neutral', + required: false, + validator: (value) => Object.keys(EXTENSION_ICON_NAMES).indexOf(value) > -1, + }, + widgetName: { + type: String, + required: true, + }, }, data() { return { - loading: false, - statusIcon: null, + isLoading: false, error: null, }; }, + watch: { + isLoading(newValue) { + this.$emit('is-loading', newValue); + }, + }, async mounted() { - this.loading = true; + this.isLoading = true; try { await this.fetch(this.fetchCollapsedData, FETCH_TYPE_COLLAPSED); @@ -69,20 +88,14 @@ export default { this.error = this.errorText; } - this.loading = false; + this.isLoading = false; }, methods: { fetch(handler, dataType) { const requests = this.multiPolling ? handler() : [handler]; - const allData = []; const promises = requests.map((request) => { return new Promise((resolve, reject) => { - const setData = (data) => { - this.$emit('input', { ...this.value, [dataType]: data }); - resolve(data); - }; - const poll = new Poll({ resource: { fetchData: () => request(), @@ -95,17 +108,7 @@ export default { return; } - if (this.multiPolling) { - allData.push(response.data); - - if (allData.length === requests.length) { - setData(allData); - } - - return; - } - - setData(response.data); + resolve(response.data); }, errorCallback: (e) => { Sentry.captureException(e); @@ -117,7 +120,9 @@ export default { }); }); - return Promise.all(promises); + return Promise.all(promises).then((data) => { + this.$emit('input', { ...this.value, [dataType]: this.multiPolling ? data : data[0] }); + }); }, }, }; @@ -126,13 +131,18 @@ export default { <template> <section class="media-section" data-testid="widget-extension"> <div class="media gl-p-5"> - <!-- status icon will go here --> + <status-icon + :level="1" + :name="widgetName" + :is-loading="isLoading" + :icon-name="statusIconName" + /> <div class="media-body gl-display-flex gl-flex-direction-row! gl-align-self-center" data-testid="widget-extension-top-level" > <div class="gl-flex-grow-1" data-testid="widget-extension-top-level-summary"> - <slot name="summary">{{ summary }}</slot> + <slot name="summary">{{ isLoading ? loadingText : summary }}</slot> </div> <!-- actions will go here --> <!-- toggle button will go here --> diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index ce5d0c45a1e..870c57fd6f3 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -37,7 +37,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:refactor_mr_widgets_extensions, project) push_frontend_feature_flag(:refactor_code_quality_extension, project) push_frontend_feature_flag(:refactor_mr_widget_test_summary, project) - push_frontend_feature_flag(:rebase_without_ci_ui, project) push_frontend_feature_flag(:issue_assignees_widget, @project) push_frontend_feature_flag(:realtime_labels, project) push_frontend_feature_flag(:refactor_security_extension, @project) diff --git a/app/events/groups/group_deleted_event.rb b/app/events/groups/group_deleted_event.rb new file mode 100644 index 00000000000..d89cce17be9 --- /dev/null +++ b/app/events/groups/group_deleted_event.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Groups + class GroupDeletedEvent < ::Gitlab::EventStore::Event + def schema + { + 'type' => 'object', + 'properties' => { + 'group_id' => { 'type' => 'integer' }, + 'root_namespace_id' => { 'type' => 'integer' } + }, + 'required' => %w[group_id root_namespace_id] + } + end + end +end diff --git a/app/services/groups/destroy_service.rb b/app/services/groups/destroy_service.rb index bcf3110ca21..02a760ccf29 100644 --- a/app/services/groups/destroy_service.rb +++ b/app/services/groups/destroy_service.rb @@ -45,6 +45,8 @@ module Groups .execute(blocking: true) end + publish_event + group end # rubocop: enable CodeReuse/ActiveRecord @@ -91,6 +93,17 @@ module Groups end end # rubocop:enable CodeReuse/ActiveRecord + + def publish_event + event = Groups::GroupDeletedEvent.new( + data: { + group_id: group.id, + root_namespace_id: group.root_ancestor.id + } + ) + + Gitlab::EventStore.publish(event) + end end end |