summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/registry
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-09-30 12:09:53 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-09-30 12:09:53 +0000
commit6aa5c04c74d2d70ee7d19ef3a155b2def9dd46de (patch)
tree81f7b81234bc5b889c57e71f87b94878ab286383 /app/assets/javascripts/registry
parent418c3b29009dcc0a2c6b4872557d0274ba0b8077 (diff)
downloadgitlab-ce-6aa5c04c74d2d70ee7d19ef3a155b2def9dd46de.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/registry')
-rw-r--r--app/assets/javascripts/registry/settings/components/registry_settings_app.vue50
-rw-r--r--app/assets/javascripts/registry/settings/components/settings_form.vue96
-rw-r--r--app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue32
-rw-r--r--app/assets/javascripts/registry/shared/utils.js22
4 files changed, 132 insertions, 68 deletions
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 2ee7bbef4c6..fcb86fd18f0 100644
--- a/app/assets/javascripts/registry/settings/components/registry_settings_app.vue
+++ b/app/assets/javascripts/registry/settings/components/registry_settings_app.vue
@@ -1,7 +1,7 @@
<script>
-import { mapActions, mapGetters, mapState } from 'vuex';
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
-
+import { isEqual } 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';
@@ -19,21 +19,39 @@ export default {
GlSprintf,
GlLink,
},
+ inject: ['projectPath', 'isAdmin', 'adminSettingsPath', 'enableHistoricEntries'],
i18n: {
UNAVAILABLE_FEATURE_TITLE,
UNAVAILABLE_FEATURE_INTRO_TEXT,
FETCH_SETTINGS_ERROR_MESSAGE,
},
+ apollo: {
+ containerExpirationPolicy: {
+ query: expirationPolicyQuery,
+ variables() {
+ return {
+ projectPath: this.projectPath,
+ };
+ },
+ update: data => data.project?.containerExpirationPolicy,
+ result({ data }) {
+ this.workingCopy = { ...data.project?.containerExpirationPolicy };
+ },
+ error(e) {
+ this.fetchSettingsError = e;
+ },
+ },
+ },
data() {
return {
fetchSettingsError: false,
+ containerExpirationPolicy: null,
+ workingCopy: {},
};
},
computed: {
- ...mapState(['isAdmin', 'adminSettingsPath']),
- ...mapGetters({ isDisabled: 'getIsDisabled' }),
- showSettingForm() {
- return !this.isDisabled && !this.fetchSettingsError;
+ isDisabled() {
+ return !(this.containerExpirationPolicy || this.enableHistoricEntries);
},
showDisabledFormMessage() {
return this.isDisabled && !this.fetchSettingsError;
@@ -41,21 +59,27 @@ export default {
unavailableFeatureMessage() {
return this.isAdmin ? UNAVAILABLE_ADMIN_FEATURE_TEXT : UNAVAILABLE_USER_FEATURE_TEXT;
},
- },
- mounted() {
- this.fetchSettings().catch(() => {
- this.fetchSettingsError = true;
- });
+ isEdited() {
+ return !isEqual(this.containerExpirationPolicy, this.workingCopy);
+ },
},
methods: {
- ...mapActions(['fetchSettings']),
+ restoreOriginal() {
+ this.workingCopy = { ...this.containerExpirationPolicy };
+ },
},
};
</script>
<template>
<div>
- <settings-form v-if="showSettingForm" />
+ <settings-form
+ v-if="containerExpirationPolicy"
+ v-model="workingCopy"
+ :is-loading="$apollo.queries.containerExpirationPolicy.loading"
+ :is-edited="isEdited"
+ @reset="restoreOriginal"
+ />
<template v-else>
<gl-alert
v-if="showDisabledFormMessage"
diff --git a/app/assets/javascripts/registry/settings/components/settings_form.vue b/app/assets/javascripts/registry/settings/components/settings_form.vue
index 25c88daa54d..7deb1f92686 100644
--- a/app/assets/javascripts/registry/settings/components/settings_form.vue
+++ b/app/assets/javascripts/registry/settings/components/settings_form.vue
@@ -1,28 +1,45 @@
<script>
-import { get } from 'lodash';
-import { mapActions, mapState, mapGetters } from 'vuex';
-import { GlCard, GlButton, GlLoadingIcon } from '@gitlab/ui';
+import { GlCard, GlButton } from '@gitlab/ui';
import Tracking from '~/tracking';
-import { mapComputed } from '~/vuex_shared/bindings';
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';
export default {
components: {
GlCard,
GlButton,
- GlLoadingIcon,
ExpirationPolicyFields,
},
mixins: [Tracking.mixin()],
+ inject: ['projectPath'],
+ props: {
+ value: {
+ type: Object,
+ required: true,
+ },
+ isLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isEdited: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
labelsConfig: {
cols: 3,
align: 'right',
},
+ formOptions: formOptionsGenerator(),
i18n: {
CLEANUP_POLICY_CARD_HEADER,
SET_CLEANUP_POLICY_BUTTON,
@@ -34,49 +51,74 @@ export default {
},
fieldsAreValid: true,
apiErrors: null,
+ mutationLoading: false,
};
},
computed: {
- ...mapState(['formOptions', 'isLoading']),
- ...mapGetters({ isEdited: 'getIsEdited' }),
- ...mapComputed([{ key: 'settings', getter: 'getSettings' }], 'updateSettings'),
+ showLoadingIcon() {
+ return this.isLoading || this.mutationLoading;
+ },
isSubmitButtonDisabled() {
- return !this.fieldsAreValid || this.isLoading;
+ return !this.fieldsAreValid || this.showLoadingIcon;
},
isCancelButtonDisabled() {
- return !this.isEdited || this.isLoading;
+ return !this.isEdited || this.isLoading || this.mutationLoading;
+ },
+ mutationVariables() {
+ return {
+ projectPath: this.projectPath,
+ enabled: this.value.enabled,
+ cadence: this.value.cadence,
+ olderThan: this.value.olderThan,
+ keepN: this.value.keepN,
+ nameRegex: this.value.nameRegex,
+ nameRegexKeep: this.value.nameRegexKeep,
+ };
},
},
methods: {
- ...mapActions(['resetSettings', 'saveSettings']),
reset() {
this.track('reset_form');
this.apiErrors = null;
- this.resetSettings();
+ this.$emit('reset');
},
setApiErrors(response) {
- const messages = get(response, 'data.message', []);
-
- this.apiErrors = Object.keys(messages).reduce((acc, curr) => {
- if (curr.startsWith('container_expiration_policy.')) {
- const key = curr.replace('container_expiration_policy.', '');
- acc[key] = get(messages, [curr, 0], '');
- }
+ this.apiErrors = response.graphQLErrors.reduce((acc, curr) => {
+ curr.extensions.problems.forEach(item => {
+ acc[item.path[0]] = item.message;
+ });
return acc;
}, {});
},
submit() {
this.track('submit_form');
this.apiErrors = null;
- this.saveSettings()
- .then(() => this.$toast.show(UPDATE_SETTINGS_SUCCESS_MESSAGE, { type: 'success' }))
- .catch(({ response }) => {
- this.setApiErrors(response);
+ this.mutationLoading = true;
+ return this.$apollo
+ .mutate({
+ mutation: updateContainerExpirationPolicyMutation,
+ variables: {
+ input: this.mutationVariables,
+ },
+ update: updateContainerExpirationPolicy(this.projectPath),
+ })
+ .then(({ data }) => {
+ const errorMessage = data?.updateContainerExpirationPolicy?.errors[0];
+ if (errorMessage) {
+ this.$toast.show(errorMessage, { type: 'error' });
+ }
+ this.$toast.show(UPDATE_SETTINGS_SUCCESS_MESSAGE, { type: 'success' });
+ })
+ .catch(error => {
+ this.setApiErrors(error);
this.$toast.show(UPDATE_SETTINGS_ERROR_MESSAGE, { type: 'error' });
+ })
+ .finally(() => {
+ this.mutationLoading = false;
});
},
onModelChange(changePayload) {
- this.settings = changePayload.newValue;
+ this.$emit('input', changePayload.newValue);
if (this.apiErrors) {
this.apiErrors[changePayload.modified] = undefined;
}
@@ -93,8 +135,8 @@ export default {
</template>
<template #default>
<expiration-policy-fields
- :value="settings"
- :form-options="formOptions"
+ :value="value"
+ :form-options="$options.formOptions"
:is-loading="isLoading"
:api-errors="apiErrors"
@validated="fieldsAreValid = true"
@@ -115,12 +157,12 @@ export default {
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-loading-icon v-if="isLoading" class="gl-ml-3" />
</gl-button>
</template>
</gl-card>
diff --git a/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue b/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue
index 1ff2f6f99e5..2b8e9f6ff64 100644
--- a/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue
+++ b/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue
@@ -68,34 +68,31 @@ export default {
{
name: 'expiration-policy-interval',
label: EXPIRATION_INTERVAL_LABEL,
- model: 'older_than',
- optionKey: 'olderThan',
+ model: 'olderThan',
},
{
name: 'expiration-policy-schedule',
label: EXPIRATION_SCHEDULE_LABEL,
model: 'cadence',
- optionKey: 'cadence',
},
{
name: 'expiration-policy-latest',
label: KEEP_N_LABEL,
- model: 'keep_n',
- optionKey: 'keepN',
+ model: 'keepN',
},
],
textAreaList: [
{
name: 'expiration-policy-name-matching',
label: NAME_REGEX_LABEL,
- model: 'name_regex',
+ model: 'nameRegex',
placeholder: NAME_REGEX_PLACEHOLDER,
description: NAME_REGEX_DESCRIPTION,
},
{
name: 'expiration-policy-keep-name',
label: NAME_REGEX_KEEP_LABEL,
- model: 'name_regex_keep',
+ model: 'nameRegexKeep',
placeholder: NAME_REGEX_KEEP_PLACEHOLDER,
description: NAME_REGEX_KEEP_DESCRIPTION,
},
@@ -107,17 +104,16 @@ export default {
},
computed: {
...mapComputedToEvent(
- ['enabled', 'cadence', 'older_than', 'keep_n', 'name_regex', 'name_regex_keep'],
+ ['enabled', 'cadence', 'olderThan', 'keepN', 'nameRegex', 'nameRegexKeep'],
'value',
),
policyEnabledText() {
return this.enabled ? ENABLED_TEXT : DISABLED_TEXT;
},
textAreaValidation() {
- const nameRegexErrors =
- this.apiErrors?.name_regex || this.validateRegexLength(this.name_regex);
+ const nameRegexErrors = this.apiErrors?.nameRegex || this.validateRegexLength(this.nameRegex);
const nameKeepRegexErrors =
- this.apiErrors?.name_regex_keep || this.validateRegexLength(this.name_regex_keep);
+ this.apiErrors?.nameRegexKeep || this.validateRegexLength(this.nameRegexKeep);
return {
/*
@@ -127,11 +123,11 @@ export default {
* false: red border, error message
* So in this function we keep null if the are no message otherwise we 'invert' the error message
*/
- name_regex: {
+ nameRegex: {
state: nameRegexErrors === null ? null : !nameRegexErrors,
message: nameRegexErrors,
},
- name_regex_keep: {
+ nameRegexKeep: {
state: nameKeepRegexErrors === null ? null : !nameKeepRegexErrors,
message: nameKeepRegexErrors,
},
@@ -139,8 +135,8 @@ export default {
},
fieldsValidity() {
return (
- this.textAreaValidation.name_regex.state !== false &&
- this.textAreaValidation.name_regex_keep.state !== false
+ this.textAreaValidation.nameRegex.state !== false &&
+ this.textAreaValidation.nameRegexKeep.state !== false
);
},
isFormElementDisabled() {
@@ -216,11 +212,7 @@ export default {
:disabled="isFormElementDisabled"
@input="updateModel($event, select.model)"
>
- <option
- v-for="option in formOptions[select.optionKey]"
- :key="option.key"
- :value="option.key"
- >
+ <option v-for="option in formOptions[select.model]" :key="option.key" :value="option.key">
{{ option.label }}
</option>
</gl-form-select>
diff --git a/app/assets/javascripts/registry/shared/utils.js b/app/assets/javascripts/registry/shared/utils.js
index f84325cd438..bdf1ab9507d 100644
--- a/app/assets/javascripts/registry/shared/utils.js
+++ b/app/assets/javascripts/registry/shared/utils.js
@@ -21,20 +21,26 @@ export const mapComputedToEvent = (list, root) => {
return result;
};
-export const optionLabelGenerator = (collection, singularSentence, pluralSentence) =>
+export const olderThanTranslationGenerator = variable =>
+ n__(
+ '%d day until tags are automatically removed',
+ '%d days until tags are automatically removed',
+ 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: n__(singularSentence, pluralSentence, option.variable),
+ label: translationFn(option.variable),
}));
export const formOptionsGenerator = () => {
return {
- olderThan: optionLabelGenerator(
- OLDER_THAN_OPTIONS,
- '%d days until tags are automatically removed',
- '%d day until tags are automatically removed',
- ),
+ olderThan: optionLabelGenerator(OLDER_THAN_OPTIONS, olderThanTranslationGenerator),
cadence: CADENCE_OPTIONS,
- keepN: optionLabelGenerator(KEEP_N_OPTIONS, '%d tag per image name', '%d tags per image name'),
+ keepN: optionLabelGenerator(KEEP_N_OPTIONS, keepNTranslationGenerator),
};
};