diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-19 10:33:21 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-19 10:33:21 +0300 |
commit | 36a59d088eca61b834191dacea009677a96c052f (patch) | |
tree | e4f33972dab5d8ef79e3944a9f403035fceea43f /app/assets/javascripts/clusters | |
parent | a1761f15ec2cae7c7f7bbda39a75494add0dfd6f (diff) |
Add latest changes from gitlab-org/gitlab@15-0-stable-eev15.0.0-rc42
Diffstat (limited to 'app/assets/javascripts/clusters')
14 files changed, 295 insertions, 36 deletions
diff --git a/app/assets/javascripts/clusters/agents/components/activity_events_list.vue b/app/assets/javascripts/clusters/agents/components/activity_events_list.vue index 6567ce203bc..18c6503bfb2 100644 --- a/app/assets/javascripts/clusters/agents/components/activity_events_list.vue +++ b/app/assets/javascripts/clusters/agents/components/activity_events_list.vue @@ -28,17 +28,17 @@ export default { }, i18n: { emptyText: s__( - 'ClusterAgents|See Agent activity updates such as tokens created or revoked and clusters connected or not connected.', + 'ClusterAgents|See agent activity updates, like tokens created or revoked and clusters connected or not connected.', ), - emptyTooltip: s__('ClusterAgents|What is GitLab Agent activity?'), + emptyTooltip: s__('ClusterAgents|What is agent activity?'), error: s__( - 'ClusterAgents|An error occurred while retrieving GitLab Agent activity. Reload the page to try again.', + 'ClusterAgents|An error occurred while retrieving agent activity. Reload the page to try again.', ), today: __('Today'), yesterday: __('Yesterday'), }, - emptyHelpLink: helpPagePath('user/clusters/agent/install/index', { - anchor: 'view-agent-activity', + emptyHelpLink: helpPagePath('user/clusters/agent/work_with_agent', { + anchor: 'view-an-agents-activity-information', }), borderClasses: 'gl-border-b-1 gl-border-b-solid gl-border-b-gray-100', apollo: { @@ -68,8 +68,8 @@ export default { }, emptyStateTitle() { return n__( - "ClusterAgents|There's no activity from the past day", - "ClusterAgents|There's no activity from the past %d days", + 'ClusterAgents|No activity occurred in the past day', + 'ClusterAgents|No activity occurred in the past %d days', EVENTS_STORED_DAYS, ); }, @@ -124,7 +124,7 @@ export default { <template> <div> - <gl-loading-icon v-if="isLoading" size="md" /> + <gl-loading-icon v-if="isLoading" size="lg" /> <div v-else-if="hasEvents"> <div 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/show.vue b/app/assets/javascripts/clusters/agents/components/show.vue index 5df3e0811a5..e3de8339325 100644 --- a/app/assets/javascripts/clusters/agents/components/show.vue +++ b/app/assets/javascripts/clusters/agents/components/show.vue @@ -140,7 +140,7 @@ export default { </span> </template> - <gl-loading-icon v-if="isLoading" size="md" class="gl-m-3" /> + <gl-loading-icon v-if="isLoading" size="lg" class="gl-m-3" /> <div v-else> <token-table :tokens="tokens" :cluster-agent-id="clusterAgent.id" :cursor="cursor" /> 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/clusters/components/new_cluster.vue b/app/assets/javascripts/clusters/components/new_cluster.vue index 8f3e2916270..41a33a8459f 100644 --- a/app/assets/javascripts/clusters/components/new_cluster.vue +++ b/app/assets/javascripts/clusters/components/new_cluster.vue @@ -1,6 +1,6 @@ <script> import { GlLink, GlSprintf } from '@gitlab/ui'; -import { mapState } from 'vuex'; +import { helpPagePath } from '~/helpers/help_page_helper'; import { s__ } from '~/locale'; export default { @@ -10,13 +10,11 @@ export default { 'ClusterIntegration|Enter details about your cluster. %{linkStart}How do I use a certificate to connect to my cluster?%{linkEnd}', ), }, + clusterConnectHelpPath: helpPagePath('user/project/clusters/add_existing_cluster'), components: { GlLink, GlSprintf, }, - computed: { - ...mapState(['clusterConnectHelpPath']), - }, }; </script> @@ -26,7 +24,7 @@ export default { <p> <gl-sprintf :message="$options.i18n.information"> <template #link="{ content }"> - <gl-link :href="clusterConnectHelpPath" target="_blank">{{ content }}</gl-link> + <gl-link :href="$options.clusterConnectHelpPath">{{ content }}</gl-link> </template> </gl-sprintf> </p> diff --git a/app/assets/javascripts/clusters/components/remove_cluster_confirmation.vue b/app/assets/javascripts/clusters/components/remove_cluster_confirmation.vue index 98db620e3ab..dca89133931 100644 --- a/app/assets/javascripts/clusters/components/remove_cluster_confirmation.vue +++ b/app/assets/javascripts/clusters/components/remove_cluster_confirmation.vue @@ -159,7 +159,7 @@ export default { ) }}</span> <template #modal-footer> - <gl-button variant="secondary" @click="handleCancel">{{ __('Cancel') }}</gl-button> + <gl-button @click="handleCancel">{{ __('Cancel') }}</gl-button> <template v-if="confirmCleanup"> <gl-button :disabled="!canSubmit" diff --git a/app/assets/javascripts/clusters/forms/components/integration_form.vue b/app/assets/javascripts/clusters/forms/components/integration_form.vue index 3f61a1b18a7..b2a8381f937 100644 --- a/app/assets/javascripts/clusters/forms/components/integration_form.vue +++ b/app/assets/javascripts/clusters/forms/components/integration_form.vue @@ -140,7 +140,7 @@ export default { <div v-if="editable" class="form group gl-display-flex gl-justify-content-end"> <gl-button category="primary" - variant="success" + variant="confirm" type="submit" :disabled="!canSubmit" :aria-disabled="!canSubmit" diff --git a/app/assets/javascripts/clusters/gke_cluster_namespace/index.js b/app/assets/javascripts/clusters/gke_cluster_namespace/index.js new file mode 100644 index 00000000000..2b3dfb99328 --- /dev/null +++ b/app/assets/javascripts/clusters/gke_cluster_namespace/index.js @@ -0,0 +1,37 @@ +/** + * Disables & hides the namespace inputs when the gitlab-managed checkbox is checked/unchecked. + */ + +const setDisabled = (el, isDisabled) => { + if (isDisabled) { + el.classList.add('hidden'); + el.querySelector('input').setAttribute('disabled', true); + } else { + el.classList.remove('hidden'); + el.querySelector('input').removeAttribute('disabled'); + } +}; + +const setState = (glManagedCheckbox) => { + const glManaged = document.querySelector('.js-namespace-prefixed'); + const selfManaged = document.querySelector('.js-namespace'); + + if (glManagedCheckbox.checked) { + setDisabled(glManaged, false); + setDisabled(selfManaged, true); + } else { + setDisabled(glManaged, true); + setDisabled(selfManaged, false); + } +}; + +const initGkeNamespace = () => { + const glManagedCheckbox = document.querySelector('.js-gl-managed'); + + if (glManagedCheckbox) { + setState(glManagedCheckbox); // this is needed in order to set the initial state + glManagedCheckbox.addEventListener('change', () => setState(glManagedCheckbox)); + } +}; + +export default initGkeNamespace; diff --git a/app/assets/javascripts/clusters/new_cluster.js b/app/assets/javascripts/clusters/new_cluster.js index 71f585fd307..4df6872bcc1 100644 --- a/app/assets/javascripts/clusters/new_cluster.js +++ b/app/assets/javascripts/clusters/new_cluster.js @@ -1,17 +1,15 @@ import Vue from 'vue'; import NewCluster from './components/new_cluster.vue'; -import { createStore } from './stores/new_cluster'; export default () => { - const entryPoint = document.querySelector('#js-cluster-new'); + const el = document.querySelector('#js-cluster-new'); - if (!entryPoint) { + if (!el) { return null; } return new Vue({ - el: '#js-cluster-new', - store: createStore(entryPoint.dataset), + el, render(createElement) { return createElement(NewCluster); }, diff --git a/app/assets/javascripts/clusters/stores/new_cluster/index.js b/app/assets/javascripts/clusters/stores/new_cluster/index.js deleted file mode 100644 index 87f1c05fdf9..00000000000 --- a/app/assets/javascripts/clusters/stores/new_cluster/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import Vue from 'vue'; -import Vuex from 'vuex'; -import state from './state'; - -Vue.use(Vuex); - -export const createStore = (initialState) => - new Vuex.Store({ - state: state(initialState), - }); - -export default createStore; diff --git a/app/assets/javascripts/clusters/stores/new_cluster/state.js b/app/assets/javascripts/clusters/stores/new_cluster/state.js deleted file mode 100644 index 1ca1ac8de18..00000000000 --- a/app/assets/javascripts/clusters/stores/new_cluster/state.js +++ /dev/null @@ -1,3 +0,0 @@ -export default (initialState = {}) => ({ - clusterConnectHelpPath: initialState.clusterConnectHelpPath, -}); |