2022-08-27 11:52:29 +05:30
|
|
|
import { isEmpty } from 'lodash';
|
2021-09-04 01:27:46 +05:30
|
|
|
import { queryToObject, setUrlParams } from '~/lib/utils/url_utility';
|
|
|
|
import {
|
|
|
|
filterToQueryObject,
|
|
|
|
processFilters,
|
|
|
|
urlQueryToFilter,
|
|
|
|
prepareTokens,
|
|
|
|
} from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
|
2022-06-21 17:19:12 +05:30
|
|
|
import { parseBoolean } from '~/lib/utils/common_utils';
|
2021-09-04 01:27:46 +05:30
|
|
|
import {
|
2022-06-21 17:19:12 +05:30
|
|
|
PARAM_KEY_PAUSED,
|
2021-09-04 01:27:46 +05:30
|
|
|
PARAM_KEY_STATUS,
|
|
|
|
PARAM_KEY_RUNNER_TYPE,
|
2021-09-30 23:02:18 +05:30
|
|
|
PARAM_KEY_TAG,
|
|
|
|
PARAM_KEY_SEARCH,
|
2022-11-25 23:54:43 +05:30
|
|
|
PARAM_KEY_MEMBERSHIP,
|
2021-09-04 01:27:46 +05:30
|
|
|
PARAM_KEY_SORT,
|
|
|
|
PARAM_KEY_AFTER,
|
|
|
|
PARAM_KEY_BEFORE,
|
|
|
|
DEFAULT_SORT,
|
2022-11-25 23:54:43 +05:30
|
|
|
DEFAULT_MEMBERSHIP,
|
2021-09-04 01:27:46 +05:30
|
|
|
RUNNER_PAGE_SIZE,
|
2021-10-27 15:23:28 +05:30
|
|
|
} from './constants';
|
2022-04-04 11:22:00 +05:30
|
|
|
import { getPaginationVariables } from './utils';
|
2021-09-04 01:27:46 +05:30
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
/**
|
|
|
|
* The filters and sorting of the runners are built around
|
|
|
|
* an object called "search" that contains the current state
|
|
|
|
* of search in the UI. For example:
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* const search = {
|
|
|
|
* // The current tab
|
|
|
|
* runnerType: 'INSTANCE_TYPE',
|
|
|
|
*
|
|
|
|
* // Filters in the search bar
|
|
|
|
* filters: [
|
|
|
|
* { type: 'status', value: { data: 'ACTIVE', operator: '=' } },
|
|
|
|
* { type: 'filtered-search-term', value: { data: '' } },
|
|
|
|
* ],
|
|
|
|
*
|
|
|
|
* // Current sorting value
|
|
|
|
* sort: 'CREATED_DESC',
|
|
|
|
*
|
|
|
|
* // Pagination information
|
2022-08-27 11:52:29 +05:30
|
|
|
* pagination: { "after": "..." },
|
2021-12-11 22:18:48 +05:30
|
|
|
* };
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* An object in this format can be used to generate URLs
|
|
|
|
* with the search parameters or by runner components
|
|
|
|
* a input using a v-model.
|
|
|
|
*
|
|
|
|
* @module runner_search_utils
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validates a search value
|
|
|
|
* @param {Object} search
|
|
|
|
* @returns {boolean} True if the value follows the search format.
|
|
|
|
*/
|
2022-11-25 23:54:43 +05:30
|
|
|
export const searchValidator = ({ runnerType, membership, filters, sort }) => {
|
2021-12-11 22:18:48 +05:30
|
|
|
return (
|
|
|
|
(runnerType === null || typeof runnerType === 'string') &&
|
2022-11-25 23:54:43 +05:30
|
|
|
(membership === null || typeof membership === 'string') &&
|
2021-12-11 22:18:48 +05:30
|
|
|
Array.isArray(filters) &&
|
|
|
|
typeof sort === 'string'
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2021-09-04 01:27:46 +05:30
|
|
|
const getPaginationFromParams = (params) => {
|
|
|
|
return {
|
2022-08-27 11:52:29 +05:30
|
|
|
after: params[PARAM_KEY_AFTER],
|
|
|
|
before: params[PARAM_KEY_BEFORE],
|
2021-09-04 01:27:46 +05:30
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2022-03-02 08:16:31 +05:30
|
|
|
// Outdated URL parameters
|
2022-06-21 17:19:12 +05:30
|
|
|
const STATUS_ACTIVE = 'ACTIVE';
|
|
|
|
const STATUS_PAUSED = 'PAUSED';
|
2022-08-27 11:52:29 +05:30
|
|
|
const PARAM_KEY_PAGE = 'page';
|
2022-06-21 17:19:12 +05:30
|
|
|
|
|
|
|
/**
|
|
|
|
* Replaces params into a URL
|
|
|
|
*
|
|
|
|
* @param {String} url - Original URL
|
|
|
|
* @param {Object} params - Query parameters to update
|
|
|
|
* @returns Updated URL
|
|
|
|
*/
|
|
|
|
const updateUrlParams = (url, params = {}) => {
|
|
|
|
return setUrlParams(params, url, false, true, true);
|
|
|
|
};
|
2022-03-02 08:16:31 +05:30
|
|
|
|
2022-08-27 11:52:29 +05:30
|
|
|
const outdatedStatusParams = (status) => {
|
|
|
|
if (status === STATUS_ACTIVE) {
|
|
|
|
return {
|
|
|
|
[PARAM_KEY_PAUSED]: ['false'],
|
|
|
|
[PARAM_KEY_STATUS]: [], // Important! clear PARAM_KEY_STATUS to avoid a redirection loop!
|
|
|
|
};
|
|
|
|
} else if (status === STATUS_PAUSED) {
|
|
|
|
return {
|
|
|
|
[PARAM_KEY_PAUSED]: ['true'],
|
|
|
|
[PARAM_KEY_STATUS]: [], // Important! clear PARAM_KEY_STATUS to avoid a redirection loop!
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
};
|
|
|
|
|
2022-03-02 08:16:31 +05:30
|
|
|
/**
|
|
|
|
* Returns an updated URL for old (or deprecated) admin runner URLs.
|
|
|
|
*
|
|
|
|
* Use for redirecting users to currently used URLs.
|
|
|
|
*
|
|
|
|
* @param {String?} URL
|
|
|
|
* @returns Updated URL if outdated, `null` otherwise
|
|
|
|
*/
|
|
|
|
export const updateOutdatedUrl = (url = window.location.href) => {
|
|
|
|
const urlObj = new URL(url);
|
|
|
|
const query = urlObj.search;
|
|
|
|
const params = queryToObject(query, { gatherArrays: true });
|
|
|
|
|
2022-08-27 11:52:29 +05:30
|
|
|
// Remove `page` completely, not needed for keyset pagination
|
|
|
|
const pageParams = PARAM_KEY_PAGE in params ? { [PARAM_KEY_PAGE]: null } : {};
|
|
|
|
|
|
|
|
const status = params[PARAM_KEY_STATUS]?.[0];
|
|
|
|
const redirectParams = {
|
|
|
|
// Replace paused status (active, paused) with a paused flag
|
|
|
|
...outdatedStatusParams(status),
|
|
|
|
...pageParams,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!isEmpty(redirectParams)) {
|
|
|
|
return updateUrlParams(url, redirectParams);
|
2022-03-02 08:16:31 +05:30
|
|
|
}
|
2022-08-27 11:52:29 +05:30
|
|
|
return null;
|
2022-03-02 08:16:31 +05:30
|
|
|
};
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
/**
|
|
|
|
* Takes a URL query and transforms it into a "search" object
|
|
|
|
* @param {String?} query
|
|
|
|
* @returns {Object} A search object
|
|
|
|
*/
|
2021-09-04 01:27:46 +05:30
|
|
|
export const fromUrlQueryToSearch = (query = window.location.search) => {
|
|
|
|
const params = queryToObject(query, { gatherArrays: true });
|
2021-12-11 22:18:48 +05:30
|
|
|
const runnerType = params[PARAM_KEY_RUNNER_TYPE]?.[0] || null;
|
2022-11-25 23:54:43 +05:30
|
|
|
const membership = params[PARAM_KEY_MEMBERSHIP]?.[0] || null;
|
2021-09-04 01:27:46 +05:30
|
|
|
|
|
|
|
return {
|
2021-12-11 22:18:48 +05:30
|
|
|
runnerType,
|
2022-11-25 23:54:43 +05:30
|
|
|
membership: membership || DEFAULT_MEMBERSHIP,
|
2021-09-04 01:27:46 +05:30
|
|
|
filters: prepareTokens(
|
|
|
|
urlQueryToFilter(query, {
|
2022-06-21 17:19:12 +05:30
|
|
|
filterNamesAllowList: [PARAM_KEY_PAUSED, PARAM_KEY_STATUS, PARAM_KEY_TAG],
|
2021-09-04 01:27:46 +05:30
|
|
|
filteredSearchTermKey: PARAM_KEY_SEARCH,
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
sort: params[PARAM_KEY_SORT] || DEFAULT_SORT,
|
|
|
|
pagination: getPaginationFromParams(params),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
/**
|
|
|
|
* Takes a "search" object and transforms it into a URL.
|
|
|
|
*
|
|
|
|
* @param {Object} search
|
|
|
|
* @param {String} url
|
|
|
|
* @returns {String} New URL for the page
|
|
|
|
*/
|
2021-09-04 01:27:46 +05:30
|
|
|
export const fromSearchToUrl = (
|
2022-11-25 23:54:43 +05:30
|
|
|
{ runnerType = null, membership = null, filters = [], sort = null, pagination = {} },
|
2021-09-04 01:27:46 +05:30
|
|
|
url = window.location.href,
|
|
|
|
) => {
|
|
|
|
const filterParams = {
|
|
|
|
// Defaults
|
|
|
|
[PARAM_KEY_STATUS]: [],
|
|
|
|
[PARAM_KEY_RUNNER_TYPE]: [],
|
2022-11-25 23:54:43 +05:30
|
|
|
[PARAM_KEY_MEMBERSHIP]: [],
|
2021-09-30 23:02:18 +05:30
|
|
|
[PARAM_KEY_TAG]: [],
|
2023-03-04 22:38:38 +05:30
|
|
|
[PARAM_KEY_PAUSED]: [],
|
2021-09-04 01:27:46 +05:30
|
|
|
// Current filters
|
|
|
|
...filterToQueryObject(processFilters(filters), {
|
|
|
|
filteredSearchTermKey: PARAM_KEY_SEARCH,
|
|
|
|
}),
|
|
|
|
};
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
if (runnerType) {
|
|
|
|
filterParams[PARAM_KEY_RUNNER_TYPE] = [runnerType];
|
|
|
|
}
|
|
|
|
|
2022-11-25 23:54:43 +05:30
|
|
|
if (membership && membership !== DEFAULT_MEMBERSHIP) {
|
|
|
|
filterParams[PARAM_KEY_MEMBERSHIP] = [membership];
|
|
|
|
}
|
|
|
|
|
2021-09-30 23:02:18 +05:30
|
|
|
if (!filterParams[PARAM_KEY_SEARCH]) {
|
|
|
|
filterParams[PARAM_KEY_SEARCH] = null;
|
|
|
|
}
|
|
|
|
|
2021-09-04 01:27:46 +05:30
|
|
|
const isDefaultSort = sort !== DEFAULT_SORT;
|
|
|
|
const otherParams = {
|
|
|
|
// Sorting & Pagination
|
|
|
|
[PARAM_KEY_SORT]: isDefaultSort ? sort : null,
|
2022-08-27 11:52:29 +05:30
|
|
|
[PARAM_KEY_BEFORE]: pagination?.before || null,
|
|
|
|
[PARAM_KEY_AFTER]: pagination?.after || null,
|
2021-09-04 01:27:46 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
return setUrlParams({ ...filterParams, ...otherParams }, url, false, true, true);
|
|
|
|
};
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
/**
|
|
|
|
* Takes a "search" object and transforms it into variables for runner a GraphQL query.
|
|
|
|
*
|
|
|
|
* @param {Object} search
|
|
|
|
* @returns {Object} Hash of filter values
|
|
|
|
*/
|
|
|
|
export const fromSearchToVariables = ({
|
|
|
|
runnerType = null,
|
2022-11-25 23:54:43 +05:30
|
|
|
membership = null,
|
2021-12-11 22:18:48 +05:30
|
|
|
filters = [],
|
|
|
|
sort = null,
|
|
|
|
pagination = {},
|
|
|
|
} = {}) => {
|
2022-04-04 11:22:00 +05:30
|
|
|
const filterVariables = {};
|
2021-09-04 01:27:46 +05:30
|
|
|
|
|
|
|
const queryObj = filterToQueryObject(processFilters(filters), {
|
|
|
|
filteredSearchTermKey: PARAM_KEY_SEARCH,
|
|
|
|
});
|
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
[filterVariables.status] = queryObj[PARAM_KEY_STATUS] || [];
|
|
|
|
filterVariables.search = queryObj[PARAM_KEY_SEARCH];
|
|
|
|
filterVariables.tagList = queryObj[PARAM_KEY_TAG];
|
2021-09-30 23:02:18 +05:30
|
|
|
|
2022-06-21 17:19:12 +05:30
|
|
|
if (queryObj[PARAM_KEY_PAUSED]) {
|
|
|
|
filterVariables.paused = parseBoolean(queryObj[PARAM_KEY_PAUSED]);
|
|
|
|
} else {
|
|
|
|
filterVariables.paused = undefined;
|
|
|
|
}
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
if (runnerType) {
|
2022-04-04 11:22:00 +05:30
|
|
|
filterVariables.type = runnerType;
|
2021-12-11 22:18:48 +05:30
|
|
|
}
|
2022-11-25 23:54:43 +05:30
|
|
|
if (membership) {
|
|
|
|
filterVariables.membership = membership;
|
|
|
|
}
|
2021-09-04 01:27:46 +05:30
|
|
|
if (sort) {
|
2022-04-04 11:22:00 +05:30
|
|
|
filterVariables.sort = sort;
|
2021-09-04 01:27:46 +05:30
|
|
|
}
|
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
const paginationVariables = getPaginationVariables(pagination, RUNNER_PAGE_SIZE);
|
2021-09-04 01:27:46 +05:30
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
return {
|
|
|
|
...filterVariables,
|
|
|
|
...paginationVariables,
|
|
|
|
};
|
2021-09-04 01:27:46 +05:30
|
|
|
};
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
/**
|
|
|
|
* Decides whether or not a search object is the "default" or empty.
|
|
|
|
*
|
|
|
|
* A search is filtered if the user has entered filtering criteria.
|
|
|
|
*
|
|
|
|
* @param {Object} search
|
|
|
|
* @returns true if this search is filtered, false otherwise
|
|
|
|
*/
|
|
|
|
export const isSearchFiltered = ({ runnerType = null, filters = [], pagination = {} } = {}) => {
|
|
|
|
return Boolean(
|
2022-08-27 11:52:29 +05:30
|
|
|
runnerType !== null || filters?.length !== 0 || pagination?.before || pagination?.after,
|
2022-07-23 23:45:48 +05:30
|
|
|
);
|
|
|
|
};
|