summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-10 15:08:54 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-10 15:08:54 +0000
commit11e5d1b9ca3efa7be34ddebb708a6aedb4e91639 (patch)
tree999fdffb9d3db2e5200994e289e50fa3a3a1684a /app/assets
parent7351a484d79236b7e9d47c86f2fcc970b7ae10b0 (diff)
downloadgitlab-ce-11e5d1b9ca3efa7be34ddebb708a6aedb4e91639.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/pages/groups/registry/repositories/index.js8
-rw-r--r--app/assets/javascripts/pages/projects/registry/repositories/index.js6
-rw-r--r--app/assets/javascripts/registry/explorer/constants.js32
-rw-r--r--app/assets/javascripts/registry/explorer/index.js33
-rw-r--r--app/assets/javascripts/registry/explorer/pages/details.vue7
-rw-r--r--app/assets/javascripts/registry/explorer/pages/index.vue11
-rw-r--r--app/assets/javascripts/registry/explorer/pages/list.vue7
-rw-r--r--app/assets/javascripts/registry/explorer/router.js42
-rw-r--r--app/assets/javascripts/registry/explorer/stores/actions.js115
-rw-r--r--app/assets/javascripts/registry/explorer/stores/index.js16
-rw-r--r--app/assets/javascripts/registry/explorer/stores/mutation_types.js7
-rw-r--r--app/assets/javascripts/registry/explorer/stores/mutations.js32
-rw-r--r--app/assets/javascripts/registry/explorer/stores/state.js8
-rw-r--r--app/assets/javascripts/registry/list/index.js15
-rw-r--r--app/assets/javascripts/repository/utils/dom.js4
15 files changed, 335 insertions, 8 deletions
diff --git a/app/assets/javascripts/pages/groups/registry/repositories/index.js b/app/assets/javascripts/pages/groups/registry/repositories/index.js
index 635513afd95..52fb839e3fd 100644
--- a/app/assets/javascripts/pages/groups/registry/repositories/index.js
+++ b/app/assets/javascripts/pages/groups/registry/repositories/index.js
@@ -1,3 +1,7 @@
-import initRegistryImages from '~/registry/list';
+import initRegistryImages from '~/registry/list/index';
+import registryExplorer from '~/registry/explorer/index';
-document.addEventListener('DOMContentLoaded', initRegistryImages);
+document.addEventListener('DOMContentLoaded', () => {
+ initRegistryImages();
+ registryExplorer();
+});
diff --git a/app/assets/javascripts/pages/projects/registry/repositories/index.js b/app/assets/javascripts/pages/projects/registry/repositories/index.js
index 59310b3f76f..52fb839e3fd 100644
--- a/app/assets/javascripts/pages/projects/registry/repositories/index.js
+++ b/app/assets/javascripts/pages/projects/registry/repositories/index.js
@@ -1,3 +1,7 @@
import initRegistryImages from '~/registry/list/index';
+import registryExplorer from '~/registry/explorer/index';
-document.addEventListener('DOMContentLoaded', initRegistryImages);
+document.addEventListener('DOMContentLoaded', () => {
+ initRegistryImages();
+ registryExplorer();
+});
diff --git a/app/assets/javascripts/registry/explorer/constants.js b/app/assets/javascripts/registry/explorer/constants.js
new file mode 100644
index 00000000000..bb311157627
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/constants.js
@@ -0,0 +1,32 @@
+import { __ } from '~/locale';
+
+export const FETCH_IMAGES_LIST_ERROR_MESSAGE = __(
+ 'Something went wrong while fetching the packages list.',
+);
+export const FETCH_TAGS_LIST_ERROR_MESSAGE = __(
+ 'Something went wrong while fetching the tags list.',
+);
+
+export const DELETE_IMAGE_ERROR_MESSAGE = __('Something went wrong while deleting the image.');
+export const DELETE_IMAGE_SUCCESS_MESSAGE = __('Image deleted successfully');
+export const DELETE_TAG_ERROR_MESSAGE = __('Something went wrong while deleting the tag.');
+export const DELETE_TAG_SUCCESS_MESSAGE = __('Tag deleted successfully');
+export const DELETE_TAGS_ERROR_MESSAGE = __('Something went wrong while deleting the tags.');
+export const DELETE_TAGS_SUCCESS_MESSAGE = __('Tags deleted successfully');
+
+export const DEFAULT_PAGE = 1;
+export const DEFAULT_PAGE_SIZE = 10;
+
+export const GROUP_PAGE_TYPE = 'groups';
+
+export const LIST_KEY_TAG = 'name';
+export const LIST_KEY_IMAGE_ID = 'short_revision';
+export const LIST_KEY_SIZE = 'total_size';
+export const LIST_KEY_LAST_UPDATED = 'created_at';
+export const LIST_KEY_ACTIONS = 'actions';
+export const LIST_KEY_CHECKBOX = 'checkbox';
+
+export const LIST_LABEL_TAG = __('Tag');
+export const LIST_LABEL_IMAGE_ID = __('Image ID');
+export const LIST_LABEL_SIZE = __('Size');
+export const LIST_LABEL_LAST_UPDATED = __('Last Updated');
diff --git a/app/assets/javascripts/registry/explorer/index.js b/app/assets/javascripts/registry/explorer/index.js
new file mode 100644
index 00000000000..daa2e4fb109
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/index.js
@@ -0,0 +1,33 @@
+import Vue from 'vue';
+import Translate from '~/vue_shared/translate';
+import RegistryExplorer from './pages/index.vue';
+import { createStore } from './stores';
+import createRouter from './router';
+
+Vue.use(Translate);
+
+export default () => {
+ const el = document.getElementById('js-container-registry');
+
+ if (!el) {
+ return null;
+ }
+
+ const { endpoint } = el.dataset;
+
+ const store = createStore();
+ const router = createRouter(endpoint, store);
+ store.dispatch('setInitialState', el.dataset);
+
+ return new Vue({
+ el,
+ store,
+ router,
+ components: {
+ RegistryExplorer,
+ },
+ render(createElement) {
+ return createElement('registry-explorer');
+ },
+ });
+};
diff --git a/app/assets/javascripts/registry/explorer/pages/details.vue b/app/assets/javascripts/registry/explorer/pages/details.vue
new file mode 100644
index 00000000000..6d32ba41eae
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/pages/details.vue
@@ -0,0 +1,7 @@
+<script>
+export default {};
+</script>
+
+<template>
+ <div></div>
+</template>
diff --git a/app/assets/javascripts/registry/explorer/pages/index.vue b/app/assets/javascripts/registry/explorer/pages/index.vue
new file mode 100644
index 00000000000..deefbfc40e0
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/pages/index.vue
@@ -0,0 +1,11 @@
+<script>
+export default {};
+</script>
+
+<template>
+ <div class="position-relative">
+ <transition name="slide">
+ <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
new file mode 100644
index 00000000000..6d32ba41eae
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/pages/list.vue
@@ -0,0 +1,7 @@
+<script>
+export default {};
+</script>
+
+<template>
+ <div></div>
+</template>
diff --git a/app/assets/javascripts/registry/explorer/router.js b/app/assets/javascripts/registry/explorer/router.js
new file mode 100644
index 00000000000..8cf35b8f245
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/router.js
@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import VueRouter from 'vue-router';
+import { __ } from '~/locale';
+import List from './pages/list.vue';
+import Details from './pages/details.vue';
+
+Vue.use(VueRouter);
+
+export default function createRouter(base, store) {
+ const router = new VueRouter({
+ base,
+ mode: 'history',
+ routes: [
+ {
+ name: 'list',
+ path: '/',
+ component: List,
+ meta: {
+ name: __('Container Registry'),
+ },
+ beforeEnter: (to, from, next) => {
+ store.dispatch('requestImagesList');
+ next();
+ },
+ },
+ {
+ name: 'details',
+ path: '/:id',
+ component: Details,
+ meta: {
+ name: __('Tags'),
+ },
+ beforeEnter: (to, from, next) => {
+ store.dispatch('requestTagsList', { id: to.params.id });
+ next();
+ },
+ },
+ ],
+ });
+
+ return router;
+}
diff --git a/app/assets/javascripts/registry/explorer/stores/actions.js b/app/assets/javascripts/registry/explorer/stores/actions.js
new file mode 100644
index 00000000000..7c06a12a5fc
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/stores/actions.js
@@ -0,0 +1,115 @@
+import axios from '~/lib/utils/axios_utils';
+import createFlash from '~/flash';
+import * as types from './mutation_types';
+import {
+ FETCH_IMAGES_LIST_ERROR_MESSAGE,
+ 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';
+
+export const setInitialState = ({ commit }, data) => commit(types.SET_INITIAL_STATE, data);
+
+export const receiveImagesListSuccess = ({ commit }, { data, headers }) => {
+ commit(types.SET_IMAGES_LIST_SUCCESS, data);
+ commit(types.SET_PAGINATION, headers);
+};
+
+export const receiveTagsListSuccess = ({ commit }, { data, headers }) => {
+ commit(types.SET_TAGS_LIST_SUCCESS, data);
+ commit(types.SET_TAGS_PAGINATION, headers);
+};
+
+export const requestImagesList = ({ commit, dispatch, state }, pagination = {}) => {
+ commit(types.SET_MAIN_LOADING, true);
+ const { page = DEFAULT_PAGE, perPage = DEFAULT_PAGE_SIZE } = pagination;
+
+ return axios
+ .get(state.config.endpoint, { params: { page, per_page: perPage } })
+ .then(({ data, headers }) => {
+ dispatch('receiveImagesListSuccess', { data, headers });
+ })
+ .catch(() => {
+ createFlash(FETCH_IMAGES_LIST_ERROR_MESSAGE);
+ })
+ .finally(() => {
+ commit(types.SET_MAIN_LOADING, false);
+ });
+};
+
+export const requestTagsList = ({ commit, dispatch }, { pagination = {}, id }) => {
+ commit(types.SET_MAIN_LOADING, true);
+ const url = window.atob(id);
+
+ const { page = DEFAULT_PAGE, perPage = DEFAULT_PAGE_SIZE } = pagination;
+ return axios
+ .get(url, { params: { page, per_page: perPage } })
+ .then(({ data, headers }) => {
+ dispatch('receiveTagsListSuccess', { data, headers });
+ })
+ .catch(() => {
+ createFlash(FETCH_TAGS_LIST_ERROR_MESSAGE);
+ })
+ .finally(() => {
+ commit(types.SET_MAIN_LOADING, false);
+ });
+};
+
+export const requestDeleteTag = ({ commit, dispatch, state }, { tag, imageId }) => {
+ commit(types.SET_MAIN_LOADING, true);
+ return axios
+ .delete(tag.destroy_path)
+ .then(() => {
+ createFlash(DELETE_TAG_SUCCESS_MESSAGE, 'success');
+ dispatch('requestTagsList', { pagination: state.tagsPagination, id: imageId });
+ })
+ .catch(() => {
+ createFlash(DELETE_TAG_ERROR_MESSAGE);
+ })
+ .finally(() => {
+ commit(types.SET_MAIN_LOADING, false);
+ });
+};
+
+export const requestDeleteTags = ({ commit, dispatch, state }, { ids, imageId }) => {
+ commit(types.SET_MAIN_LOADING, true);
+ const url = `/${state.config.projectPath}/registry/repository/${imageId}/tags/bulk_destroy`;
+
+ return axios
+ .delete(url, { params: { ids } })
+ .then(() => {
+ createFlash(DELETE_TAGS_SUCCESS_MESSAGE, 'success');
+ dispatch('requestTagsList', { pagination: state.tagsPagination, id: imageId });
+ })
+ .catch(() => {
+ createFlash(DELETE_TAGS_ERROR_MESSAGE);
+ })
+ .finally(() => {
+ commit(types.SET_MAIN_LOADING, false);
+ });
+};
+
+export const requestDeleteImage = ({ commit, dispatch, state }, destroyPath) => {
+ commit(types.SET_MAIN_LOADING, true);
+
+ return axios
+ .delete(destroyPath)
+ .then(() => {
+ dispatch('requestImagesList', { pagination: state.pagination });
+ createFlash(DELETE_IMAGE_SUCCESS_MESSAGE, 'success');
+ })
+ .catch(() => {
+ createFlash(DELETE_IMAGE_ERROR_MESSAGE);
+ })
+ .finally(() => {
+ commit(types.SET_MAIN_LOADING, false);
+ });
+};
+
+export default () => {};
diff --git a/app/assets/javascripts/registry/explorer/stores/index.js b/app/assets/javascripts/registry/explorer/stores/index.js
new file mode 100644
index 00000000000..91a35aac149
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/stores/index.js
@@ -0,0 +1,16 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import * as actions from './actions';
+import mutations from './mutations';
+import state from './state';
+
+Vue.use(Vuex);
+
+export const createStore = () =>
+ new Vuex.Store({
+ state,
+ actions,
+ mutations,
+ });
+
+export default createStore();
diff --git a/app/assets/javascripts/registry/explorer/stores/mutation_types.js b/app/assets/javascripts/registry/explorer/stores/mutation_types.js
new file mode 100644
index 00000000000..92b747dffc5
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/stores/mutation_types.js
@@ -0,0 +1,7 @@
+export const SET_INITIAL_STATE = 'SET_INITIAL_STATE';
+
+export const SET_IMAGES_LIST_SUCCESS = 'SET_PACKAGE_LIST_SUCCESS';
+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';
diff --git a/app/assets/javascripts/registry/explorer/stores/mutations.js b/app/assets/javascripts/registry/explorer/stores/mutations.js
new file mode 100644
index 00000000000..186f36a759a
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/stores/mutations.js
@@ -0,0 +1,32 @@
+import * as types from './mutation_types';
+import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
+
+export default {
+ [types.SET_INITIAL_STATE](state, config) {
+ state.config = {
+ ...config,
+ };
+ },
+
+ [types.SET_IMAGES_LIST_SUCCESS](state, images) {
+ state.images = images;
+ },
+
+ [types.SET_TAGS_LIST_SUCCESS](state, tags) {
+ state.tags = tags;
+ },
+
+ [types.SET_MAIN_LOADING](state, isLoading) {
+ state.isLoading = isLoading;
+ },
+
+ [types.SET_PAGINATION](state, headers) {
+ const normalizedHeaders = normalizeHeaders(headers);
+ state.pagination = parseIntPagination(normalizedHeaders);
+ },
+
+ [types.SET_TAGS_PAGINATION](state, headers) {
+ const normalizedHeaders = normalizeHeaders(headers);
+ state.tagsPagination = parseIntPagination(normalizedHeaders);
+ },
+};
diff --git a/app/assets/javascripts/registry/explorer/stores/state.js b/app/assets/javascripts/registry/explorer/stores/state.js
new file mode 100644
index 00000000000..91a378f139b
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/stores/state.js
@@ -0,0 +1,8 @@
+export default () => ({
+ isLoading: false,
+ config: {},
+ images: [],
+ tags: [],
+ pagination: {},
+ tagsPagination: {},
+});
diff --git a/app/assets/javascripts/registry/list/index.js b/app/assets/javascripts/registry/list/index.js
index 3d0ff327b42..e8e54fda169 100644
--- a/app/assets/javascripts/registry/list/index.js
+++ b/app/assets/javascripts/registry/list/index.js
@@ -4,14 +4,20 @@ import Translate from '~/vue_shared/translate';
Vue.use(Translate);
-export default () =>
- new Vue({
- el: '#js-vue-registry-images',
+export default () => {
+ const el = document.getElementById('js-vue-registry-images');
+
+ if (!el) {
+ return null;
+ }
+
+ return new Vue({
+ el,
components: {
registryApp,
},
data() {
- const { dataset } = document.querySelector(this.$options.el);
+ const { dataset } = el;
return {
registryData: {
endpoint: dataset.endpoint,
@@ -35,3 +41,4 @@ export default () =>
});
},
});
+};
diff --git a/app/assets/javascripts/repository/utils/dom.js b/app/assets/javascripts/repository/utils/dom.js
index 81565a00d82..abf726194ac 100644
--- a/app/assets/javascripts/repository/utils/dom.js
+++ b/app/assets/javascripts/repository/utils/dom.js
@@ -1,3 +1,5 @@
+import { joinPaths } from '~/lib/utils/url_utility';
+
export const updateElementsVisibility = (selector, isVisible) => {
document.querySelectorAll(selector).forEach(elem => elem.classList.toggle('hidden', !isVisible));
};
@@ -6,6 +8,6 @@ export const updateFormAction = (selector, basePath, path) => {
const form = document.querySelector(selector);
if (form) {
- form.action = `${basePath}${path}`;
+ form.action = joinPaths(basePath, path);
}
};