diff options
Diffstat (limited to 'app/assets/javascripts/token_access/components/token_access.vue')
-rw-r--r-- | app/assets/javascripts/token_access/components/token_access.vue | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/app/assets/javascripts/token_access/components/token_access.vue b/app/assets/javascripts/token_access/components/token_access.vue new file mode 100644 index 00000000000..24565c441d8 --- /dev/null +++ b/app/assets/javascripts/token_access/components/token_access.vue @@ -0,0 +1,206 @@ +<script> +import { GlButton, GlCard, GlFormInput, GlLoadingIcon, GlToggle } from '@gitlab/ui'; +import createFlash from '~/flash'; +import { __, s__ } from '~/locale'; +import addProjectCIJobTokenScopeMutation from '../graphql/mutations/add_project_ci_job_token_scope.mutation.graphql'; +import removeProjectCIJobTokenScopeMutation from '../graphql/mutations/remove_project_ci_job_token_scope.mutation.graphql'; +import updateCIJobTokenScopeMutation from '../graphql/mutations/update_ci_job_token_scope.mutation.graphql'; +import getCIJobTokenScopeQuery from '../graphql/queries/get_ci_job_token_scope.query.graphql'; +import getProjectsWithCIJobTokenScopeQuery from '../graphql/queries/get_projects_with_ci_job_token_scope.query.graphql'; +import TokenProjectsTable from './token_projects_table.vue'; + +export default { + i18n: { + toggleLabelTitle: s__('CICD|Limit CI_JOB_TOKEN access'), + toggleHelpText: s__( + `CICD|Select projects that can be accessed by API requests authenticated with this project's CI_JOB_TOKEN CI/CD variable.`, + ), + cardHeaderTitle: s__('CICD|Add an existing project to the scope'), + addProject: __('Add project'), + cancel: __('Cancel'), + addProjectPlaceholder: __('Paste project path (i.e. gitlab-org/gitlab)'), + projectsFetchError: __('There was a problem fetching the projects'), + scopeFetchError: __('There was a problem fetching the job token scope value'), + }, + components: { + GlButton, + GlCard, + GlFormInput, + GlLoadingIcon, + GlToggle, + TokenProjectsTable, + }, + inject: { + fullPath: { + default: '', + }, + }, + apollo: { + jobTokenScopeEnabled: { + query: getCIJobTokenScopeQuery, + variables() { + return { + fullPath: this.fullPath, + }; + }, + update(data) { + return data.project.ciCdSettings.jobTokenScopeEnabled; + }, + error() { + createFlash({ message: this.$options.i18n.scopeFetchError }); + }, + }, + projects: { + query: getProjectsWithCIJobTokenScopeQuery, + variables() { + return { + fullPath: this.fullPath, + }; + }, + update(data) { + return data.project?.ciJobTokenScope?.projects?.nodes ?? []; + }, + error() { + createFlash({ message: this.$options.i18n.projectsFetchError }); + }, + }, + }, + data() { + return { + jobTokenScopeEnabled: null, + targetProjectPath: '', + projects: [], + }; + }, + computed: { + isProjectPathEmpty() { + return this.targetProjectPath === ''; + }, + }, + methods: { + async updateCIJobTokenScope() { + try { + const { + data: { + ciCdSettingsUpdate: { errors }, + }, + } = await this.$apollo.mutate({ + mutation: updateCIJobTokenScopeMutation, + variables: { + input: { + fullPath: this.fullPath, + jobTokenScopeEnabled: this.jobTokenScopeEnabled, + }, + }, + }); + + if (errors.length) { + throw new Error(errors[0]); + } + } catch (error) { + createFlash({ message: error }); + } finally { + if (this.jobTokenScopeEnabled) { + this.getProjects(); + } + } + }, + async addProject() { + try { + const { + data: { + ciJobTokenScopeAddProject: { errors }, + }, + } = await this.$apollo.mutate({ + mutation: addProjectCIJobTokenScopeMutation, + variables: { + input: { + projectPath: this.fullPath, + targetProjectPath: this.targetProjectPath, + }, + }, + }); + + if (errors.length) { + throw new Error(errors[0]); + } + } catch (error) { + createFlash({ message: error }); + } finally { + this.clearTargetProjectPath(); + this.getProjects(); + } + }, + async removeProject(removeTargetPath) { + try { + const { + data: { + ciJobTokenScopeRemoveProject: { errors }, + }, + } = await this.$apollo.mutate({ + mutation: removeProjectCIJobTokenScopeMutation, + variables: { + input: { + projectPath: this.fullPath, + targetProjectPath: removeTargetPath, + }, + }, + }); + + if (errors.length) { + throw new Error(errors[0]); + } + } catch (error) { + createFlash({ message: error }); + } finally { + this.getProjects(); + } + }, + clearTargetProjectPath() { + this.targetProjectPath = ''; + }, + getProjects() { + this.$apollo.queries.projects.refetch(); + }, + }, +}; +</script> +<template> + <div> + <gl-loading-icon v-if="$apollo.loading" size="md" class="gl-mt-5" /> + <template v-else> + <gl-toggle + v-model="jobTokenScopeEnabled" + :label="$options.i18n.toggleLabelTitle" + :help="$options.i18n.toggleHelpText" + @change="updateCIJobTokenScope" + /> + <div v-if="jobTokenScopeEnabled" data-testid="token-section"> + <gl-card class="gl-mt-5"> + <template #header> + <h5 class="gl-my-0">{{ $options.i18n.cardHeaderTitle }}</h5> + </template> + <template #default> + <gl-form-input + v-model="targetProjectPath" + :placeholder="$options.i18n.addProjectPlaceholder" + /> + </template> + <template #footer> + <gl-button + variant="confirm" + :disabled="isProjectPathEmpty" + data-testid="add-project-button" + @click="addProject" + > + {{ $options.i18n.addProject }} + </gl-button> + <gl-button @click="clearTargetProjectPath">{{ $options.i18n.cancel }}</gl-button> + </template> + </gl-card> + + <token-projects-table :projects="projects" @removeProject="removeProject" /> + </div> + </template> + </div> +</template> |