Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9f0d276489
commit
f2fd07aa1c
90 changed files with 1064 additions and 512 deletions
|
@ -308,6 +308,10 @@ Rails/RakeEnvironment:
|
|||
# Context on why it's disabled: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93419#note_1048223982
|
||||
Enabled: false
|
||||
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96675#note_1094403693
|
||||
Rails/WhereExists:
|
||||
Enabled: false
|
||||
|
||||
# GitLab ###################################################################
|
||||
|
||||
Gitlab/ModuleWithInstanceVariables:
|
||||
|
|
|
@ -3920,7 +3920,6 @@ Layout/LineLength:
|
|||
- 'spec/features/groups/settings/repository_spec.rb'
|
||||
- 'spec/features/groups_spec.rb'
|
||||
- 'spec/features/ide/static_object_external_storage_csp_spec.rb'
|
||||
- 'spec/features/incidents/user_views_incident_spec.rb'
|
||||
- 'spec/features/invites_spec.rb'
|
||||
- 'spec/features/issuables/issuable_list_spec.rb'
|
||||
- 'spec/features/issuables/markdown_references/internal_references_spec.rb'
|
||||
|
|
|
@ -1,30 +1,28 @@
|
|||
---
|
||||
# Cop supports --auto-correct.
|
||||
Rails/NegateInclude:
|
||||
# Offense count: 65
|
||||
# Temporarily disabled due to too many offenses
|
||||
Enabled: false
|
||||
Details: grace period
|
||||
Exclude:
|
||||
- 'app/finders/projects_finder.rb'
|
||||
- 'app/helpers/application_settings_helper.rb'
|
||||
- 'app/helpers/projects_helper.rb'
|
||||
- 'app/helpers/tree_helper.rb'
|
||||
- 'app/models/concerns/timebox.rb'
|
||||
- 'app/models/integrations/chat_message/pipeline_message.rb'
|
||||
- 'app/models/integrations/field.rb'
|
||||
- 'app/models/label.rb'
|
||||
- 'app/models/merge_request.rb'
|
||||
- 'app/models/milestone.rb'
|
||||
- 'app/services/todo_service.rb'
|
||||
- 'app/services/work_items/parent_links/create_service.rb'
|
||||
- 'config/application.rb'
|
||||
- 'config/initializers/1_settings.rb'
|
||||
- 'danger/roulette/Dangerfile'
|
||||
- 'ee/app/finders/security/pipeline_vulnerabilities_finder.rb'
|
||||
- 'ee/app/models/ee/epic.rb'
|
||||
- 'ee/app/models/ee/vulnerability.rb'
|
||||
- 'ee/app/services/epic_issues/create_service.rb'
|
||||
- 'ee/app/services/security/ingestion/tasks/ingest_remediations.rb'
|
||||
- 'ee/app/services/security/security_orchestration_policies/validate_policy_service.rb'
|
||||
- 'lib/api/maven_packages.rb'
|
||||
- 'lib/generators/gitlab/usage_metric_generator.rb'
|
||||
- 'lib/gitlab/background_migration/legacy_upload_mover.rb'
|
||||
- 'lib/gitlab/ci/build/rules/rule/clause/exists.rb'
|
||||
- 'lib/gitlab/ci/parsers/coverage/sax_document.rb'
|
||||
|
@ -38,11 +36,10 @@ Rails/NegateInclude:
|
|||
- 'lib/gitlab/task_helpers.rb'
|
||||
- 'lib/gitlab/url_blocker.rb'
|
||||
- 'lib/gitlab_edition.rb'
|
||||
- 'qa/qa/page/merge_request/show.rb'
|
||||
- 'qa/qa/runtime/ip_address.rb'
|
||||
- 'qa/qa/support/run.rb'
|
||||
- 'qa/qa/tools/delete_test_users.rb'
|
||||
- 'qa/qa/vendor/jenkins/page/configure_job.rb'
|
||||
- 'qa/qa/vendor/jenkins/page/last_job_console.rb'
|
||||
- 'rubocop/cop/gitlab/feature_available_usage.rb'
|
||||
- 'rubocop/cop/graphql/id_type.rb'
|
||||
- 'rubocop/cop/migration/add_reference.rb'
|
||||
|
@ -56,3 +53,4 @@ Rails/NegateInclude:
|
|||
- 'spec/support/matchers/pushed_frontend_feature_flags_matcher.rb'
|
||||
- 'spec/support/shared_contexts/markdown_golden_master_shared_examples.rb'
|
||||
- 'spec/uploaders/object_storage_spec.rb'
|
||||
- 'tooling/danger/specs.rb'
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
---
|
||||
# Cop supports --auto-correct.
|
||||
Rails/WhereExists:
|
||||
# Offense count: 48
|
||||
# Temporarily disabled due to too many offenses
|
||||
Enabled: false
|
||||
Exclude:
|
||||
- 'app/models/application_setting/term.rb'
|
||||
- 'app/models/ci/pipeline_artifact.rb'
|
||||
- 'app/models/ci/ref.rb'
|
||||
- 'app/models/clusters/agent.rb'
|
||||
- 'app/models/concerns/has_wiki.rb'
|
||||
- 'app/models/concerns/noteable.rb'
|
||||
- 'app/models/container_repository.rb'
|
||||
- 'app/models/design_management/design.rb'
|
||||
- 'app/models/group.rb'
|
||||
- 'app/models/group_deploy_token.rb'
|
||||
- 'app/models/label.rb'
|
||||
- 'app/models/lfs_object.rb'
|
||||
- 'app/models/merge_request_diff.rb'
|
||||
- 'app/models/namespace.rb'
|
||||
- 'app/models/project.rb'
|
||||
- 'app/models/protected_branch/push_access_level.rb'
|
||||
- 'app/services/projects/transfer_service.rb'
|
||||
- 'app/services/todos/destroy/unauthorized_features_service.rb'
|
||||
- 'db/migrate/20210422195929_create_elastic_reindexing_slices.rb'
|
||||
- 'ee/app/models/approval_merge_request_rule_source.rb'
|
||||
- 'ee/app/models/concerns/ee/protected_ref_access.rb'
|
||||
- 'ee/app/models/ee/epic.rb'
|
||||
- 'ee/app/models/ee/group_member.rb'
|
||||
- 'ee/app/models/ee/milestone_release.rb'
|
||||
- 'ee/app/models/geo_node.rb'
|
||||
- 'ee/app/models/merge_requests/external_status_check.rb'
|
||||
- 'ee/app/models/merge_train.rb'
|
||||
- 'ee/app/workers/concerns/elastic/indexing_control.rb'
|
||||
- 'lib/gitlab/auth.rb'
|
||||
- 'lib/gitlab/checks/matching_merge_request.rb'
|
||||
- 'lib/gitlab/database/partitioning/detached_partition_dropper.rb'
|
||||
- 'spec/lib/bulk_imports/projects/pipelines/snippets_repository_pipeline_spec.rb'
|
||||
- 'spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_features_spec.rb'
|
||||
- 'spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb'
|
||||
- 'spec/models/user_spec.rb'
|
||||
- 'spec/services/clusters/cleanup/service_account_service_spec.rb'
|
||||
- 'spec/services/clusters/destroy_service_spec.rb'
|
|
@ -1 +1 @@
|
|||
b55578ec476e8bc8ecd9775ee7e9960b52e0f6e0
|
||||
f75740430e51520d3edcd22065285cec050d2b74
|
||||
|
|
|
@ -11,7 +11,7 @@ export default {
|
|||
BoardSettingsSidebar,
|
||||
BoardTopBar,
|
||||
},
|
||||
inject: ['disabled'],
|
||||
inject: ['disabled', 'fullBoardId'],
|
||||
computed: {
|
||||
...mapGetters(['isSidebarOpen']),
|
||||
},
|
||||
|
@ -27,7 +27,7 @@ export default {
|
|||
<template>
|
||||
<div class="boards-app gl-relative" :class="{ 'is-compact': isSidebarOpen }">
|
||||
<board-top-bar />
|
||||
<board-content :disabled="disabled" />
|
||||
<board-content :disabled="disabled" :board-id="fullBoardId" />
|
||||
<board-settings-sidebar />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -3,12 +3,24 @@ import { GlAlert } from '@gitlab/ui';
|
|||
import { sortBy, throttle } from 'lodash';
|
||||
import Draggable from 'vuedraggable';
|
||||
import { mapState, mapGetters, mapActions } from 'vuex';
|
||||
import { s__ } from '~/locale';
|
||||
import { formatBoardLists } from 'ee_else_ce/boards/boards_util';
|
||||
import BoardAddNewColumn from 'ee_else_ce/boards/components/board_add_new_column.vue';
|
||||
import { defaultSortableOptions } from '~/sortable/constants';
|
||||
import { DraggableItemTypes } from '../constants';
|
||||
import {
|
||||
DraggableItemTypes,
|
||||
issuableTypes,
|
||||
BoardType,
|
||||
listsQuery,
|
||||
} from 'ee_else_ce/boards/constants';
|
||||
import BoardColumn from './board_column.vue';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
fetchError: s__(
|
||||
'Boards|An error occurred while fetching the board lists. Please reload the page.',
|
||||
),
|
||||
},
|
||||
draggableItemTypes: DraggableItemTypes,
|
||||
components: {
|
||||
BoardAddNewColumn,
|
||||
|
@ -19,26 +31,76 @@ export default {
|
|||
EpicsSwimlanes: () => import('ee_component/boards/components/epics_swimlanes.vue'),
|
||||
GlAlert,
|
||||
},
|
||||
inject: ['canAdminList'],
|
||||
inject: ['canAdminList', 'boardType', 'fullPath', 'issuableType', 'isApolloBoard'],
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
boardId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
boardHeight: null,
|
||||
boardListsApollo: {},
|
||||
apolloError: null,
|
||||
updatedBoardId: this.boardId,
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
boardListsApollo: {
|
||||
query() {
|
||||
return listsQuery[this.issuableType].query;
|
||||
},
|
||||
variables() {
|
||||
return this.queryVariables;
|
||||
},
|
||||
skip() {
|
||||
return !this.isApolloBoard;
|
||||
},
|
||||
update(data) {
|
||||
const { lists } = data[this.boardType].board;
|
||||
return formatBoardLists(lists);
|
||||
},
|
||||
result() {
|
||||
// this allows us to delay fetching lists when we switch a board to fetch the actual board lists
|
||||
// instead of fetching lists for the "previous" board
|
||||
this.updatedBoardId = this.boardId;
|
||||
},
|
||||
error() {
|
||||
this.apolloError = this.$options.i18n.fetchError;
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['boardLists', 'error', 'addColumnForm']),
|
||||
...mapGetters(['isSwimlanesOn', 'isEpicBoard', 'isIssueBoard']),
|
||||
...mapGetters(['isSwimlanesOn']),
|
||||
isIssueBoard() {
|
||||
return this.issuableType === issuableTypes.issue;
|
||||
},
|
||||
isEpicBoard() {
|
||||
return this.issuableType === issuableTypes.epic;
|
||||
},
|
||||
addColumnFormVisible() {
|
||||
return this.addColumnForm?.visible;
|
||||
},
|
||||
queryVariables() {
|
||||
return {
|
||||
...(this.isIssueBoard && {
|
||||
isGroup: this.boardType === BoardType.group,
|
||||
isProject: this.boardType === BoardType.project,
|
||||
}),
|
||||
fullPath: this.fullPath,
|
||||
boardId: this.boardId,
|
||||
filterParams: this.filterParams,
|
||||
};
|
||||
},
|
||||
boardListsToUse() {
|
||||
return sortBy([...Object.values(this.boardLists)], 'position');
|
||||
const lists = this.isApolloBoard ? this.boardListsApollo : this.boardLists;
|
||||
return sortBy([...Object.values(lists)], 'position');
|
||||
},
|
||||
canDragColumns() {
|
||||
return this.canAdminList;
|
||||
|
@ -59,6 +121,9 @@ export default {
|
|||
|
||||
return this.canDragColumns ? options : {};
|
||||
},
|
||||
errorToDisplay() {
|
||||
return this.isApolloBoard ? this.apolloError : this.error;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.setBoardHeight();
|
||||
|
@ -88,8 +153,8 @@ export default {
|
|||
|
||||
<template>
|
||||
<div v-cloak data-qa-selector="boards_list">
|
||||
<gl-alert v-if="error" variant="danger" :dismissible="true" @dismiss="unsetError">
|
||||
{{ error }}
|
||||
<gl-alert v-if="errorToDisplay" variant="danger" :dismissible="true" @dismiss="unsetError">
|
||||
{{ errorToDisplay }}
|
||||
</gl-alert>
|
||||
<component
|
||||
:is="boardColumnWrapper"
|
||||
|
|
|
@ -53,6 +53,8 @@ function mountBoardApp(el) {
|
|||
store,
|
||||
apolloProvider,
|
||||
provide: {
|
||||
isApolloBoard: window.gon?.features?.apolloBoards,
|
||||
fullBoardId: fullBoardId(boardId),
|
||||
disabled: parseBoolean(el.dataset.disabled),
|
||||
groupId: Number(groupId),
|
||||
rootPath,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import $ from 'jquery';
|
||||
import issuableEventHub from '~/issues/list/eventhub';
|
||||
import LabelsSelect from '~/labels/labels_select';
|
||||
import MilestoneSelect from '~/milestones/milestone_select';
|
||||
import { mountMilestoneDropdown } from '~/sidebar/mount_sidebar';
|
||||
import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
|
||||
|
||||
const HIDDEN_CLASS = 'hidden';
|
||||
|
@ -55,7 +55,7 @@ export default class IssuableBulkUpdateSidebar {
|
|||
|
||||
initDropdowns() {
|
||||
new LabelsSelect();
|
||||
new MilestoneSelect();
|
||||
mountMilestoneDropdown();
|
||||
|
||||
// Checking IS_EE and using ee_else_ce is odd, but we do it here to satisfy
|
||||
// the import/no-unresolved lint rule when FOSS_ONLY=1, even though at
|
||||
|
|
|
@ -29,7 +29,7 @@ export default {
|
|||
dagDocPath: {
|
||||
default: null,
|
||||
},
|
||||
emptySvgPath: {
|
||||
emptyDagSvgPath: {
|
||||
default: '',
|
||||
},
|
||||
pipelineIid: {
|
||||
|
@ -213,7 +213,7 @@ export default {
|
|||
/>
|
||||
<gl-empty-state
|
||||
v-else-if="hasNoDependentJobs"
|
||||
:svg-path="emptySvgPath"
|
||||
:svg-path="emptyDagSvgPath"
|
||||
:title="$options.emptyStateTexts.title"
|
||||
>
|
||||
<template #description>
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
<script>
|
||||
import { GlBadge, GlTabs, GlTab } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import { failedJobsTabName, jobsTabName, needsTabName, testReportTabName } from '../constants';
|
||||
import PipelineGraphWrapper from './graph/graph_component_wrapper.vue';
|
||||
import Dag from './dag/dag.vue';
|
||||
import FailedJobsApp from './jobs/failed_jobs_app.vue';
|
||||
import JobsApp from './jobs/jobs_app.vue';
|
||||
import TestReports from './test_reports/test_reports.vue';
|
||||
import {
|
||||
failedJobsTabName,
|
||||
jobsTabName,
|
||||
needsTabName,
|
||||
pipelineTabName,
|
||||
testReportTabName,
|
||||
} from '../constants';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
|
@ -19,20 +20,16 @@ export default {
|
|||
},
|
||||
},
|
||||
tabNames: {
|
||||
pipeline: pipelineTabName,
|
||||
needs: needsTabName,
|
||||
jobs: jobsTabName,
|
||||
failures: failedJobsTabName,
|
||||
tests: testReportTabName,
|
||||
},
|
||||
components: {
|
||||
Dag,
|
||||
GlBadge,
|
||||
GlTab,
|
||||
GlTabs,
|
||||
JobsApp,
|
||||
FailedJobsApp,
|
||||
PipelineGraphWrapper,
|
||||
TestReports,
|
||||
},
|
||||
inject: [
|
||||
'defaultTabValue',
|
||||
|
@ -41,14 +38,27 @@ export default {
|
|||
'totalJobCount',
|
||||
'testsCount',
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
activeTab: this.defaultTabValue,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showFailedJobsTab() {
|
||||
return this.failedJobsCount > 0;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route(to) {
|
||||
this.activeTab = to.name;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isActive(tabName) {
|
||||
return tabName === this.defaultTabValue;
|
||||
return tabName === this.activeTab;
|
||||
},
|
||||
navigateTo(tabName) {
|
||||
this.$router.push({ name: tabName });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -59,10 +69,12 @@ export default {
|
|||
<gl-tab
|
||||
ref="pipelineTab"
|
||||
:title="$options.i18n.tabs.pipelineTitle"
|
||||
:active="isActive($options.tabNames.pipeline)"
|
||||
data-testid="pipeline-tab"
|
||||
lazy
|
||||
@click="navigateTo($options.tabNames.pipeline)"
|
||||
>
|
||||
<pipeline-graph-wrapper />
|
||||
<router-view />
|
||||
</gl-tab>
|
||||
<gl-tab
|
||||
ref="dagTab"
|
||||
|
@ -70,15 +82,21 @@ export default {
|
|||
:active="isActive($options.tabNames.needs)"
|
||||
data-testid="dag-tab"
|
||||
lazy
|
||||
@click="navigateTo($options.tabNames.needs)"
|
||||
>
|
||||
<dag />
|
||||
<router-view />
|
||||
</gl-tab>
|
||||
<gl-tab :active="isActive($options.tabNames.jobs)" data-testid="jobs-tab" lazy>
|
||||
<gl-tab
|
||||
:active="isActive($options.tabNames.jobs)"
|
||||
data-testid="jobs-tab"
|
||||
lazy
|
||||
@click="navigateTo($options.tabNames.jobs)"
|
||||
>
|
||||
<template #title>
|
||||
<span class="gl-mr-2">{{ $options.i18n.tabs.jobsTitle }}</span>
|
||||
<gl-badge size="sm" data-testid="builds-counter">{{ totalJobCount }}</gl-badge>
|
||||
</template>
|
||||
<jobs-app />
|
||||
<router-view />
|
||||
</gl-tab>
|
||||
<gl-tab
|
||||
v-if="showFailedJobsTab"
|
||||
|
@ -86,19 +104,25 @@ export default {
|
|||
:active="isActive($options.tabNames.failures)"
|
||||
data-testid="failed-jobs-tab"
|
||||
lazy
|
||||
@click="navigateTo($options.tabNames.failures)"
|
||||
>
|
||||
<template #title>
|
||||
<span class="gl-mr-2">{{ $options.i18n.tabs.failedJobsTitle }}</span>
|
||||
<gl-badge size="sm" data-testid="failed-builds-counter">{{ failedJobsCount }}</gl-badge>
|
||||
</template>
|
||||
<failed-jobs-app :failed-jobs-summary="failedJobsSummary" />
|
||||
<router-view :failed-jobs-summary="failedJobsSummary" />
|
||||
</gl-tab>
|
||||
<gl-tab :active="isActive($options.tabNames.tests)" data-testid="tests-tab" lazy>
|
||||
<gl-tab
|
||||
:active="isActive($options.tabNames.tests)"
|
||||
data-testid="tests-tab"
|
||||
lazy
|
||||
@click="navigateTo($options.tabNames.tests)"
|
||||
>
|
||||
<template #title>
|
||||
<span class="gl-mr-2">{{ $options.i18n.tabs.testsTitle }}</span>
|
||||
<gl-badge size="sm" data-testid="tests-counter">{{ testsCount }}</gl-badge>
|
||||
</template>
|
||||
<test-reports />
|
||||
<router-view />
|
||||
</gl-tab>
|
||||
<slot></slot>
|
||||
</gl-tabs>
|
||||
|
|
|
@ -47,8 +47,7 @@ export const CHILD_VIEW = 'child';
|
|||
|
||||
// Pipeline tabs
|
||||
|
||||
export const TAB_QUERY_PARAM = 'tab';
|
||||
|
||||
export const pipelineTabName = 'graph';
|
||||
export const needsTabName = 'dag';
|
||||
export const jobsTabName = 'builds';
|
||||
export const failedJobsTabName = 'failures';
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import VueRouter from 'vue-router';
|
||||
import { createAlert } from '~/flash';
|
||||
import { __, s__ } from '~/locale';
|
||||
import createDagApp from './pipeline_details_dag';
|
||||
|
@ -32,9 +33,16 @@ export default async function initPipelineDetailsBundle() {
|
|||
if (gon.features?.pipelineTabsVue) {
|
||||
const { createAppOptions } = await import('ee_else_ce/pipelines/pipeline_tabs');
|
||||
const { createPipelineTabs } = await import('./pipeline_tabs');
|
||||
const { routes } = await import('ee_else_ce/pipelines/routes');
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
base: dataset.pipelinePath,
|
||||
routes,
|
||||
});
|
||||
|
||||
try {
|
||||
const appOptions = createAppOptions(SELECTORS.PIPELINE_TABS, apolloProvider);
|
||||
const appOptions = createAppOptions(SELECTORS.PIPELINE_TABS, apolloProvider, router);
|
||||
createPipelineTabs(appOptions);
|
||||
} catch {
|
||||
createAlert({
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
import Vuex from 'vuex';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import PipelineTabs from 'ee_else_ce/pipelines/components/pipeline_tabs.vue';
|
||||
import { removeParams, updateHistory } from '~/lib/utils/url_utility';
|
||||
import { TAB_QUERY_PARAM } from '~/pipelines/constants';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import createTestReportsStore from './stores/test_reports';
|
||||
import { getPipelineDefaultTab, reportToSentry } from './utils';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
Vue.use(VueRouter);
|
||||
Vue.use(Vuex);
|
||||
|
||||
export const createAppOptions = (selector, apolloProvider) => {
|
||||
export const createAppOptions = (selector, apolloProvider, router) => {
|
||||
const el = document.querySelector(selector);
|
||||
|
||||
if (!el) return null;
|
||||
|
@ -40,6 +40,7 @@ export const createAppOptions = (selector, apolloProvider) => {
|
|||
suiteEndpoint,
|
||||
blobPath,
|
||||
hasTestReport,
|
||||
emptyDagSvgPath,
|
||||
emptyStateImagePath,
|
||||
artifactsExpiredImagePath,
|
||||
isFullCodequalityReportAvailable,
|
||||
|
@ -65,6 +66,7 @@ export const createAppOptions = (selector, apolloProvider) => {
|
|||
}),
|
||||
},
|
||||
}),
|
||||
router,
|
||||
provide: {
|
||||
canGenerateCodequalityReports: parseBoolean(canGenerateCodequalityReports),
|
||||
codequalityReportDownloadPath,
|
||||
|
@ -91,6 +93,7 @@ export const createAppOptions = (selector, apolloProvider) => {
|
|||
suiteEndpoint,
|
||||
blobPath,
|
||||
hasTestReport,
|
||||
emptyDagSvgPath,
|
||||
emptyStateImagePath,
|
||||
artifactsExpiredImagePath,
|
||||
testsCount,
|
||||
|
@ -107,12 +110,6 @@ export const createAppOptions = (selector, apolloProvider) => {
|
|||
export const createPipelineTabs = (options) => {
|
||||
if (!options) return;
|
||||
|
||||
updateHistory({
|
||||
url: removeParams([TAB_QUERY_PARAM]),
|
||||
title: document.title,
|
||||
replace: true,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue(options);
|
||||
};
|
||||
|
|
20
app/assets/javascripts/pipelines/routes.js
Normal file
20
app/assets/javascripts/pipelines/routes.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import PipelineGraphWrapper from './components/graph/graph_component_wrapper.vue';
|
||||
import Dag from './components/dag/dag.vue';
|
||||
import FailedJobsApp from './components/jobs/failed_jobs_app.vue';
|
||||
import JobsApp from './components/jobs/jobs_app.vue';
|
||||
import TestReports from './components/test_reports/test_reports.vue';
|
||||
import {
|
||||
pipelineTabName,
|
||||
needsTabName,
|
||||
jobsTabName,
|
||||
failedJobsTabName,
|
||||
testReportTabName,
|
||||
} from './constants';
|
||||
|
||||
export const routes = [
|
||||
{ name: pipelineTabName, path: '/', component: PipelineGraphWrapper },
|
||||
{ name: needsTabName, path: '/dag', component: Dag },
|
||||
{ name: jobsTabName, path: '/builds', component: JobsApp },
|
||||
{ name: failedJobsTabName, path: '/failures', component: FailedJobsApp },
|
||||
{ name: testReportTabName, path: '/test_report', component: TestReports },
|
||||
];
|
|
@ -1,12 +1,7 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import { pickBy } from 'lodash';
|
||||
import { getParameterValues } from '~/lib/utils/url_utility';
|
||||
import {
|
||||
NEEDS_PROPERTY,
|
||||
SUPPORTED_FILTER_PARAMETERS,
|
||||
TAB_QUERY_PARAM,
|
||||
validPipelineTabNames,
|
||||
} from './constants';
|
||||
import { parseUrlPathname } from '~/lib/utils/url_utility';
|
||||
import { NEEDS_PROPERTY, SUPPORTED_FILTER_PARAMETERS, validPipelineTabNames } from './constants';
|
||||
/*
|
||||
The following functions are the main engine in transforming the data as
|
||||
received from the endpoint into the format the d3 graph expects.
|
||||
|
@ -145,10 +140,12 @@ export const reportMessageToSentry = (component, message, context) => {
|
|||
};
|
||||
|
||||
export const getPipelineDefaultTab = (url) => {
|
||||
const [tabQueryValue] = getParameterValues(TAB_QUERY_PARAM, url);
|
||||
const strippedUrl = parseUrlPathname(url);
|
||||
const regexp = /\w*$/;
|
||||
const [tabName] = strippedUrl.match(regexp);
|
||||
|
||||
if (tabQueryValue && validPipelineTabNames.includes(tabQueryValue)) {
|
||||
return tabQueryValue;
|
||||
if (tabName && validPipelineTabNames.includes(tabName)) {
|
||||
return tabName;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<script>
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { IssuableType, WorkspaceType } from '~/issues/constants';
|
||||
import { __ } from '~/locale';
|
||||
import { IssuableAttributeType } from '../../constants';
|
||||
import SidebarDropdown from '../sidebar_dropdown.vue';
|
||||
|
||||
const noMilestone = {
|
||||
id: 0,
|
||||
title: __('No milestone'),
|
||||
};
|
||||
|
||||
const placeholderMilestone = {
|
||||
id: -1,
|
||||
title: __('Select milestone'),
|
||||
};
|
||||
|
||||
export default {
|
||||
issuableAttribute: IssuableAttributeType.Milestone,
|
||||
components: {
|
||||
SidebarDropdown,
|
||||
},
|
||||
props: {
|
||||
attrWorkspacePath: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
issuableType: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator(value) {
|
||||
return [IssuableType.Issue, IssuableType.MergeRequest].includes(value);
|
||||
},
|
||||
},
|
||||
workspaceType: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator(value) {
|
||||
return [WorkspaceType.group, WorkspaceType.project].includes(value);
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
milestone: placeholderMilestone,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
value() {
|
||||
return this.milestone.id === placeholderMilestone.id
|
||||
? undefined
|
||||
: getIdFromGraphQLId(this.milestone.id);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleChange(milestone) {
|
||||
this.milestone = milestone.id === null ? noMilestone : milestone;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<input type="hidden" name="update[milestone_id]" :value="value" />
|
||||
<sidebar-dropdown
|
||||
:attr-workspace-path="attrWorkspacePath"
|
||||
:current-attribute="milestone"
|
||||
:issuable-attribute="$options.issuableAttribute"
|
||||
:issuable-type="issuableType"
|
||||
:workspace-type="workspaceType"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
|
@ -8,7 +8,7 @@ import {
|
|||
GlSearchBoxByType,
|
||||
} from '@gitlab/ui';
|
||||
import { kebabCase, snakeCase } from 'lodash';
|
||||
import { IssuableType } from '~/issues/constants';
|
||||
import { IssuableType, WorkspaceType } from '~/issues/constants';
|
||||
import { __ } from '~/locale';
|
||||
import {
|
||||
defaultEpicSort,
|
||||
|
@ -73,6 +73,14 @@ export default {
|
|||
return [IssuableType.Issue, IssuableType.MergeRequest].includes(value);
|
||||
},
|
||||
},
|
||||
workspaceType: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: WorkspaceType.project,
|
||||
validator(value) {
|
||||
return [WorkspaceType.group, WorkspaceType.project].includes(value);
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -86,7 +94,7 @@ export default {
|
|||
query() {
|
||||
const { list } = this.issuableAttributeQuery;
|
||||
const { query } = list[this.issuableType];
|
||||
return query;
|
||||
return query[this.workspaceType] || query;
|
||||
},
|
||||
variables() {
|
||||
if (!this.isEpic) {
|
||||
|
|
|
@ -53,6 +53,7 @@ import updateIssueAssigneesMutation from '~/vue_shared/components/sidebar/querie
|
|||
import updateMergeRequestAssigneesMutation from '~/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql';
|
||||
import getEscalationStatusQuery from '~/sidebar/queries/escalation_status.query.graphql';
|
||||
import updateEscalationStatusMutation from '~/sidebar/queries/update_escalation_status.mutation.graphql';
|
||||
import groupMilestonesQuery from './queries/group_milestones.query.graphql';
|
||||
import projectIssueMilestoneMutation from './queries/project_issue_milestone.mutation.graphql';
|
||||
import projectIssueMilestoneQuery from './queries/project_issue_milestone.query.graphql';
|
||||
import projectMilestonesQuery from './queries/project_milestones.query.graphql';
|
||||
|
@ -241,10 +242,16 @@ export const issuableMilestoneQueries = {
|
|||
|
||||
export const milestonesQueries = {
|
||||
[IssuableType.Issue]: {
|
||||
query: projectMilestonesQuery,
|
||||
query: {
|
||||
[WorkspaceType.group]: groupMilestonesQuery,
|
||||
[WorkspaceType.project]: projectMilestonesQuery,
|
||||
},
|
||||
},
|
||||
[IssuableType.MergeRequest]: {
|
||||
query: projectMilestonesQuery,
|
||||
query: {
|
||||
[WorkspaceType.group]: groupMilestonesQuery,
|
||||
[WorkspaceType.project]: projectMilestonesQuery,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import CollapsedAssigneeList from '~/sidebar/components/assignees/collapsed_assi
|
|||
import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue';
|
||||
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
|
||||
import SidebarDueDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue';
|
||||
import BulkUpdateMilestoneDropdown from '~/sidebar/components/milestone/bulk_update_milestone_dropdown.vue';
|
||||
import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue';
|
||||
import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue';
|
||||
import SidebarDropdownWidget from '~/sidebar/components/sidebar_dropdown_widget.vue';
|
||||
|
@ -289,6 +290,31 @@ function mountMilestoneSelect() {
|
|||
});
|
||||
}
|
||||
|
||||
export function mountMilestoneDropdown() {
|
||||
const el = document.querySelector('.js-milestone-dropdown');
|
||||
|
||||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
name: 'BulkUpdateMilestoneDropdownRoot',
|
||||
apolloProvider,
|
||||
render(createElement) {
|
||||
return createElement(BulkUpdateMilestoneDropdown, {
|
||||
props: {
|
||||
attrWorkspacePath: el.dataset.fullPath,
|
||||
issuableType: isInIssuePage() ? IssuableType.Issue : IssuableType.MergeRequest,
|
||||
workspaceType: el.dataset.workspaceType,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function mountSidebarLabels() {
|
||||
const el = document.querySelector('.js-sidebar-labels');
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ class Groups::BoardsController < Groups::ApplicationController
|
|||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:board_multi_select, group)
|
||||
push_frontend_feature_flag(:apollo_boards, group)
|
||||
push_frontend_feature_flag(:realtime_labels, group)
|
||||
experiment(:prominent_create_board_btn, subject: current_user) do |e|
|
||||
e.control {}
|
||||
|
|
|
@ -7,6 +7,7 @@ class Projects::BoardsController < Projects::ApplicationController
|
|||
before_action :check_issues_available!
|
||||
before_action do
|
||||
push_frontend_feature_flag(:board_multi_select, project)
|
||||
push_frontend_feature_flag(:apollo_boards, project)
|
||||
push_frontend_feature_flag(:realtime_labels, project&.group)
|
||||
experiment(:prominent_create_board_btn, subject: current_user) do |e|
|
||||
e.control {}
|
||||
|
|
|
@ -140,21 +140,13 @@ class Projects::PipelinesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def builds
|
||||
if Feature.enabled?(:pipeline_tabs_vue, project)
|
||||
redirect_to pipeline_path(@pipeline, tab: 'builds')
|
||||
else
|
||||
render_show
|
||||
end
|
||||
render_show
|
||||
end
|
||||
|
||||
def dag
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
if Feature.enabled?(:pipeline_tabs_vue, project)
|
||||
redirect_to pipeline_path(@pipeline, tab: 'dag')
|
||||
else
|
||||
render_show
|
||||
end
|
||||
render_show
|
||||
end
|
||||
format.json do
|
||||
render json: Ci::DagPipelineSerializer
|
||||
|
@ -165,9 +157,7 @@ class Projects::PipelinesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def failures
|
||||
if Feature.enabled?(:pipeline_tabs_vue, project)
|
||||
redirect_to pipeline_path(@pipeline, tab: 'failures')
|
||||
elsif @pipeline.failed_builds.present?
|
||||
if @pipeline.failed_builds.present?
|
||||
render_show
|
||||
else
|
||||
redirect_to pipeline_path(@pipeline)
|
||||
|
@ -222,11 +212,7 @@ class Projects::PipelinesController < Projects::ApplicationController
|
|||
def test_report
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
if Feature.enabled?(:pipeline_tabs_vue, project)
|
||||
redirect_to pipeline_path(@pipeline, tab: 'test_report')
|
||||
else
|
||||
render_show
|
||||
end
|
||||
render_show
|
||||
end
|
||||
format.json do
|
||||
render json: TestReportSerializer
|
||||
|
|
|
@ -19,6 +19,7 @@ module Projects
|
|||
blob_path: project_blob_path(project, pipeline.sha),
|
||||
has_test_report: pipeline.has_test_reports?,
|
||||
empty_state_image_path: image_path('illustrations/empty-state/empty-test-cases-lg.svg'),
|
||||
empty_dag_svg_path: image_path('illustrations/empty-state/empty-dag-md.svg'),
|
||||
artifacts_expired_image_path: image_path('illustrations/pipeline.svg'),
|
||||
tests_count: pipeline.test_report_summary.total[:count]
|
||||
}
|
||||
|
|
|
@ -13,8 +13,7 @@ module AlertManagement
|
|||
key: Settings.attr_encrypted_db_key_base_32,
|
||||
algorithm: 'aes-256-gcm'
|
||||
|
||||
default_value_for(:endpoint_identifier, allows_nil: false) { SecureRandom.hex(8) }
|
||||
default_value_for(:token) { generate_token }
|
||||
attribute :endpoint_identifier, default: -> { SecureRandom.hex(8) }
|
||||
|
||||
validates :project, presence: true
|
||||
validates :active, inclusion: { in: [true, false] }
|
||||
|
|
|
@ -639,14 +639,6 @@ class ApplicationSetting < ApplicationRecord
|
|||
validates :inactive_projects_send_warning_email_after_months,
|
||||
numericality: { only_integer: true, greater_than: 0, less_than: :inactive_projects_delete_after_months }
|
||||
|
||||
validates :cube_api_base_url,
|
||||
addressable_url: { allow_localhost: true, allow_local_network: false },
|
||||
allow_blank: true
|
||||
|
||||
validates :product_analytics_enabled,
|
||||
presence: true,
|
||||
allow_blank: true
|
||||
|
||||
attr_encrypted :asset_proxy_secret_key,
|
||||
mode: :per_attribute_iv,
|
||||
key: Settings.attr_encrypted_db_key_base_truncated,
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
class ProjectImportEntity < ProjectEntity
|
||||
include ImportHelper
|
||||
|
||||
expose :import_source
|
||||
expose :import_status
|
||||
expose :human_import_status_name
|
||||
expose :import_source, documentation: { type: 'string', example: 'source/source-repo' }
|
||||
expose :import_status, documentation: {
|
||||
type: 'string', example: 'scheduled', values: %w[scheduled started finished failed canceled]
|
||||
}
|
||||
expose :human_import_status_name, documentation: { type: 'string', example: 'canceled' }
|
||||
|
||||
expose :provider_link do |project, options|
|
||||
expose :provider_link, documentation: { type: 'string', example: '/source/source-repo' } do |project, options|
|
||||
provider_project_link_url(options[:provider_url], project[:import_source])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -119,6 +119,7 @@
|
|||
= render_if_exists 'admin/application_settings/feishu_integration'
|
||||
= render 'admin/application_settings/third_party_offers'
|
||||
= render 'admin/application_settings/snowplow'
|
||||
= render_if_exists 'admin/application_settings/product_analytics' if Feature.enabled?(:cube_api_proxy)
|
||||
= render 'admin/application_settings/error_tracking' if Feature.enabled?(:gitlab_error_tracking)
|
||||
= render 'admin/application_settings/eks'
|
||||
= render 'admin/application_settings/floc'
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
= c.body do
|
||||
- enable_service_ping_link_url = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'enable-or-disable-usage-statistics')
|
||||
- enable_service_ping_link = '<a href="%{url}">'.html_safe % { url: enable_service_ping_link_url }
|
||||
- generate_manually_link_url = help_page_path('administration/troubleshooting/gitlab_rails_cheat_sheet', anchor: 'generate-service-ping')
|
||||
- generate_manually_link_url = help_page_path('development/service_ping/troubleshooting', anchor: 'generate-service-ping')
|
||||
- generate_manually_link = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: generate_manually_link_url }
|
||||
|
||||
= html_escape(s_('%{enable_service_ping_link_start}Enable%{link_end} or %{generate_manually_link_start}generate%{link_end} Service Ping to preview and download service usage data payload.')) % { enable_service_ping_link_start: enable_service_ping_link, generate_manually_link_start: generate_manually_link, link_end: '</a>'.html_safe }
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
= gl_tab_link_to s_('Branches|Overview'), project_branches_path(@project), { item_active: @mode == 'overview', title: s_('Branches|Show overview of the branches') }
|
||||
= gl_tab_link_to s_('Branches|Active'), project_branches_filtered_path(@project, state: 'active'), { title: s_('Branches|Show active branches') }
|
||||
= gl_tab_link_to s_('Branches|Stale'), project_branches_filtered_path(@project, state: 'stale'), { title: s_('Branches|Show stale branches') }
|
||||
= gl_tab_link_to s_('Branches|All'), project_branches_filtered_path(@project, state: 'all'), { item_active: !%w[overview active stale].include?(@mode), title: s_('Branches|Show all branches') }
|
||||
= gl_tab_link_to s_('Branches|All'), project_branches_filtered_path(@project, state: 'all'), { item_active: %w[overview active stale].exclude?(@mode), title: s_('Branches|Show all branches') }
|
||||
|
||||
.nav-controls
|
||||
#js-branches-sort-dropdown{ data: { project_branches_filtered_path: project_branches_path(@project, state: 'all'), sort_options: branches_sort_options_hash.to_json, mode: @mode } }
|
||||
|
|
|
@ -30,4 +30,4 @@
|
|||
#js-pipeline-tabs{ data: js_pipeline_tabs_data(@project, @pipeline, @current_user) }
|
||||
- else
|
||||
= render "projects/pipelines/with_tabs", pipeline: @pipeline, stages: @stages, pipeline_has_errors: pipeline_has_errors
|
||||
.js-pipeline-details-vue{ data: { metrics_path: namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: @project.namespace, project_id: @project, format: :json), pipeline_project_path: @project.full_path, pipeline_iid: @pipeline.iid, graphql_resource_etag: graphql_etag_pipeline_path(@pipeline) } }
|
||||
.js-pipeline-details-vue{ data: { metrics_path: namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: @project.namespace, project_id: @project, format: :json), pipeline_project_path: @project.full_path, pipeline_iid: @pipeline.iid, graphql_resource_etag: graphql_etag_pipeline_path(@pipeline), pipeline_path: pipeline_path(@pipeline) } }
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
.title
|
||||
= _('Milestone')
|
||||
.filter-item
|
||||
= dropdown_tag(_("Select milestone"), options: { title: _("Assign milestone"), toggle_class: "js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone", placeholder: _("Search milestones"), data: { show_no: true, field_name: "update[milestone_id]", project_id: @project.id, use_id: true, default_label: _("Milestone") } })
|
||||
.js-milestone-dropdown{ data: { full_path: @project.full_path, workspace_type: Namespaces::ProjectNamespace.sti_name.downcase } }
|
||||
- if is_issue
|
||||
= render_if_exists 'shared/issuable/iterations_dropdown', parent: @project.group
|
||||
- if is_issue
|
||||
|
|
8
config/feature_flags/development/apollo_boards.yml
Normal file
8
config/feature_flags/development/apollo_boards.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: apollo_boards
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102719
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381210
|
||||
milestone: '15.6'
|
||||
type: development
|
||||
group: group::product planning
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: enable_new_sentry_clientside_integration
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102650
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344832
|
||||
milestone: '15.6'
|
||||
type: development
|
||||
group: group::runner
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: enable_old_sentry_clientside_integration
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102650
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344832
|
||||
milestone: '15.6'
|
||||
type: development
|
||||
group: group::runner
|
||||
default_enabled: true
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: search_index_curation
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98809
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/375274
|
||||
milestone: '15.6'
|
||||
type: development
|
||||
group: group::global search
|
||||
default_enabled: false
|
|
@ -45,6 +45,8 @@ metadata:
|
|||
description: Operations related to project hooks
|
||||
- name: project_import_bitbucket
|
||||
description: Operations related to import BitBucket projects
|
||||
- name: project_import_github
|
||||
description: Operations related to import GitHub projects
|
||||
- name: release_links
|
||||
description: Operations related to release assets (links)
|
||||
- name: releases
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveUsersForeignKeyToProjects < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
remove_foreign_key_if_exists :projects, column: :creator_id
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_foreign_key :projects, :users, column: :creator_id, on_delete: :nullify, validate: false
|
||||
end
|
||||
end
|
1
db/schema_migrations/20221104094042
Normal file
1
db/schema_migrations/20221104094042
Normal file
|
@ -0,0 +1 @@
|
|||
7ddb85c1acfd3fbeddbe96857d329ad09cd21210e6765ff36d4b9f516a7c10be
|
|
@ -32626,9 +32626,6 @@ ALTER TABLE ONLY service_desk_settings
|
|||
ALTER TABLE ONLY design_management_designs_versions
|
||||
ADD CONSTRAINT fk_03c671965c FOREIGN KEY (design_id) REFERENCES design_management_designs(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY projects
|
||||
ADD CONSTRAINT fk_03ec10b0d3 FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE SET NULL NOT VALID;
|
||||
|
||||
ALTER TABLE ONLY issues
|
||||
ADD CONSTRAINT fk_05f1e72feb FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||
|
||||
|
|
|
@ -232,12 +232,9 @@ who are aware of the risks.
|
|||
- [Troubleshooting Kubernetes](https://docs.gitlab.com/charts/troubleshooting/kubernetes_cheat_sheet.html)
|
||||
- [Troubleshooting PostgreSQL](troubleshooting/postgresql.md)
|
||||
- [Guide to test environments](troubleshooting/test_environments.md) (for Support Engineers)
|
||||
- [GitLab Rails console commands](troubleshooting/gitlab_rails_cheat_sheet.md) (for Support Engineers)
|
||||
- [Troubleshooting SSL](troubleshooting/ssl.md)
|
||||
- Related links:
|
||||
- [GitLab Developer Documentation](../development/index.md)
|
||||
- [Repairing and recovering broken Git repositories](https://git.seveas.net/repairing-and-recovering-broken-git-repositories.html)
|
||||
- [Testing with OpenSSL](https://www.feistyduck.com/library/openssl-cookbook/online/testing-with-openssl/index.html)
|
||||
- [`strace` zine](https://wizardzines.com/zines/strace/)
|
||||
- GitLab.com-specific resources:
|
||||
- [Example group SAML and SCIM configurations](../user/group/saml_sso/example_saml_config.md)
|
||||
|
|
|
@ -1,91 +1,11 @@
|
|||
---
|
||||
stage: Systems
|
||||
group: Distribution
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
redirect_to: 'index.md'
|
||||
remove_date: '2023-02-01'
|
||||
---
|
||||
|
||||
# GitLab Rails Console Cheat Sheet **(FREE SELF)**
|
||||
This document was moved to [another location](index.md).
|
||||
|
||||
This is the GitLab Support Team's collection of information regarding the GitLab Rails
|
||||
console, for use while troubleshooting. It is listed here for transparency,
|
||||
and for users with experience with these tools. If you are currently
|
||||
having an issue with GitLab, it is highly recommended that you first check
|
||||
our guide on [our Rails console](../operations/rails_console.md),
|
||||
and your [support options](https://about.gitlab.com/support/), before attempting to use
|
||||
this information.
|
||||
|
||||
WARNING:
|
||||
Some of these scripts could be damaging if not run correctly,
|
||||
or under the right conditions. We highly recommend running them under the
|
||||
guidance of a Support Engineer, or running them in a test environment with a
|
||||
backup of the instance ready to be restored, just in case.
|
||||
|
||||
WARNING:
|
||||
As GitLab changes, changes to the code are inevitable,
|
||||
and so some scripts may not work as they once used to. These are not kept
|
||||
up-to-date as these scripts/commands were added as they were found/needed. As
|
||||
mentioned above, we recommend running these scripts under the supervision of a
|
||||
Support Engineer, who can also verify that they continue to work as they
|
||||
should and, if needed, update the script for the latest version of GitLab.
|
||||
|
||||
## Mirrors
|
||||
|
||||
### Find mirrors with "bad decrypt" errors
|
||||
|
||||
This content has been converted to a Rake task, see [verify database values can be decrypted using the current secrets](../raketasks/check.md#verify-database-values-can-be-decrypted-using-the-current-secrets).
|
||||
|
||||
### Transfer mirror users and tokens to a single service account
|
||||
|
||||
This content has been moved to [Troubleshooting Repository mirroring](../../user/project/repository/mirror/index.md#transfer-mirror-users-and-tokens-to-a-single-service-account-in-rails-console).
|
||||
|
||||
## Merge requests
|
||||
|
||||
## CI
|
||||
|
||||
This content has been moved to [Troubleshooting CI/CD](../../ci/troubleshooting.md).
|
||||
|
||||
## License
|
||||
|
||||
This content has been moved to [Activate GitLab EE with a license file or key](../../user/admin_area/license_file.md).
|
||||
|
||||
## Registry
|
||||
|
||||
### Registry Disk Space Usage by Project
|
||||
|
||||
Find this content in the [Container Registry troubleshooting documentation](../packages/container_registry.md#registry-disk-space-usage-by-project).
|
||||
|
||||
### Run the Cleanup policy now
|
||||
|
||||
Find this content in the [Container Registry troubleshooting documentation](../packages/container_registry.md#run-the-cleanup-policy-now).
|
||||
|
||||
## Sidekiq
|
||||
|
||||
This content has been moved to [Troubleshooting Sidekiq](../sidekiq/sidekiq_troubleshooting.md).
|
||||
|
||||
## Geo
|
||||
|
||||
### Reverify all uploads (or any SSF data type which is verified)
|
||||
|
||||
Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#reverify-all-uploads-or-any-ssf-data-type-which-is-verified).
|
||||
|
||||
### Artifacts
|
||||
|
||||
Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#find-failed-artifacts).
|
||||
|
||||
### Repository verification failures
|
||||
|
||||
Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#find-repository-verification-failures).
|
||||
|
||||
### Resync repositories
|
||||
|
||||
Moved to [Geo replication troubleshooting - Resync repository types except for project or project wiki repositories](../geo/replication/troubleshooting.md#repository-types-except-for-project-or-project-wiki-repositories).
|
||||
|
||||
Moved to [Geo replication troubleshooting - Resync project and project wiki repositories](../geo/replication/troubleshooting.md#resync-project-and-project-wiki-repositories).
|
||||
|
||||
### Blob types
|
||||
|
||||
Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#blob-types).
|
||||
|
||||
## Generate Service Ping
|
||||
|
||||
This content has been moved to [Service Ping Troubleshooting](../../development/service_ping/troubleshooting.md).
|
||||
<!-- This redirect file can be deleted after 2023-02-01. -->
|
||||
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
|
||||
|
|
|
@ -9,19 +9,21 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
This page documents a collection of resources to help you troubleshoot a GitLab
|
||||
installation.
|
||||
|
||||
This list is not necessarily comprehensive. If you don't find what you're looking
|
||||
for in this list, you should search the documentation.
|
||||
|
||||
## Troubleshooting guides
|
||||
|
||||
- [SSL](ssl.md)
|
||||
- [Geo](../geo/replication/troubleshooting.md)
|
||||
- [GitLab Rails console cheat sheet](gitlab_rails_cheat_sheet.md)
|
||||
- [Example group SAML and SCIM configurations](../../user/group/saml_sso/example_saml_config.md)
|
||||
- [SAML](../../user/group/saml_sso/troubleshooting.md)
|
||||
- [Kubernetes cheat sheet](https://docs.gitlab.com/charts/troubleshooting/kubernetes_cheat_sheet.html)
|
||||
- [Linux cheat sheet](linux_cheat_sheet.md)
|
||||
- [Parsing GitLab logs with `jq`](../logs/log_parsing.md)
|
||||
- [Diagnostics tools](diagnostics_tools.md)
|
||||
|
||||
Some feature documentation pages also have a troubleshooting section at the end
|
||||
that you can check for feature-specific help.
|
||||
that you can check for feature-specific help, including helpful Rails commands.
|
||||
|
||||
If you need a testing environment to troubleshoot, see the
|
||||
[apps for a testing environment](test_environments.md).
|
||||
|
|
|
@ -190,6 +190,30 @@ If you add a member to a group by using the [share a group with another group](.
|
|||
- Remove the member from the shared group. You must be a group owner to do this.
|
||||
- From the group's membership page, remove access from the entire shared group.
|
||||
|
||||
## Seat usage alerts
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348481) in GitLab 15.2 [with a flag](../../administration/feature_flags.md) named `seat_flag_alerts`.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/362041) in GitLab 15.4. Feature flag `seat_flag_alerts` removed.
|
||||
|
||||
If you have the Owner role of the top-level group, an alert notifies you
|
||||
of your total seat usage.
|
||||
|
||||
The alert displays on group, subgroup, and project
|
||||
pages, and only for top-level groups linked to subscriptions enrolled
|
||||
in [quarterly subscription reconciliations](../quarterly_reconciliation.md).
|
||||
After you dismiss the alert, it doesn't display until another seat is used.
|
||||
|
||||
The alert displays based on the following seat usage. You cannot configure the
|
||||
amounts at which the alert displays.
|
||||
|
||||
| Seats in subscription | Seat usage |
|
||||
|-----------------------|------------------------------------------------------------------------|
|
||||
| 0-15 | One seat remaining in the subscription. |
|
||||
| 16-25 | Two seats remaining in the subscription. |
|
||||
| 26-99 | 10% of seats have been used. |
|
||||
| 100-999 | 8% of seats have been used. |
|
||||
| 1000+ | 5% of seats have been used |
|
||||
|
||||
## Upgrade your GitLab SaaS subscription tier
|
||||
|
||||
To upgrade your [GitLab tier](https://about.gitlab.com/pricing/):
|
||||
|
|
|
@ -187,6 +187,7 @@ module API
|
|||
mount ::API::FreezePeriods
|
||||
mount ::API::Keys
|
||||
mount ::API::ImportBitbucketServer
|
||||
mount ::API::ImportGithub
|
||||
mount ::API::Metadata
|
||||
mount ::API::MergeRequestDiffs
|
||||
mount ::API::ProjectHooks
|
||||
|
@ -261,7 +262,6 @@ module API
|
|||
mount ::API::GroupVariables
|
||||
mount ::API::Groups
|
||||
mount ::API::HelmPackages
|
||||
mount ::API::ImportGithub
|
||||
mount ::API::Integrations
|
||||
mount ::API::Integrations::JiraConnect::Subscriptions
|
||||
mount ::API::Invitations
|
||||
|
|
|
@ -6,15 +6,18 @@ module API
|
|||
include ::API::ProjectsRelationBuilder
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
expose :default_branch_or_main, as: :default_branch, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
|
||||
expose :default_branch_or_main, documentation: { type: 'string', example: 'main' }, as: :default_branch, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
|
||||
# Avoids an N+1 query: https://github.com/mbleigh/acts-as-taggable-on/issues/91#issuecomment-168273770
|
||||
|
||||
expose :topic_names, as: :tag_list
|
||||
expose :topic_names, as: :topics
|
||||
expose :topic_names, as: :tag_list, documentation: { type: 'string', is_array: true, example: 'tag' }
|
||||
expose :topic_names, as: :topics, documentation: { type: 'string', is_array: true, example: 'topic' }
|
||||
|
||||
expose :ssh_url_to_repo, :http_url_to_repo, :web_url, :readme_url
|
||||
expose :ssh_url_to_repo, documentation: { type: 'string', example: 'git@gitlab.example.com:gitlab/gitlab.git' }
|
||||
expose :http_url_to_repo, documentation: { type: 'string', example: 'https://gitlab.example.com/gitlab/gitlab.git' }
|
||||
expose :web_url, documentation: { type: 'string', example: 'https://gitlab.example.com/gitlab/gitlab' }
|
||||
expose :readme_url, documentation: { type: 'string', example: 'https://gitlab.example.com/gitlab/gitlab/blob/master/README.md' }
|
||||
|
||||
expose :license_url, if: :license do |project|
|
||||
expose :license_url, if: :license, documentation: { type: 'string', example: 'https://gitlab.example.com/gitlab/gitlab/blob/master/LICENCE' } do |project|
|
||||
license = project.repository.license_blob
|
||||
|
||||
if license
|
||||
|
@ -26,13 +29,13 @@ module API
|
|||
project.repository.license
|
||||
end
|
||||
|
||||
expose :avatar_url do |project, options|
|
||||
expose :avatar_url, documentation: { type: 'string', example: 'http://example.com/uploads/project/avatar/3/uploads/avatar.png' } do |project, options|
|
||||
project.avatar_url(only_path: false)
|
||||
end
|
||||
|
||||
expose :forks_count
|
||||
expose :star_count
|
||||
expose :last_activity_at
|
||||
expose :forks_count, documentation: { type: 'integer', example: 1 }
|
||||
expose :star_count, documentation: { type: 'integer', example: 1 }
|
||||
expose :last_activity_at, documentation: { type: 'dateTime', example: '2013-09-30T13:46:02Z' }
|
||||
expose :namespace, using: 'API::Entities::NamespaceBasic'
|
||||
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
module API
|
||||
module Entities
|
||||
class BasicRepositoryStorageMove < Grape::Entity
|
||||
expose :id
|
||||
expose :created_at
|
||||
expose :human_state_name, as: :state
|
||||
expose :source_storage_name
|
||||
expose :destination_storage_name
|
||||
expose :id, documentation: { type: 'integer', example: 1 }
|
||||
expose :created_at, documentation: { type: 'dateTime', example: '2020-05-07T04:27:17.234Z' }
|
||||
expose :human_state_name, as: :state, documentation: { type: 'string', example: 'scheduled' }
|
||||
expose :source_storage_name, documentation: { type: 'string', example: 'default' }
|
||||
expose :destination_storage_name, documentation: { type: 'string', example: 'storage1' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,148 +5,148 @@ module API
|
|||
class Project < BasicProjectDetails
|
||||
include ::API::Helpers::RelatedResourcesHelpers
|
||||
|
||||
expose :container_registry_url, as: :container_registry_image_prefix, if: -> (_, _) { Gitlab.config.registry.enabled }
|
||||
expose :container_registry_url, as: :container_registry_image_prefix, documentation: { type: 'string', example: 'registry.gitlab.example.com/gitlab/gitlab-client' }, if: -> (_, _) { Gitlab.config.registry.enabled }
|
||||
|
||||
expose :_links do
|
||||
expose :self do |project|
|
||||
expose :self, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/4' } do |project|
|
||||
expose_url(api_v4_projects_path(id: project.id))
|
||||
end
|
||||
|
||||
expose :issues, if: -> (project, options) { issues_available?(project, options) } do |project|
|
||||
expose :issues, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/4/issues' }, if: -> (project, options) { issues_available?(project, options) } do |project|
|
||||
expose_url(api_v4_projects_issues_path(id: project.id))
|
||||
end
|
||||
|
||||
expose :merge_requests, if: -> (project, options) { mrs_available?(project, options) } do |project|
|
||||
expose :merge_requests, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/4/merge_requests' }, if: -> (project, options) { mrs_available?(project, options) } do |project|
|
||||
expose_url(api_v4_projects_merge_requests_path(id: project.id))
|
||||
end
|
||||
|
||||
expose :repo_branches do |project|
|
||||
expose :repo_branches, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/4/repository/branches' } do |project|
|
||||
expose_url(api_v4_projects_repository_branches_path(id: project.id))
|
||||
end
|
||||
|
||||
expose :labels do |project|
|
||||
expose :labels, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/4/labels' } do |project|
|
||||
expose_url(api_v4_projects_labels_path(id: project.id))
|
||||
end
|
||||
|
||||
expose :events do |project|
|
||||
expose :events, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/4/events' } do |project|
|
||||
expose_url(api_v4_projects_events_path(id: project.id))
|
||||
end
|
||||
|
||||
expose :members do |project|
|
||||
expose :members, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/4/members' } do |project|
|
||||
expose_url(api_v4_projects_members_path(id: project.id))
|
||||
end
|
||||
|
||||
expose :cluster_agents do |project|
|
||||
expose :cluster_agents, documentation: { type: 'string', example: 'https://gitlab.example.com/api/v4/projects/4/cluster_agents' } do |project|
|
||||
expose_url(api_v4_projects_cluster_agents_path(id: project.id))
|
||||
end
|
||||
end
|
||||
|
||||
expose :packages_enabled
|
||||
expose :empty_repo?, as: :empty_repo
|
||||
expose :archived?, as: :archived
|
||||
expose :visibility
|
||||
expose :packages_enabled, documentation: { type: 'boolean' }
|
||||
expose :empty_repo?, as: :empty_repo, documentation: { type: 'boolean' }
|
||||
expose :archived?, as: :archived, documentation: { type: 'boolean' }
|
||||
expose :visibility, documentation: { type: 'string', example: 'public' }
|
||||
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
|
||||
expose :resolve_outdated_diff_discussions
|
||||
expose :resolve_outdated_diff_discussions, documentation: { type: 'boolean' }
|
||||
expose :container_expiration_policy,
|
||||
using: Entities::ContainerExpirationPolicy,
|
||||
if: -> (project, _) { project.container_expiration_policy }
|
||||
|
||||
# Expose old field names with the new permissions methods to keep API compatible
|
||||
# TODO: remove in API v5, replaced by *_access_level
|
||||
expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) }
|
||||
expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) }
|
||||
expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
|
||||
expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
|
||||
expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
|
||||
expose(:container_registry_enabled) { |project, options| project.feature_available?(:container_registry, options[:current_user]) }
|
||||
expose :service_desk_enabled
|
||||
expose :service_desk_address, if: -> (project, options) do
|
||||
expose(:issues_enabled, documentation: { type: 'boolean' }) { |project, options| project.feature_available?(:issues, options[:current_user]) }
|
||||
expose(:merge_requests_enabled, documentation: { type: 'boolean' }) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) }
|
||||
expose(:wiki_enabled, documentation: { type: 'boolean' }) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
|
||||
expose(:jobs_enabled, documentation: { type: 'boolean' }) { |project, options| project.feature_available?(:builds, options[:current_user]) }
|
||||
expose(:snippets_enabled, documentation: { type: 'boolean' }) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
|
||||
expose(:container_registry_enabled, documentation: { type: 'boolean' }) { |project, options| project.feature_available?(:container_registry, options[:current_user]) }
|
||||
expose :service_desk_enabled, documentation: { type: 'boolean' }
|
||||
expose :service_desk_address, documentation: { type: 'string', example: 'address@example.com' }, if: -> (project, options) do
|
||||
Ability.allowed?(options[:current_user], :admin_issue, project)
|
||||
end
|
||||
|
||||
expose(:can_create_merge_request_in) do |project, options|
|
||||
expose(:can_create_merge_request_in, documentation: { type: 'boolean' }) do |project, options|
|
||||
Ability.allowed?(options[:current_user], :create_merge_request_in, project)
|
||||
end
|
||||
|
||||
expose(:issues_access_level) { |project, options| project_feature_string_access_level(project, :issues) }
|
||||
expose(:repository_access_level) { |project, options| project_feature_string_access_level(project, :repository) }
|
||||
expose(:merge_requests_access_level) { |project, options| project_feature_string_access_level(project, :merge_requests) }
|
||||
expose(:forking_access_level) { |project, options| project_feature_string_access_level(project, :forking) }
|
||||
expose(:wiki_access_level) { |project, options| project_feature_string_access_level(project, :wiki) }
|
||||
expose(:builds_access_level) { |project, options| project_feature_string_access_level(project, :builds) }
|
||||
expose(:snippets_access_level) { |project, options| project_feature_string_access_level(project, :snippets) }
|
||||
expose(:pages_access_level) { |project, options| project_feature_string_access_level(project, :pages) }
|
||||
expose(:operations_access_level) { |project, options| project_feature_string_access_level(project, :operations) }
|
||||
expose(:analytics_access_level) { |project, options| project_feature_string_access_level(project, :analytics) }
|
||||
expose(:container_registry_access_level) { |project, options| project_feature_string_access_level(project, :container_registry) }
|
||||
expose(:security_and_compliance_access_level) { |project, options| project_feature_string_access_level(project, :security_and_compliance) }
|
||||
expose(:releases_access_level) { |project, options| project_feature_string_access_level(project, :releases) }
|
||||
expose(:issues_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :issues) }
|
||||
expose(:repository_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :repository) }
|
||||
expose(:merge_requests_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :merge_requests) }
|
||||
expose(:forking_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :forking) }
|
||||
expose(:wiki_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :wiki) }
|
||||
expose(:builds_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :builds) }
|
||||
expose(:snippets_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :snippets) }
|
||||
expose(:pages_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :pages) }
|
||||
expose(:operations_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :operations) }
|
||||
expose(:analytics_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :analytics) }
|
||||
expose(:container_registry_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :container_registry) }
|
||||
expose(:security_and_compliance_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :security_and_compliance) }
|
||||
expose(:releases_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :releases) }
|
||||
|
||||
expose :emails_disabled
|
||||
expose :shared_runners_enabled
|
||||
expose :lfs_enabled?, as: :lfs_enabled
|
||||
expose :creator_id
|
||||
expose :emails_disabled, documentation: { type: 'boolean' }
|
||||
expose :shared_runners_enabled, documentation: { type: 'boolean' }
|
||||
expose :lfs_enabled?, as: :lfs_enabled, documentation: { type: 'boolean' }
|
||||
expose :creator_id, documentation: { type: 'integer', example: 1 }
|
||||
expose :forked_from_project, using: Entities::BasicProjectDetails, if: ->(project, options) do
|
||||
project.forked? && Ability.allowed?(options[:current_user], :read_project, project.forked_from_project)
|
||||
end
|
||||
expose :mr_default_target_self, if: -> (project) { project.forked? }
|
||||
expose :mr_default_target_self, if: -> (project) { project.forked? }, documentation: { type: 'boolean' }
|
||||
|
||||
expose :import_url, if: -> (project, options) { Ability.allowed?(options[:current_user], :admin_project, project) } do |project|
|
||||
expose :import_url, documentation: { type: 'string', example: 'https://gitlab.com/gitlab/gitlab.git' }, if: -> (project, options) { Ability.allowed?(options[:current_user], :admin_project, project) } do |project|
|
||||
project[:import_url]
|
||||
end
|
||||
expose :import_type, if: -> (project, options) { Ability.allowed?(options[:current_user], :admin_project, project) }
|
||||
expose :import_status
|
||||
expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] } do |project|
|
||||
expose :import_type, documentation: { type: 'string', example: 'git' }, if: -> (project, options) { Ability.allowed?(options[:current_user], :admin_project, project) }
|
||||
expose :import_status, documentation: { type: 'string', example: 'none' }
|
||||
expose :import_error, documentation: { type: 'string', example: 'Import error' }, if: lambda { |_project, options| options[:user_can_admin_project] } do |project|
|
||||
project.import_state&.last_error
|
||||
end
|
||||
|
||||
expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) }
|
||||
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
|
||||
expose :ci_default_git_depth
|
||||
expose :ci_forward_deployment_enabled
|
||||
expose(:ci_job_token_scope_enabled) { |p, _| p.ci_outbound_job_token_scope_enabled? }
|
||||
expose :ci_separated_caches
|
||||
expose :ci_opt_in_jwt
|
||||
expose :ci_allow_fork_pipelines_to_run_in_parent_project
|
||||
expose :public_builds, as: :public_jobs
|
||||
expose :build_git_strategy, if: lambda { |project, options| options[:user_can_admin_project] } do |project, options|
|
||||
expose :open_issues_count, documentation: { type: 'integer', example: 1 }, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) }
|
||||
expose :runners_token, documentation: { type: 'string', example: 'b8547b1dc37721d05889db52fa2f02' }, if: lambda { |_project, options| options[:user_can_admin_project] }
|
||||
expose :ci_default_git_depth, documentation: { type: 'integer', example: 20 }
|
||||
expose :ci_forward_deployment_enabled, documentation: { type: 'boolean' }
|
||||
expose(:ci_job_token_scope_enabled, documentation: { type: 'boolean' }) { |p, _| p.ci_outbound_job_token_scope_enabled? }
|
||||
expose :ci_separated_caches, documentation: { type: 'boolean' }
|
||||
expose :ci_opt_in_jwt, documentation: { type: 'boolean' }
|
||||
expose :ci_allow_fork_pipelines_to_run_in_parent_project, documentation: { type: 'boolean' }
|
||||
expose :public_builds, as: :public_jobs, documentation: { type: 'boolean' }
|
||||
expose :build_git_strategy, documentation: { type: 'string', example: 'fetch' }, if: lambda { |project, options| options[:user_can_admin_project] } do |project, options|
|
||||
project.build_allow_git_fetch ? 'fetch' : 'clone'
|
||||
end
|
||||
expose :build_timeout
|
||||
expose :auto_cancel_pending_pipelines
|
||||
expose :ci_config_path, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
|
||||
expose :shared_with_groups do |project, options|
|
||||
expose :build_timeout, documentation: { type: 'integer', example: 3600 }
|
||||
expose :auto_cancel_pending_pipelines, documentation: { type: 'string', example: 'enabled' }
|
||||
expose :ci_config_path, documentation: { type: 'string', example: '' }, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
|
||||
expose :shared_with_groups, documentation: { is_array: true } do |project, options|
|
||||
user = options[:current_user]
|
||||
|
||||
SharedGroupWithProject.represent(project.visible_group_links(for_user: user), options)
|
||||
end
|
||||
expose :only_allow_merge_if_pipeline_succeeds
|
||||
expose :allow_merge_on_skipped_pipeline
|
||||
expose :restrict_user_defined_variables
|
||||
expose :request_access_enabled
|
||||
expose :only_allow_merge_if_all_discussions_are_resolved
|
||||
expose :remove_source_branch_after_merge
|
||||
expose :printing_merge_request_link_enabled
|
||||
expose :merge_method
|
||||
expose :squash_option
|
||||
expose :enforce_auth_checks_on_uploads
|
||||
expose :suggestion_commit_message
|
||||
expose :merge_commit_template
|
||||
expose :squash_commit_template
|
||||
expose :issue_branch_template
|
||||
expose :only_allow_merge_if_pipeline_succeeds, documentation: { type: 'boolean' }
|
||||
expose :allow_merge_on_skipped_pipeline, documentation: { type: 'boolean' }
|
||||
expose :restrict_user_defined_variables, documentation: { type: 'boolean' }
|
||||
expose :request_access_enabled, documentation: { type: 'boolean' }
|
||||
expose :only_allow_merge_if_all_discussions_are_resolved, documentation: { type: 'boolean' }
|
||||
expose :remove_source_branch_after_merge, documentation: { type: 'boolean' }
|
||||
expose :printing_merge_request_link_enabled, documentation: { type: 'boolean' }
|
||||
expose :merge_method, documentation: { type: 'string', example: 'merge' }
|
||||
expose :squash_option, documentation: { type: 'string', example: 'default_off' }
|
||||
expose :enforce_auth_checks_on_uploads, documentation: { type: 'boolean' }
|
||||
expose :suggestion_commit_message, documentation: { type: 'string', example: 'Suggestion message' }
|
||||
expose :merge_commit_template, documentation: { type: 'string', example: '%(title)' }
|
||||
expose :squash_commit_template, documentation: { type: 'string', example: '%(source_branch)' }
|
||||
expose :issue_branch_template, documentation: { type: 'string', example: '%(title)' }
|
||||
expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) {
|
||||
options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project)
|
||||
}
|
||||
expose :auto_devops_enabled?, as: :auto_devops_enabled
|
||||
expose :auto_devops_deploy_strategy do |project, options|
|
||||
expose :auto_devops_enabled?, as: :auto_devops_enabled, documentation: { type: 'boolean' }
|
||||
expose :auto_devops_deploy_strategy, documentation: { type: 'string', example: 'continuous' } do |project, options|
|
||||
project.auto_devops.nil? ? 'continuous' : project.auto_devops.deploy_strategy
|
||||
end
|
||||
expose :autoclose_referenced_issues
|
||||
expose :repository_storage, if: ->(project, options) {
|
||||
expose :autoclose_referenced_issues, documentation: { type: 'boolean' }
|
||||
expose :repository_storage, documentation: { type: 'string', example: 'default' }, if: ->(project, options) {
|
||||
Ability.allowed?(options[:current_user], :change_repository_storage, project)
|
||||
}
|
||||
expose :keep_latest_artifacts_available?, as: :keep_latest_artifact
|
||||
expose :runner_token_expiration_interval
|
||||
expose :keep_latest_artifacts_available?, as: :keep_latest_artifact, documentation: { type: 'boolean' }
|
||||
expose :runner_token_expiration_interval, documentation: { type: 'integer', example: 3600 }
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def self.preload_resource(project)
|
||||
|
|
|
@ -3,7 +3,11 @@
|
|||
module API
|
||||
module Entities
|
||||
class ProjectGroupLink < Grape::Entity
|
||||
expose :id, :project_id, :group_id, :group_access, :expires_at
|
||||
expose :id, documentation: { type: 'integer', example: 1 }
|
||||
expose :project_id, documentation: { type: 'integer', example: 1 }
|
||||
expose :group_id, documentation: { type: 'integer', example: 1 }
|
||||
expose :group_access, documentation: { type: 'integer', example: 10 }
|
||||
expose :expires_at, documentation: { type: 'date', example: '2016-09-26' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,10 +3,13 @@
|
|||
module API
|
||||
module Entities
|
||||
class ProjectIdentity < Grape::Entity
|
||||
expose :id, :description
|
||||
expose :name, :name_with_namespace
|
||||
expose :path, :path_with_namespace
|
||||
expose :created_at
|
||||
expose :id, documentation: { type: 'integer', example: 1 }
|
||||
expose :description, documentation: { type: 'string', example: 'desc' }
|
||||
expose :name, documentation: { type: 'string', example: 'project1' }
|
||||
expose :name_with_namespace, documentation: { type: 'string', example: 'John Doe / project1' }
|
||||
expose :path, documentation: { type: 'string', example: 'project1' }
|
||||
expose :path_with_namespace, documentation: { type: 'string', example: 'namespace1/project1' }
|
||||
expose :created_at, documentation: { type: 'dateTime', example: '2020-05-07T04:27:17.016Z' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,12 +5,16 @@ module API
|
|||
class ProjectRepositoryStorage < Grape::Entity
|
||||
include Gitlab::Routing
|
||||
|
||||
expose :disk_path do |project|
|
||||
expose :disk_path, documentation: {
|
||||
type: 'string',
|
||||
example: '@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b'
|
||||
} do |project|
|
||||
project.repository.disk_path
|
||||
end
|
||||
|
||||
expose :id, as: :project_id
|
||||
expose :repository_storage, :created_at
|
||||
expose :id, as: :project_id, documentation: { type: 'integer', example: 1 }
|
||||
expose :repository_storage, documentation: { type: 'string', example: 'default' }
|
||||
expose :created_at, documentation: { type: 'dateTime', example: '2012-10-12T17:04:47Z' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -37,7 +37,15 @@ module API
|
|||
|
||||
desc 'Import a GitHub project' do
|
||||
detail 'This feature was introduced in GitLab 11.3.4.'
|
||||
success ::ProjectEntity
|
||||
success code: 201, model: ::ProjectEntity
|
||||
failure [
|
||||
{ code: 400, message: 'Bad request' },
|
||||
{ code: 401, message: 'Unauthorized' },
|
||||
{ code: 403, message: 'Forbidden' },
|
||||
{ code: 422, message: 'Unprocessable entity' },
|
||||
{ code: 503, message: 'Service unavailable' }
|
||||
]
|
||||
tags ['project_import_github']
|
||||
end
|
||||
params do
|
||||
requires :personal_access_token, type: String, desc: 'GitHub personal access token'
|
||||
|
@ -58,6 +66,18 @@ module API
|
|||
end
|
||||
end
|
||||
|
||||
desc 'Cancel GitHub project import' do
|
||||
detail 'This feature was introduced in GitLab 15.5'
|
||||
success code: 200, model: ProjectImportEntity
|
||||
failure [
|
||||
{ code: 400, message: 'Bad request' },
|
||||
{ code: 401, message: 'Unauthorized' },
|
||||
{ code: 403, message: 'Forbidden' },
|
||||
{ code: 404, message: 'Not found' },
|
||||
{ code: 503, message: 'Service unavailable' }
|
||||
]
|
||||
tags ['project_import_github']
|
||||
end
|
||||
params do
|
||||
requires :project_id, type: Integer, desc: 'ID of importing project to be canceled'
|
||||
end
|
||||
|
|
|
@ -147,6 +147,12 @@ module Gitlab
|
|||
'in the body of your migration class'
|
||||
end
|
||||
|
||||
if !options.delete(:allow_partition) && partition?(table_name)
|
||||
raise ArgumentError, 'add_concurrent_index can not be used on a partitioned ' \
|
||||
'table. Please use add_concurrent_partitioned_index on the partitioned table ' \
|
||||
'as we need to create indexes on each partition and an index on the parent table'
|
||||
end
|
||||
|
||||
options = options.merge({ algorithm: :concurrently })
|
||||
|
||||
if index_exists?(table_name, column_name, **options)
|
||||
|
@ -1284,6 +1290,14 @@ into similar problems in the future (e.g. when new tables are created).
|
|||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
||||
|
||||
def partition?(table_name)
|
||||
if view_exists?(:postgres_partitions)
|
||||
Gitlab::Database::PostgresPartition.partition_exists?(table_name)
|
||||
else
|
||||
Gitlab::Database::PostgresPartition.legacy_partition_exists?(table_name)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def multiple_columns(columns, separator: ', ')
|
||||
|
|
|
@ -40,7 +40,7 @@ module Gitlab
|
|||
|
||||
partitioned_table.postgres_partitions.order(:name).each do |partition|
|
||||
partition_index_name = generated_index_name(partition.identifier, options[:name])
|
||||
partition_options = options.merge(name: partition_index_name)
|
||||
partition_options = options.merge(name: partition_index_name, allow_partition: true)
|
||||
|
||||
add_concurrent_index(partition.identifier, column_names, partition_options)
|
||||
end
|
||||
|
|
|
@ -19,6 +19,20 @@ module Gitlab
|
|||
|
||||
scope :for_parent_table, ->(name) { where("parent_identifier = concat(current_schema(), '.', ?)", name).order(:name) }
|
||||
|
||||
def self.partition_exists?(table_name)
|
||||
where("identifier = concat(current_schema(), '.', ?)", table_name).exists?
|
||||
end
|
||||
|
||||
def self.legacy_partition_exists?(table_name)
|
||||
result = connection.select_value(<<~SQL)
|
||||
SELECT true FROM pg_class
|
||||
WHERE relname = '#{table_name}'
|
||||
AND relispartition = true;
|
||||
SQL
|
||||
|
||||
!!result
|
||||
end
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
|
|
@ -17,11 +17,18 @@ module Gitlab
|
|||
gon.markdown_surround_selection = current_user&.markdown_surround_selection
|
||||
gon.markdown_automatic_lists = current_user&.markdown_automatic_lists
|
||||
|
||||
if Gitlab.config.sentry.enabled
|
||||
# Support for Sentry setup via configuration will be removed in 16.0
|
||||
# in favor of Gitlab::CurrentSettings.
|
||||
if Feature.enabled?(:enable_old_sentry_clientside_integration) && Gitlab.config.sentry.enabled
|
||||
gon.sentry_dsn = Gitlab.config.sentry.clientside_dsn
|
||||
gon.sentry_environment = Gitlab.config.sentry.environment
|
||||
end
|
||||
|
||||
if Feature.enabled?(:enable_new_sentry_clientside_integration) && Gitlab::CurrentSettings.sentry_enabled
|
||||
gon.sentry_dsn = Gitlab::CurrentSettings.sentry_clientside_dsn
|
||||
gon.sentry_environment = Gitlab::CurrentSettings.sentry_environment
|
||||
end
|
||||
|
||||
gon.recaptcha_api_server_url = ::Recaptcha.configuration.api_server_url
|
||||
gon.recaptcha_sitekey = Gitlab::CurrentSettings.recaptcha_site_key
|
||||
gon.gitlab_url = Gitlab.config.gitlab.url
|
||||
|
|
|
@ -2734,15 +2734,27 @@ msgstr ""
|
|||
msgid "AdminSettings|CI/CD limits"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Clickhouse URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Configure Let's Encrypt"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Configure product analytics to track events within your project applications."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Cube API URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Cube API key"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Delete inactive projects"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2791,6 +2803,9 @@ msgstr ""
|
|||
msgid "AdminSettings|Enable pipeline suggestion banner"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Enable product analytics"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Enable shared runners for new projects"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2836,6 +2851,18 @@ msgstr ""
|
|||
msgid "AdminSettings|Instance runners expiration"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Jitsu administrator email"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Jitsu administrator password"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Jitsu host"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Jitsu project ID"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Keep the latest artifacts for all jobs in the latest successful pipelines"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2974,9 +3001,18 @@ msgstr ""
|
|||
msgid "AdminSettings|Size and domain settings for Pages static sites."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|The ID of the project in Jitsu. The project contains all analytics instances."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|The URL of your Cube instance."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|The default domain to use for Auto Review Apps and Auto Deploy stages in all projects."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|The host of your Jitsu instance."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
|
||||
msgstr ""
|
||||
|
||||
|
@ -3001,6 +3037,15 @@ msgstr ""
|
|||
msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Used to connect Jitsu to the Clickhouse instance."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Used to generate short-lived API access tokens."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Used to retrieve dashboard data from the Cube instance."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
|
||||
msgstr ""
|
||||
|
||||
|
@ -5332,9 +5377,6 @@ msgstr ""
|
|||
msgid "Assign labels"
|
||||
msgstr ""
|
||||
|
||||
msgid "Assign milestone"
|
||||
msgstr ""
|
||||
|
||||
msgid "Assign myself"
|
||||
msgstr ""
|
||||
|
||||
|
@ -30890,6 +30932,9 @@ msgstr ""
|
|||
msgid "Proceed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Product analytics"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProductAnalytics|Audience"
|
||||
msgstr ""
|
||||
|
||||
|
@ -36144,10 +36189,10 @@ msgstr ""
|
|||
msgid "SecurityOrchestration|%{agent} for %{namespaces}"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|%{branches} %{plural}"
|
||||
msgid "SecurityOrchestration|%{branches} and %{lastBranch} branches"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|%{branches} and %{lastBranch} %{plural}"
|
||||
msgid "SecurityOrchestration|%{branches} branch"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|%{scanners}"
|
||||
|
@ -36504,10 +36549,10 @@ msgstr ""
|
|||
msgid "SecurityOrchestration|the %{branches}"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|the %{namespaces} %{plural}"
|
||||
msgid "SecurityOrchestration|the %{namespaces} and %{lastNamespace} namespaces"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|the %{namespaces} and %{lastNamespace} %{plural}"
|
||||
msgid "SecurityOrchestration|the %{namespaces} namespace"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|vulnerabilities"
|
||||
|
@ -48842,11 +48887,6 @@ msgstr ""
|
|||
msgid "my-topic"
|
||||
msgstr ""
|
||||
|
||||
msgid "namespace"
|
||||
msgid_plural "namespaces"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "needs to be between 10 minutes and 1 month"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -98,12 +98,17 @@ module QA
|
|||
let(:mrs) { fetch_mrs(imported_project, target_api_client) }
|
||||
let(:issues) { fetch_issues(imported_project, target_api_client) }
|
||||
|
||||
let(:import_failures) { imported_group.import_details.sum([]) { |details| details[:failures] } }
|
||||
|
||||
before do
|
||||
destination_group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
|
||||
end
|
||||
|
||||
# rubocop:disable RSpec/InstanceVariable
|
||||
after do |example|
|
||||
# Log failures for easier debugging
|
||||
Runtime::Logger.error("Import failures: #{import_failures}") if example.exception && !import_failures.empty?
|
||||
|
||||
next unless defined?(@import_time)
|
||||
|
||||
# save data for comparison notification creation
|
||||
|
@ -112,7 +117,7 @@ module QA
|
|||
{
|
||||
importer: :gitlab,
|
||||
import_time: @import_time,
|
||||
errors: imported_group.import_details.sum([]) { |details| details[:failures] },
|
||||
errors: import_failures,
|
||||
source: {
|
||||
name: "GitLab Source",
|
||||
project_name: source_project.path_with_namespace,
|
||||
|
@ -154,7 +159,7 @@ module QA
|
|||
end
|
||||
# rubocop:enable RSpec/InstanceVariable
|
||||
|
||||
it "migrates large gitlab group via api", testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358842' do
|
||||
it "migrates large gitlab group via api", testcase: "https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358842" do
|
||||
start = Time.now
|
||||
|
||||
# trigger import and log imported group path
|
||||
|
@ -165,7 +170,11 @@ module QA
|
|||
|
||||
# wait for import to finish and save import time
|
||||
logger.info("== Waiting for import to be finished ==")
|
||||
expect { imported_group.import_status }.to eventually_eq('finished').within(import_wait_duration)
|
||||
expect { imported_group.import_status }.not_to eventually_eq("started").within(import_wait_duration)
|
||||
# finished status actually means success, don't wait for finished status explicitly
|
||||
# because test would wait full duration if returned status is "failed"
|
||||
expect(imported_group.import_status).to eq("finished")
|
||||
|
||||
@import_time = Time.now - start
|
||||
|
||||
aggregate_failures do
|
||||
|
|
|
@ -20,23 +20,11 @@ RSpec.describe Projects::PipelinesController do
|
|||
end
|
||||
|
||||
shared_examples 'the show page' do |param|
|
||||
it 'redirects to pipeline path with param' do
|
||||
it 'renders the show template' do
|
||||
get param, params: { namespace_id: project.namespace, project_id: project, id: pipeline }
|
||||
|
||||
expect(response).to redirect_to(pipeline_path(pipeline, tab: param))
|
||||
end
|
||||
|
||||
context 'when the FF pipeline_tabs_vue is disabled' do
|
||||
before do
|
||||
stub_feature_flags(pipeline_tabs_vue: false)
|
||||
end
|
||||
|
||||
it 'renders the show template' do
|
||||
get param, params: { namespace_id: project.namespace, project_id: project, id: pipeline }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to render_template :show
|
||||
end
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to render_template :show
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -710,37 +698,25 @@ RSpec.describe Projects::PipelinesController do
|
|||
describe 'GET failures' do
|
||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
|
||||
context 'with ff `pipeline_tabs_vue` disabled' do
|
||||
context 'with failed jobs' do
|
||||
before do
|
||||
stub_feature_flags(pipeline_tabs_vue: false)
|
||||
create(:ci_build, :failed, pipeline: pipeline, name: 'hello')
|
||||
end
|
||||
|
||||
context 'with failed jobs' do
|
||||
before do
|
||||
create(:ci_build, :failed, pipeline: pipeline, name: 'hello')
|
||||
end
|
||||
it 'shows the page' do
|
||||
get :failures, params: { namespace_id: project.namespace, project_id: project, id: pipeline }
|
||||
|
||||
it 'shows the page' do
|
||||
get :failures, params: { namespace_id: project.namespace, project_id: project, id: pipeline }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to render_template :show
|
||||
end
|
||||
end
|
||||
|
||||
context 'without failed jobs' do
|
||||
it 'redirects to the main pipeline page' do
|
||||
get :failures, params: { namespace_id: project.namespace, project_id: project, id: pipeline }
|
||||
|
||||
expect(response).to redirect_to(pipeline_path(pipeline))
|
||||
end
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to render_template :show
|
||||
end
|
||||
end
|
||||
|
||||
it 'redirects to the pipeline page with `failures` query param' do
|
||||
get :failures, params: { namespace_id: project.namespace, project_id: project, id: pipeline }
|
||||
context 'without failed jobs' do
|
||||
it 'redirects to the main pipeline page' do
|
||||
get :failures, params: { namespace_id: project.namespace, project_id: project, id: pipeline }
|
||||
|
||||
expect(response).to redirect_to(pipeline_path(pipeline, tab: 'failures'))
|
||||
expect(response).to redirect_to(pipeline_path(pipeline))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ RSpec.describe 'Database schema' do
|
|||
project_error_tracking_settings: %w[sentry_project_id],
|
||||
project_group_links: %w[group_id],
|
||||
project_statistics: %w[namespace_id],
|
||||
projects: %w[ci_id mirror_user_id],
|
||||
projects: %w[creator_id ci_id mirror_user_id],
|
||||
redirect_routes: %w[source_id],
|
||||
repository_languages: %w[programming_language_id],
|
||||
routes: %w[source_id],
|
||||
|
|
|
@ -22,6 +22,7 @@ RSpec.describe 'Issue board filters', :js do
|
|||
let(:filter_submit) { find('.gl-search-box-by-click-search-button') }
|
||||
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ RSpec.describe 'Project issue boards', :js do
|
|||
|
||||
context 'signed in user' do
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
project.add_maintainer(user)
|
||||
project.add_maintainer(user2)
|
||||
|
||||
|
@ -29,7 +30,7 @@ RSpec.describe 'Project issue boards', :js do
|
|||
|
||||
context 'no lists' do
|
||||
before do
|
||||
visit_project_board_path_without_query_limit(project, board)
|
||||
visit_project_board(project, board)
|
||||
end
|
||||
|
||||
it 'creates default lists' do
|
||||
|
@ -73,7 +74,7 @@ RSpec.describe 'Project issue boards', :js do
|
|||
let_it_be(:issue10) { create(:labeled_issue, project: project, title: 'issue +', description: 'A+ great issue', labels: [a_plus]) }
|
||||
|
||||
before do
|
||||
visit_project_board_path_without_query_limit(project, board)
|
||||
visit_project_board(project, board)
|
||||
end
|
||||
|
||||
it 'shows description tooltip on list title', :quarantine do
|
||||
|
@ -124,7 +125,7 @@ RSpec.describe 'Project issue boards', :js do
|
|||
it 'infinite scrolls list' do
|
||||
create_list(:labeled_issue, 30, project: project, labels: [planning])
|
||||
|
||||
visit_project_board_path_without_query_limit(project, board)
|
||||
visit_project_board(project, board)
|
||||
|
||||
page.within(find('.board:nth-child(2)')) do
|
||||
expect(page.find('.board-header')).to have_content('38')
|
||||
|
@ -203,7 +204,7 @@ RSpec.describe 'Project issue boards', :js do
|
|||
expect(find('.board:nth-child(3) [data-testid="board-list-header"]')).to have_content(planning.title)
|
||||
|
||||
# Make sure list positions are preserved after a reload
|
||||
visit_project_board_path_without_query_limit(project, board)
|
||||
visit_project_board(project, board)
|
||||
|
||||
expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(development.title)
|
||||
expect(find('.board:nth-child(3) [data-testid="board-list-header"]')).to have_content(planning.title)
|
||||
|
@ -215,15 +216,19 @@ RSpec.describe 'Project issue boards', :js do
|
|||
let_it_be(:list2) { create(:list, board: board, label: development, position: 1) }
|
||||
|
||||
it 'changes position of list' do
|
||||
visit_project_board_path_without_query_limit(project, board)
|
||||
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
|
||||
visit_project_board(project, board)
|
||||
end
|
||||
|
||||
drag(list_from_index: 0, list_to_index: 1, selector: '.board-header')
|
||||
|
||||
expect(find('.board:nth-child(1) [data-testid="board-list-header"]')).to have_content(development.title)
|
||||
expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(planning.title)
|
||||
|
||||
# Make sure list positions are preserved after a reload
|
||||
visit_project_board_path_without_query_limit(project, board)
|
||||
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
|
||||
# Make sure list positions are preserved after a reload
|
||||
visit_project_board(project, board)
|
||||
end
|
||||
|
||||
expect(find('.board:nth-child(1) [data-testid="board-list-header"]')).to have_content(development.title)
|
||||
expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(planning.title)
|
||||
|
@ -234,7 +239,9 @@ RSpec.describe 'Project issue boards', :js do
|
|||
selector = '.board:not(.is-ghost) .board-header'
|
||||
expect(page).to have_selector(selector, text: development.title, count: 1)
|
||||
|
||||
drag(list_from_index: 2, list_to_index: 1, selector: '.board-header', perform_drop: false)
|
||||
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
|
||||
drag(list_from_index: 2, list_to_index: 1, selector: '.board-header', perform_drop: false)
|
||||
end
|
||||
|
||||
expect(page).to have_selector(selector, text: development.title, count: 1)
|
||||
end
|
||||
|
@ -492,7 +499,7 @@ RSpec.describe 'Project issue boards', :js do
|
|||
|
||||
context 'keyboard shortcuts' do
|
||||
before do
|
||||
visit_project_board_path_without_query_limit(project, board)
|
||||
visit_project_board(project, board)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
|
@ -505,6 +512,7 @@ RSpec.describe 'Project issue boards', :js do
|
|||
|
||||
context 'signed out user' do
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
visit project_board_path(project, board)
|
||||
wait_for_requests
|
||||
end
|
||||
|
@ -526,6 +534,7 @@ RSpec.describe 'Project issue boards', :js do
|
|||
let_it_be(:user_guest) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
project.add_guest(user_guest)
|
||||
sign_in(user_guest)
|
||||
visit project_board_path(project, board)
|
||||
|
@ -587,11 +596,9 @@ RSpec.describe 'Project issue boards', :js do
|
|||
end
|
||||
end
|
||||
|
||||
def visit_project_board_path_without_query_limit(project, board)
|
||||
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
|
||||
visit project_board_path(project, board)
|
||||
def visit_project_board(project, board)
|
||||
visit project_board_path(project, board)
|
||||
|
||||
wait_for_requests
|
||||
end
|
||||
wait_for_requests
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,6 +15,7 @@ RSpec.describe 'Issue Boards', :js do
|
|||
let!(:issue3) { create(:labeled_issue, project: project, title: 'testing 3', labels: [label], relative_position: 1) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
project.add_maintainer(user)
|
||||
|
||||
sign_in(user)
|
||||
|
|
|
@ -20,6 +20,7 @@ RSpec.describe 'Project issue boards sidebar labels', :js do
|
|||
let(:card) { find('.board:nth-child(2)').first('.board-card') }
|
||||
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
project.add_maintainer(user)
|
||||
|
||||
sign_in(user)
|
||||
|
|
|
@ -15,6 +15,7 @@ RSpec.describe 'Project issue boards sidebar', :js do
|
|||
let_it_be(:issue, reload: true) { create(:issue, project: project, relative_position: 1) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
project.add_maintainer(user)
|
||||
|
||||
sign_in(user)
|
||||
|
|
|
@ -31,6 +31,7 @@ RSpec.describe 'User adds lists', :js do
|
|||
|
||||
with_them do
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
sign_in(user)
|
||||
|
||||
set_cookie('sidebar_collapsed', 'true')
|
||||
|
|
|
@ -44,6 +44,7 @@ RSpec.describe 'User visits issue boards', :js do
|
|||
|
||||
with_them do
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
visit board_path
|
||||
|
||||
wait_for_requests
|
||||
|
@ -59,6 +60,7 @@ RSpec.describe 'User visits issue boards', :js do
|
|||
end
|
||||
|
||||
context "project boards" do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
let_it_be(:board) { create_default(:board, project: project) }
|
||||
let_it_be(:backlog_list) { create_default(:backlog_list, board: board) }
|
||||
|
||||
|
@ -68,6 +70,7 @@ RSpec.describe 'User visits issue boards', :js do
|
|||
end
|
||||
|
||||
context "group boards" do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
let_it_be(:board) { create_default(:board, group: group) }
|
||||
let_it_be(:backlog_list) { create_default(:backlog_list, board: board) }
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ RSpec.describe 'Group Issue Boards', :js do
|
|||
let(:card) { find('.board:nth-child(1)').first('.board-card') }
|
||||
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
sign_in(user)
|
||||
|
||||
visit group_board_path(group, board)
|
||||
|
|
|
@ -4,12 +4,16 @@ require "spec_helper"
|
|||
|
||||
RSpec.describe "User views incident" do
|
||||
let_it_be(:project) { create(:project_empty_repo, :public) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:incident) { create(:incident, project: project, description: "# Description header\n\n**Lorem** _ipsum_ dolor sit [amet](https://example.com)", author: user) }
|
||||
let_it_be(:note) { create(:note, noteable: incident, project: project, author: user) }
|
||||
let_it_be(:guest) { create(:user) }
|
||||
let_it_be(:developer) { create(:user) }
|
||||
let_it_be(:user) { developer }
|
||||
let(:author) { developer }
|
||||
let(:description) { "# Description header\n\n**Lorem** _ipsum_ dolor sit [amet](https://example.com)" }
|
||||
let(:incident) { create(:incident, project: project, description: description, author: author) }
|
||||
|
||||
before_all do
|
||||
project.add_developer(user)
|
||||
project.add_developer(developer)
|
||||
project.add_guest(guest)
|
||||
end
|
||||
|
||||
before do
|
||||
|
@ -18,57 +22,61 @@ RSpec.describe "User views incident" do
|
|||
visit(project_issues_incident_path(project, incident))
|
||||
end
|
||||
|
||||
it { expect(page).to have_header_with_correct_id_and_link(1, "Description header", "description-header") }
|
||||
specify do
|
||||
expect(page).to have_header_with_correct_id_and_link(1, 'Description header', 'description-header')
|
||||
end
|
||||
|
||||
it_behaves_like 'page meta description', ' Description header Lorem ipsum dolor sit amet'
|
||||
|
||||
describe 'user actions' do
|
||||
it 'shows the merge request and incident actions', :js, :aggregate_failures do
|
||||
expected_href = new_project_issue_path(project,
|
||||
issuable_template: 'incident',
|
||||
issue: { issue_type: 'incident' },
|
||||
add_related_issue: incident.iid)
|
||||
|
||||
click_button 'Incident actions'
|
||||
|
||||
expect(page).to have_link('New related incident', href: new_project_issue_path(project, { issuable_template: 'incident', issue: { issue_type: 'incident' }, add_related_issue: incident.iid }))
|
||||
expect(page).to have_link('New related incident', href: expected_href)
|
||||
expect(page).to have_button('Create merge request')
|
||||
expect(page).to have_button('Close incident')
|
||||
end
|
||||
|
||||
context 'when user is a guest' do
|
||||
before do
|
||||
project.add_guest(user)
|
||||
context 'when user is guest' do
|
||||
let(:user) { guest }
|
||||
|
||||
login_as(user)
|
||||
context 'and author' do
|
||||
let(:author) { guest }
|
||||
|
||||
visit(project_issues_incident_path(project, incident))
|
||||
it 'does not show the incident actions', :js do
|
||||
expect(page).not_to have_button('Incident actions')
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not show the incident actions', :js, :aggregate_failures do
|
||||
expect(page).not_to have_button('Incident actions')
|
||||
context 'and not author' do
|
||||
it 'shows incident actions', :js do
|
||||
click_button 'Incident actions'
|
||||
|
||||
expect(page).to have_link 'Report abuse'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the project is archived' do
|
||||
before do
|
||||
before_all do
|
||||
project.update!(archived: true)
|
||||
visit(project_issues_incident_path(project, incident))
|
||||
end
|
||||
|
||||
it 'hides the merge request and incident actions', :aggregate_failures do
|
||||
expect(page).not_to have_link('New incident')
|
||||
expect(page).not_to have_button('Create merge request')
|
||||
expect(page).not_to have_link('Close incident')
|
||||
it 'does not show the incident actions', :js do
|
||||
expect(page).not_to have_button('Incident actions')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'user status' do
|
||||
subject { visit(project_issues_incident_path(project, incident)) }
|
||||
|
||||
context 'when showing status of the author of the incident' do
|
||||
it_behaves_like 'showing user status' do
|
||||
let(:user_with_status) { user }
|
||||
end
|
||||
end
|
||||
subject { visit(project_issues_incident_path(project, incident)) }
|
||||
|
||||
context 'when showing status of a user who commented on an incident', :js do
|
||||
it_behaves_like 'showing user status' do
|
||||
let(:user_with_status) { user }
|
||||
end
|
||||
|
|
|
@ -417,7 +417,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
|
|||
click_button 'Select milestone'
|
||||
wait_for_requests
|
||||
items.map do |item|
|
||||
click_link item
|
||||
click_button item
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
|
|||
click_button 'Edit issues'
|
||||
check 'Select all'
|
||||
click_button 'Select milestone'
|
||||
click_link milestone.title
|
||||
click_button milestone.title
|
||||
click_update_issues_button
|
||||
|
||||
expect(page.find('.issue')).to have_content milestone.title
|
||||
|
@ -97,7 +97,7 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
|
|||
click_button 'Edit issues'
|
||||
check 'Select all'
|
||||
click_button 'Select milestone'
|
||||
click_link 'No milestone'
|
||||
click_button 'No milestone'
|
||||
click_update_issues_button
|
||||
|
||||
expect(find('.issue:first-of-type')).not_to have_text milestone.title
|
||||
|
|
|
@ -130,7 +130,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do
|
|||
click_button 'Edit merge requests'
|
||||
check 'Select all'
|
||||
click_button 'Select milestone'
|
||||
click_link text
|
||||
click_button text
|
||||
click_update_merge_requests_button
|
||||
end
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ describe('BoardApp', () => {
|
|||
store,
|
||||
provide: {
|
||||
...provide,
|
||||
fullBoardId: 'gid://gitlab/Board/1',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
import { GlAlert } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import Draggable from 'vuedraggable';
|
||||
import Vuex from 'vuex';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue';
|
||||
import getters from 'ee_else_ce/boards/stores/getters';
|
||||
import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql';
|
||||
import BoardColumn from '~/boards/components/board_column.vue';
|
||||
import BoardContent from '~/boards/components/board_content.vue';
|
||||
import BoardContentSidebar from '~/boards/components/board_content_sidebar.vue';
|
||||
import { mockLists } from '../mock_data';
|
||||
import { mockLists, boardListsQueryResponse } from '../mock_data';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
Vue.use(Vuex);
|
||||
|
||||
const actions = {
|
||||
|
@ -18,6 +23,7 @@ const actions = {
|
|||
|
||||
describe('BoardContent', () => {
|
||||
let wrapper;
|
||||
let fakeApollo;
|
||||
window.gon = {};
|
||||
|
||||
const defaultState = {
|
||||
|
@ -35,19 +41,33 @@ describe('BoardContent', () => {
|
|||
});
|
||||
};
|
||||
|
||||
const createComponent = ({ state, props = {}, canAdminList = true } = {}) => {
|
||||
const createComponent = ({
|
||||
state,
|
||||
props = {},
|
||||
canAdminList = true,
|
||||
isApolloBoard = false,
|
||||
issuableType = 'issue',
|
||||
boardListQueryHandler = jest.fn().mockResolvedValue(boardListsQueryResponse),
|
||||
} = {}) => {
|
||||
fakeApollo = createMockApollo([[boardListsQuery, boardListQueryHandler]]);
|
||||
|
||||
const store = createStore({
|
||||
...defaultState,
|
||||
...state,
|
||||
});
|
||||
wrapper = shallowMount(BoardContent, {
|
||||
apolloProvider: fakeApollo,
|
||||
propsData: {
|
||||
lists: mockLists,
|
||||
disabled: false,
|
||||
boardId: 'gid://gitlab/Board/1',
|
||||
...props,
|
||||
},
|
||||
provide: {
|
||||
canAdminList,
|
||||
boardType: 'group',
|
||||
fullPath: 'gitlab-org/gitlab',
|
||||
issuableType,
|
||||
isApolloBoard,
|
||||
},
|
||||
store,
|
||||
});
|
||||
|
@ -78,6 +98,7 @@ describe('BoardContent', () => {
|
|||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
fakeApollo = null;
|
||||
});
|
||||
|
||||
describe('default', () => {
|
||||
|
@ -112,7 +133,7 @@ describe('BoardContent', () => {
|
|||
|
||||
describe('when issuableType is not issue', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ state: { issuableType: 'foo' } });
|
||||
createComponent({ issuableType: 'foo' });
|
||||
});
|
||||
|
||||
it('does not render BoardContentSidebar', () => {
|
||||
|
@ -139,4 +160,19 @@ describe('BoardContent', () => {
|
|||
expect(wrapper.findComponent(Draggable).exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when Apollo boards FF is on', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent({ isApolloBoard: true });
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('renders a BoardColumn component per list', () => {
|
||||
expect(wrapper.findAllComponents(BoardColumn)).toHaveLength(mockLists.length);
|
||||
});
|
||||
|
||||
it('renders BoardContentSidebar', () => {
|
||||
expect(wrapper.findComponent(BoardContentSidebar).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -433,8 +433,11 @@ export const mockList = {
|
|||
label: null,
|
||||
assignee: null,
|
||||
milestone: null,
|
||||
iteration: null,
|
||||
loading: false,
|
||||
issuesCount: 1,
|
||||
maxIssueCount: 0,
|
||||
__typename: 'BoardList',
|
||||
};
|
||||
|
||||
export const mockLabelList = {
|
||||
|
@ -449,11 +452,15 @@ export const mockLabelList = {
|
|||
color: '#F0AD4E',
|
||||
textColor: '#FFFFFF',
|
||||
description: null,
|
||||
descriptionHtml: null,
|
||||
},
|
||||
assignee: null,
|
||||
milestone: null,
|
||||
iteration: null,
|
||||
loading: false,
|
||||
issuesCount: 0,
|
||||
maxIssueCount: 0,
|
||||
__typename: 'BoardList',
|
||||
};
|
||||
|
||||
export const mockMilestoneList = {
|
||||
|
@ -844,6 +851,22 @@ export const mockGroupLabelsResponse = {
|
|||
},
|
||||
};
|
||||
|
||||
export const boardListsQueryResponse = {
|
||||
data: {
|
||||
group: {
|
||||
id: 'gid://gitlab/Group/1',
|
||||
board: {
|
||||
id: 'gid://gitlab/Board/1',
|
||||
hideBacklogList: false,
|
||||
lists: {
|
||||
nodes: mockLists,
|
||||
},
|
||||
},
|
||||
__typename: 'Group',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const boardListQueryResponse = (issuesCount = 20) => ({
|
||||
data: {
|
||||
boardList: {
|
||||
|
|
|
@ -2,10 +2,6 @@ import { shallowMount } from '@vue/test-utils';
|
|||
import { GlTab } from '@gitlab/ui';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import PipelineTabs from '~/pipelines/components/pipeline_tabs.vue';
|
||||
import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_wrapper.vue';
|
||||
import Dag from '~/pipelines/components/dag/dag.vue';
|
||||
import JobsApp from '~/pipelines/components/jobs/jobs_app.vue';
|
||||
import TestReports from '~/pipelines/components/test_reports/test_reports.vue';
|
||||
|
||||
describe('The Pipeline Tabs', () => {
|
||||
let wrapper;
|
||||
|
@ -16,12 +12,6 @@ describe('The Pipeline Tabs', () => {
|
|||
const findPipelineTab = () => wrapper.findByTestId('pipeline-tab');
|
||||
const findTestsTab = () => wrapper.findByTestId('tests-tab');
|
||||
|
||||
const findDagApp = () => wrapper.findComponent(Dag);
|
||||
const findFailedJobsApp = () => wrapper.findComponent(JobsApp);
|
||||
const findJobsApp = () => wrapper.findComponent(JobsApp);
|
||||
const findPipelineApp = () => wrapper.findComponent(PipelineGraphWrapper);
|
||||
const findTestsApp = () => wrapper.findComponent(TestReports);
|
||||
|
||||
const findFailedJobsBadge = () => wrapper.findByTestId('failed-builds-counter');
|
||||
const findJobsBadge = () => wrapper.findByTestId('builds-counter');
|
||||
const findTestsBadge = () => wrapper.findByTestId('tests-counter');
|
||||
|
@ -43,6 +33,7 @@ describe('The Pipeline Tabs', () => {
|
|||
},
|
||||
stubs: {
|
||||
GlTab,
|
||||
RouterView: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
@ -54,17 +45,16 @@ describe('The Pipeline Tabs', () => {
|
|||
|
||||
describe('Tabs', () => {
|
||||
it.each`
|
||||
tabName | tabComponent | appComponent
|
||||
${'Pipeline'} | ${findPipelineTab} | ${findPipelineApp}
|
||||
${'Dag'} | ${findDagTab} | ${findDagApp}
|
||||
${'Jobs'} | ${findJobsTab} | ${findJobsApp}
|
||||
${'Failed Jobs'} | ${findFailedJobsTab} | ${findFailedJobsApp}
|
||||
${'Tests'} | ${findTestsTab} | ${findTestsApp}
|
||||
`('shows $tabName tab with its associated component', ({ appComponent, tabComponent }) => {
|
||||
tabName | tabComponent
|
||||
${'Pipeline'} | ${findPipelineTab}
|
||||
${'Dag'} | ${findDagTab}
|
||||
${'Jobs'} | ${findJobsTab}
|
||||
${'Failed Jobs'} | ${findFailedJobsTab}
|
||||
${'Tests'} | ${findTestsTab}
|
||||
`('shows $tabName tab', ({ tabComponent }) => {
|
||||
createComponent();
|
||||
|
||||
expect(tabComponent().exists()).toBe(true);
|
||||
expect(appComponent().exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('with no failed jobs', () => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createJobsHash, generateJobNeedsDict, getPipelineDefaultTab } from '~/pipelines/utils';
|
||||
import { TAB_QUERY_PARAM, validPipelineTabNames } from '~/pipelines/constants';
|
||||
import { validPipelineTabNames } from '~/pipelines/constants';
|
||||
|
||||
describe('utils functions', () => {
|
||||
const jobName1 = 'build_1';
|
||||
|
@ -173,18 +173,25 @@ describe('utils functions', () => {
|
|||
|
||||
describe('getPipelineDefaultTab', () => {
|
||||
const baseUrl = 'http://gitlab.com/user/multi-projects-small/-/pipelines/332/';
|
||||
it('returns null if there was no `tab` params', () => {
|
||||
it('returns null if there is only the base url', () => {
|
||||
expect(getPipelineDefaultTab(baseUrl)).toBe(null);
|
||||
});
|
||||
|
||||
it('returns null if there was no valid tab param', () => {
|
||||
expect(getPipelineDefaultTab(`${baseUrl}?${TAB_QUERY_PARAM}=invalid`)).toBe(null);
|
||||
it('returns null if there was no valid last url part', () => {
|
||||
expect(getPipelineDefaultTab(`${baseUrl}something`)).toBe(null);
|
||||
});
|
||||
|
||||
it('returns the correct tab name if present', () => {
|
||||
validPipelineTabNames.forEach((tabName) => {
|
||||
expect(getPipelineDefaultTab(`${baseUrl}?${TAB_QUERY_PARAM}=${tabName}`)).toBe(tabName);
|
||||
expect(getPipelineDefaultTab(`${baseUrl}${tabName}`)).toBe(tabName);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the right value even with query params', () => {
|
||||
const [tabName] = validPipelineTabNames;
|
||||
expect(getPipelineDefaultTab(`${baseUrl}${tabName}?query="something"&query2="else"`)).toBe(
|
||||
tabName,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { createAppOptions, createPipelineTabs } from '~/pipelines/pipeline_tabs';
|
||||
import { updateHistory } from '~/lib/utils/url_utility';
|
||||
import { createAppOptions } from '~/pipelines/pipeline_tabs';
|
||||
|
||||
jest.mock('~/lib/utils/url_utility', () => ({
|
||||
removeParams: () => 'gitlab.com',
|
||||
updateHistory: jest.fn(),
|
||||
joinPaths: () => {},
|
||||
setUrlFragment: () => {},
|
||||
}));
|
||||
|
@ -64,32 +62,4 @@ describe('~/pipelines/pipeline_tabs.js', () => {
|
|||
expect(createAppOptions('foo', null)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createPipelineTabs', () => {
|
||||
const title = 'Pipeline Tabs';
|
||||
|
||||
beforeAll(() => {
|
||||
document.title = title;
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
document.title = '';
|
||||
});
|
||||
|
||||
it('calls `updateHistory` with correct params', () => {
|
||||
createPipelineTabs({});
|
||||
|
||||
expect(updateHistory).toHaveBeenCalledWith({
|
||||
title,
|
||||
url: 'gitlab.com',
|
||||
replace: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns early if options aren't provided", () => {
|
||||
createPipelineTabs();
|
||||
|
||||
expect(updateHistory).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { nextTick } from 'vue';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { IssuableType, WorkspaceType } from '~/issues/constants';
|
||||
import { __ } from '~/locale';
|
||||
import BulkUpdateMilestoneDropdown from '~/sidebar/components/milestone/bulk_update_milestone_dropdown.vue';
|
||||
import SidebarDropdown from '~/sidebar/components/sidebar_dropdown.vue';
|
||||
|
||||
describe('BulkUpdateMilestoneDropdown component', () => {
|
||||
let wrapper;
|
||||
|
||||
const propsData = {
|
||||
attrWorkspacePath: 'full/path',
|
||||
issuableType: IssuableType.Issue,
|
||||
workspaceType: WorkspaceType.project,
|
||||
};
|
||||
|
||||
const findHiddenInput = () => wrapper.find('input');
|
||||
const findSidebarDropdown = () => wrapper.findComponent(SidebarDropdown);
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMount(BulkUpdateMilestoneDropdown, { propsData });
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders SidebarDropdown', () => {
|
||||
expect(findSidebarDropdown().props()).toMatchObject({
|
||||
attrWorkspacePath: propsData.attrWorkspacePath,
|
||||
issuableAttribute: BulkUpdateMilestoneDropdown.issuableAttribute,
|
||||
issuableType: propsData.issuableType,
|
||||
workspaceType: propsData.workspaceType,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders hidden input', () => {
|
||||
expect(findHiddenInput().attributes()).toEqual({
|
||||
type: 'hidden',
|
||||
name: 'update[milestone_id]',
|
||||
value: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
describe('when SidebarDropdown emits `change` event', () => {
|
||||
describe('when valid milestone is emitted', () => {
|
||||
it('updates the hidden input value', async () => {
|
||||
const milestone = {
|
||||
id: 'gid://gitlab/Milestone/52',
|
||||
title: __('Milestone 52'),
|
||||
};
|
||||
|
||||
findSidebarDropdown().vm.$emit('change', milestone);
|
||||
await nextTick();
|
||||
|
||||
expect(findHiddenInput().attributes('value')).toBe(
|
||||
getIdFromGraphQLId(milestone.id).toString(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when null milestone is emitted', () => {
|
||||
it('updates the hidden input value to `0`', async () => {
|
||||
const milestone = { id: null };
|
||||
|
||||
findSidebarDropdown().vm.$emit('change', milestone);
|
||||
await nextTick();
|
||||
|
||||
expect(findHiddenInput().attributes('value')).toBe('0');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -31,6 +31,7 @@ RSpec.describe Projects::PipelineHelper do
|
|||
suite_endpoint: project_pipeline_test_path(project, pipeline, suite_name: 'suite', format: :json),
|
||||
blob_path: project_blob_path(project, pipeline.sha),
|
||||
has_test_report: pipeline.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:test)),
|
||||
empty_dag_svg_path: match_asset_path('illustrations/empty-state/empty-dag-md.svg'),
|
||||
empty_state_image_path: match_asset_path('illustrations/empty-state/empty-test-cases-lg.svg'),
|
||||
artifacts_expired_image_path: match_asset_path('illustrations/pipeline.svg'),
|
||||
tests_count: pipeline.test_report_summary.total[:count]
|
||||
|
|
|
@ -389,6 +389,40 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
|
|||
|
||||
model.add_concurrent_index(:users, :foo)
|
||||
end
|
||||
|
||||
context 'when targeting a partition table' do
|
||||
let(:schema) { 'public' }
|
||||
let(:name) { '_test_partition_01' }
|
||||
let(:identifier) { "#{schema}.#{name}" }
|
||||
|
||||
before do
|
||||
model.execute(<<~SQL)
|
||||
CREATE TABLE public._test_partitioned_table (
|
||||
id serial NOT NULL,
|
||||
partition_id serial NOT NULL,
|
||||
PRIMARY KEY (id, partition_id)
|
||||
) PARTITION BY LIST(partition_id);
|
||||
|
||||
CREATE TABLE #{identifier} PARTITION OF public._test_partitioned_table
|
||||
FOR VALUES IN (1);
|
||||
SQL
|
||||
end
|
||||
|
||||
context 'when allow_partition is true' do
|
||||
it 'creates the index concurrently' do
|
||||
expect(model).to receive(:add_index).with(:_test_partition_01, :foo, algorithm: :concurrently)
|
||||
|
||||
model.add_concurrent_index(:_test_partition_01, :foo, allow_partition: true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when allow_partition is not provided' do
|
||||
it 'raises ArgumentError' do
|
||||
expect { model.add_concurrent_index(:_test_partition_01, :foo) }
|
||||
.to raise_error(ArgumentError, /use add_concurrent_partitioned_index/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'inside a transaction' do
|
||||
|
@ -2889,4 +2923,36 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
|
|||
model.add_sequence(:test_table, :test_column, :test_table_id_seq, 1)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#partition?" do
|
||||
subject { model.partition?(table_name) }
|
||||
|
||||
let(:table_name) { 'ci_builds_metadata' }
|
||||
|
||||
context "when a partition table exist" do
|
||||
context 'when the view postgres_partitions exists' do
|
||||
it 'calls the view', :aggregate_failures do
|
||||
expect(Gitlab::Database::PostgresPartition).to receive(:partition_exists?).with(table_name).and_call_original
|
||||
expect(subject).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the view postgres_partitions does not exist' do
|
||||
before do
|
||||
allow(model).to receive(:view_exists?).and_return(false)
|
||||
end
|
||||
|
||||
it 'does not call the view', :aggregate_failures do
|
||||
expect(Gitlab::Database::PostgresPartition).to receive(:legacy_partition_exists?).with(table_name).and_call_original
|
||||
expect(subject).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when a partition table does not exist" do
|
||||
let(:table_name) { 'partition_does_not_exist' }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -65,8 +65,11 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
|
|||
end
|
||||
|
||||
def expect_add_concurrent_index_and_call_original(table, column, index)
|
||||
expect(migration).to receive(:add_concurrent_index).ordered.with(table, column, { name: index })
|
||||
.and_wrap_original { |_, table, column, options| connection.add_index(table, column, **options) }
|
||||
expect(migration).to receive(:add_concurrent_index).ordered.with(table, column, { name: index, allow_partition: true })
|
||||
.and_wrap_original do |_, table, column, options|
|
||||
options.delete(:allow_partition)
|
||||
connection.add_index(table, column, **options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -91,7 +94,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
|
|||
|
||||
it 'forwards them to the index helper methods', :aggregate_failures do
|
||||
expect(migration).to receive(:add_concurrent_index)
|
||||
.with(partition1_identifier, column_name, { name: partition1_index, where: 'x > 0', unique: true })
|
||||
.with(partition1_identifier, column_name, { name: partition1_index, where: 'x > 0', unique: true, allow_partition: true })
|
||||
|
||||
expect(migration).to receive(:add_index)
|
||||
.with(table_name, column_name, { name: index_name, where: 'x > 0', unique: true })
|
||||
|
|
|
@ -72,4 +72,36 @@ RSpec.describe Gitlab::Database::PostgresPartition, type: :model do
|
|||
expect(find(identifier).condition).to eq("FOR VALUES FROM ('2020-01-01 00:00:00+00') TO ('2020-02-01 00:00:00+00')")
|
||||
end
|
||||
end
|
||||
|
||||
describe '.partition_exists?' do
|
||||
subject { described_class.partition_exists?(table_name) }
|
||||
|
||||
context 'when the partition exists' do
|
||||
let(:table_name) { "ci_builds_metadata" }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when the partition does not exist' do
|
||||
let(:table_name) { 'partition_does_not_exist' }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.legacy_partition_exists?' do
|
||||
subject { described_class.legacy_partition_exists?(table_name) }
|
||||
|
||||
context 'when the partition exists' do
|
||||
let(:table_name) { "ci_builds_metadata" }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when the partition does not exist' do
|
||||
let(:table_name) { 'partition_does_not_exist' }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,6 +39,72 @@ RSpec.describe Gitlab::GonHelper do
|
|||
helper.add_gon_variables
|
||||
end
|
||||
end
|
||||
|
||||
describe 'sentry configuration' do
|
||||
let(:legacy_clientside_dsn) { 'https://xxx@sentry-legacy.example.com/1' }
|
||||
let(:clientside_dsn) { 'https://xxx@sentry.example.com/1' }
|
||||
let(:environment) { 'production' }
|
||||
|
||||
context 'with enable_old_sentry_clientside_integration enabled' do
|
||||
before do
|
||||
stub_feature_flags(
|
||||
enable_old_sentry_clientside_integration: true,
|
||||
enable_new_sentry_clientside_integration: false
|
||||
)
|
||||
|
||||
stub_config(sentry: { enabled: true, clientside_dsn: legacy_clientside_dsn, environment: environment })
|
||||
end
|
||||
|
||||
it 'sets sentry dsn and environment from config' do
|
||||
expect(gon).to receive(:sentry_dsn=).with(legacy_clientside_dsn)
|
||||
expect(gon).to receive(:sentry_environment=).with(environment)
|
||||
|
||||
helper.add_gon_variables
|
||||
end
|
||||
end
|
||||
|
||||
context 'with enable_new_sentry_clientside_integration enabled' do
|
||||
before do
|
||||
stub_feature_flags(
|
||||
enable_old_sentry_clientside_integration: false,
|
||||
enable_new_sentry_clientside_integration: true
|
||||
)
|
||||
|
||||
stub_application_setting(sentry_enabled: true)
|
||||
stub_application_setting(sentry_clientside_dsn: clientside_dsn)
|
||||
stub_application_setting(sentry_environment: environment)
|
||||
end
|
||||
|
||||
it 'sets sentry dsn and environment from application settings' do
|
||||
expect(gon).to receive(:sentry_dsn=).with(clientside_dsn)
|
||||
expect(gon).to receive(:sentry_environment=).with(environment)
|
||||
|
||||
helper.add_gon_variables
|
||||
end
|
||||
end
|
||||
|
||||
context 'with enable_old_sentry_clientside_integration and enable_new_sentry_clientside_integration enabled' do
|
||||
before do
|
||||
stub_feature_flags(
|
||||
enable_old_sentry_clientside_integration: true,
|
||||
enable_new_sentry_clientside_integration: true
|
||||
)
|
||||
|
||||
stub_config(sentry: { enabled: true, clientside_dsn: legacy_clientside_dsn, environment: environment })
|
||||
|
||||
stub_application_setting(sentry_enabled: true)
|
||||
stub_application_setting(sentry_clientside_dsn: clientside_dsn)
|
||||
stub_application_setting(sentry_environment: environment)
|
||||
end
|
||||
|
||||
it 'sets sentry dsn and environment from application settings' do
|
||||
expect(gon).to receive(:sentry_dsn=).with(clientside_dsn)
|
||||
expect(gon).to receive(:sentry_environment=).with(environment)
|
||||
|
||||
helper.add_gon_variables
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#push_frontend_feature_flag' do
|
||||
|
|
|
@ -13,6 +13,11 @@ RSpec.describe AlertManagement::HttpIntegration do
|
|||
it { is_expected.to belong_to(:project) }
|
||||
end
|
||||
|
||||
describe 'default values' do
|
||||
it { expect(described_class.new.endpoint_identifier).to be_present }
|
||||
it { expect(described_class.new(endpoint_identifier: 'test').endpoint_identifier).to eq('test') }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
it { is_expected.to validate_presence_of(:project) }
|
||||
it { is_expected.to validate_presence_of(:name) }
|
||||
|
@ -124,10 +129,6 @@ RSpec.describe AlertManagement::HttpIntegration do
|
|||
end
|
||||
|
||||
context 'when unsaved' do
|
||||
context 'when unassigned' do
|
||||
it_behaves_like 'valid token'
|
||||
end
|
||||
|
||||
context 'when assigned' do
|
||||
include_context 'assign token', 'random_token'
|
||||
|
||||
|
|
|
@ -4089,7 +4089,6 @@
|
|||
- './spec/features/incidents/user_creates_new_incident_spec.rb'
|
||||
- './spec/features/incidents/user_filters_incidents_by_status_spec.rb'
|
||||
- './spec/features/incidents/user_searches_incidents_spec.rb'
|
||||
- './spec/features/incidents/user_views_incident_spec.rb'
|
||||
- './spec/features/invites_spec.rb'
|
||||
- './spec/features/issuables/issuable_list_spec.rb'
|
||||
- './spec/features/issuables/markdown_references/internal_references_spec.rb'
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
RSpec.shared_examples 'multiple issue boards' do
|
||||
context 'authorized user' do
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
parent.add_maintainer(user)
|
||||
|
||||
login_as(user)
|
||||
|
@ -121,6 +122,7 @@ RSpec.shared_examples 'multiple issue boards' do
|
|||
|
||||
context 'unauthorized user' do
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
visit boards_path
|
||||
wait_for_requests
|
||||
end
|
||||
|
|
|
@ -5,6 +5,7 @@ RSpec.shared_examples 'multiple and scoped issue boards' do |route_definition|
|
|||
|
||||
context 'multiple issue boards' do
|
||||
before do
|
||||
stub_feature_flags(apollo_boards: false)
|
||||
board_parent.add_reporter(user)
|
||||
stub_licensed_features(multiple_group_issue_boards: true)
|
||||
end
|
||||
|
|
|
@ -52,6 +52,7 @@ const (
|
|||
geoGitProjectPattern = `^/[^-].+\.git/` // Prevent matching routes like /-/push_from_secondary
|
||||
projectPattern = `^/([^/]+/){1,}[^/]+/`
|
||||
apiProjectPattern = apiPattern + `v4/projects/[^/]+` // API: Projects can be encoded via group%2Fsubgroup%2Fproject
|
||||
apiGroupPattern = apiPattern + `v4/groups/[^/]+`
|
||||
apiTopicPattern = apiPattern + `v4/topics`
|
||||
snippetUploadPattern = `^/uploads/personal_snippet`
|
||||
userUploadPattern = `^/uploads/user`
|
||||
|
@ -303,6 +304,7 @@ func configureRoutes(u *upstream) {
|
|||
// we need to declare each routes until we have fixed all the routes on the rails codebase.
|
||||
// Overall status can be seen at https://gitlab.com/groups/gitlab-org/-/epics/1802#current-status
|
||||
u.route("POST", apiProjectPattern+`/wikis/attachments\z`, tempfileMultipartProxy),
|
||||
u.route("POST", apiGroupPattern+`/wikis/attachments\z`, tempfileMultipartProxy),
|
||||
u.route("POST", apiPattern+`graphql\z`, tempfileMultipartProxy),
|
||||
u.route("POST", apiTopicPattern, tempfileMultipartProxy),
|
||||
u.route("PUT", apiTopicPattern, tempfileMultipartProxy),
|
||||
|
|
|
@ -138,6 +138,8 @@ func TestAcceleratedUpload(t *testing.T) {
|
|||
{"POST", `/api/v4/groups`, false},
|
||||
{"PUT", `/api/v4/groups/5`, false},
|
||||
{"PUT", `/api/v4/groups/group%2Fsubgroup`, false},
|
||||
{"POST", `/api/v4/groups/1/wikis/attachments`, false},
|
||||
{"POST", `/api/v4/groups/my%2Fsubgroup/wikis/attachments`, false},
|
||||
{"POST", `/api/v4/users`, false},
|
||||
{"PUT", `/api/v4/users/42`, false},
|
||||
{"PUT", "/api/v4/projects/9001/packages/nuget/v1/files", true},
|
||||
|
|
Loading…
Reference in a new issue