summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/registry
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/registry')
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/delete_alert.vue2
-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/partial_cleanup_alert.vue4
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue5
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue4
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue2
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue4
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/image_list.vue6
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue23
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue8
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue12
-rw-r--r--app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue81
-rw-r--r--app/assets/javascripts/registry/explorer/constants/details.js21
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/fragments/container_repository.fragment.graphql11
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/index.js1
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_container_repositories_details.query.graphql26
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql5
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_group_container_repositories.query.graphql23
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_project_container_repositories.query.graphql23
-rw-r--r--app/assets/javascripts/registry/explorer/index.js30
-rw-r--r--app/assets/javascripts/registry/explorer/pages/details.vue15
-rw-r--r--app/assets/javascripts/registry/explorer/pages/list.vue138
-rw-r--r--app/assets/javascripts/registry/settings/components/expiration_input.vue4
-rw-r--r--app/assets/javascripts/registry/settings/components/expiration_toggle.vue2
-rw-r--r--app/assets/javascripts/registry/settings/components/registry_settings_app.vue2
-rw-r--r--app/assets/javascripts/registry/settings/components/settings_form.vue16
-rw-r--r--app/assets/javascripts/registry/settings/constants.js4
-rw-r--r--app/assets/javascripts/registry/settings/graphql/utils/cache_update.js4
-rw-r--r--app/assets/javascripts/registry/settings/utils.js10
29 files changed, 346 insertions, 200 deletions
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/delete_alert.vue b/app/assets/javascripts/registry/explorer/components/details_page/delete_alert.vue
index 8bdf043a106..56d2ff86fb7 100644
--- a/app/assets/javascripts/registry/explorer/components/details_page/delete_alert.vue
+++ b/app/assets/javascripts/registry/explorer/components/details_page/delete_alert.vue
@@ -60,7 +60,7 @@ export default {
@dismiss="$emit('change', null)"
>
<gl-sprintf :message="deleteAlertConfig.message">
- <template #docLink="{content}">
+ <template #docLink="{ content }">
<gl-link :href="garbageCollectionHelpPagePath" target="_blank">
{{ content }}
</gl-link>
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 3eeb7b29386..ed02aa264ed 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,12 +1,29 @@
<script>
import { GlSprintf } from '@gitlab/ui';
-import { sprintf } from '~/locale';
+import { sprintf, n__ } from '~/locale';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
-import { DETAILS_PAGE_TITLE, UPDATED_AT } from '../../constants/index';
+import {
+ DETAILS_PAGE_TITLE,
+ UPDATED_AT,
+ CLEANUP_UNSCHEDULED_TEXT,
+ CLEANUP_SCHEDULED_TEXT,
+ CLEANUP_ONGOING_TEXT,
+ CLEANUP_UNFINISHED_TEXT,
+ CLEANUP_DISABLED_TEXT,
+ CLEANUP_SCHEDULED_TOOLTIP,
+ CLEANUP_ONGOING_TOOLTIP,
+ CLEANUP_UNFINISHED_TOOLTIP,
+ CLEANUP_DISABLED_TOOLTIP,
+ UNFINISHED_STATUS,
+ UNSCHEDULED_STATUS,
+ SCHEDULED_STATUS,
+ ONGOING_STATUS,
+} from '../../constants/index';
export default {
+ name: 'DetailsHeader',
components: { GlSprintf, TitleArea, MetadataItem },
mixins: [timeagoMixin],
props: {
@@ -14,6 +31,11 @@ export default {
type: Object,
required: true,
},
+ metadataLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
visibilityIcon() {
@@ -25,6 +47,24 @@ export default {
updatedText() {
return sprintf(UPDATED_AT, { time: this.timeAgo });
},
+ tagCountText() {
+ return n__('%d tag', '%d tags', this.image.tagsCount);
+ },
+ cleanupTextAndTooltip() {
+ if (!this.image.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),
+ }),
+ },
+ [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];
+ },
},
i18n: {
DETAILS_PAGE_TITLE,
@@ -33,7 +73,7 @@ export default {
</script>
<template>
- <title-area>
+ <title-area :metadata-loading="metadataLoading">
<template #title>
<gl-sprintf :message="$options.i18n.DETAILS_PAGE_TITLE">
<template #imageName>
@@ -41,6 +81,20 @@ export default {
</template>
</gl-sprintf>
</template>
+ <template #metadata-tags-count>
+ <metadata-item icon="tag" :text="tagCountText" data-testid="tags-count" />
+ </template>
+
+ <template #metadata-cleanup>
+ <metadata-item
+ icon="expire"
+ :text="cleanupTextAndTooltip.text"
+ :text-tooltip="cleanupTextAndTooltip.tooltip"
+ size="xl"
+ data-testid="cleanup"
+ />
+ </template>
+
<template #metadata-updated>
<metadata-item
:icon="visibilityIcon"
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/partial_cleanup_alert.vue b/app/assets/javascripts/registry/explorer/components/details_page/partial_cleanup_alert.vue
index d13d815a59e..12095655126 100644
--- a/app/assets/javascripts/registry/explorer/components/details_page/partial_cleanup_alert.vue
+++ b/app/assets/javascripts/registry/explorer/components/details_page/partial_cleanup_alert.vue
@@ -23,12 +23,12 @@ export default {
<template>
<gl-alert variant="warning" :title="$options.i18n.DELETE_ALERT_TITLE" @dismiss="$emit('dismiss')">
<gl-sprintf :message="$options.i18n.DELETE_ALERT_LINK_TEXT">
- <template #adminLink="{content}">
+ <template #adminLink="{ content }">
<gl-link data-testid="run-link" :href="runCleanupPoliciesHelpPagePath" target="_blank">{{
content
}}</gl-link>
</template>
- <template #docLink="{content}">
+ <template #docLink="{ content }">
<gl-link data-testid="help-link" :href="cleanupPoliciesHelpPagePath" target="_blank">{{
content
}}</gl-link>
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 ad39a898e7b..1e0736c4a53 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
@@ -4,6 +4,7 @@ import TagsListRow from './tags_list_row.vue';
import { REMOVE_TAGS_BUTTON_TITLE, TAGS_LIST_TITLE } from '../../constants/index';
export default {
+ name: 'TagsList',
components: {
GlButton,
TagsListRow,
@@ -31,10 +32,10 @@ export default {
},
computed: {
hasSelectedItems() {
- return this.tags.some(tag => this.selectedItems[tag.name]);
+ return this.tags.some((tag) => this.selectedItems[tag.name]);
},
showMultiDeleteButton() {
- return this.tags.some(tag => tag.canDelete) && !this.isMobile;
+ return this.tags.some((tag) => tag.canDelete) && !this.isMobile;
},
},
methods: {
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 3a5cccc7d08..2e4a489f2cb 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
@@ -140,9 +140,7 @@ export default {
<template #left-secondary>
<span data-testid="size">
{{ formattedSize }}
- <template v-if="formattedSize && layers"
- >&middot;</template
- >
+ <template v-if="formattedSize && layers">&middot;</template>
{{ layers }}
</span>
</template>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue b/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue
index 319666210d6..07ee3c6083b 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue
@@ -19,8 +19,8 @@ export default {
GlDropdown,
CodeInstruction,
},
- inject: ['config', 'dockerBuildCommand', 'dockerPushCommand', 'dockerLoginCommand'],
mixins: [Tracking.mixin({ label: trackingLabel })],
+ inject: ['config', 'dockerBuildCommand', 'dockerPushCommand', 'dockerLoginCommand'],
trackingLabel,
i18n: {
QUICK_START,
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue b/app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue
index 26e9fee63af..a68c4de5aa6 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue
@@ -3,12 +3,12 @@ import { GlEmptyState, GlSprintf, GlLink } from '@gitlab/ui';
export default {
name: 'GroupEmptyState',
- inject: ['config'],
components: {
GlEmptyState,
GlSprintf,
GlLink,
},
+ inject: ['config'],
};
</script>
<template>
@@ -25,7 +25,7 @@ export default {
)
"
>
- <template #docLink="{content}">
+ <template #docLink="{ content }">
<gl-link :href="config.helpPagePath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/image_list.vue b/app/assets/javascripts/registry/explorer/components/list_page/image_list.vue
index f8b3233438f..10ad99d5956 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/image_list.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/image_list.vue
@@ -13,6 +13,11 @@ export default {
type: Array,
required: true,
},
+ metadataLoading: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
pageInfo: {
type: Object,
required: true,
@@ -33,6 +38,7 @@ export default {
:key="index"
:item="listItem"
:first="index === 0"
+ :metadata-loading="metadataLoading"
@delete="$emit('delete', $event)"
/>
<div class="gl-display-flex gl-justify-content-center">
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 3fe61dc231a..264a3c27cde 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
@@ -1,5 +1,5 @@
<script>
-import { GlTooltipDirective, GlIcon, GlSprintf } from '@gitlab/ui';
+import { GlTooltipDirective, GlIcon, GlSprintf, GlSkeletonLoader } from '@gitlab/ui';
import { n__ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
@@ -18,13 +18,14 @@ import {
} from '../../constants/index';
export default {
- name: 'ImageListrow',
+ name: 'ImageListRow',
components: {
ClipboardButton,
DeleteButton,
GlSprintf,
GlIcon,
ListItem,
+ GlSkeletonLoader,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -34,6 +35,11 @@ export default {
type: Object,
required: true,
},
+ metadataLoading: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
},
i18n: {
LIST_DELETE_BUTTON_DISABLED,
@@ -107,7 +113,11 @@ export default {
/>
</template>
<template #left-secondary>
- <span class="gl-display-flex gl-align-items-center" data-testid="tagsCount">
+ <span
+ v-if="!metadataLoading"
+ class="gl-display-flex gl-align-items-center"
+ data-testid="tags-count"
+ >
<gl-icon name="tag" class="gl-mr-2" />
<gl-sprintf :message="tagsCountText">
<template #count>
@@ -115,6 +125,13 @@ export default {
</template>
</gl-sprintf>
</span>
+
+ <div v-else class="gl-w-full">
+ <gl-skeleton-loader :width="900" :height="16" preserve-aspect-ratio="xMinYMax meet">
+ <circle cx="6" cy="8" r="6" />
+ <rect x="16" y="4" width="100" height="8" rx="4" />
+ </gl-skeleton-loader>
+ </div>
</template>
<template #right-action>
<delete-button
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue b/app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue
index 5308b025cc0..5aa04419ca0 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue
@@ -45,7 +45,7 @@ export default {
<template #description>
<p>
<gl-sprintf :message="$options.i18n.introText">
- <template #docLink="{content}">
+ <template #docLink="{ content }">
<gl-link :href="config.helpPagePath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
@@ -53,10 +53,10 @@ export default {
<h5>{{ $options.i18n.quickStart }}</h5>
<p>
<gl-sprintf :message="$options.i18n.notLoggedInMessage">
- <template #twofaDocLink="{content}">
+ <template #twofaDocLink="{ content }">
<gl-link :href="config.twoFactorAuthHelpLink" target="_blank">{{ content }}</gl-link>
</template>
- <template #personalAccessTokensDocLink="{content}">
+ <template #personalAccessTokensDocLink="{ content }">
<gl-link :href="config.personalAccessTokensHelpLink" target="_blank">{{
content
}}</gl-link>
@@ -81,7 +81,7 @@ export default {
<p class="gl-mb-4">
{{ $options.i18n.addImageText }}
</p>
- <gl-form-input-group class="gl-mb-4 ">
+ <gl-form-input-group class="gl-mb-4">
<gl-form-input
:value="dockerBuildCommand"
readonly
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue b/app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue
index c2bd01701df..f01e3c9d24a 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue
@@ -13,6 +13,7 @@ import {
} from '../../constants/index';
export default {
+ name: 'ListHeader',
components: {
TitleArea,
MetadataItem,
@@ -43,6 +44,11 @@ export default {
required: false,
default: false,
},
+ metadataLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
loader: {
repeat: 10,
@@ -92,7 +98,11 @@ export default {
</script>
<template>
- <title-area :title="$options.i18n.CONTAINER_REGISTRY_TITLE" :info-messages="infoMessages">
+ <title-area
+ :title="$options.i18n.CONTAINER_REGISTRY_TITLE"
+ :info-messages="infoMessages"
+ :metadata-loading="metadataLoading"
+ >
<template #right-actions>
<slot name="commands"></slot>
</template>
diff --git a/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue b/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue
index 1cedcc41b2b..e77eda31596 100644
--- a/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue
+++ b/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue
@@ -1,66 +1,51 @@
<script>
-/* eslint-disable vue/no-v-html */
-// We are forced to use `v-html` untill this gitlab-ui issue is resolved: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1079
-// then we can re-write this to use gl-breadcrumb
-import { initial, first, last } from 'lodash';
-import { sanitize } from '~/lib/dompurify';
+// We are using gl-breadcrumb only at the last child of the handwritten breadcrumb
+// until this gitlab-ui issue is resolved: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1079
+//
+// See the CSS workaround in app/assets/stylesheets/pages/registry.scss when this file is changed.
+import { GlBreadcrumb, GlIcon } from '@gitlab/ui';
export default {
- props: {
- crumbs: {
- type: Array,
- required: true,
- },
+ components: {
+ GlBreadcrumb,
+ GlIcon,
},
computed: {
- parsedCrumbs() {
- return this.crumbs.map(c => ({ ...c, innerHTML: sanitize(c.innerHTML) }));
- },
rootRoute() {
- return this.$router.options.routes.find(r => r.meta.root);
+ return this.$router.options.routes.find((r) => r.meta.root);
+ },
+ detailsRoute() {
+ return this.$router.options.routes.find((r) => r.name === 'details');
},
isRootRoute() {
return this.$route.name === this.rootRoute.name;
},
- rootCrumbs() {
- return initial(this.parsedCrumbs);
- },
- divider() {
- const { classList, tagName, innerHTML } = first(this.crumbs).querySelector('svg');
- return { classList: [...classList], tagName, innerHTML: sanitize(innerHTML) };
+ isLoaded() {
+ return this.isRootRoute || this.$store?.state.imageDetails?.name;
},
- lastCrumb() {
- const { children } = last(this.crumbs);
- const { tagName, className } = first(children);
- return {
- tagName,
- className,
- text: this.$route.meta.nameGenerator(),
- path: { to: this.$route.name },
- };
+ allCrumbs() {
+ const crumbs = [
+ {
+ text: this.rootRoute.meta.nameGenerator(),
+ to: this.rootRoute.path,
+ },
+ ];
+ if (!this.isRootRoute) {
+ crumbs.push({
+ text: this.detailsRoute.meta.nameGenerator(),
+ href: this.detailsRoute.meta.path,
+ });
+ }
+ return crumbs;
},
},
};
</script>
<template>
- <ul>
- <li
- v-for="(crumb, index) in rootCrumbs"
- :key="index"
- :class="crumb.className"
- v-html="crumb.innerHTML"
- ></li>
- <li v-if="!isRootRoute">
- <router-link ref="rootRouteLink" :to="rootRoute.path">
- {{ rootRoute.meta.nameGenerator() }}
- </router-link>
- <component :is="divider.tagName" :class="divider.classList" v-html="divider.innerHTML" />
- </li>
- <li>
- <component :is="lastCrumb.tagName" ref="lastCrumb" :class="lastCrumb.className">
- <router-link ref="childRouteLink" :to="lastCrumb.path">{{ lastCrumb.text }}</router-link>
- </component>
- </li>
- </ul>
+ <gl-breadcrumb :key="isLoaded" :items="allCrumbs">
+ <template #separator>
+ <gl-icon name="angle-right" :size="8" />
+ </template>
+ </gl-breadcrumb>
</template>
diff --git a/app/assets/javascripts/registry/explorer/constants/details.js b/app/assets/javascripts/registry/explorer/constants/details.js
index 1babaaa93da..b5627352857 100644
--- a/app/assets/javascripts/registry/explorer/constants/details.js
+++ b/app/assets/javascripts/registry/explorer/constants/details.js
@@ -60,6 +60,22 @@ export const UPDATED_AT = s__('ContainerRegistry|Last updated %{time}');
export const NOT_AVAILABLE_TEXT = __('N/A');
export const NOT_AVAILABLE_SIZE = __('0 bytes');
+
+export const CLEANUP_UNSCHEDULED_TEXT = s__('ContainerRegistry|Cleanup will run %{time}');
+export const CLEANUP_SCHEDULED_TEXT = s__('ContainerRegistry|Cleanup pending');
+export const CLEANUP_ONGOING_TEXT = s__('ContainerRegistry|Cleanup in progress');
+export const CLEANUP_UNFINISHED_TEXT = s__('ContainerRegistry|Cleanup incomplete');
+export const CLEANUP_DISABLED_TEXT = s__('ContainerRegistry|Cleanup disabled');
+
+export const CLEANUP_SCHEDULED_TOOLTIP = s__('ContainerRegistry|Cleanup will run soon');
+export const CLEANUP_ONGOING_TOOLTIP = s__('ContainerRegistry|Cleanup is currently removing tags');
+export const CLEANUP_UNFINISHED_TOOLTIP = s__(
+ 'ContainerRegistry|Cleanup ran but some tags were not removed',
+);
+export const CLEANUP_DISABLED_TOOLTIP = s__(
+ 'ContainerRegistry|Cleanup is disabled for this project',
+);
+
// Parameters
export const DEFAULT_PAGE = 1;
@@ -76,3 +92,8 @@ export const ALERT_MESSAGES = {
[ALERT_SUCCESS_TAGS]: DELETE_TAGS_SUCCESS_MESSAGE,
[ALERT_DANGER_TAGS]: DELETE_TAGS_ERROR_MESSAGE,
};
+
+export const UNFINISHED_STATUS = 'UNFINISHED';
+export const UNSCHEDULED_STATUS = 'UNSCHEDULED';
+export const SCHEDULED_STATUS = 'SCHEDULED';
+export const ONGOING_STATUS = 'ONGOING';
diff --git a/app/assets/javascripts/registry/explorer/graphql/fragments/container_repository.fragment.graphql b/app/assets/javascripts/registry/explorer/graphql/fragments/container_repository.fragment.graphql
deleted file mode 100644
index 9a3579ee8e0..00000000000
--- a/app/assets/javascripts/registry/explorer/graphql/fragments/container_repository.fragment.graphql
+++ /dev/null
@@ -1,11 +0,0 @@
-fragment ContainerRepositoryFields on ContainerRepository {
- id
- name
- path
- status
- location
- canDelete
- createdAt
- tagsCount
- expirationPolicyStartedAt
-}
diff --git a/app/assets/javascripts/registry/explorer/graphql/index.js b/app/assets/javascripts/registry/explorer/graphql/index.js
index 16152eb81f6..d934bcc7419 100644
--- a/app/assets/javascripts/registry/explorer/graphql/index.js
+++ b/app/assets/javascripts/registry/explorer/graphql/index.js
@@ -8,6 +8,7 @@ export const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(
{},
{
+ batchMax: 1,
assumeImmutableResults: true,
},
),
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repositories_details.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repositories_details.query.graphql
new file mode 100644
index 00000000000..8b6d778c655
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repositories_details.query.graphql
@@ -0,0 +1,26 @@
+query getContainerRepositoriesDetails(
+ $fullPath: ID!
+ $name: String
+ $first: Int
+ $last: Int
+ $after: String
+ $before: String
+ $isGroupPage: Boolean!
+) {
+ project(fullPath: $fullPath) @skip(if: $isGroupPage) {
+ containerRepositories(name: $name, after: $after, before: $before, first: $first, last: $last) {
+ nodes {
+ id
+ tagsCount
+ }
+ }
+ }
+ group(fullPath: $fullPath) @include(if: $isGroupPage) {
+ containerRepositories(name: $name, after: $after, before: $before, first: $first, last: $last) {
+ nodes {
+ id
+ tagsCount
+ }
+ }
+ }
+}
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 b40200e020b..3fd019467ac 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
@@ -18,6 +18,7 @@ query getContainerRepositoryDetails(
updatedAt
tagsCount
expirationPolicyStartedAt
+ expirationPolicyCleanupStatus
tags(after: $after, before: $before, first: $first, last: $last) {
nodes {
digest
@@ -36,6 +37,10 @@ query getContainerRepositoryDetails(
}
project {
visibility
+ containerExpirationPolicy {
+ enabled
+ nextRunAt
+ }
}
}
}
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_group_container_repositories.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_group_container_repositories.query.graphql
deleted file mode 100644
index 348eda97ea7..00000000000
--- a/app/assets/javascripts/registry/explorer/graphql/queries/get_group_container_repositories.query.graphql
+++ /dev/null
@@ -1,23 +0,0 @@
-#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
-#import "../fragments/container_repository.fragment.graphql"
-
-query getGroupContainerRepositories(
- $fullPath: ID!
- $name: String
- $first: Int
- $last: Int
- $after: String
- $before: String
-) {
- group(fullPath: $fullPath) {
- containerRepositoriesCount
- containerRepositories(name: $name, after: $after, before: $before, first: $first, last: $last) {
- nodes {
- ...ContainerRepositoryFields
- }
- pageInfo {
- ...PageInfo
- }
- }
- }
-}
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_project_container_repositories.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_project_container_repositories.query.graphql
deleted file mode 100644
index 338e27745f7..00000000000
--- a/app/assets/javascripts/registry/explorer/graphql/queries/get_project_container_repositories.query.graphql
+++ /dev/null
@@ -1,23 +0,0 @@
-#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
-#import "../fragments/container_repository.fragment.graphql"
-
-query getProjectContainerRepositories(
- $fullPath: ID!
- $name: String
- $first: Int
- $last: Int
- $after: String
- $before: String
-) {
- project(fullPath: $fullPath) {
- containerRepositoriesCount
- containerRepositories(name: $name, after: $after, before: $before, first: $first, last: $last) {
- nodes {
- ...ContainerRepositoryFields
- }
- pageInfo {
- ...PageInfo
- }
- }
- }
-}
diff --git a/app/assets/javascripts/registry/explorer/index.js b/app/assets/javascripts/registry/explorer/index.js
index d887b6a1b15..a3890ab5c42 100644
--- a/app/assets/javascripts/registry/explorer/index.js
+++ b/app/assets/javascripts/registry/explorer/index.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import { GlToast } from '@gitlab/ui';
import Translate from '~/vue_shared/translate';
import { parseBoolean } from '~/lib/utils/common_utils';
+import PerformancePlugin from '~/performance/vue_performance_plugin';
import RegistryExplorer from './pages/index.vue';
import RegistryBreadcrumb from './components/registry_breadcrumb.vue';
import createRouter from './router';
@@ -10,6 +11,17 @@ import { apolloProvider } from './graphql/index';
Vue.use(Translate);
Vue.use(GlToast);
+Vue.use(PerformancePlugin, {
+ components: [
+ 'RegistryListPage',
+ 'ListHeader',
+ 'ImageListRow',
+ 'RegistryDetailsPage',
+ 'DetailsHeader',
+ 'TagsList',
+ ],
+});
+
export default () => {
const el = document.getElementById('js-container-registry');
@@ -59,16 +71,28 @@ export default () => {
});
const attachBreadcrumb = () => {
- const breadCrumbEl = document.querySelector('nav .js-breadcrumbs-list');
- const crumbs = [...document.querySelectorAll('.js-breadcrumbs-list li')];
+ const breadCrumbEls = document.querySelectorAll('nav .js-breadcrumbs-list li');
+ const breadCrumbEl = breadCrumbEls[breadCrumbEls.length - 1];
+ const crumbs = [breadCrumbEl.querySelector('h2')];
+ const nestedBreadcrumbEl = document.createElement('div');
+ breadCrumbEl.replaceChild(nestedBreadcrumbEl, breadCrumbEl.querySelector('h2'));
return new Vue({
- el: breadCrumbEl,
+ el: nestedBreadcrumbEl,
router,
apolloProvider,
components: {
RegistryBreadcrumb,
},
render(createElement) {
+ // FIXME(@tnir): this is a workaround until the MR gets merged:
+ // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48115
+ const parentEl = breadCrumbEl.parentElement.parentElement;
+ if (parentEl) {
+ parentEl.classList.remove('breadcrumbs-container');
+ parentEl.classList.add('gl-display-flex');
+ parentEl.classList.add('w-100');
+ }
+ // End of FIXME(@tnir)
return createElement('registry-breadcrumb', {
class: breadCrumbEl.className,
props: {
diff --git a/app/assets/javascripts/registry/explorer/pages/details.vue b/app/assets/javascripts/registry/explorer/pages/details.vue
index 540f02d58d4..0894fd6fcfa 100644
--- a/app/assets/javascripts/registry/explorer/pages/details.vue
+++ b/app/assets/javascripts/registry/explorer/pages/details.vue
@@ -22,9 +22,11 @@ import {
ALERT_DANGER_TAGS,
GRAPHQL_PAGE_SIZE,
FETCH_IMAGES_LIST_ERROR_MESSAGE,
+ UNFINISHED_STATUS,
} from '../constants/index';
export default {
+ name: 'RegistryDetailsPage',
components: {
DeleteAlert,
PartialCleanupAlert,
@@ -35,11 +37,11 @@ export default {
TagsLoader,
EmptyTagsState,
},
- inject: ['breadCrumbState', 'config'],
directives: {
GlResizeObserver: GlResizeObserverDirective,
},
mixins: [Tracking.mixin()],
+ inject: ['breadCrumbState', 'config'],
apollo: {
image: {
query: getContainerRepositoryDetailsQuery,
@@ -83,7 +85,10 @@ export default {
return this.image?.tags?.nodes || [];
},
showPartialCleanupWarning() {
- return this.image?.expirationPolicyStartedAt && !this.dismissPartialCleanupWarning;
+ return (
+ this.image?.expirationPolicyCleanupStatus === UNFINISHED_STATUS &&
+ !this.dismissPartialCleanupWarning
+ );
},
tracking() {
return {
@@ -97,7 +102,7 @@ export default {
},
methods: {
deleteTags(toBeDeleted) {
- this.itemsToBeDeleted = this.tags.filter(tag => toBeDeleted[tag.name]);
+ this.itemsToBeDeleted = this.tags.filter((tag) => toBeDeleted[tag.name]);
this.track('click_button');
this.$refs.deleteModal.show();
},
@@ -111,7 +116,7 @@ export default {
mutation: deleteContainerRepositoryTagsMutation,
variables: {
id: this.queryVariables.id,
- tagNames: itemsToBeDeleted.map(i => i.name),
+ tagNames: itemsToBeDeleted.map((i) => i.name),
},
awaitRefetchQueries: true,
refetchQueries: [
@@ -183,7 +188,7 @@ export default {
@dismiss="dismissPartialCleanupWarning = true"
/>
- <details-header :image="image" />
+ <details-header :image="image" :metadata-loading="isLoading" />
<tags-loader v-if="isLoading" />
<template v-else>
diff --git a/app/assets/javascripts/registry/explorer/pages/list.vue b/app/assets/javascripts/registry/explorer/pages/list.vue
index 3192ba82db8..336a997d629 100644
--- a/app/assets/javascripts/registry/explorer/pages/list.vue
+++ b/app/assets/javascripts/registry/explorer/pages/list.vue
@@ -9,17 +9,13 @@ import {
GlSkeletonLoader,
GlSearchBoxByClick,
} from '@gitlab/ui';
+import { get } from 'lodash';
+import getContainerRepositoriesQuery from 'shared_queries/container_registry/get_container_repositories.query.graphql';
import Tracking from '~/tracking';
import createFlash from '~/flash';
-
-import ProjectEmptyState from '../components/list_page/project_empty_state.vue';
-import GroupEmptyState from '../components/list_page/group_empty_state.vue';
import RegistryHeader from '../components/list_page/registry_header.vue';
-import ImageList from '../components/list_page/image_list.vue';
-import CliCommands from '../components/list_page/cli_commands.vue';
-import getProjectContainerRepositoriesQuery from '../graphql/queries/get_project_container_repositories.query.graphql';
-import getGroupContainerRepositoriesQuery from '../graphql/queries/get_group_container_repositories.query.graphql';
+import getContainerRepositoriesDetails from '../graphql/queries/get_container_repositories_details.query.graphql';
import deleteContainerRepositoryMutation from '../graphql/mutations/delete_container_repository.mutation.graphql';
import {
@@ -38,12 +34,25 @@ import {
} from '../constants/index';
export default {
- name: 'RegistryListApp',
+ name: 'RegistryListPage',
components: {
GlEmptyState,
- ProjectEmptyState,
- GroupEmptyState,
- ImageList,
+ ProjectEmptyState: () =>
+ import(
+ /* webpackChunkName: 'container_registry_components' */ '../components/list_page/project_empty_state.vue'
+ ),
+ GroupEmptyState: () =>
+ import(
+ /* webpackChunkName: 'container_registry_components' */ '../components/list_page/group_empty_state.vue'
+ ),
+ ImageList: () =>
+ import(
+ /* webpackChunkName: 'container_registry_components' */ '../components/list_page/image_list.vue'
+ ),
+ CliCommands: () =>
+ import(
+ /* webpackChunkName: 'container_registry_components' */ '../components/list_page/cli_commands.vue'
+ ),
GlModal,
GlSprintf,
GlLink,
@@ -51,13 +60,12 @@ export default {
GlSkeletonLoader,
GlSearchBoxByClick,
RegistryHeader,
- CliCommands,
},
- inject: ['config'],
directives: {
GlTooltip: GlTooltipDirective,
},
mixins: [Tracking.mixin()],
+ inject: ['config'],
loader: {
repeat: 10,
width: 1000,
@@ -74,10 +82,8 @@ export default {
EMPTY_RESULT_MESSAGE,
},
apollo: {
- images: {
- query() {
- return this.graphQlQuery;
- },
+ baseImages: {
+ query: getContainerRepositoriesQuery,
variables() {
return this.queryVariables;
},
@@ -92,10 +98,26 @@ export default {
createFlash({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
},
},
+ additionalDetails: {
+ skip() {
+ return !this.fetchAdditionalDetails;
+ },
+ query: getContainerRepositoriesDetails,
+ variables() {
+ return this.queryVariables;
+ },
+ update(data) {
+ return data[this.graphqlResource]?.containerRepositories.nodes;
+ },
+ error() {
+ createFlash({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
+ },
+ },
},
data() {
return {
- images: [],
+ baseImages: [],
+ additionalDetails: [],
pageInfo: {},
containerRepositoriesCount: 0,
itemToDelete: {},
@@ -103,21 +125,24 @@ export default {
searchValue: null,
name: null,
mutationLoading: false,
+ fetchAdditionalDetails: false,
};
},
computed: {
+ images() {
+ return this.baseImages.map((image, index) => ({
+ ...image,
+ ...get(this.additionalDetails, index, {}),
+ }));
+ },
graphqlResource() {
return this.config.isGroupPage ? 'group' : 'project';
},
- graphQlQuery() {
- return this.config.isGroupPage
- ? getGroupContainerRepositoriesQuery
- : getProjectContainerRepositoriesQuery;
- },
queryVariables() {
return {
name: this.name,
fullPath: this.config.isGroupPage ? this.config.groupPath : this.config.projectPath,
+ isGroupPage: this.config.isGroupPage,
first: GRAPHQL_PAGE_SIZE,
};
},
@@ -127,7 +152,7 @@ export default {
};
},
isLoading() {
- return this.$apollo.queries.images.loading || this.mutationLoading;
+ return this.$apollo.queries.baseImages.loading || this.mutationLoading;
},
showCommands() {
return Boolean(!this.isLoading && !this.config?.isGroupPage && this.images?.length);
@@ -141,6 +166,13 @@ export default {
: DELETE_IMAGE_ERROR_MESSAGE;
},
},
+ mounted() {
+ // If the two graphql calls - which are not batched - resolve togheter we will have a race
+ // condition when apollo sets the cache, with this we give the 'base' call an headstart
+ setTimeout(() => {
+ this.fetchAdditionalDetails = true;
+ }, 200);
+ },
methods: {
deleteImage(item) {
this.track('click_button');
@@ -175,30 +207,46 @@ export default {
this.deleteAlertType = null;
this.itemToDelete = {};
},
- fetchNextPage() {
+ updateQuery(_, { fetchMoreResult }) {
+ return fetchMoreResult;
+ },
+ async fetchNextPage() {
if (this.pageInfo?.hasNextPage) {
- this.$apollo.queries.images.fetchMore({
- variables: {
- after: this.pageInfo?.endCursor,
- first: GRAPHQL_PAGE_SIZE,
- },
- updateQuery(previousResult, { fetchMoreResult }) {
- return fetchMoreResult;
- },
+ const variables = {
+ after: this.pageInfo?.endCursor,
+ first: GRAPHQL_PAGE_SIZE,
+ };
+
+ this.$apollo.queries.baseImages.fetchMore({
+ variables,
+ updateQuery: this.updateQuery,
+ });
+
+ await this.$nextTick();
+
+ this.$apollo.queries.additionalDetails.fetchMore({
+ variables,
+ updateQuery: this.updateQuery,
});
}
},
- fetchPreviousPage() {
+ async fetchPreviousPage() {
if (this.pageInfo?.hasPreviousPage) {
- this.$apollo.queries.images.fetchMore({
- variables: {
- first: null,
- before: this.pageInfo?.startCursor,
- last: GRAPHQL_PAGE_SIZE,
- },
- updateQuery(previousResult, { fetchMoreResult }) {
- return fetchMoreResult;
- },
+ const variables = {
+ first: null,
+ before: this.pageInfo?.startCursor,
+ last: GRAPHQL_PAGE_SIZE,
+ };
+ this.$apollo.queries.baseImages.fetchMore({
+ variables,
+ updateQuery: this.updateQuery,
+ });
+
+ await this.$nextTick();
+
+ this.$apollo.queries.additionalDetails.fetchMore({
+ variables,
+ updateQuery: this.updateQuery,
});
}
},
@@ -230,7 +278,7 @@ export default {
<template #description>
<p>
<gl-sprintf :message="$options.i18n.CONNECTION_ERROR_MESSAGE">
- <template #docLink="{content}">
+ <template #docLink="{ content }">
<gl-link :href="`${config.helpPagePath}#docker-connection-error`" target="_blank">
{{ content }}
</gl-link>
@@ -242,6 +290,7 @@ export default {
<template v-else>
<registry-header
+ :metadata-loading="isLoading"
:images-count="containerRepositoriesCount"
:expiration-policy="config.expirationPolicy"
:help-page-path="config.helpPagePath"
@@ -285,6 +334,7 @@ export default {
<image-list
v-if="images.length"
:images="images"
+ :metadata-loading="$apollo.queries.additionalDetails.loading"
:page-info="pageInfo"
@delete="deleteImage"
@prev-page="fetchPreviousPage"
diff --git a/app/assets/javascripts/registry/settings/components/expiration_input.vue b/app/assets/javascripts/registry/settings/components/expiration_input.vue
index 2dbd9d26f60..42b7c7918a5 100644
--- a/app/assets/javascripts/registry/settings/components/expiration_input.vue
+++ b/app/assets/javascripts/registry/settings/components/expiration_input.vue
@@ -83,7 +83,7 @@ export default {
<template #label>
<span data-testid="label">
<gl-sprintf :message="label">
- <template #italic="{content}">
+ <template #italic="{ content }">
<i>{{ content }}</i>
</template>
</gl-sprintf>
@@ -100,7 +100,7 @@ export default {
<template #description>
<span data-testid="description" class="gl-text-gray-400">
<gl-sprintf :message="description">
- <template #link="{content}">
+ <template #link="{ content }">
<gl-link :href="tagsRegexHelpPagePath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
diff --git a/app/assets/javascripts/registry/settings/components/expiration_toggle.vue b/app/assets/javascripts/registry/settings/components/expiration_toggle.vue
index 7f045244926..0ffd8216ab1 100644
--- a/app/assets/javascripts/registry/settings/components/expiration_toggle.vue
+++ b/app/assets/javascripts/registry/settings/components/expiration_toggle.vue
@@ -42,7 +42,7 @@ export default {
<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}">
+ <template #strong="{ content }">
<strong>{{ content }}</strong>
</template>
</gl-sprintf>
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 35c7a8be4ea..66eb681784e 100644
--- a/app/assets/javascripts/registry/settings/components/registry_settings_app.vue
+++ b/app/assets/javascripts/registry/settings/components/registry_settings_app.vue
@@ -33,7 +33,7 @@ export default {
projectPath: this.projectPath,
};
},
- update: data => data.project?.containerExpirationPolicy,
+ update: (data) => data.project?.containerExpirationPolicy,
result({ data }) {
this.workingCopy = { ...get(data, 'project.containerExpirationPolicy', {}) };
},
diff --git a/app/assets/javascripts/registry/settings/components/settings_form.vue b/app/assets/javascripts/registry/settings/components/settings_form.vue
index 1f374c7b60e..7043cea49ba 100644
--- a/app/assets/javascripts/registry/settings/components/settings_form.vue
+++ b/app/assets/javascripts/registry/settings/components/settings_form.vue
@@ -96,7 +96,7 @@ export default {
return this.isLoading || this.mutationLoading;
},
fieldsAreValid() {
- return Object.values(this.localErrors).every(error => error);
+ return Object.values(this.localErrors).every((error) => error);
},
isSubmitButtonDisabled() {
return !this.fieldsAreValid || this.showLoadingIcon;
@@ -121,7 +121,7 @@ export default {
},
methods: {
findDefaultOption(option) {
- return this.value[option] || this.$options.formOptions[option].find(f => f.default)?.key;
+ return this.value[option] || this.$options.formOptions[option].find((f) => f.default)?.key;
},
reset() {
this.track('reset_form');
@@ -131,7 +131,7 @@ export default {
},
setApiErrors(response) {
this.apiErrors = response.graphQLErrors.reduce((acc, curr) => {
- curr.extensions.problems.forEach(item => {
+ curr.extensions.problems.forEach((item) => {
acc[item.path[0]] = item.message;
});
return acc;
@@ -163,7 +163,7 @@ export default {
this.$toast.show(UPDATE_SETTINGS_SUCCESS_MESSAGE, { type: 'success' });
}
})
- .catch(error => {
+ .catch((error) => {
this.setApiErrors(error);
this.$toast.show(UPDATE_SETTINGS_ERROR_MESSAGE, { type: 'error' });
})
@@ -214,10 +214,10 @@ export default {
<div>
<p>
<gl-sprintf :message="$options.i18n.KEEP_INFO_TEXT">
- <template #strong="{content}">
+ <template #strong="{ content }">
<strong>{{ content }}</strong>
</template>
- <template #secondStrong="{content}">
+ <template #secondStrong="{ content }">
<strong>{{ content }}</strong>
</template>
</gl-sprintf>
@@ -253,10 +253,10 @@ export default {
<div>
<p>
<gl-sprintf :message="$options.i18n.REMOVE_INFO_TEXT">
- <template #strong="{content}">
+ <template #strong="{ content }">
<strong>{{ content }}</strong>
</template>
- <template #secondStrong="{content}">
+ <template #secondStrong="{ content }">
<strong>{{ content }}</strong>
</template>
</gl-sprintf>
diff --git a/app/assets/javascripts/registry/settings/constants.js b/app/assets/javascripts/registry/settings/constants.js
index 21c54299632..165c4aae3cb 100644
--- a/app/assets/javascripts/registry/settings/constants.js
+++ b/app/assets/javascripts/registry/settings/constants.js
@@ -23,7 +23,7 @@ export const KEEP_INFO_TEXT = s__(
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}',
+ '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');
@@ -34,7 +34,7 @@ export const EXPIRATION_SCHEDULE_LABEL = s__('ContainerRegistry|Remove tags olde
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}',
+ 'ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}View regex examples.%{linkEnd}',
);
export const ENABLED_TOGGLE_DESCRIPTION = s__(
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 05b4125a2fc..6becaa38c7e 100644
--- a/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js
+++ b/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js
@@ -1,14 +1,14 @@
import { produce } from 'immer';
import expirationPolicyQuery from '../queries/get_expiration_policy.query.graphql';
-export const updateContainerExpirationPolicy = projectPath => (client, { data: updatedData }) => {
+export const updateContainerExpirationPolicy = (projectPath) => (client, { data: updatedData }) => {
const queryAndParams = {
query: expirationPolicyQuery,
variables: { projectPath },
};
const sourceData = client.readQuery(queryAndParams);
- const data = produce(sourceData, draftState => {
+ const data = produce(sourceData, (draftState) => {
// eslint-disable-next-line no-param-reassign
draftState.project.containerExpirationPolicy = {
...updatedData.updateContainerExpirationPolicy.containerExpirationPolicy,
diff --git a/app/assets/javascripts/registry/settings/utils.js b/app/assets/javascripts/registry/settings/utils.js
index 51b4fb6bdb8..4a2d7c7d466 100644
--- a/app/assets/javascripts/registry/settings/utils.js
+++ b/app/assets/javascripts/registry/settings/utils.js
@@ -1,18 +1,18 @@
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);
+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 olderThanTranslationGenerator = (variable) => n__('%d day', '%d days', variable);
-export const keepNTranslationGenerator = 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 => ({
+ collection.map((option) => ({
...option,
label: translationFn(option.variable),
}));