From aee0a117a889461ce8ced6fcf73207fe017f1d99 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 20 Dec 2021 13:37:47 +0000 Subject: Add latest changes from gitlab-org/gitlab@14-6-stable-ee --- .../list/components/infrastructure_search.vue | 45 ++++++++ .../list/components/infrastructure_title.vue | 53 +++++++++ .../list/components/packages_list.vue | 127 +++++++++++++++++++++ .../list/components/packages_list_app.vue | 119 +++++++++++++++++++ .../infrastructure_registry/list/constants.js | 51 +++++++++ .../infrastructure_registry/list/stores/actions.js | 83 ++++++++++++++ .../infrastructure_registry/list/stores/getters.js | 5 + .../infrastructure_registry/list/stores/index.js | 20 ++++ .../list/stores/mutation_types.js | 7 ++ .../list/stores/mutations.js | 33 ++++++ .../infrastructure_registry/list/stores/state.js | 54 +++++++++ .../infrastructure_registry/list/utils.js | 25 ++++ 12 files changed, 622 insertions(+) create mode 100644 app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/infrastructure_search.vue create mode 100644 app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/infrastructure_title.vue create mode 100644 app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list.vue create mode 100644 app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue create mode 100644 app/assets/javascripts/packages_and_registries/infrastructure_registry/list/constants.js create mode 100644 app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js create mode 100644 app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/getters.js create mode 100644 app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/index.js create mode 100644 app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/mutation_types.js create mode 100644 app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/mutations.js create mode 100644 app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/state.js create mode 100644 app/assets/javascripts/packages_and_registries/infrastructure_registry/list/utils.js (limited to 'app/assets/javascripts/packages_and_registries/infrastructure_registry/list') diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/infrastructure_search.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/infrastructure_search.vue new file mode 100644 index 00000000000..c611f92036d --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/infrastructure_search.vue @@ -0,0 +1,45 @@ + + + diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/infrastructure_title.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/infrastructure_title.vue new file mode 100644 index 00000000000..2a479c65d0c --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/infrastructure_title.vue @@ -0,0 +1,53 @@ + + + diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list.vue new file mode 100644 index 00000000000..a5f367bc1f6 --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list.vue @@ -0,0 +1,127 @@ + + + diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue new file mode 100644 index 00000000000..462618a7f12 --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue @@ -0,0 +1,119 @@ + + + diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/constants.js b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/constants.js new file mode 100644 index 00000000000..7af3fc1c2db --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/constants.js @@ -0,0 +1,51 @@ +import { __ } from '~/locale'; + +export const FETCH_PACKAGES_LIST_ERROR_MESSAGE = __( + 'Something went wrong while fetching the packages list.', +); +export const DELETE_PACKAGE_SUCCESS_MESSAGE = __('Package deleted successfully'); + +export const DEFAULT_PAGE = 1; +export const DEFAULT_PAGE_SIZE = 20; + +export const GROUP_PAGE_TYPE = 'groups'; + +export const LIST_KEY_NAME = 'name'; +export const LIST_KEY_PROJECT = 'project_path'; +export const LIST_KEY_VERSION = 'version'; +export const LIST_KEY_PACKAGE_TYPE = 'type'; +export const LIST_KEY_CREATED_AT = 'created_at'; + +export const LIST_LABEL_NAME = __('Name'); +export const LIST_LABEL_PROJECT = __('Project'); +export const LIST_LABEL_VERSION = __('Version'); +export const LIST_LABEL_PACKAGE_TYPE = __('Type'); +export const LIST_LABEL_CREATED_AT = __('Published'); + +// The following is not translated because it is used to build a JavaScript exception error message +export const MISSING_DELETE_PATH_ERROR = 'Missing delete_api_path link'; + +export const SORT_FIELDS = [ + { + orderBy: LIST_KEY_NAME, + label: LIST_LABEL_NAME, + }, + { + orderBy: LIST_KEY_PROJECT, + label: LIST_LABEL_PROJECT, + }, + { + orderBy: LIST_KEY_VERSION, + label: LIST_LABEL_VERSION, + }, + { + orderBy: LIST_KEY_PACKAGE_TYPE, + label: LIST_LABEL_PACKAGE_TYPE, + }, + { + orderBy: LIST_KEY_CREATED_AT, + label: LIST_LABEL_CREATED_AT, + }, +]; + +export const TERRAFORM_SEARCH_TYPE = Object.freeze({ value: { data: 'terraform_module' } }); diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js new file mode 100644 index 00000000000..488860e5bc2 --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/actions.js @@ -0,0 +1,83 @@ +import Api from '~/api'; +import createFlash from '~/flash'; +import axios from '~/lib/utils/axios_utils'; +import { DELETE_PACKAGE_ERROR_MESSAGE } from '~/packages_and_registries/shared/constants'; +import { + FETCH_PACKAGES_LIST_ERROR_MESSAGE, + DELETE_PACKAGE_SUCCESS_MESSAGE, + DEFAULT_PAGE, + DEFAULT_PAGE_SIZE, + MISSING_DELETE_PATH_ERROR, + TERRAFORM_SEARCH_TYPE, +} from '../constants'; +import { getNewPaginationPage } from '../utils'; +import * as types from './mutation_types'; + +export const setInitialState = ({ commit }, data) => commit(types.SET_INITIAL_STATE, data); +export const setLoading = ({ commit }, data) => commit(types.SET_MAIN_LOADING, data); +export const setSorting = ({ commit }, data) => commit(types.SET_SORTING, data); +export const setFilter = ({ commit }, data) => commit(types.SET_FILTER, data); + +export const receivePackagesListSuccess = ({ commit }, { data, headers }) => { + commit(types.SET_PACKAGE_LIST_SUCCESS, data); + commit(types.SET_PAGINATION, headers); +}; + +export const requestPackagesList = ({ dispatch, state }, params = {}) => { + dispatch('setLoading', true); + + const { page = DEFAULT_PAGE, per_page = DEFAULT_PAGE_SIZE } = params; + const { sort, orderBy } = state.sorting; + const type = state.config.forceTerraform + ? TERRAFORM_SEARCH_TYPE + : state.filter.find((f) => f.type === 'type'); + const name = state.filter.find((f) => f.type === 'filtered-search-term'); + const packageFilters = { package_type: type?.value?.data, package_name: name?.value?.data }; + + const apiMethod = state.config.isGroupPage ? 'groupPackages' : 'projectPackages'; + + return Api[apiMethod](state.config.resourceId, { + params: { page, per_page, sort, order_by: orderBy, ...packageFilters }, + }) + .then(({ data, headers }) => { + dispatch('receivePackagesListSuccess', { data, headers }); + }) + .catch(() => { + createFlash({ + message: FETCH_PACKAGES_LIST_ERROR_MESSAGE, + }); + }) + .finally(() => { + dispatch('setLoading', false); + }); +}; + +export const requestDeletePackage = ({ dispatch, state }, { _links }) => { + if (!_links || !_links.delete_api_path) { + createFlash({ + message: DELETE_PACKAGE_ERROR_MESSAGE, + }); + const error = new Error(MISSING_DELETE_PATH_ERROR); + return Promise.reject(error); + } + + dispatch('setLoading', true); + return axios + .delete(_links.delete_api_path) + .then(() => { + const { page: currentPage, perPage, total } = state.pagination; + const page = getNewPaginationPage(currentPage, perPage, total - 1); + + dispatch('requestPackagesList', { page }); + createFlash({ + message: DELETE_PACKAGE_SUCCESS_MESSAGE, + type: 'success', + }); + }) + .catch(() => { + dispatch('setLoading', false); + createFlash({ + message: DELETE_PACKAGE_ERROR_MESSAGE, + }); + }); +}; diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/getters.js b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/getters.js new file mode 100644 index 00000000000..5989303280e --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/getters.js @@ -0,0 +1,5 @@ +import { beautifyPath } from '~/packages_and_registries/shared/utils'; +import { LIST_KEY_PROJECT } from '../constants'; + +export default (state) => + state.packages.map((p) => ({ ...p, projectPathName: beautifyPath(p[LIST_KEY_PROJECT]) })); diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/index.js b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/index.js new file mode 100644 index 00000000000..1d6a4bf831d --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/index.js @@ -0,0 +1,20 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import * as actions from './actions'; +import getList from './getters'; +import mutations from './mutations'; +import state from './state'; + +Vue.use(Vuex); + +export const createStore = () => + new Vuex.Store({ + state, + getters: { + getList, + }, + actions, + mutations, + }); + +export default createStore(); diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/mutation_types.js b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/mutation_types.js new file mode 100644 index 00000000000..561ad97f7e3 --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/mutation_types.js @@ -0,0 +1,7 @@ +export const SET_INITIAL_STATE = 'SET_INITIAL_STATE'; + +export const SET_PACKAGE_LIST_SUCCESS = 'SET_PACKAGE_LIST_SUCCESS'; +export const SET_PAGINATION = 'SET_PAGINATION'; +export const SET_MAIN_LOADING = 'SET_MAIN_LOADING'; +export const SET_SORTING = 'SET_SORTING'; +export const SET_FILTER = 'SET_FILTER'; diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/mutations.js b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/mutations.js new file mode 100644 index 00000000000..98165e581b0 --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/mutations.js @@ -0,0 +1,33 @@ +import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; +import { GROUP_PAGE_TYPE } from '../constants'; +import * as types from './mutation_types'; + +export default { + [types.SET_INITIAL_STATE](state, config) { + state.config = { + ...config, + isGroupPage: config.pageType === GROUP_PAGE_TYPE, + }; + }, + + [types.SET_PACKAGE_LIST_SUCCESS](state, packages) { + state.packages = packages; + }, + + [types.SET_MAIN_LOADING](state, isLoading) { + state.isLoading = isLoading; + }, + + [types.SET_PAGINATION](state, headers) { + const normalizedHeaders = normalizeHeaders(headers); + state.pagination = parseIntPagination(normalizedHeaders); + }, + + [types.SET_SORTING](state, sorting) { + state.sorting = { ...state.sorting, ...sorting }; + }, + + [types.SET_FILTER](state, filter) { + state.filter = filter; + }, +}; diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/state.js b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/state.js new file mode 100644 index 00000000000..60f02eddc9f --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/stores/state.js @@ -0,0 +1,54 @@ +export default () => ({ + /** + * Determine if the component is loading data from the API + */ + isLoading: false, + /** + * configuration object, set once at store creation with the following structure + * { + * resourceId: String, + * pageType: String, + * emptyListIllustration: String, + * emptyListHelpUrl: String, + * comingSoon: { projectPath: String, suggestedContributions : String } | null; + * } + */ + config: {}, + /** + * Each object in `packages` has the following structure: + * { + * id: String + * name: String, + * version: String, + * package_type: String // endpoint to request the list + * } + */ + packages: [], + /** + * Pagination object has the following structure: + * { + * perPage: Number, + * page: Number + * total: Number + * } + */ + pagination: {}, + /** + * Sorting object has the following structure: + * { + * sort: String, + * orderBy: String + * } + */ + sorting: { + sort: 'desc', + orderBy: 'created_at', + }, + /** + * The search query that is used to filter packages by name + */ + filter: [], + /** + * The selected TAB of the package types tabs + */ +}); diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/utils.js b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/utils.js new file mode 100644 index 00000000000..537b30d2ca4 --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/utils.js @@ -0,0 +1,25 @@ +import { LIST_KEY_PROJECT, SORT_FIELDS } from './constants'; + +export const sortableFields = (isGroupPage) => + SORT_FIELDS.filter((f) => f.orderBy !== LIST_KEY_PROJECT || isGroupPage); + +/** + * A small util function that works out if the delete action has deleted the + * last item on the current paginated page and if so, returns the previous + * page. This ensures the user won't end up on an empty paginated page. + * + * @param {number} currentPage The current page the user is on + * @param {number} perPage Number of items to display per page + * @param {number} totalPackages The total number of items + */ +export const getNewPaginationPage = (currentPage, perPage, totalItems) => { + if (totalItems <= perPage) { + return 1; + } + + if (currentPage > 1 && (currentPage - 1) * perPage >= totalItems) { + return currentPage - 1; + } + + return currentPage; +}; -- cgit v1.2.3