mirror of
https://github.com/gitlabhq/gitlabhq.git
synced 2025-08-13 14:44:59 +00:00
Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
@ -232,10 +232,10 @@ export const getFilterTokens = (locationSearch) =>
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const isNotEmptySearchToken = (token) =>
|
export const isNotEmptySearchToken = (token) =>
|
||||||
!(token.type === FILTERED_SEARCH_TERM && !token.value.data);
|
!(token.type === FILTERED_SEARCH_TERM && !token.value.data);
|
||||||
|
|
||||||
const isSpecialFilter = (type, data) => {
|
export const isSpecialFilter = (type, data) => {
|
||||||
const isAssigneeIdParam =
|
const isAssigneeIdParam =
|
||||||
type === TOKEN_TYPE_ASSIGNEE &&
|
type === TOKEN_TYPE_ASSIGNEE &&
|
||||||
isPositiveInteger(data) &&
|
isPositiveInteger(data) &&
|
||||||
|
@ -2,15 +2,38 @@
|
|||||||
import { GlEmptyState } from '@gitlab/ui';
|
import { GlEmptyState } from '@gitlab/ui';
|
||||||
import * as Sentry from '@sentry/browser';
|
import * as Sentry from '@sentry/browser';
|
||||||
import fuzzaldrinPlus from 'fuzzaldrin-plus';
|
import fuzzaldrinPlus from 'fuzzaldrin-plus';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
import { fetchPolicies } from '~/lib/graphql';
|
import { fetchPolicies } from '~/lib/graphql';
|
||||||
|
import { isPositiveInteger } from '~/lib/utils/number_utils';
|
||||||
import axios from '~/lib/utils/axios_utils';
|
import axios from '~/lib/utils/axios_utils';
|
||||||
|
import { getParameterByName } from '~/lib/utils/url_utility';
|
||||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||||
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
|
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
|
||||||
import { issuableListTabs } from '~/vue_shared/issuable/list/constants';
|
import { DEFAULT_PAGE_SIZE, issuableListTabs } from '~/vue_shared/issuable/list/constants';
|
||||||
|
import {
|
||||||
|
convertToSearchQuery,
|
||||||
|
convertToApiParams,
|
||||||
|
getInitialPageParams,
|
||||||
|
getFilterTokens,
|
||||||
|
isSortKey,
|
||||||
|
} from '~/issues/list/utils';
|
||||||
import {
|
import {
|
||||||
OPERATORS_IS_NOT,
|
OPERATORS_IS_NOT,
|
||||||
OPERATORS_IS_NOT_OR,
|
OPERATORS_IS_NOT_OR,
|
||||||
} from '~/vue_shared/components/filtered_search_bar/constants';
|
} from '~/vue_shared/components/filtered_search_bar/constants';
|
||||||
|
import {
|
||||||
|
MAX_LIST_SIZE,
|
||||||
|
ISSUE_REFERENCE,
|
||||||
|
PARAM_STATE,
|
||||||
|
PARAM_FIRST_PAGE_SIZE,
|
||||||
|
PARAM_LAST_PAGE_SIZE,
|
||||||
|
PARAM_PAGE_AFTER,
|
||||||
|
PARAM_PAGE_BEFORE,
|
||||||
|
PARAM_SORT,
|
||||||
|
CREATED_DESC,
|
||||||
|
UPDATED_DESC,
|
||||||
|
urlSortParams,
|
||||||
|
} from '~/issues/list/constants';
|
||||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||||
import { TYPENAME_USER } from '~/graphql_shared/constants';
|
import { TYPENAME_USER } from '~/graphql_shared/constants';
|
||||||
import searchUsersQuery from '~/issues/list/queries/search_users.query.graphql';
|
import searchUsersQuery from '~/issues/list/queries/search_users.query.graphql';
|
||||||
@ -24,12 +47,12 @@ import {
|
|||||||
noSearchNoFilterTitle,
|
noSearchNoFilterTitle,
|
||||||
searchPlaceholder,
|
searchPlaceholder,
|
||||||
SERVICE_DESK_BOT_USERNAME,
|
SERVICE_DESK_BOT_USERNAME,
|
||||||
MAX_LIST_SIZE,
|
|
||||||
STATUS_OPEN,
|
STATUS_OPEN,
|
||||||
STATUS_CLOSED,
|
STATUS_CLOSED,
|
||||||
STATUS_ALL,
|
STATUS_ALL,
|
||||||
WORKSPACE_PROJECT,
|
WORKSPACE_PROJECT,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
|
import { convertToUrlParams } from '../utils';
|
||||||
import {
|
import {
|
||||||
searchWithinTokenBase,
|
searchWithinTokenBase,
|
||||||
assigneeTokenBase,
|
assigneeTokenBase,
|
||||||
@ -68,6 +91,7 @@ export default {
|
|||||||
'fullPath',
|
'fullPath',
|
||||||
'isServiceDeskSupported',
|
'isServiceDeskSupported',
|
||||||
'hasAnyIssues',
|
'hasAnyIssues',
|
||||||
|
'initialSort',
|
||||||
],
|
],
|
||||||
props: {
|
props: {
|
||||||
eeSearchTokens: {
|
eeSearchTokens: {
|
||||||
@ -81,7 +105,12 @@ export default {
|
|||||||
serviceDeskIssues: [],
|
serviceDeskIssues: [],
|
||||||
serviceDeskIssuesCounts: {},
|
serviceDeskIssuesCounts: {},
|
||||||
sortOptions: [],
|
sortOptions: [],
|
||||||
|
filterTokens: [],
|
||||||
|
pageInfo: {},
|
||||||
|
pageParams: {},
|
||||||
|
sortKey: CREATED_DESC,
|
||||||
state: STATUS_OPEN,
|
state: STATUS_OPEN,
|
||||||
|
pageSize: DEFAULT_PAGE_SIZE,
|
||||||
issuesError: null,
|
issuesError: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -109,7 +138,7 @@ export default {
|
|||||||
Sentry.captureException(error);
|
Sentry.captureException(error);
|
||||||
},
|
},
|
||||||
skip() {
|
skip() {
|
||||||
return !this.hasAnyIssues;
|
return this.shouldSkipQuery;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
serviceDeskIssuesCounts: {
|
serviceDeskIssuesCounts: {
|
||||||
@ -124,6 +153,9 @@ export default {
|
|||||||
this.issuesError = this.$options.i18n.errorFetchingCounts;
|
this.issuesError = this.$options.i18n.errorFetchingCounts;
|
||||||
Sentry.captureException(error);
|
Sentry.captureException(error);
|
||||||
},
|
},
|
||||||
|
skip() {
|
||||||
|
return this.shouldSkipQuery;
|
||||||
|
},
|
||||||
context: {
|
context: {
|
||||||
isSingleRequest: true,
|
isSingleRequest: true,
|
||||||
},
|
},
|
||||||
@ -131,14 +163,23 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
queryVariables() {
|
queryVariables() {
|
||||||
|
const isIidSearch = ISSUE_REFERENCE.test(this.searchQuery);
|
||||||
return {
|
return {
|
||||||
fullPath: this.fullPath,
|
fullPath: this.fullPath,
|
||||||
|
iid: isIidSearch ? this.searchQuery.slice(1) : undefined,
|
||||||
isProject: this.isProject,
|
isProject: this.isProject,
|
||||||
isSignedIn: this.isSignedIn,
|
isSignedIn: this.isSignedIn,
|
||||||
authorUsername: SERVICE_DESK_BOT_USERNAME,
|
authorUsername: SERVICE_DESK_BOT_USERNAME,
|
||||||
|
sort: this.sortKey,
|
||||||
state: this.state,
|
state: this.state,
|
||||||
|
...this.pageParams,
|
||||||
|
...this.apiFilterParams,
|
||||||
|
search: isIidSearch ? undefined : this.searchQuery,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
shouldSkipQuery() {
|
||||||
|
return !this.hasAnyIssues || isEmpty(this.pageParams);
|
||||||
|
},
|
||||||
tabCounts() {
|
tabCounts() {
|
||||||
const { openedIssues, closedIssues, allIssues } = this.serviceDeskIssuesCounts;
|
const { openedIssues, closedIssues, allIssues } = this.serviceDeskIssuesCounts;
|
||||||
return {
|
return {
|
||||||
@ -147,12 +188,40 @@ export default {
|
|||||||
[STATUS_ALL]: allIssues?.count,
|
[STATUS_ALL]: allIssues?.count,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
urlParams() {
|
||||||
|
return {
|
||||||
|
sort: urlSortParams[this.sortKey],
|
||||||
|
state: this.state,
|
||||||
|
...this.urlFilterParams,
|
||||||
|
first_page_size: this.pageParams.firstPageSize,
|
||||||
|
last_page_size: this.pageParams.lastPageSize,
|
||||||
|
page_after: this.pageParams.afterCursor ?? undefined,
|
||||||
|
page_before: this.pageParams.beforeCursor ?? undefined,
|
||||||
|
};
|
||||||
|
},
|
||||||
isInfoBannerVisible() {
|
isInfoBannerVisible() {
|
||||||
return this.isServiceDeskSupported && this.hasAnyIssues;
|
return this.isServiceDeskSupported && this.hasAnyIssues;
|
||||||
},
|
},
|
||||||
hasOrFeature() {
|
hasOrFeature() {
|
||||||
return this.glFeatures.orIssuableQueries;
|
return this.glFeatures.orIssuableQueries;
|
||||||
},
|
},
|
||||||
|
hasSearch() {
|
||||||
|
return Boolean(
|
||||||
|
this.searchQuery ||
|
||||||
|
Object.keys(this.urlFilterParams).length ||
|
||||||
|
this.pageParams.afterCursor ||
|
||||||
|
this.pageParams.beforeCursor,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
apiFilterParams() {
|
||||||
|
return convertToApiParams(this.filterTokens);
|
||||||
|
},
|
||||||
|
urlFilterParams() {
|
||||||
|
return convertToUrlParams(this.filterTokens);
|
||||||
|
},
|
||||||
|
searchQuery() {
|
||||||
|
return convertToSearchQuery(this.filterTokens);
|
||||||
|
},
|
||||||
searchTokens() {
|
searchTokens() {
|
||||||
const preloadedUsers = [];
|
const preloadedUsers = [];
|
||||||
|
|
||||||
@ -219,7 +288,15 @@ export default {
|
|||||||
return tokens;
|
return tokens;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
$route(newValue, oldValue) {
|
||||||
|
if (newValue.fullPath !== oldValue.fullPath) {
|
||||||
|
this.updateData(getParameterByName(PARAM_SORT));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
|
this.updateData(this.initialSort);
|
||||||
this.cache = {};
|
this.cache = {};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -287,6 +364,37 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
this.pageParams = getInitialPageParams(this.pageSize);
|
||||||
|
|
||||||
|
this.$router.push({ query: this.urlParams });
|
||||||
|
},
|
||||||
|
handleFilter(tokens) {
|
||||||
|
this.filterTokens = tokens;
|
||||||
|
this.pageParams = getInitialPageParams(this.pageSize);
|
||||||
|
|
||||||
|
this.$router.push({ query: this.urlParams });
|
||||||
|
},
|
||||||
|
updateData(sortValue) {
|
||||||
|
const firstPageSize = getParameterByName(PARAM_FIRST_PAGE_SIZE);
|
||||||
|
const lastPageSize = getParameterByName(PARAM_LAST_PAGE_SIZE);
|
||||||
|
const state = getParameterByName(PARAM_STATE);
|
||||||
|
|
||||||
|
const defaultSortKey = state === STATUS_CLOSED ? UPDATED_DESC : CREATED_DESC;
|
||||||
|
const graphQLSortKey = isSortKey(sortValue?.toUpperCase()) && sortValue.toUpperCase();
|
||||||
|
|
||||||
|
const sortKey = graphQLSortKey || defaultSortKey;
|
||||||
|
|
||||||
|
this.filterTokens = getFilterTokens(window.location.search);
|
||||||
|
|
||||||
|
this.pageParams = getInitialPageParams(
|
||||||
|
this.pageSize,
|
||||||
|
isPositiveInteger(firstPageSize) ? parseInt(firstPageSize, 10) : undefined,
|
||||||
|
isPositiveInteger(lastPageSize) ? parseInt(lastPageSize, 10) : undefined,
|
||||||
|
getParameterByName(PARAM_PAGE_AFTER),
|
||||||
|
getParameterByName(PARAM_PAGE_BEFORE),
|
||||||
|
);
|
||||||
|
this.sortKey = sortKey;
|
||||||
|
this.state = state || STATUS_OPEN;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -297,16 +405,22 @@ export default {
|
|||||||
<info-banner v-if="isInfoBannerVisible" />
|
<info-banner v-if="isInfoBannerVisible" />
|
||||||
<issuable-list
|
<issuable-list
|
||||||
namespace="service-desk"
|
namespace="service-desk"
|
||||||
recent-searches-storage-key="issues"
|
recent-searches-storage-key="service-desk-issues"
|
||||||
:error="issuesError"
|
:error="issuesError"
|
||||||
:search-input-placeholder="$options.i18n.searchPlaceholder"
|
:search-input-placeholder="$options.i18n.searchPlaceholder"
|
||||||
:search-tokens="searchTokens"
|
:search-tokens="searchTokens"
|
||||||
|
:initial-filter-value="filterTokens"
|
||||||
|
:show-filtered-search-friendly-text="hasOrFeature"
|
||||||
:sort-options="sortOptions"
|
:sort-options="sortOptions"
|
||||||
|
:initial-sort-by="sortKey"
|
||||||
:issuables="serviceDeskIssues"
|
:issuables="serviceDeskIssues"
|
||||||
:tabs="$options.issuableListTabs"
|
:tabs="$options.issuableListTabs"
|
||||||
:tab-counts="tabCounts"
|
:tab-counts="tabCounts"
|
||||||
:current-tab="state"
|
:current-tab="state"
|
||||||
|
:default-page-size="pageSize"
|
||||||
|
sync-filter-and-sort
|
||||||
@click-tab="handleClickTab"
|
@click-tab="handleClickTab"
|
||||||
|
@filter="handleFilter"
|
||||||
>
|
>
|
||||||
<template #empty-state>
|
<template #empty-state>
|
||||||
<gl-empty-state
|
<gl-empty-state
|
||||||
|
@ -1,12 +1,231 @@
|
|||||||
import { __, s__ } from '~/locale';
|
import { __, s__ } from '~/locale';
|
||||||
|
import {
|
||||||
|
FILTERED_SEARCH_TERM,
|
||||||
|
OPERATOR_IS,
|
||||||
|
OPERATOR_NOT,
|
||||||
|
OPERATOR_OR,
|
||||||
|
TOKEN_TYPE_ASSIGNEE,
|
||||||
|
TOKEN_TYPE_CONFIDENTIAL,
|
||||||
|
TOKEN_TYPE_EPIC,
|
||||||
|
TOKEN_TYPE_HEALTH,
|
||||||
|
TOKEN_TYPE_ITERATION,
|
||||||
|
TOKEN_TYPE_LABEL,
|
||||||
|
TOKEN_TYPE_MILESTONE,
|
||||||
|
TOKEN_TYPE_MY_REACTION,
|
||||||
|
TOKEN_TYPE_RELEASE,
|
||||||
|
TOKEN_TYPE_TYPE,
|
||||||
|
TOKEN_TYPE_WEIGHT,
|
||||||
|
TOKEN_TYPE_SEARCH_WITHIN,
|
||||||
|
} from '~/vue_shared/components/filtered_search_bar/constants';
|
||||||
|
import {
|
||||||
|
ALTERNATIVE_FILTER,
|
||||||
|
API_PARAM,
|
||||||
|
NORMAL_FILTER,
|
||||||
|
SPECIAL_FILTER,
|
||||||
|
URL_PARAM,
|
||||||
|
} from '~/issues/list/constants';
|
||||||
|
|
||||||
export const SERVICE_DESK_BOT_USERNAME = 'support-bot';
|
export const SERVICE_DESK_BOT_USERNAME = 'support-bot';
|
||||||
export const MAX_LIST_SIZE = 10;
|
export const ISSUE_REFERENCE = /^#\d+$/;
|
||||||
|
|
||||||
export const STATUS_ALL = 'all';
|
export const STATUS_ALL = 'all';
|
||||||
export const STATUS_CLOSED = 'closed';
|
export const STATUS_CLOSED = 'closed';
|
||||||
export const STATUS_OPEN = 'opened';
|
export const STATUS_OPEN = 'opened';
|
||||||
|
|
||||||
export const WORKSPACE_PROJECT = 'project';
|
export const WORKSPACE_PROJECT = 'project';
|
||||||
|
|
||||||
|
export const filtersMap = {
|
||||||
|
[FILTERED_SEARCH_TERM]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'search',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[undefined]: {
|
||||||
|
[NORMAL_FILTER]: 'search',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[TOKEN_TYPE_SEARCH_WITHIN]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'in',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[OPERATOR_IS]: {
|
||||||
|
[NORMAL_FILTER]: 'in',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[TOKEN_TYPE_ASSIGNEE]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'assigneeUsernames',
|
||||||
|
[SPECIAL_FILTER]: 'assigneeId',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[OPERATOR_IS]: {
|
||||||
|
[NORMAL_FILTER]: 'assignee_username[]',
|
||||||
|
[SPECIAL_FILTER]: 'assignee_id',
|
||||||
|
[ALTERNATIVE_FILTER]: 'assignee_username',
|
||||||
|
},
|
||||||
|
[OPERATOR_NOT]: {
|
||||||
|
[NORMAL_FILTER]: 'not[assignee_username][]',
|
||||||
|
},
|
||||||
|
[OPERATOR_OR]: {
|
||||||
|
[NORMAL_FILTER]: 'or[assignee_username][]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[TOKEN_TYPE_MILESTONE]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'milestoneTitle',
|
||||||
|
[SPECIAL_FILTER]: 'milestoneWildcardId',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[OPERATOR_IS]: {
|
||||||
|
[NORMAL_FILTER]: 'milestone_title',
|
||||||
|
[SPECIAL_FILTER]: 'milestone_title',
|
||||||
|
},
|
||||||
|
[OPERATOR_NOT]: {
|
||||||
|
[NORMAL_FILTER]: 'not[milestone_title]',
|
||||||
|
[SPECIAL_FILTER]: 'not[milestone_title]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[TOKEN_TYPE_LABEL]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'labelName',
|
||||||
|
[SPECIAL_FILTER]: 'labelName',
|
||||||
|
[ALTERNATIVE_FILTER]: 'labelNames',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[OPERATOR_IS]: {
|
||||||
|
[NORMAL_FILTER]: 'label_name[]',
|
||||||
|
[SPECIAL_FILTER]: 'label_name[]',
|
||||||
|
[ALTERNATIVE_FILTER]: 'label_name',
|
||||||
|
},
|
||||||
|
[OPERATOR_NOT]: {
|
||||||
|
[NORMAL_FILTER]: 'not[label_name][]',
|
||||||
|
},
|
||||||
|
[OPERATOR_OR]: {
|
||||||
|
[ALTERNATIVE_FILTER]: 'or[label_name][]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[TOKEN_TYPE_TYPE]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'types',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[OPERATOR_IS]: {
|
||||||
|
[NORMAL_FILTER]: 'type[]',
|
||||||
|
},
|
||||||
|
[OPERATOR_NOT]: {
|
||||||
|
[NORMAL_FILTER]: 'not[type][]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[TOKEN_TYPE_RELEASE]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'releaseTag',
|
||||||
|
[SPECIAL_FILTER]: 'releaseTagWildcardId',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[OPERATOR_IS]: {
|
||||||
|
[NORMAL_FILTER]: 'release_tag',
|
||||||
|
[SPECIAL_FILTER]: 'release_tag',
|
||||||
|
},
|
||||||
|
[OPERATOR_NOT]: {
|
||||||
|
[NORMAL_FILTER]: 'not[release_tag]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[TOKEN_TYPE_MY_REACTION]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'myReactionEmoji',
|
||||||
|
[SPECIAL_FILTER]: 'myReactionEmoji',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[OPERATOR_IS]: {
|
||||||
|
[NORMAL_FILTER]: 'my_reaction_emoji',
|
||||||
|
[SPECIAL_FILTER]: 'my_reaction_emoji',
|
||||||
|
},
|
||||||
|
[OPERATOR_NOT]: {
|
||||||
|
[NORMAL_FILTER]: 'not[my_reaction_emoji]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[TOKEN_TYPE_CONFIDENTIAL]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'confidential',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[OPERATOR_IS]: {
|
||||||
|
[NORMAL_FILTER]: 'confidential',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[TOKEN_TYPE_ITERATION]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'iterationId',
|
||||||
|
[SPECIAL_FILTER]: 'iterationWildcardId',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[OPERATOR_IS]: {
|
||||||
|
[NORMAL_FILTER]: 'iteration_id',
|
||||||
|
[SPECIAL_FILTER]: 'iteration_id',
|
||||||
|
},
|
||||||
|
[OPERATOR_NOT]: {
|
||||||
|
[NORMAL_FILTER]: 'not[iteration_id]',
|
||||||
|
[SPECIAL_FILTER]: 'not[iteration_id]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[TOKEN_TYPE_EPIC]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'epicId',
|
||||||
|
[SPECIAL_FILTER]: 'epicId',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[OPERATOR_IS]: {
|
||||||
|
[NORMAL_FILTER]: 'epic_id',
|
||||||
|
[SPECIAL_FILTER]: 'epic_id',
|
||||||
|
},
|
||||||
|
[OPERATOR_NOT]: {
|
||||||
|
[NORMAL_FILTER]: 'not[epic_id]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[TOKEN_TYPE_WEIGHT]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'weight',
|
||||||
|
[SPECIAL_FILTER]: 'weight',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[OPERATOR_IS]: {
|
||||||
|
[NORMAL_FILTER]: 'weight',
|
||||||
|
[SPECIAL_FILTER]: 'weight',
|
||||||
|
},
|
||||||
|
[OPERATOR_NOT]: {
|
||||||
|
[NORMAL_FILTER]: 'not[weight]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[TOKEN_TYPE_HEALTH]: {
|
||||||
|
[API_PARAM]: {
|
||||||
|
[NORMAL_FILTER]: 'healthStatusFilter',
|
||||||
|
[SPECIAL_FILTER]: 'healthStatusFilter',
|
||||||
|
},
|
||||||
|
[URL_PARAM]: {
|
||||||
|
[OPERATOR_IS]: {
|
||||||
|
[NORMAL_FILTER]: 'health_status',
|
||||||
|
[SPECIAL_FILTER]: 'health_status',
|
||||||
|
},
|
||||||
|
[OPERATOR_NOT]: {
|
||||||
|
[NORMAL_FILTER]: 'not[health_status]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const errorFetchingCounts = __('An error occurred while getting issue counts');
|
export const errorFetchingCounts = __('An error occurred while getting issue counts');
|
||||||
export const errorFetchingIssues = __('An error occurred while loading issues');
|
export const errorFetchingIssues = __('An error occurred while loading issues');
|
||||||
export const noSearchNoFilterTitle = __('Please select at least one filter to see results');
|
export const noSearchNoFilterTitle = __('Please select at least one filter to see results');
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import VueApollo from 'vue-apollo';
|
import VueApollo from 'vue-apollo';
|
||||||
|
import VueRouter from 'vue-router';
|
||||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||||
import ServiceDeskListApp from 'ee_else_ce/service_desk/components/service_desk_list_app.vue';
|
import ServiceDeskListApp from 'ee_else_ce/service_desk/components/service_desk_list_app.vue';
|
||||||
import { gqlClient } from './graphql';
|
import { gqlClient } from './graphql';
|
||||||
@ -23,6 +24,7 @@ export async function mountServiceDeskListApp() {
|
|||||||
projectDataIsProject,
|
projectDataIsProject,
|
||||||
projectDataIsSignedIn,
|
projectDataIsSignedIn,
|
||||||
projectDataHasAnyIssues,
|
projectDataHasAnyIssues,
|
||||||
|
projectDataInitialSort,
|
||||||
serviceDeskEmailAddress,
|
serviceDeskEmailAddress,
|
||||||
canAdminIssues,
|
canAdminIssues,
|
||||||
canEditProjectSettings,
|
canEditProjectSettings,
|
||||||
@ -34,6 +36,7 @@ export async function mountServiceDeskListApp() {
|
|||||||
} = el.dataset;
|
} = el.dataset;
|
||||||
|
|
||||||
Vue.use(VueApollo);
|
Vue.use(VueApollo);
|
||||||
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
return new Vue({
|
return new Vue({
|
||||||
el,
|
el,
|
||||||
@ -41,6 +44,11 @@ export async function mountServiceDeskListApp() {
|
|||||||
apolloProvider: new VueApollo({
|
apolloProvider: new VueApollo({
|
||||||
defaultClient: await gqlClient(),
|
defaultClient: await gqlClient(),
|
||||||
}),
|
}),
|
||||||
|
router: new VueRouter({
|
||||||
|
base: window.location.pathname,
|
||||||
|
mode: 'history',
|
||||||
|
routes: [{ path: '/' }],
|
||||||
|
}),
|
||||||
provide: {
|
provide: {
|
||||||
releasesPath: projectDataReleasesPath,
|
releasesPath: projectDataReleasesPath,
|
||||||
autocompleteAwardEmojisPath: projectDataAutocompleteAwardEmojisPath,
|
autocompleteAwardEmojisPath: projectDataAutocompleteAwardEmojisPath,
|
||||||
@ -61,6 +69,7 @@ export async function mountServiceDeskListApp() {
|
|||||||
isServiceDeskSupported: parseBoolean(isServiceDeskSupported),
|
isServiceDeskSupported: parseBoolean(isServiceDeskSupported),
|
||||||
isServiceDeskEnabled: parseBoolean(isServiceDeskEnabled),
|
isServiceDeskEnabled: parseBoolean(isServiceDeskEnabled),
|
||||||
hasAnyIssues: parseBoolean(projectDataHasAnyIssues),
|
hasAnyIssues: parseBoolean(projectDataHasAnyIssues),
|
||||||
|
initialSort: projectDataInitialSort,
|
||||||
},
|
},
|
||||||
render: (createComponent) => createComponent(ServiceDeskListApp),
|
render: (createComponent) => createComponent(ServiceDeskListApp),
|
||||||
});
|
});
|
||||||
|
37
app/assets/javascripts/service_desk/utils.js
Normal file
37
app/assets/javascripts/service_desk/utils.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import {
|
||||||
|
OPERATOR_OR,
|
||||||
|
TOKEN_TYPE_LABEL,
|
||||||
|
} from '~/vue_shared/components/filtered_search_bar/constants';
|
||||||
|
import { isSpecialFilter, isNotEmptySearchToken } from '~/issues/list/utils';
|
||||||
|
import {
|
||||||
|
ALTERNATIVE_FILTER,
|
||||||
|
NORMAL_FILTER,
|
||||||
|
SPECIAL_FILTER,
|
||||||
|
URL_PARAM,
|
||||||
|
} from '~/issues/list/constants';
|
||||||
|
import { filtersMap } from './constants';
|
||||||
|
|
||||||
|
const getFilterType = ({ type, value: { data, operator } }) => {
|
||||||
|
const isUnionedLabel = type === TOKEN_TYPE_LABEL && operator === OPERATOR_OR;
|
||||||
|
|
||||||
|
if (isUnionedLabel) {
|
||||||
|
return ALTERNATIVE_FILTER;
|
||||||
|
}
|
||||||
|
if (isSpecialFilter(type, data)) {
|
||||||
|
return SPECIAL_FILTER;
|
||||||
|
}
|
||||||
|
return NORMAL_FILTER;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertToUrlParams = (filterTokens) => {
|
||||||
|
const urlParamsMap = filterTokens.filter(isNotEmptySearchToken).reduce((acc, token) => {
|
||||||
|
const filterType = getFilterType(token);
|
||||||
|
const urlParam = filtersMap[token.type][URL_PARAM][token.value.operator]?.[filterType];
|
||||||
|
return acc.set(
|
||||||
|
urlParam,
|
||||||
|
acc.has(urlParam) ? [acc.get(urlParam), token.value.data].flat() : token.value.data,
|
||||||
|
);
|
||||||
|
}, new Map());
|
||||||
|
|
||||||
|
return Object.fromEntries(urlParamsMap);
|
||||||
|
};
|
@ -41,7 +41,7 @@ To enable the agent server on a single node:
|
|||||||
1. [Reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
|
1. [Reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
|
||||||
|
|
||||||
For additional configuration options, see the **Enable GitLab KAS** section of the
|
For additional configuration options, see the **Enable GitLab KAS** section of the
|
||||||
[`gitlab.rb.template`](https://gitlab.com/gitlab-org/omnibus-gitlab/-/blob/master/files/gitlab-config-template/gitlab.rb.template).
|
[`gitlab.rb.template`](https://gitlab.com/gitlab-org/omnibus-gitlab/-/blob/be52c36c243a3422ec38b7d45d459682a07e195f/files/gitlab-config-template/gitlab.rb.template#L1951).
|
||||||
|
|
||||||
##### Configure KAS to listen on a UNIX socket
|
##### Configure KAS to listen on a UNIX socket
|
||||||
|
|
||||||
|
@ -236,37 +236,61 @@ The project for a new Gem should always be created in [`gitlab-org/ruby/gems` na
|
|||||||
|
|
||||||
1. Determine a suitable name for the gem. If it's a GitLab-owned gem, prefix
|
1. Determine a suitable name for the gem. If it's a GitLab-owned gem, prefix
|
||||||
the gem name with `gitlab-`. For example, `gitlab-sidekiq-fetcher`.
|
the gem name with `gitlab-`. For example, `gitlab-sidekiq-fetcher`.
|
||||||
1. Create the gem or fork as necessary.
|
1. Locally create the gem or fork as necessary.
|
||||||
1. Ensure the `gitlab_rubygems` group is an owner of the new gem by running:
|
1. [Publish an empty `0.0.1` version of the gem to rubygems.org](https://guides.rubygems.org/publishing/#publishing-to-rubygemsorg) to ensure the gem name is reserved.
|
||||||
|
1. Add the [`gitlab_rubygems`](https://rubygems.org/profiles/gitlab_rubygems) and [`gitlab-qa`](https://rubygems.org/profiles/gitlab-qa) users as owners of the new gem by running:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
gem owner <gem-name> --add gitlab_rubygems
|
gem owner <gem-name> --add gitlab_rubygems
|
||||||
|
gem owner <gem-name> --add gitlab-qa
|
||||||
```
|
```
|
||||||
|
|
||||||
1. [Publish the gem to rubygems.org](https://guides.rubygems.org/publishing/#publishing-to-rubygemsorg)
|
1. Optional. Add some or all of the following users as co-owners:
|
||||||
1. Visit `https://rubygems.org/gems/<gem-name>` and verify that the gem published
|
- [Marin Jankovski](https://rubygems.org/profiles/marinjankovski)
|
||||||
successfully and `gitlab_rubygems` is also an owner.
|
- [Rémy Coutable](https://rubygems.org/profiles/rymai)
|
||||||
1. Create a project in [`gitlab-org/ruby/gems` namespace](https://gitlab.com/gitlab-org/ruby/gems/).
|
- [Stan Hu](https://rubygems.org/profiles/stanhu)
|
||||||
|
1. Optional. Add any other relevant developers as co-owners.
|
||||||
|
1. Visit `https://rubygems.org/gems/<gem-name>` and verify that the gem was published
|
||||||
|
successfully and `gitlab_rubygems` & `gitlab-qa` are also owners.
|
||||||
|
1. Create a project in the [`gitlab-org/ruby/gems` group](https://gitlab.com/gitlab-org/ruby/gems/). To create this project:
|
||||||
|
1. Follow the [instructions for new projects](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#creating-a-new-project).
|
||||||
|
1. Follow the instructions for setting up a [CI/CD configuration](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#cicd-configuration).
|
||||||
|
1. Use the [shared CI/CD config](https://gitlab.com/gitlab-org/quality/pipeline-common/-/blob/master/ci/gem-release.yml)
|
||||||
|
to release and publish new gem versions by adding the following to their `.gitlab-ci.yml`:
|
||||||
|
|
||||||
- To create this project:
|
```yaml
|
||||||
1. Follow the [instructions for new projects](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#creating-a-new-project).
|
include:
|
||||||
1. Follow the instructions for setting up a [CI/CD configuration](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#cicd-configuration).
|
- project: 'gitlab-org/quality/pipeline-common'
|
||||||
1. Follow the instructions for [publishing a project](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#publishing-a-project).
|
file: '/ci/gem-release.yml'
|
||||||
- See [issue #325463](https://gitlab.com/gitlab-org/gitlab/-/issues/325463)
|
```
|
||||||
for an example.
|
|
||||||
- In some cases we may want to move a gem to its own namespace. Some
|
|
||||||
examples might be that it will naturally have more than one project
|
|
||||||
(say, something that has plugins as separate libraries), or that we
|
|
||||||
expect users outside GitLab to be maintainers on this project as
|
|
||||||
well as GitLab team members.
|
|
||||||
|
|
||||||
The latter situation (maintainers from outside GitLab) could also
|
This job will handle building and publishing the gem (it uses a `gilab-qa` Rubygems.org
|
||||||
apply if someone who currently works at GitLab wants to maintain
|
API token inherited from the `gitlab-org/ruby/gems` group, in order to publish the gem
|
||||||
the gem beyond their time working at GitLab.
|
package), as well as creating the tag, release and populating its release notes by
|
||||||
|
using the
|
||||||
|
[Generate changelog data](../api/repositories.md#generate-changelog-data)
|
||||||
|
API endpoint.
|
||||||
|
|
||||||
When publishing a gem to RubyGems.org, also note the section on
|
For instructions for when and how to generate a changelog entry file, see the
|
||||||
[gem owners](https://about.gitlab.com/handbook/developer-onboarding/#ruby-gems)
|
dedicated [Changelog entries](changelog.md)
|
||||||
in the handbook.
|
page.
|
||||||
|
[To be consistent with the GitLab project](changelog.md),
|
||||||
|
Gem projects could also define a changelog YAML configuration file at
|
||||||
|
`.gitlab/changelog_config.yml` with the same content
|
||||||
|
as [in the `gitlab-styles` gem](https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/blob/master/.gitlab/changelog_config.yml).
|
||||||
|
1. To ease the release process, you could also create a `.gitlab/merge_request_templates/Release.md` MR template with the same content
|
||||||
|
as [in the `gitlab-styles` gem](https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/raw/master/.gitlab/merge_request_templates/Release.md)
|
||||||
|
(make sure to replace `gitlab-styles` with the actual gem name).
|
||||||
|
1. Follow the instructions for [publishing a project](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#publishing-a-project).
|
||||||
|
|
||||||
|
Notes: In some cases we may want to move a gem to its own namespace. Some
|
||||||
|
examples might be that it will naturally have more than one project
|
||||||
|
(say, something that has plugins as separate libraries), or that we
|
||||||
|
expect users outside GitLab to be maintainers on this project as
|
||||||
|
well as GitLab team members.
|
||||||
|
The latter situation (maintainers from outside GitLab) could also
|
||||||
|
apply if someone who currently works at GitLab wants to maintain
|
||||||
|
the gem beyond their time working at GitLab.
|
||||||
|
|
||||||
## The `vendor/gems/`
|
## The `vendor/gems/`
|
||||||
|
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
---
|
---
|
||||||
stage: Deploy
|
stage: Deploy
|
||||||
group: Environments
|
group: Environments
|
||||||
info: A tutorial for structuring a repository for GitOps deployments
|
info: A tutorial for deploying a GitLab repository using Flux
|
||||||
---
|
---
|
||||||
|
|
||||||
# Tutorial: Structure your repository for GitOps deployments **(FREE)**
|
# Tutorial: Deploy a Git repository using Flux **(FREE)**
|
||||||
|
|
||||||
In this tutorial, you'll create a GitLab project that builds and deploys an application
|
In this tutorial, you'll create a GitLab project that builds and deploys an application
|
||||||
to a Kubernetes cluster using Flux. You'll set up a sample manifest project, configure it to
|
to a Kubernetes cluster using Flux. You'll set up a sample manifest project, configure it to
|
||||||
push manifests to a deployment branch, and configure Flux to sync the deployment branch.
|
push manifests to a deployment branch, and configure Flux to sync the deployment branch. With this
|
||||||
|
setup, you can run additional steps in GitLab pipelines before Flux picks up the changes
|
||||||
|
from the repository.
|
||||||
|
|
||||||
This tutorial deploys an application from a public project. If you want to add a non-public project, you should create a [project deploy token](../../../project/deploy_tokens/index.md).
|
This tutorial deploys an application from a public project. If you want to add a non-public project, you should create a [project deploy token](../../../project/deploy_tokens/index.md).
|
||||||
|
|
||||||
|
@ -8,7 +8,9 @@ info: A tutorial for deploying an OCI artifact using Flux
|
|||||||
|
|
||||||
This tutorial teaches you how to package your Kubernetes manifests into an [OCI](https://opencontainers.org/)
|
This tutorial teaches you how to package your Kubernetes manifests into an [OCI](https://opencontainers.org/)
|
||||||
artifact and deploy them to your cluster using Flux. You'll set up a sample manifest project, configure it to
|
artifact and deploy them to your cluster using Flux. You'll set up a sample manifest project, configure it to
|
||||||
store manifests as an artifact in the project's Container Registry, and configure Flux to sync the artifact.
|
store manifests as an artifact in the project's Container Registry, and configure Flux to sync the artifact. With this
|
||||||
|
setup, you can run additional steps in GitLab pipelines before Flux picks up the changes
|
||||||
|
from the OCI image.
|
||||||
|
|
||||||
This tutorial deploys an application from a public project. If you want to add a non-public project, you should create a [project deploy token](../../../project/deploy_tokens/index.md).
|
This tutorial deploys an application from a public project. If you want to add a non-public project, you should create a [project deploy token](../../../project/deploy_tokens/index.md).
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@
|
|||||||
"clipboard": "^2.0.8",
|
"clipboard": "^2.0.8",
|
||||||
"compression-webpack-plugin": "^5.0.2",
|
"compression-webpack-plugin": "^5.0.2",
|
||||||
"copy-webpack-plugin": "^6.4.1",
|
"copy-webpack-plugin": "^6.4.1",
|
||||||
"core-js": "^3.31.1",
|
"core-js": "^3.32.0",
|
||||||
"cron-validator": "^1.1.1",
|
"cron-validator": "^1.1.1",
|
||||||
"cronstrue": "^1.122.0",
|
"cronstrue": "^1.122.0",
|
||||||
"cropper": "^2.3.0",
|
"cropper": "^2.3.0",
|
||||||
@ -202,8 +202,8 @@
|
|||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"traverse": "^0.6.7",
|
"traverse": "^0.6.7",
|
||||||
"unified": "^10.1.2",
|
"unified": "^10.1.2",
|
||||||
"unist-builder": "^3.0.1",
|
"unist-builder": "^4.0.0",
|
||||||
"unist-util-visit-parents": "^5.1.3",
|
"unist-util-visit-parents": "5.1.3",
|
||||||
"url-loader": "^4.1.1",
|
"url-loader": "^4.1.1",
|
||||||
"uuid": "8.1.0",
|
"uuid": "8.1.0",
|
||||||
"visibilityjs": "^1.2.4",
|
"visibilityjs": "^1.2.4",
|
||||||
|
@ -2,14 +2,17 @@ import { shallowMount } from '@vue/test-utils';
|
|||||||
import Vue, { nextTick } from 'vue';
|
import Vue, { nextTick } from 'vue';
|
||||||
import VueApollo from 'vue-apollo';
|
import VueApollo from 'vue-apollo';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
|
import VueRouter from 'vue-router';
|
||||||
import * as Sentry from '@sentry/browser';
|
import * as Sentry from '@sentry/browser';
|
||||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||||
|
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||||
|
import { TEST_HOST } from 'helpers/test_constants';
|
||||||
import waitForPromises from 'helpers/wait_for_promises';
|
import waitForPromises from 'helpers/wait_for_promises';
|
||||||
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
|
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
|
||||||
import { issuableListTabs } from '~/vue_shared/issuable/list/constants';
|
import { issuableListTabs } from '~/vue_shared/issuable/list/constants';
|
||||||
import { TYPENAME_USER } from '~/graphql_shared/constants';
|
import { TYPENAME_USER } from '~/graphql_shared/constants';
|
||||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||||
import { STATUS_CLOSED, STATUS_OPEN } from '~/service_desk/constants';
|
import { STATUS_CLOSED, STATUS_OPEN, STATUS_ALL } from '~/service_desk/constants';
|
||||||
import getServiceDeskIssuesQuery from 'ee_else_ce/service_desk/queries/get_service_desk_issues.query.graphql';
|
import getServiceDeskIssuesQuery from 'ee_else_ce/service_desk/queries/get_service_desk_issues.query.graphql';
|
||||||
import getServiceDeskIssuesCountsQuery from 'ee_else_ce/service_desk/queries/get_service_desk_issues_counts.query.graphql';
|
import getServiceDeskIssuesCountsQuery from 'ee_else_ce/service_desk/queries/get_service_desk_issues_counts.query.graphql';
|
||||||
import ServiceDeskListApp from '~/service_desk/components/service_desk_list_app.vue';
|
import ServiceDeskListApp from '~/service_desk/components/service_desk_list_app.vue';
|
||||||
@ -27,14 +30,19 @@ import {
|
|||||||
import {
|
import {
|
||||||
getServiceDeskIssuesQueryResponse,
|
getServiceDeskIssuesQueryResponse,
|
||||||
getServiceDeskIssuesCountsQueryResponse,
|
getServiceDeskIssuesCountsQueryResponse,
|
||||||
|
filteredTokens,
|
||||||
|
urlParams,
|
||||||
|
locationSearch,
|
||||||
} from '../mock_data';
|
} from '../mock_data';
|
||||||
|
|
||||||
jest.mock('@sentry/browser');
|
jest.mock('@sentry/browser');
|
||||||
|
|
||||||
describe('ServiceDeskListApp', () => {
|
describe('CE ServiceDeskListApp', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
let router;
|
||||||
|
|
||||||
Vue.use(VueApollo);
|
Vue.use(VueApollo);
|
||||||
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
const defaultProvide = {
|
const defaultProvide = {
|
||||||
releasesPath: 'releases/path',
|
releasesPath: 'releases/path',
|
||||||
@ -49,6 +57,7 @@ describe('ServiceDeskListApp', () => {
|
|||||||
fullPath: 'path/to/project',
|
fullPath: 'path/to/project',
|
||||||
isServiceDeskSupported: true,
|
isServiceDeskSupported: true,
|
||||||
hasAnyIssues: true,
|
hasAnyIssues: true,
|
||||||
|
initialSort: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
let defaultQueryResponse = getServiceDeskIssuesQueryResponse;
|
let defaultQueryResponse = getServiceDeskIssuesQueryResponse;
|
||||||
@ -82,6 +91,8 @@ describe('ServiceDeskListApp', () => {
|
|||||||
[getServiceDeskIssuesCountsQuery, serviceDeskIssuesCountsQueryResponseHandler],
|
[getServiceDeskIssuesCountsQuery, serviceDeskIssuesCountsQueryResponseHandler],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
router = new VueRouter({ mode: 'history' });
|
||||||
|
|
||||||
return shallowMount(ServiceDeskListApp, {
|
return shallowMount(ServiceDeskListApp, {
|
||||||
apolloProvider: createMockApollo(
|
apolloProvider: createMockApollo(
|
||||||
requestHandlers,
|
requestHandlers,
|
||||||
@ -98,6 +109,7 @@ describe('ServiceDeskListApp', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
router,
|
||||||
provide: {
|
provide: {
|
||||||
...defaultProvide,
|
...defaultProvide,
|
||||||
...provide,
|
...provide,
|
||||||
@ -106,6 +118,7 @@ describe('ServiceDeskListApp', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
setWindowLocation(TEST_HOST);
|
||||||
wrapper = createComponent();
|
wrapper = createComponent();
|
||||||
return waitForPromises();
|
return waitForPromises();
|
||||||
});
|
});
|
||||||
@ -113,7 +126,7 @@ describe('ServiceDeskListApp', () => {
|
|||||||
it('fetches service desk issues and renders them in the issuable list', () => {
|
it('fetches service desk issues and renders them in the issuable list', () => {
|
||||||
expect(findIssuableList().props()).toMatchObject({
|
expect(findIssuableList().props()).toMatchObject({
|
||||||
namespace: 'service-desk',
|
namespace: 'service-desk',
|
||||||
recentSearchesStorageKey: 'issues',
|
recentSearchesStorageKey: 'service-desk-issues',
|
||||||
issuables: defaultQueryResponse.data.project.issues.nodes,
|
issuables: defaultQueryResponse.data.project.issues.nodes,
|
||||||
tabs: issuableListTabs,
|
tabs: issuableListTabs,
|
||||||
currentTab: STATUS_OPEN,
|
currentTab: STATUS_OPEN,
|
||||||
@ -145,6 +158,36 @@ describe('ServiceDeskListApp', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Initial url params', () => {
|
||||||
|
describe('search', () => {
|
||||||
|
it('is set from the url params', () => {
|
||||||
|
setWindowLocation(locationSearch);
|
||||||
|
wrapper = createComponent();
|
||||||
|
|
||||||
|
expect(router.history.current.query).toMatchObject({ search: 'find issues' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('state', () => {
|
||||||
|
it('is set from the url params', () => {
|
||||||
|
const initialState = STATUS_ALL;
|
||||||
|
setWindowLocation(`?state=${initialState}`);
|
||||||
|
wrapper = createComponent();
|
||||||
|
|
||||||
|
expect(findIssuableList().props('currentTab')).toBe(initialState);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('filter tokens', () => {
|
||||||
|
it('are set from the url params', () => {
|
||||||
|
setWindowLocation(locationSearch);
|
||||||
|
wrapper = createComponent();
|
||||||
|
|
||||||
|
expect(findIssuableList().props('initialFilterValue')).toEqual(filteredTokens);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Tokens', () => {
|
describe('Tokens', () => {
|
||||||
const mockCurrentUser = {
|
const mockCurrentUser = {
|
||||||
id: 1,
|
id: 1,
|
||||||
@ -200,14 +243,36 @@ describe('ServiceDeskListApp', () => {
|
|||||||
|
|
||||||
describe('Events', () => {
|
describe('Events', () => {
|
||||||
describe('when "click-tab" event is emitted by IssuableList', () => {
|
describe('when "click-tab" event is emitted by IssuableList', () => {
|
||||||
it('updates ui to the new tab', async () => {
|
beforeEach(() => {
|
||||||
createComponent();
|
wrapper = createComponent();
|
||||||
|
router.push = jest.fn();
|
||||||
|
|
||||||
findIssuableList().vm.$emit('click-tab', STATUS_CLOSED);
|
findIssuableList().vm.$emit('click-tab', STATUS_CLOSED);
|
||||||
|
});
|
||||||
|
|
||||||
await nextTick();
|
it('updates ui to the new tab', () => {
|
||||||
expect(findIssuableList().props('currentTab')).toBe(STATUS_CLOSED);
|
expect(findIssuableList().props('currentTab')).toBe(STATUS_CLOSED);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('updates url to the new tab', () => {
|
||||||
|
expect(router.push).toHaveBeenCalledWith({
|
||||||
|
query: expect.objectContaining({ state: STATUS_CLOSED }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when "filter" event is emitted by IssuableList', () => {
|
||||||
|
it('updates IssuableList with url params', async () => {
|
||||||
|
wrapper = createComponent();
|
||||||
|
router.push = jest.fn();
|
||||||
|
|
||||||
|
findIssuableList().vm.$emit('filter', filteredTokens);
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
expect(router.push).toHaveBeenCalledWith({
|
||||||
|
query: expect.objectContaining(urlParams),
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,3 +1,20 @@
|
|||||||
|
import {
|
||||||
|
FILTERED_SEARCH_TERM,
|
||||||
|
OPERATOR_IS,
|
||||||
|
OPERATOR_NOT,
|
||||||
|
OPERATOR_OR,
|
||||||
|
TOKEN_TYPE_ASSIGNEE,
|
||||||
|
TOKEN_TYPE_CONFIDENTIAL,
|
||||||
|
TOKEN_TYPE_EPIC,
|
||||||
|
TOKEN_TYPE_ITERATION,
|
||||||
|
TOKEN_TYPE_LABEL,
|
||||||
|
TOKEN_TYPE_MILESTONE,
|
||||||
|
TOKEN_TYPE_MY_REACTION,
|
||||||
|
TOKEN_TYPE_RELEASE,
|
||||||
|
TOKEN_TYPE_WEIGHT,
|
||||||
|
TOKEN_TYPE_HEALTH,
|
||||||
|
} from '~/vue_shared/components/filtered_search_bar/constants';
|
||||||
|
|
||||||
export const getServiceDeskIssuesQueryResponse = {
|
export const getServiceDeskIssuesQueryResponse = {
|
||||||
data: {
|
data: {
|
||||||
project: {
|
project: {
|
||||||
@ -116,3 +133,104 @@ export const getServiceDeskIssuesCountsQueryResponse = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const filteredTokens = [
|
||||||
|
{ type: FILTERED_SEARCH_TERM, value: { data: 'find issues', operator: 'undefined' } },
|
||||||
|
{ type: TOKEN_TYPE_ASSIGNEE, value: { data: 'bart', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_ASSIGNEE, value: { data: 'lisa', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_ASSIGNEE, value: { data: '5', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_ASSIGNEE, value: { data: 'patty', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_ASSIGNEE, value: { data: 'selma', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_ASSIGNEE, value: { data: 'carl', operator: OPERATOR_OR } },
|
||||||
|
{ type: TOKEN_TYPE_ASSIGNEE, value: { data: 'lenny', operator: OPERATOR_OR } },
|
||||||
|
{ type: TOKEN_TYPE_MILESTONE, value: { data: 'season 3', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_MILESTONE, value: { data: 'season 4', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_MILESTONE, value: { data: 'season 20', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_MILESTONE, value: { data: 'season 30', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_LABEL, value: { data: 'cartoon', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_LABEL, value: { data: 'tv', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_LABEL, value: { data: 'live action', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_LABEL, value: { data: 'drama', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_LABEL, value: { data: 'comedy', operator: OPERATOR_OR } },
|
||||||
|
{ type: TOKEN_TYPE_LABEL, value: { data: 'sitcom', operator: OPERATOR_OR } },
|
||||||
|
{ type: TOKEN_TYPE_RELEASE, value: { data: 'v3', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_RELEASE, value: { data: 'v4', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_RELEASE, value: { data: 'v20', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_RELEASE, value: { data: 'v30', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_MY_REACTION, value: { data: 'thumbsup', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_MY_REACTION, value: { data: 'thumbsdown', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_CONFIDENTIAL, value: { data: 'yes', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_ITERATION, value: { data: '4', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_ITERATION, value: { data: '12', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_ITERATION, value: { data: '20', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_ITERATION, value: { data: '42', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_EPIC, value: { data: '12', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_EPIC, value: { data: '34', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_WEIGHT, value: { data: '1', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_WEIGHT, value: { data: '3', operator: OPERATOR_NOT } },
|
||||||
|
{ type: TOKEN_TYPE_HEALTH, value: { data: 'atRisk', operator: OPERATOR_IS } },
|
||||||
|
{ type: TOKEN_TYPE_HEALTH, value: { data: 'onTrack', operator: OPERATOR_NOT } },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const urlParams = {
|
||||||
|
search: 'find issues',
|
||||||
|
'assignee_username[]': ['bart', 'lisa', '5'],
|
||||||
|
'not[assignee_username][]': ['patty', 'selma'],
|
||||||
|
'or[assignee_username][]': ['carl', 'lenny'],
|
||||||
|
milestone_title: ['season 3', 'season 4'],
|
||||||
|
'not[milestone_title]': ['season 20', 'season 30'],
|
||||||
|
'label_name[]': ['cartoon', 'tv'],
|
||||||
|
'not[label_name][]': ['live action', 'drama'],
|
||||||
|
'or[label_name][]': ['comedy', 'sitcom'],
|
||||||
|
release_tag: ['v3', 'v4'],
|
||||||
|
'not[release_tag]': ['v20', 'v30'],
|
||||||
|
my_reaction_emoji: 'thumbsup',
|
||||||
|
'not[my_reaction_emoji]': 'thumbsdown',
|
||||||
|
confidential: 'yes',
|
||||||
|
iteration_id: ['4', '12'],
|
||||||
|
'not[iteration_id]': ['20', '42'],
|
||||||
|
epic_id: '12',
|
||||||
|
'not[epic_id]': '34',
|
||||||
|
weight: '1',
|
||||||
|
'not[weight]': '3',
|
||||||
|
health_status: 'atRisk',
|
||||||
|
'not[health_status]': 'onTrack',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const locationSearch = [
|
||||||
|
'?search=find+issues',
|
||||||
|
'assignee_username[]=bart',
|
||||||
|
'assignee_username[]=lisa',
|
||||||
|
'assignee_username[]=5',
|
||||||
|
'not[assignee_username][]=patty',
|
||||||
|
'not[assignee_username][]=selma',
|
||||||
|
'or[assignee_username][]=carl',
|
||||||
|
'or[assignee_username][]=lenny',
|
||||||
|
'milestone_title=season+3',
|
||||||
|
'milestone_title=season+4',
|
||||||
|
'not[milestone_title]=season+20',
|
||||||
|
'not[milestone_title]=season+30',
|
||||||
|
'label_name[]=cartoon',
|
||||||
|
'label_name[]=tv',
|
||||||
|
'not[label_name][]=live action',
|
||||||
|
'not[label_name][]=drama',
|
||||||
|
'or[label_name][]=comedy',
|
||||||
|
'or[label_name][]=sitcom',
|
||||||
|
'release_tag=v3',
|
||||||
|
'release_tag=v4',
|
||||||
|
'not[release_tag]=v20',
|
||||||
|
'not[release_tag]=v30',
|
||||||
|
'my_reaction_emoji=thumbsup',
|
||||||
|
'not[my_reaction_emoji]=thumbsdown',
|
||||||
|
'confidential=yes',
|
||||||
|
'iteration_id=4',
|
||||||
|
'iteration_id=12',
|
||||||
|
'not[iteration_id]=20',
|
||||||
|
'not[iteration_id]=42',
|
||||||
|
'epic_id=12',
|
||||||
|
'not[epic_id]=34',
|
||||||
|
'weight=1',
|
||||||
|
'not[weight]=3',
|
||||||
|
'health_status=atRisk',
|
||||||
|
'not[health_status]=onTrack',
|
||||||
|
].join('&');
|
||||||
|
@ -23,10 +23,12 @@ RSpec.describe Tooling::Danger::ModelValidations, feature_category: :tooling do
|
|||||||
describe '#add_comment_for_added_validations' do
|
describe '#add_comment_for_added_validations' do
|
||||||
let(:file_lines) { file_diff.map { |line| line.delete_prefix('+').delete_prefix('-') } }
|
let(:file_lines) { file_diff.map { |line| line.delete_prefix('+').delete_prefix('-') } }
|
||||||
let(:filename) { 'app/models/user.rb' }
|
let(:filename) { 'app/models/user.rb' }
|
||||||
|
let(:added_filename) { 'app/models/user.rb' }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(model_validations.project_helper).to receive(:file_lines).and_return(file_lines)
|
allow(model_validations.project_helper).to receive(:file_lines).and_return(file_lines)
|
||||||
allow(model_validations.helper).to receive(:all_changed_files).and_return([filename])
|
allow(model_validations.helper).to receive(:added_files).and_return([added_filename])
|
||||||
|
allow(model_validations.helper).to receive(:modified_files).and_return([filename])
|
||||||
allow(model_validations.helper).to receive(:changed_lines).with(filename).and_return(file_diff)
|
allow(model_validations.helper).to receive(:changed_lines).with(filename).and_return(file_diff)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -83,11 +85,13 @@ RSpec.describe Tooling::Danger::ModelValidations, feature_category: :tooling do
|
|||||||
app/models/users/user_follow_user.rb
|
app/models/users/user_follow_user.rb
|
||||||
ee/app/models/ee/user.rb
|
ee/app/models/ee/user.rb
|
||||||
ee/app/models/sca/license_policy.rb
|
ee/app/models/sca/license_policy.rb
|
||||||
|
app/models/concerns/presentable.rb
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
all_new_files = %w[
|
added_files = %w[app/models/user_preferences.rb app/models/concerns/presentable.rb]
|
||||||
|
modified_files = %w[
|
||||||
app/models/user.rb
|
app/models/user.rb
|
||||||
app/models/users/user_follow_user.rb
|
app/models/users/user_follow_user.rb
|
||||||
ee/app/models/ee/user.rb
|
ee/app/models/ee/user.rb
|
||||||
@ -96,7 +100,8 @@ RSpec.describe Tooling::Danger::ModelValidations, feature_category: :tooling do
|
|||||||
app/assets/index.js
|
app/assets/index.js
|
||||||
]
|
]
|
||||||
|
|
||||||
allow(model_validations.helper).to receive(:all_changed_files).and_return(all_new_files)
|
allow(model_validations.helper).to receive(:added_files).and_return(added_files)
|
||||||
|
allow(model_validations.helper).to receive(:modified_files).and_return(modified_files)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns added and modified files' do
|
it 'returns added and modified files' do
|
||||||
|
@ -7,8 +7,11 @@ module Tooling
|
|||||||
module ModelValidations
|
module ModelValidations
|
||||||
include ::Tooling::Danger::Suggestor
|
include ::Tooling::Danger::Suggestor
|
||||||
|
|
||||||
MODEL_FILES_REGEX = 'app/models'
|
MODEL_FILES_PATH = 'app/models'
|
||||||
|
MODEL_CONCERN_FILES_PATH = 'app/models/concerns'
|
||||||
EE_PREFIX = 'ee/'
|
EE_PREFIX = 'ee/'
|
||||||
|
MODEL_FILES_REGEX = %r{\A(#{EE_PREFIX})?#{MODEL_FILES_PATH}}
|
||||||
|
MODEL_CONCERN_FILES_REGEX = %r{\A(#{EE_PREFIX})?#{MODEL_CONCERN_FILES_PATH}}
|
||||||
VALIDATION_METHODS = %w[validates validate validates_each validates_with validates_associated].freeze
|
VALIDATION_METHODS = %w[validates validate validates_each validates_with validates_associated].freeze
|
||||||
VALIDATIONS_REGEX = /^\+\s*(.*\.)?(#{VALIDATION_METHODS.join('|')})[( ]/
|
VALIDATIONS_REGEX = /^\+\s*(.*\.)?(#{VALIDATION_METHODS.join('|')})[( ]/
|
||||||
|
|
||||||
@ -32,10 +35,10 @@ module Tooling
|
|||||||
end
|
end
|
||||||
|
|
||||||
def changed_model_files
|
def changed_model_files
|
||||||
changed_files = helper.all_changed_files
|
added_files = helper.added_files
|
||||||
ee_folder_prefix = "(#{EE_PREFIX})?"
|
modified_files = helper.modified_files
|
||||||
|
|
||||||
changed_files.grep(%r{\A#{ee_folder_prefix}#{MODEL_FILES_REGEX}})
|
modified_files.grep(MODEL_FILES_REGEX) + added_files.grep(MODEL_CONCERN_FILES_REGEX)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
40
yarn.lock
40
yarn.lock
@ -2478,7 +2478,12 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397"
|
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397"
|
||||||
integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==
|
integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==
|
||||||
|
|
||||||
"@types/unist@*", "@types/unist@^2.0.0":
|
"@types/unist@*", "@types/unist@^3.0.0":
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.0.tgz#988ae8af1e5239e89f9fbb1ade4c935f4eeedf9a"
|
||||||
|
integrity sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w==
|
||||||
|
|
||||||
|
"@types/unist@^2.0.0":
|
||||||
version "2.0.6"
|
version "2.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
|
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
|
||||||
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
|
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
|
||||||
@ -4267,10 +4272,10 @@ core-js-pure@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
|
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
|
||||||
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
|
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
|
||||||
|
|
||||||
core-js@^3.29.1, core-js@^3.31.1, core-js@^3.6.5:
|
core-js@^3.29.1, core-js@^3.32.0, core-js@^3.6.5:
|
||||||
version "3.31.1"
|
version "3.32.0"
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.31.1.tgz#f2b0eea9be9da0def2c5fece71064a7e5d687653"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.0.tgz#7643d353d899747ab1f8b03d2803b0312a0fb3b6"
|
||||||
integrity sha512-2sKLtfq1eFST7l7v62zaqXacPc7uG8ZAya8ogijLhTtaKNcpzpB4TMoTw2Si+8GYKRwFPMMtUT0263QFWFfqyQ==
|
integrity sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww==
|
||||||
|
|
||||||
core-util-is@~1.0.0:
|
core-util-is@~1.0.0:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
@ -12554,13 +12559,20 @@ unique-slug@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
imurmurhash "^0.1.4"
|
imurmurhash "^0.1.4"
|
||||||
|
|
||||||
unist-builder@^3.0.0, unist-builder@^3.0.1:
|
unist-builder@^3.0.0:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-3.0.1.tgz#258b89dcadd3c973656b2327b347863556907f58"
|
resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-3.0.1.tgz#258b89dcadd3c973656b2327b347863556907f58"
|
||||||
integrity sha512-gnpOw7DIpCA0vpr6NqdPvTWnlPTApCTRzr+38E6hCWx3rz/cjo83SsKIlS1Z+L5ttScQ2AwutNnb8+tAvpb6qQ==
|
integrity sha512-gnpOw7DIpCA0vpr6NqdPvTWnlPTApCTRzr+38E6hCWx3rz/cjo83SsKIlS1Z+L5ttScQ2AwutNnb8+tAvpb6qQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/unist" "^2.0.0"
|
"@types/unist" "^2.0.0"
|
||||||
|
|
||||||
|
unist-builder@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-4.0.0.tgz#817b326c015a6f9f5e92bb55b8e8bc5e578fe243"
|
||||||
|
integrity sha512-wmRFnH+BLpZnTKpc5L7O67Kac89s9HMrtELpnNaE6TAobq5DTZZs5YaTQfAZBA9bFPECx2uVAPO31c+GVug8mg==
|
||||||
|
dependencies:
|
||||||
|
"@types/unist" "^3.0.0"
|
||||||
|
|
||||||
unist-util-generated@^2.0.0:
|
unist-util-generated@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-2.0.0.tgz#86fafb77eb6ce9bfa6b663c3f5ad4f8e56a60113"
|
resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-2.0.0.tgz#86fafb77eb6ce9bfa6b663c3f5ad4f8e56a60113"
|
||||||
@ -12585,18 +12597,18 @@ unist-util-stringify-position@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/unist" "^2.0.0"
|
"@types/unist" "^2.0.0"
|
||||||
|
|
||||||
unist-util-visit-parents@^4.0.0:
|
unist-util-visit-parents@5.1.3, unist-util-visit-parents@^5.0.0:
|
||||||
version "4.1.1"
|
version "5.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-4.1.1.tgz#e83559a4ad7e6048a46b1bdb22614f2f3f4724f2"
|
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz#b4520811b0ca34285633785045df7a8d6776cfeb"
|
||||||
integrity sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw==
|
integrity sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/unist" "^2.0.0"
|
"@types/unist" "^2.0.0"
|
||||||
unist-util-is "^5.0.0"
|
unist-util-is "^5.0.0"
|
||||||
|
|
||||||
unist-util-visit-parents@^5.0.0, unist-util-visit-parents@^5.1.3:
|
unist-util-visit-parents@^4.0.0:
|
||||||
version "5.1.3"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz#b4520811b0ca34285633785045df7a8d6776cfeb"
|
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-4.1.1.tgz#e83559a4ad7e6048a46b1bdb22614f2f3f4724f2"
|
||||||
integrity sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==
|
integrity sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/unist" "^2.0.0"
|
"@types/unist" "^2.0.0"
|
||||||
unist-util-is "^5.0.0"
|
unist-util-is "^5.0.0"
|
||||||
|
Reference in New Issue
Block a user