Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9f0d276489
commit
f2fd07aa1c
|
@ -308,6 +308,10 @@ Rails/RakeEnvironment:
|
||||||
# Context on why it's disabled: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93419#note_1048223982
|
# Context on why it's disabled: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93419#note_1048223982
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96675#note_1094403693
|
||||||
|
Rails/WhereExists:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
# GitLab ###################################################################
|
# GitLab ###################################################################
|
||||||
|
|
||||||
Gitlab/ModuleWithInstanceVariables:
|
Gitlab/ModuleWithInstanceVariables:
|
||||||
|
|
|
@ -3920,7 +3920,6 @@ Layout/LineLength:
|
||||||
- 'spec/features/groups/settings/repository_spec.rb'
|
- 'spec/features/groups/settings/repository_spec.rb'
|
||||||
- 'spec/features/groups_spec.rb'
|
- 'spec/features/groups_spec.rb'
|
||||||
- 'spec/features/ide/static_object_external_storage_csp_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/invites_spec.rb'
|
||||||
- 'spec/features/issuables/issuable_list_spec.rb'
|
- 'spec/features/issuables/issuable_list_spec.rb'
|
||||||
- 'spec/features/issuables/markdown_references/internal_references_spec.rb'
|
- 'spec/features/issuables/markdown_references/internal_references_spec.rb'
|
||||||
|
|
|
@ -1,30 +1,28 @@
|
||||||
---
|
---
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Rails/NegateInclude:
|
Rails/NegateInclude:
|
||||||
# Offense count: 65
|
Details: grace period
|
||||||
# Temporarily disabled due to too many offenses
|
|
||||||
Enabled: false
|
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/finders/projects_finder.rb'
|
- 'app/finders/projects_finder.rb'
|
||||||
- 'app/helpers/application_settings_helper.rb'
|
- 'app/helpers/application_settings_helper.rb'
|
||||||
- 'app/helpers/projects_helper.rb'
|
- 'app/helpers/projects_helper.rb'
|
||||||
- 'app/helpers/tree_helper.rb'
|
- 'app/helpers/tree_helper.rb'
|
||||||
- 'app/models/concerns/timebox.rb'
|
|
||||||
- 'app/models/integrations/chat_message/pipeline_message.rb'
|
- 'app/models/integrations/chat_message/pipeline_message.rb'
|
||||||
|
- 'app/models/integrations/field.rb'
|
||||||
- 'app/models/label.rb'
|
- 'app/models/label.rb'
|
||||||
- 'app/models/merge_request.rb'
|
- 'app/models/merge_request.rb'
|
||||||
|
- 'app/models/milestone.rb'
|
||||||
- 'app/services/todo_service.rb'
|
- 'app/services/todo_service.rb'
|
||||||
|
- 'app/services/work_items/parent_links/create_service.rb'
|
||||||
- 'config/application.rb'
|
- 'config/application.rb'
|
||||||
- 'config/initializers/1_settings.rb'
|
- 'config/initializers/1_settings.rb'
|
||||||
- 'danger/roulette/Dangerfile'
|
- 'danger/roulette/Dangerfile'
|
||||||
- 'ee/app/finders/security/pipeline_vulnerabilities_finder.rb'
|
- 'ee/app/finders/security/pipeline_vulnerabilities_finder.rb'
|
||||||
- 'ee/app/models/ee/epic.rb'
|
|
||||||
- 'ee/app/models/ee/vulnerability.rb'
|
- 'ee/app/models/ee/vulnerability.rb'
|
||||||
- 'ee/app/services/epic_issues/create_service.rb'
|
- 'ee/app/services/epic_issues/create_service.rb'
|
||||||
- 'ee/app/services/security/ingestion/tasks/ingest_remediations.rb'
|
- 'ee/app/services/security/ingestion/tasks/ingest_remediations.rb'
|
||||||
- 'ee/app/services/security/security_orchestration_policies/validate_policy_service.rb'
|
- 'ee/app/services/security/security_orchestration_policies/validate_policy_service.rb'
|
||||||
- 'lib/api/maven_packages.rb'
|
- 'lib/api/maven_packages.rb'
|
||||||
- 'lib/generators/gitlab/usage_metric_generator.rb'
|
|
||||||
- 'lib/gitlab/background_migration/legacy_upload_mover.rb'
|
- 'lib/gitlab/background_migration/legacy_upload_mover.rb'
|
||||||
- 'lib/gitlab/ci/build/rules/rule/clause/exists.rb'
|
- 'lib/gitlab/ci/build/rules/rule/clause/exists.rb'
|
||||||
- 'lib/gitlab/ci/parsers/coverage/sax_document.rb'
|
- 'lib/gitlab/ci/parsers/coverage/sax_document.rb'
|
||||||
|
@ -38,11 +36,10 @@ Rails/NegateInclude:
|
||||||
- 'lib/gitlab/task_helpers.rb'
|
- 'lib/gitlab/task_helpers.rb'
|
||||||
- 'lib/gitlab/url_blocker.rb'
|
- 'lib/gitlab/url_blocker.rb'
|
||||||
- 'lib/gitlab_edition.rb'
|
- 'lib/gitlab_edition.rb'
|
||||||
|
- 'qa/qa/page/merge_request/show.rb'
|
||||||
- 'qa/qa/runtime/ip_address.rb'
|
- 'qa/qa/runtime/ip_address.rb'
|
||||||
- 'qa/qa/support/run.rb'
|
- 'qa/qa/support/run.rb'
|
||||||
- 'qa/qa/tools/delete_test_users.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/gitlab/feature_available_usage.rb'
|
||||||
- 'rubocop/cop/graphql/id_type.rb'
|
- 'rubocop/cop/graphql/id_type.rb'
|
||||||
- 'rubocop/cop/migration/add_reference.rb'
|
- 'rubocop/cop/migration/add_reference.rb'
|
||||||
|
@ -56,3 +53,4 @@ Rails/NegateInclude:
|
||||||
- 'spec/support/matchers/pushed_frontend_feature_flags_matcher.rb'
|
- 'spec/support/matchers/pushed_frontend_feature_flags_matcher.rb'
|
||||||
- 'spec/support/shared_contexts/markdown_golden_master_shared_examples.rb'
|
- 'spec/support/shared_contexts/markdown_golden_master_shared_examples.rb'
|
||||||
- 'spec/uploaders/object_storage_spec.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,
|
BoardSettingsSidebar,
|
||||||
BoardTopBar,
|
BoardTopBar,
|
||||||
},
|
},
|
||||||
inject: ['disabled'],
|
inject: ['disabled', 'fullBoardId'],
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['isSidebarOpen']),
|
...mapGetters(['isSidebarOpen']),
|
||||||
},
|
},
|
||||||
|
@ -27,7 +27,7 @@ export default {
|
||||||
<template>
|
<template>
|
||||||
<div class="boards-app gl-relative" :class="{ 'is-compact': isSidebarOpen }">
|
<div class="boards-app gl-relative" :class="{ 'is-compact': isSidebarOpen }">
|
||||||
<board-top-bar />
|
<board-top-bar />
|
||||||
<board-content :disabled="disabled" />
|
<board-content :disabled="disabled" :board-id="fullBoardId" />
|
||||||
<board-settings-sidebar />
|
<board-settings-sidebar />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -3,12 +3,24 @@ import { GlAlert } from '@gitlab/ui';
|
||||||
import { sortBy, throttle } from 'lodash';
|
import { sortBy, throttle } from 'lodash';
|
||||||
import Draggable from 'vuedraggable';
|
import Draggable from 'vuedraggable';
|
||||||
import { mapState, mapGetters, mapActions } from 'vuex';
|
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 BoardAddNewColumn from 'ee_else_ce/boards/components/board_add_new_column.vue';
|
||||||
import { defaultSortableOptions } from '~/sortable/constants';
|
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';
|
import BoardColumn from './board_column.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
i18n: {
|
||||||
|
fetchError: s__(
|
||||||
|
'Boards|An error occurred while fetching the board lists. Please reload the page.',
|
||||||
|
),
|
||||||
|
},
|
||||||
draggableItemTypes: DraggableItemTypes,
|
draggableItemTypes: DraggableItemTypes,
|
||||||
components: {
|
components: {
|
||||||
BoardAddNewColumn,
|
BoardAddNewColumn,
|
||||||
|
@ -19,26 +31,76 @@ export default {
|
||||||
EpicsSwimlanes: () => import('ee_component/boards/components/epics_swimlanes.vue'),
|
EpicsSwimlanes: () => import('ee_component/boards/components/epics_swimlanes.vue'),
|
||||||
GlAlert,
|
GlAlert,
|
||||||
},
|
},
|
||||||
inject: ['canAdminList'],
|
inject: ['canAdminList', 'boardType', 'fullPath', 'issuableType', 'isApolloBoard'],
|
||||||
props: {
|
props: {
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
boardId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
boardHeight: null,
|
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: {
|
computed: {
|
||||||
...mapState(['boardLists', 'error', 'addColumnForm']),
|
...mapState(['boardLists', 'error', 'addColumnForm']),
|
||||||
...mapGetters(['isSwimlanesOn', 'isEpicBoard', 'isIssueBoard']),
|
...mapGetters(['isSwimlanesOn']),
|
||||||
|
isIssueBoard() {
|
||||||
|
return this.issuableType === issuableTypes.issue;
|
||||||
|
},
|
||||||
|
isEpicBoard() {
|
||||||
|
return this.issuableType === issuableTypes.epic;
|
||||||
|
},
|
||||||
addColumnFormVisible() {
|
addColumnFormVisible() {
|
||||||
return this.addColumnForm?.visible;
|
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() {
|
boardListsToUse() {
|
||||||
return sortBy([...Object.values(this.boardLists)], 'position');
|
const lists = this.isApolloBoard ? this.boardListsApollo : this.boardLists;
|
||||||
|
return sortBy([...Object.values(lists)], 'position');
|
||||||
},
|
},
|
||||||
canDragColumns() {
|
canDragColumns() {
|
||||||
return this.canAdminList;
|
return this.canAdminList;
|
||||||
|
@ -59,6 +121,9 @@ export default {
|
||||||
|
|
||||||
return this.canDragColumns ? options : {};
|
return this.canDragColumns ? options : {};
|
||||||
},
|
},
|
||||||
|
errorToDisplay() {
|
||||||
|
return this.isApolloBoard ? this.apolloError : this.error;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.setBoardHeight();
|
this.setBoardHeight();
|
||||||
|
@ -88,8 +153,8 @@ export default {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-cloak data-qa-selector="boards_list">
|
<div v-cloak data-qa-selector="boards_list">
|
||||||
<gl-alert v-if="error" variant="danger" :dismissible="true" @dismiss="unsetError">
|
<gl-alert v-if="errorToDisplay" variant="danger" :dismissible="true" @dismiss="unsetError">
|
||||||
{{ error }}
|
{{ errorToDisplay }}
|
||||||
</gl-alert>
|
</gl-alert>
|
||||||
<component
|
<component
|
||||||
:is="boardColumnWrapper"
|
:is="boardColumnWrapper"
|
||||||
|
|
|
@ -53,6 +53,8 @@ function mountBoardApp(el) {
|
||||||
store,
|
store,
|
||||||
apolloProvider,
|
apolloProvider,
|
||||||
provide: {
|
provide: {
|
||||||
|
isApolloBoard: window.gon?.features?.apolloBoards,
|
||||||
|
fullBoardId: fullBoardId(boardId),
|
||||||
disabled: parseBoolean(el.dataset.disabled),
|
disabled: parseBoolean(el.dataset.disabled),
|
||||||
groupId: Number(groupId),
|
groupId: Number(groupId),
|
||||||
rootPath,
|
rootPath,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import issuableEventHub from '~/issues/list/eventhub';
|
import issuableEventHub from '~/issues/list/eventhub';
|
||||||
import LabelsSelect from '~/labels/labels_select';
|
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';
|
import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
|
||||||
|
|
||||||
const HIDDEN_CLASS = 'hidden';
|
const HIDDEN_CLASS = 'hidden';
|
||||||
|
@ -55,7 +55,7 @@ export default class IssuableBulkUpdateSidebar {
|
||||||
|
|
||||||
initDropdowns() {
|
initDropdowns() {
|
||||||
new LabelsSelect();
|
new LabelsSelect();
|
||||||
new MilestoneSelect();
|
mountMilestoneDropdown();
|
||||||
|
|
||||||
// Checking IS_EE and using ee_else_ce is odd, but we do it here to satisfy
|
// 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
|
// the import/no-unresolved lint rule when FOSS_ONLY=1, even though at
|
||||||
|
|
|
@ -29,7 +29,7 @@ export default {
|
||||||
dagDocPath: {
|
dagDocPath: {
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
emptySvgPath: {
|
emptyDagSvgPath: {
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
pipelineIid: {
|
pipelineIid: {
|
||||||
|
@ -213,7 +213,7 @@ export default {
|
||||||
/>
|
/>
|
||||||
<gl-empty-state
|
<gl-empty-state
|
||||||
v-else-if="hasNoDependentJobs"
|
v-else-if="hasNoDependentJobs"
|
||||||
:svg-path="emptySvgPath"
|
:svg-path="emptyDagSvgPath"
|
||||||
:title="$options.emptyStateTexts.title"
|
:title="$options.emptyStateTexts.title"
|
||||||
>
|
>
|
||||||
<template #description>
|
<template #description>
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import { GlBadge, GlTabs, GlTab } from '@gitlab/ui';
|
import { GlBadge, GlTabs, GlTab } from '@gitlab/ui';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
import { failedJobsTabName, jobsTabName, needsTabName, testReportTabName } from '../constants';
|
import {
|
||||||
import PipelineGraphWrapper from './graph/graph_component_wrapper.vue';
|
failedJobsTabName,
|
||||||
import Dag from './dag/dag.vue';
|
jobsTabName,
|
||||||
import FailedJobsApp from './jobs/failed_jobs_app.vue';
|
needsTabName,
|
||||||
import JobsApp from './jobs/jobs_app.vue';
|
pipelineTabName,
|
||||||
import TestReports from './test_reports/test_reports.vue';
|
testReportTabName,
|
||||||
|
} from '../constants';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
i18n: {
|
i18n: {
|
||||||
|
@ -19,20 +20,16 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tabNames: {
|
tabNames: {
|
||||||
|
pipeline: pipelineTabName,
|
||||||
needs: needsTabName,
|
needs: needsTabName,
|
||||||
jobs: jobsTabName,
|
jobs: jobsTabName,
|
||||||
failures: failedJobsTabName,
|
failures: failedJobsTabName,
|
||||||
tests: testReportTabName,
|
tests: testReportTabName,
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Dag,
|
|
||||||
GlBadge,
|
GlBadge,
|
||||||
GlTab,
|
GlTab,
|
||||||
GlTabs,
|
GlTabs,
|
||||||
JobsApp,
|
|
||||||
FailedJobsApp,
|
|
||||||
PipelineGraphWrapper,
|
|
||||||
TestReports,
|
|
||||||
},
|
},
|
||||||
inject: [
|
inject: [
|
||||||
'defaultTabValue',
|
'defaultTabValue',
|
||||||
|
@ -41,14 +38,27 @@ export default {
|
||||||
'totalJobCount',
|
'totalJobCount',
|
||||||
'testsCount',
|
'testsCount',
|
||||||
],
|
],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeTab: this.defaultTabValue,
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
showFailedJobsTab() {
|
showFailedJobsTab() {
|
||||||
return this.failedJobsCount > 0;
|
return this.failedJobsCount > 0;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
$route(to) {
|
||||||
|
this.activeTab = to.name;
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isActive(tabName) {
|
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
|
<gl-tab
|
||||||
ref="pipelineTab"
|
ref="pipelineTab"
|
||||||
:title="$options.i18n.tabs.pipelineTitle"
|
:title="$options.i18n.tabs.pipelineTitle"
|
||||||
|
:active="isActive($options.tabNames.pipeline)"
|
||||||
data-testid="pipeline-tab"
|
data-testid="pipeline-tab"
|
||||||
lazy
|
lazy
|
||||||
|
@click="navigateTo($options.tabNames.pipeline)"
|
||||||
>
|
>
|
||||||
<pipeline-graph-wrapper />
|
<router-view />
|
||||||
</gl-tab>
|
</gl-tab>
|
||||||
<gl-tab
|
<gl-tab
|
||||||
ref="dagTab"
|
ref="dagTab"
|
||||||
|
@ -70,15 +82,21 @@ export default {
|
||||||
:active="isActive($options.tabNames.needs)"
|
:active="isActive($options.tabNames.needs)"
|
||||||
data-testid="dag-tab"
|
data-testid="dag-tab"
|
||||||
lazy
|
lazy
|
||||||
|
@click="navigateTo($options.tabNames.needs)"
|
||||||
>
|
>
|
||||||
<dag />
|
<router-view />
|
||||||
</gl-tab>
|
</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>
|
<template #title>
|
||||||
<span class="gl-mr-2">{{ $options.i18n.tabs.jobsTitle }}</span>
|
<span class="gl-mr-2">{{ $options.i18n.tabs.jobsTitle }}</span>
|
||||||
<gl-badge size="sm" data-testid="builds-counter">{{ totalJobCount }}</gl-badge>
|
<gl-badge size="sm" data-testid="builds-counter">{{ totalJobCount }}</gl-badge>
|
||||||
</template>
|
</template>
|
||||||
<jobs-app />
|
<router-view />
|
||||||
</gl-tab>
|
</gl-tab>
|
||||||
<gl-tab
|
<gl-tab
|
||||||
v-if="showFailedJobsTab"
|
v-if="showFailedJobsTab"
|
||||||
|
@ -86,19 +104,25 @@ export default {
|
||||||
:active="isActive($options.tabNames.failures)"
|
:active="isActive($options.tabNames.failures)"
|
||||||
data-testid="failed-jobs-tab"
|
data-testid="failed-jobs-tab"
|
||||||
lazy
|
lazy
|
||||||
|
@click="navigateTo($options.tabNames.failures)"
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<span class="gl-mr-2">{{ $options.i18n.tabs.failedJobsTitle }}</span>
|
<span class="gl-mr-2">{{ $options.i18n.tabs.failedJobsTitle }}</span>
|
||||||
<gl-badge size="sm" data-testid="failed-builds-counter">{{ failedJobsCount }}</gl-badge>
|
<gl-badge size="sm" data-testid="failed-builds-counter">{{ failedJobsCount }}</gl-badge>
|
||||||
</template>
|
</template>
|
||||||
<failed-jobs-app :failed-jobs-summary="failedJobsSummary" />
|
<router-view :failed-jobs-summary="failedJobsSummary" />
|
||||||
</gl-tab>
|
</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>
|
<template #title>
|
||||||
<span class="gl-mr-2">{{ $options.i18n.tabs.testsTitle }}</span>
|
<span class="gl-mr-2">{{ $options.i18n.tabs.testsTitle }}</span>
|
||||||
<gl-badge size="sm" data-testid="tests-counter">{{ testsCount }}</gl-badge>
|
<gl-badge size="sm" data-testid="tests-counter">{{ testsCount }}</gl-badge>
|
||||||
</template>
|
</template>
|
||||||
<test-reports />
|
<router-view />
|
||||||
</gl-tab>
|
</gl-tab>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</gl-tabs>
|
</gl-tabs>
|
||||||
|
|
|
@ -47,8 +47,7 @@ export const CHILD_VIEW = 'child';
|
||||||
|
|
||||||
// Pipeline tabs
|
// Pipeline tabs
|
||||||
|
|
||||||
export const TAB_QUERY_PARAM = 'tab';
|
export const pipelineTabName = 'graph';
|
||||||
|
|
||||||
export const needsTabName = 'dag';
|
export const needsTabName = 'dag';
|
||||||
export const jobsTabName = 'builds';
|
export const jobsTabName = 'builds';
|
||||||
export const failedJobsTabName = 'failures';
|
export const failedJobsTabName = 'failures';
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import VueRouter from 'vue-router';
|
||||||
import { createAlert } from '~/flash';
|
import { createAlert } from '~/flash';
|
||||||
import { __, s__ } from '~/locale';
|
import { __, s__ } from '~/locale';
|
||||||
import createDagApp from './pipeline_details_dag';
|
import createDagApp from './pipeline_details_dag';
|
||||||
|
@ -32,9 +33,16 @@ export default async function initPipelineDetailsBundle() {
|
||||||
if (gon.features?.pipelineTabsVue) {
|
if (gon.features?.pipelineTabsVue) {
|
||||||
const { createAppOptions } = await import('ee_else_ce/pipelines/pipeline_tabs');
|
const { createAppOptions } = await import('ee_else_ce/pipelines/pipeline_tabs');
|
||||||
const { createPipelineTabs } = await import('./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 {
|
try {
|
||||||
const appOptions = createAppOptions(SELECTORS.PIPELINE_TABS, apolloProvider);
|
const appOptions = createAppOptions(SELECTORS.PIPELINE_TABS, apolloProvider, router);
|
||||||
createPipelineTabs(appOptions);
|
createPipelineTabs(appOptions);
|
||||||
} catch {
|
} catch {
|
||||||
createAlert({
|
createAlert({
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import VueRouter from 'vue-router';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import VueApollo from 'vue-apollo';
|
import VueApollo from 'vue-apollo';
|
||||||
import PipelineTabs from 'ee_else_ce/pipelines/components/pipeline_tabs.vue';
|
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 { parseBoolean } from '~/lib/utils/common_utils';
|
||||||
import createTestReportsStore from './stores/test_reports';
|
import createTestReportsStore from './stores/test_reports';
|
||||||
import { getPipelineDefaultTab, reportToSentry } from './utils';
|
import { getPipelineDefaultTab, reportToSentry } from './utils';
|
||||||
|
|
||||||
Vue.use(VueApollo);
|
Vue.use(VueApollo);
|
||||||
|
Vue.use(VueRouter);
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
export const createAppOptions = (selector, apolloProvider) => {
|
export const createAppOptions = (selector, apolloProvider, router) => {
|
||||||
const el = document.querySelector(selector);
|
const el = document.querySelector(selector);
|
||||||
|
|
||||||
if (!el) return null;
|
if (!el) return null;
|
||||||
|
@ -40,6 +40,7 @@ export const createAppOptions = (selector, apolloProvider) => {
|
||||||
suiteEndpoint,
|
suiteEndpoint,
|
||||||
blobPath,
|
blobPath,
|
||||||
hasTestReport,
|
hasTestReport,
|
||||||
|
emptyDagSvgPath,
|
||||||
emptyStateImagePath,
|
emptyStateImagePath,
|
||||||
artifactsExpiredImagePath,
|
artifactsExpiredImagePath,
|
||||||
isFullCodequalityReportAvailable,
|
isFullCodequalityReportAvailable,
|
||||||
|
@ -65,6 +66,7 @@ export const createAppOptions = (selector, apolloProvider) => {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
router,
|
||||||
provide: {
|
provide: {
|
||||||
canGenerateCodequalityReports: parseBoolean(canGenerateCodequalityReports),
|
canGenerateCodequalityReports: parseBoolean(canGenerateCodequalityReports),
|
||||||
codequalityReportDownloadPath,
|
codequalityReportDownloadPath,
|
||||||
|
@ -91,6 +93,7 @@ export const createAppOptions = (selector, apolloProvider) => {
|
||||||
suiteEndpoint,
|
suiteEndpoint,
|
||||||
blobPath,
|
blobPath,
|
||||||
hasTestReport,
|
hasTestReport,
|
||||||
|
emptyDagSvgPath,
|
||||||
emptyStateImagePath,
|
emptyStateImagePath,
|
||||||
artifactsExpiredImagePath,
|
artifactsExpiredImagePath,
|
||||||
testsCount,
|
testsCount,
|
||||||
|
@ -107,12 +110,6 @@ export const createAppOptions = (selector, apolloProvider) => {
|
||||||
export const createPipelineTabs = (options) => {
|
export const createPipelineTabs = (options) => {
|
||||||
if (!options) return;
|
if (!options) return;
|
||||||
|
|
||||||
updateHistory({
|
|
||||||
url: removeParams([TAB_QUERY_PARAM]),
|
|
||||||
title: document.title,
|
|
||||||
replace: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new Vue(options);
|
new Vue(options);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 * as Sentry from '@sentry/browser';
|
||||||
import { pickBy } from 'lodash';
|
import { pickBy } from 'lodash';
|
||||||
import { getParameterValues } from '~/lib/utils/url_utility';
|
import { parseUrlPathname } from '~/lib/utils/url_utility';
|
||||||
import {
|
import { NEEDS_PROPERTY, SUPPORTED_FILTER_PARAMETERS, validPipelineTabNames } from './constants';
|
||||||
NEEDS_PROPERTY,
|
|
||||||
SUPPORTED_FILTER_PARAMETERS,
|
|
||||||
TAB_QUERY_PARAM,
|
|
||||||
validPipelineTabNames,
|
|
||||||
} from './constants';
|
|
||||||
/*
|
/*
|
||||||
The following functions are the main engine in transforming the data as
|
The following functions are the main engine in transforming the data as
|
||||||
received from the endpoint into the format the d3 graph expects.
|
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) => {
|
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)) {
|
if (tabName && validPipelineTabNames.includes(tabName)) {
|
||||||
return tabQueryValue;
|
return tabName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
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,
|
GlSearchBoxByType,
|
||||||
} from '@gitlab/ui';
|
} from '@gitlab/ui';
|
||||||
import { kebabCase, snakeCase } from 'lodash';
|
import { kebabCase, snakeCase } from 'lodash';
|
||||||
import { IssuableType } from '~/issues/constants';
|
import { IssuableType, WorkspaceType } from '~/issues/constants';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
import {
|
import {
|
||||||
defaultEpicSort,
|
defaultEpicSort,
|
||||||
|
@ -73,6 +73,14 @@ export default {
|
||||||
return [IssuableType.Issue, IssuableType.MergeRequest].includes(value);
|
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() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -86,7 +94,7 @@ export default {
|
||||||
query() {
|
query() {
|
||||||
const { list } = this.issuableAttributeQuery;
|
const { list } = this.issuableAttributeQuery;
|
||||||
const { query } = list[this.issuableType];
|
const { query } = list[this.issuableType];
|
||||||
return query;
|
return query[this.workspaceType] || query;
|
||||||
},
|
},
|
||||||
variables() {
|
variables() {
|
||||||
if (!this.isEpic) {
|
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 updateMergeRequestAssigneesMutation from '~/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql';
|
||||||
import getEscalationStatusQuery from '~/sidebar/queries/escalation_status.query.graphql';
|
import getEscalationStatusQuery from '~/sidebar/queries/escalation_status.query.graphql';
|
||||||
import updateEscalationStatusMutation from '~/sidebar/queries/update_escalation_status.mutation.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 projectIssueMilestoneMutation from './queries/project_issue_milestone.mutation.graphql';
|
||||||
import projectIssueMilestoneQuery from './queries/project_issue_milestone.query.graphql';
|
import projectIssueMilestoneQuery from './queries/project_issue_milestone.query.graphql';
|
||||||
import projectMilestonesQuery from './queries/project_milestones.query.graphql';
|
import projectMilestonesQuery from './queries/project_milestones.query.graphql';
|
||||||
|
@ -241,10 +242,16 @@ export const issuableMilestoneQueries = {
|
||||||
|
|
||||||
export const milestonesQueries = {
|
export const milestonesQueries = {
|
||||||
[IssuableType.Issue]: {
|
[IssuableType.Issue]: {
|
||||||
query: projectMilestonesQuery,
|
query: {
|
||||||
|
[WorkspaceType.group]: groupMilestonesQuery,
|
||||||
|
[WorkspaceType.project]: projectMilestonesQuery,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
[IssuableType.MergeRequest]: {
|
[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 SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue';
|
||||||
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
|
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
|
||||||
import SidebarDueDateWidget from '~/sidebar/components/date/sidebar_date_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 SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue';
|
||||||
import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue';
|
import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue';
|
||||||
import SidebarDropdownWidget from '~/sidebar/components/sidebar_dropdown_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() {
|
export function mountSidebarLabels() {
|
||||||
const el = document.querySelector('.js-sidebar-labels');
|
const el = document.querySelector('.js-sidebar-labels');
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ class Groups::BoardsController < Groups::ApplicationController
|
||||||
|
|
||||||
before_action do
|
before_action do
|
||||||
push_frontend_feature_flag(:board_multi_select, group)
|
push_frontend_feature_flag(:board_multi_select, group)
|
||||||
|
push_frontend_feature_flag(:apollo_boards, group)
|
||||||
push_frontend_feature_flag(:realtime_labels, group)
|
push_frontend_feature_flag(:realtime_labels, group)
|
||||||
experiment(:prominent_create_board_btn, subject: current_user) do |e|
|
experiment(:prominent_create_board_btn, subject: current_user) do |e|
|
||||||
e.control {}
|
e.control {}
|
||||||
|
|
|
@ -7,6 +7,7 @@ class Projects::BoardsController < Projects::ApplicationController
|
||||||
before_action :check_issues_available!
|
before_action :check_issues_available!
|
||||||
before_action do
|
before_action do
|
||||||
push_frontend_feature_flag(:board_multi_select, project)
|
push_frontend_feature_flag(:board_multi_select, project)
|
||||||
|
push_frontend_feature_flag(:apollo_boards, project)
|
||||||
push_frontend_feature_flag(:realtime_labels, project&.group)
|
push_frontend_feature_flag(:realtime_labels, project&.group)
|
||||||
experiment(:prominent_create_board_btn, subject: current_user) do |e|
|
experiment(:prominent_create_board_btn, subject: current_user) do |e|
|
||||||
e.control {}
|
e.control {}
|
||||||
|
|
|
@ -140,22 +140,14 @@ class Projects::PipelinesController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def builds
|
def builds
|
||||||
if Feature.enabled?(:pipeline_tabs_vue, project)
|
|
||||||
redirect_to pipeline_path(@pipeline, tab: 'builds')
|
|
||||||
else
|
|
||||||
render_show
|
render_show
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def dag
|
def dag
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do
|
format.html do
|
||||||
if Feature.enabled?(:pipeline_tabs_vue, project)
|
|
||||||
redirect_to pipeline_path(@pipeline, tab: 'dag')
|
|
||||||
else
|
|
||||||
render_show
|
render_show
|
||||||
end
|
end
|
||||||
end
|
|
||||||
format.json do
|
format.json do
|
||||||
render json: Ci::DagPipelineSerializer
|
render json: Ci::DagPipelineSerializer
|
||||||
.new(project: @project, current_user: @current_user)
|
.new(project: @project, current_user: @current_user)
|
||||||
|
@ -165,9 +157,7 @@ class Projects::PipelinesController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def failures
|
def failures
|
||||||
if Feature.enabled?(:pipeline_tabs_vue, project)
|
if @pipeline.failed_builds.present?
|
||||||
redirect_to pipeline_path(@pipeline, tab: 'failures')
|
|
||||||
elsif @pipeline.failed_builds.present?
|
|
||||||
render_show
|
render_show
|
||||||
else
|
else
|
||||||
redirect_to pipeline_path(@pipeline)
|
redirect_to pipeline_path(@pipeline)
|
||||||
|
@ -222,12 +212,8 @@ class Projects::PipelinesController < Projects::ApplicationController
|
||||||
def test_report
|
def test_report
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do
|
format.html do
|
||||||
if Feature.enabled?(:pipeline_tabs_vue, project)
|
|
||||||
redirect_to pipeline_path(@pipeline, tab: 'test_report')
|
|
||||||
else
|
|
||||||
render_show
|
render_show
|
||||||
end
|
end
|
||||||
end
|
|
||||||
format.json do
|
format.json do
|
||||||
render json: TestReportSerializer
|
render json: TestReportSerializer
|
||||||
.new(current_user: @current_user)
|
.new(current_user: @current_user)
|
||||||
|
|
|
@ -19,6 +19,7 @@ module Projects
|
||||||
blob_path: project_blob_path(project, pipeline.sha),
|
blob_path: project_blob_path(project, pipeline.sha),
|
||||||
has_test_report: pipeline.has_test_reports?,
|
has_test_report: pipeline.has_test_reports?,
|
||||||
empty_state_image_path: image_path('illustrations/empty-state/empty-test-cases-lg.svg'),
|
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'),
|
artifacts_expired_image_path: image_path('illustrations/pipeline.svg'),
|
||||||
tests_count: pipeline.test_report_summary.total[:count]
|
tests_count: pipeline.test_report_summary.total[:count]
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,7 @@ module AlertManagement
|
||||||
key: Settings.attr_encrypted_db_key_base_32,
|
key: Settings.attr_encrypted_db_key_base_32,
|
||||||
algorithm: 'aes-256-gcm'
|
algorithm: 'aes-256-gcm'
|
||||||
|
|
||||||
default_value_for(:endpoint_identifier, allows_nil: false) { SecureRandom.hex(8) }
|
attribute :endpoint_identifier, default: -> { SecureRandom.hex(8) }
|
||||||
default_value_for(:token) { generate_token }
|
|
||||||
|
|
||||||
validates :project, presence: true
|
validates :project, presence: true
|
||||||
validates :active, inclusion: { in: [true, false] }
|
validates :active, inclusion: { in: [true, false] }
|
||||||
|
|
|
@ -639,14 +639,6 @@ class ApplicationSetting < ApplicationRecord
|
||||||
validates :inactive_projects_send_warning_email_after_months,
|
validates :inactive_projects_send_warning_email_after_months,
|
||||||
numericality: { only_integer: true, greater_than: 0, less_than: :inactive_projects_delete_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,
|
attr_encrypted :asset_proxy_secret_key,
|
||||||
mode: :per_attribute_iv,
|
mode: :per_attribute_iv,
|
||||||
key: Settings.attr_encrypted_db_key_base_truncated,
|
key: Settings.attr_encrypted_db_key_base_truncated,
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
class ProjectImportEntity < ProjectEntity
|
class ProjectImportEntity < ProjectEntity
|
||||||
include ImportHelper
|
include ImportHelper
|
||||||
|
|
||||||
expose :import_source
|
expose :import_source, documentation: { type: 'string', example: 'source/source-repo' }
|
||||||
expose :import_status
|
expose :import_status, documentation: {
|
||||||
expose :human_import_status_name
|
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])
|
provider_project_link_url(options[:provider_url], project[:import_source])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -119,6 +119,7 @@
|
||||||
= render_if_exists 'admin/application_settings/feishu_integration'
|
= render_if_exists 'admin/application_settings/feishu_integration'
|
||||||
= render 'admin/application_settings/third_party_offers'
|
= render 'admin/application_settings/third_party_offers'
|
||||||
= render 'admin/application_settings/snowplow'
|
= 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/error_tracking' if Feature.enabled?(:gitlab_error_tracking)
|
||||||
= render 'admin/application_settings/eks'
|
= render 'admin/application_settings/eks'
|
||||||
= render 'admin/application_settings/floc'
|
= render 'admin/application_settings/floc'
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
= c.body do
|
= 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_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 }
|
- 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 }
|
- 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 }
|
= 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|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|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|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
|
.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 } }
|
#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) }
|
#js-pipeline-tabs{ data: js_pipeline_tabs_data(@project, @pipeline, @current_user) }
|
||||||
- else
|
- else
|
||||||
= render "projects/pipelines/with_tabs", pipeline: @pipeline, stages: @stages, pipeline_has_errors: pipeline_has_errors
|
= 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
|
.title
|
||||||
= _('Milestone')
|
= _('Milestone')
|
||||||
.filter-item
|
.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
|
- if is_issue
|
||||||
= render_if_exists 'shared/issuable/iterations_dropdown', parent: @project.group
|
= render_if_exists 'shared/issuable/iterations_dropdown', parent: @project.group
|
||||||
- if is_issue
|
- if is_issue
|
||||||
|
|
|
@ -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
|
description: Operations related to project hooks
|
||||||
- name: project_import_bitbucket
|
- name: project_import_bitbucket
|
||||||
description: Operations related to import BitBucket projects
|
description: Operations related to import BitBucket projects
|
||||||
|
- name: project_import_github
|
||||||
|
description: Operations related to import GitHub projects
|
||||||
- name: release_links
|
- name: release_links
|
||||||
description: Operations related to release assets (links)
|
description: Operations related to release assets (links)
|
||||||
- name: releases
|
- 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
|
|
@ -0,0 +1 @@
|
||||||
|
7ddb85c1acfd3fbeddbe96857d329ad09cd21210e6765ff36d4b9f516a7c10be
|
|
@ -32626,9 +32626,6 @@ ALTER TABLE ONLY service_desk_settings
|
||||||
ALTER TABLE ONLY design_management_designs_versions
|
ALTER TABLE ONLY design_management_designs_versions
|
||||||
ADD CONSTRAINT fk_03c671965c FOREIGN KEY (design_id) REFERENCES design_management_designs(id) ON DELETE CASCADE;
|
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
|
ALTER TABLE ONLY issues
|
||||||
ADD CONSTRAINT fk_05f1e72feb FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL;
|
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 Kubernetes](https://docs.gitlab.com/charts/troubleshooting/kubernetes_cheat_sheet.html)
|
||||||
- [Troubleshooting PostgreSQL](troubleshooting/postgresql.md)
|
- [Troubleshooting PostgreSQL](troubleshooting/postgresql.md)
|
||||||
- [Guide to test environments](troubleshooting/test_environments.md) (for Support Engineers)
|
- [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)
|
- [Troubleshooting SSL](troubleshooting/ssl.md)
|
||||||
- Related links:
|
- Related links:
|
||||||
- [GitLab Developer Documentation](../development/index.md)
|
- [GitLab Developer Documentation](../development/index.md)
|
||||||
- [Repairing and recovering broken Git repositories](https://git.seveas.net/repairing-and-recovering-broken-git-repositories.html)
|
- [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)
|
- [Testing with OpenSSL](https://www.feistyduck.com/library/openssl-cookbook/online/testing-with-openssl/index.html)
|
||||||
- [`strace` zine](https://wizardzines.com/zines/strace/)
|
- [`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
|
redirect_to: 'index.md'
|
||||||
group: Distribution
|
remove_date: '2023-02-01'
|
||||||
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
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 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
|
<!-- This redirect file can be deleted after 2023-02-01. -->
|
||||||
console, for use while troubleshooting. It is listed here for transparency,
|
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||||
and for users with experience with these tools. If you are currently
|
<!-- 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. -->
|
||||||
having an issue with GitLab, it is highly recommended that you first check
|
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
|
||||||
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).
|
|
||||||
|
|
|
@ -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
|
This page documents a collection of resources to help you troubleshoot a GitLab
|
||||||
installation.
|
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
|
## Troubleshooting guides
|
||||||
|
|
||||||
- [SSL](ssl.md)
|
- [SSL](ssl.md)
|
||||||
- [Geo](../geo/replication/troubleshooting.md)
|
- [Geo](../geo/replication/troubleshooting.md)
|
||||||
- [GitLab Rails console cheat sheet](gitlab_rails_cheat_sheet.md)
|
- [SAML](../../user/group/saml_sso/troubleshooting.md)
|
||||||
- [Example group SAML and SCIM configurations](../../user/group/saml_sso/example_saml_config.md)
|
|
||||||
- [Kubernetes cheat sheet](https://docs.gitlab.com/charts/troubleshooting/kubernetes_cheat_sheet.html)
|
- [Kubernetes cheat sheet](https://docs.gitlab.com/charts/troubleshooting/kubernetes_cheat_sheet.html)
|
||||||
- [Linux cheat sheet](linux_cheat_sheet.md)
|
- [Linux cheat sheet](linux_cheat_sheet.md)
|
||||||
- [Parsing GitLab logs with `jq`](../logs/log_parsing.md)
|
- [Parsing GitLab logs with `jq`](../logs/log_parsing.md)
|
||||||
- [Diagnostics tools](diagnostics_tools.md)
|
- [Diagnostics tools](diagnostics_tools.md)
|
||||||
|
|
||||||
Some feature documentation pages also have a troubleshooting section at the end
|
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
|
If you need a testing environment to troubleshoot, see the
|
||||||
[apps for a testing environment](test_environments.md).
|
[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.
|
- 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.
|
- 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
|
## Upgrade your GitLab SaaS subscription tier
|
||||||
|
|
||||||
To upgrade your [GitLab tier](https://about.gitlab.com/pricing/):
|
To upgrade your [GitLab tier](https://about.gitlab.com/pricing/):
|
||||||
|
|
|
@ -187,6 +187,7 @@ module API
|
||||||
mount ::API::FreezePeriods
|
mount ::API::FreezePeriods
|
||||||
mount ::API::Keys
|
mount ::API::Keys
|
||||||
mount ::API::ImportBitbucketServer
|
mount ::API::ImportBitbucketServer
|
||||||
|
mount ::API::ImportGithub
|
||||||
mount ::API::Metadata
|
mount ::API::Metadata
|
||||||
mount ::API::MergeRequestDiffs
|
mount ::API::MergeRequestDiffs
|
||||||
mount ::API::ProjectHooks
|
mount ::API::ProjectHooks
|
||||||
|
@ -261,7 +262,6 @@ module API
|
||||||
mount ::API::GroupVariables
|
mount ::API::GroupVariables
|
||||||
mount ::API::Groups
|
mount ::API::Groups
|
||||||
mount ::API::HelmPackages
|
mount ::API::HelmPackages
|
||||||
mount ::API::ImportGithub
|
|
||||||
mount ::API::Integrations
|
mount ::API::Integrations
|
||||||
mount ::API::Integrations::JiraConnect::Subscriptions
|
mount ::API::Integrations::JiraConnect::Subscriptions
|
||||||
mount ::API::Invitations
|
mount ::API::Invitations
|
||||||
|
|
|
@ -6,15 +6,18 @@ module API
|
||||||
include ::API::ProjectsRelationBuilder
|
include ::API::ProjectsRelationBuilder
|
||||||
include Gitlab::Utils::StrongMemoize
|
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
|
# 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: :tag_list, documentation: { type: 'string', is_array: true, example: 'tag' }
|
||||||
expose :topic_names, as: :topics
|
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
|
license = project.repository.license_blob
|
||||||
|
|
||||||
if license
|
if license
|
||||||
|
@ -26,13 +29,13 @@ module API
|
||||||
project.repository.license
|
project.repository.license
|
||||||
end
|
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)
|
project.avatar_url(only_path: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :forks_count
|
expose :forks_count, documentation: { type: 'integer', example: 1 }
|
||||||
expose :star_count
|
expose :star_count, documentation: { type: 'integer', example: 1 }
|
||||||
expose :last_activity_at
|
expose :last_activity_at, documentation: { type: 'dateTime', example: '2013-09-30T13:46:02Z' }
|
||||||
expose :namespace, using: 'API::Entities::NamespaceBasic'
|
expose :namespace, using: 'API::Entities::NamespaceBasic'
|
||||||
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
|
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
module API
|
module API
|
||||||
module Entities
|
module Entities
|
||||||
class BasicRepositoryStorageMove < Grape::Entity
|
class BasicRepositoryStorageMove < Grape::Entity
|
||||||
expose :id
|
expose :id, documentation: { type: 'integer', example: 1 }
|
||||||
expose :created_at
|
expose :created_at, documentation: { type: 'dateTime', example: '2020-05-07T04:27:17.234Z' }
|
||||||
expose :human_state_name, as: :state
|
expose :human_state_name, as: :state, documentation: { type: 'string', example: 'scheduled' }
|
||||||
expose :source_storage_name
|
expose :source_storage_name, documentation: { type: 'string', example: 'default' }
|
||||||
expose :destination_storage_name
|
expose :destination_storage_name, documentation: { type: 'string', example: 'storage1' }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,148 +5,148 @@ module API
|
||||||
class Project < BasicProjectDetails
|
class Project < BasicProjectDetails
|
||||||
include ::API::Helpers::RelatedResourcesHelpers
|
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 :_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))
|
expose_url(api_v4_projects_path(id: project.id))
|
||||||
end
|
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))
|
expose_url(api_v4_projects_issues_path(id: project.id))
|
||||||
end
|
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))
|
expose_url(api_v4_projects_merge_requests_path(id: project.id))
|
||||||
end
|
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))
|
expose_url(api_v4_projects_repository_branches_path(id: project.id))
|
||||||
end
|
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))
|
expose_url(api_v4_projects_labels_path(id: project.id))
|
||||||
end
|
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))
|
expose_url(api_v4_projects_events_path(id: project.id))
|
||||||
end
|
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))
|
expose_url(api_v4_projects_members_path(id: project.id))
|
||||||
end
|
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))
|
expose_url(api_v4_projects_cluster_agents_path(id: project.id))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :packages_enabled
|
expose :packages_enabled, documentation: { type: 'boolean' }
|
||||||
expose :empty_repo?, as: :empty_repo
|
expose :empty_repo?, as: :empty_repo, documentation: { type: 'boolean' }
|
||||||
expose :archived?, as: :archived
|
expose :archived?, as: :archived, documentation: { type: 'boolean' }
|
||||||
expose :visibility
|
expose :visibility, documentation: { type: 'string', example: 'public' }
|
||||||
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
|
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,
|
expose :container_expiration_policy,
|
||||||
using: Entities::ContainerExpirationPolicy,
|
using: Entities::ContainerExpirationPolicy,
|
||||||
if: -> (project, _) { project.container_expiration_policy }
|
if: -> (project, _) { project.container_expiration_policy }
|
||||||
|
|
||||||
# Expose old field names with the new permissions methods to keep API compatible
|
# Expose old field names with the new permissions methods to keep API compatible
|
||||||
# TODO: remove in API v5, replaced by *_access_level
|
# TODO: remove in API v5, replaced by *_access_level
|
||||||
expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) }
|
expose(:issues_enabled, documentation: { type: 'boolean' }) { |project, options| project.feature_available?(:issues, options[:current_user]) }
|
||||||
expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) }
|
expose(:merge_requests_enabled, documentation: { type: 'boolean' }) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) }
|
||||||
expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
|
expose(:wiki_enabled, documentation: { type: 'boolean' }) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
|
||||||
expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
|
expose(:jobs_enabled, documentation: { type: 'boolean' }) { |project, options| project.feature_available?(:builds, options[:current_user]) }
|
||||||
expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
|
expose(:snippets_enabled, documentation: { type: 'boolean' }) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
|
||||||
expose(:container_registry_enabled) { |project, options| project.feature_available?(:container_registry, options[:current_user]) }
|
expose(:container_registry_enabled, documentation: { type: 'boolean' }) { |project, options| project.feature_available?(:container_registry, options[:current_user]) }
|
||||||
expose :service_desk_enabled
|
expose :service_desk_enabled, documentation: { type: 'boolean' }
|
||||||
expose :service_desk_address, if: -> (project, options) do
|
expose :service_desk_address, documentation: { type: 'string', example: 'address@example.com' }, if: -> (project, options) do
|
||||||
Ability.allowed?(options[:current_user], :admin_issue, project)
|
Ability.allowed?(options[:current_user], :admin_issue, project)
|
||||||
end
|
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)
|
Ability.allowed?(options[:current_user], :create_merge_request_in, project)
|
||||||
end
|
end
|
||||||
|
|
||||||
expose(:issues_access_level) { |project, options| project_feature_string_access_level(project, :issues) }
|
expose(:issues_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :issues) }
|
||||||
expose(:repository_access_level) { |project, options| project_feature_string_access_level(project, :repository) }
|
expose(:repository_access_level, documentation: { type: 'string', example: 'enabled' }) { |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(:merge_requests_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :merge_requests) }
|
||||||
expose(:forking_access_level) { |project, options| project_feature_string_access_level(project, :forking) }
|
expose(:forking_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :forking) }
|
||||||
expose(:wiki_access_level) { |project, options| project_feature_string_access_level(project, :wiki) }
|
expose(:wiki_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :wiki) }
|
||||||
expose(:builds_access_level) { |project, options| project_feature_string_access_level(project, :builds) }
|
expose(:builds_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :builds) }
|
||||||
expose(:snippets_access_level) { |project, options| project_feature_string_access_level(project, :snippets) }
|
expose(:snippets_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :snippets) }
|
||||||
expose(:pages_access_level) { |project, options| project_feature_string_access_level(project, :pages) }
|
expose(:pages_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :pages) }
|
||||||
expose(:operations_access_level) { |project, options| project_feature_string_access_level(project, :operations) }
|
expose(:operations_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :operations) }
|
||||||
expose(:analytics_access_level) { |project, options| project_feature_string_access_level(project, :analytics) }
|
expose(:analytics_access_level, documentation: { type: 'string', example: 'enabled' }) { |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(: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) { |project, options| project_feature_string_access_level(project, :security_and_compliance) }
|
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) { |project, options| project_feature_string_access_level(project, :releases) }
|
expose(:releases_access_level, documentation: { type: 'string', example: 'enabled' }) { |project, options| project_feature_string_access_level(project, :releases) }
|
||||||
|
|
||||||
expose :emails_disabled
|
expose :emails_disabled, documentation: { type: 'boolean' }
|
||||||
expose :shared_runners_enabled
|
expose :shared_runners_enabled, documentation: { type: 'boolean' }
|
||||||
expose :lfs_enabled?, as: :lfs_enabled
|
expose :lfs_enabled?, as: :lfs_enabled, documentation: { type: 'boolean' }
|
||||||
expose :creator_id
|
expose :creator_id, documentation: { type: 'integer', example: 1 }
|
||||||
expose :forked_from_project, using: Entities::BasicProjectDetails, if: ->(project, options) do
|
expose :forked_from_project, using: Entities::BasicProjectDetails, if: ->(project, options) do
|
||||||
project.forked? && Ability.allowed?(options[:current_user], :read_project, project.forked_from_project)
|
project.forked? && Ability.allowed?(options[:current_user], :read_project, project.forked_from_project)
|
||||||
end
|
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]
|
project[:import_url]
|
||||||
end
|
end
|
||||||
expose :import_type, if: -> (project, options) { Ability.allowed?(options[:current_user], :admin_project, project) }
|
expose :import_type, documentation: { type: 'string', example: 'git' }, if: -> (project, options) { Ability.allowed?(options[:current_user], :admin_project, project) }
|
||||||
expose :import_status
|
expose :import_status, documentation: { type: 'string', example: 'none' }
|
||||||
expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] } do |project|
|
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
|
project.import_state&.last_error
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) }
|
expose :open_issues_count, documentation: { type: 'integer', example: 1 }, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) }
|
||||||
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
|
expose :runners_token, documentation: { type: 'string', example: 'b8547b1dc37721d05889db52fa2f02' }, if: lambda { |_project, options| options[:user_can_admin_project] }
|
||||||
expose :ci_default_git_depth
|
expose :ci_default_git_depth, documentation: { type: 'integer', example: 20 }
|
||||||
expose :ci_forward_deployment_enabled
|
expose :ci_forward_deployment_enabled, documentation: { type: 'boolean' }
|
||||||
expose(:ci_job_token_scope_enabled) { |p, _| p.ci_outbound_job_token_scope_enabled? }
|
expose(:ci_job_token_scope_enabled, documentation: { type: 'boolean' }) { |p, _| p.ci_outbound_job_token_scope_enabled? }
|
||||||
expose :ci_separated_caches
|
expose :ci_separated_caches, documentation: { type: 'boolean' }
|
||||||
expose :ci_opt_in_jwt
|
expose :ci_opt_in_jwt, documentation: { type: 'boolean' }
|
||||||
expose :ci_allow_fork_pipelines_to_run_in_parent_project
|
expose :ci_allow_fork_pipelines_to_run_in_parent_project, documentation: { type: 'boolean' }
|
||||||
expose :public_builds, as: :public_jobs
|
expose :public_builds, as: :public_jobs, documentation: { type: 'boolean' }
|
||||||
expose :build_git_strategy, if: lambda { |project, options| options[:user_can_admin_project] } do |project, options|
|
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'
|
project.build_allow_git_fetch ? 'fetch' : 'clone'
|
||||||
end
|
end
|
||||||
expose :build_timeout
|
expose :build_timeout, documentation: { type: 'integer', example: 3600 }
|
||||||
expose :auto_cancel_pending_pipelines
|
expose :auto_cancel_pending_pipelines, documentation: { type: 'string', example: 'enabled' }
|
||||||
expose :ci_config_path, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
|
expose :ci_config_path, documentation: { type: 'string', example: '' }, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
|
||||||
expose :shared_with_groups do |project, options|
|
expose :shared_with_groups, documentation: { is_array: true } do |project, options|
|
||||||
user = options[:current_user]
|
user = options[:current_user]
|
||||||
|
|
||||||
SharedGroupWithProject.represent(project.visible_group_links(for_user: user), options)
|
SharedGroupWithProject.represent(project.visible_group_links(for_user: user), options)
|
||||||
end
|
end
|
||||||
expose :only_allow_merge_if_pipeline_succeeds
|
expose :only_allow_merge_if_pipeline_succeeds, documentation: { type: 'boolean' }
|
||||||
expose :allow_merge_on_skipped_pipeline
|
expose :allow_merge_on_skipped_pipeline, documentation: { type: 'boolean' }
|
||||||
expose :restrict_user_defined_variables
|
expose :restrict_user_defined_variables, documentation: { type: 'boolean' }
|
||||||
expose :request_access_enabled
|
expose :request_access_enabled, documentation: { type: 'boolean' }
|
||||||
expose :only_allow_merge_if_all_discussions_are_resolved
|
expose :only_allow_merge_if_all_discussions_are_resolved, documentation: { type: 'boolean' }
|
||||||
expose :remove_source_branch_after_merge
|
expose :remove_source_branch_after_merge, documentation: { type: 'boolean' }
|
||||||
expose :printing_merge_request_link_enabled
|
expose :printing_merge_request_link_enabled, documentation: { type: 'boolean' }
|
||||||
expose :merge_method
|
expose :merge_method, documentation: { type: 'string', example: 'merge' }
|
||||||
expose :squash_option
|
expose :squash_option, documentation: { type: 'string', example: 'default_off' }
|
||||||
expose :enforce_auth_checks_on_uploads
|
expose :enforce_auth_checks_on_uploads, documentation: { type: 'boolean' }
|
||||||
expose :suggestion_commit_message
|
expose :suggestion_commit_message, documentation: { type: 'string', example: 'Suggestion message' }
|
||||||
expose :merge_commit_template
|
expose :merge_commit_template, documentation: { type: 'string', example: '%(title)' }
|
||||||
expose :squash_commit_template
|
expose :squash_commit_template, documentation: { type: 'string', example: '%(source_branch)' }
|
||||||
expose :issue_branch_template
|
expose :issue_branch_template, documentation: { type: 'string', example: '%(title)' }
|
||||||
expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) {
|
expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) {
|
||||||
options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project)
|
options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project)
|
||||||
}
|
}
|
||||||
expose :auto_devops_enabled?, as: :auto_devops_enabled
|
expose :auto_devops_enabled?, as: :auto_devops_enabled, documentation: { type: 'boolean' }
|
||||||
expose :auto_devops_deploy_strategy do |project, options|
|
expose :auto_devops_deploy_strategy, documentation: { type: 'string', example: 'continuous' } do |project, options|
|
||||||
project.auto_devops.nil? ? 'continuous' : project.auto_devops.deploy_strategy
|
project.auto_devops.nil? ? 'continuous' : project.auto_devops.deploy_strategy
|
||||||
end
|
end
|
||||||
expose :autoclose_referenced_issues
|
expose :autoclose_referenced_issues, documentation: { type: 'boolean' }
|
||||||
expose :repository_storage, if: ->(project, options) {
|
expose :repository_storage, documentation: { type: 'string', example: 'default' }, if: ->(project, options) {
|
||||||
Ability.allowed?(options[:current_user], :change_repository_storage, project)
|
Ability.allowed?(options[:current_user], :change_repository_storage, project)
|
||||||
}
|
}
|
||||||
expose :keep_latest_artifacts_available?, as: :keep_latest_artifact
|
expose :keep_latest_artifacts_available?, as: :keep_latest_artifact, documentation: { type: 'boolean' }
|
||||||
expose :runner_token_expiration_interval
|
expose :runner_token_expiration_interval, documentation: { type: 'integer', example: 3600 }
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
def self.preload_resource(project)
|
def self.preload_resource(project)
|
||||||
|
|
|
@ -3,7 +3,11 @@
|
||||||
module API
|
module API
|
||||||
module Entities
|
module Entities
|
||||||
class ProjectGroupLink < Grape::Entity
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,10 +3,13 @@
|
||||||
module API
|
module API
|
||||||
module Entities
|
module Entities
|
||||||
class ProjectIdentity < Grape::Entity
|
class ProjectIdentity < Grape::Entity
|
||||||
expose :id, :description
|
expose :id, documentation: { type: 'integer', example: 1 }
|
||||||
expose :name, :name_with_namespace
|
expose :description, documentation: { type: 'string', example: 'desc' }
|
||||||
expose :path, :path_with_namespace
|
expose :name, documentation: { type: 'string', example: 'project1' }
|
||||||
expose :created_at
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,12 +5,16 @@ module API
|
||||||
class ProjectRepositoryStorage < Grape::Entity
|
class ProjectRepositoryStorage < Grape::Entity
|
||||||
include Gitlab::Routing
|
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
|
project.repository.disk_path
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :id, as: :project_id
|
expose :id, as: :project_id, documentation: { type: 'integer', example: 1 }
|
||||||
expose :repository_storage, :created_at
|
expose :repository_storage, documentation: { type: 'string', example: 'default' }
|
||||||
|
expose :created_at, documentation: { type: 'dateTime', example: '2012-10-12T17:04:47Z' }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,7 +37,15 @@ module API
|
||||||
|
|
||||||
desc 'Import a GitHub project' do
|
desc 'Import a GitHub project' do
|
||||||
detail 'This feature was introduced in GitLab 11.3.4.'
|
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
|
end
|
||||||
params do
|
params do
|
||||||
requires :personal_access_token, type: String, desc: 'GitHub personal access token'
|
requires :personal_access_token, type: String, desc: 'GitHub personal access token'
|
||||||
|
@ -58,6 +66,18 @@ module API
|
||||||
end
|
end
|
||||||
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
|
params do
|
||||||
requires :project_id, type: Integer, desc: 'ID of importing project to be canceled'
|
requires :project_id, type: Integer, desc: 'ID of importing project to be canceled'
|
||||||
end
|
end
|
||||||
|
|
|
@ -147,6 +147,12 @@ module Gitlab
|
||||||
'in the body of your migration class'
|
'in the body of your migration class'
|
||||||
end
|
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 })
|
options = options.merge({ algorithm: :concurrently })
|
||||||
|
|
||||||
if index_exists?(table_name, column_name, **options)
|
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
|
end
|
||||||
# rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
# 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
|
private
|
||||||
|
|
||||||
def multiple_columns(columns, separator: ', ')
|
def multiple_columns(columns, separator: ', ')
|
||||||
|
|
|
@ -40,7 +40,7 @@ module Gitlab
|
||||||
|
|
||||||
partitioned_table.postgres_partitions.order(:name).each do |partition|
|
partitioned_table.postgres_partitions.order(:name).each do |partition|
|
||||||
partition_index_name = generated_index_name(partition.identifier, options[:name])
|
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)
|
add_concurrent_index(partition.identifier, column_names, partition_options)
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,6 +19,20 @@ module Gitlab
|
||||||
|
|
||||||
scope :for_parent_table, ->(name) { where("parent_identifier = concat(current_schema(), '.', ?)", name).order(:name) }
|
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
|
def to_s
|
||||||
name
|
name
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,11 +17,18 @@ module Gitlab
|
||||||
gon.markdown_surround_selection = current_user&.markdown_surround_selection
|
gon.markdown_surround_selection = current_user&.markdown_surround_selection
|
||||||
gon.markdown_automatic_lists = current_user&.markdown_automatic_lists
|
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_dsn = Gitlab.config.sentry.clientside_dsn
|
||||||
gon.sentry_environment = Gitlab.config.sentry.environment
|
gon.sentry_environment = Gitlab.config.sentry.environment
|
||||||
end
|
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_api_server_url = ::Recaptcha.configuration.api_server_url
|
||||||
gon.recaptcha_sitekey = Gitlab::CurrentSettings.recaptcha_site_key
|
gon.recaptcha_sitekey = Gitlab::CurrentSettings.recaptcha_site_key
|
||||||
gon.gitlab_url = Gitlab.config.gitlab.url
|
gon.gitlab_url = Gitlab.config.gitlab.url
|
||||||
|
|
|
@ -2734,15 +2734,27 @@ msgstr ""
|
||||||
msgid "AdminSettings|CI/CD limits"
|
msgid "AdminSettings|CI/CD limits"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AdminSettings|Clickhouse URL"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "AdminSettings|Configure Let's Encrypt"
|
msgid "AdminSettings|Configure Let's Encrypt"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
|
msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
|
||||||
msgstr ""
|
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}"
|
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AdminSettings|Cube API URL"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AdminSettings|Cube API key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "AdminSettings|Delete inactive projects"
|
msgid "AdminSettings|Delete inactive projects"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -2791,6 +2803,9 @@ msgstr ""
|
||||||
msgid "AdminSettings|Enable pipeline suggestion banner"
|
msgid "AdminSettings|Enable pipeline suggestion banner"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AdminSettings|Enable product analytics"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "AdminSettings|Enable shared runners for new projects"
|
msgid "AdminSettings|Enable shared runners for new projects"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -2836,6 +2851,18 @@ msgstr ""
|
||||||
msgid "AdminSettings|Instance runners expiration"
|
msgid "AdminSettings|Instance runners expiration"
|
||||||
msgstr ""
|
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"
|
msgid "AdminSettings|Keep the latest artifacts for all jobs in the latest successful pipelines"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -2974,9 +3001,18 @@ msgstr ""
|
||||||
msgid "AdminSettings|Size and domain settings for Pages static sites."
|
msgid "AdminSettings|Size and domain settings for Pages static sites."
|
||||||
msgstr ""
|
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."
|
msgid "AdminSettings|The default domain to use for Auto Review Apps and Auto Deploy stages in all projects."
|
||||||
msgstr ""
|
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."
|
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -3001,6 +3037,15 @@ msgstr ""
|
||||||
msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
|
msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
|
||||||
msgstr ""
|
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."
|
msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -5332,9 +5377,6 @@ msgstr ""
|
||||||
msgid "Assign labels"
|
msgid "Assign labels"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Assign milestone"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Assign myself"
|
msgid "Assign myself"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -30890,6 +30932,9 @@ msgstr ""
|
||||||
msgid "Proceed"
|
msgid "Proceed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Product analytics"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProductAnalytics|Audience"
|
msgid "ProductAnalytics|Audience"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -36144,10 +36189,10 @@ msgstr ""
|
||||||
msgid "SecurityOrchestration|%{agent} for %{namespaces}"
|
msgid "SecurityOrchestration|%{agent} for %{namespaces}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityOrchestration|%{branches} %{plural}"
|
msgid "SecurityOrchestration|%{branches} and %{lastBranch} branches"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityOrchestration|%{branches} and %{lastBranch} %{plural}"
|
msgid "SecurityOrchestration|%{branches} branch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityOrchestration|%{scanners}"
|
msgid "SecurityOrchestration|%{scanners}"
|
||||||
|
@ -36504,10 +36549,10 @@ msgstr ""
|
||||||
msgid "SecurityOrchestration|the %{branches}"
|
msgid "SecurityOrchestration|the %{branches}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityOrchestration|the %{namespaces} %{plural}"
|
msgid "SecurityOrchestration|the %{namespaces} and %{lastNamespace} namespaces"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityOrchestration|the %{namespaces} and %{lastNamespace} %{plural}"
|
msgid "SecurityOrchestration|the %{namespaces} namespace"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityOrchestration|vulnerabilities"
|
msgid "SecurityOrchestration|vulnerabilities"
|
||||||
|
@ -48842,11 +48887,6 @@ msgstr ""
|
||||||
msgid "my-topic"
|
msgid "my-topic"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "namespace"
|
|
||||||
msgid_plural "namespaces"
|
|
||||||
msgstr[0] ""
|
|
||||||
msgstr[1] ""
|
|
||||||
|
|
||||||
msgid "needs to be between 10 minutes and 1 month"
|
msgid "needs to be between 10 minutes and 1 month"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -98,12 +98,17 @@ module QA
|
||||||
let(:mrs) { fetch_mrs(imported_project, target_api_client) }
|
let(:mrs) { fetch_mrs(imported_project, target_api_client) }
|
||||||
let(:issues) { fetch_issues(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
|
before do
|
||||||
destination_group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
|
destination_group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
|
||||||
end
|
end
|
||||||
|
|
||||||
# rubocop:disable RSpec/InstanceVariable
|
# rubocop:disable RSpec/InstanceVariable
|
||||||
after do |example|
|
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)
|
next unless defined?(@import_time)
|
||||||
|
|
||||||
# save data for comparison notification creation
|
# save data for comparison notification creation
|
||||||
|
@ -112,7 +117,7 @@ module QA
|
||||||
{
|
{
|
||||||
importer: :gitlab,
|
importer: :gitlab,
|
||||||
import_time: @import_time,
|
import_time: @import_time,
|
||||||
errors: imported_group.import_details.sum([]) { |details| details[:failures] },
|
errors: import_failures,
|
||||||
source: {
|
source: {
|
||||||
name: "GitLab Source",
|
name: "GitLab Source",
|
||||||
project_name: source_project.path_with_namespace,
|
project_name: source_project.path_with_namespace,
|
||||||
|
@ -154,7 +159,7 @@ module QA
|
||||||
end
|
end
|
||||||
# rubocop:enable RSpec/InstanceVariable
|
# 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
|
start = Time.now
|
||||||
|
|
||||||
# trigger import and log imported group path
|
# trigger import and log imported group path
|
||||||
|
@ -165,7 +170,11 @@ module QA
|
||||||
|
|
||||||
# wait for import to finish and save import time
|
# wait for import to finish and save import time
|
||||||
logger.info("== Waiting for import to be finished ==")
|
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
|
@import_time = Time.now - start
|
||||||
|
|
||||||
aggregate_failures do
|
aggregate_failures do
|
||||||
|
|
|
@ -20,17 +20,6 @@ RSpec.describe Projects::PipelinesController do
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples 'the show page' do |param|
|
shared_examples 'the show page' do |param|
|
||||||
it 'redirects to pipeline path with param' 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
|
it 'renders the show template' do
|
||||||
get param, params: { namespace_id: project.namespace, project_id: project, id: pipeline }
|
get param, params: { namespace_id: project.namespace, project_id: project, id: pipeline }
|
||||||
|
|
||||||
|
@ -38,7 +27,6 @@ RSpec.describe Projects::PipelinesController do
|
||||||
expect(response).to render_template :show
|
expect(response).to render_template :show
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET index.json' do
|
describe 'GET index.json' do
|
||||||
before do
|
before do
|
||||||
|
@ -710,11 +698,6 @@ RSpec.describe Projects::PipelinesController do
|
||||||
describe 'GET failures' do
|
describe 'GET failures' do
|
||||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||||
|
|
||||||
context 'with ff `pipeline_tabs_vue` disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(pipeline_tabs_vue: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with failed jobs' do
|
context 'with failed jobs' do
|
||||||
before do
|
before do
|
||||||
create(:ci_build, :failed, pipeline: pipeline, name: 'hello')
|
create(:ci_build, :failed, pipeline: pipeline, name: 'hello')
|
||||||
|
@ -737,13 +720,6 @@ RSpec.describe Projects::PipelinesController do
|
||||||
end
|
end
|
||||||
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 }
|
|
||||||
|
|
||||||
expect(response).to redirect_to(pipeline_path(pipeline, tab: 'failures'))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET stages.json' do
|
describe 'GET stages.json' do
|
||||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||||
let(:build_stage) { create(:ci_stage, name: 'build', pipeline: pipeline) }
|
let(:build_stage) { create(:ci_stage, name: 'build', pipeline: pipeline) }
|
||||||
|
|
|
@ -80,7 +80,7 @@ RSpec.describe 'Database schema' do
|
||||||
project_error_tracking_settings: %w[sentry_project_id],
|
project_error_tracking_settings: %w[sentry_project_id],
|
||||||
project_group_links: %w[group_id],
|
project_group_links: %w[group_id],
|
||||||
project_statistics: %w[namespace_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],
|
redirect_routes: %w[source_id],
|
||||||
repository_languages: %w[programming_language_id],
|
repository_languages: %w[programming_language_id],
|
||||||
routes: %w[source_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') }
|
let(:filter_submit) { find('.gl-search-box-by-click-search-button') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ RSpec.describe 'Project issue boards', :js do
|
||||||
|
|
||||||
context 'signed in user' do
|
context 'signed in user' do
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
project.add_maintainer(user2)
|
project.add_maintainer(user2)
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ RSpec.describe 'Project issue boards', :js do
|
||||||
|
|
||||||
context 'no lists' do
|
context 'no lists' do
|
||||||
before do
|
before do
|
||||||
visit_project_board_path_without_query_limit(project, board)
|
visit_project_board(project, board)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates default lists' do
|
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]) }
|
let_it_be(:issue10) { create(:labeled_issue, project: project, title: 'issue +', description: 'A+ great issue', labels: [a_plus]) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
visit_project_board_path_without_query_limit(project, board)
|
visit_project_board(project, board)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'shows description tooltip on list title', :quarantine do
|
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
|
it 'infinite scrolls list' do
|
||||||
create_list(:labeled_issue, 30, project: project, labels: [planning])
|
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
|
page.within(find('.board:nth-child(2)')) do
|
||||||
expect(page.find('.board-header')).to have_content('38')
|
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)
|
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
|
# 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(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)
|
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) }
|
let_it_be(:list2) { create(:list, board: board, label: development, position: 1) }
|
||||||
|
|
||||||
it 'changes position of list' do
|
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')
|
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(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)
|
expect(find('.board:nth-child(2) [data-testid="board-list-header"]')).to have_content(planning.title)
|
||||||
|
|
||||||
|
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
|
# Make sure list positions are preserved after a reload
|
||||||
visit_project_board_path_without_query_limit(project, board)
|
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(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)
|
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'
|
selector = '.board:not(.is-ghost) .board-header'
|
||||||
expect(page).to have_selector(selector, text: development.title, count: 1)
|
expect(page).to have_selector(selector, text: development.title, count: 1)
|
||||||
|
|
||||||
|
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)
|
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)
|
expect(page).to have_selector(selector, text: development.title, count: 1)
|
||||||
end
|
end
|
||||||
|
@ -492,7 +499,7 @@ RSpec.describe 'Project issue boards', :js do
|
||||||
|
|
||||||
context 'keyboard shortcuts' do
|
context 'keyboard shortcuts' do
|
||||||
before do
|
before do
|
||||||
visit_project_board_path_without_query_limit(project, board)
|
visit_project_board(project, board)
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -505,6 +512,7 @@ RSpec.describe 'Project issue boards', :js do
|
||||||
|
|
||||||
context 'signed out user' do
|
context 'signed out user' do
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
visit project_board_path(project, board)
|
visit project_board_path(project, board)
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
end
|
end
|
||||||
|
@ -526,6 +534,7 @@ RSpec.describe 'Project issue boards', :js do
|
||||||
let_it_be(:user_guest) { create(:user) }
|
let_it_be(:user_guest) { create(:user) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
project.add_guest(user_guest)
|
project.add_guest(user_guest)
|
||||||
sign_in(user_guest)
|
sign_in(user_guest)
|
||||||
visit project_board_path(project, board)
|
visit project_board_path(project, board)
|
||||||
|
@ -587,11 +596,9 @@ RSpec.describe 'Project issue boards', :js do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit_project_board_path_without_query_limit(project, board)
|
def visit_project_board(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)
|
visit project_board_path(project, board)
|
||||||
|
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
end
|
end
|
||||||
end
|
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) }
|
let!(:issue3) { create(:labeled_issue, project: project, title: 'testing 3', labels: [label], relative_position: 1) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
|
|
||||||
sign_in(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') }
|
let(:card) { find('.board:nth-child(2)').first('.board-card') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
|
|
||||||
sign_in(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) }
|
let_it_be(:issue, reload: true) { create(:issue, project: project, relative_position: 1) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
|
|
@ -31,6 +31,7 @@ RSpec.describe 'User adds lists', :js do
|
||||||
|
|
||||||
with_them do
|
with_them do
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
|
||||||
set_cookie('sidebar_collapsed', 'true')
|
set_cookie('sidebar_collapsed', 'true')
|
||||||
|
|
|
@ -44,6 +44,7 @@ RSpec.describe 'User visits issue boards', :js do
|
||||||
|
|
||||||
with_them do
|
with_them do
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
visit board_path
|
visit board_path
|
||||||
|
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
|
@ -59,6 +60,7 @@ RSpec.describe 'User visits issue boards', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "project boards" do
|
context "project boards" do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
let_it_be(:board) { create_default(:board, project: project) }
|
let_it_be(:board) { create_default(:board, project: project) }
|
||||||
let_it_be(:backlog_list) { create_default(:backlog_list, board: board) }
|
let_it_be(:backlog_list) { create_default(:backlog_list, board: board) }
|
||||||
|
|
||||||
|
@ -68,6 +70,7 @@ RSpec.describe 'User visits issue boards', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "group boards" do
|
context "group boards" do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
let_it_be(:board) { create_default(:board, group: group) }
|
let_it_be(:board) { create_default(:board, group: group) }
|
||||||
let_it_be(:backlog_list) { create_default(:backlog_list, board: board) }
|
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') }
|
let(:card) { find('.board:nth-child(1)').first('.board-card') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
|
||||||
visit group_board_path(group, board)
|
visit group_board_path(group, board)
|
||||||
|
|
|
@ -4,12 +4,16 @@ require "spec_helper"
|
||||||
|
|
||||||
RSpec.describe "User views incident" do
|
RSpec.describe "User views incident" do
|
||||||
let_it_be(:project) { create(:project_empty_repo, :public) }
|
let_it_be(:project) { create(:project_empty_repo, :public) }
|
||||||
let_it_be(:user) { create(:user) }
|
let_it_be(:guest) { 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(:developer) { create(:user) }
|
||||||
let_it_be(:note) { create(:note, noteable: incident, project: project, author: 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
|
before_all do
|
||||||
project.add_developer(user)
|
project.add_developer(developer)
|
||||||
|
project.add_guest(guest)
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -18,57 +22,61 @@ RSpec.describe "User views incident" do
|
||||||
visit(project_issues_incident_path(project, incident))
|
visit(project_issues_incident_path(project, incident))
|
||||||
end
|
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'
|
it_behaves_like 'page meta description', ' Description header Lorem ipsum dolor sit amet'
|
||||||
|
|
||||||
describe 'user actions' do
|
describe 'user actions' do
|
||||||
it 'shows the merge request and incident actions', :js, :aggregate_failures 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'
|
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('Create merge request')
|
||||||
expect(page).to have_button('Close incident')
|
expect(page).to have_button('Close incident')
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user is a guest' do
|
context 'when user is guest' do
|
||||||
before do
|
let(:user) { guest }
|
||||||
project.add_guest(user)
|
|
||||||
|
|
||||||
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
|
end
|
||||||
|
|
||||||
it 'does not show the incident actions', :js, :aggregate_failures do
|
context 'and not author' do
|
||||||
expect(page).not_to have_button('Incident actions')
|
it 'shows incident actions', :js do
|
||||||
|
click_button 'Incident actions'
|
||||||
|
|
||||||
|
expect(page).to have_link 'Report abuse'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the project is archived' do
|
context 'when the project is archived' do
|
||||||
before do
|
before_all do
|
||||||
project.update!(archived: true)
|
project.update!(archived: true)
|
||||||
visit(project_issues_incident_path(project, incident))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'hides the merge request and incident actions', :aggregate_failures do
|
it 'does not show the incident actions', :js do
|
||||||
expect(page).not_to have_link('New incident')
|
expect(page).not_to have_button('Incident actions')
|
||||||
expect(page).not_to have_button('Create merge request')
|
|
||||||
expect(page).not_to have_link('Close incident')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'user status' do
|
describe 'user status' do
|
||||||
|
context 'when showing status of the author of the incident' do
|
||||||
subject { visit(project_issues_incident_path(project, incident)) }
|
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
|
|
||||||
|
|
||||||
context 'when showing status of a user who commented on an incident', :js do
|
|
||||||
it_behaves_like 'showing user status' do
|
it_behaves_like 'showing user status' do
|
||||||
let(:user_with_status) { user }
|
let(:user_with_status) { user }
|
||||||
end
|
end
|
||||||
|
|
|
@ -417,7 +417,7 @@ RSpec.describe 'Issues > Labels bulk assignment' do
|
||||||
click_button 'Select milestone'
|
click_button 'Select milestone'
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
items.map do |item|
|
items.map do |item|
|
||||||
click_link item
|
click_button item
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ RSpec.describe 'Multiple issue updating from issues#index', :js do
|
||||||
click_button 'Edit issues'
|
click_button 'Edit issues'
|
||||||
check 'Select all'
|
check 'Select all'
|
||||||
click_button 'Select milestone'
|
click_button 'Select milestone'
|
||||||
click_link milestone.title
|
click_button milestone.title
|
||||||
click_update_issues_button
|
click_update_issues_button
|
||||||
|
|
||||||
expect(page.find('.issue')).to have_content milestone.title
|
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'
|
click_button 'Edit issues'
|
||||||
check 'Select all'
|
check 'Select all'
|
||||||
click_button 'Select milestone'
|
click_button 'Select milestone'
|
||||||
click_link 'No milestone'
|
click_button 'No milestone'
|
||||||
click_update_issues_button
|
click_update_issues_button
|
||||||
|
|
||||||
expect(find('.issue:first-of-type')).not_to have_text milestone.title
|
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'
|
click_button 'Edit merge requests'
|
||||||
check 'Select all'
|
check 'Select all'
|
||||||
click_button 'Select milestone'
|
click_button 'Select milestone'
|
||||||
click_link text
|
click_button text
|
||||||
click_update_merge_requests_button
|
click_update_merge_requests_button
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ describe('BoardApp', () => {
|
||||||
store,
|
store,
|
||||||
provide: {
|
provide: {
|
||||||
...provide,
|
...provide,
|
||||||
|
fullBoardId: 'gid://gitlab/Board/1',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
import { GlAlert } from '@gitlab/ui';
|
import { GlAlert } from '@gitlab/ui';
|
||||||
import { shallowMount } from '@vue/test-utils';
|
import { shallowMount } from '@vue/test-utils';
|
||||||
import Vue, { nextTick } from 'vue';
|
import Vue, { nextTick } from 'vue';
|
||||||
|
import VueApollo from 'vue-apollo';
|
||||||
import Draggable from 'vuedraggable';
|
import Draggable from 'vuedraggable';
|
||||||
import Vuex from 'vuex';
|
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 EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue';
|
||||||
import getters from 'ee_else_ce/boards/stores/getters';
|
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 BoardColumn from '~/boards/components/board_column.vue';
|
||||||
import BoardContent from '~/boards/components/board_content.vue';
|
import BoardContent from '~/boards/components/board_content.vue';
|
||||||
import BoardContentSidebar from '~/boards/components/board_content_sidebar.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);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
|
@ -18,6 +23,7 @@ const actions = {
|
||||||
|
|
||||||
describe('BoardContent', () => {
|
describe('BoardContent', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
let fakeApollo;
|
||||||
window.gon = {};
|
window.gon = {};
|
||||||
|
|
||||||
const defaultState = {
|
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({
|
const store = createStore({
|
||||||
...defaultState,
|
...defaultState,
|
||||||
...state,
|
...state,
|
||||||
});
|
});
|
||||||
wrapper = shallowMount(BoardContent, {
|
wrapper = shallowMount(BoardContent, {
|
||||||
|
apolloProvider: fakeApollo,
|
||||||
propsData: {
|
propsData: {
|
||||||
lists: mockLists,
|
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
boardId: 'gid://gitlab/Board/1',
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
canAdminList,
|
canAdminList,
|
||||||
|
boardType: 'group',
|
||||||
|
fullPath: 'gitlab-org/gitlab',
|
||||||
|
issuableType,
|
||||||
|
isApolloBoard,
|
||||||
},
|
},
|
||||||
store,
|
store,
|
||||||
});
|
});
|
||||||
|
@ -78,6 +98,7 @@ describe('BoardContent', () => {
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
wrapper.destroy();
|
wrapper.destroy();
|
||||||
|
fakeApollo = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('default', () => {
|
describe('default', () => {
|
||||||
|
@ -112,7 +133,7 @@ describe('BoardContent', () => {
|
||||||
|
|
||||||
describe('when issuableType is not issue', () => {
|
describe('when issuableType is not issue', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createComponent({ state: { issuableType: 'foo' } });
|
createComponent({ issuableType: 'foo' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not render BoardContentSidebar', () => {
|
it('does not render BoardContentSidebar', () => {
|
||||||
|
@ -139,4 +160,19 @@ describe('BoardContent', () => {
|
||||||
expect(wrapper.findComponent(Draggable).exists()).toBe(false);
|
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,
|
label: null,
|
||||||
assignee: null,
|
assignee: null,
|
||||||
milestone: null,
|
milestone: null,
|
||||||
|
iteration: null,
|
||||||
loading: false,
|
loading: false,
|
||||||
issuesCount: 1,
|
issuesCount: 1,
|
||||||
|
maxIssueCount: 0,
|
||||||
|
__typename: 'BoardList',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mockLabelList = {
|
export const mockLabelList = {
|
||||||
|
@ -449,11 +452,15 @@ export const mockLabelList = {
|
||||||
color: '#F0AD4E',
|
color: '#F0AD4E',
|
||||||
textColor: '#FFFFFF',
|
textColor: '#FFFFFF',
|
||||||
description: null,
|
description: null,
|
||||||
|
descriptionHtml: null,
|
||||||
},
|
},
|
||||||
assignee: null,
|
assignee: null,
|
||||||
milestone: null,
|
milestone: null,
|
||||||
|
iteration: null,
|
||||||
loading: false,
|
loading: false,
|
||||||
issuesCount: 0,
|
issuesCount: 0,
|
||||||
|
maxIssueCount: 0,
|
||||||
|
__typename: 'BoardList',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mockMilestoneList = {
|
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) => ({
|
export const boardListQueryResponse = (issuesCount = 20) => ({
|
||||||
data: {
|
data: {
|
||||||
boardList: {
|
boardList: {
|
||||||
|
|
|
@ -2,10 +2,6 @@ import { shallowMount } from '@vue/test-utils';
|
||||||
import { GlTab } from '@gitlab/ui';
|
import { GlTab } from '@gitlab/ui';
|
||||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||||
import PipelineTabs from '~/pipelines/components/pipeline_tabs.vue';
|
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', () => {
|
describe('The Pipeline Tabs', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
@ -16,12 +12,6 @@ describe('The Pipeline Tabs', () => {
|
||||||
const findPipelineTab = () => wrapper.findByTestId('pipeline-tab');
|
const findPipelineTab = () => wrapper.findByTestId('pipeline-tab');
|
||||||
const findTestsTab = () => wrapper.findByTestId('tests-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 findFailedJobsBadge = () => wrapper.findByTestId('failed-builds-counter');
|
||||||
const findJobsBadge = () => wrapper.findByTestId('builds-counter');
|
const findJobsBadge = () => wrapper.findByTestId('builds-counter');
|
||||||
const findTestsBadge = () => wrapper.findByTestId('tests-counter');
|
const findTestsBadge = () => wrapper.findByTestId('tests-counter');
|
||||||
|
@ -43,6 +33,7 @@ describe('The Pipeline Tabs', () => {
|
||||||
},
|
},
|
||||||
stubs: {
|
stubs: {
|
||||||
GlTab,
|
GlTab,
|
||||||
|
RouterView: true,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -54,17 +45,16 @@ describe('The Pipeline Tabs', () => {
|
||||||
|
|
||||||
describe('Tabs', () => {
|
describe('Tabs', () => {
|
||||||
it.each`
|
it.each`
|
||||||
tabName | tabComponent | appComponent
|
tabName | tabComponent
|
||||||
${'Pipeline'} | ${findPipelineTab} | ${findPipelineApp}
|
${'Pipeline'} | ${findPipelineTab}
|
||||||
${'Dag'} | ${findDagTab} | ${findDagApp}
|
${'Dag'} | ${findDagTab}
|
||||||
${'Jobs'} | ${findJobsTab} | ${findJobsApp}
|
${'Jobs'} | ${findJobsTab}
|
||||||
${'Failed Jobs'} | ${findFailedJobsTab} | ${findFailedJobsApp}
|
${'Failed Jobs'} | ${findFailedJobsTab}
|
||||||
${'Tests'} | ${findTestsTab} | ${findTestsApp}
|
${'Tests'} | ${findTestsTab}
|
||||||
`('shows $tabName tab with its associated component', ({ appComponent, tabComponent }) => {
|
`('shows $tabName tab', ({ tabComponent }) => {
|
||||||
createComponent();
|
createComponent();
|
||||||
|
|
||||||
expect(tabComponent().exists()).toBe(true);
|
expect(tabComponent().exists()).toBe(true);
|
||||||
expect(appComponent().exists()).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with no failed jobs', () => {
|
describe('with no failed jobs', () => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { createJobsHash, generateJobNeedsDict, getPipelineDefaultTab } from '~/pipelines/utils';
|
import { createJobsHash, generateJobNeedsDict, getPipelineDefaultTab } from '~/pipelines/utils';
|
||||||
import { TAB_QUERY_PARAM, validPipelineTabNames } from '~/pipelines/constants';
|
import { validPipelineTabNames } from '~/pipelines/constants';
|
||||||
|
|
||||||
describe('utils functions', () => {
|
describe('utils functions', () => {
|
||||||
const jobName1 = 'build_1';
|
const jobName1 = 'build_1';
|
||||||
|
@ -173,18 +173,25 @@ describe('utils functions', () => {
|
||||||
|
|
||||||
describe('getPipelineDefaultTab', () => {
|
describe('getPipelineDefaultTab', () => {
|
||||||
const baseUrl = 'http://gitlab.com/user/multi-projects-small/-/pipelines/332/';
|
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);
|
expect(getPipelineDefaultTab(baseUrl)).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns null if there was no valid tab param', () => {
|
it('returns null if there was no valid last url part', () => {
|
||||||
expect(getPipelineDefaultTab(`${baseUrl}?${TAB_QUERY_PARAM}=invalid`)).toBe(null);
|
expect(getPipelineDefaultTab(`${baseUrl}something`)).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the correct tab name if present', () => {
|
it('returns the correct tab name if present', () => {
|
||||||
validPipelineTabNames.forEach((tabName) => {
|
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 { createAppOptions } from '~/pipelines/pipeline_tabs';
|
||||||
import { updateHistory } from '~/lib/utils/url_utility';
|
|
||||||
|
|
||||||
jest.mock('~/lib/utils/url_utility', () => ({
|
jest.mock('~/lib/utils/url_utility', () => ({
|
||||||
removeParams: () => 'gitlab.com',
|
removeParams: () => 'gitlab.com',
|
||||||
updateHistory: jest.fn(),
|
|
||||||
joinPaths: () => {},
|
joinPaths: () => {},
|
||||||
setUrlFragment: () => {},
|
setUrlFragment: () => {},
|
||||||
}));
|
}));
|
||||||
|
@ -64,32 +62,4 @@ describe('~/pipelines/pipeline_tabs.js', () => {
|
||||||
expect(createAppOptions('foo', null)).toBe(null);
|
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),
|
suite_endpoint: project_pipeline_test_path(project, pipeline, suite_name: 'suite', format: :json),
|
||||||
blob_path: project_blob_path(project, pipeline.sha),
|
blob_path: project_blob_path(project, pipeline.sha),
|
||||||
has_test_report: pipeline.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:test)),
|
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'),
|
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'),
|
artifacts_expired_image_path: match_asset_path('illustrations/pipeline.svg'),
|
||||||
tests_count: pipeline.test_report_summary.total[:count]
|
tests_count: pipeline.test_report_summary.total[:count]
|
||||||
|
|
|
@ -389,6 +389,40 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
|
||||||
|
|
||||||
model.add_concurrent_index(:users, :foo)
|
model.add_concurrent_index(:users, :foo)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
context 'inside a transaction' do
|
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)
|
model.add_sequence(:test_table, :test_column, :test_table_id_seq, 1)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -65,8 +65,11 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
|
||||||
end
|
end
|
||||||
|
|
||||||
def expect_add_concurrent_index_and_call_original(table, column, index)
|
def expect_add_concurrent_index_and_call_original(table, column, index)
|
||||||
expect(migration).to receive(:add_concurrent_index).ordered.with(table, column, { name: index })
|
expect(migration).to receive(:add_concurrent_index).ordered.with(table, column, { name: index, allow_partition: true })
|
||||||
.and_wrap_original { |_, table, column, options| connection.add_index(table, column, **options) }
|
.and_wrap_original do |_, table, column, options|
|
||||||
|
options.delete(:allow_partition)
|
||||||
|
connection.add_index(table, column, **options)
|
||||||
|
end
|
||||||
end
|
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
|
it 'forwards them to the index helper methods', :aggregate_failures do
|
||||||
expect(migration).to receive(:add_concurrent_index)
|
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)
|
expect(migration).to receive(:add_index)
|
||||||
.with(table_name, column_name, { name: index_name, where: 'x > 0', unique: true })
|
.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')")
|
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
|
||||||
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
|
end
|
||||||
|
|
|
@ -39,6 +39,72 @@ RSpec.describe Gitlab::GonHelper do
|
||||||
helper.add_gon_variables
|
helper.add_gon_variables
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
describe '#push_frontend_feature_flag' do
|
describe '#push_frontend_feature_flag' do
|
||||||
|
|
|
@ -13,6 +13,11 @@ RSpec.describe AlertManagement::HttpIntegration do
|
||||||
it { is_expected.to belong_to(:project) }
|
it { is_expected.to belong_to(:project) }
|
||||||
end
|
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
|
describe 'validations' do
|
||||||
it { is_expected.to validate_presence_of(:project) }
|
it { is_expected.to validate_presence_of(:project) }
|
||||||
it { is_expected.to validate_presence_of(:name) }
|
it { is_expected.to validate_presence_of(:name) }
|
||||||
|
@ -124,10 +129,6 @@ RSpec.describe AlertManagement::HttpIntegration do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when unsaved' do
|
context 'when unsaved' do
|
||||||
context 'when unassigned' do
|
|
||||||
it_behaves_like 'valid token'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when assigned' do
|
context 'when assigned' do
|
||||||
include_context 'assign token', 'random_token'
|
include_context 'assign token', 'random_token'
|
||||||
|
|
||||||
|
|
|
@ -4089,7 +4089,6 @@
|
||||||
- './spec/features/incidents/user_creates_new_incident_spec.rb'
|
- './spec/features/incidents/user_creates_new_incident_spec.rb'
|
||||||
- './spec/features/incidents/user_filters_incidents_by_status_spec.rb'
|
- './spec/features/incidents/user_filters_incidents_by_status_spec.rb'
|
||||||
- './spec/features/incidents/user_searches_incidents_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/invites_spec.rb'
|
||||||
- './spec/features/issuables/issuable_list_spec.rb'
|
- './spec/features/issuables/issuable_list_spec.rb'
|
||||||
- './spec/features/issuables/markdown_references/internal_references_spec.rb'
|
- './spec/features/issuables/markdown_references/internal_references_spec.rb'
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
RSpec.shared_examples 'multiple issue boards' do
|
RSpec.shared_examples 'multiple issue boards' do
|
||||||
context 'authorized user' do
|
context 'authorized user' do
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
parent.add_maintainer(user)
|
parent.add_maintainer(user)
|
||||||
|
|
||||||
login_as(user)
|
login_as(user)
|
||||||
|
@ -121,6 +122,7 @@ RSpec.shared_examples 'multiple issue boards' do
|
||||||
|
|
||||||
context 'unauthorized user' do
|
context 'unauthorized user' do
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
visit boards_path
|
visit boards_path
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ RSpec.shared_examples 'multiple and scoped issue boards' do |route_definition|
|
||||||
|
|
||||||
context 'multiple issue boards' do
|
context 'multiple issue boards' do
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(apollo_boards: false)
|
||||||
board_parent.add_reporter(user)
|
board_parent.add_reporter(user)
|
||||||
stub_licensed_features(multiple_group_issue_boards: true)
|
stub_licensed_features(multiple_group_issue_boards: true)
|
||||||
end
|
end
|
||||||
|
|
|
@ -52,6 +52,7 @@ const (
|
||||||
geoGitProjectPattern = `^/[^-].+\.git/` // Prevent matching routes like /-/push_from_secondary
|
geoGitProjectPattern = `^/[^-].+\.git/` // Prevent matching routes like /-/push_from_secondary
|
||||||
projectPattern = `^/([^/]+/){1,}[^/]+/`
|
projectPattern = `^/([^/]+/){1,}[^/]+/`
|
||||||
apiProjectPattern = apiPattern + `v4/projects/[^/]+` // API: Projects can be encoded via group%2Fsubgroup%2Fproject
|
apiProjectPattern = apiPattern + `v4/projects/[^/]+` // API: Projects can be encoded via group%2Fsubgroup%2Fproject
|
||||||
|
apiGroupPattern = apiPattern + `v4/groups/[^/]+`
|
||||||
apiTopicPattern = apiPattern + `v4/topics`
|
apiTopicPattern = apiPattern + `v4/topics`
|
||||||
snippetUploadPattern = `^/uploads/personal_snippet`
|
snippetUploadPattern = `^/uploads/personal_snippet`
|
||||||
userUploadPattern = `^/uploads/user`
|
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.
|
// 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
|
// 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", apiProjectPattern+`/wikis/attachments\z`, tempfileMultipartProxy),
|
||||||
|
u.route("POST", apiGroupPattern+`/wikis/attachments\z`, tempfileMultipartProxy),
|
||||||
u.route("POST", apiPattern+`graphql\z`, tempfileMultipartProxy),
|
u.route("POST", apiPattern+`graphql\z`, tempfileMultipartProxy),
|
||||||
u.route("POST", apiTopicPattern, tempfileMultipartProxy),
|
u.route("POST", apiTopicPattern, tempfileMultipartProxy),
|
||||||
u.route("PUT", apiTopicPattern, tempfileMultipartProxy),
|
u.route("PUT", apiTopicPattern, tempfileMultipartProxy),
|
||||||
|
|
|
@ -138,6 +138,8 @@ func TestAcceleratedUpload(t *testing.T) {
|
||||||
{"POST", `/api/v4/groups`, false},
|
{"POST", `/api/v4/groups`, false},
|
||||||
{"PUT", `/api/v4/groups/5`, false},
|
{"PUT", `/api/v4/groups/5`, false},
|
||||||
{"PUT", `/api/v4/groups/group%2Fsubgroup`, 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},
|
{"POST", `/api/v4/users`, false},
|
||||||
{"PUT", `/api/v4/users/42`, false},
|
{"PUT", `/api/v4/users/42`, false},
|
||||||
{"PUT", "/api/v4/projects/9001/packages/nuget/v1/files", true},
|
{"PUT", "/api/v4/projects/9001/packages/nuget/v1/files", true},
|
||||||
|
|
Loading…
Reference in New Issue