diff --git a/Gemfile b/Gemfile index 9de79a3346a..ed52cf0cd06 100644 --- a/Gemfile +++ b/Gemfile @@ -74,6 +74,7 @@ gem 'devise-pbkdf2-encryptable', '~> 0.0.0', path: 'vendor/gems/devise-pbkdf2-en gem 'bcrypt', '~> 3.1', '>= 3.1.14' # rubocop:todo Gemfile/MissingFeatureCategory gem 'doorkeeper', '~> 5.6', '>= 5.6.6' # rubocop:todo Gemfile/MissingFeatureCategory gem 'doorkeeper-openid_connect', '~> 1.8', '>= 1.8.7' # rubocop:todo Gemfile/MissingFeatureCategory +gem 'doorkeeper-device_authorization_grant', '~> 1.0.0', feature_category: :system_access gem 'rexml', '~> 3.2.6' # rubocop:todo Gemfile/MissingFeatureCategory gem 'ruby-saml', '~> 1.15.0' # rubocop:todo Gemfile/MissingFeatureCategory gem 'omniauth', '~> 2.1.0' # rubocop:todo Gemfile/MissingFeatureCategory diff --git a/Gemfile.checksum b/Gemfile.checksum index f07cfcbb4e9..bce06ea26d1 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -128,6 +128,7 @@ {"name":"docile","version":"1.4.0","platform":"ruby","checksum":"5f1734bde23721245c20c3d723e76c104208e1aa01277a69901ce770f0ebb8d3"}, {"name":"domain_name","version":"0.5.20190701","platform":"ruby","checksum":"000a600454cb4a344769b2f10b531765ea7bd3a304fe47ed12e5ca1eab969851"}, {"name":"doorkeeper","version":"5.6.6","platform":"ruby","checksum":"2344e86c77770526efcda893b5217aa13d1c7eb1b40de840b58b19eb1ff757e0"}, +{"name":"doorkeeper-device_authorization_grant","version":"1.0.3","platform":"ruby","checksum":"94c3ac12a0d50942850ecd58ed64298b397a5e903e8880cb68d4085600932679"}, {"name":"doorkeeper-openid_connect","version":"1.8.7","platform":"ruby","checksum":"71edaf33118deefe25674ba3f8280c32835f057351f70e9beb222c0fd6b8e786"}, {"name":"dotenv","version":"2.7.6","platform":"ruby","checksum":"2451ed5e8e43776d7a787e51d6f8903b98e446146c7ad143d5678cc2c409d547"}, {"name":"dry-cli","version":"1.0.0","platform":"ruby","checksum":"28ead169f872954dd08910eb8ead59cf86cd18b4aab321e8eeefe945749569f0"}, @@ -621,7 +622,7 @@ {"name":"sd_notify","version":"0.1.1","platform":"ruby","checksum":"cbc7ac6caa7cedd26b30a72b5eeb6f36050dc0752df263452ea24fb5a4ad3131"}, {"name":"seed-fu","version":"2.3.7","platform":"ruby","checksum":"f19673443e9af799b730e3d4eca6a89b39e5a36825015dffd00d02ea3365cf74"}, {"name":"selenium-webdriver","version":"4.21.1","platform":"ruby","checksum":"c30b64014532fc5156c60797985f839f36adbe60ff4653e7112b008dc1c83263"}, -{"name":"semver_dialects","version":"3.2.0","platform":"ruby","checksum":"11559c8bd77db40be1e9312598c94c1b1b1e2129785d030a19f0db4b11f5555f"}, +{"name":"semver_dialects","version":"3.2.1","platform":"ruby","checksum":"4547361a8f6589b9cbc4c3f6f52bef19cebb7d3beca719aa9ddb1f63788d4b23"}, {"name":"sentry-rails","version":"5.17.3","platform":"ruby","checksum":"017771c42d739c0ad2213a581ca9d005cf543227bc13662cd1ca9909f2429459"}, {"name":"sentry-ruby","version":"5.17.3","platform":"ruby","checksum":"61791a4b0bb0f95cd87aceeaa1efa6d4ab34d64236c9d5df820478adfe2fbbfc"}, {"name":"sentry-sidekiq","version":"5.17.3","platform":"ruby","checksum":"d0714a218999e41e38127d0c174e0ee62a32b069f92e85b544e0c2125eca2c58"}, diff --git a/Gemfile.lock b/Gemfile.lock index 2dc3de3c483..f7b12287ba5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -503,6 +503,8 @@ GEM unf (>= 0.0.5, < 1.0.0) doorkeeper (5.6.6) railties (>= 5) + doorkeeper-device_authorization_grant (1.0.3) + doorkeeper (~> 5.5) doorkeeper-openid_connect (1.8.7) doorkeeper (>= 5.5, < 5.7) jwt (>= 2.5) @@ -1652,7 +1654,7 @@ GEM rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) - semver_dialects (3.2.0) + semver_dialects (3.2.1) deb_version (~> 1.0.1) pastel (~> 0.8.0) thor (~> 1.3) @@ -1973,6 +1975,7 @@ DEPENDENCIES diffy (~> 3.4) discordrb-webhooks (~> 3.5) doorkeeper (~> 5.6, >= 5.6.6) + doorkeeper-device_authorization_grant (~> 1.0.0) doorkeeper-openid_connect (~> 1.8, >= 1.8.7) duo_api (~> 1.3) ed25519 (~> 1.3.0) diff --git a/app/assets/javascripts/ci/pipeline_details/dag/components/dag_graph.vue b/app/assets/javascripts/ci/pipeline_details/dag/components/dag_graph.vue index b81bdbf8da3..5c2d7b187e8 100644 --- a/app/assets/javascripts/ci/pipeline_details/dag/components/dag_graph.vue +++ b/app/assets/javascripts/ci/pipeline_details/dag/components/dag_graph.vue @@ -28,11 +28,7 @@ export default { containerClasses: ['dag-graph-container', 'gl-display-flex', 'gl-flex-direction-column'].join( ' ', ), - hoverFadeClasses: [ - 'gl-cursor-pointer', - 'gl-duration-slow', - 'gl-transition-timing-function-ease', - ].join(' '), + hoverFadeClasses: ['gl-cursor-pointer', 'gl-duration-slow', 'gl-ease-ease'].join(' '), }, gitLabColorRotation: [ '#e17223', diff --git a/app/assets/javascripts/ci/pipeline_details/graph/components/links_inner.vue b/app/assets/javascripts/ci/pipeline_details/graph/components/links_inner.vue index 89cce7d5787..ef6c09b25a6 100644 --- a/app/assets/javascripts/ci/pipeline_details/graph/components/links_inner.vue +++ b/app/assets/javascripts/ci/pipeline_details/graph/components/links_inner.vue @@ -152,7 +152,7 @@ export default { :key="link.path" :ref="link.ref" :d="link.path" - class="gl-fill-transparent gl-duration-slow gl-transition-timing-function-ease" + class="gl-fill-transparent gl-duration-slow gl-ease-ease" :class="getLinkClasses(link)" :stroke-width="$options.STROKE_WIDTH" /> diff --git a/app/assets/javascripts/ci/pipeline_details/graph/components/stage_column_component.vue b/app/assets/javascripts/ci/pipeline_details/graph/components/stage_column_component.vue index 6236284fab8..2605332774a 100644 --- a/app/assets/javascripts/ci/pipeline_details/graph/components/stage_column_component.vue +++ b/app/assets/javascripts/ci/pipeline_details/graph/components/stage_column_component.vue @@ -218,10 +218,7 @@ export default { :pipeline-id="pipelineId" :stage-name="showStageName ? group.stageName : ''" :css-class-job-name="$options.jobClasses" - :class="[ - { 'gl-opacity-3': isFadedOut(group.name) }, - 'gl-duration-slow gl-transition-timing-function-ease', - ]" + :class="[{ 'gl-opacity-3': isFadedOut(group.name) }, 'gl-duration-slow gl-ease-ease']" @pipelineActionRequestComplete="$emit('refreshPipelineGraph')" @setSkipRetryModal="$emit('setSkipRetryModal')" /> diff --git a/app/assets/javascripts/ci/pipeline_editor/components/graph/job_pill.vue b/app/assets/javascripts/ci/pipeline_editor/components/graph/job_pill.vue index babfca1b50a..26981f63020 100644 --- a/app/assets/javascripts/ci/pipeline_editor/components/graph/job_pill.vue +++ b/app/assets/javascripts/ci/pipeline_editor/components/graph/job_pill.vue @@ -61,7 +61,7 @@ export default {
({ ...user, text: user.name, - username: `@${user.username}`, value: user.id, }); @@ -29,7 +32,7 @@ export default { GlCollapsibleListbox, }, props: { - placeholder: { + sourceUser: { type: Object, required: false, default: () => ({}), @@ -38,6 +41,9 @@ export default { data() { return { + isConfirmLoading: false, + isCancelLoading: false, + isNotifyLoading: false, isLoadingInitial: true, isLoadingMore: false, isValidated: false, @@ -97,7 +103,7 @@ export default { } if (this.selectedUser) { - return this.selectedUser.username; + return `@${this.selectedUser.username}`; } return s__('UserMapping|Select user'); @@ -112,16 +118,16 @@ export default { }, statusIsAwaitingApproval() { - return this.placeholder.status === PLACEHOLDER_STATUS_AWAITING_APPROVAL; + return this.sourceUser.status === PLACEHOLDER_STATUS_AWAITING_APPROVAL; }, statusIsReassigning() { - return this.placeholder.status === PLACEHOLDER_STATUS_REASSIGNING; + return this.sourceUser.status === PLACEHOLDER_STATUS_REASSIGNING; }, }, created() { if (this.statusIsAwaitingApproval || this.statusIsReassigning) { - this.selectedUser = this.placeholder.reassignToUser; + this.selectedUser = this.sourceUser.reassignToUser; } this.debouncedSetSearch = debounce(this.setSearch, DEFAULT_DEBOUNCE_AND_THROTTLE_MS); @@ -176,15 +182,64 @@ export default { }, onNotify() { + this.isNotifyLoading = true; this.$toast.show(s__('UserMapping|Notification email sent.')); + this.isNotifyLoading = false; }, onCancel() { - this.$emit('cancel'); + this.isCancelLoading = true; + this.$apollo + .mutate({ + mutation: importSourceUserCancelReassignmentMutation, + variables: { + id: this.sourceUser.id, + }, + }) + .then(({ data }) => { + const { errors } = getFirstPropertyValue(data); + if (errors?.length) { + createAlert({ message: errors.join() }); + } + }) + .catch(() => { + createAlert({ + message: s__( + 'UserMapping|There was a problem cancelling placeholder user reassignment.', + ), + }); + }) + .finally(() => { + this.isCancelLoading = false; + }); }, onConfirm() { this.isValidated = true; if (!this.userSelectInvalid) { - this.$emit('confirm', this.selectedUserValue); + this.isConfirmLoading = true; + this.$apollo + .mutate({ + mutation: this.selectedUser.id + ? importSourceUserReassignMutation + : importSourceUserKeepAsPlaceholderMutation, + variables: { + id: this.sourceUser.id, + ...(this.selectedUser.id ? { userId: this.selectedUser.id } : {}), + }, + }) + .then(({ data }) => { + const { errors } = getFirstPropertyValue(data); + if (errors?.length) { + createAlert({ message: errors.join() }); + } + }) + .catch(() => { + createAlert({ + message: s__('UserMapping|There was a problem reassigning placeholder user.'), + }); + }) + .finally(() => { + this.isConfirmLoading = false; + }); } }, }, @@ -220,7 +275,7 @@ export default { :size="32" :src="item.avatarUrl" :label="item.text" - :sub-label="item.username" + :sub-label="`@${item.username}`" /> @@ -246,15 +301,28 @@ export default {
- {{ - confirmText - }} + {{ confirmText }} diff --git a/app/assets/javascripts/members/components/placeholders/placeholders_table.vue b/app/assets/javascripts/members/components/placeholders/placeholders_table.vue index 4a137aa139c..77638578e6d 100644 --- a/app/assets/javascripts/members/components/placeholders/placeholders_table.vue +++ b/app/assets/javascripts/members/components/placeholders/placeholders_table.vue @@ -84,17 +84,19 @@ export default { isReassignedItem(item) { return ( - (item.status === PLACEHOLDER_STATUS_KEPT_AS_PLACEHOLDER || - item.status === PLACEHOLDER_STATUS_COMPLETED) && - item.reassignToUser + item.status === PLACEHOLDER_STATUS_KEPT_AS_PLACEHOLDER || + item.status === PLACEHOLDER_STATUS_COMPLETED ); }, + reassginedUser(item) { + if (item.status === PLACEHOLDER_STATUS_KEPT_AS_PLACEHOLDER) { + return item.placeholderUser; + } + if (item.status === PLACEHOLDER_STATUS_COMPLETED) { + return item.reassignToUser; + } - onCancel(item) { - this.$emit('cancel', item); - }, - onConfirm(item, selectedUserId) { - this.$emit('confirm', item, selectedUserId); + return {}; }, }, }; @@ -134,18 +136,13 @@ export default { diff --git a/app/assets/javascripts/members/placeholders/graphql/mutations/cancel_reassignment.mutation.graphql b/app/assets/javascripts/members/placeholders/graphql/mutations/cancel_reassignment.mutation.graphql new file mode 100644 index 00000000000..0ad15241ee1 --- /dev/null +++ b/app/assets/javascripts/members/placeholders/graphql/mutations/cancel_reassignment.mutation.graphql @@ -0,0 +1,10 @@ +#import "../fragments/import_source_user.fragment.graphql" + +mutation cancelReassignment($id: ImportSourceUserID!) { + importSourceUserCancelReassignment(input: { id: $id }) { + errors + importSourceUser { + ...ImportSourceUser + } + } +} diff --git a/app/assets/javascripts/members/placeholders/graphql/mutations/keep_as_placeholder.mutation.graphql b/app/assets/javascripts/members/placeholders/graphql/mutations/keep_as_placeholder.mutation.graphql new file mode 100644 index 00000000000..3f68e9ec796 --- /dev/null +++ b/app/assets/javascripts/members/placeholders/graphql/mutations/keep_as_placeholder.mutation.graphql @@ -0,0 +1,10 @@ +#import "../fragments/import_source_user.fragment.graphql" + +mutation keepAsPlaceholder($id: ImportSourceUserID!) { + importSourceUserKeepAsPlaceholder(input: { id: $id }) { + errors + importSourceUser { + ...ImportSourceUser + } + } +} diff --git a/app/assets/javascripts/members/placeholders/graphql/mutations/reassign.mutation.graphql b/app/assets/javascripts/members/placeholders/graphql/mutations/reassign.mutation.graphql new file mode 100644 index 00000000000..3964e976f87 --- /dev/null +++ b/app/assets/javascripts/members/placeholders/graphql/mutations/reassign.mutation.graphql @@ -0,0 +1,10 @@ +#import "../fragments/import_source_user.fragment.graphql" + +mutation reassignPlaceholder($id: ImportSourceUserID!, $userId: UserID!) { + importSourceUserReassign(input: { id: $id, assigneeUserId: $userId }) { + errors + importSourceUser { + ...ImportSourceUser + } + } +} diff --git a/app/assets/javascripts/token_access/components/token_access_app.vue b/app/assets/javascripts/token_access/components/token_access_app.vue index d2d5e6b2a5a..482a7e49259 100644 --- a/app/assets/javascripts/token_access/components/token_access_app.vue +++ b/app/assets/javascripts/token_access/components/token_access_app.vue @@ -1,17 +1,22 @@