summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/registry
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-10-17 12:07:33 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-10-17 12:07:33 +0000
commit6b75320f525f841454f1ab162d141d3610f2e77b (patch)
tree4971c27759e4fbc18b85e71800c3b9c12346317e /app/assets/javascripts/registry
parent4226aca420920c1844e8eade4798a2dff188a6fc (diff)
downloadgitlab-ce-6b75320f525f841454f1ab162d141d3610f2e77b.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/registry')
-rw-r--r--app/assets/javascripts/registry/components/app.vue117
-rw-r--r--app/assets/javascripts/registry/components/collapsible_container.vue10
-rw-r--r--app/assets/javascripts/registry/components/group_empty_state.vue46
-rw-r--r--app/assets/javascripts/registry/components/project_empty_state.vue133
-rw-r--r--app/assets/javascripts/registry/components/table_registry.vue19
-rw-r--r--app/assets/javascripts/registry/index.js31
-rw-r--r--app/assets/javascripts/registry/stores/actions.js2
-rw-r--r--app/assets/javascripts/registry/stores/getters.js1
-rw-r--r--app/assets/javascripts/registry/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/registry/stores/mutations.js5
-rw-r--r--app/assets/javascripts/registry/stores/state.js1
11 files changed, 253 insertions, 113 deletions
diff --git a/app/assets/javascripts/registry/components/app.vue b/app/assets/javascripts/registry/components/app.vue
index a20bae9e37e..11b2c3b7016 100644
--- a/app/assets/javascripts/registry/components/app.vue
+++ b/app/assets/javascripts/registry/components/app.vue
@@ -2,17 +2,19 @@
import { mapGetters, mapActions } from 'vuex';
import { GlLoadingIcon, GlEmptyState } from '@gitlab/ui';
import store from '../stores';
-import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
import CollapsibleContainer from './collapsible_container.vue';
+import ProjectEmptyState from './project_empty_state.vue';
+import GroupEmptyState from './group_empty_state.vue';
import { s__, sprintf } from '../../locale';
export default {
name: 'RegistryListApp',
components: {
- clipboardButton,
CollapsibleContainer,
GlEmptyState,
GlLoadingIcon,
+ ProjectEmptyState,
+ GroupEmptyState,
},
props: {
characterError: {
@@ -38,19 +40,27 @@ export default {
},
personalAccessTokensHelpLink: {
type: String,
- required: true,
+ required: false,
+ default: null,
},
registryHostUrlWithPort: {
type: String,
- required: true,
+ required: false,
+ default: null,
},
repositoryUrl: {
type: String,
required: true,
},
+ isGroupPage: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
twoFactorAuthHelpLink: {
type: String,
- required: true,
+ required: false,
+ default: null,
},
},
store,
@@ -91,37 +101,10 @@ export default {
false,
);
},
- notLoggedInToRegistryText() {
- return sprintf(
- s__(`ContainerRegistry|If you are not already logged in, you need to authenticate to
- the Container Registry by using your GitLab username and password. If you have
- %{twofaDocLinkStart}Two-Factor Authentication%{twofaDocLinkEnd} enabled, use a
- %{personalAccessTokensDocLinkStart}Personal Access Token
- %{personalAccessTokensDocLinkEnd}instead of a password.`),
- {
- twofaDocLinkStart: `<a href="${this.twoFactorAuthHelpLink}" target="_blank">`,
- twofaDocLinkEnd: '</a>',
- personalAccessTokensDocLinkStart: `<a href="${this.personalAccessTokensHelpLink}" target="_blank">`,
- personalAccessTokensDocLinkEnd: '</a>',
- },
- false,
- );
- },
- dockerLoginCommand() {
- // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
- return `docker login ${this.registryHostUrlWithPort}`;
- },
- dockerBuildCommand() {
- // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
- return `docker build -t ${this.repositoryUrl} .`;
- },
- dockerPushCommand() {
- // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
- return `docker push ${this.repositoryUrl}`;
- },
},
created() {
this.setMainEndpoint(this.endpoint);
+ this.setIsDeleteDisabled(this.isGroupPage);
},
mounted() {
if (!this.characterError) {
@@ -129,7 +112,7 @@ export default {
}
},
methods: {
- ...mapActions(['setMainEndpoint', 'fetchRepos']),
+ ...mapActions(['setMainEndpoint', 'fetchRepos', 'setIsDeleteDisabled']),
},
};
</script>
@@ -152,57 +135,19 @@ export default {
<p v-html="introText"></p>
<collapsible-container v-for="item in repos" :key="item.id" :repo="item" />
</div>
-
- <gl-empty-state
- v-else
- :title="s__('ContainerRegistry|There are no container images stored for this project')"
- :svg-path="noContainersImage"
- class="container-message"
- >
- <template #description>
- <p class="js-no-container-images-text" v-html="noContainerImagesText"></p>
- <h5>{{ s__('ContainerRegistry|Quick Start') }}</h5>
- <p class="js-not-logged-in-to-registry-text" v-html="notLoggedInToRegistryText"></p>
- <div class="input-group append-bottom-10">
- <input :value="dockerLoginCommand" type="text" class="form-control monospace" readonly />
- <span class="input-group-append">
- <clipboard-button
- :text="dockerLoginCommand"
- :title="s__('ContainerRegistry|Copy login command')"
- class="input-group-text"
- />
- </span>
- </div>
- <p>
- {{
- s__(
- 'ContainerRegistry|You can add an image to this registry with the following commands:',
- )
- }}
- </p>
-
- <div class="input-group append-bottom-10">
- <input :value="dockerBuildCommand" type="text" class="form-control monospace" readonly />
- <span class="input-group-append">
- <clipboard-button
- :text="dockerBuildCommand"
- :title="s__('ContainerRegistry|Copy build command')"
- class="input-group-text"
- />
- </span>
- </div>
-
- <div class="input-group">
- <input :value="dockerPushCommand" type="text" class="form-control monospace" readonly />
- <span class="input-group-append">
- <clipboard-button
- :text="dockerPushCommand"
- :title="s__('ContainerRegistry|Copy push command')"
- class="input-group-text"
- />
- </span>
- </div>
- </template>
- </gl-empty-state>
+ <project-empty-state
+ v-else-if="!isGroupPage"
+ :no-containers-image="noContainersImage"
+ :help-page-path="helpPagePath"
+ :repository-url="repositoryUrl"
+ :two-factor-auth-help-link="twoFactorAuthHelpLink"
+ :personal-access-tokens-help-link="personalAccessTokensHelpLink"
+ :registry-host-url-with-port="registryHostUrlWithPort"
+ />
+ <group-empty-state
+ v-else-if="isGroupPage"
+ :no-containers-image="noContainersImage"
+ :help-page-path="helpPagePath"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/registry/components/collapsible_container.vue b/app/assets/javascripts/registry/components/collapsible_container.vue
index 3e31d24088e..ed48331f459 100644
--- a/app/assets/javascripts/registry/components/collapsible_container.vue
+++ b/app/assets/javascripts/registry/components/collapsible_container.vue
@@ -1,5 +1,5 @@
<script>
-import { mapActions } from 'vuex';
+import { mapActions, mapGetters } from 'vuex';
import { GlLoadingIcon, GlButton, GlTooltipDirective, GlModal, GlModalDirective } from '@gitlab/ui';
import createFlash from '../../flash';
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
@@ -35,9 +35,13 @@ export default {
};
},
computed: {
+ ...mapGetters(['isDeleteDisabled']),
iconName() {
return this.isOpen ? 'angle-up' : 'angle-right';
},
+ canDeleteRepo() {
+ return this.repo.canDelete && !this.isDeleteDisabled;
+ },
},
methods: {
...mapActions(['fetchRepos', 'fetchList', 'deleteItem']),
@@ -80,7 +84,7 @@ export default {
<div class="controls d-none d-sm-block float-right">
<gl-button
- v-if="repo.canDelete"
+ v-if="canDeleteRepo"
v-gl-tooltip
v-gl-modal="modalId"
:title="s__('ContainerRegistry|Remove repository')"
@@ -98,7 +102,7 @@ export default {
<gl-loading-icon v-if="repo.isLoading" size="md" class="append-bottom-20" />
<div v-else-if="!repo.isLoading && isOpen" class="container-image-tags">
- <table-registry v-if="repo.list.length" :repo="repo" />
+ <table-registry v-if="repo.list.length" :repo="repo" :can-delete-repo="canDeleteRepo" />
<div v-else class="nothing-here-block">
{{ s__('ContainerRegistry|No tags in Container Registry for this container image.') }}
diff --git a/app/assets/javascripts/registry/components/group_empty_state.vue b/app/assets/javascripts/registry/components/group_empty_state.vue
new file mode 100644
index 00000000000..7885fd2146d
--- /dev/null
+++ b/app/assets/javascripts/registry/components/group_empty_state.vue
@@ -0,0 +1,46 @@
+<script>
+import { GlEmptyState } from '@gitlab/ui';
+import { s__, sprintf } from '~/locale';
+
+export default {
+ name: 'GroupEmptyState',
+ components: {
+ GlEmptyState,
+ },
+ props: {
+ noContainersImage: {
+ type: String,
+ required: true,
+ },
+ helpPagePath: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ noContainerImagesText() {
+ return sprintf(
+ s__(
+ `ContainerRegistry|With the Container Registry, every project can have its own space to store its Docker images. Push at least one Docker image in one of this group's projects in order to show up here. %{docLinkStart}More Information%{docLinkEnd}`,
+ ),
+ {
+ docLinkStart: `<a href="${this.helpPagePath}" target="_blank">`,
+ docLinkEnd: '</a>',
+ },
+ false,
+ );
+ },
+ },
+};
+</script>
+<template>
+ <gl-empty-state
+ :title="s__('ContainerRegistry|There are no container images available in this group')"
+ :svg-path="noContainersImage"
+ class="container-message"
+ >
+ <template #description>
+ <p class="js-no-container-images-text" v-html="noContainerImagesText"></p>
+ </template>
+ </gl-empty-state>
+</template>
diff --git a/app/assets/javascripts/registry/components/project_empty_state.vue b/app/assets/javascripts/registry/components/project_empty_state.vue
new file mode 100644
index 00000000000..80ef31004c8
--- /dev/null
+++ b/app/assets/javascripts/registry/components/project_empty_state.vue
@@ -0,0 +1,133 @@
+<script>
+import { GlEmptyState } from '@gitlab/ui';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import { s__, sprintf } from '~/locale';
+
+export default {
+ name: 'ProjectEmptyState',
+ components: {
+ ClipboardButton,
+ GlEmptyState,
+ },
+ props: {
+ noContainersImage: {
+ type: String,
+ required: true,
+ },
+ repositoryUrl: {
+ type: String,
+ required: true,
+ },
+ helpPagePath: {
+ type: String,
+ required: true,
+ },
+ twoFactorAuthHelpLink: {
+ type: String,
+ required: true,
+ },
+ personalAccessTokensHelpLink: {
+ type: String,
+ required: true,
+ },
+ registryHostUrlWithPort: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ dockerBuildCommand() {
+ // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
+ return `docker build -t ${this.repositoryUrl} .`;
+ },
+ dockerPushCommand() {
+ // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
+ return `docker push ${this.repositoryUrl}`;
+ },
+ dockerLoginCommand() {
+ // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
+ return `docker login ${this.registryHostUrlWithPort}`;
+ },
+ noContainerImagesText() {
+ return sprintf(
+ s__(`ContainerRegistry|With the Container Registry, every project can have its own space to
+ store its Docker images. %{docLinkStart}More Information%{docLinkEnd}`),
+ {
+ docLinkStart: `<a href="${this.helpPagePath}" target="_blank">`,
+ docLinkEnd: '</a>',
+ },
+ false,
+ );
+ },
+ notLoggedInToRegistryText() {
+ return sprintf(
+ s__(`ContainerRegistry|If you are not already logged in, you need to authenticate to
+ the Container Registry by using your GitLab username and password. If you have
+ %{twofaDocLinkStart}Two-Factor Authentication%{twofaDocLinkEnd} enabled, use a
+ %{personalAccessTokensDocLinkStart}Personal Access Token%{personalAccessTokensDocLinkEnd}
+ instead of a password.`),
+ {
+ twofaDocLinkStart: `<a href="${this.twoFactorAuthHelpLink}" target="_blank">`,
+ twofaDocLinkEnd: '</a>',
+ personalAccessTokensDocLinkStart: `<a href="${this.personalAccessTokensHelpLink}" target="_blank">`,
+ personalAccessTokensDocLinkEnd: '</a>',
+ },
+ false,
+ );
+ },
+ },
+};
+</script>
+<template>
+ <gl-empty-state
+ :title="s__('ContainerRegistry|There are no container images stored for this project')"
+ :svg-path="noContainersImage"
+ class="container-message"
+ >
+ <template #description>
+ <p class="js-no-container-images-text" v-html="noContainerImagesText"></p>
+ <h5>{{ s__('ContainerRegistry|Quick Start') }}</h5>
+ <p class="js-not-logged-in-to-registry-text" v-html="notLoggedInToRegistryText"></p>
+ <div class="input-group append-bottom-10">
+ <input :value="dockerLoginCommand" type="text" class="form-control monospace" readonly />
+ <span class="input-group-append">
+ <clipboard-button
+ :text="dockerLoginCommand"
+ :title="s__('ContainerRegistry|Copy login command')"
+ class="input-group-text"
+ />
+ </span>
+ </div>
+ <p></p>
+ <p>
+ {{
+ s__(
+ 'ContainerRegistry|You can add an image to this registry with the following commands:',
+ )
+ }}
+ </p>
+
+ <div class="input-group append-bottom-10">
+ <input :value="dockerBuildCommand" type="text" class="form-control monospace" readonly />
+ <span class="input-group-append">
+ <clipboard-button
+ :text="dockerBuildCommand"
+ :title="s__('ContainerRegistry|Copy build command')"
+ class="input-group-text"
+ />
+ </span>
+ </div>
+
+ <div class="input-group">
+ <input :value="dockerPushCommand" type="text" class="form-control monospace" readonly />
+ <span class="input-group-append">
+ <clipboard-button
+ :text="dockerPushCommand"
+ :title="s__('ContainerRegistry|Copy push command')"
+ class="input-group-text"
+ />
+ </span>
+ </div>
+ </template>
+ </gl-empty-state>
+</template>
diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue
index 00acc0eb04a..ac7272c4d29 100644
--- a/app/assets/javascripts/registry/components/table_registry.vue
+++ b/app/assets/javascripts/registry/components/table_registry.vue
@@ -1,5 +1,5 @@
<script>
-import { mapActions } from 'vuex';
+import { mapActions, mapGetters } from 'vuex';
import {
GlButton,
GlFormCheckbox,
@@ -35,6 +35,11 @@ export default {
type: Object,
required: true,
},
+ canDeleteRepo: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
},
data() {
return {
@@ -45,6 +50,7 @@ export default {
};
},
computed: {
+ ...mapGetters(['isDeleteDisabled']),
bulkDeletePath() {
return this.repo.tagsPath ? this.repo.tagsPath.replace('?format=json', '/bulk_destroy') : '';
},
@@ -165,6 +171,9 @@ export default {
}
}
},
+ canDeleteRow(item) {
+ return item && item.canDelete && !this.isDeleteDisabled;
+ },
},
};
</script>
@@ -175,7 +184,7 @@ export default {
<tr>
<th>
<gl-form-checkbox
- v-if="repo.canDelete"
+ v-if="canDeleteRepo"
class="js-select-all-checkbox"
:checked="selectAllChecked"
@change="onSelectAllChange"
@@ -187,7 +196,7 @@ export default {
<th>{{ s__('ContainerRegistry|Last Updated') }}</th>
<th>
<gl-button
- v-if="repo.canDelete"
+ v-if="canDeleteRepo"
v-gl-tooltip
v-gl-modal="modalId"
:disabled="!itemsToBeDeleted || itemsToBeDeleted.length === 0"
@@ -208,7 +217,7 @@ export default {
<tr v-for="(item, index) in repo.list" :key="item.tag" class="registry-image-row">
<td class="check">
<gl-form-checkbox
- v-if="item.canDelete"
+ v-if="canDeleteRow(item)"
class="js-select-checkbox"
:checked="itemsToBeDeleted && itemsToBeDeleted.includes(index)"
@change="updateItemsToBeDeleted(index)"
@@ -244,7 +253,7 @@ export default {
<td class="content action-buttons">
<gl-button
- v-if="item.canDelete"
+ v-if="canDeleteRow(item)"
v-gl-modal="modalId"
:title="s__('ContainerRegistry|Remove tag')"
:aria-label="s__('ContainerRegistry|Remove tag')"
diff --git a/app/assets/javascripts/registry/index.js b/app/assets/javascripts/registry/index.js
index 38c3d67042c..18fd360f586 100644
--- a/app/assets/javascripts/registry/index.js
+++ b/app/assets/javascripts/registry/index.js
@@ -13,29 +13,24 @@ export default () =>
data() {
const { dataset } = document.querySelector(this.$options.el);
return {
- characterError: Boolean(dataset.characterError),
- containersErrorImage: dataset.containersErrorImage,
- endpoint: dataset.endpoint,
- helpPagePath: dataset.helpPagePath,
- noContainersImage: dataset.noContainersImage,
- personalAccessTokensHelpLink: dataset.personalAccessTokensHelpLink,
- registryHostUrlWithPort: dataset.registryHostUrlWithPort,
- repositoryUrl: dataset.repositoryUrl,
- twoFactorAuthHelpLink: dataset.twoFactorAuthHelpLink,
+ registryData: {
+ endpoint: dataset.endpoint,
+ characterError: Boolean(dataset.characterError),
+ helpPagePath: dataset.helpPagePath,
+ noContainersImage: dataset.noContainersImage,
+ containersErrorImage: dataset.containersErrorImage,
+ repositoryUrl: dataset.repositoryUrl,
+ isGroupPage: dataset.isGroupPage,
+ personalAccessTokensHelpLink: dataset.personalAccessTokensHelpLink,
+ registryHostUrlWithPort: dataset.registryHostUrlWithPort,
+ twoFactorAuthHelpLink: dataset.twoFactorAuthHelpLink,
+ },
};
},
render(createElement) {
return createElement('registry-app', {
props: {
- characterError: this.characterError,
- containersErrorImage: this.containersErrorImage,
- endpoint: this.endpoint,
- helpPagePath: this.helpPagePath,
- noContainersImage: this.noContainersImage,
- personalAccessTokensHelpLink: this.personalAccessTokensHelpLink,
- registryHostUrlWithPort: this.registryHostUrlWithPort,
- repositoryUrl: this.repositoryUrl,
- twoFactorAuthHelpLink: this.twoFactorAuthHelpLink,
+ ...this.registryData,
},
});
},
diff --git a/app/assets/javascripts/registry/stores/actions.js b/app/assets/javascripts/registry/stores/actions.js
index a2e0130e79e..2121f518a7a 100644
--- a/app/assets/javascripts/registry/stores/actions.js
+++ b/app/assets/javascripts/registry/stores/actions.js
@@ -20,7 +20,6 @@ export const fetchRepos = ({ commit, state }) => {
export const fetchList = ({ commit }, { repo, page }) => {
commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo);
-
return axios
.get(repo.tagsPath, { params: { page } })
.then(response => {
@@ -40,6 +39,7 @@ export const multiDeleteItems = (_, { path, items }) =>
axios.delete(path, { params: { ids: items } });
export const setMainEndpoint = ({ commit }, data) => commit(types.SET_MAIN_ENDPOINT, data);
+export const setIsDeleteDisabled = ({ commit }, data) => commit(types.SET_IS_DELETE_DISABLED, data);
export const toggleLoading = ({ commit }) => commit(types.TOGGLE_MAIN_LOADING);
// prevent babel-plugin-rewire from generating an invalid default during karma tests
diff --git a/app/assets/javascripts/registry/stores/getters.js b/app/assets/javascripts/registry/stores/getters.js
index f4923512578..ac90bde1b2a 100644
--- a/app/assets/javascripts/registry/stores/getters.js
+++ b/app/assets/javascripts/registry/stores/getters.js
@@ -1,5 +1,6 @@
export const isLoading = state => state.isLoading;
export const repos = state => state.repos;
+export const isDeleteDisabled = state => state.isDeleteDisabled;
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/registry/stores/mutation_types.js b/app/assets/javascripts/registry/stores/mutation_types.js
index 2c69bf11807..6740bfede1a 100644
--- a/app/assets/javascripts/registry/stores/mutation_types.js
+++ b/app/assets/javascripts/registry/stores/mutation_types.js
@@ -1,4 +1,5 @@
export const SET_MAIN_ENDPOINT = 'SET_MAIN_ENDPOINT';
+export const SET_IS_DELETE_DISABLED = 'SET_IS_DELETE_DISABLED';
export const SET_REPOS_LIST = 'SET_REPOS_LIST';
export const TOGGLE_MAIN_LOADING = 'TOGGLE_MAIN_LOADING';
diff --git a/app/assets/javascripts/registry/stores/mutations.js b/app/assets/javascripts/registry/stores/mutations.js
index 8ace6657ad1..ea5925247d1 100644
--- a/app/assets/javascripts/registry/stores/mutations.js
+++ b/app/assets/javascripts/registry/stores/mutations.js
@@ -6,6 +6,10 @@ export default {
Object.assign(state, { endpoint });
},
+ [types.SET_IS_DELETE_DISABLED](state, isDeleteDisabled) {
+ Object.assign(state, { isDeleteDisabled });
+ },
+
[types.SET_REPOS_LIST](state, list) {
Object.assign(state, {
repos: list.map(el => ({
@@ -17,6 +21,7 @@ export default {
location: el.location,
name: el.path,
tagsPath: el.tags_path,
+ projectId: el.project_id,
})),
});
},
diff --git a/app/assets/javascripts/registry/stores/state.js b/app/assets/javascripts/registry/stores/state.js
index feeac10cbe1..724c64b4994 100644
--- a/app/assets/javascripts/registry/stores/state.js
+++ b/app/assets/javascripts/registry/stores/state.js
@@ -1,6 +1,7 @@
export default () => ({
isLoading: false,
endpoint: '', // initial endpoint to fetch the repos list
+ isDeleteDisabled: false, // controls the delete buttons in the registry
/**
* Each object in `repos` has the following strucure:
* {