diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-27 18:10:01 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-27 18:10:01 +0300 |
commit | 6d82b3a0c58f427e90bb8665cd13931128753a23 (patch) | |
tree | f89d05cab91e0b56d6fae7194c8b048d75314846 /app/assets/javascripts | |
parent | 863ba7d77355b305b06112b0c6c3cab3c09898b0 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts')
7 files changed, 243 insertions, 1 deletions
diff --git a/app/assets/javascripts/clusters/agents/components/revoke_token_button.vue b/app/assets/javascripts/clusters/agents/components/revoke_token_button.vue new file mode 100644 index 00000000000..7d36cbb170d --- /dev/null +++ b/app/assets/javascripts/clusters/agents/components/revoke_token_button.vue @@ -0,0 +1,201 @@ +<script> +import { + GlButton, + GlModalDirective, + GlTooltip, + GlModal, + GlFormGroup, + GlFormInput, + GlSprintf, +} from '@gitlab/ui'; +import { s__, __, sprintf } from '~/locale'; +import { REVOKE_TOKEN_MODAL_ID, TOKEN_STATUS_ACTIVE } from '../constants'; +import revokeAgentToken from '../graphql/mutations/revoke_token.mutation.graphql'; +import getClusterAgentQuery from '../graphql/queries/get_cluster_agent.query.graphql'; +import { removeTokenFromStore } from '../graphql/cache_update'; + +export default { + components: { + GlButton, + GlTooltip, + GlModal, + GlFormGroup, + GlFormInput, + GlSprintf, + }, + directives: { + GlModalDirective, + }, + inject: ['agentName', 'projectPath', 'canAdminCluster'], + props: { + token: { + required: true, + type: Object, + validator: (value) => ['id', 'name'].every((prop) => value[prop]), + }, + cursor: { + required: true, + type: Object, + }, + }, + i18n: { + revokeButton: s__('ClusterAgents|Revoke token'), + dropdownDisabledHint: s__( + 'ClusterAgents|Requires a Maintainer or greater role to perform this action', + ), + modalTitle: s__('ClusterAgents|Revoke access token?'), + modalBody: s__( + 'ClusterAgents|Are you sure you want to revoke this token? You cannot undo this action.', + ), + modalInputLabel: s__('ClusterAgents|To revoke the token, type %{name} to confirm:'), + modalCancel: __('Cancel'), + successMessage: s__('ClusterAgents|%{name} successfully revoked'), + defaultError: __('An error occurred. Please try again.'), + }, + data() { + return { + loading: false, + error: null, + revokeConfirmText: null, + tokenName: null, + variables: { + agentName: this.agentName, + projectPath: this.projectPath, + tokenStatus: TOKEN_STATUS_ACTIVE, + ...this.cursor, + }, + }; + }, + computed: { + revokeBtnDisabled() { + return this.loading || !this.canAdminCluster; + }, + modalId() { + return sprintf(REVOKE_TOKEN_MODAL_ID, { + tokenName: this.token.name, + }); + }, + primaryModalProps() { + return { + text: this.$options.i18n.revokeButton, + attributes: [ + { disabled: this.loading || this.disableModalSubmit, loading: this.loading }, + { variant: 'danger' }, + ], + }; + }, + cancelModalProps() { + return { + text: this.$options.i18n.modalCancel, + attributes: [], + }; + }, + disableModalSubmit() { + return this.revokeConfirmText !== this.token.name; + }, + }, + methods: { + async revokeToken() { + if (this.disableModalSubmit || this.loading) { + return; + } + + this.loading = true; + this.error = null; + this.tokenName = this.token.name; + + try { + const { errors } = await this.revokeTokenMutation(); + + if (errors.length) { + throw new Error(errors[0]); + } + } catch (error) { + this.error = error?.message || this.$options.i18n.defaultError; + } finally { + this.loading = false; + const successMessage = sprintf(this.$options.i18n.successMessage, { + name: this.tokenName, + }); + + this.$toast.show(this.error || successMessage); + + this.hideModal(); + } + }, + revokeTokenMutation() { + return this.$apollo + .mutate({ + mutation: revokeAgentToken, + variables: { + input: { + id: this.token.id, + }, + }, + update: (store) => { + removeTokenFromStore(store, this.token, getClusterAgentQuery, this.variables); + }, + }) + + .then(({ data: { clusterAgentTokenRevoke } }) => { + return clusterAgentTokenRevoke; + }); + }, + resetModal() { + this.loading = false; + this.error = null; + this.revokeConfirmText = null; + }, + hideModal() { + this.resetModal(); + this.$refs.modal.hide(); + }, + }, +}; +</script> + +<template> + <div> + <div ref="revokeToken" class="gl-display-inline-block"> + <gl-button + v-gl-modal-directive="modalId" + icon="remove" + category="secondary" + variant="danger" + :disabled="revokeBtnDisabled" + :title="$options.i18n.revokeButton" + :aria-label="$options.i18n.revokeButton" + /> + + <gl-tooltip + v-if="!canAdminCluster" + :target="() => $refs.revokeToken" + :title="$options.i18n.dropdownDisabledHint" + /> + </div> + + <gl-modal + ref="modal" + :modal-id="modalId" + :title="$options.i18n.modalTitle" + :action-primary="primaryModalProps" + :action-cancel="cancelModalProps" + size="sm" + @primary="revokeToken" + @hide="hideModal" + > + <p>{{ $options.i18n.modalBody }}</p> + + <gl-form-group> + <template #label> + <gl-sprintf :message="$options.i18n.modalInputLabel"> + <template #name> + <code>{{ token.name }}</code> + </template> + </gl-sprintf> + </template> + <gl-form-input v-model="revokeConfirmText" @keydown.enter="revokeToken" /> + </gl-form-group> + </gl-modal> + </div> +</template> diff --git a/app/assets/javascripts/clusters/agents/components/token_table.vue b/app/assets/javascripts/clusters/agents/components/token_table.vue index fbb39c28d78..9e64c9da712 100644 --- a/app/assets/javascripts/clusters/agents/components/token_table.vue +++ b/app/assets/javascripts/clusters/agents/components/token_table.vue @@ -3,6 +3,7 @@ import { GlEmptyState, GlTable, GlTooltip, GlTruncate } from '@gitlab/ui'; import { s__ } from '~/locale'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import CreateTokenButton from './create_token_button.vue'; +import RevokeTokenButton from './revoke_token_button.vue'; export default { components: { @@ -12,6 +13,7 @@ export default { GlTruncate, TimeAgoTooltip, CreateTokenButton, + RevokeTokenButton, }, i18n: { createdBy: s__('ClusterAgents|Created by'), @@ -66,6 +68,11 @@ export default { label: this.$options.i18n.description, tdAttr: { 'data-testid': 'agent-token-description' }, }, + { + key: 'actions', + label: '', + tdAttr: { 'data-testid': 'agent-token-revoke' }, + }, ]; }, }, @@ -119,6 +126,10 @@ export default { </gl-tooltip> </div> </template> + + <template #cell(actions)="{ item }"> + <revoke-token-button :token="item" :cluster-agent-id="clusterAgentId" :cursor="cursor" /> + </template> </gl-table> </div> diff --git a/app/assets/javascripts/clusters/agents/constants.js b/app/assets/javascripts/clusters/agents/constants.js index 50d8f5e9e40..962fa243903 100644 --- a/app/assets/javascripts/clusters/agents/constants.js +++ b/app/assets/javascripts/clusters/agents/constants.js @@ -44,3 +44,5 @@ export const EVENT_ACTIONS_OPEN = 'open_modal'; export const EVENT_ACTIONS_CLICK = 'click_button'; export const TOKEN_NAME_LIMIT = 255; + +export const REVOKE_TOKEN_MODAL_ID = 'revoke-token-%{tokenName}'; diff --git a/app/assets/javascripts/clusters/agents/graphql/cache_update.js b/app/assets/javascripts/clusters/agents/graphql/cache_update.js index 0219c4150eb..8db79c82708 100644 --- a/app/assets/javascripts/clusters/agents/graphql/cache_update.js +++ b/app/assets/javascripts/clusters/agents/graphql/cache_update.js @@ -22,3 +22,25 @@ export function addAgentTokenToStore(store, clusterAgentTokenCreate, query, vari }); } } + +export function removeTokenFromStore(store, revokeToken, query, variables) { + if (!hasErrors(revokeToken)) { + const sourceData = store.readQuery({ + query, + variables, + }); + + const data = produce(sourceData, (draftData) => { + draftData.project.clusterAgent.tokens.nodes = draftData.project.clusterAgent.tokens.nodes.filter( + ({ id }) => id !== revokeToken.id, + ); + draftData.project.clusterAgent.tokens.count -= 1; + }); + + store.writeQuery({ + query, + variables, + data, + }); + } +} diff --git a/app/assets/javascripts/clusters/agents/graphql/mutations/revoke_token.mutation.graphql b/app/assets/javascripts/clusters/agents/graphql/mutations/revoke_token.mutation.graphql new file mode 100644 index 00000000000..6f1c6a66690 --- /dev/null +++ b/app/assets/javascripts/clusters/agents/graphql/mutations/revoke_token.mutation.graphql @@ -0,0 +1,5 @@ +mutation revokeAgentToken($input: ClusterAgentTokenRevokeInput!) { + clusterAgentTokenRevoke(input: $input) { + errors + } +} diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue index 4dfd672f99b..8a5325cf218 100644 --- a/app/assets/javascripts/diffs/components/compare_versions.vue +++ b/app/assets/javascripts/diffs/components/compare_versions.vue @@ -79,7 +79,7 @@ export default { </script> <template> - <div class="mr-version-controls border-top"> + <div class="mr-version-controls"> <div class="mr-version-menus-container content-block"> <gl-button v-if="hasChanges" diff --git a/app/assets/javascripts/security_configuration/index.js b/app/assets/javascripts/security_configuration/index.js index 65cf1ec27a3..dcc41a38067 100644 --- a/app/assets/javascripts/security_configuration/index.js +++ b/app/assets/javascripts/security_configuration/index.js @@ -37,6 +37,7 @@ export const initSecurityConfiguration = (el) => { return new Vue({ el, apolloProvider, + name: 'SecurityConfigurationRoot', provide: { projectFullPath, upgradePath, |