diff options
Diffstat (limited to 'app/assets/javascripts/jobs/components/job/manual_variables_form.vue')
-rw-r--r-- | app/assets/javascripts/jobs/components/job/manual_variables_form.vue | 163 |
1 files changed, 128 insertions, 35 deletions
diff --git a/app/assets/javascripts/jobs/components/job/manual_variables_form.vue b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue index 2f97301979c..d7bbd6daed2 100644 --- a/app/assets/javascripts/jobs/components/job/manual_variables_form.vue +++ b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue @@ -5,15 +5,24 @@ import { GlFormInput, GlButton, GlLink, + GlLoadingIcon, GlSprintf, + GlTooltipDirective, } from '@gitlab/ui'; -import { uniqueId } from 'lodash'; +import { cloneDeep, uniqueId } from 'lodash'; import { mapActions } from 'vuex'; +import { fetchPolicies } from '~/lib/graphql'; +import { createAlert } from '~/flash'; +import { convertToGraphQLId } from '~/graphql_shared/utils'; +import { JOB_GRAPHQL_ERRORS, GRAPHQL_ID_TYPES } from '~/jobs/constants'; import { helpPagePath } from '~/helpers/help_page_helper'; +import { redirectTo } from '~/lib/utils/url_utility'; import { s__ } from '~/locale'; +import GetJob from './graphql/queries/get_job.query.graphql'; +import retryJobWithVariablesMutation from './graphql/mutations/job_retry_with_variables.mutation.graphql'; // This component is a port of ~/jobs/components/job/legacy_manual_variables_form.vue -// It is meant to fetch the job information via GraphQL instead of REST API. +// It is meant to fetch/update the job information via GraphQL instead of REST API. export default { name: 'ManualVariablesForm', @@ -23,59 +32,93 @@ export default { GlFormInput, GlButton, GlLink, + GlLoadingIcon, GlSprintf, }, - props: { - action: { - type: Object, - required: false, - default: null, - validator(value) { - return ( - value === null || - (Object.prototype.hasOwnProperty.call(value, 'path') && - Object.prototype.hasOwnProperty.call(value, 'method') && - Object.prototype.hasOwnProperty.call(value, 'button_title')) - ); + directives: { + GlTooltip: GlTooltipDirective, + }, + inject: ['projectPath'], + apollo: { + variables: { + query: GetJob, + variables() { + return { + fullPath: this.projectPath, + id: convertToGraphQLId(GRAPHQL_ID_TYPES.commitStatus, this.jobId), + }; + }, + fetchPolicy: fetchPolicies.CACHE_AND_NETWORK, + update(data) { + const jobVariables = cloneDeep(data?.project?.job?.manualVariables?.nodes); + return [...jobVariables.reverse(), ...this.variables]; + }, + error() { + createAlert({ message: JOB_GRAPHQL_ERRORS.jobQueryErrorText }); }, }, }, + props: { + isRetryable: { + type: Boolean, + required: true, + }, + jobId: { + type: Number, + required: true, + }, + }, inputTypes: { key: 'key', value: 'value', }, i18n: { + clearInputs: s__('CiVariables|Clear inputs'), + formHelpText: s__( + 'CiVariables|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used as default', + ), header: s__('CiVariables|Variables'), keyLabel: s__('CiVariables|Key'), - valueLabel: s__('CiVariables|Value'), keyPlaceholder: s__('CiVariables|Input variable key'), + runAgainButtonText: s__('CiVariables|Run job again'), + triggerButtonText: s__('CiVariables|Trigger this manual action'), + valueLabel: s__('CiVariables|Value'), valuePlaceholder: s__('CiVariables|Input variable value'), - formHelpText: s__( - 'CiVariables|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used as default', - ), + }, + variableValueKeys: { + rest: 'secret_value', + gql: 'value', }, data() { return { + job: {}, variables: [ { - key: '', - secretValue: '', id: uniqueId(), + key: '', + value: '', }, ], + runAgainBtnDisabled: false, triggerBtnDisabled: false, }; }, computed: { - variableSettings() { - return helpPagePath('ci/variables/index', { anchor: 'add-a-cicd-variable-to-a-project' }); - }, preparedVariables() { - // we need to ensure no empty variables are passed to the API - // and secretValue should be snake_case when passed to the API + // filtering out 'id' along with empty variables to send only key, value in the mutation. + // This will be removed in: https://gitlab.com/gitlab-org/gitlab/-/issues/377268 + return this.variables .filter((variable) => variable.key !== '') - .map(({ key, secretValue }) => ({ key, secret_value: secretValue })); + .map(({ key, value }) => ({ key, [this.valueKey]: value })); + }, + valueKey() { + return this.isRetryable + ? this.$options.variableValueKeys.gql + : this.$options.variableValueKeys.rest; + }, + variableSettings() { + return helpPagePath('ci/variables/index', { anchor: 'add-a-cicd-variable-to-a-project' }); }, }, methods: { @@ -88,9 +131,9 @@ export default { } this.variables.push({ - key: '', - secret_value: '', id: uniqueId(), + key: '', + value: '', }); }, canRemove(index) { @@ -105,7 +148,34 @@ export default { inputRef(type, id) { return `${this.$options.inputTypes[type]}-${id}`; }, - trigger() { + navigateToRetriedJob(retryPath) { + redirectTo(retryPath); + }, + async retryJob() { + try { + const { data } = await this.$apollo.mutate({ + mutation: retryJobWithVariablesMutation, + variables: { + id: convertToGraphQLId(GRAPHQL_ID_TYPES.ciBuild, this.jobId), + // we need to ensure no empty variables are passed to the API + variables: this.preparedVariables, + }, + }); + if (data.jobRetry?.errors?.length) { + createAlert({ message: data.jobRetry.errors[0] }); + } else { + this.navigateToRetriedJob(data.jobRetry?.job?.webPath); + } + } catch (error) { + createAlert({ message: JOB_GRAPHQL_ERRORS.retryMutationErrorText }); + } + }, + runAgain() { + this.runAgainBtnDisabled = true; + + this.retryJob(); + }, + triggerJob() { this.triggerBtnDisabled = true; this.triggerManualJob(this.preparedVariables); @@ -114,7 +184,8 @@ export default { }; </script> <template> - <div class="row gl-justify-content-center"> + <gl-loading-icon v-if="$apollo.queries.variables.loading" class="gl-mt-9" size="lg" /> + <div v-else class="row gl-justify-content-center"> <div class="col-10" data-testid="manual-vars-form"> <label>{{ $options.i18n.header }}</label> @@ -147,7 +218,7 @@ export default { </template> <gl-form-input :ref="inputRef('value', variable.id)" - v-model="variable.secretValue" + v-model="variable.value" :placeholder="$options.i18n.valuePlaceholder" data-testid="ci-variable-value" /> @@ -155,11 +226,13 @@ export default { <gl-button v-if="canRemove(index)" + v-gl-tooltip + :aria-label="$options.i18n.clearInputs" + :title="$options.i18n.clearInputs" class="gl-flex-grow-0 gl-flex-basis-0" category="tertiary" variant="danger" icon="clear" - :aria-label="__('Delete variable')" data-testid="delete-variable-btn" @click="deleteVariable(variable.id)" /> @@ -177,7 +250,27 @@ export default { </template> </gl-sprintf> </div> - <div class="gl-display-flex gl-justify-content-center gl-mt-5"> + <div v-if="isRetryable" class="gl-display-flex gl-justify-content-center gl-mt-5"> + <gl-button + class="gl-mt-5" + :aria-label="__('Cancel')" + data-testid="cancel-btn" + @click="$emit('hideManualVariablesForm')" + >{{ __('Cancel') }}</gl-button + > + <gl-button + class="gl-mt-5" + variant="confirm" + category="primary" + :aria-label="__('Run manual job again')" + :disabled="runAgainBtnDisabled" + data-testid="run-manual-job-btn" + @click="runAgain" + > + {{ $options.i18n.runAgainButtonText }} + </gl-button> + </div> + <div v-else class="gl-display-flex gl-justify-content-center gl-mt-5"> <gl-button class="gl-mt-5" variant="confirm" @@ -185,9 +278,9 @@ export default { :aria-label="__('Trigger manual job')" :disabled="triggerBtnDisabled" data-testid="trigger-manual-job-btn" - @click="trigger" + @click="triggerJob" > - {{ action.button_title }} + {{ $options.i18n.triggerButtonText }} </gl-button> </div> </div> |