Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
41482e5dce
commit
2896c7471a
|
@ -12,11 +12,9 @@ Style/OpenStructUse:
|
|||
- lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
|
||||
- lib/gitlab/testing/request_inspector_middleware.rb
|
||||
- lib/mattermost/session.rb
|
||||
- spec/controllers/groups/clusters_controller_spec.rb
|
||||
- spec/controllers/projects/clusters_controller_spec.rb
|
||||
- spec/factories/go_module_versions.rb
|
||||
- spec/factories/wiki_pages.rb
|
||||
- spec/features/projects/clusters_spec.rb
|
||||
- spec/graphql/mutations/branches/create_spec.rb
|
||||
- spec/graphql/mutations/clusters/agent_tokens/create_spec.rb
|
||||
- spec/graphql/mutations/clusters/agents/create_spec.rb
|
||||
|
|
|
@ -22,6 +22,7 @@ query accessTokensGetProjects(
|
|||
avatarUrl
|
||||
}
|
||||
pageInfo {
|
||||
__typename
|
||||
...PageInfo
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ApolloLink, Observable } from 'apollo-link';
|
||||
import { ApolloLink, Observable } from '@apollo/client/core';
|
||||
import { print } from 'graphql';
|
||||
import cable from '~/actioncable_consumer';
|
||||
import { uuids } from '~/lib/utils/uuids';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
|
||||
import { defaultDataIdFromObject } from '@apollo/client/core';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import produce from 'immer';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import introspectionQueryResultData from './graphql/fragmentTypes.json';
|
||||
import getCurrentIntegrationQuery from './graphql/queries/get_current_integration.query.graphql';
|
||||
|
||||
const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
});
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const resolvers = {
|
||||
|
@ -55,9 +49,5 @@ const resolvers = {
|
|||
};
|
||||
|
||||
export default new VueApollo({
|
||||
defaultClient: createDefaultClient(resolvers, {
|
||||
cacheConfig: {
|
||||
fragmentMatcher,
|
||||
},
|
||||
}),
|
||||
defaultClient: createDefaultClient(resolvers),
|
||||
});
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"__schema":{"types":[{"kind":"UNION","name":"AlertManagementIntegration","possibleTypes":[{"name":"AlertManagementHttpIntegration"},{"name":"AlertManagementPrometheusIntegration"}]}]}}
|
|
@ -5,6 +5,7 @@ query getIntegrations($projectPath: ID!) {
|
|||
id
|
||||
alertManagementIntegrations {
|
||||
nodes {
|
||||
__typename
|
||||
...IntegrationItem
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
fragment Count on UsageTrendsMeasurement {
|
||||
__typename
|
||||
count
|
||||
recordedAt
|
||||
}
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
import { IntrospectionFragmentMatcher, defaultDataIdFromObject } from 'apollo-cache-inmemory';
|
||||
import { defaultDataIdFromObject } from '@apollo/client/core';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import introspectionQueryResultData from '~/sidebar/fragmentTypes.json';
|
||||
|
||||
const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
});
|
||||
|
||||
export const gqlClient = createDefaultClient(
|
||||
{},
|
||||
|
@ -14,8 +9,6 @@ export const gqlClient = createDefaultClient(
|
|||
// eslint-disable-next-line no-underscore-dangle
|
||||
return object.__typename === 'BoardList' ? object.iid : defaultDataIdFromObject(object);
|
||||
},
|
||||
|
||||
fragmentMatcher,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
|
@ -1,27 +1,14 @@
|
|||
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import BoardsSelector from 'ee_else_ce/boards/components/boards_selector.vue';
|
||||
import store from '~/boards/stores';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import introspectionQueryResultData from '~/sidebar/fragmentTypes.json';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
});
|
||||
|
||||
const apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(
|
||||
{},
|
||||
{
|
||||
cacheConfig: {
|
||||
fragmentMatcher,
|
||||
},
|
||||
},
|
||||
),
|
||||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
|
||||
export default (params = {}) => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ApolloLink, Observable } from 'apollo-link';
|
||||
import { ApolloLink, Observable } from '@apollo/client/core';
|
||||
|
||||
export const apolloCaptchaLink = new ApolloLink((operation, forward) =>
|
||||
forward(operation).flatMap((result) => {
|
||||
|
|
|
@ -1,25 +1,10 @@
|
|||
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import { vulnerabilityLocationTypes } from '~/graphql_shared/fragment_types/vulnerability_location_types';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
// We create a fragment matcher so that we can create a fragment from an interface
|
||||
// Without this, Apollo throws a heuristic fragment matcher warning
|
||||
const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData: vulnerabilityLocationTypes,
|
||||
});
|
||||
|
||||
const defaultClient = createDefaultClient(
|
||||
{},
|
||||
{
|
||||
cacheConfig: {
|
||||
fragmentMatcher,
|
||||
},
|
||||
},
|
||||
);
|
||||
const defaultClient = createDefaultClient();
|
||||
|
||||
export default new VueApollo({
|
||||
defaultClient,
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { defaultDataIdFromObject, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import { defaultDataIdFromObject } from '@apollo/client/core';
|
||||
import produce from 'immer';
|
||||
import { uniqueId } from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import introspectionQueryResultData from './graphql/fragmentTypes.json';
|
||||
import activeDiscussionQuery from './graphql/queries/active_discussion.query.graphql';
|
||||
import getDesignQuery from './graphql/queries/get_design.query.graphql';
|
||||
import typeDefs from './graphql/typedefs.graphql';
|
||||
|
@ -13,10 +12,6 @@ import { addPendingTodoToStore } from './utils/cache_update';
|
|||
import { extractTodoIdFromDeletePath, createPendingTodo } from './utils/design_management_utils';
|
||||
import { CREATE_DESIGN_TODO_EXISTS_ERROR } from './utils/error_messages';
|
||||
|
||||
const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
});
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const resolvers = {
|
||||
|
@ -85,7 +80,6 @@ const defaultClient = createDefaultClient(
|
|||
}
|
||||
return defaultDataIdFromObject(object);
|
||||
},
|
||||
fragmentMatcher,
|
||||
},
|
||||
typeDefs,
|
||||
},
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"__schema":{"types":[{"kind":"INTERFACE","name":"User","possibleTypes":[{"name":"UserCore"}]},{"kind":"UNION","name":"NoteableType","possibleTypes":[{"name":"Design"},{"name":"Issue"},{"name":"MergeRequest"}]}]}}
|
|
@ -1,17 +0,0 @@
|
|||
export const vulnerabilityLocationTypes = {
|
||||
__schema: {
|
||||
types: [
|
||||
{
|
||||
kind: 'UNION',
|
||||
name: 'VulnerabilityLocation',
|
||||
possibleTypes: [
|
||||
{ name: 'VulnerabilityLocationContainerScanning' },
|
||||
{ name: 'VulnerabilityLocationDast' },
|
||||
{ name: 'VulnerabilityLocationDependencyScanning' },
|
||||
{ name: 'VulnerabilityLocationSast' },
|
||||
{ name: 'VulnerabilityLocationSecretDetection' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
{"AlertManagementIntegration":["AlertManagementHttpIntegration","AlertManagementPrometheusIntegration"],"CurrentUserTodos":["BoardEpic","Design","Epic","EpicIssue","Issue","MergeRequest"],"DependencyLinkMetadata":["NugetDependencyLinkMetadata"],"DesignFields":["Design","DesignAtVersion"],"Entry":["Blob","Submodule","TreeEntry"],"Eventable":["BoardEpic","Epic"],"Issuable":["Epic","Issue","MergeRequest"],"JobNeedUnion":["CiBuildNeed","CiJob"],"MemberInterface":["GroupMember","ProjectMember"],"NoteableInterface":["AlertManagementAlert","BoardEpic","Design","Epic","EpicIssue","Issue","MergeRequest","Snippet","Vulnerability"],"NoteableType":["Design","Issue","MergeRequest"],"OrchestrationPolicy":["ScanExecutionPolicy","ScanResultPolicy"],"PackageFileMetadata":["ConanFileMetadata","HelmFileMetadata"],"PackageMetadata":["ComposerMetadata","ConanMetadata","MavenMetadata","NugetMetadata","PypiMetadata"],"ResolvableInterface":["Discussion","Note"],"Service":["BaseService","JiraService"],"TimeboxReportInterface":["Iteration","Milestone"],"User":["MergeRequestAssignee","MergeRequestReviewer","UserCore"],"VulnerabilityDetail":["VulnerabilityDetailBase","VulnerabilityDetailBoolean","VulnerabilityDetailCode","VulnerabilityDetailCommit","VulnerabilityDetailDiff","VulnerabilityDetailFileLocation","VulnerabilityDetailInt","VulnerabilityDetailList","VulnerabilityDetailMarkdown","VulnerabilityDetailModuleLocation","VulnerabilityDetailTable","VulnerabilityDetailText","VulnerabilityDetailUrl"],"VulnerabilityLocation":["VulnerabilityLocationClusterImageScanning","VulnerabilityLocationContainerScanning","VulnerabilityLocationCoverageFuzzing","VulnerabilityLocationDast","VulnerabilityLocationDependencyScanning","VulnerabilityLocationGeneric","VulnerabilityLocationSast","VulnerabilityLocationSecretDetection"]}
|
|
@ -181,6 +181,9 @@ export default {
|
|||
return data[this.namespace]?.issues.nodes ?? [];
|
||||
},
|
||||
result({ data }) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
this.pageInfo = data[this.namespace]?.issues.pageInfo ?? {};
|
||||
this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery();
|
||||
},
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
fragment IssueFragment on Issue {
|
||||
__typename
|
||||
id
|
||||
iid
|
||||
closedAt
|
||||
|
@ -18,6 +19,7 @@ fragment IssueFragment on Issue {
|
|||
webUrl
|
||||
assignees {
|
||||
nodes {
|
||||
__typename
|
||||
id
|
||||
avatarUrl
|
||||
name
|
||||
|
@ -26,6 +28,7 @@ fragment IssueFragment on Issue {
|
|||
}
|
||||
}
|
||||
author {
|
||||
__typename
|
||||
id
|
||||
avatarUrl
|
||||
name
|
||||
|
|
|
@ -51,7 +51,9 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
jobs: {},
|
||||
jobs: {
|
||||
list: [],
|
||||
},
|
||||
hasError: false,
|
||||
isAlertDismissed: false,
|
||||
scope: null,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ApolloLink } from 'apollo-link';
|
||||
import { ApolloLink } from '@apollo/client/core';
|
||||
import { memoize } from 'lodash';
|
||||
|
||||
export const FEATURE_CATEGORY_HEADER = 'x-gitlab-feature-category';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Observable } from 'apollo-link';
|
||||
import { onError } from 'apollo-link-error';
|
||||
import { Observable } from '@apollo/client/core';
|
||||
import { onError } from '@apollo/client/link/error';
|
||||
import { isNavigatingAway } from '~/lib/utils/is_navigating_away';
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { InMemoryCache } from 'apollo-cache-inmemory';
|
||||
import { ApolloClient } from 'apollo-client';
|
||||
import { ApolloLink } from 'apollo-link';
|
||||
import { BatchHttpLink } from 'apollo-link-batch-http';
|
||||
import { HttpLink } from 'apollo-link-http';
|
||||
import { ApolloClient, InMemoryCache, ApolloLink, HttpLink } from '@apollo/client/core';
|
||||
import { BatchHttpLink } from '@apollo/client/link/batch-http';
|
||||
import { createUploadLink } from 'apollo-upload-client';
|
||||
import ActionCableLink from '~/actioncable_link';
|
||||
import { apolloCaptchaLink } from '~/captcha/apollo_captcha_link';
|
||||
import possibleTypes from '~/graphql_shared/possibleTypes.json';
|
||||
import { StartupJSLink } from '~/lib/utils/apollo_startup_js_link';
|
||||
import csrf from '~/lib/utils/csrf';
|
||||
import { objectToQuery, queryToObject } from '~/lib/utils/url_utility';
|
||||
|
@ -21,6 +19,33 @@ export const fetchPolicies = {
|
|||
CACHE_ONLY: 'cache-only',
|
||||
};
|
||||
|
||||
export const typePolicies = {
|
||||
Repository: {
|
||||
merge: true,
|
||||
},
|
||||
UserPermissions: {
|
||||
merge: true,
|
||||
},
|
||||
MergeRequestPermissions: {
|
||||
merge: true,
|
||||
},
|
||||
ContainerRepositoryConnection: {
|
||||
merge: true,
|
||||
},
|
||||
TimelogConnection: {
|
||||
merge: true,
|
||||
},
|
||||
BranchList: {
|
||||
merge: true,
|
||||
},
|
||||
InstanceSecurityDashboard: {
|
||||
merge: true,
|
||||
},
|
||||
PipelinePermissions: {
|
||||
merge: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const stripWhitespaceFromQuery = (url, path) => {
|
||||
/* eslint-disable-next-line no-unused-vars */
|
||||
const [_, params] = url.split(path);
|
||||
|
@ -46,6 +71,30 @@ export const stripWhitespaceFromQuery = (url, path) => {
|
|||
return `${path}?${reassembled}`;
|
||||
};
|
||||
|
||||
const acs = [];
|
||||
|
||||
let pendingApolloMutations = 0;
|
||||
|
||||
// ### Why track pendingApolloMutations, but calculate pendingApolloRequests?
|
||||
//
|
||||
// In Apollo 2, we had a single link for counting operations.
|
||||
//
|
||||
// With Apollo 3, the `forward().map(...)` of deduped queries is never called.
|
||||
// So, we resorted to calculating the sum of `inFlightLinkObservables?.size`.
|
||||
// However! Mutations don't use `inFLightLinkObservables`, but since they are likely
|
||||
// not deduped we can count them...
|
||||
//
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55062#note_838943715
|
||||
// https://www.apollographql.com/docs/react/v2/networking/network-layer/#query-deduplication
|
||||
Object.defineProperty(window, 'pendingApolloRequests', {
|
||||
get() {
|
||||
return acs.reduce(
|
||||
(sum, ac) => sum + (ac?.queryManager?.inFlightLinkObservables?.size || 0),
|
||||
pendingApolloMutations,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default (resolvers = {}, config = {}) => {
|
||||
const {
|
||||
baseUrl,
|
||||
|
@ -56,6 +105,7 @@ export default (resolvers = {}, config = {}) => {
|
|||
path = '/api/graphql',
|
||||
useGet = false,
|
||||
} = config;
|
||||
let ac = null;
|
||||
let uri = `${gon.relative_url_root || ''}${path}`;
|
||||
|
||||
if (baseUrl) {
|
||||
|
@ -75,16 +125,6 @@ export default (resolvers = {}, config = {}) => {
|
|||
batchMax,
|
||||
};
|
||||
|
||||
const requestCounterLink = new ApolloLink((operation, forward) => {
|
||||
window.pendingApolloRequests = window.pendingApolloRequests || 0;
|
||||
window.pendingApolloRequests += 1;
|
||||
|
||||
return forward(operation).map((response) => {
|
||||
window.pendingApolloRequests -= 1;
|
||||
return response;
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
This custom fetcher intervention is to deal with an issue where we are using GET to access
|
||||
eTag polling, but Apollo Client adds excessive whitespace, which causes the
|
||||
|
@ -138,6 +178,22 @@ export default (resolvers = {}, config = {}) => {
|
|||
);
|
||||
};
|
||||
|
||||
const hasMutation = (operation) =>
|
||||
(operation?.query?.definitions || []).some((x) => x.operation === 'mutation');
|
||||
|
||||
const requestCounterLink = new ApolloLink((operation, forward) => {
|
||||
if (hasMutation(operation)) {
|
||||
pendingApolloMutations += 1;
|
||||
}
|
||||
|
||||
return forward(operation).map((response) => {
|
||||
if (hasMutation(operation)) {
|
||||
pendingApolloMutations -= 1;
|
||||
}
|
||||
return response;
|
||||
});
|
||||
});
|
||||
|
||||
const appLink = ApolloLink.split(
|
||||
hasSubscriptionOperation,
|
||||
new ActionCableLink(),
|
||||
|
@ -155,19 +211,23 @@ export default (resolvers = {}, config = {}) => {
|
|||
),
|
||||
);
|
||||
|
||||
return new ApolloClient({
|
||||
ac = new ApolloClient({
|
||||
typeDefs,
|
||||
link: appLink,
|
||||
cache: new InMemoryCache({
|
||||
typePolicies,
|
||||
possibleTypes,
|
||||
...cacheConfig,
|
||||
freezeResults: true,
|
||||
}),
|
||||
resolvers,
|
||||
assumeImmutableResults: true,
|
||||
defaultOptions: {
|
||||
query: {
|
||||
fetchPolicy,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
acs.push(ac);
|
||||
|
||||
return ac;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ApolloLink, Observable } from 'apollo-link';
|
||||
import { ApolloLink, Observable } from '@apollo/client/core';
|
||||
import { parse } from 'graphql';
|
||||
import { isEqual, pickBy } from 'lodash';
|
||||
|
||||
|
|
|
@ -96,6 +96,9 @@ export default {
|
|||
return data[this.graphqlResource]?.containerRepositories.nodes;
|
||||
},
|
||||
result({ data }) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
this.pageInfo = data[this.graphqlResource]?.containerRepositories?.pageInfo;
|
||||
this.containerRepositoriesCount = data[this.graphqlResource]?.containerRepositoriesCount;
|
||||
},
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"__schema": {
|
||||
"types": [
|
||||
{
|
||||
"kind": "UNION",
|
||||
"name": "PackageMetadata",
|
||||
"possibleTypes": [
|
||||
{ "name": "ComposerMetadata" },
|
||||
{ "name": "ConanMetadata" },
|
||||
{ "name": "MavenMetadata" },
|
||||
{ "name": "NugetMetadata" },
|
||||
{ "name": "PypiMetadata" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,22 +1,9 @@
|
|||
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import introspectionQueryResultData from './fragmentTypes.json';
|
||||
|
||||
const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
});
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
export const apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(
|
||||
{},
|
||||
{
|
||||
cacheConfig: {
|
||||
fragmentMatcher,
|
||||
},
|
||||
},
|
||||
),
|
||||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
import { GlProgressBar, GlSprintf, GlAlert } from '@gitlab/ui';
|
||||
import eventHub from '~/invite_members/event_hub';
|
||||
import { s__ } from '~/locale';
|
||||
import { ACTION_LABELS, ACTION_SECTIONS } from '../constants';
|
||||
import { getCookie, removeCookie, parseBoolean } from '~/lib/utils/common_utils';
|
||||
import { ACTION_LABELS, ACTION_SECTIONS, INVITE_MODAL_OPEN_COOKIE } from '../constants';
|
||||
import LearnGitlabSectionCard from './learn_gitlab_section_card.vue';
|
||||
|
||||
export default {
|
||||
|
@ -26,7 +27,7 @@ export default {
|
|||
required: true,
|
||||
type: Object,
|
||||
},
|
||||
inviteMembersOpen: {
|
||||
inviteMembers: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
|
@ -53,7 +54,7 @@ export default {
|
|||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.inviteMembersOpen) {
|
||||
if (this.inviteMembers && this.getCookieForInviteMembers()) {
|
||||
this.openInviteMembersModal('celebrate');
|
||||
}
|
||||
|
||||
|
@ -63,6 +64,13 @@ export default {
|
|||
eventHub.$off('showSuccessfulInvitationsAlert', this.handleShowSuccessfulInvitationsAlert);
|
||||
},
|
||||
methods: {
|
||||
getCookieForInviteMembers() {
|
||||
const value = parseBoolean(getCookie(INVITE_MODAL_OPEN_COOKIE));
|
||||
|
||||
removeCookie(INVITE_MODAL_OPEN_COOKIE);
|
||||
|
||||
return value;
|
||||
},
|
||||
openInviteMembersModal(mode) {
|
||||
eventHub.$emit('openModal', { mode, inviteeType: 'members', source: 'learn-gitlab' });
|
||||
},
|
||||
|
|
|
@ -95,3 +95,5 @@ export const ACTION_SECTIONS = {
|
|||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const INVITE_MODAL_OPEN_COOKIE = 'confetti_post_signup';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Vue from 'vue';
|
||||
import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils';
|
||||
import LearnGitlab from '../components/learn_gitlab.vue';
|
||||
|
||||
function initLearnGitlab() {
|
||||
|
@ -13,13 +13,13 @@ function initLearnGitlab() {
|
|||
const actions = convertObjectPropsToCamelCase(JSON.parse(el.dataset.actions));
|
||||
const sections = convertObjectPropsToCamelCase(JSON.parse(el.dataset.sections));
|
||||
const project = convertObjectPropsToCamelCase(JSON.parse(el.dataset.project));
|
||||
const { inviteMembersOpen } = el.dataset;
|
||||
const { inviteMembers } = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
render(createElement) {
|
||||
return createElement(LearnGitlab, {
|
||||
props: { actions, sections, project, inviteMembersOpen },
|
||||
props: { actions, sections, project, inviteMembers: parseBoolean(inviteMembers) },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
@ -49,7 +49,7 @@ export default {
|
|||
pipelineEtag: {
|
||||
query: getPipelineEtag,
|
||||
update(data) {
|
||||
return data.etags.pipeline;
|
||||
return data.etags?.pipeline;
|
||||
},
|
||||
},
|
||||
pipeline: {
|
||||
|
|
|
@ -196,7 +196,7 @@ export default {
|
|||
currentBranch: {
|
||||
query: getCurrentBranch,
|
||||
update(data) {
|
||||
return data.workBranches.current.name;
|
||||
return data.workBranches?.current?.name;
|
||||
},
|
||||
},
|
||||
starterTemplate: {
|
||||
|
@ -214,7 +214,7 @@ export default {
|
|||
return data.project?.ciTemplate?.content || '';
|
||||
},
|
||||
result({ data }) {
|
||||
this.updateCiConfig(data.project?.ciTemplate?.content || '');
|
||||
this.updateCiConfig(data?.project?.ciTemplate?.content || '');
|
||||
},
|
||||
error() {
|
||||
this.reportFailure(LOAD_FAILURE_UNKNOWN);
|
||||
|
|
|
@ -36,6 +36,9 @@ export default {
|
|||
return data.project?.pipeline?.jobs?.nodes || [];
|
||||
},
|
||||
result({ data }) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
this.jobsPageInfo = data.project?.pipeline?.jobs?.pageInfo || {};
|
||||
},
|
||||
error() {
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"__schema":{"types":[{"kind":"UNION","name":"JobNeedUnion","possibleTypes":[{"name":"CiBuildNeed"},{"name":"CiJob"}]}]}}
|
|
@ -1,19 +1,10 @@
|
|||
import VueApollo from 'vue-apollo';
|
||||
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import introspectionQueryResultData from './graphql/fragmentTypes.json';
|
||||
|
||||
export const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
});
|
||||
|
||||
export const apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(
|
||||
{},
|
||||
{
|
||||
cacheConfig: {
|
||||
fragmentMatcher,
|
||||
},
|
||||
useGet: true,
|
||||
},
|
||||
),
|
||||
|
|
|
@ -9,6 +9,8 @@ import {
|
|||
linkedIssueTypesMap,
|
||||
addRelatedIssueErrorMap,
|
||||
addRelatedItemErrorMap,
|
||||
issuablesFormCategoryHeaderTextMap,
|
||||
issuablesFormInputTextMap,
|
||||
} from '../constants';
|
||||
import RelatedIssuableInput from './related_issuable_input.vue';
|
||||
|
||||
|
@ -134,6 +136,12 @@ export default {
|
|||
epics: mergeUrlParams({ confidential_only: true }, this.autoCompleteSources.epics),
|
||||
};
|
||||
},
|
||||
issuableCategoryHeaderText() {
|
||||
return issuablesFormCategoryHeaderTextMap[this.issuableType];
|
||||
},
|
||||
issuableInputText() {
|
||||
return issuablesFormInputTextMap[this.issuableType];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onPendingIssuableRemoveRequest(params) {
|
||||
|
@ -162,7 +170,7 @@ export default {
|
|||
<form @submit.prevent="onFormSubmit">
|
||||
<template v-if="showCategorizedIssues">
|
||||
<gl-form-group
|
||||
:label="__('The current issue')"
|
||||
:label="issuableCategoryHeaderText"
|
||||
label-for="linked-issue-type-radio"
|
||||
label-class="label-bold"
|
||||
class="mb-2"
|
||||
|
@ -175,7 +183,7 @@ export default {
|
|||
/>
|
||||
</gl-form-group>
|
||||
<p class="bold">
|
||||
{{ __('the following issue(s)') }}
|
||||
{{ issuableInputText }}
|
||||
</p>
|
||||
</template>
|
||||
<related-issuable-input
|
||||
|
|
|
@ -5,6 +5,9 @@ import {
|
|||
issuableQaClassMap,
|
||||
linkedIssueTypesMap,
|
||||
linkedIssueTypesTextMap,
|
||||
issuablesBlockHeaderTextMap,
|
||||
issuablesBlockHelpTextMap,
|
||||
issuablesBlockAddButtonTextMap,
|
||||
} from '../constants';
|
||||
import AddIssuableForm from './add_issuable_form.vue';
|
||||
import RelatedIssuesList from './related_issues_list.vue';
|
||||
|
@ -105,6 +108,15 @@ export default {
|
|||
hasBody() {
|
||||
return this.isFormVisible || this.shouldShowTokenBody;
|
||||
},
|
||||
headerText() {
|
||||
return issuablesBlockHeaderTextMap[this.issuableType];
|
||||
},
|
||||
helpLinkText() {
|
||||
return issuablesBlockHelpTextMap[this.issuableType];
|
||||
},
|
||||
addIssuableButtonText() {
|
||||
return issuablesBlockAddButtonTextMap[this.issuableType];
|
||||
},
|
||||
badgeLabel() {
|
||||
return this.isFetching && this.relatedIssues.length === 0 ? '...' : this.relatedIssues.length;
|
||||
},
|
||||
|
@ -138,13 +150,14 @@ export default {
|
|||
href="#related-issues"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<slot name="header-text">{{ __('Linked issues') }}</slot>
|
||||
<slot name="header-text">{{ headerText }}</slot>
|
||||
<gl-link
|
||||
v-if="hasHelpPath"
|
||||
:href="helpPath"
|
||||
target="_blank"
|
||||
class="gl-display-flex gl-align-items-center gl-ml-2 gl-text-gray-500"
|
||||
:aria-label="__('Read more about related issues')"
|
||||
data-testid="help-link"
|
||||
:aria-label="helpLinkText"
|
||||
>
|
||||
<gl-icon name="question" :size="12" />
|
||||
</gl-link>
|
||||
|
@ -160,7 +173,7 @@ export default {
|
|||
v-if="canAdmin"
|
||||
data-qa-selector="related_issues_plus_button"
|
||||
icon="plus"
|
||||
:aria-label="__('Add a related issue')"
|
||||
:aria-label="addIssuableButtonText"
|
||||
:class="qaClass"
|
||||
@click="$emit('toggleAddRelatedIssuesForm', $event)"
|
||||
/>
|
||||
|
|
|
@ -104,3 +104,28 @@ export const PathIdSeparator = {
|
|||
Epic: '&',
|
||||
Issue: '#',
|
||||
};
|
||||
|
||||
export const issuablesBlockHeaderTextMap = {
|
||||
[issuableTypesMap.ISSUE]: __('Linked issues'),
|
||||
[issuableTypesMap.EPIC]: __('Linked epics'),
|
||||
};
|
||||
|
||||
export const issuablesBlockHelpTextMap = {
|
||||
[issuableTypesMap.ISSUE]: __('Read more about related issues'),
|
||||
[issuableTypesMap.EPIC]: __('Read more about related epics'),
|
||||
};
|
||||
|
||||
export const issuablesBlockAddButtonTextMap = {
|
||||
[issuableTypesMap.ISSUE]: __('Add a related issue'),
|
||||
[issuableTypesMap.EPIC]: __('Add a related epic'),
|
||||
};
|
||||
|
||||
export const issuablesFormCategoryHeaderTextMap = {
|
||||
[issuableTypesMap.ISSUE]: __('The current issue'),
|
||||
[issuableTypesMap.EPIC]: __('The current epic'),
|
||||
};
|
||||
|
||||
export const issuablesFormInputTextMap = {
|
||||
[issuableTypesMap.ISSUE]: __('the following issue(s)'),
|
||||
[issuableTypesMap.EPIC]: __('the following epic(s)'),
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ export default function initRelatedIssues() {
|
|||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el: relatedIssuesRootElement,
|
||||
name: 'RelatedIssuesApp',
|
||||
components: {
|
||||
relatedIssuesRoot: RelatedIssuesRoot,
|
||||
},
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"__schema":{"types":[{"kind":"INTERFACE","name":"Entry","possibleTypes":[{"name":"Blob"},{"name":"Submodule"},{"name":"TreeEntry"}]}]}}
|
|
@ -1,19 +1,11 @@
|
|||
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import introspectionQueryResultData from './fragmentTypes.json';
|
||||
import { fetchLogsTree } from './log_tree';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
// We create a fragment matcher so that we can create a fragment from an interface
|
||||
// Without this, Apollo throws a heuristic fragment matcher warning
|
||||
const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
});
|
||||
|
||||
const defaultClient = createDefaultClient(
|
||||
{
|
||||
Query: {
|
||||
|
@ -43,7 +35,6 @@ const defaultClient = createDefaultClient(
|
|||
},
|
||||
{
|
||||
cacheConfig: {
|
||||
fragmentMatcher,
|
||||
dataIdFromObject: (obj) => {
|
||||
/* eslint-disable @gitlab/require-i18n-strings */
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
|
|
|
@ -28,10 +28,12 @@ query getGroupRunners(
|
|||
edges {
|
||||
webUrl
|
||||
node {
|
||||
__typename
|
||||
...RunnerNode
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
__typename
|
||||
...PageInfo
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ query getRunner($id: CiRunnerID!) {
|
|||
# We have an id in deeply nested fragment
|
||||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
runner(id: $id) {
|
||||
__typename
|
||||
...RunnerDetails
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ query getRunners(
|
|||
editAdminUrl
|
||||
}
|
||||
pageInfo {
|
||||
__typename
|
||||
...PageInfo
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
fragment RunnerNode on CiRunner {
|
||||
__typename
|
||||
id
|
||||
description
|
||||
runnerType
|
||||
|
|
|
@ -96,6 +96,9 @@ export default {
|
|||
return data.workspace?.issuable;
|
||||
},
|
||||
result({ data }) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
const issuable = data.workspace?.issuable;
|
||||
if (issuable) {
|
||||
this.selected = cloneDeep(issuable.assignees.nodes);
|
||||
|
|
|
@ -66,6 +66,9 @@ export default {
|
|||
return data.workspace?.issuable?.confidential || false;
|
||||
},
|
||||
result({ data }) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
this.$emit('confidentialityUpdated', data.workspace?.issuable?.confidential);
|
||||
},
|
||||
error() {
|
||||
|
|
|
@ -86,6 +86,9 @@ export default {
|
|||
return data.workspace?.issuable || {};
|
||||
},
|
||||
result({ data }) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
this.$emit(`${this.dateType}Updated`, data.workspace?.issuable?.[this.dateType]);
|
||||
},
|
||||
error() {
|
||||
|
|
|
@ -61,6 +61,9 @@ export default {
|
|||
return data.workspace?.issuable?.subscribed || false;
|
||||
},
|
||||
result({ data }) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
this.emailsDisabled = this.parentIsGroup
|
||||
? data.workspace?.emailsDisabled
|
||||
: data.workspace?.issuable?.emailsDisabled;
|
||||
|
|
|
@ -59,6 +59,10 @@ export default {
|
|||
return data.workspace?.issuable?.currentUserTodos.nodes[0]?.id;
|
||||
},
|
||||
result({ data }) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentUserTodos = data.workspace?.issuable?.currentUserTodos?.nodes ?? [];
|
||||
this.todoId = currentUserTodos[0]?.id;
|
||||
this.$emit('todoUpdated', currentUserTodos.length > 0);
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"__schema":{"types":[{"kind":"UNION","name":"Issuable","possibleTypes":[{"name":"Issue"},{"name":"MergeRequest"}]}, {"kind":"INTERFACE","name":"User","possibleTypes":[{"name":"UserCore"}]}]}}
|
|
@ -1,14 +1,8 @@
|
|||
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import produce from 'immer';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import getIssueStateQuery from '~/issues/show/queries/get_issue_state.query.graphql';
|
||||
import { resolvers as workItemResolvers } from '~/work_items/graphql/resolvers';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import introspectionQueryResultData from './fragmentTypes.json';
|
||||
|
||||
const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
});
|
||||
|
||||
const resolvers = {
|
||||
...workItemResolvers,
|
||||
|
@ -24,11 +18,7 @@ const resolvers = {
|
|||
},
|
||||
};
|
||||
|
||||
export const defaultClient = createDefaultClient(resolvers, {
|
||||
cacheConfig: {
|
||||
fragmentMatcher,
|
||||
},
|
||||
});
|
||||
export const defaultClient = createDefaultClient(resolvers);
|
||||
|
||||
export const apolloProvider = new VueApollo({
|
||||
defaultClient,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defaultDataIdFromObject } from '@apollo/client/core';
|
||||
import { GlToast } from '@gitlab/ui';
|
||||
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
|
|
|
@ -3,7 +3,6 @@ query getState($projectPath: ID!, $iid: String!) {
|
|||
id
|
||||
archived
|
||||
onlyAllowMergeIfPipelineSucceeds
|
||||
|
||||
mergeRequest(iid: $iid) {
|
||||
id
|
||||
autoMergeEnabled
|
||||
|
|
|
@ -4,6 +4,7 @@ query autoMergeEnabled($projectPath: ID!, $iid: String!) {
|
|||
project(fullPath: $projectPath) {
|
||||
id
|
||||
mergeRequest(iid: $iid) {
|
||||
id
|
||||
...autoMergeEnabled
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
query readyToMerge($projectPath: ID!, $iid: String!) {
|
||||
project(fullPath: $projectPath) {
|
||||
id
|
||||
...ReadyToMerge
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
|
||||
import { defaultDataIdFromObject } from '@apollo/client/core';
|
||||
import produce from 'immer';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
|
|
|
@ -31,10 +31,6 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
enableLabelPermalinks: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
labelFilterParam: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
@ -142,11 +138,8 @@ export default {
|
|||
return label.title || label.name;
|
||||
},
|
||||
labelTarget(label) {
|
||||
if (this.enableLabelPermalinks) {
|
||||
const value = encodeURIComponent(this.labelTitle(label));
|
||||
return `?${this.labelFilterParam}[]=${value}`;
|
||||
}
|
||||
return '#';
|
||||
const value = encodeURIComponent(this.labelTitle(label));
|
||||
return `?${this.labelFilterParam}[]=${value}`;
|
||||
},
|
||||
/**
|
||||
* This is needed as an independent method since
|
||||
|
|
|
@ -133,11 +133,6 @@ export default {
|
|||
required: false,
|
||||
default: 2,
|
||||
},
|
||||
enableLabelPermalinks: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
labelFilterParam: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
@ -321,7 +316,6 @@ export default {
|
|||
:data-qa-issuable-title="issuable.title"
|
||||
:issuable-symbol="issuableSymbol"
|
||||
:issuable="issuable"
|
||||
:enable-label-permalinks="enableLabelPermalinks"
|
||||
:label-filter-param="labelFilterParam"
|
||||
:show-checkbox="showBulkEditSidebar"
|
||||
:checked="issuableChecked(issuable)"
|
||||
|
|
|
@ -102,8 +102,8 @@ export default {
|
|||
error(error) {
|
||||
this.showError(error);
|
||||
},
|
||||
result({ loading }) {
|
||||
if (loading) {
|
||||
result({ loading, data }) {
|
||||
if (loading || !data) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"__schema":{"types":[{"kind":"INTERFACE","name":"LocalWorkItemWidget","possibleTypes":[{"name":"LocalTitleWidget"}]}]}}
|
|
@ -1,23 +1,14 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import workItemQuery from './work_item.query.graphql';
|
||||
import introspectionQueryResultData from './fragmentTypes.json';
|
||||
import { resolvers } from './resolvers';
|
||||
import typeDefs from './typedefs.graphql';
|
||||
|
||||
const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
introspectionQueryResultData,
|
||||
});
|
||||
|
||||
export function createApolloProvider() {
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const defaultClient = createDefaultClient(resolvers, {
|
||||
cacheConfig: {
|
||||
fragmentMatcher,
|
||||
},
|
||||
typeDefs,
|
||||
});
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class Admin::RunnersController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def destroy
|
||||
@runner.destroy
|
||||
Ci::UnregisterRunnerService.new(@runner).execute
|
||||
|
||||
redirect_to admin_runners_path, status: :found
|
||||
end
|
||||
|
|
|
@ -35,7 +35,7 @@ class Groups::RunnersController < Groups::ApplicationController
|
|||
if @runner.belongs_to_more_than_one_project?
|
||||
redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: :found, alert: _('Runner was not deleted because it is assigned to multiple projects.')
|
||||
else
|
||||
@runner.destroy
|
||||
Ci::UnregisterRunnerService.new(@runner).execute
|
||||
|
||||
redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: :found
|
||||
end
|
||||
|
|
|
@ -23,7 +23,7 @@ class Projects::RunnersController < Projects::ApplicationController
|
|||
|
||||
def destroy
|
||||
if @runner.only_for?(project)
|
||||
@runner.destroy
|
||||
Ci::UnregisterRunnerService.new(@runner).execute
|
||||
end
|
||||
|
||||
redirect_to project_runners_path(@project), status: :found
|
||||
|
|
|
@ -20,7 +20,7 @@ module Mutations
|
|||
error = authenticate_delete_runner!(runner)
|
||||
return { errors: [error] } if error
|
||||
|
||||
runner.destroy!
|
||||
::Ci::UnregisterRunnerService.new(runner).execute
|
||||
|
||||
{ errors: runner.errors.full_messages }
|
||||
end
|
||||
|
|
|
@ -32,13 +32,13 @@ module Mutations
|
|||
params = global_id_compatibility_params(attributes).merge(author_id: current_user.id)
|
||||
|
||||
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
||||
work_item = ::WorkItems::CreateService.new(project: project, current_user: current_user, params: params, spam_params: spam_params).execute
|
||||
create_result = ::WorkItems::CreateService.new(project: project, current_user: current_user, params: params, spam_params: spam_params).execute
|
||||
|
||||
check_spam_action_response!(work_item)
|
||||
check_spam_action_response!(create_result[:work_item]) if create_result[:work_item]
|
||||
|
||||
{
|
||||
work_item: work_item.valid? ? work_item : nil,
|
||||
errors: errors_on_object(work_item)
|
||||
work_item: create_result.success? ? create_result[:work_item] : nil,
|
||||
errors: create_result.errors
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
class UnregisterRunnerService
|
||||
attr_reader :runner
|
||||
|
||||
# @param [Ci::Runner] runner the runner to unregister/destroy
|
||||
def initialize(runner)
|
||||
@runner = runner
|
||||
end
|
||||
|
||||
def execute
|
||||
@runner&.destroy
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module WorkItems
|
||||
class CreateService
|
||||
include ::Services::ReturnServiceResponses
|
||||
|
||||
def initialize(project:, current_user: nil, params: {}, spam_params:)
|
||||
@create_service = ::Issues::CreateService.new(
|
||||
project: project,
|
||||
|
@ -10,10 +12,28 @@ module WorkItems
|
|||
spam_params: spam_params,
|
||||
build_service: ::WorkItems::BuildService.new(project: project, current_user: current_user, params: params)
|
||||
)
|
||||
@current_user = current_user
|
||||
@project = project
|
||||
end
|
||||
|
||||
def execute
|
||||
@create_service.execute
|
||||
unless @current_user.can?(:create_work_item, @project)
|
||||
return error(_('Operation not allowed'), :forbidden)
|
||||
end
|
||||
|
||||
work_item = @create_service.execute
|
||||
|
||||
if work_item.valid?
|
||||
success(payload(work_item))
|
||||
else
|
||||
error(work_item.errors.full_messages, :unprocessable_entity, pass_back: payload(work_item))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def payload(work_item)
|
||||
{ work_item: work_item }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
|
||||
= form_tag path do
|
||||
%input{ :name => "_method", :type => "hidden", :value => "delete" }/
|
||||
= submit_tag _('Revoke'), class: 'gl-button btn btn-danger btn-sm', data: { confirm: _('Are you sure?') }
|
||||
= submit_tag _('Revoke'), class: 'gl-button btn btn-danger btn-sm', aria: { label: s_('AuthorizedApplication|Revoke application') }, data: { confirm: s_('AuthorizedApplication|Are you sure you want to revoke this application?'), confirm_btn_variant: 'danger' }
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
- page_title _("Learn GitLab")
|
||||
- add_page_specific_style 'page_bundles/learn_gitlab'
|
||||
- data = learn_gitlab_data(@project)
|
||||
- invite_members_open = session.delete(:confetti_post_signup)
|
||||
|
||||
= render 'projects/invite_members_modal', project: @project
|
||||
|
||||
|
@ -10,4 +9,4 @@
|
|||
- e.control do
|
||||
#js-learn-gitlab-app{ data: data }
|
||||
- e.candidate do
|
||||
#js-learn-gitlab-app{ data: data.merge(invite_members_open: invite_members_open) }
|
||||
#js-learn-gitlab-app{ data: data.merge(invite_members: 'true') }
|
||||
|
|
|
@ -150,6 +150,8 @@ function generateEntries() {
|
|||
}
|
||||
|
||||
const alias = {
|
||||
// Map Apollo client to apollo/client/core to prevent react related imports from being loaded
|
||||
'@apollo/client$': '@apollo/client/core',
|
||||
'~': path.join(ROOT_PATH, 'app/assets/javascripts'),
|
||||
emojis: path.join(ROOT_PATH, 'fixtures/emojis'),
|
||||
empty_states: path.join(ROOT_PATH, 'app/views/shared/empty_states'),
|
||||
|
|
|
@ -47,7 +47,7 @@ module.exports = {
|
|||
'bootstrap/dist/js/bootstrap.js',
|
||||
'sortablejs/modular/sortable.esm.js',
|
||||
'popper.js',
|
||||
'apollo-client',
|
||||
'@apollo/client/core',
|
||||
'source-map',
|
||||
'mousetrap',
|
||||
],
|
||||
|
|
|
@ -152,7 +152,7 @@ Root-level queries are defined in
|
|||
### Multiplex queries
|
||||
|
||||
GitLab supports batching queries into a single request using
|
||||
[apollo-link-batch-http](https://www.apollographql.com/docs/link/links/batch-http/). More
|
||||
[`@apollo/client/link/batch-http`](https://www.apollographql.com/docs/react/api/link/apollo-link-batch-http/). More
|
||||
information about multiplexed queries is also available for
|
||||
[GraphQL Ruby](https://graphql-ruby.org/queries/multiplex.html), the
|
||||
library GitLab uses on the backend.
|
||||
|
|
|
@ -171,6 +171,7 @@ POST /projects/:id/ci/lint
|
|||
| `content` | string | yes | The CI/CD configuration content. |
|
||||
| `dry_run` | boolean | no | Run [pipeline creation simulation](../ci/lint.md#simulate-a-pipeline), or only do static check. This is false by default. |
|
||||
| `include_jobs` | boolean | no | If the list of jobs that would exist in a static check or pipeline simulation should be included in the response. This is false by default. |
|
||||
| `ref` | string | no | When `dry_run` is `true`, sets the branch or tag to use. Defaults to the project's default branch when not set. |
|
||||
|
||||
Example request:
|
||||
|
||||
|
@ -220,6 +221,7 @@ GET /projects/:id/ci/lint
|
|||
| ---------- | ------- | -------- | -------- |
|
||||
| `dry_run` | boolean | no | Run pipeline creation simulation, or only do static check. |
|
||||
| `include_jobs` | boolean | no | If the list of jobs that would exist in a static check or pipeline simulation should be included in the response. This is false by default. |
|
||||
| `ref` | string | no | When `dry_run` is `true`, sets the branch or tag to use. Defaults to the project's default branch when not set. |
|
||||
|
||||
Example request:
|
||||
|
||||
|
|
|
@ -73,10 +73,8 @@ The chart indicates the project's progress throughout that milestone (for issues
|
|||
In particular, it shows how many issues were or are still open for a given day in the
|
||||
milestone's corresponding period.
|
||||
|
||||
The burndown chart can also be toggled to display the cumulative open issue
|
||||
weight for a given day. When using this feature, make sure issue weights have
|
||||
been properly assigned, since an open issue with no weight adds zero to the
|
||||
cumulative value.
|
||||
You can also toggle the burndown chart to display the
|
||||
[cumulative open issue weight](#switch-between-number-of-issues-and-issue-weight) for a given day.
|
||||
|
||||
### Fixed burndown charts
|
||||
|
||||
|
@ -123,12 +121,21 @@ To view a group's burnup chart:
|
|||
### How burnup charts work
|
||||
|
||||
Burnup charts have separate lines for total work and completed work. The total line
|
||||
shows when scope is reduced or added to a milestone. The completed work is a count
|
||||
of issues closed.
|
||||
shows changes to the scope of a milestone. When an open issue is moved to another
|
||||
milestone, the "total issues" goes down but the "completed issues" stays the same.
|
||||
The completed work is a count of issues closed. When an issue is closed, the "total
|
||||
issues" remains the same and "completed issues" goes up.
|
||||
|
||||
Burnup charts can show either the total number of issues or total weight for each
|
||||
day of the milestone. Use the toggle above the charts to switch between total
|
||||
and weight.
|
||||
## Switch between number of issues and issue weight
|
||||
|
||||
In both burndown or burnup charts you can view them
|
||||
either by the total number of issues
|
||||
or the total weight for each day of the milestone.
|
||||
|
||||
To switch between the two settings, select either **Issues** or **Issue weight** above the charts.
|
||||
|
||||
When sorting by weight, make sure all your issues
|
||||
have weight assigned, because issues with no weight don't show on the chart.
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
|
||||
|
|
|
@ -151,10 +151,10 @@ There are also tabs below these that show the following:
|
|||
|
||||
### Burndown Charts
|
||||
|
||||
The milestone view contains a [burndown chart](burndown_and_burnup_charts.md),
|
||||
The milestone view contains a [burndown and burnup chart](burndown_and_burnup_charts.md),
|
||||
showing the progress of completing a milestone.
|
||||
|
||||
![burndown chart](img/burndown_chart_v13_6.png)
|
||||
![burndown chart](img/burndown_and_burnup_charts_v13_6.png)
|
||||
|
||||
### Milestone sidebar
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ module API
|
|||
delete '/', feature_category: :runner do
|
||||
authenticate_runner!
|
||||
|
||||
destroy_conditionally!(current_runner)
|
||||
destroy_conditionally!(current_runner) { ::Ci::UnregisterRunnerService.new(current_runner).execute }
|
||||
end
|
||||
|
||||
desc 'Validates authentication credentials' do
|
||||
|
|
|
@ -110,7 +110,7 @@ module API
|
|||
|
||||
authenticate_delete_runner!(runner)
|
||||
|
||||
destroy_conditionally!(runner)
|
||||
destroy_conditionally!(runner) { ::Ci::UnregisterRunnerService.new(runner).execute }
|
||||
end
|
||||
|
||||
desc 'List jobs running on a runner' do
|
||||
|
|
|
@ -42,6 +42,7 @@ module API
|
|||
params do
|
||||
optional :dry_run, type: Boolean, default: false, desc: 'Run pipeline creation simulation, or only do static check.'
|
||||
optional :include_jobs, type: Boolean, desc: 'Whether or not to include CI jobs in the response'
|
||||
optional :ref, type: String, desc: 'Branch or tag used to execute a dry run. Defaults to the default branch of the project. Only used when dry_run is true'
|
||||
end
|
||||
get ':id/ci/lint', urgency: :low do
|
||||
authorize! :download_code, user_project
|
||||
|
@ -52,7 +53,7 @@ module API
|
|||
|
||||
result = Gitlab::Ci::Lint
|
||||
.new(project: user_project, current_user: current_user)
|
||||
.validate(content, dry_run: params[:dry_run])
|
||||
.validate(content, dry_run: params[:dry_run], ref: params[:ref] || user_project.default_branch)
|
||||
|
||||
present result, with: Entities::Ci::Lint::Result, current_user: current_user, include_jobs: params[:include_jobs]
|
||||
end
|
||||
|
@ -66,13 +67,14 @@ module API
|
|||
requires :content, type: String, desc: 'Content of .gitlab-ci.yml'
|
||||
optional :dry_run, type: Boolean, default: false, desc: 'Run pipeline creation simulation, or only do static check.'
|
||||
optional :include_jobs, type: Boolean, desc: 'Whether or not to include CI jobs in the response'
|
||||
optional :ref, type: String, desc: 'Branch or tag used to execute a dry run. Defaults to the default branch of the project. Only used when dry_run is true'
|
||||
end
|
||||
post ':id/ci/lint', urgency: :low do
|
||||
authorize! :create_pipeline, user_project
|
||||
|
||||
result = Gitlab::Ci::Lint
|
||||
.new(project: user_project, current_user: current_user)
|
||||
.validate(params[:content], dry_run: params[:dry_run])
|
||||
.validate(params[:content], dry_run: params[:dry_run], ref: params[:ref] || user_project.default_branch)
|
||||
|
||||
status 200
|
||||
present result, with: Entities::Ci::Lint::Result, current_user: current_user, include_jobs: params[:include_jobs]
|
||||
|
|
|
@ -24,9 +24,9 @@ module Gitlab
|
|||
@sha = sha || project&.repository&.commit&.sha
|
||||
end
|
||||
|
||||
def validate(content, dry_run: false)
|
||||
def validate(content, dry_run: false, ref: @project&.default_branch)
|
||||
if dry_run
|
||||
simulate_pipeline_creation(content)
|
||||
simulate_pipeline_creation(content, ref)
|
||||
else
|
||||
static_validation(content)
|
||||
end
|
||||
|
@ -34,9 +34,9 @@ module Gitlab
|
|||
|
||||
private
|
||||
|
||||
def simulate_pipeline_creation(content)
|
||||
def simulate_pipeline_creation(content, ref)
|
||||
pipeline = ::Ci::CreatePipelineService
|
||||
.new(@project, @current_user, ref: @project.default_branch)
|
||||
.new(@project, @current_user, ref: ref)
|
||||
.execute(:push, dry_run: true, content: content)
|
||||
.payload
|
||||
|
||||
|
|
|
@ -2091,6 +2091,9 @@ msgstr ""
|
|||
msgid "Add a numbered list"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add a related epic"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add a related issue"
|
||||
msgstr ""
|
||||
|
||||
|
@ -5158,6 +5161,12 @@ msgstr ""
|
|||
msgid "Authorized applications (%{size})"
|
||||
msgstr ""
|
||||
|
||||
msgid "AuthorizedApplication|Are you sure you want to revoke this application?"
|
||||
msgstr ""
|
||||
|
||||
msgid "AuthorizedApplication|Revoke application"
|
||||
msgstr ""
|
||||
|
||||
msgid "Authors: %{authors}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -21755,6 +21764,9 @@ msgstr ""
|
|||
msgid "Linked emails (%{email_count})"
|
||||
msgstr ""
|
||||
|
||||
msgid "Linked epics"
|
||||
msgstr ""
|
||||
|
||||
msgid "Linked issues"
|
||||
msgstr ""
|
||||
|
||||
|
@ -29685,6 +29697,9 @@ msgstr ""
|
|||
msgid "Read more about project permissions %{help_link_open}here%{help_link_close}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Read more about related epics"
|
||||
msgstr ""
|
||||
|
||||
msgid "Read more about related issues"
|
||||
msgstr ""
|
||||
|
||||
|
@ -36091,6 +36106,9 @@ msgstr ""
|
|||
msgid "The contents of this group, its subgroups and projects will be permanently removed after %{deletion_adjourned_period} days on %{date}. After this point, your data cannot be recovered."
|
||||
msgstr ""
|
||||
|
||||
msgid "The current epic"
|
||||
msgstr ""
|
||||
|
||||
msgid "The current issue"
|
||||
msgstr ""
|
||||
|
||||
|
@ -44006,6 +44024,9 @@ msgstr ""
|
|||
msgid "the file"
|
||||
msgstr ""
|
||||
|
||||
msgid "the following epic(s)"
|
||||
msgstr ""
|
||||
|
||||
msgid "the following issue(s)"
|
||||
msgstr ""
|
||||
|
||||
|
|
14
package.json
14
package.json
|
@ -47,6 +47,7 @@
|
|||
"webpack-prod": "NODE_OPTIONS=\"--max-old-space-size=3584\" NODE_ENV=production webpack --config config/webpack.config.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.3.11",
|
||||
"@babel/core": "^7.10.1",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.1",
|
||||
"@babel/plugin-proposal-json-strings": "^7.10.1",
|
||||
|
@ -94,13 +95,7 @@
|
|||
"@tiptap/vue-2": "^2.0.0-beta.77",
|
||||
"@toast-ui/editor": "^2.5.2",
|
||||
"@toast-ui/vue-editor": "^2.5.2",
|
||||
"apollo-cache-inmemory": "^1.6.6",
|
||||
"apollo-client": "^2.6.10",
|
||||
"apollo-link": "^1.2.14",
|
||||
"apollo-link-batch-http": "^1.2.14",
|
||||
"apollo-link-error": "^1.1.13",
|
||||
"apollo-link-http": "^1.5.17",
|
||||
"apollo-upload-client": "^13.0.0",
|
||||
"apollo-upload-client": "^14.1.3",
|
||||
"autosize": "^5.0.1",
|
||||
"aws-sdk": "^2.637.0",
|
||||
"axios": "^0.24.0",
|
||||
|
@ -186,7 +181,7 @@
|
|||
"uuid": "8.1.0",
|
||||
"visibilityjs": "^1.2.4",
|
||||
"vue": "^2.6.12",
|
||||
"vue-apollo": "^3.0.3",
|
||||
"vue-apollo": "^3.0.7",
|
||||
"vue-loader": "^15.9.6",
|
||||
"vue-observe-visibility": "^1.0.0",
|
||||
"vue-resize": "^1.0.1",
|
||||
|
@ -244,7 +239,7 @@
|
|||
"markdownlint-cli": "0.31.0",
|
||||
"md5": "^2.2.1",
|
||||
"miragejs": "^0.1.40",
|
||||
"mock-apollo-client": "^0.7.0",
|
||||
"mock-apollo-client": "1.2.0",
|
||||
"nodemon": "^2.0.4",
|
||||
"postcss": "^8.4.5",
|
||||
"prettier": "2.2.1",
|
||||
|
@ -266,6 +261,7 @@
|
|||
"bootstrap-vue": "https://docs.gitlab.com/ee/development/fe_guide/dependencies.html#bootstrapvue"
|
||||
},
|
||||
"resolutions": {
|
||||
"@apollo/client/subscriptions-transport-ws": "0.11.0",
|
||||
"chokidar": "^3.4.0",
|
||||
"@types/node": "14.17.5"
|
||||
},
|
||||
|
|
|
@ -4,9 +4,10 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Admin::RunnersController do
|
||||
let_it_be(:runner) { create(:ci_runner) }
|
||||
let_it_be(:user) { create(:admin) }
|
||||
|
||||
before do
|
||||
sign_in(create(:admin))
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
describe '#index' do
|
||||
|
@ -104,6 +105,10 @@ RSpec.describe Admin::RunnersController do
|
|||
|
||||
describe '#destroy' do
|
||||
it 'destroys the runner' do
|
||||
expect_next_instance_of(Ci::UnregisterRunnerService, runner) do |service|
|
||||
expect(service).to receive(:execute).once.and_call_original
|
||||
end
|
||||
|
||||
delete :destroy, params: { id: runner.id }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
|
|
|
@ -309,7 +309,8 @@ RSpec.describe Groups::ClustersController do
|
|||
.to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
|
||||
allow_any_instance_of(GoogleApi::CloudPlatform::Client)
|
||||
.to receive(:projects_zones_clusters_create) do
|
||||
OpenStruct.new(
|
||||
double(
|
||||
'instance',
|
||||
self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
|
||||
status: 'RUNNING'
|
||||
)
|
||||
|
|
|
@ -190,6 +190,10 @@ RSpec.describe Groups::RunnersController do
|
|||
end
|
||||
|
||||
it 'destroys the runner and redirects' do
|
||||
expect_next_instance_of(Ci::UnregisterRunnerService, runner) do |service|
|
||||
expect(service).to receive(:execute).once.and_call_original
|
||||
end
|
||||
|
||||
delete :destroy, params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
|
|
|
@ -37,6 +37,10 @@ RSpec.describe Projects::RunnersController do
|
|||
|
||||
describe '#destroy' do
|
||||
it 'destroys the runner' do
|
||||
expect_next_instance_of(Ci::UnregisterRunnerService, runner) do |service|
|
||||
expect(service).to receive(:execute).once.and_call_original
|
||||
end
|
||||
|
||||
delete :destroy, params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
|
|
|
@ -99,7 +99,8 @@ RSpec.describe 'Clusters', :js do
|
|||
|
||||
allow_any_instance_of(GoogleApi::CloudPlatform::Client)
|
||||
.to receive(:projects_zones_clusters_create) do
|
||||
OpenStruct.new(
|
||||
double(
|
||||
'cluster_control',
|
||||
self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
|
||||
status: 'RUNNING'
|
||||
)
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import { InMemoryCache } from 'apollo-cache-inmemory';
|
||||
import { InMemoryCache } from '@apollo/client/core';
|
||||
import { createMockClient as createMockApolloClient } from 'mock-apollo-client';
|
||||
import VueApollo from 'vue-apollo';
|
||||
|
||||
const defaultCacheOptions = {
|
||||
fragmentMatcher: { match: () => true },
|
||||
addTypename: false,
|
||||
};
|
||||
import possibleTypes from '~/graphql_shared/possibleTypes.json';
|
||||
import { typePolicies } from '~/lib/graphql';
|
||||
|
||||
export function createMockClient(handlers = [], resolvers = {}, cacheOptions = {}) {
|
||||
const cache = new InMemoryCache({
|
||||
...defaultCacheOptions,
|
||||
possibleTypes,
|
||||
typePolicies,
|
||||
addTypename: false,
|
||||
...cacheOptions,
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import { InMemoryCache } from 'apollo-cache-inmemory';
|
||||
import { ApolloClient } from 'apollo-client';
|
||||
import { ApolloLink } from 'apollo-link';
|
||||
import gql from 'graphql-tag';
|
||||
import { InMemoryCache, ApolloClient, ApolloLink, gql } from '@apollo/client/core';
|
||||
|
||||
const FOO_QUERY = gql`
|
||||
query {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
import { gql } from '@apollo/client/core';
|
||||
import cable from '~/actioncable_consumer';
|
||||
import ActionCableLink from '~/actioncable_link';
|
||||
|
||||
|
|
|
@ -135,7 +135,6 @@ describe('AdminUsersTable component', () => {
|
|||
});
|
||||
|
||||
it('creates a flash message and captures the error', () => {
|
||||
expect(createFlash).toHaveBeenCalledTimes(2);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: 'Could not load user group counts. Please refresh the page to try again.',
|
||||
captureError: true,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import AxiosMockAdapter from 'axios-mock-adapter';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql';
|
||||
import updateHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql';
|
||||
|
@ -68,10 +68,7 @@ describe('AlertsSettingsWrapper', () => {
|
|||
const findAlertsSettingsForm = () => wrapper.findComponent(AlertsSettingsForm);
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
|
||||
async function destroyHttpIntegration(localWrapper) {
|
||||
await jest.runOnlyPendingTimers();
|
||||
await nextTick();
|
||||
|
||||
function destroyHttpIntegration(localWrapper) {
|
||||
localWrapper
|
||||
.find(IntegrationsList)
|
||||
.vm.$emit('delete-integration', { id: integrationToDestroy.id });
|
||||
|
@ -474,11 +471,11 @@ describe('AlertsSettingsWrapper', () => {
|
|||
|
||||
it('calls a mutation with correct parameters and destroys a integration', async () => {
|
||||
createComponentWithApollo();
|
||||
await waitForPromises();
|
||||
|
||||
await destroyHttpIntegration(wrapper);
|
||||
destroyHttpIntegration(wrapper);
|
||||
|
||||
expect(destroyIntegrationHandler).toHaveBeenCalled();
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findIntegrations()).toHaveLength(3);
|
||||
|
|
|
@ -38,6 +38,7 @@ export const getIntegrationsQueryResponse = {
|
|||
alertManagementIntegrations: {
|
||||
nodes: [
|
||||
{
|
||||
__typename: 'AlertManagementIntegration',
|
||||
id: '37',
|
||||
type: 'HTTP',
|
||||
active: true,
|
||||
|
@ -48,6 +49,7 @@ export const getIntegrationsQueryResponse = {
|
|||
apiUrl: null,
|
||||
},
|
||||
{
|
||||
__typename: 'AlertManagementIntegration',
|
||||
id: '41',
|
||||
type: 'HTTP',
|
||||
active: true,
|
||||
|
@ -58,6 +60,7 @@ export const getIntegrationsQueryResponse = {
|
|||
apiUrl: null,
|
||||
},
|
||||
{
|
||||
__typename: 'AlertManagementIntegration',
|
||||
id: '40',
|
||||
type: 'HTTP',
|
||||
active: true,
|
||||
|
@ -68,6 +71,7 @@ export const getIntegrationsQueryResponse = {
|
|||
apiUrl: null,
|
||||
},
|
||||
{
|
||||
__typename: 'AlertManagementIntegration',
|
||||
id: '12',
|
||||
type: 'PROMETHEUS',
|
||||
active: false,
|
||||
|
@ -83,6 +87,7 @@ export const getIntegrationsQueryResponse = {
|
|||
};
|
||||
|
||||
export const integrationToDestroy = {
|
||||
__typename: 'AlertManagementIntegration',
|
||||
id: '37',
|
||||
type: 'HTTP',
|
||||
active: true,
|
||||
|
@ -97,6 +102,7 @@ export const destroyIntegrationResponse = {
|
|||
httpIntegrationDestroy: {
|
||||
errors: [],
|
||||
integration: {
|
||||
__typename: 'AlertManagementIntegration',
|
||||
id: '37',
|
||||
type: 'HTTP',
|
||||
active: true,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const defaultPageInfo = {
|
||||
__typename: 'PageInfo',
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: null,
|
||||
|
|
|
@ -129,7 +129,7 @@ describe('UsageTrendsCountChart', () => {
|
|||
const recordedAt = '2020-08-01';
|
||||
describe('when the fetchMore query returns data', () => {
|
||||
beforeEach(async () => {
|
||||
const newData = [{ recordedAt, count: 5 }];
|
||||
const newData = [{ __typename: 'UsageTrendsMeasurement', recordedAt, count: 5 }];
|
||||
queryHandler = mockQueryResponse({
|
||||
key: queryResponseDataKey,
|
||||
data: mockCountsData1,
|
||||
|
|
|
@ -4,6 +4,7 @@ import { shallowMount } from '@vue/test-utils';
|
|||
import Vue, { nextTick } from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import UsersChart from '~/analytics/usage_trends/components/users_chart.vue';
|
||||
import usersQuery from '~/analytics/usage_trends/graphql/queries/users.query.graphql';
|
||||
import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
|
||||
|
@ -86,7 +87,7 @@ describe('UsersChart', () => {
|
|||
describe('with data', () => {
|
||||
beforeEach(async () => {
|
||||
wrapper = createComponent({ users: mockCountsData2 });
|
||||
await nextTick();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('hides the skeleton loader', () => {
|
||||
|
@ -156,7 +157,7 @@ describe('UsersChart', () => {
|
|||
jest
|
||||
.spyOn(wrapper.vm.$apollo.queries.users, 'fetchMore')
|
||||
.mockImplementation(jest.fn().mockRejectedValue());
|
||||
await nextTick();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('calls fetchMore', () => {
|
||||
|
|
|
@ -4,11 +4,11 @@ export const mockUsageCounts = [
|
|||
];
|
||||
|
||||
export const mockCountsData1 = [
|
||||
{ recordedAt: '2020-07-23', count: 52 },
|
||||
{ recordedAt: '2020-07-22', count: 40 },
|
||||
{ recordedAt: '2020-07-21', count: 31 },
|
||||
{ recordedAt: '2020-06-14', count: 23 },
|
||||
{ recordedAt: '2020-06-12', count: 20 },
|
||||
{ __typename: 'UsageTrendsMeasurement', recordedAt: '2020-07-23', count: 52 },
|
||||
{ __typename: 'UsageTrendsMeasurement', recordedAt: '2020-07-22', count: 40 },
|
||||
{ __typename: 'UsageTrendsMeasurement', recordedAt: '2020-07-21', count: 31 },
|
||||
{ __typename: 'UsageTrendsMeasurement', recordedAt: '2020-06-14', count: 23 },
|
||||
{ __typename: 'UsageTrendsMeasurement', recordedAt: '2020-06-12', count: 20 },
|
||||
];
|
||||
|
||||
export const countsMonthlyChartData1 = [
|
||||
|
@ -17,11 +17,11 @@ export const countsMonthlyChartData1 = [
|
|||
];
|
||||
|
||||
export const mockCountsData2 = [
|
||||
{ recordedAt: '2020-07-28', count: 10 },
|
||||
{ recordedAt: '2020-07-27', count: 9 },
|
||||
{ recordedAt: '2020-06-26', count: 14 },
|
||||
{ recordedAt: '2020-06-25', count: 23 },
|
||||
{ recordedAt: '2020-06-24', count: 25 },
|
||||
{ __typename: 'UsageTrendsMeasurement', recordedAt: '2020-07-28', count: 10 },
|
||||
{ __typename: 'UsageTrendsMeasurement', recordedAt: '2020-07-27', count: 9 },
|
||||
{ __typename: 'UsageTrendsMeasurement', recordedAt: '2020-06-26', count: 14 },
|
||||
{ __typename: 'UsageTrendsMeasurement', recordedAt: '2020-06-25', count: 23 },
|
||||
{ __typename: 'UsageTrendsMeasurement', recordedAt: '2020-06-24', count: 25 },
|
||||
];
|
||||
|
||||
export const countsMonthlyChartData2 = [
|
||||
|
|
|
@ -106,10 +106,6 @@ describe('Board list component', () => {
|
|||
fetchItemsForList: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = createComponent();
|
||||
});
|
||||
|
||||
it('does not load issues if already loading', () => {
|
||||
wrapper = createComponent({
|
||||
actions,
|
||||
|
@ -143,6 +139,7 @@ describe('Board list component', () => {
|
|||
await nextTick();
|
||||
await waitForPromises();
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
|
||||
expect(wrapper.find('.board-list-count').text()).toBe('Showing 1 of 20 issues');
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ApolloLink, Observable } from 'apollo-link';
|
||||
import { ApolloLink, Observable } from '@apollo/client/core';
|
||||
|
||||
import { apolloCaptchaLink } from '~/captcha/apollo_captcha_link';
|
||||
import UnsolvedCaptchaError from '~/captcha/unsolved_captcha_error';
|
||||
|
|
|
@ -137,7 +137,8 @@ describe('ClusterAgentShow', () => {
|
|||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('displays agent create information with unknown user', () => {
|
||||
it('displays agent create information with unknown user', async () => {
|
||||
await waitForPromises();
|
||||
expect(findCreatedText()).toMatchInterpolatedText('Created by Unknown user 2 days ago');
|
||||
});
|
||||
});
|
||||
|
@ -153,19 +154,25 @@ describe('ClusterAgentShow', () => {
|
|||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('displays token header with no count', () => {
|
||||
it('displays token header with no count', async () => {
|
||||
await waitForPromises();
|
||||
expect(findTokenCount()).toMatchInterpolatedText(`${ClusterAgentShow.i18n.tokens}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the token list has additional pages', () => {
|
||||
const pageInfo = {
|
||||
const pageInfoResponse = {
|
||||
hasNextPage: true,
|
||||
hasPreviousPage: false,
|
||||
startCursor: 'prev',
|
||||
endCursor: 'next',
|
||||
};
|
||||
|
||||
const pageInfo = {
|
||||
...pageInfoResponse,
|
||||
__typename: 'PageInfo',
|
||||
};
|
||||
|
||||
const tokenPagination = {
|
||||
...defaultClusterAgent,
|
||||
tokens: {
|
||||
|
@ -184,7 +191,7 @@ describe('ClusterAgentShow', () => {
|
|||
});
|
||||
|
||||
it('should pass pageInfo to the pagination component', () => {
|
||||
expect(findPaginationButtons().props()).toMatchObject(pageInfo);
|
||||
expect(findPaginationButtons().props()).toMatchObject(pageInfoResponse);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue