diff options
Diffstat (limited to 'app/assets/javascripts/registry/settings')
13 files changed, 567 insertions, 64 deletions
diff --git a/app/assets/javascripts/registry/settings/components/expiration_dropdown.vue b/app/assets/javascripts/registry/settings/components/expiration_dropdown.vue new file mode 100644 index 00000000000..d75fb31fd98 --- /dev/null +++ b/app/assets/javascripts/registry/settings/components/expiration_dropdown.vue @@ -0,0 +1,50 @@ +<script> +import { GlFormGroup, GlFormSelect } from '@gitlab/ui'; + +export default { + components: { + GlFormGroup, + GlFormSelect, + }, + props: { + formOptions: { + type: Array, + required: false, + default: () => [], + }, + disabled: { + type: Boolean, + required: false, + default: false, + }, + value: { + type: String, + required: false, + default: '', + }, + name: { + type: String, + required: true, + }, + label: { + type: String, + required: true, + }, + }, +}; +</script> + +<template> + <gl-form-group :id="`${name}-form-group`" :label-for="name" :label="label"> + <gl-form-select :id="name" :value="value" :disabled="disabled" @input="$emit('input', $event)"> + <option + v-for="option in formOptions" + :key="option.key" + :value="option.key" + data-testid="option" + > + {{ option.label }} + </option> + </gl-form-select> + </gl-form-group> +</template> diff --git a/app/assets/javascripts/registry/settings/components/expiration_input.vue b/app/assets/javascripts/registry/settings/components/expiration_input.vue new file mode 100644 index 00000000000..2dbd9d26f60 --- /dev/null +++ b/app/assets/javascripts/registry/settings/components/expiration_input.vue @@ -0,0 +1,110 @@ +<script> +import { GlFormGroup, GlFormInput, GlSprintf, GlLink } from '@gitlab/ui'; +import { NAME_REGEX_LENGTH, TEXT_AREA_INVALID_FEEDBACK } from '../constants'; + +export default { + components: { + GlFormGroup, + GlFormInput, + GlSprintf, + GlLink, + }, + inject: ['tagsRegexHelpPagePath'], + props: { + error: { + type: String, + required: false, + default: '', + }, + disabled: { + type: Boolean, + required: false, + default: false, + }, + value: { + type: String, + required: false, + default: '', + }, + name: { + type: String, + required: true, + }, + label: { + type: String, + required: true, + }, + placeholder: { + type: String, + required: false, + default: '', + }, + description: { + type: String, + required: true, + }, + }, + computed: { + textAreaLengthErrorMessage() { + return this.isInputValid(this.value) ? '' : TEXT_AREA_INVALID_FEEDBACK; + }, + inputValidation() { + const nameRegexErrors = this.error || this.textAreaLengthErrorMessage; + return { + state: nameRegexErrors === null ? null : !nameRegexErrors, + message: nameRegexErrors, + }; + }, + internalValue: { + get() { + return this.value; + }, + set(value) { + this.$emit('input', value); + this.$emit('validation', this.isInputValid(value)); + }, + }, + }, + methods: { + isInputValid(value) { + return !value || value.length <= NAME_REGEX_LENGTH; + }, + }, +}; +</script> + +<template> + <gl-form-group + :id="`${name}-form-group`" + :label-for="name" + :state="inputValidation.state" + :invalid-feedback="inputValidation.message" + > + <template #label> + <span data-testid="label"> + <gl-sprintf :message="label"> + <template #italic="{content}"> + <i>{{ content }}</i> + </template> + </gl-sprintf> + </span> + </template> + <gl-form-input + :id="name" + v-model="internalValue" + :placeholder="placeholder" + :state="inputValidation.state" + :disabled="disabled" + trim + /> + <template #description> + <span data-testid="description" class="gl-text-gray-400"> + <gl-sprintf :message="description"> + <template #link="{content}"> + <gl-link :href="tagsRegexHelpPagePath" target="_blank">{{ content }}</gl-link> + </template> + </gl-sprintf> + </span> + </template> + </gl-form-group> +</template> diff --git a/app/assets/javascripts/registry/settings/components/expiration_run_text.vue b/app/assets/javascripts/registry/settings/components/expiration_run_text.vue new file mode 100644 index 00000000000..fd9ca6a54c5 --- /dev/null +++ b/app/assets/javascripts/registry/settings/components/expiration_run_text.vue @@ -0,0 +1,46 @@ +<script> +import { GlFormGroup, GlFormInput } from '@gitlab/ui'; +import { NEXT_CLEANUP_LABEL, NOT_SCHEDULED_POLICY_TEXT } from '~/registry/settings/constants'; + +export default { + components: { + GlFormGroup, + GlFormInput, + }, + props: { + value: { + type: String, + required: false, + default: NOT_SCHEDULED_POLICY_TEXT, + }, + enabled: { + type: Boolean, + required: false, + default: false, + }, + }, + computed: { + parsedValue() { + return this.enabled ? this.value : NOT_SCHEDULED_POLICY_TEXT; + }, + }, + i18n: { + NEXT_CLEANUP_LABEL, + }, +}; +</script> + +<template> + <gl-form-group + id="expiration-policy-info-text-group" + :label="$options.i18n.NEXT_CLEANUP_LABEL" + label-for="expiration-policy-info-text" + > + <gl-form-input + id="expiration-policy-info-text" + class="gl-pl-0!" + plaintext + :value="parsedValue" + /> + </gl-form-group> +</template> diff --git a/app/assets/javascripts/registry/settings/components/expiration_toggle.vue b/app/assets/javascripts/registry/settings/components/expiration_toggle.vue new file mode 100644 index 00000000000..7f045244926 --- /dev/null +++ b/app/assets/javascripts/registry/settings/components/expiration_toggle.vue @@ -0,0 +1,52 @@ +<script> +import { GlFormGroup, GlToggle, GlSprintf } from '@gitlab/ui'; +import { ENABLED_TOGGLE_DESCRIPTION, DISABLED_TOGGLE_DESCRIPTION } from '../constants'; + +export default { + components: { + GlFormGroup, + GlToggle, + GlSprintf, + }, + props: { + disabled: { + type: Boolean, + required: false, + default: false, + }, + value: { + type: Boolean, + required: false, + default: false, + }, + }, + computed: { + enabled: { + get() { + return this.value; + }, + set(value) { + this.$emit('input', value); + }, + }, + toggleText() { + return this.enabled ? ENABLED_TOGGLE_DESCRIPTION : DISABLED_TOGGLE_DESCRIPTION; + }, + }, +}; +</script> + +<template> + <gl-form-group id="expiration-policy-toggle-group" label-for="expiration-policy-toggle"> + <div class="gl-display-flex"> + <gl-toggle id="expiration-policy-toggle" v-model="enabled" :disabled="disabled" /> + <span class="gl-ml-5 gl-line-height-24" data-testid="description"> + <gl-sprintf :message="toggleText"> + <template #strong="{content}"> + <strong>{{ content }}</strong> + </template> + </gl-sprintf> + </span> + </div> + </gl-form-group> +</template> diff --git a/app/assets/javascripts/registry/settings/components/registry_settings_app.vue b/app/assets/javascripts/registry/settings/components/registry_settings_app.vue index 264d39a406a..35c7a8be4ea 100644 --- a/app/assets/javascripts/registry/settings/components/registry_settings_app.vue +++ b/app/assets/javascripts/registry/settings/components/registry_settings_app.vue @@ -1,17 +1,17 @@ <script> import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui'; -import { isEqual, get } from 'lodash'; -import expirationPolicyQuery from '../graphql/queries/get_expiration_policy.graphql'; -import { FETCH_SETTINGS_ERROR_MESSAGE } from '../../shared/constants'; - -import SettingsForm from './settings_form.vue'; +import { isEqual, get, isEmpty } from 'lodash'; +import expirationPolicyQuery from '../graphql/queries/get_expiration_policy.query.graphql'; import { + FETCH_SETTINGS_ERROR_MESSAGE, UNAVAILABLE_FEATURE_TITLE, UNAVAILABLE_FEATURE_INTRO_TEXT, UNAVAILABLE_USER_FEATURE_TEXT, UNAVAILABLE_ADMIN_FEATURE_TEXT, } from '../constants'; +import SettingsForm from './settings_form.vue'; + export default { components: { SettingsForm, @@ -60,6 +60,9 @@ export default { return this.isAdmin ? UNAVAILABLE_ADMIN_FEATURE_TEXT : UNAVAILABLE_USER_FEATURE_TEXT; }, isEdited() { + if (isEmpty(this.containerExpirationPolicy) && isEmpty(this.workingCopy)) { + return false; + } return !isEqual(this.containerExpirationPolicy, this.workingCopy); }, }, diff --git a/app/assets/javascripts/registry/settings/components/settings_form.vue b/app/assets/javascripts/registry/settings/components/settings_form.vue index fe4aee6806e..1f374c7b60e 100644 --- a/app/assets/javascripts/registry/settings/components/settings_form.vue +++ b/app/assets/javascripts/registry/settings/components/settings_form.vue @@ -1,21 +1,41 @@ <script> -import { GlCard, GlButton } from '@gitlab/ui'; +import { GlCard, GlButton, GlSprintf } from '@gitlab/ui'; import Tracking from '~/tracking'; import { UPDATE_SETTINGS_ERROR_MESSAGE, UPDATE_SETTINGS_SUCCESS_MESSAGE, -} from '../../shared/constants'; -import ExpirationPolicyFields from '../../shared/components/expiration_policy_fields.vue'; -import { SET_CLEANUP_POLICY_BUTTON, CLEANUP_POLICY_CARD_HEADER } from '../constants'; -import { formOptionsGenerator } from '~/registry/shared/utils'; -import updateContainerExpirationPolicyMutation from '../graphql/mutations/update_container_expiration_policy.graphql'; -import { updateContainerExpirationPolicy } from '../graphql/utils/cache_update'; + SET_CLEANUP_POLICY_BUTTON, + KEEP_HEADER_TEXT, + KEEP_INFO_TEXT, + KEEP_N_LABEL, + NAME_REGEX_KEEP_LABEL, + NAME_REGEX_KEEP_DESCRIPTION, + REMOVE_HEADER_TEXT, + REMOVE_INFO_TEXT, + EXPIRATION_SCHEDULE_LABEL, + NAME_REGEX_LABEL, + NAME_REGEX_PLACEHOLDER, + NAME_REGEX_DESCRIPTION, + CADENCE_LABEL, + EXPIRATION_POLICY_FOOTER_NOTE, +} from '~/registry/settings/constants'; +import { formOptionsGenerator } from '~/registry/settings/utils'; +import updateContainerExpirationPolicyMutation from '~/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql'; +import { updateContainerExpirationPolicy } from '~/registry/settings/graphql/utils/cache_update'; +import ExpirationDropdown from './expiration_dropdown.vue'; +import ExpirationInput from './expiration_input.vue'; +import ExpirationToggle from './expiration_toggle.vue'; +import ExpirationRunText from './expiration_run_text.vue'; export default { components: { GlCard, GlButton, - ExpirationPolicyFields, + GlSprintf, + ExpirationDropdown, + ExpirationInput, + ExpirationToggle, + ExpirationRunText, }, mixins: [Tracking.mixin()], inject: ['projectPath'], @@ -35,22 +55,31 @@ export default { default: false, }, }, - labelsConfig: { - cols: 3, - align: 'right', - }, + formOptions: formOptionsGenerator(), i18n: { - CLEANUP_POLICY_CARD_HEADER, + KEEP_HEADER_TEXT, + KEEP_INFO_TEXT, + KEEP_N_LABEL, + NAME_REGEX_KEEP_LABEL, SET_CLEANUP_POLICY_BUTTON, + NAME_REGEX_KEEP_DESCRIPTION, + REMOVE_HEADER_TEXT, + REMOVE_INFO_TEXT, + EXPIRATION_SCHEDULE_LABEL, + NAME_REGEX_LABEL, + NAME_REGEX_PLACEHOLDER, + NAME_REGEX_DESCRIPTION, + CADENCE_LABEL, + EXPIRATION_POLICY_FOOTER_NOTE, }, data() { return { tracking: { label: 'docker_container_retention_and_expiration_policies', }, - fieldsAreValid: true, - apiErrors: null, + apiErrors: {}, + localErrors: {}, mutationLoading: false, }; }, @@ -66,12 +95,18 @@ export default { showLoadingIcon() { return this.isLoading || this.mutationLoading; }, + fieldsAreValid() { + return Object.values(this.localErrors).every(error => error); + }, isSubmitButtonDisabled() { return !this.fieldsAreValid || this.showLoadingIcon; }, isCancelButtonDisabled() { return !this.isEdited || this.isLoading || this.mutationLoading; }, + isFieldDisabled() { + return this.showLoadingIcon || !this.value.enabled; + }, mutationVariables() { return { projectPath: this.projectPath, @@ -90,7 +125,8 @@ export default { }, reset() { this.track('reset_form'); - this.apiErrors = null; + this.apiErrors = {}; + this.localErrors = {}; this.$emit('reset'); }, setApiErrors(response) { @@ -101,9 +137,15 @@ export default { return acc; }, {}); }, + setLocalErrors(state, model) { + this.localErrors = { + ...this.localErrors, + [model]: state, + }; + }, submit() { this.track('submit_form'); - this.apiErrors = null; + this.apiErrors = {}; this.mutationLoading = true; return this.$apollo .mutate({ @@ -129,11 +171,9 @@ export default { this.mutationLoading = false; }); }, - onModelChange(changePayload) { - this.$emit('input', changePayload.newValue); - if (this.apiErrors) { - this.apiErrors[changePayload.modified] = undefined; - } + onModelChange(newValue, model) { + this.$emit('input', { ...this.value, [model]: newValue }); + this.apiErrors[model] = undefined; }, }, }; @@ -141,42 +181,133 @@ export default { <template> <form ref="form-element" @submit.prevent="submit" @reset.prevent="reset"> - <gl-card> + <expiration-toggle + :value="prefilledForm.enabled" + :disabled="showLoadingIcon" + class="gl-mb-0!" + data-testid="enable-toggle" + @input="onModelChange($event, 'enabled')" + /> + + <div class="gl-display-flex gl-mt-7"> + <expiration-dropdown + v-model="prefilledForm.cadence" + :disabled="isFieldDisabled" + :form-options="$options.formOptions.cadence" + :label="$options.i18n.CADENCE_LABEL" + name="cadence" + class="gl-mr-7 gl-mb-0!" + data-testid="cadence-dropdown" + @input="onModelChange($event, 'cadence')" + /> + <expiration-run-text + :value="prefilledForm.nextRunAt" + :enabled="prefilledForm.enabled" + class="gl-mb-0!" + /> + </div> + <gl-card class="gl-mt-7"> <template #header> - {{ $options.i18n.CLEANUP_POLICY_CARD_HEADER }} + {{ $options.i18n.KEEP_HEADER_TEXT }} </template> <template #default> - <expiration-policy-fields - :value="prefilledForm" - :form-options="$options.formOptions" - :is-loading="isLoading" - :api-errors="apiErrors" - @validated="fieldsAreValid = true" - @invalidated="fieldsAreValid = false" - @input="onModelChange" - /> + <div> + <p> + <gl-sprintf :message="$options.i18n.KEEP_INFO_TEXT"> + <template #strong="{content}"> + <strong>{{ content }}</strong> + </template> + <template #secondStrong="{content}"> + <strong>{{ content }}</strong> + </template> + </gl-sprintf> + </p> + <expiration-dropdown + v-model="prefilledForm.keepN" + :disabled="isFieldDisabled" + :form-options="$options.formOptions.keepN" + :label="$options.i18n.KEEP_N_LABEL" + name="keep-n" + data-testid="keep-n-dropdown" + @input="onModelChange($event, 'keepN')" + /> + <expiration-input + v-model="prefilledForm.nameRegexKeep" + :error="apiErrors.nameRegexKeep" + :disabled="isFieldDisabled" + :label="$options.i18n.NAME_REGEX_KEEP_LABEL" + :description="$options.i18n.NAME_REGEX_KEEP_DESCRIPTION" + name="keep-regex" + data-testid="keep-regex-input" + @input="onModelChange($event, 'nameRegexKeep')" + @validation="setLocalErrors($event, 'nameRegexKeep')" + /> + </div> </template> - <template #footer> - <gl-button - ref="cancel-button" - type="reset" - class="gl-mr-3 gl-display-block float-right" - :disabled="isCancelButtonDisabled" - > - {{ __('Cancel') }} - </gl-button> - <gl-button - ref="save-button" - type="submit" - :disabled="isSubmitButtonDisabled" - :loading="showLoadingIcon" - variant="success" - category="primary" - class="js-no-auto-disable" - > - {{ $options.i18n.SET_CLEANUP_POLICY_BUTTON }} - </gl-button> + </gl-card> + <gl-card class="gl-mt-7"> + <template #header> + {{ $options.i18n.REMOVE_HEADER_TEXT }} + </template> + <template #default> + <div> + <p> + <gl-sprintf :message="$options.i18n.REMOVE_INFO_TEXT"> + <template #strong="{content}"> + <strong>{{ content }}</strong> + </template> + <template #secondStrong="{content}"> + <strong>{{ content }}</strong> + </template> + </gl-sprintf> + </p> + <expiration-dropdown + v-model="prefilledForm.olderThan" + :disabled="isFieldDisabled" + :form-options="$options.formOptions.olderThan" + :label="$options.i18n.EXPIRATION_SCHEDULE_LABEL" + name="older-than" + data-testid="older-than-dropdown" + @input="onModelChange($event, 'olderThan')" + /> + <expiration-input + v-model="prefilledForm.nameRegex" + :error="apiErrors.nameRegex" + :disabled="isFieldDisabled" + :label="$options.i18n.NAME_REGEX_LABEL" + :placeholder="$options.i18n.NAME_REGEX_PLACEHOLDER" + :description="$options.i18n.NAME_REGEX_DESCRIPTION" + name="remove-regex" + data-testid="remove-regex-input" + @input="onModelChange($event, 'nameRegex')" + @validation="setLocalErrors($event, 'nameRegex')" + /> + </div> </template> </gl-card> + <div class="gl-mt-7 gl-display-flex gl-align-items-center"> + <gl-button + data-testid="save-button" + type="submit" + :disabled="isSubmitButtonDisabled" + :loading="showLoadingIcon" + variant="success" + category="primary" + class="js-no-auto-disable gl-mr-4" + > + {{ $options.i18n.SET_CLEANUP_POLICY_BUTTON }} + </gl-button> + <gl-button + data-testid="cancel-button" + type="reset" + :disabled="isCancelButtonDisabled" + class="gl-mr-4" + > + {{ __('Cancel') }} + </gl-button> + <span class="gl-font-style-italic gl-text-gray-400">{{ + $options.i18n.EXPIRATION_POLICY_FOOTER_NOTE + }}</span> + </div> </form> </template> diff --git a/app/assets/javascripts/registry/settings/constants.js b/app/assets/javascripts/registry/settings/constants.js index e790658f491..21c54299632 100644 --- a/app/assets/javascripts/registry/settings/constants.js +++ b/app/assets/javascripts/registry/settings/constants.js @@ -1,7 +1,6 @@ import { s__, __ } from '~/locale'; -export const SET_CLEANUP_POLICY_BUTTON = s__('ContainerRegistry|Set cleanup policy'); -export const CLEANUP_POLICY_CARD_HEADER = s__('ContainerRegistry|Tag expiration policy'); +export const SET_CLEANUP_POLICY_BUTTON = __('Save'); export const UNAVAILABLE_FEATURE_TITLE = s__( `ContainerRegistry|Cleanup policy for tags is disabled`, ); @@ -12,3 +11,81 @@ export const UNAVAILABLE_USER_FEATURE_TEXT = __(`Please contact your administrat export const UNAVAILABLE_ADMIN_FEATURE_TEXT = s__( `ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature.`, ); + +export const TEXT_AREA_INVALID_FEEDBACK = s__( + 'ContainerRegistry|The value of this input should be less than 256 characters', +); + +export const KEEP_HEADER_TEXT = s__('ContainerRegistry|Keep these tags'); +export const KEEP_INFO_TEXT = s__( + 'ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept.', +); +export const KEEP_N_LABEL = s__('ContainerRegistry|Keep the most recent:'); +export const NAME_REGEX_KEEP_LABEL = s__('ContainerRegistry|Keep tags matching:'); +export const NAME_REGEX_KEEP_DESCRIPTION = s__( + 'ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}', +); + +export const REMOVE_HEADER_TEXT = s__('ContainerRegistry|Remove these tags'); +export const REMOVE_INFO_TEXT = s__( + 'ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them.', +); +export const EXPIRATION_SCHEDULE_LABEL = s__('ContainerRegistry|Remove tags older than:'); +export const NAME_REGEX_LABEL = s__('ContainerRegistry|Remove tags matching:'); +export const NAME_REGEX_PLACEHOLDER = '.*'; +export const NAME_REGEX_DESCRIPTION = s__( + 'ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}', +); + +export const ENABLED_TOGGLE_DESCRIPTION = s__( + 'ContainerRegistry|%{strongStart}Enabled%{strongEnd} - Tags that match the rules on this page are automatically scheduled for deletion.', +); +export const DISABLED_TOGGLE_DESCRIPTION = s__( + 'ContainerRegistry|%{strongStart}Disabled%{strongEnd} - Tags will not be automatically deleted.', +); + +export const CADENCE_LABEL = s__('ContainerRegistry|Run cleanup:'); + +export const NEXT_CLEANUP_LABEL = s__('ContainerRegistry|Next cleanup scheduled to run on:'); +export const NOT_SCHEDULED_POLICY_TEXT = s__('ContainerRegistry|Not yet scheduled'); +export const EXPIRATION_POLICY_FOOTER_NOTE = s__( + 'ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time', +); + +export const KEEP_N_OPTIONS = [ + { key: 'ONE_TAG', variable: 1, default: false }, + { key: 'FIVE_TAGS', variable: 5, default: false }, + { key: 'TEN_TAGS', variable: 10, default: true }, + { key: 'TWENTY_FIVE_TAGS', variable: 25, default: false }, + { key: 'FIFTY_TAGS', variable: 50, default: false }, + { key: 'ONE_HUNDRED_TAGS', variable: 100, default: false }, +]; + +export const CADENCE_OPTIONS = [ + { key: 'EVERY_DAY', label: __('Every day'), default: true }, + { key: 'EVERY_WEEK', label: __('Every week'), default: false }, + { key: 'EVERY_TWO_WEEKS', label: __('Every two weeks'), default: false }, + { key: 'EVERY_MONTH', label: __('Every month'), default: false }, + { key: 'EVERY_THREE_MONTHS', label: __('Every three months'), default: false }, +]; + +export const OLDER_THAN_OPTIONS = [ + { key: 'SEVEN_DAYS', variable: 7, default: false }, + { key: 'FOURTEEN_DAYS', variable: 14, default: false }, + { key: 'THIRTY_DAYS', variable: 30, default: false }, + { key: 'NINETY_DAYS', variable: 90, default: true }, +]; + +export const FETCH_SETTINGS_ERROR_MESSAGE = s__( + 'ContainerRegistry|Something went wrong while fetching the cleanup policy.', +); + +export const UPDATE_SETTINGS_ERROR_MESSAGE = s__( + 'ContainerRegistry|Something went wrong while updating the cleanup policy.', +); + +export const UPDATE_SETTINGS_SUCCESS_MESSAGE = s__( + 'ContainerRegistry|Cleanup policy successfully saved.', +); + +export const NAME_REGEX_LENGTH = 255; diff --git a/app/assets/javascripts/registry/settings/graphql/fragments/container_expiration_policy.fragment.graphql b/app/assets/javascripts/registry/settings/graphql/fragments/container_expiration_policy.fragment.graphql index 224e0ed9472..1d6c89133af 100644 --- a/app/assets/javascripts/registry/settings/graphql/fragments/container_expiration_policy.fragment.graphql +++ b/app/assets/javascripts/registry/settings/graphql/fragments/container_expiration_policy.fragment.graphql @@ -5,4 +5,5 @@ fragment ContainerExpirationPolicyFields on ContainerExpirationPolicy { nameRegex nameRegexKeep olderThan + nextRunAt } diff --git a/app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.graphql b/app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql index c40cd115ab0..c40cd115ab0 100644 --- a/app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.graphql +++ b/app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql diff --git a/app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.graphql b/app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.query.graphql index c171be0ad07..c171be0ad07 100644 --- a/app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.graphql +++ b/app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.query.graphql diff --git a/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js b/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js index 88067d52b51..05b4125a2fc 100644 --- a/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js +++ b/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js @@ -1,5 +1,5 @@ import { produce } from 'immer'; -import expirationPolicyQuery from '../queries/get_expiration_policy.graphql'; +import expirationPolicyQuery from '../queries/get_expiration_policy.query.graphql'; export const updateContainerExpirationPolicy = projectPath => (client, { data: updatedData }) => { const queryAndParams = { diff --git a/app/assets/javascripts/registry/settings/registry_settings_bundle.js b/app/assets/javascripts/registry/settings/registry_settings_bundle.js index f7b1c5abd3a..6a4584b1b28 100644 --- a/app/assets/javascripts/registry/settings/registry_settings_bundle.js +++ b/app/assets/javascripts/registry/settings/registry_settings_bundle.js @@ -13,7 +13,13 @@ export default () => { if (!el) { return null; } - const { projectPath, isAdmin, adminSettingsPath, enableHistoricEntries } = el.dataset; + const { + isAdmin, + enableHistoricEntries, + projectPath, + adminSettingsPath, + tagsRegexHelpPagePath, + } = el.dataset; return new Vue({ el, apolloProvider, @@ -21,10 +27,11 @@ export default () => { RegistrySettingsApp, }, provide: { - projectPath, isAdmin: parseBoolean(isAdmin), - adminSettingsPath, enableHistoricEntries: parseBoolean(enableHistoricEntries), + projectPath, + adminSettingsPath, + tagsRegexHelpPagePath, }, render(createElement) { return createElement('registry-settings-app', {}); diff --git a/app/assets/javascripts/registry/settings/utils.js b/app/assets/javascripts/registry/settings/utils.js new file mode 100644 index 00000000000..51b4fb6bdb8 --- /dev/null +++ b/app/assets/javascripts/registry/settings/utils.js @@ -0,0 +1,26 @@ +import { n__ } from '~/locale'; +import { KEEP_N_OPTIONS, CADENCE_OPTIONS, OLDER_THAN_OPTIONS } from './constants'; + +export const findDefaultOption = options => { + const item = options.find(o => o.default); + return item ? item.key : null; +}; + +export const olderThanTranslationGenerator = variable => n__('%d day', '%d days', variable); + +export const keepNTranslationGenerator = variable => + n__('%d tag per image name', '%d tags per image name', variable); + +export const optionLabelGenerator = (collection, translationFn) => + collection.map(option => ({ + ...option, + label: translationFn(option.variable), + })); + +export const formOptionsGenerator = () => { + return { + olderThan: optionLabelGenerator(OLDER_THAN_OPTIONS, olderThanTranslationGenerator), + cadence: CADENCE_OPTIONS, + keepN: optionLabelGenerator(KEEP_N_OPTIONS, keepNTranslationGenerator), + }; +}; |