diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-25 12:08:19 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-25 12:08:19 +0000 |
commit | e6baeabaa9651d90b03bb64ffce75a2c3cb89aab (patch) | |
tree | 85f3cbd6e437b17be59505cf3ac4794c1838609e /app/assets/javascripts/registry | |
parent | 5064bf8c5647d4c4430cbb4d097cf1592416de29 (diff) | |
download | gitlab-ce-e6baeabaa9651d90b03bb64ffce75a2c3cb89aab.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/registry')
10 files changed, 129 insertions, 51 deletions
diff --git a/app/assets/javascripts/registry/explorer/constants.js b/app/assets/javascripts/registry/explorer/constants.js index f5d935a2d70..586231d19c7 100644 --- a/app/assets/javascripts/registry/explorer/constants.js +++ b/app/assets/javascripts/registry/explorer/constants.js @@ -34,7 +34,7 @@ export const LIST_KEY_CHECKBOX = 'checkbox'; export const LIST_LABEL_TAG = s__('ContainerRegistry|Tag'); export const LIST_LABEL_IMAGE_ID = s__('ContainerRegistry|Image ID'); -export const LIST_LABEL_SIZE = s__('ContainerRegistry|Size'); +export const LIST_LABEL_SIZE = s__('ContainerRegistry|Compressed Size'); export const LIST_LABEL_LAST_UPDATED = s__('ContainerRegistry|Last Updated'); export const EXPIRATION_POLICY_ALERT_TITLE = s__( diff --git a/app/assets/javascripts/registry/explorer/index.js b/app/assets/javascripts/registry/explorer/index.js index a36978303c6..9269aa074f8 100644 --- a/app/assets/javascripts/registry/explorer/index.js +++ b/app/assets/javascripts/registry/explorer/index.js @@ -1,4 +1,5 @@ import Vue from 'vue'; +import { GlToast } from '@gitlab/ui'; import Translate from '~/vue_shared/translate'; import RegistryExplorer from './pages/index.vue'; import RegistryBreadcrumb from './components/registry_breadcrumb.vue'; @@ -6,6 +7,7 @@ import { createStore } from './stores'; import createRouter from './router'; Vue.use(Translate); +Vue.use(GlToast); export default () => { const el = document.getElementById('js-container-registry'); diff --git a/app/assets/javascripts/registry/explorer/pages/details.vue b/app/assets/javascripts/registry/explorer/pages/details.vue index be9a27e2c42..68a066f97e1 100644 --- a/app/assets/javascripts/registry/explorer/pages/details.vue +++ b/app/assets/javascripts/registry/explorer/pages/details.vue @@ -31,6 +31,10 @@ import { LIST_LABEL_IMAGE_ID, LIST_LABEL_SIZE, LIST_LABEL_LAST_UPDATED, + DELETE_TAG_SUCCESS_MESSAGE, + DELETE_TAG_ERROR_MESSAGE, + DELETE_TAGS_SUCCESS_MESSAGE, + DELETE_TAGS_ERROR_MESSAGE, } from '../constants'; export default { @@ -176,17 +180,37 @@ export default { }, handleSingleDelete(itemToDelete) { this.itemsToBeDeleted = []; - this.requestDeleteTag({ tag: itemToDelete, params: this.$route.params.id }); + return this.requestDeleteTag({ tag: itemToDelete, params: this.$route.params.id }) + .then(() => + this.$toast.show(DELETE_TAG_SUCCESS_MESSAGE, { + type: 'success', + }), + ) + .catch(() => + this.$toast.show(DELETE_TAG_ERROR_MESSAGE, { + type: 'error', + }), + ); }, handleMultipleDelete() { const { itemsToBeDeleted } = this; this.itemsToBeDeleted = []; this.selectedItems = []; - this.requestDeleteTags({ + return this.requestDeleteTags({ ids: itemsToBeDeleted.map(x => this.tags[x].name), params: this.$route.params.id, - }); + }) + .then(() => + this.$toast.show(DELETE_TAGS_SUCCESS_MESSAGE, { + type: 'success', + }), + ) + .catch(() => + this.$toast.show(DELETE_TAGS_ERROR_MESSAGE, { + type: 'error', + }), + ); }, onDeletionConfirmed() { this.track('confirm_delete'); diff --git a/app/assets/javascripts/registry/explorer/pages/index.vue b/app/assets/javascripts/registry/explorer/pages/index.vue index 19ae3bee640..95d83c82987 100644 --- a/app/assets/javascripts/registry/explorer/pages/index.vue +++ b/app/assets/javascripts/registry/explorer/pages/index.vue @@ -1,11 +1,48 @@ <script> -export default {}; +import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui'; +import { mapState, mapActions, mapGetters } from 'vuex'; +import { s__ } from '~/locale'; + +export default { + components: { + GlAlert, + GlSprintf, + GlLink, + }, + i18n: { + garbageCollectionTipText: s__( + 'ContainerRegistry|This Registry contains deleted image tag data. Remember to run %{docLinkStart}garbage collection%{docLinkEnd} to remove the stale data from storage.', + ), + }, + computed: { + ...mapState(['config']), + ...mapGetters(['showGarbageCollection']), + }, + methods: { + ...mapActions(['setShowGarbageCollectionTip']), + }, +}; </script> <template> <div> + <gl-alert + v-if="showGarbageCollection" + variant="tip" + class="my-2" + @dismiss="setShowGarbageCollectionTip(false)" + > + <gl-sprintf :message="$options.i18n.garbageCollectionTipText"> + <template #docLink="{content}"> + <gl-link :href="config.garbageCollectionHelpPagePath" target="_blank"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + </gl-alert> + <transition name="slide"> - <router-view /> + <router-view ref="router-view" /> </transition> </div> </template> diff --git a/app/assets/javascripts/registry/explorer/pages/list.vue b/app/assets/javascripts/registry/explorer/pages/list.vue index 7e321e927d3..1ed24fbb59f 100644 --- a/app/assets/javascripts/registry/explorer/pages/list.vue +++ b/app/assets/javascripts/registry/explorer/pages/list.vue @@ -12,11 +12,13 @@ import { GlSkeletonLoader, } from '@gitlab/ui'; import Tracking from '~/tracking'; +import { s__ } from '~/locale'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ProjectEmptyState from '../components/project_empty_state.vue'; import GroupEmptyState from '../components/group_empty_state.vue'; import ProjectPolicyAlert from '../components/project_policy_alert.vue'; import QuickstartDropdown from '../components/quickstart_dropdown.vue'; +import { DELETE_IMAGE_SUCCESS_MESSAGE, DELETE_IMAGE_ERROR_MESSAGE } from '../constants'; export default { name: 'RegistryListApp', @@ -44,6 +46,23 @@ export default { width: 1000, height: 40, }, + i18n: { + containerRegistryTitle: s__('ContainerRegistry|Container Registry'), + connectionErrorTitle: s__('ContainerRegistry|Docker connection error'), + connectionErrorMessage: s__( + `ContainerRegistry|We are having trouble connecting to Docker, which could be due to an issue with your project name or path. %{docLinkStart}More Information%{docLinkEnd}`, + ), + introText: s__( + `ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images. %{docLinkStart}More Information%{docLinkEnd}`, + ), + deleteButtonDisabled: s__( + 'ContainerRegistry|Missing or insufficient permission, delete button disabled', + ), + removeRepositoryLabel: s__('ContainerRegistry|Remove repository'), + removeRepositoryModalText: s__( + 'ContainerRegistry|You are about to remove repository %{title}. Once you confirm, this repository will be permanently deleted.', + ), + }, data() { return { itemToDelete: {}, @@ -76,10 +95,22 @@ export default { this.itemToDelete = item; this.$refs.deleteModal.show(); }, - handleDeleteRepository() { + handleDeleteImage() { this.track('confirm_delete'); - this.requestDeleteImage(this.itemToDelete.destroy_path); - this.itemToDelete = {}; + return this.requestDeleteImage(this.itemToDelete.destroy_path) + .then(() => + this.$toast.show(DELETE_IMAGE_SUCCESS_MESSAGE, { + type: 'success', + }), + ) + .catch(() => + this.$toast.show(DELETE_IMAGE_ERROR_MESSAGE, { + type: 'error', + }), + ) + .finally(() => { + this.itemToDelete = {}; + }); }, encodeListItem(item) { const params = JSON.stringify({ name: item.path, tags_path: item.tags_path, id: item.id }); @@ -95,18 +126,12 @@ export default { <gl-empty-state v-if="config.characterError" - :title="s__('ContainerRegistry|Docker connection error')" + :title="$options.i18n.connectionErrorTitle" :svg-path="config.containersErrorImage" > <template #description> <p> - <gl-sprintf - :message=" - s__(`ContainerRegistry|We are having trouble connecting to Docker, which could be due to an - issue with your project name or path. - %{docLinkStart}More Information%{docLinkEnd}`) - " - > + <gl-sprintf :message="$options.i18n.connectionErrorMessage"> <template #docLink="{content}"> <gl-link :href="`${config.helpPagePath}#docker-connection-error`" target="_blank"> {{ content }} @@ -120,17 +145,11 @@ export default { <template v-else> <div> <div class="d-flex justify-content-between align-items-center"> - <h4>{{ s__('ContainerRegistry|Container Registry') }}</h4> + <h4>{{ $options.i18n.containerRegistryTitle }}</h4> <quickstart-dropdown v-if="showQuickStartDropdown" class="d-none d-sm-block" /> </div> <p> - <gl-sprintf - :message=" - s__(`ContainerRegistry|With the Docker Container Registry integrated into GitLab, every - project can have its own space to store its Docker images. - %{docLinkStart}More Information%{docLinkEnd}`) - " - > + <gl-sprintf :message="$options.i18n.introText"> <template #docLink="{content}"> <gl-link :href="config.helpPagePath" target="_blank"> {{ content }} @@ -180,16 +199,14 @@ export default { <div v-gl-tooltip="{ disabled: listItem.destroy_path }" class="d-none d-sm-block" - :title=" - s__('ContainerRegistry|Missing or insufficient permission, delete button disabled') - " + :title="$options.i18n.deleteButtonDisabled" > <gl-button ref="deleteImageButton" v-gl-tooltip :disabled="!listItem.destroy_path" - :title="s__('ContainerRegistry|Remove repository')" - :aria-label="s__('ContainerRegistry|Remove repository')" + :title="$options.i18n.removeRepositoryLabel" + :aria-label="$options.i18n.removeRepositoryLabel" class="btn-inverted" variant="danger" @click="deleteImage(listItem)" @@ -217,16 +234,12 @@ export default { ref="deleteModal" modal-id="delete-image-modal" ok-variant="danger" - @ok="handleDeleteRepository" + @ok="handleDeleteImage" @cancel="track('cancel_delete')" > - <template #modal-title>{{ s__('ContainerRegistry|Remove repository') }}</template> + <template #modal-title>{{ $options.i18n.removeRepositoryLabel }}</template> <p> - <gl-sprintf - :message=" s__( - 'ContainerRegistry|You are about to remove repository %{title}. Once you confirm, this repository will be permanently deleted.', - )," - > + <gl-sprintf :message="$options.i18n.removeRepositoryModalText"> <template #title> <b>{{ itemToDelete.path }}</b> </template> diff --git a/app/assets/javascripts/registry/explorer/stores/actions.js b/app/assets/javascripts/registry/explorer/stores/actions.js index 7d8201949f4..2abd72cb9a8 100644 --- a/app/assets/javascripts/registry/explorer/stores/actions.js +++ b/app/assets/javascripts/registry/explorer/stores/actions.js @@ -6,16 +6,12 @@ import { DEFAULT_PAGE, DEFAULT_PAGE_SIZE, FETCH_TAGS_LIST_ERROR_MESSAGE, - DELETE_TAG_SUCCESS_MESSAGE, - DELETE_TAG_ERROR_MESSAGE, - DELETE_TAGS_SUCCESS_MESSAGE, - DELETE_TAGS_ERROR_MESSAGE, - DELETE_IMAGE_ERROR_MESSAGE, - DELETE_IMAGE_SUCCESS_MESSAGE, } from '../constants'; import { decodeAndParse } from '../utils'; export const setInitialState = ({ commit }, data) => commit(types.SET_INITIAL_STATE, data); +export const setShowGarbageCollectionTip = ({ commit }, data) => + commit(types.SET_SHOW_GARBAGE_COLLECTION_TIP, data); export const receiveImagesListSuccess = ({ commit }, { data, headers }) => { commit(types.SET_IMAGES_LIST_SUCCESS, data); @@ -67,11 +63,10 @@ export const requestDeleteTag = ({ commit, dispatch, state }, { tag, params }) = return axios .delete(tag.destroy_path) .then(() => { - createFlash(DELETE_TAG_SUCCESS_MESSAGE, 'success'); + dispatch('setShowGarbageCollectionTip', true); return dispatch('requestTagsList', { pagination: state.tagsPagination, params }); }) .catch(() => { - createFlash(DELETE_TAG_ERROR_MESSAGE); commit(types.SET_MAIN_LOADING, false); }); }; @@ -85,11 +80,10 @@ export const requestDeleteTags = ({ commit, dispatch, state }, { ids, params }) return axios .delete(url, { params: { ids } }) .then(() => { - createFlash(DELETE_TAGS_SUCCESS_MESSAGE, 'success'); + dispatch('setShowGarbageCollectionTip', true); return dispatch('requestTagsList', { pagination: state.tagsPagination, params }); }) .catch(() => { - createFlash(DELETE_TAGS_ERROR_MESSAGE); commit(types.SET_MAIN_LOADING, false); }); }; @@ -100,11 +94,8 @@ export const requestDeleteImage = ({ commit, dispatch, state }, destroyPath) => return axios .delete(destroyPath) .then(() => { + dispatch('setShowGarbageCollectionTip', true); dispatch('requestImagesList', { pagination: state.pagination }); - createFlash(DELETE_IMAGE_SUCCESS_MESSAGE, 'success'); - }) - .catch(() => { - createFlash(DELETE_IMAGE_ERROR_MESSAGE); }) .finally(() => { commit(types.SET_MAIN_LOADING, false); diff --git a/app/assets/javascripts/registry/explorer/stores/getters.js b/app/assets/javascripts/registry/explorer/stores/getters.js index 1136257a024..a371d0e6356 100644 --- a/app/assets/javascripts/registry/explorer/stores/getters.js +++ b/app/assets/javascripts/registry/explorer/stores/getters.js @@ -18,3 +18,7 @@ export const dockerLoginCommand = state => { /* eslint-disable @gitlab/require-i18n-strings */ return `docker login ${state.config.registryHostUrlWithPort}`; }; + +export const showGarbageCollection = state => { + return state.showGarbageCollectionTip && state.config.isAdmin; +}; diff --git a/app/assets/javascripts/registry/explorer/stores/mutation_types.js b/app/assets/javascripts/registry/explorer/stores/mutation_types.js index 92b747dffc5..86eaa0dd2f1 100644 --- a/app/assets/javascripts/registry/explorer/stores/mutation_types.js +++ b/app/assets/javascripts/registry/explorer/stores/mutation_types.js @@ -5,3 +5,4 @@ export const SET_PAGINATION = 'SET_PAGINATION'; export const SET_MAIN_LOADING = 'SET_MAIN_LOADING'; export const SET_TAGS_PAGINATION = 'SET_TAGS_PAGINATION'; export const SET_TAGS_LIST_SUCCESS = 'SET_TAGS_LIST_SUCCESS'; +export const SET_SHOW_GARBAGE_COLLECTION_TIP = 'SET_SHOW_GARBAGE_COLLECTION_TIP'; diff --git a/app/assets/javascripts/registry/explorer/stores/mutations.js b/app/assets/javascripts/registry/explorer/stores/mutations.js index 6055efcbd46..fda788051c0 100644 --- a/app/assets/javascripts/registry/explorer/stores/mutations.js +++ b/app/assets/javascripts/registry/explorer/stores/mutations.js @@ -7,6 +7,7 @@ export default { ...config, expirationPolicy: config.expirationPolicy ? JSON.parse(config.expirationPolicy) : undefined, isGroupPage: config.isGroupPage !== undefined, + isAdmin: config.isAdmin !== undefined, }; }, @@ -22,6 +23,10 @@ export default { state.isLoading = isLoading; }, + [types.SET_SHOW_GARBAGE_COLLECTION_TIP](state, showGarbageCollectionTip) { + state.showGarbageCollectionTip = showGarbageCollectionTip; + }, + [types.SET_PAGINATION](state, headers) { const normalizedHeaders = normalizeHeaders(headers); state.pagination = parseIntPagination(normalizedHeaders); diff --git a/app/assets/javascripts/registry/explorer/stores/state.js b/app/assets/javascripts/registry/explorer/stores/state.js index 91a378f139b..694006aac81 100644 --- a/app/assets/javascripts/registry/explorer/stores/state.js +++ b/app/assets/javascripts/registry/explorer/stores/state.js @@ -1,5 +1,6 @@ export default () => ({ isLoading: false, + showGarbageCollectionTip: false, config: {}, images: [], tags: [], |