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:
Diffstat (limited to 'app/assets/javascripts/registry')
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/details_header.vue60
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue155
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue30
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue4
-rw-r--r--app/assets/javascripts/registry/explorer/constants/details.js2
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql27
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags.query.graphql29
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags_count.query.graphql6
-rw-r--r--app/assets/javascripts/registry/explorer/pages/details.vue105
-rw-r--r--app/assets/javascripts/registry/settings/components/expiration_dropdown.vue50
-rw-r--r--app/assets/javascripts/registry/settings/components/expiration_input.vue110
-rw-r--r--app/assets/javascripts/registry/settings/components/expiration_run_text.vue46
-rw-r--r--app/assets/javascripts/registry/settings/components/expiration_toggle.vue62
-rw-r--r--app/assets/javascripts/registry/settings/components/registry_settings_app.vue106
-rw-r--r--app/assets/javascripts/registry/settings/components/settings_form.vue313
-rw-r--r--app/assets/javascripts/registry/settings/constants.js91
-rw-r--r--app/assets/javascripts/registry/settings/graphql/fragments/container_expiration_policy.fragment.graphql9
-rw-r--r--app/assets/javascripts/registry/settings/graphql/index.js14
-rw-r--r--app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql10
-rw-r--r--app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.query.graphql9
-rw-r--r--app/assets/javascripts/registry/settings/graphql/utils/cache_update.js21
-rw-r--r--app/assets/javascripts/registry/settings/registry_settings_bundle.js40
-rw-r--r--app/assets/javascripts/registry/settings/utils.js26
23 files changed, 248 insertions, 1077 deletions
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/details_header.vue b/app/assets/javascripts/registry/explorer/components/details_page/details_header.vue
index f46068acd68..80ed9a32039 100644
--- a/app/assets/javascripts/registry/explorer/components/details_page/details_header.vue
+++ b/app/assets/javascripts/registry/explorer/components/details_page/details_header.vue
@@ -1,6 +1,6 @@
<script>
import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import { sprintf, n__ } from '~/locale';
+import { sprintf, n__, s__ } from '~/locale';
import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
@@ -23,6 +23,8 @@ import {
ROOT_IMAGE_TOOLTIP,
} from '../../constants/index';
+import getContainerRepositoryTagsCountQuery from '../../graphql/queries/get_container_repository_tags_count.query.graphql';
+
export default {
name: 'DetailsHeader',
components: { GlButton, GlIcon, TitleArea, MetadataItem },
@@ -35,60 +37,77 @@ export default {
type: Object,
required: true,
},
- metadataLoading: {
- type: Boolean,
- required: false,
- default: false,
- },
disabled: {
type: Boolean,
default: false,
required: false,
},
},
+ data() {
+ return {
+ containerRepository: {},
+ fetchTagsCount: false,
+ };
+ },
+ apollo: {
+ containerRepository: {
+ query: getContainerRepositoryTagsCountQuery,
+ variables() {
+ return {
+ id: this.image.id,
+ };
+ },
+ },
+ },
computed: {
+ imageDetails() {
+ return { ...this.image, ...this.containerRepository };
+ },
visibilityIcon() {
- return this.image?.project?.visibility === 'public' ? 'eye' : 'eye-slash';
+ return this.imageDetails?.project?.visibility === 'public' ? 'eye' : 'eye-slash';
},
timeAgo() {
- return this.timeFormatted(this.image.updatedAt);
+ return this.timeFormatted(this.imageDetails.updatedAt);
},
updatedText() {
return sprintf(UPDATED_AT, { time: this.timeAgo });
},
tagCountText() {
- return n__('%d tag', '%d tags', this.image.tagsCount);
+ if (this.$apollo.queries.containerRepository.loading) {
+ return s__('ContainerRegistry|-- tags');
+ }
+ return n__('%d tag', '%d tags', this.imageDetails.tagsCount);
},
cleanupTextAndTooltip() {
- if (!this.image.project.containerExpirationPolicy?.enabled) {
+ if (!this.imageDetails.project.containerExpirationPolicy?.enabled) {
return { text: CLEANUP_DISABLED_TEXT, tooltip: CLEANUP_DISABLED_TOOLTIP };
}
return {
[UNSCHEDULED_STATUS]: {
text: sprintf(CLEANUP_UNSCHEDULED_TEXT, {
- time: this.timeFormatted(this.image.project.containerExpirationPolicy.nextRunAt),
+ time: this.timeFormatted(this.imageDetails.project.containerExpirationPolicy.nextRunAt),
}),
},
[SCHEDULED_STATUS]: { text: CLEANUP_SCHEDULED_TEXT, tooltip: CLEANUP_SCHEDULED_TOOLTIP },
[ONGOING_STATUS]: { text: CLEANUP_ONGOING_TEXT, tooltip: CLEANUP_ONGOING_TOOLTIP },
[UNFINISHED_STATUS]: { text: CLEANUP_UNFINISHED_TEXT, tooltip: CLEANUP_UNFINISHED_TOOLTIP },
- }[this.image?.expirationPolicyCleanupStatus];
+ }[this.imageDetails?.expirationPolicyCleanupStatus];
},
deleteButtonDisabled() {
- return this.disabled || !this.image.canDelete;
+ return this.disabled || !this.imageDetails.canDelete;
},
rootImageTooltip() {
- return !this.image.name ? ROOT_IMAGE_TOOLTIP : '';
+ return !this.imageDetails.name ? ROOT_IMAGE_TOOLTIP : '';
},
imageName() {
- return this.image.name || ROOT_IMAGE_TEXT;
+ return this.imageDetails.name || ROOT_IMAGE_TEXT;
},
},
};
</script>
<template>
- <title-area :metadata-loading="metadataLoading">
+ <title-area>
<template #title>
<span data-testid="title">
{{ imageName }}
@@ -124,13 +143,8 @@ export default {
/>
</template>
<template #right-actions>
- <gl-button
- v-if="!metadataLoading"
- variant="danger"
- :disabled="deleteButtonDisabled"
- @click="$emit('delete')"
- >
- {{ __('Delete') }}
+ <gl-button variant="danger" :disabled="deleteButtonDisabled" @click="$emit('delete')">
+ {{ __('Delete image repository') }}
</gl-button>
</template>
</title-area>
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue b/app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue
index bc10246614a..3e19a646f53 100644
--- a/app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue
+++ b/app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue
@@ -1,19 +1,32 @@
<script>
-import { GlButton } from '@gitlab/ui';
-import { REMOVE_TAGS_BUTTON_TITLE, TAGS_LIST_TITLE } from '../../constants/index';
+import { GlButton, GlKeysetPagination } from '@gitlab/ui';
+import createFlash from '~/flash';
+import { joinPaths } from '~/lib/utils/url_utility';
+import {
+ REMOVE_TAGS_BUTTON_TITLE,
+ TAGS_LIST_TITLE,
+ GRAPHQL_PAGE_SIZE,
+ FETCH_IMAGES_LIST_ERROR_MESSAGE,
+} from '../../constants/index';
+import getContainerRepositoryTagsQuery from '../../graphql/queries/get_container_repository_tags.query.graphql';
+import EmptyState from './empty_state.vue';
import TagsListRow from './tags_list_row.vue';
+import TagsLoader from './tags_loader.vue';
export default {
name: 'TagsList',
components: {
GlButton,
+ GlKeysetPagination,
TagsListRow,
+ EmptyState,
+ TagsLoader,
},
+ inject: ['config'],
props: {
- tags: {
- type: Array,
- required: false,
- default: () => [],
+ id: {
+ type: [Number, String],
+ required: true,
},
isMobile: {
type: Boolean,
@@ -25,17 +38,46 @@ export default {
default: false,
required: false,
},
+ isImageLoading: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
},
i18n: {
REMOVE_TAGS_BUTTON_TITLE,
TAGS_LIST_TITLE,
},
+ apollo: {
+ containerRepository: {
+ query: getContainerRepositoryTagsQuery,
+ variables() {
+ return this.queryVariables;
+ },
+ error() {
+ createFlash({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
+ },
+ },
+ },
data() {
return {
selectedItems: {},
+ containerRepository: {},
};
},
computed: {
+ tags() {
+ return this.containerRepository?.tags?.nodes || [];
+ },
+ tagsPageInfo() {
+ return this.containerRepository?.tags?.pageInfo;
+ },
+ queryVariables() {
+ return {
+ id: joinPaths(this.config.gidPrefix, `${this.id}`),
+ first: GRAPHQL_PAGE_SIZE,
+ };
+ },
hasSelectedItems() {
return this.tags.some((tag) => this.selectedItems[tag.name]);
},
@@ -45,42 +87,93 @@ export default {
multiDeleteButtonIsDisabled() {
return !this.hasSelectedItems || this.disabled;
},
+ showPagination() {
+ return this.tagsPageInfo.hasPreviousPage || this.tagsPageInfo.hasNextPage;
+ },
+ hasNoTags() {
+ return this.tags.length === 0;
+ },
+ isLoading() {
+ return this.isImageLoading || this.$apollo.queries.containerRepository.loading;
+ },
},
methods: {
updateSelectedItems(name) {
this.$set(this.selectedItems, name, !this.selectedItems[name]);
},
+ mapTagsToBeDleeted(items) {
+ return this.tags.filter((tag) => items[tag.name]);
+ },
+ fetchNextPage() {
+ this.$apollo.queries.containerRepository.fetchMore({
+ variables: {
+ after: this.tagsPageInfo?.endCursor,
+ first: GRAPHQL_PAGE_SIZE,
+ },
+ updateQuery(previousResult, { fetchMoreResult }) {
+ return fetchMoreResult;
+ },
+ });
+ },
+ fetchPreviousPage() {
+ this.$apollo.queries.containerRepository.fetchMore({
+ variables: {
+ first: null,
+ before: this.tagsPageInfo?.startCursor,
+ last: GRAPHQL_PAGE_SIZE,
+ },
+ updateQuery(previousResult, { fetchMoreResult }) {
+ return fetchMoreResult;
+ },
+ });
+ },
},
};
</script>
<template>
<div>
- <div class="gl-display-flex gl-justify-content-space-between gl-mb-3">
- <h5 data-testid="list-title">
- {{ $options.i18n.TAGS_LIST_TITLE }}
- </h5>
+ <tags-loader v-if="isLoading" />
+ <template v-else>
+ <empty-state v-if="hasNoTags" :no-containers-image="config.noContainersImage" />
+ <template v-else>
+ <div class="gl-display-flex gl-justify-content-space-between gl-mb-3">
+ <h5 data-testid="list-title">
+ {{ $options.i18n.TAGS_LIST_TITLE }}
+ </h5>
- <gl-button
- v-if="showMultiDeleteButton"
- :disabled="multiDeleteButtonIsDisabled"
- category="secondary"
- variant="danger"
- @click="$emit('delete', selectedItems)"
- >
- {{ $options.i18n.REMOVE_TAGS_BUTTON_TITLE }}
- </gl-button>
- </div>
- <tags-list-row
- v-for="(tag, index) in tags"
- :key="tag.path"
- :tag="tag"
- :first="index === 0"
- :selected="selectedItems[tag.name]"
- :is-mobile="isMobile"
- :disabled="disabled"
- @select="updateSelectedItems(tag.name)"
- @delete="$emit('delete', { [tag.name]: true })"
- />
+ <gl-button
+ v-if="showMultiDeleteButton"
+ :disabled="multiDeleteButtonIsDisabled"
+ category="secondary"
+ variant="danger"
+ @click="$emit('delete', mapTagsToBeDleeted(selectedItems))"
+ >
+ {{ $options.i18n.REMOVE_TAGS_BUTTON_TITLE }}
+ </gl-button>
+ </div>
+ <tags-list-row
+ v-for="(tag, index) in tags"
+ :key="tag.path"
+ :tag="tag"
+ :first="index === 0"
+ :selected="selectedItems[tag.name]"
+ :is-mobile="isMobile"
+ :disabled="disabled"
+ @select="updateSelectedItems(tag.name)"
+ @delete="$emit('delete', mapTagsToBeDleeted({ [tag.name]: true }))"
+ />
+ <div class="gl-display-flex gl-justify-content-center">
+ <gl-keyset-pagination
+ v-if="showPagination"
+ :has-next-page="tagsPageInfo.hasNextPage"
+ :has-previous-page="tagsPageInfo.hasPreviousPage"
+ class="gl-mt-3"
+ @prev="fetchPreviousPage"
+ @next="fetchNextPage"
+ />
+ </div>
+ </template>
+ </template>
</div>
</template>
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue b/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue
index 74027a376a7..45eb2ce51e4 100644
--- a/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue
+++ b/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue
@@ -50,6 +50,11 @@ export default {
default: false,
required: false,
},
+ disabled: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
},
i18n: {
REMOVE_TAG_BUTTON_TITLE,
@@ -92,19 +97,25 @@ export default {
tagLocation() {
return this.tag.path?.replace(`:${this.tag.name}`, '');
},
- invalidTag() {
+ isInvalidTag() {
return !this.tag.digest;
},
+ isCheckboxDisabled() {
+ return this.isInvalidTag || this.disabled;
+ },
+ isDeleteDisabled() {
+ return this.isInvalidTag || this.disabled || !this.tag.canDelete;
+ },
},
};
</script>
<template>
- <list-item v-bind="$attrs" :selected="selected">
+ <list-item v-bind="$attrs" :selected="selected" :disabled="disabled">
<template #left-action>
<gl-form-checkbox
v-if="tag.canDelete"
- :disabled="invalidTag"
+ :disabled="isCheckboxDisabled"
class="gl-m-0"
:checked="selected"
@change="$emit('select')"
@@ -126,10 +137,11 @@ export default {
:title="tag.location"
:text="tag.location"
category="tertiary"
+ :disabled="disabled"
/>
<gl-icon
- v-if="invalidTag"
+ v-if="isInvalidTag"
v-gl-tooltip="{ title: $options.i18n.MISSING_MANIFEST_WARNING_TOOLTIP }"
name="warning"
class="gl-text-orange-500 gl-mb-2 gl-ml-2"
@@ -162,7 +174,7 @@ export default {
</template>
<template #right-action>
<delete-button
- :disabled="!tag.canDelete || invalidTag"
+ :disabled="isDeleteDisabled"
:title="$options.i18n.REMOVE_TAG_BUTTON_TITLE"
:tooltip-title="$options.i18n.REMOVE_TAG_BUTTON_DISABLE_TOOLTIP"
:tooltip-disabled="tag.canDelete"
@@ -172,7 +184,7 @@ export default {
/>
</template>
- <template v-if="!invalidTag" #details-published>
+ <template v-if="!isInvalidTag" #details-published>
<details-row icon="clock" data-testid="published-date-detail">
<gl-sprintf :message="$options.i18n.PUBLISHED_DETAILS_ROW_TEXT">
<template #repositoryPath>
@@ -187,7 +199,7 @@ export default {
</gl-sprintf>
</details-row>
</template>
- <template v-if="!invalidTag" #details-manifest-digest>
+ <template v-if="!isInvalidTag" #details-manifest-digest>
<details-row icon="log" data-testid="manifest-detail">
<gl-sprintf :message="$options.i18n.MANIFEST_DETAILS_ROW_TEST">
<template #digest>
@@ -200,10 +212,11 @@ export default {
:text="tag.digest"
category="tertiary"
size="small"
+ :disabled="disabled"
/>
</details-row>
</template>
- <template v-if="!invalidTag" #details-configuration-digest>
+ <template v-if="!isInvalidTag" #details-configuration-digest>
<details-row icon="cloud-gear" data-testid="configuration-detail">
<gl-sprintf :message="$options.i18n.CONFIGURATION_DETAILS_ROW_TEST">
<template #digest>
@@ -216,6 +229,7 @@ export default {
:text="formattedRevision"
category="tertiary"
size="small"
+ :disabled="disabled"
/>
</details-row>
</template>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue b/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
index 0373a84b271..930ad01c758 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
@@ -78,6 +78,9 @@ export default {
imageName() {
return this.item.name ? this.item.path : `${this.item.path}/ ${ROOT_IMAGE_TEXT}`;
},
+ routerLinkEvent() {
+ return this.deleting ? '' : 'click';
+ },
},
};
</script>
@@ -97,6 +100,7 @@ export default {
class="gl-text-body gl-font-weight-bold"
data-testid="details-link"
data-qa-selector="registry_image_content"
+ :event="routerLinkEvent"
:to="{ name: 'details', params: { id } }"
>
{{ imageName }}
diff --git a/app/assets/javascripts/registry/explorer/constants/details.js b/app/assets/javascripts/registry/explorer/constants/details.js
index 7220f9646db..5dcc042a9c4 100644
--- a/app/assets/javascripts/registry/explorer/constants/details.js
+++ b/app/assets/javascripts/registry/explorer/constants/details.js
@@ -31,7 +31,7 @@ export const CONFIGURATION_DETAILS_ROW_TEST = s__(
);
export const REMOVE_TAG_BUTTON_TITLE = s__('ContainerRegistry|Remove tag');
-export const REMOVE_TAGS_BUTTON_TITLE = s__('ContainerRegistry|Delete selected');
+export const REMOVE_TAGS_BUTTON_TITLE = s__('ContainerRegistry|Delete selected tags');
export const REMOVE_TAG_CONFIRMATION_TEXT = s__(
`ContainerRegistry|You are about to remove %{item}. Are you sure?`,
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql
index 3fd019467ac..88c2e667afd 100644
--- a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql
+++ b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql
@@ -1,12 +1,4 @@
-#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
-
-query getContainerRepositoryDetails(
- $id: ID!
- $first: Int
- $last: Int
- $after: String
- $before: String
-) {
+query getContainerRepositoryDetails($id: ID!) {
containerRepository(id: $id) {
id
name
@@ -16,25 +8,8 @@ query getContainerRepositoryDetails(
canDelete
createdAt
updatedAt
- tagsCount
expirationPolicyStartedAt
expirationPolicyCleanupStatus
- tags(after: $after, before: $before, first: $first, last: $last) {
- nodes {
- digest
- location
- path
- name
- revision
- shortRevision
- createdAt
- totalSize
- canDelete
- }
- pageInfo {
- ...PageInfo
- }
- }
project {
visibility
containerExpirationPolicy {
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags.query.graphql
new file mode 100644
index 00000000000..a703c2dd0ac
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags.query.graphql
@@ -0,0 +1,29 @@
+#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
+
+query getContainerRepositoryTags(
+ $id: ID!
+ $first: Int
+ $last: Int
+ $after: String
+ $before: String
+) {
+ containerRepository(id: $id) {
+ id
+ tags(after: $after, before: $before, first: $first, last: $last) {
+ nodes {
+ digest
+ location
+ path
+ name
+ revision
+ shortRevision
+ createdAt
+ totalSize
+ canDelete
+ }
+ pageInfo {
+ ...PageInfo
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags_count.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags_count.query.graphql
new file mode 100644
index 00000000000..9092a71edb0
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_tags_count.query.graphql
@@ -0,0 +1,6 @@
+query getContainerRepositoryTagsCount($id: ID!) {
+ containerRepository(id: $id) {
+ id
+ tagsCount
+ }
+}
diff --git a/app/assets/javascripts/registry/explorer/pages/details.vue b/app/assets/javascripts/registry/explorer/pages/details.vue
index 2f515356fa7..34ec3b085a5 100644
--- a/app/assets/javascripts/registry/explorer/pages/details.vue
+++ b/app/assets/javascripts/registry/explorer/pages/details.vue
@@ -1,5 +1,5 @@
<script>
-import { GlKeysetPagination, GlResizeObserverDirective } from '@gitlab/ui';
+import { GlResizeObserverDirective } from '@gitlab/ui';
import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
@@ -21,7 +21,6 @@ import {
ALERT_SUCCESS_TAGS,
ALERT_DANGER_TAGS,
ALERT_DANGER_IMAGE,
- GRAPHQL_PAGE_SIZE,
FETCH_IMAGES_LIST_ERROR_MESSAGE,
UNFINISHED_STATUS,
MISSING_OR_DELETED_IMAGE_BREADCRUMB,
@@ -36,7 +35,6 @@ export default {
DeleteAlert,
PartialCleanupAlert,
DetailsHeader,
- GlKeysetPagination,
DeleteModal,
TagsList,
TagsLoader,
@@ -50,16 +48,12 @@ export default {
mixins: [Tracking.mixin()],
inject: ['breadCrumbState', 'config'],
apollo: {
- image: {
+ containerRepository: {
query: getContainerRepositoryDetailsQuery,
variables() {
return this.queryVariables;
},
- update(data) {
- return data.containerRepository;
- },
- result({ data }) {
- this.tagsPageInfo = data.containerRepository?.tags?.pageInfo;
+ result() {
this.updateBreadcrumb();
},
error() {
@@ -69,8 +63,7 @@ export default {
},
data() {
return {
- image: {},
- tagsPageInfo: {},
+ containerRepository: {},
itemsToBeDeleted: [],
isMobile: false,
mutationLoading: false,
@@ -83,19 +76,15 @@ export default {
queryVariables() {
return {
id: joinPaths(this.config.gidPrefix, `${this.$route.params.id}`),
- first: GRAPHQL_PAGE_SIZE,
};
},
isLoading() {
- return this.$apollo.queries.image.loading || this.mutationLoading;
- },
- tags() {
- return this.image?.tags?.nodes || [];
+ return this.$apollo.queries.containerRepository.loading || this.mutationLoading;
},
showPartialCleanupWarning() {
return (
this.config.showUnfinishedTagCleanupCallout &&
- this.image?.expirationPolicyCleanupStatus === UNFINISHED_STATUS &&
+ this.containerRepository?.expirationPolicyCleanupStatus === UNFINISHED_STATUS &&
!this.hidePartialCleanupWarning
);
},
@@ -105,26 +94,20 @@ export default {
this.itemsToBeDeleted?.length > 1 ? 'bulk_registry_tag_delete' : 'registry_tag_delete',
};
},
- showPagination() {
- return this.tagsPageInfo.hasPreviousPage || this.tagsPageInfo.hasNextPage;
- },
- hasNoTags() {
- return this.tags.length === 0;
- },
pageActionsAreDisabled() {
- return Boolean(this.image?.status);
+ return Boolean(this.containerRepository?.status);
},
},
methods: {
updateBreadcrumb() {
- const name = this.image?.id
- ? this.image?.name || ROOT_IMAGE_TEXT
+ const name = this.containerRepository?.id
+ ? this.containerRepository?.name || ROOT_IMAGE_TEXT
: MISSING_OR_DELETED_IMAGE_BREADCRUMB;
this.breadCrumbState.updateName(name);
},
deleteTags(toBeDeleted) {
this.deleteImageAlert = false;
- this.itemsToBeDeleted = this.tags.filter((tag) => toBeDeleted[tag.name]);
+ this.itemsToBeDeleted = toBeDeleted;
this.track('click_button');
this.$refs.deleteModal.show();
},
@@ -170,33 +153,6 @@ export default {
handleResize() {
this.isMobile = GlBreakpointInstance.getBreakpointSize() === 'xs';
},
- fetchNextPage() {
- if (this.tagsPageInfo?.hasNextPage) {
- this.$apollo.queries.image.fetchMore({
- variables: {
- after: this.tagsPageInfo?.endCursor,
- first: GRAPHQL_PAGE_SIZE,
- },
- updateQuery(previousResult, { fetchMoreResult }) {
- return fetchMoreResult;
- },
- });
- }
- },
- fetchPreviousPage() {
- if (this.tagsPageInfo?.hasPreviousPage) {
- this.$apollo.queries.image.fetchMore({
- variables: {
- first: null,
- before: this.tagsPageInfo?.startCursor,
- last: GRAPHQL_PAGE_SIZE,
- },
- updateQuery(previousResult, { fetchMoreResult }) {
- return fetchMoreResult;
- },
- });
- }
- },
dismissPartialCleanupWarning() {
this.hidePartialCleanupWarning = true;
axios.post(this.config.userCalloutsPath, {
@@ -205,7 +161,7 @@ export default {
},
deleteImage() {
this.deleteImageAlert = true;
- this.itemsToBeDeleted = [{ path: this.image.path }];
+ this.itemsToBeDeleted = [{ path: this.containerRepository.path }];
this.$refs.deleteModal.show();
},
deleteImageError() {
@@ -221,7 +177,7 @@ export default {
<template>
<div v-gl-resize-observer="handleResize" class="gl-my-3">
- <template v-if="image">
+ <template v-if="containerRepository">
<delete-alert
v-model="deleteAlertType"
:garbage-collection-help-page-path="config.garbageCollectionHelpPagePath"
@@ -236,40 +192,27 @@ export default {
@dismiss="dismissPartialCleanupWarning"
/>
- <status-alert v-if="image.status" :status="image.status" />
+ <status-alert v-if="containerRepository.status" :status="containerRepository.status" />
<details-header
- :image="image"
- :metadata-loading="isLoading"
+ v-if="!isLoading"
+ :image="containerRepository"
:disabled="pageActionsAreDisabled"
@delete="deleteImage"
/>
<tags-loader v-if="isLoading" />
- <template v-else>
- <empty-state v-if="hasNoTags" :no-containers-image="config.noContainersImage" />
- <template v-else>
- <tags-list
- :tags="tags"
- :is-mobile="isMobile"
- :disabled="pageActionsAreDisabled"
- @delete="deleteTags"
- />
- <div class="gl-display-flex gl-justify-content-center">
- <gl-keyset-pagination
- v-if="showPagination"
- :has-next-page="tagsPageInfo.hasNextPage"
- :has-previous-page="tagsPageInfo.hasPreviousPage"
- class="gl-mt-3"
- @prev="fetchPreviousPage"
- @next="fetchNextPage"
- />
- </div>
- </template>
- </template>
+ <tags-list
+ v-else
+ :id="$route.params.id"
+ :is-image-loading="isLoading"
+ :is-mobile="isMobile"
+ :disabled="pageActionsAreDisabled"
+ @delete="deleteTags"
+ />
<delete-image
- :id="image.id"
+ :id="containerRepository.id"
ref="deleteImage"
use-update-fn
@start="deleteImageIniit"
diff --git a/app/assets/javascripts/registry/settings/components/expiration_dropdown.vue b/app/assets/javascripts/registry/settings/components/expiration_dropdown.vue
deleted file mode 100644
index d75fb31fd98..00000000000
--- a/app/assets/javascripts/registry/settings/components/expiration_dropdown.vue
+++ /dev/null
@@ -1,50 +0,0 @@
-<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
deleted file mode 100644
index 42b7c7918a5..00000000000
--- a/app/assets/javascripts/registry/settings/components/expiration_input.vue
+++ /dev/null
@@ -1,110 +0,0 @@
-<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
deleted file mode 100644
index fd9ca6a54c5..00000000000
--- a/app/assets/javascripts/registry/settings/components/expiration_run_text.vue
+++ /dev/null
@@ -1,46 +0,0 @@
-<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
deleted file mode 100644
index 6aa78c69ba9..00000000000
--- a/app/assets/javascripts/registry/settings/components/expiration_toggle.vue
+++ /dev/null
@@ -1,62 +0,0 @@
-<script>
-import { GlFormGroup, GlToggle, GlSprintf } from '@gitlab/ui';
-import { s__ } from '~/locale';
-import { ENABLED_TOGGLE_DESCRIPTION, DISABLED_TOGGLE_DESCRIPTION } from '../constants';
-
-export default {
- i18n: {
- toggleLabel: s__('ContainerRegistry|Enable expiration policy'),
- },
- 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"
- :label="$options.i18n.toggleLabel"
- label-position="hidden"
- :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
deleted file mode 100644
index 480590ec71e..00000000000
--- a/app/assets/javascripts/registry/settings/components/registry_settings_app.vue
+++ /dev/null
@@ -1,106 +0,0 @@
-<script>
-import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
-import { isEqual, get, isEmpty } from 'lodash';
-import {
- FETCH_SETTINGS_ERROR_MESSAGE,
- UNAVAILABLE_FEATURE_TITLE,
- UNAVAILABLE_FEATURE_INTRO_TEXT,
- UNAVAILABLE_USER_FEATURE_TEXT,
- UNAVAILABLE_ADMIN_FEATURE_TEXT,
-} from '../constants';
-import expirationPolicyQuery from '../graphql/queries/get_expiration_policy.query.graphql';
-
-import SettingsForm from './settings_form.vue';
-
-export default {
- components: {
- SettingsForm,
- GlAlert,
- 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 = { ...get(data, 'project.containerExpirationPolicy', {}) };
- },
- error(e) {
- this.fetchSettingsError = e;
- },
- },
- },
- data() {
- return {
- fetchSettingsError: false,
- containerExpirationPolicy: null,
- workingCopy: {},
- };
- },
- computed: {
- isDisabled() {
- return !(this.containerExpirationPolicy || this.enableHistoricEntries);
- },
- showDisabledFormMessage() {
- return this.isDisabled && !this.fetchSettingsError;
- },
- unavailableFeatureMessage() {
- 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);
- },
- },
- methods: {
- restoreOriginal() {
- this.workingCopy = { ...this.containerExpirationPolicy };
- },
- },
-};
-</script>
-
-<template>
- <div>
- <settings-form
- v-if="!isDisabled"
- v-model="workingCopy"
- :is-loading="$apollo.queries.containerExpirationPolicy.loading"
- :is-edited="isEdited"
- @reset="restoreOriginal"
- />
- <template v-else>
- <gl-alert
- v-if="showDisabledFormMessage"
- :dismissible="false"
- :title="$options.i18n.UNAVAILABLE_FEATURE_TITLE"
- variant="tip"
- >
- {{ $options.i18n.UNAVAILABLE_FEATURE_INTRO_TEXT }}
-
- <gl-sprintf :message="unavailableFeatureMessage">
- <template #link="{ content }">
- <gl-link :href="adminSettingsPath" target="_blank">{{ content }}</gl-link>
- </template>
- </gl-sprintf>
- </gl-alert>
- <gl-alert v-else-if="fetchSettingsError" variant="warning" :dismissible="false">
- <gl-sprintf :message="$options.i18n.FETCH_SETTINGS_ERROR_MESSAGE" />
- </gl-alert>
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/registry/settings/components/settings_form.vue b/app/assets/javascripts/registry/settings/components/settings_form.vue
deleted file mode 100644
index 1360e09a75d..00000000000
--- a/app/assets/javascripts/registry/settings/components/settings_form.vue
+++ /dev/null
@@ -1,313 +0,0 @@
-<script>
-import { GlCard, GlButton, GlSprintf } from '@gitlab/ui';
-import {
- UPDATE_SETTINGS_ERROR_MESSAGE,
- UPDATE_SETTINGS_SUCCESS_MESSAGE,
- 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 updateContainerExpirationPolicyMutation from '~/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql';
-import { updateContainerExpirationPolicy } from '~/registry/settings/graphql/utils/cache_update';
-import { formOptionsGenerator } from '~/registry/settings/utils';
-import Tracking from '~/tracking';
-import ExpirationDropdown from './expiration_dropdown.vue';
-import ExpirationInput from './expiration_input.vue';
-import ExpirationRunText from './expiration_run_text.vue';
-import ExpirationToggle from './expiration_toggle.vue';
-
-export default {
- components: {
- GlCard,
- GlButton,
- GlSprintf,
- ExpirationDropdown,
- ExpirationInput,
- ExpirationToggle,
- ExpirationRunText,
- },
- 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,
- },
- },
-
- formOptions: formOptionsGenerator(),
- i18n: {
- 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',
- },
- apiErrors: {},
- localErrors: {},
- mutationLoading: false,
- };
- },
- computed: {
- prefilledForm() {
- return {
- ...this.value,
- cadence: this.findDefaultOption('cadence'),
- keepN: this.findDefaultOption('keepN'),
- olderThan: this.findDefaultOption('olderThan'),
- };
- },
- 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,
- enabled: this.prefilledForm.enabled,
- cadence: this.prefilledForm.cadence,
- olderThan: this.prefilledForm.olderThan,
- keepN: this.prefilledForm.keepN,
- nameRegex: this.prefilledForm.nameRegex,
- nameRegexKeep: this.prefilledForm.nameRegexKeep,
- };
- },
- },
- methods: {
- findDefaultOption(option) {
- return this.value[option] || this.$options.formOptions[option].find((f) => f.default)?.key;
- },
- reset() {
- this.track('reset_form');
- this.apiErrors = {};
- this.localErrors = {};
- this.$emit('reset');
- },
- setApiErrors(response) {
- this.apiErrors = response.graphQLErrors.reduce((acc, curr) => {
- curr.extensions.problems.forEach((item) => {
- acc[item.path[0]] = item.message;
- });
- return acc;
- }, {});
- },
- setLocalErrors(state, model) {
- this.localErrors = {
- ...this.localErrors,
- [model]: state,
- };
- },
- submit() {
- this.track('submit_form');
- this.apiErrors = {};
- 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' });
- } else {
- 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(newValue, model) {
- this.$emit('input', { ...this.value, [model]: newValue });
- this.apiErrors[model] = undefined;
- },
- },
-};
-</script>
-
-<template>
- <form ref="form-element" @submit.prevent="submit" @reset.prevent="reset">
- <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.KEEP_HEADER_TEXT }}
- </template>
- <template #default>
- <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>
- </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"
- category="primary"
- variant="confirm"
- 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
deleted file mode 100644
index 165c4aae3cb..00000000000
--- a/app/assets/javascripts/registry/settings/constants.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import { s__, __ } from '~/locale';
-
-export const SET_CLEANUP_POLICY_BUTTON = __('Save');
-export const UNAVAILABLE_FEATURE_TITLE = s__(
- `ContainerRegistry|Cleanup policy for tags is disabled`,
-);
-export const UNAVAILABLE_FEATURE_INTRO_TEXT = s__(
- `ContainerRegistry|This project's cleanup policy for tags is not enabled.`,
-);
-export const UNAVAILABLE_USER_FEATURE_TEXT = __(`Please contact your administrator.`);
-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}View regex examples.%{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}View regex examples.%{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
deleted file mode 100644
index 1d6c89133af..00000000000
--- a/app/assets/javascripts/registry/settings/graphql/fragments/container_expiration_policy.fragment.graphql
+++ /dev/null
@@ -1,9 +0,0 @@
-fragment ContainerExpirationPolicyFields on ContainerExpirationPolicy {
- cadence
- enabled
- keepN
- nameRegex
- nameRegexKeep
- olderThan
- nextRunAt
-}
diff --git a/app/assets/javascripts/registry/settings/graphql/index.js b/app/assets/javascripts/registry/settings/graphql/index.js
deleted file mode 100644
index 16152eb81f6..00000000000
--- a/app/assets/javascripts/registry/settings/graphql/index.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createDefaultClient from '~/lib/graphql';
-
-Vue.use(VueApollo);
-
-export const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(
- {},
- {
- assumeImmutableResults: true,
- },
- ),
-});
diff --git a/app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql b/app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql
deleted file mode 100644
index c40cd115ab0..00000000000
--- a/app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql
+++ /dev/null
@@ -1,10 +0,0 @@
-#import "../fragments/container_expiration_policy.fragment.graphql"
-
-mutation updateContainerExpirationPolicy($input: UpdateContainerExpirationPolicyInput!) {
- updateContainerExpirationPolicy(input: $input) {
- containerExpirationPolicy {
- ...ContainerExpirationPolicyFields
- }
- errors
- }
-}
diff --git a/app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.query.graphql b/app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.query.graphql
deleted file mode 100644
index c171be0ad07..00000000000
--- a/app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.query.graphql
+++ /dev/null
@@ -1,9 +0,0 @@
-#import "../fragments/container_expiration_policy.fragment.graphql"
-
-query getProjectExpirationPolicy($projectPath: ID!) {
- project(fullPath: $projectPath) {
- containerExpirationPolicy {
- ...ContainerExpirationPolicyFields
- }
- }
-}
diff --git a/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js b/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js
deleted file mode 100644
index c4b2af13862..00000000000
--- a/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { produce } from 'immer';
-import expirationPolicyQuery from '../queries/get_expiration_policy.query.graphql';
-
-export const updateContainerExpirationPolicy = (projectPath) => (client, { data: updatedData }) => {
- const queryAndParams = {
- query: expirationPolicyQuery,
- variables: { projectPath },
- };
- const sourceData = client.readQuery(queryAndParams);
-
- const data = produce(sourceData, (draftState) => {
- draftState.project.containerExpirationPolicy = {
- ...updatedData.updateContainerExpirationPolicy.containerExpirationPolicy,
- };
- });
-
- client.writeQuery({
- ...queryAndParams,
- data,
- });
-};
diff --git a/app/assets/javascripts/registry/settings/registry_settings_bundle.js b/app/assets/javascripts/registry/settings/registry_settings_bundle.js
deleted file mode 100644
index 65af6f846aa..00000000000
--- a/app/assets/javascripts/registry/settings/registry_settings_bundle.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import { GlToast } from '@gitlab/ui';
-import Vue from 'vue';
-import { parseBoolean } from '~/lib/utils/common_utils';
-import Translate from '~/vue_shared/translate';
-import RegistrySettingsApp from './components/registry_settings_app.vue';
-import { apolloProvider } from './graphql/index';
-
-Vue.use(GlToast);
-Vue.use(Translate);
-
-export default () => {
- const el = document.getElementById('js-registry-settings');
- if (!el) {
- return null;
- }
- const {
- isAdmin,
- enableHistoricEntries,
- projectPath,
- adminSettingsPath,
- tagsRegexHelpPagePath,
- } = el.dataset;
- return new Vue({
- el,
- apolloProvider,
- components: {
- RegistrySettingsApp,
- },
- provide: {
- isAdmin: parseBoolean(isAdmin),
- 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
deleted file mode 100644
index 4a2d7c7d466..00000000000
--- a/app/assets/javascripts/registry/settings/utils.js
+++ /dev/null
@@ -1,26 +0,0 @@
-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),
- };
-};