Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-03 00:09:44 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-03 00:09:44 +0300
commitf96f2720d1b21b76eadedc54fdea67cb70e98d94 (patch)
tree527d27d5ceb816969e315b6223b3ddb2ca128dae /app/assets/javascripts/registry
parentad05e1db038a2e983d25555144fa29063e060c50 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/registry')
-rw-r--r--app/assets/javascripts/registry/settings/components/expiration_textarea.vue3
-rw-r--r--app/assets/javascripts/registry/settings/components/registry_settings_app.vue5
-rw-r--r--app/assets/javascripts/registry/settings/components/settings_form.vue233
-rw-r--r--app/assets/javascripts/registry/settings/constants.js14
-rw-r--r--app/assets/javascripts/registry/shared/utils.js7
5 files changed, 194 insertions, 68 deletions
diff --git a/app/assets/javascripts/registry/settings/components/expiration_textarea.vue b/app/assets/javascripts/registry/settings/components/expiration_textarea.vue
index 1e1194ebb5c..3f817e37dca 100644
--- a/app/assets/javascripts/registry/settings/components/expiration_textarea.vue
+++ b/app/assets/javascripts/registry/settings/components/expiration_textarea.vue
@@ -36,7 +36,8 @@ export default {
},
placeholder: {
type: String,
- required: true,
+ required: false,
+ default: '',
},
description: {
type: String,
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..e236834d8e1 100644
--- a/app/assets/javascripts/registry/settings/components/registry_settings_app.vue
+++ b/app/assets/javascripts/registry/settings/components/registry_settings_app.vue
@@ -1,6 +1,6 @@
<script>
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
-import { isEqual, get } from 'lodash';
+import { isEqual, get, isEmpty } from 'lodash';
import expirationPolicyQuery from '../graphql/queries/get_expiration_policy.graphql';
import { FETCH_SETTINGS_ERROR_MESSAGE } from '../../shared/constants';
@@ -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..d0a8081e455 100644
--- a/app/assets/javascripts/registry/settings/components/settings_form.vue
+++ b/app/assets/javascripts/registry/settings/components/settings_form.vue
@@ -1,21 +1,43 @@
<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';
+} from '~/registry/shared/constants';
+import {
+ 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/shared/utils';
-import updateContainerExpirationPolicyMutation from '../graphql/mutations/update_container_expiration_policy.graphql';
-import { updateContainerExpirationPolicy } from '../graphql/utils/cache_update';
+import updateContainerExpirationPolicyMutation from '~/registry/settings/graphql/mutations/update_container_expiration_policy.graphql';
+import { updateContainerExpirationPolicy } from '~/registry/settings/graphql/utils/cache_update';
+import ExpirationDropdown from './expiration_dropdown.vue';
+import ExpirationTextarea from './expiration_textarea.vue';
+import ExpirationToggle from './expiration_toggle.vue';
+import ExpirationRunText from './expiration_run_text.vue';
export default {
components: {
GlCard,
GlButton,
- ExpirationPolicyFields,
+ GlSprintf,
+ ExpirationDropdown,
+ ExpirationTextarea,
+ ExpirationToggle,
+ ExpirationRunText,
},
mixins: [Tracking.mixin()],
inject: ['projectPath'],
@@ -35,22 +57,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 +97,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 +127,8 @@ export default {
},
reset() {
this.track('reset_form');
- this.apiErrors = null;
+ this.apiErrors = {};
+ this.localErrors = {};
this.$emit('reset');
},
setApiErrors(response) {
@@ -101,9 +139,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 +173,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 +183,129 @@ 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" 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-textarea
+ 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-textarea"
+ @input="onModelChange($event, 'nameRegexKeep')"
+ @validation="setLocalErrors($event, 'nameRegexKeep')"
+ />
+ </div>
+ </template>
+ </gl-card>
+ <gl-card class="gl-mt-7">
+ <template #header>
+ {{ $options.i18n.REMOVE_HEADER_TEXT }}
</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>
+ <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-textarea
+ 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-textarea"
+ @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 bc3ec3104ad..ba5820196ff 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 = __('Save');
-export const CLEANUP_POLICY_CARD_HEADER = s__('ContainerRegistry|Tag expiration policy');
export const UNAVAILABLE_FEATURE_TITLE = s__(
`ContainerRegistry|Cleanup policy for tags is disabled`,
);
@@ -19,34 +18,33 @@ export const TEXT_AREA_INVALID_FEEDBACK = s__(
export const KEEP_HEADER_TEXT = s__('ContainerRegistry|Keep these tags');
export const KEEP_INFO_TEXT = s__(
- 'ContainerRegistry|Tags that match these rules will always be %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag will always be kept.',
+ '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_PLACEHOLDER = 'production-v.*';
export const NAME_REGEX_KEEP_DESCRIPTION = s__(
- 'ContainerRegistry|Tags with names matching this regex pattern will be kept. %{linkStart}More information%{linkEnd}',
+ '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 will be %{strongStart}removed%{strongEnd}, unless kept by a rule above.',
+ '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 matching this regex pattern will be removed. %{linkStart}More information%{linkEnd}',
+ 'ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}',
);
export const ENABLED_TEXT = __('Enabled');
export const DISABLED_TEXT = __('Disabled');
export const ENABLE_TOGGLE_DESCRIPTION = s__(
- 'ContainerRegistry|%{toggleStatus} - Tags matching the rules defined below will be automatically scheduled for deletion.',
+ 'ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion.',
);
-export const CADENCE_LABEL = s__('ContainerRegistry|Run cleanup every:');
+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');
diff --git a/app/assets/javascripts/registry/shared/utils.js b/app/assets/javascripts/registry/shared/utils.js
index bdf1ab9507d..5c8c505f835 100644
--- a/app/assets/javascripts/registry/shared/utils.js
+++ b/app/assets/javascripts/registry/shared/utils.js
@@ -21,12 +21,7 @@ export const mapComputedToEvent = (list, root) => {
return result;
};
-export const olderThanTranslationGenerator = variable =>
- n__(
- '%d day until tags are automatically removed',
- '%d days until tags are automatically removed',
- variable,
- );
+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);