2020-03-13 15:44:24 +05:30
|
|
|
<script>
|
|
|
|
import {
|
|
|
|
GlEmptyState,
|
|
|
|
GlTooltipDirective,
|
|
|
|
GlModal,
|
|
|
|
GlSprintf,
|
|
|
|
GlLink,
|
2020-04-22 19:07:51 +05:30
|
|
|
GlAlert,
|
2020-04-08 14:13:33 +05:30
|
|
|
GlSkeletonLoader,
|
2020-03-13 15:44:24 +05:30
|
|
|
} from '@gitlab/ui';
|
2021-03-08 18:12:59 +05:30
|
|
|
import { get } from 'lodash';
|
|
|
|
import getContainerRepositoriesQuery from 'shared_queries/container_registry/get_container_repositories.query.graphql';
|
2021-02-22 17:27:13 +05:30
|
|
|
import createFlash from '~/flash';
|
2021-03-11 19:13:27 +05:30
|
|
|
import Tracking from '~/tracking';
|
|
|
|
import RegistrySearch from '~/vue_shared/components/registry/registry_search.vue';
|
|
|
|
import DeleteImage from '../components/delete_image.vue';
|
2020-06-23 00:09:42 +05:30
|
|
|
import RegistryHeader from '../components/list_page/registry_header.vue';
|
2020-05-24 23:13:21 +05:30
|
|
|
|
2020-04-22 19:07:51 +05:30
|
|
|
import {
|
|
|
|
DELETE_IMAGE_SUCCESS_MESSAGE,
|
|
|
|
DELETE_IMAGE_ERROR_MESSAGE,
|
|
|
|
CONNECTION_ERROR_TITLE,
|
|
|
|
CONNECTION_ERROR_MESSAGE,
|
|
|
|
REMOVE_REPOSITORY_MODAL_TEXT,
|
2020-05-24 23:13:21 +05:30
|
|
|
REMOVE_REPOSITORY_LABEL,
|
|
|
|
EMPTY_RESULT_TITLE,
|
|
|
|
EMPTY_RESULT_MESSAGE,
|
2021-02-22 17:27:13 +05:30
|
|
|
GRAPHQL_PAGE_SIZE,
|
|
|
|
FETCH_IMAGES_LIST_ERROR_MESSAGE,
|
2021-03-11 19:13:27 +05:30
|
|
|
SORT_FIELDS,
|
2020-06-23 00:09:42 +05:30
|
|
|
} from '../constants/index';
|
2021-03-11 19:13:27 +05:30
|
|
|
import getContainerRepositoriesDetails from '../graphql/queries/get_container_repositories_details.query.graphql';
|
2020-03-13 15:44:24 +05:30
|
|
|
|
|
|
|
export default {
|
2021-03-08 18:12:59 +05:30
|
|
|
name: 'RegistryListPage',
|
2020-03-13 15:44:24 +05:30
|
|
|
components: {
|
|
|
|
GlEmptyState,
|
2021-03-08 18:12:59 +05:30
|
|
|
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'
|
|
|
|
),
|
2020-03-13 15:44:24 +05:30
|
|
|
GlModal,
|
|
|
|
GlSprintf,
|
|
|
|
GlLink,
|
2020-04-22 19:07:51 +05:30
|
|
|
GlAlert,
|
2020-04-08 14:13:33 +05:30
|
|
|
GlSkeletonLoader,
|
2020-06-23 00:09:42 +05:30
|
|
|
RegistryHeader,
|
2021-03-11 19:13:27 +05:30
|
|
|
DeleteImage,
|
|
|
|
RegistrySearch,
|
2020-03-13 15:44:24 +05:30
|
|
|
},
|
|
|
|
directives: {
|
|
|
|
GlTooltip: GlTooltipDirective,
|
|
|
|
},
|
|
|
|
mixins: [Tracking.mixin()],
|
2021-03-08 18:12:59 +05:30
|
|
|
inject: ['config'],
|
2020-04-08 14:13:33 +05:30
|
|
|
loader: {
|
|
|
|
repeat: 10,
|
|
|
|
width: 1000,
|
|
|
|
height: 40,
|
|
|
|
},
|
2020-04-22 19:07:51 +05:30
|
|
|
i18n: {
|
2020-05-24 23:13:21 +05:30
|
|
|
CONNECTION_ERROR_TITLE,
|
|
|
|
CONNECTION_ERROR_MESSAGE,
|
|
|
|
REMOVE_REPOSITORY_MODAL_TEXT,
|
|
|
|
REMOVE_REPOSITORY_LABEL,
|
|
|
|
EMPTY_RESULT_TITLE,
|
|
|
|
EMPTY_RESULT_MESSAGE,
|
2020-04-22 19:07:51 +05:30
|
|
|
},
|
2021-03-11 19:13:27 +05:30
|
|
|
searchConfig: SORT_FIELDS,
|
2021-02-22 17:27:13 +05:30
|
|
|
apollo: {
|
2021-03-08 18:12:59 +05:30
|
|
|
baseImages: {
|
|
|
|
query: getContainerRepositoriesQuery,
|
2021-02-22 17:27:13 +05:30
|
|
|
variables() {
|
|
|
|
return this.queryVariables;
|
|
|
|
},
|
|
|
|
update(data) {
|
|
|
|
return data[this.graphqlResource]?.containerRepositories.nodes;
|
|
|
|
},
|
|
|
|
result({ data }) {
|
|
|
|
this.pageInfo = data[this.graphqlResource]?.containerRepositories?.pageInfo;
|
|
|
|
this.containerRepositoriesCount = data[this.graphqlResource]?.containerRepositoriesCount;
|
|
|
|
},
|
|
|
|
error() {
|
|
|
|
createFlash({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
|
|
|
|
},
|
|
|
|
},
|
2021-03-08 18:12:59 +05:30
|
|
|
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 });
|
|
|
|
},
|
|
|
|
},
|
2021-02-22 17:27:13 +05:30
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
data() {
|
|
|
|
return {
|
2021-03-08 18:12:59 +05:30
|
|
|
baseImages: [],
|
|
|
|
additionalDetails: [],
|
2021-02-22 17:27:13 +05:30
|
|
|
pageInfo: {},
|
|
|
|
containerRepositoriesCount: 0,
|
2020-03-13 15:44:24 +05:30
|
|
|
itemToDelete: {},
|
2020-04-22 19:07:51 +05:30
|
|
|
deleteAlertType: null,
|
2021-03-11 19:13:27 +05:30
|
|
|
filter: [],
|
|
|
|
sorting: { orderBy: 'UPDATED', sort: 'desc' },
|
2021-02-22 17:27:13 +05:30
|
|
|
name: null,
|
|
|
|
mutationLoading: false,
|
2021-03-08 18:12:59 +05:30
|
|
|
fetchAdditionalDetails: false,
|
2020-03-13 15:44:24 +05:30
|
|
|
};
|
|
|
|
},
|
|
|
|
computed: {
|
2021-03-08 18:12:59 +05:30
|
|
|
images() {
|
|
|
|
return this.baseImages.map((image, index) => ({
|
|
|
|
...image,
|
|
|
|
...get(this.additionalDetails, index, {}),
|
|
|
|
}));
|
|
|
|
},
|
2021-02-22 17:27:13 +05:30
|
|
|
graphqlResource() {
|
|
|
|
return this.config.isGroupPage ? 'group' : 'project';
|
|
|
|
},
|
|
|
|
queryVariables() {
|
|
|
|
return {
|
|
|
|
name: this.name,
|
2021-03-11 19:13:27 +05:30
|
|
|
sort: this.sortBy,
|
2021-02-22 17:27:13 +05:30
|
|
|
fullPath: this.config.isGroupPage ? this.config.groupPath : this.config.projectPath,
|
2021-03-08 18:12:59 +05:30
|
|
|
isGroupPage: this.config.isGroupPage,
|
2021-02-22 17:27:13 +05:30
|
|
|
first: GRAPHQL_PAGE_SIZE,
|
|
|
|
};
|
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
tracking() {
|
|
|
|
return {
|
|
|
|
label: 'registry_repository_delete',
|
|
|
|
};
|
|
|
|
},
|
2021-02-22 17:27:13 +05:30
|
|
|
isLoading() {
|
2021-03-08 18:12:59 +05:30
|
|
|
return this.$apollo.queries.baseImages.loading || this.mutationLoading;
|
2021-02-22 17:27:13 +05:30
|
|
|
},
|
2020-06-23 00:09:42 +05:30
|
|
|
showCommands() {
|
2020-04-22 19:07:51 +05:30
|
|
|
return Boolean(!this.isLoading && !this.config?.isGroupPage && this.images?.length);
|
|
|
|
},
|
|
|
|
showDeleteAlert() {
|
|
|
|
return this.deleteAlertType && this.itemToDelete?.path;
|
|
|
|
},
|
|
|
|
deleteImageAlertMessage() {
|
|
|
|
return this.deleteAlertType === 'success'
|
|
|
|
? DELETE_IMAGE_SUCCESS_MESSAGE
|
|
|
|
: DELETE_IMAGE_ERROR_MESSAGE;
|
|
|
|
},
|
2021-03-11 19:13:27 +05:30
|
|
|
sortBy() {
|
|
|
|
const { orderBy, sort } = this.sorting;
|
|
|
|
return `${orderBy}_${sort}`.toUpperCase();
|
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
},
|
2021-03-08 18:12:59 +05:30
|
|
|
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);
|
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
methods: {
|
|
|
|
deleteImage(item) {
|
|
|
|
this.track('click_button');
|
|
|
|
this.itemToDelete = item;
|
|
|
|
this.$refs.deleteModal.show();
|
|
|
|
},
|
2020-04-22 19:07:51 +05:30
|
|
|
dismissDeleteAlert() {
|
|
|
|
this.deleteAlertType = null;
|
|
|
|
this.itemToDelete = {};
|
|
|
|
},
|
2021-03-08 18:12:59 +05:30
|
|
|
updateQuery(_, { fetchMoreResult }) {
|
|
|
|
return fetchMoreResult;
|
|
|
|
},
|
|
|
|
async fetchNextPage() {
|
2021-02-22 17:27:13 +05:30
|
|
|
if (this.pageInfo?.hasNextPage) {
|
2021-03-08 18:12:59 +05:30
|
|
|
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,
|
2021-02-22 17:27:13 +05:30
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
2021-03-08 18:12:59 +05:30
|
|
|
async fetchPreviousPage() {
|
2021-02-22 17:27:13 +05:30
|
|
|
if (this.pageInfo?.hasPreviousPage) {
|
2021-03-08 18:12:59 +05:30
|
|
|
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,
|
2021-02-22 17:27:13 +05:30
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
2021-03-11 19:13:27 +05:30
|
|
|
startDelete() {
|
|
|
|
this.track('confirm_delete');
|
|
|
|
this.mutationLoading = true;
|
|
|
|
},
|
|
|
|
updateSorting(value) {
|
|
|
|
this.sorting = {
|
|
|
|
...this.sorting,
|
|
|
|
...value,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
doFilter() {
|
|
|
|
const search = this.filter.find((i) => i.type === 'filtered-search-term');
|
|
|
|
this.name = search?.value?.data;
|
|
|
|
},
|
2020-03-13 15:44:24 +05:30
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
2020-10-24 23:57:45 +05:30
|
|
|
<div>
|
2020-04-22 19:07:51 +05:30
|
|
|
<gl-alert
|
|
|
|
v-if="showDeleteAlert"
|
|
|
|
:variant="deleteAlertType"
|
2021-02-22 17:27:13 +05:30
|
|
|
class="gl-mt-5"
|
2020-04-22 19:07:51 +05:30
|
|
|
dismissible
|
|
|
|
@dismiss="dismissDeleteAlert"
|
|
|
|
>
|
|
|
|
<gl-sprintf :message="deleteImageAlertMessage">
|
|
|
|
<template #title>
|
|
|
|
{{ itemToDelete.path }}
|
|
|
|
</template>
|
|
|
|
</gl-sprintf>
|
|
|
|
</gl-alert>
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
<gl-empty-state
|
|
|
|
v-if="config.characterError"
|
2020-05-24 23:13:21 +05:30
|
|
|
:title="$options.i18n.CONNECTION_ERROR_TITLE"
|
2020-03-13 15:44:24 +05:30
|
|
|
:svg-path="config.containersErrorImage"
|
|
|
|
>
|
|
|
|
<template #description>
|
|
|
|
<p>
|
2020-05-24 23:13:21 +05:30
|
|
|
<gl-sprintf :message="$options.i18n.CONNECTION_ERROR_MESSAGE">
|
2021-03-08 18:12:59 +05:30
|
|
|
<template #docLink="{ content }">
|
2020-03-13 15:44:24 +05:30
|
|
|
<gl-link :href="`${config.helpPagePath}#docker-connection-error`" target="_blank">
|
|
|
|
{{ content }}
|
|
|
|
</gl-link>
|
|
|
|
</template>
|
|
|
|
</gl-sprintf>
|
|
|
|
</p>
|
|
|
|
</template>
|
|
|
|
</gl-empty-state>
|
|
|
|
|
|
|
|
<template v-else>
|
2020-06-23 00:09:42 +05:30
|
|
|
<registry-header
|
2021-03-08 18:12:59 +05:30
|
|
|
:metadata-loading="isLoading"
|
2021-02-22 17:27:13 +05:30
|
|
|
:images-count="containerRepositoriesCount"
|
2020-06-23 00:09:42 +05:30
|
|
|
:expiration-policy="config.expirationPolicy"
|
|
|
|
:help-page-path="config.helpPagePath"
|
|
|
|
:expiration-policy-help-page-path="config.expirationPolicyHelpPagePath"
|
|
|
|
:hide-expiration-policy-data="config.isGroupPage"
|
|
|
|
>
|
|
|
|
<template #commands>
|
|
|
|
<cli-commands v-if="showCommands" />
|
|
|
|
</template>
|
|
|
|
</registry-header>
|
2020-03-13 15:44:24 +05:30
|
|
|
|
2021-03-11 19:13:27 +05:30
|
|
|
<registry-search
|
|
|
|
:filter="filter"
|
|
|
|
:sorting="sorting"
|
|
|
|
:tokens="[]"
|
|
|
|
:sortable-fields="$options.searchConfig"
|
|
|
|
@sorting:changed="updateSorting"
|
|
|
|
@filter:changed="filter = $event"
|
|
|
|
@filter:submit="doFilter"
|
|
|
|
/>
|
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
<div v-if="isLoading" class="gl-mt-5">
|
2020-04-08 14:13:33 +05:30
|
|
|
<gl-skeleton-loader
|
|
|
|
v-for="index in $options.loader.repeat"
|
|
|
|
:key="index"
|
|
|
|
:width="$options.loader.width"
|
|
|
|
:height="$options.loader.height"
|
|
|
|
preserve-aspect-ratio="xMinYMax meet"
|
|
|
|
>
|
|
|
|
<rect width="500" x="10" y="10" height="20" rx="4" />
|
|
|
|
<circle cx="525" cy="20" r="10" />
|
|
|
|
<rect x="960" y="0" width="40" height="40" rx="4" />
|
|
|
|
</gl-skeleton-loader>
|
|
|
|
</div>
|
|
|
|
<template v-else>
|
2021-02-22 17:27:13 +05:30
|
|
|
<template v-if="images.length > 0 || name">
|
2020-05-24 23:13:21 +05:30
|
|
|
<image-list
|
|
|
|
v-if="images.length"
|
|
|
|
:images="images"
|
2021-03-08 18:12:59 +05:30
|
|
|
:metadata-loading="$apollo.queries.additionalDetails.loading"
|
2021-02-22 17:27:13 +05:30
|
|
|
:page-info="pageInfo"
|
2020-05-24 23:13:21 +05:30
|
|
|
@delete="deleteImage"
|
2021-02-22 17:27:13 +05:30
|
|
|
@prev-page="fetchPreviousPage"
|
|
|
|
@next-page="fetchNextPage"
|
2020-03-13 15:44:24 +05:30
|
|
|
/>
|
2020-04-08 14:13:33 +05:30
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
<gl-empty-state
|
|
|
|
v-else
|
|
|
|
:svg-path="config.noContainersImage"
|
|
|
|
data-testid="emptySearch"
|
|
|
|
:title="$options.i18n.EMPTY_RESULT_TITLE"
|
|
|
|
>
|
|
|
|
<template #description>
|
|
|
|
{{ $options.i18n.EMPTY_RESULT_MESSAGE }}
|
|
|
|
</template>
|
|
|
|
</gl-empty-state>
|
|
|
|
</template>
|
2020-03-13 15:44:24 +05:30
|
|
|
<template v-else>
|
|
|
|
<project-empty-state v-if="!config.isGroupPage" />
|
|
|
|
<group-empty-state v-else />
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
2021-03-11 19:13:27 +05:30
|
|
|
<delete-image
|
|
|
|
:id="itemToDelete.id"
|
|
|
|
@start="startDelete"
|
|
|
|
@error="deleteAlertType = 'danger'"
|
|
|
|
@success="deleteAlertType = 'success'"
|
|
|
|
@end="mutationLoading = false"
|
2020-03-13 15:44:24 +05:30
|
|
|
>
|
2021-03-11 19:13:27 +05:30
|
|
|
<template #default="{ doDelete }">
|
|
|
|
<gl-modal
|
|
|
|
ref="deleteModal"
|
|
|
|
modal-id="delete-image-modal"
|
|
|
|
:action-primary="{ text: __('Remove'), attributes: { variant: 'danger' } }"
|
|
|
|
@primary="doDelete"
|
|
|
|
@cancel="track('cancel_delete')"
|
|
|
|
>
|
|
|
|
<template #modal-title>{{ $options.i18n.REMOVE_REPOSITORY_LABEL }}</template>
|
|
|
|
<p>
|
|
|
|
<gl-sprintf :message="$options.i18n.REMOVE_REPOSITORY_MODAL_TEXT">
|
|
|
|
<template #title>
|
|
|
|
<b>{{ itemToDelete.path }}</b>
|
|
|
|
</template>
|
|
|
|
</gl-sprintf>
|
|
|
|
</p>
|
|
|
|
</gl-modal>
|
|
|
|
</template>
|
|
|
|
</delete-image>
|
2020-03-13 15:44:24 +05:30
|
|
|
</template>
|
|
|
|
</div>
|
|
|
|
</template>
|