Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f6b58d1490
commit
885275c832
|
|
@ -106,28 +106,28 @@ workflow:
|
||||||
# For tags, create a pipeline.
|
# For tags, create a pipeline.
|
||||||
- if: '$CI_COMMIT_TAG'
|
- if: '$CI_COMMIT_TAG'
|
||||||
variables:
|
variables:
|
||||||
<<: *ruby2-variables
|
<<: *ruby3-variables
|
||||||
PIPELINE_NAME: 'Ruby 2 $CI_COMMIT_TAG tag pipeline'
|
PIPELINE_NAME: 'Ruby 3 $CI_COMMIT_TAG tag pipeline'
|
||||||
# If `$GITLAB_INTERNAL` isn't set, don't create a pipeline.
|
# If `$GITLAB_INTERNAL` isn't set, don't create a pipeline.
|
||||||
- if: '$GITLAB_INTERNAL == null'
|
- if: '$GITLAB_INTERNAL == null'
|
||||||
when: never
|
when: never
|
||||||
# For stable, auto-deploy, and security branches, create a pipeline.
|
# For stable, auto-deploy, and security branches, create a pipeline.
|
||||||
- if: '$CI_COMMIT_BRANCH =~ /^[\d-]+-stable(-ee)?$/'
|
- if: '$CI_COMMIT_BRANCH =~ /^[\d-]+-stable(-ee)?$/'
|
||||||
variables:
|
variables:
|
||||||
<<: *ruby2-variables
|
<<: *ruby3-variables
|
||||||
NOTIFY_PIPELINE_FAILURE_CHANNEL: "releases"
|
NOTIFY_PIPELINE_FAILURE_CHANNEL: "releases"
|
||||||
PIPELINE_NAME: 'Ruby 2 $CI_COMMIT_BRANCH branch pipeline'
|
PIPELINE_NAME: 'Ruby 3 $CI_COMMIT_BRANCH branch pipeline'
|
||||||
CREATE_INCIDENT_FOR_PIPELINE_FAILURE: "true"
|
CREATE_INCIDENT_FOR_PIPELINE_FAILURE: "true"
|
||||||
BROKEN_BRANCH_INCIDENTS_PROJECT: "gitlab-org/release/tasks"
|
BROKEN_BRANCH_INCIDENTS_PROJECT: "gitlab-org/release/tasks"
|
||||||
BROKEN_BRANCH_INCIDENTS_PROJECT_TOKEN: "${BROKEN_STABLE_INCIDENTS_PROJECT_TOKEN}"
|
BROKEN_BRANCH_INCIDENTS_PROJECT_TOKEN: "${BROKEN_STABLE_INCIDENTS_PROJECT_TOKEN}"
|
||||||
- if: '$CI_COMMIT_BRANCH =~ /^\d+-\d+-auto-deploy-\d+$/'
|
- if: '$CI_COMMIT_BRANCH =~ /^\d+-\d+-auto-deploy-\d+$/'
|
||||||
variables:
|
variables:
|
||||||
<<: *ruby2-variables
|
<<: *ruby3-variables
|
||||||
PIPELINE_NAME: 'Ruby 2 $CI_COMMIT_BRANCH branch pipeline'
|
PIPELINE_NAME: 'Ruby 3 $CI_COMMIT_BRANCH branch pipeline'
|
||||||
- if: '$CI_COMMIT_BRANCH =~ /^security\//'
|
- if: '$CI_COMMIT_BRANCH =~ /^security\//'
|
||||||
variables:
|
variables:
|
||||||
<<: *ruby2-variables
|
<<: *ruby3-variables
|
||||||
PIPELINE_NAME: 'Ruby 2 $CI_COMMIT_BRANCH branch pipeline'
|
PIPELINE_NAME: 'Ruby 3 $CI_COMMIT_BRANCH branch pipeline'
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
PG_VERSION: "12"
|
PG_VERSION: "12"
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
71dbaa115c10ae08292a7df74d3f8297ab38f208
|
705e00092a936f0233fe16339aba3a1c65db36b0
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,10 @@ export const searchQuery = (state) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const scopedIssuesPath = (state) => {
|
export const scopedIssuesPath = (state) => {
|
||||||
|
if (state.searchContext?.project?.id && !state.searchContext?.project_metadata?.issues_path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
state.searchContext?.project_metadata?.issues_path ||
|
state.searchContext?.project_metadata?.issues_path ||
|
||||||
state.searchContext?.group_metadata?.issues_path ||
|
state.searchContext?.group_metadata?.issues_path ||
|
||||||
|
|
@ -54,7 +58,7 @@ export const scopedMRPath = (state) => {
|
||||||
export const defaultSearchOptions = (state, getters) => {
|
export const defaultSearchOptions = (state, getters) => {
|
||||||
const userName = gon.current_username;
|
const userName = gon.current_username;
|
||||||
|
|
||||||
return [
|
const issues = [
|
||||||
{
|
{
|
||||||
html_id: 'default-issues-assigned',
|
html_id: 'default-issues-assigned',
|
||||||
title: MSG_ISSUES_ASSIGNED_TO_ME,
|
title: MSG_ISSUES_ASSIGNED_TO_ME,
|
||||||
|
|
@ -65,6 +69,9 @@ export const defaultSearchOptions = (state, getters) => {
|
||||||
title: MSG_ISSUES_IVE_CREATED,
|
title: MSG_ISSUES_IVE_CREATED,
|
||||||
url: `${getters.scopedIssuesPath}/?author_username=${userName}`,
|
url: `${getters.scopedIssuesPath}/?author_username=${userName}`,
|
||||||
},
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const mergeRequests = [
|
||||||
{
|
{
|
||||||
html_id: 'default-mrs-assigned',
|
html_id: 'default-mrs-assigned',
|
||||||
title: MSG_MR_ASSIGNED_TO_ME,
|
title: MSG_MR_ASSIGNED_TO_ME,
|
||||||
|
|
@ -81,6 +88,7 @@ export const defaultSearchOptions = (state, getters) => {
|
||||||
url: `${getters.scopedMRPath}/?author_username=${userName}`,
|
url: `${getters.scopedMRPath}/?author_username=${userName}`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
return [...(getters.scopedIssuesPath ? issues : []), ...mergeRequests];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const projectUrl = (state) => {
|
export const projectUrl = (state) => {
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,9 @@
|
||||||
<script>
|
<script>
|
||||||
import {
|
import { GlDropdown, GlDropdownItem, GlIcon, GlTooltipDirective as GlTooltip } from '@gitlab/ui';
|
||||||
GlButton,
|
|
||||||
GlDropdown,
|
|
||||||
GlDropdownItem,
|
|
||||||
GlIcon,
|
|
||||||
GlTooltipDirective as GlTooltip,
|
|
||||||
} from '@gitlab/ui';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
GlIcon,
|
GlIcon,
|
||||||
GlButton,
|
|
||||||
GlDropdown,
|
GlDropdown,
|
||||||
GlDropdownItem,
|
GlDropdownItem,
|
||||||
},
|
},
|
||||||
|
|
@ -18,10 +11,6 @@ export default {
|
||||||
GlTooltip,
|
GlTooltip,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
isProjectsImportEnabled: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
isFinished: {
|
isFinished: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
|
|
@ -46,7 +35,7 @@ export default {
|
||||||
<template>
|
<template>
|
||||||
<span class="gl-white-space-nowrap gl-inline-flex gl-align-items-center">
|
<span class="gl-white-space-nowrap gl-inline-flex gl-align-items-center">
|
||||||
<gl-dropdown
|
<gl-dropdown
|
||||||
v-if="isProjectsImportEnabled && (isAvailableForImport || isFinished)"
|
v-if="isAvailableForImport || isFinished"
|
||||||
:text="isFinished ? __('Re-import with projects') : __('Import with projects')"
|
:text="isFinished ? __('Re-import with projects') : __('Import with projects')"
|
||||||
:disabled="isInvalid"
|
:disabled="isInvalid"
|
||||||
variant="confirm"
|
variant="confirm"
|
||||||
|
|
@ -59,16 +48,6 @@ export default {
|
||||||
isFinished ? __('Re-import without projects') : __('Import without projects')
|
isFinished ? __('Re-import without projects') : __('Import without projects')
|
||||||
}}</gl-dropdown-item>
|
}}</gl-dropdown-item>
|
||||||
</gl-dropdown>
|
</gl-dropdown>
|
||||||
<gl-button
|
|
||||||
v-else-if="isAvailableForImport || isFinished"
|
|
||||||
:disabled="isInvalid"
|
|
||||||
variant="confirm"
|
|
||||||
category="secondary"
|
|
||||||
data-qa-selector="import_group_button"
|
|
||||||
@click="$emit('import-group')"
|
|
||||||
>
|
|
||||||
{{ isFinished ? __('Re-import') : __('Import') }}
|
|
||||||
</gl-button>
|
|
||||||
<gl-icon
|
<gl-icon
|
||||||
v-if="isFinished"
|
v-if="isFinished"
|
||||||
v-gl-tooltip
|
v-gl-tooltip
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import {
|
import {
|
||||||
GlAlert,
|
GlAlert,
|
||||||
GlButton,
|
|
||||||
GlDropdown,
|
GlDropdown,
|
||||||
GlDropdownItem,
|
GlDropdownItem,
|
||||||
GlEmptyState,
|
GlEmptyState,
|
||||||
|
|
@ -50,7 +49,6 @@ const DEFAULT_TD_CLASSES = 'gl-vertical-align-top!';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
GlAlert,
|
GlAlert,
|
||||||
GlButton,
|
|
||||||
GlDropdown,
|
GlDropdown,
|
||||||
GlDropdownItem,
|
GlDropdownItem,
|
||||||
GlEmptyState,
|
GlEmptyState,
|
||||||
|
|
@ -165,10 +163,6 @@ export default {
|
||||||
],
|
],
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isProjectsImportEnabled() {
|
|
||||||
return Boolean(this.glFeatures.bulkImportProjects);
|
|
||||||
},
|
|
||||||
|
|
||||||
groups() {
|
groups() {
|
||||||
return this.bulkImportSourceGroups?.nodes ?? [];
|
return this.bulkImportSourceGroups?.nodes ?? [];
|
||||||
},
|
},
|
||||||
|
|
@ -707,11 +701,11 @@ export default {
|
||||||
</gl-sprintf>
|
</gl-sprintf>
|
||||||
</span>
|
</span>
|
||||||
<gl-dropdown
|
<gl-dropdown
|
||||||
v-if="isProjectsImportEnabled"
|
|
||||||
:text="s__('BulkImport|Import with projects')"
|
:text="s__('BulkImport|Import with projects')"
|
||||||
:disabled="!hasSelectedGroups"
|
:disabled="!hasSelectedGroups"
|
||||||
variant="confirm"
|
variant="confirm"
|
||||||
category="primary"
|
category="primary"
|
||||||
|
data-testid="import-selected-groups-dropdown"
|
||||||
class="gl-ml-4"
|
class="gl-ml-4"
|
||||||
split
|
split
|
||||||
@click="importSelectedGroups({ migrateProjects: true })"
|
@click="importSelectedGroups({ migrateProjects: true })"
|
||||||
|
|
@ -720,15 +714,6 @@ export default {
|
||||||
{{ s__('BulkImport|Import without projects') }}
|
{{ s__('BulkImport|Import without projects') }}
|
||||||
</gl-dropdown-item>
|
</gl-dropdown-item>
|
||||||
</gl-dropdown>
|
</gl-dropdown>
|
||||||
<gl-button
|
|
||||||
v-else
|
|
||||||
category="primary"
|
|
||||||
variant="confirm"
|
|
||||||
class="gl-ml-4"
|
|
||||||
:disabled="!hasSelectedGroups"
|
|
||||||
@click="importSelectedGroups"
|
|
||||||
>{{ s__('BulkImport|Import selected') }}</gl-button
|
|
||||||
>
|
|
||||||
<span class="gl-ml-3">
|
<span class="gl-ml-3">
|
||||||
<gl-icon name="information-o" :size="12" class="gl-text-blue-600" />
|
<gl-icon name="information-o" :size="12" class="gl-text-blue-600" />
|
||||||
<gl-sprintf
|
<gl-sprintf
|
||||||
|
|
@ -804,7 +789,6 @@ export default {
|
||||||
</template>
|
</template>
|
||||||
<template #cell(actions)="{ item: group, index }">
|
<template #cell(actions)="{ item: group, index }">
|
||||||
<import-actions-cell
|
<import-actions-cell
|
||||||
:is-projects-import-enabled="isProjectsImportEnabled"
|
|
||||||
:is-finished="group.flags.isFinished"
|
:is-finished="group.flags.isFinished"
|
||||||
:is-available-for-import="group.flags.isAvailableForImport"
|
:is-available-for-import="group.flags.isAvailableForImport"
|
||||||
:is-invalid="group.flags.isInvalid"
|
:is-invalid="group.flags.isInvalid"
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ export default {
|
||||||
<div class="card card-slim gl-mt-5 gl-mb-0 gl-bg-gray-10">
|
<div class="card card-slim gl-mt-5 gl-mb-0 gl-bg-gray-10">
|
||||||
<div class="card-header gl-px-5 gl-py-4 gl-bg-white">
|
<div class="card-header gl-px-5 gl-py-4 gl-bg-white">
|
||||||
<div
|
<div
|
||||||
class="card-title gl-relative gl-display-flex gl-align-items-center gl-line-height-20 gl-font-weight-bold gl-m-0"
|
class="card-title gl-relative gl-display-flex gl-flex-wrap gl-align-items-center gl-line-height-20 gl-font-weight-bold gl-m-0"
|
||||||
>
|
>
|
||||||
<gl-link
|
<gl-link
|
||||||
class="anchor gl-absolute gl-text-decoration-none"
|
class="anchor gl-absolute gl-text-decoration-none"
|
||||||
|
|
@ -82,6 +82,12 @@ export default {
|
||||||
<gl-icon name="merge-request" class="gl-ml-3 gl-mr-2 gl-text-gray-500" />
|
<gl-icon name="merge-request" class="gl-ml-3 gl-mr-2 gl-text-gray-500" />
|
||||||
<span data-testid="count" class="gl-text-gray-500">{{ totalCount }}</span>
|
<span data-testid="count" class="gl-text-gray-500">{{ totalCount }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
<p
|
||||||
|
v-if="hasClosingMergeRequest && !isFetchingMergeRequests"
|
||||||
|
class="gl-font-sm gl-font-weight-normal gl-flex-basis-full gl-mb-0 gl-text-gray-500"
|
||||||
|
>
|
||||||
|
{{ closingMergeRequestsText }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<gl-loading-icon
|
<gl-loading-icon
|
||||||
|
|
@ -110,11 +116,5 @@ export default {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-if="hasClosingMergeRequest && !isFetchingMergeRequests"
|
|
||||||
class="issue-closed-by-widget second-block gl-mt-3"
|
|
||||||
>
|
|
||||||
{{ closingMergeRequestsText }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,9 @@ export default {
|
||||||
tags() {
|
tags() {
|
||||||
return this.containerRepository?.tags?.nodes || [];
|
return this.containerRepository?.tags?.nodes || [];
|
||||||
},
|
},
|
||||||
|
hideBulkDelete() {
|
||||||
|
return !(this.containerRepository?.canDelete || false);
|
||||||
|
},
|
||||||
tagsPageInfo() {
|
tagsPageInfo() {
|
||||||
return this.containerRepository?.tags?.pageInfo;
|
return this.containerRepository?.tags?.pageInfo;
|
||||||
},
|
},
|
||||||
|
|
@ -98,9 +101,6 @@ export default {
|
||||||
sort: this.sort,
|
sort: this.sort,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
showMultiDeleteButton() {
|
|
||||||
return this.tags.some((tag) => tag.canDelete) && !this.isMobile;
|
|
||||||
},
|
|
||||||
hasNoTags() {
|
hasNoTags() {
|
||||||
return this.tags.length === 0;
|
return this.tags.length === 0;
|
||||||
},
|
},
|
||||||
|
|
@ -186,6 +186,7 @@ export default {
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<registry-list
|
<registry-list
|
||||||
|
:hidden-delete="hideBulkDelete"
|
||||||
:title="listTitle"
|
:title="listTitle"
|
||||||
:pagination="tagsPageInfo"
|
:pagination="tagsPageInfo"
|
||||||
:items="tags"
|
:items="tags"
|
||||||
|
|
|
||||||
|
|
@ -109,9 +109,6 @@ export default {
|
||||||
isInvalidTag() {
|
isInvalidTag() {
|
||||||
return !this.tag.digest;
|
return !this.tag.digest;
|
||||||
},
|
},
|
||||||
isDeleteDisabled() {
|
|
||||||
return this.disabled || !this.tag.canDelete;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -179,16 +176,16 @@ export default {
|
||||||
</gl-sprintf>
|
</gl-sprintf>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #right-action>
|
<template v-if="tag.canDelete" #right-action>
|
||||||
<gl-dropdown
|
<gl-dropdown
|
||||||
:disabled="isDeleteDisabled"
|
:disabled="disabled"
|
||||||
icon="ellipsis_v"
|
icon="ellipsis_v"
|
||||||
:text="$options.i18n.MORE_ACTIONS_TEXT"
|
:text="$options.i18n.MORE_ACTIONS_TEXT"
|
||||||
:text-sr-only="true"
|
:text-sr-only="true"
|
||||||
category="tertiary"
|
category="tertiary"
|
||||||
no-caret
|
no-caret
|
||||||
right
|
right
|
||||||
:class="{ 'gl-opacity-0 gl-pointer-events-none': isDeleteDisabled }"
|
:class="{ 'gl-opacity-0 gl-pointer-events-none': disabled }"
|
||||||
data-testid="additional-actions"
|
data-testid="additional-actions"
|
||||||
data-qa-selector="more_actions_menu"
|
data-qa-selector="more_actions_menu"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ query getContainerRepositoryTags(
|
||||||
containerRepository(id: $id) {
|
containerRepository(id: $id) {
|
||||||
id
|
id
|
||||||
tagsCount
|
tagsCount
|
||||||
|
canDelete
|
||||||
tags(after: $after, before: $before, first: $first, last: $last, name: $name, sort: $sort) {
|
tags(after: $after, before: $before, first: $first, last: $last, name: $name, sort: $sort) {
|
||||||
nodes {
|
nodes {
|
||||||
digest
|
digest
|
||||||
|
|
|
||||||
|
|
@ -122,11 +122,8 @@ export default {
|
||||||
@next-page="$emit('next-page')"
|
@next-page="$emit('next-page')"
|
||||||
>
|
>
|
||||||
<template #default="{ first, item, isSelected, selectItem }">
|
<template #default="{ first, item, isSelected, selectItem }">
|
||||||
<!-- `first` prop is used to decide whether to show the top border
|
|
||||||
for the first element. We want to show the top border only when
|
|
||||||
user has permission to bulk delete versions. -->
|
|
||||||
<version-row
|
<version-row
|
||||||
:first="canDestroy && first"
|
:first="first"
|
||||||
:package-entity="item"
|
:package-entity="item"
|
||||||
:selected="isSelected(item)"
|
:selected="isSelected(item)"
|
||||||
@delete="setItemToBeDeleted(item)"
|
@delete="setItemToBeDeleted(item)"
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ export default {
|
||||||
:select-item="selectItem"
|
:select-item="selectItem"
|
||||||
:is-selected="isSelected"
|
:is-selected="isSelected"
|
||||||
:item="item"
|
:item="item"
|
||||||
:first="index === 0"
|
:first="!hiddenDelete && index === 0"
|
||||||
></slot>
|
></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,9 @@ module SearchHelper
|
||||||
|
|
||||||
if search_has_project?
|
if search_has_project?
|
||||||
hash[:project] = { id: @project.id, name: @project.name }
|
hash[:project] = { id: @project.id, name: @project.name }
|
||||||
hash[:project_metadata] = { issues_path: project_issues_path(@project), mr_path: project_merge_requests_path(@project) }
|
hash[:project_metadata] = { mr_path: project_merge_requests_path(@project) }
|
||||||
|
hash[:project_metadata][:issues_path] = project_issues_path(@project) if @project.feature_available?(:issues, current_user)
|
||||||
|
|
||||||
hash[:code_search] = search_scope.nil?
|
hash[:code_search] = search_scope.nil?
|
||||||
hash[:ref] = @ref if @ref && can?(current_user, :read_code, @project)
|
hash[:ref] = @ref if @ref && can?(current_user, :read_code, @project)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ module HasUserType
|
||||||
migration_bot: 7,
|
migration_bot: 7,
|
||||||
security_bot: 8,
|
security_bot: 8,
|
||||||
automation_bot: 9,
|
automation_bot: 9,
|
||||||
|
security_policy_bot: 10, # Currently not in use. See https://gitlab.com/gitlab-org/gitlab/-/issues/384174
|
||||||
admin_bot: 11,
|
admin_bot: 11,
|
||||||
suggested_reviewers_bot: 12,
|
suggested_reviewers_bot: 12,
|
||||||
service_account: 13
|
service_account: 13
|
||||||
|
|
@ -27,6 +28,7 @@ module HasUserType
|
||||||
migration_bot
|
migration_bot
|
||||||
security_bot
|
security_bot
|
||||||
automation_bot
|
automation_bot
|
||||||
|
security_policy_bot
|
||||||
admin_bot
|
admin_bot
|
||||||
suggested_reviewers_bot
|
suggested_reviewers_bot
|
||||||
service_account
|
service_account
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
= form_for @application_setting, url: preferences_admin_application_settings_path(anchor: 'js-realtime-settings'), html: { class: 'fieldset-form' } do |f|
|
= gitlab_ui_form_for @application_setting, url: preferences_admin_application_settings_path(anchor: 'js-realtime-settings'), html: { class: 'fieldset-form' } do |f|
|
||||||
= form_errors(@application_setting)
|
= form_errors(@application_setting)
|
||||||
|
|
||||||
%fieldset
|
%fieldset
|
||||||
|
|
@ -8,4 +8,4 @@
|
||||||
.form-text.text-muted
|
.form-text.text-muted
|
||||||
= _('Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1.')
|
= _('Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1.')
|
||||||
|
|
||||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
= f.submit _('Save changes'), pajamas_button: true
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
= render 'devise/shared/tab_single', tab_title: 'Resend confirmation instructions'
|
= render 'devise/shared/tab_single', tab_title: 'Resend confirmation instructions'
|
||||||
.login-box.gl-p-5
|
.login-box.gl-p-5
|
||||||
.login-body
|
.login-body
|
||||||
= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f|
|
= gitlab_ui_form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f|
|
||||||
.devise-errors
|
.devise-errors
|
||||||
= render "devise/shared/error_messages", resource: resource
|
= render "devise/shared/error_messages", resource: resource
|
||||||
.form-group
|
.form-group
|
||||||
|
|
@ -13,7 +13,8 @@
|
||||||
= recaptcha_tags nonce: content_security_policy_nonce
|
= recaptcha_tags nonce: content_security_policy_nonce
|
||||||
|
|
||||||
.gl-mt-5
|
.gl-mt-5
|
||||||
= f.submit _("Resend"), class: 'gl-button btn btn-confirm'
|
= render Pajamas::ButtonComponent.new(block: true, type: :submit, variant: :confirm) do
|
||||||
|
= _("Resend")
|
||||||
|
|
||||||
.clearfix.prepend-top-20
|
.clearfix.prepend-top-20
|
||||||
= render 'devise/shared/sign_in_link'
|
= render 'devise/shared/sign_in_link'
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
"error-state-svg-path" => image_path('illustrations/feature_flag.svg'),
|
"error-state-svg-path" => image_path('illustrations/feature_flag.svg'),
|
||||||
"feature-flags-help-page-path" => help_page_path("operations/feature_flags"),
|
"feature-flags-help-page-path" => help_page_path("operations/feature_flags"),
|
||||||
"feature-flags-client-libraries-help-page-path" => help_page_path("operations/feature_flags", anchor: "choose-a-client-library"),
|
"feature-flags-client-libraries-help-page-path" => help_page_path("operations/feature_flags", anchor: "choose-a-client-library"),
|
||||||
"feature-flags-client-example-help-page-path" => help_page_path("operations/feature_flags", anchor: "golang-application-example"),
|
"feature-flags-client-example-help-page-path" => help_page_path("operations/feature_flags", anchor: "go-application-example"),
|
||||||
"feature-flags-limit-exceeded" => @project.actual_limits.exceeded?(:project_feature_flags, @project.operations_feature_flags.count),
|
"feature-flags-limit-exceeded" => @project.actual_limits.exceeded?(:project_feature_flags, @project.operations_feature_flags.count),
|
||||||
"feature-flags-limit" => @project.actual_limits.project_feature_flags,
|
"feature-flags-limit" => @project.actual_limits.project_feature_flags,
|
||||||
"unleash-api-url" => (unleash_api_url(@project) if can?(current_user, :admin_feature_flag, @project)),
|
"unleash-api-url" => (unleash_api_url(@project) if can?(current_user, :admin_feature_flag, @project)),
|
||||||
|
|
|
||||||
|
|
@ -27,4 +27,4 @@
|
||||||
= s_("GitLabPages|When enabled, a unique domain is generated to access pages.").html_safe
|
= s_("GitLabPages|When enabled, a unique domain is generated to access pages.").html_safe
|
||||||
|
|
||||||
.gl-mt-3
|
.gl-mt-3
|
||||||
= f.submit s_('GitLabPages|Save changes'), class: 'btn btn-confirm gl-button'
|
= f.submit s_('GitLabPages|Save changes'), pajamas_button: true
|
||||||
|
|
|
||||||
|
|
@ -38,4 +38,5 @@
|
||||||
- if partial_exists? "registrations/welcome/button"
|
- if partial_exists? "registrations/welcome/button"
|
||||||
= render "registrations/welcome/button"
|
= render "registrations/welcome/button"
|
||||||
- else
|
- else
|
||||||
= f.submit _('Get started!'), class: 'btn-confirm gl-button btn btn-block gl-mb-0 gl-p-3', data: { qa_selector: 'get_started_button' }
|
= render Pajamas::ButtonComponent.new(block: true, type: :submit, variant: :confirm, button_options: { class: 'gl-mb-0', data: { qa_selector: 'get_started_button' }}) do
|
||||||
|
= _('Get started!')
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
name: cache_client_with_metrics
|
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111210
|
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/392622
|
|
||||||
milestone: '15.10'
|
|
||||||
type: development
|
|
||||||
group: group::source code
|
|
||||||
default_enabled: false
|
|
||||||
|
|
@ -9,17 +9,10 @@ class ScheduleMigrationForRemediation < Gitlab::Database::Migration[2.1]
|
||||||
BATCH_SIZE = 5000
|
BATCH_SIZE = 5000
|
||||||
|
|
||||||
def up
|
def up
|
||||||
queue_batched_background_migration(
|
# no-op as described in https://docs.gitlab.com/ee/development/database/batched_background_migrations.html
|
||||||
MIGRATION,
|
|
||||||
:vulnerability_occurrences,
|
|
||||||
:id,
|
|
||||||
job_interval: DELAY_INTERVAL,
|
|
||||||
batch_size: BATCH_SIZE,
|
|
||||||
sub_batch_size: SUB_BATCH_SIZE
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def down
|
def down
|
||||||
delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, [])
|
# no-op as described in https://docs.gitlab.com/ee/development/database/batched_background_migrations.html
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ class FinalizeCiBuildNeedsBigIntConversion < Gitlab::Database::Migration[2.1]
|
||||||
TABLE_NAME = 'ci_build_needs'
|
TABLE_NAME = 'ci_build_needs'
|
||||||
|
|
||||||
def up
|
def up
|
||||||
|
return unless should_run?
|
||||||
|
|
||||||
ensure_batched_background_migration_is_finished(
|
ensure_batched_background_migration_is_finished(
|
||||||
job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
|
job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
|
||||||
table_name: TABLE_NAME,
|
table_name: TABLE_NAME,
|
||||||
|
|
@ -17,4 +19,10 @@ class FinalizeCiBuildNeedsBigIntConversion < Gitlab::Database::Migration[2.1]
|
||||||
end
|
end
|
||||||
|
|
||||||
def down; end
|
def down; end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def should_run?
|
||||||
|
!Gitlab.jh? && (Gitlab.com? || Gitlab.dev_or_test_env?)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class SwapColumnsCiBuildNeedsBigIntConversion < Gitlab::Database::Migration[2.1]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
TABLE_NAME = 'ci_build_needs'
|
||||||
|
|
||||||
|
def up
|
||||||
|
return unless should_run?
|
||||||
|
|
||||||
|
swap
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
return unless should_run?
|
||||||
|
|
||||||
|
swap
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def should_run?
|
||||||
|
!Gitlab.jh? && (Gitlab.com? || Gitlab.dev_or_test_env?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def swap
|
||||||
|
add_concurrent_index TABLE_NAME, :id_convert_to_bigint, unique: true, name:
|
||||||
|
'index_ci_build_needs_on_id_convert_to_bigint'
|
||||||
|
|
||||||
|
with_lock_retries(raise_on_exhaustion: true) do
|
||||||
|
execute "LOCK TABLE #{TABLE_NAME} IN ACCESS EXCLUSIVE MODE"
|
||||||
|
|
||||||
|
id_name = quote_column_name(:id)
|
||||||
|
temp_name = quote_column_name('id_tmp')
|
||||||
|
id_convert_to_bigint_name = quote_column_name(:id_convert_to_bigint)
|
||||||
|
|
||||||
|
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN #{id_name} TO #{temp_name}"
|
||||||
|
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN #{id_convert_to_bigint_name} TO #{id_name}"
|
||||||
|
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN #{temp_name} TO #{id_convert_to_bigint_name}"
|
||||||
|
|
||||||
|
function_name = Gitlab::Database::UnidirectionalCopyTrigger.on_table(
|
||||||
|
TABLE_NAME, connection: Ci::ApplicationRecord.connection
|
||||||
|
).name(
|
||||||
|
:id, :id_convert_to_bigint
|
||||||
|
)
|
||||||
|
execute "ALTER FUNCTION #{quote_table_name(function_name)} RESET ALL"
|
||||||
|
|
||||||
|
execute "ALTER SEQUENCE ci_build_needs_id_seq OWNED BY #{TABLE_NAME}.id"
|
||||||
|
change_column_default TABLE_NAME, :id, -> { "nextval('ci_build_needs_id_seq'::regclass)" }
|
||||||
|
change_column_default TABLE_NAME, :id_convert_to_bigint, 0
|
||||||
|
|
||||||
|
execute "ALTER TABLE #{TABLE_NAME} DROP CONSTRAINT ci_build_needs_pkey CASCADE"
|
||||||
|
rename_index TABLE_NAME, 'index_ci_build_needs_on_id_convert_to_bigint', 'ci_build_needs_pkey'
|
||||||
|
execute "ALTER TABLE #{TABLE_NAME} ADD CONSTRAINT ci_build_needs_pkey PRIMARY KEY USING INDEX ci_build_needs_pkey"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class DeleteSecurityPolicyBotUsers < Gitlab::Database::Migration[2.1]
|
||||||
|
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||||
|
|
||||||
|
class User < MigrationRecord
|
||||||
|
self.table_name = 'users'
|
||||||
|
|
||||||
|
enum user_type: { security_policy_bot: 10 }
|
||||||
|
end
|
||||||
|
|
||||||
|
def up
|
||||||
|
User.where(user_type: :security_policy_bot).delete_all
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
# no-op
|
||||||
|
|
||||||
|
# Deleted records can't be restored
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# rubocop: disable BackgroundMigration/MissingDictionaryFile
|
||||||
|
|
||||||
|
class RescheduleMigrationForRemediation < Gitlab::Database::Migration[2.1]
|
||||||
|
MIGRATION = 'MigrateRemediationsForVulnerabilityFindings'
|
||||||
|
DELAY_INTERVAL = 2.minutes
|
||||||
|
SUB_BATCH_SIZE = 500
|
||||||
|
BATCH_SIZE = 5000
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||||
|
|
||||||
|
def up
|
||||||
|
delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, [])
|
||||||
|
|
||||||
|
queue_batched_background_migration(
|
||||||
|
MIGRATION,
|
||||||
|
:vulnerability_occurrences,
|
||||||
|
:id,
|
||||||
|
job_interval: DELAY_INTERVAL,
|
||||||
|
batch_size: BATCH_SIZE,
|
||||||
|
sub_batch_size: SUB_BATCH_SIZE
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, [])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# rubocop: enable BackgroundMigration/MissingDictionaryFile
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
7df50689f7e9311ee8e5bd2513f4361be0fceef3962344d2d16bf511132c7a33
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
094eb5044e841050288c7362cc58c1b63ce4a349fe49a4c5ebee6b83a05feb56
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
e2f19bbc322127e439fffc4c1e2718288538aa6cb2d50a5248f12470b1c9491e
|
||||||
|
|
@ -12896,13 +12896,13 @@ CREATE SEQUENCE chat_teams_id_seq
|
||||||
ALTER SEQUENCE chat_teams_id_seq OWNED BY chat_teams.id;
|
ALTER SEQUENCE chat_teams_id_seq OWNED BY chat_teams.id;
|
||||||
|
|
||||||
CREATE TABLE ci_build_needs (
|
CREATE TABLE ci_build_needs (
|
||||||
id integer NOT NULL,
|
id_convert_to_bigint integer DEFAULT 0 NOT NULL,
|
||||||
name text NOT NULL,
|
name text NOT NULL,
|
||||||
artifacts boolean DEFAULT true NOT NULL,
|
artifacts boolean DEFAULT true NOT NULL,
|
||||||
optional boolean DEFAULT false NOT NULL,
|
optional boolean DEFAULT false NOT NULL,
|
||||||
build_id bigint NOT NULL,
|
build_id bigint NOT NULL,
|
||||||
partition_id bigint DEFAULT 100 NOT NULL,
|
partition_id bigint DEFAULT 100 NOT NULL,
|
||||||
id_convert_to_bigint bigint DEFAULT 0 NOT NULL
|
id bigint NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE SEQUENCE ci_build_needs_id_seq
|
CREATE SEQUENCE ci_build_needs_id_seq
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
#
|
#
|
||||||
# For a list of all options, see https://vale.sh/docs/topics/styles/
|
# For a list of all options, see https://vale.sh/docs/topics/styles/
|
||||||
extends: existence
|
extends: existence
|
||||||
message: "Instead of '%s' for the code block, use yaml, ruby, plaintext, markdown, javascript, shell, golang, python, dockerfile, or typescript."
|
message: "Instead of '%s' for the code block, use yaml, ruby, plaintext, markdown, javascript, shell, go, python, dockerfile, or typescript."
|
||||||
link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#code-blocks
|
link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#code-blocks
|
||||||
level: error
|
level: error
|
||||||
scope: raw
|
scope: raw
|
||||||
raw:
|
raw:
|
||||||
- '\`\`\`(yml|rb|text|md|bash|sh\n|js\n|go\n|py\n|docker\n|ts)'
|
- '\`\`\`(yml|rb|text|md|bash|sh\n|js\n|golang\n|py\n|docker\n|ts)'
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ swap:
|
||||||
params: parameters
|
params: parameters
|
||||||
pg: PostgreSQL
|
pg: PostgreSQL
|
||||||
'postgres$': PostgreSQL
|
'postgres$': PostgreSQL
|
||||||
|
golang: Go
|
||||||
raketask: Rake task
|
raketask: Rake task
|
||||||
raketasks: Rake tasks
|
raketasks: Rake tasks
|
||||||
rspec: RSpec
|
rspec: RSpec
|
||||||
|
|
|
||||||
|
|
@ -720,7 +720,7 @@ This error occurs when `praefect['database_port']` or `praefect['database_direct
|
||||||
|
|
||||||
## Profiling Gitaly
|
## Profiling Gitaly
|
||||||
|
|
||||||
Gitaly exposes several of the Golang built-in performance profiling tools on the Prometheus listen port. For example, if Prometheus is listening
|
Gitaly exposes several of the Go built-in performance profiling tools on the Prometheus listen port. For example, if Prometheus is listening
|
||||||
on port `9236` of the GitLab server:
|
on port `9236` of the GitLab server:
|
||||||
|
|
||||||
- Get a list of running `goroutines` and their backtraces:
|
- Get a list of running `goroutines` and their backtraces:
|
||||||
|
|
|
||||||
|
|
@ -863,7 +863,7 @@ Incorrect configuration of these values may result in intermittent
|
||||||
or persistent errors, or the Pages Daemon serving old content.
|
or persistent errors, or the Pages Daemon serving old content.
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
Expiry, interval and timeout flags use [Golang's duration formatting](https://pkg.go.dev/time#ParseDuration).
|
Expiry, interval and timeout flags use [Go duration formatting](https://pkg.go.dev/time#ParseDuration).
|
||||||
A duration string is a possibly signed sequence of decimal numbers,
|
A duration string is a possibly signed sequence of decimal numbers,
|
||||||
each with optional fraction and a unit suffix, such as `300ms`, `1.5h` or `2h45m`.
|
each with optional fraction and a unit suffix, such as `300ms`, `1.5h` or `2h45m`.
|
||||||
Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`.
|
Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`.
|
||||||
|
|
|
||||||
|
|
@ -233,3 +233,35 @@ Returns:
|
||||||
- `204 No Content` if the license is successfully deleted.
|
- `204 No Content` if the license is successfully deleted.
|
||||||
- `403 Forbidden` if the current user in not permitted to delete the license.
|
- `403 Forbidden` if the current user in not permitted to delete the license.
|
||||||
- `404 Not Found` if the license to delete could not be found.
|
- `404 Not Found` if the license to delete could not be found.
|
||||||
|
|
||||||
|
## Trigger recalculation of billable users
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
PUT /license/:id/refresh_billable_users
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | Required | Description |
|
||||||
|
| --------- | ---- | -------- | ----------- |
|
||||||
|
| `id` | integer | yes | ID of the GitLab license. |
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/license/:id/refresh_billable_users"
|
||||||
|
```
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
- `202 Accepted` if the request to refresh billable users is successfully initiated.
|
||||||
|
- `403 Forbidden` if the current user in not permitted to refresh billable users for the license.
|
||||||
|
- `404 Not Found` if the license could not be found.
|
||||||
|
|
||||||
|
| Attribute | Type | Description |
|
||||||
|
|:-----------------------------|:--------------|:------------------------------------------|
|
||||||
|
| `success` | boolean | Whether the request succeeded or not. |
|
||||||
|
|
|
||||||
|
|
@ -93,9 +93,9 @@ Additionally, since we intend to ingest data via Prometheus `remote_write` API,
|
||||||
|
|
||||||
We also need to make sure to avoid writing a lot of small writes into Clickhouse, therefore it’d be prudent to batch data before writing it into Clickhouse.
|
We also need to make sure to avoid writing a lot of small writes into Clickhouse, therefore it’d be prudent to batch data before writing it into Clickhouse.
|
||||||
|
|
||||||
We must also make sure ingestion remains decoupled with `Storage` so as to reduce undue dependence on a given storage implementation. While we do intend to use Clickhouse as our backing storage for any foreseeable future, this ensures we do not tie ourselves in into Clickhouse too much should future business requirements warrant the usage of a different backend/technology. A good way to implement this in Golang would be our implementations adhering to a standard interface, the following for example:
|
We must also make sure ingestion remains decoupled with `Storage` so as to reduce undue dependence on a given storage implementation. While we do intend to use Clickhouse as our backing storage for any foreseeable future, this ensures we do not tie ourselves in into Clickhouse too much should future business requirements warrant the usage of a different backend/technology. A good way to implement this in Go would be our implementations adhering to a standard interface, the following for example:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
type Storage interface {
|
type Storage interface {
|
||||||
Read(
|
Read(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ This has led to increased complexity across the board, from development
|
||||||
[we no longer recommend](../../../administration/nfs.md) to our
|
[we no longer recommend](../../../administration/nfs.md) to our
|
||||||
users and is no longer in use on GitLab.com.
|
users and is no longer in use on GitLab.com.
|
||||||
- Understanding all the moving parts and the flow is extremely
|
- Understanding all the moving parts and the flow is extremely
|
||||||
complicated: we have CarrierWave, Fog, Golang S3/Azure SDKs, all
|
complicated: we have CarrierWave, Fog, Go S3/Azure SDKs, all
|
||||||
being used, and that complicates testing as well.
|
being used, and that complicates testing as well.
|
||||||
- Fog and CarrierWave are not maintained to the level of the native
|
- Fog and CarrierWave are not maintained to the level of the native
|
||||||
SDKs (for example, AWS S3 SDK), so we have to maintain or monkey
|
SDKs (for example, AWS S3 SDK), so we have to maintain or monkey
|
||||||
|
|
|
||||||
|
|
@ -375,7 +375,7 @@ hierarchy. Choosing a proper solution will require a thoughtful research.
|
||||||
- Implementing a separate Go library which uses the same backend (for example, Redis) for rate limiting.
|
- Implementing a separate Go library which uses the same backend (for example, Redis) for rate limiting.
|
||||||
|
|
||||||
1. **SDK for Satellite Services (Owning Team)**
|
1. **SDK for Satellite Services (Owning Team)**
|
||||||
- Build Golang SDK.
|
- Build Go SDK.
|
||||||
- Create examples showcasing usage of the new rate limits SDK.
|
- Create examples showcasing usage of the new rate limits SDK.
|
||||||
|
|
||||||
1. **Team fan out for Satellite Services (Stage Groups)**
|
1. **Team fan out for Satellite Services (Stage Groups)**
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ choose one of these templates:
|
||||||
- [dotNET Core (`dotNET-Core.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/dotNET-Core.gitlab-ci.yml)
|
- [dotNET Core (`dotNET-Core.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/dotNET-Core.gitlab-ci.yml)
|
||||||
- [Elixir (`Elixir.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml)
|
- [Elixir (`Elixir.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml)
|
||||||
- [Flutter (`Flutter.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Flutter.gitlab-ci.yml)
|
- [Flutter (`Flutter.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Flutter.gitlab-ci.yml)
|
||||||
- [Golang (`Go.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Go.gitlab-ci.yml)
|
- [Go (`Go.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Go.gitlab-ci.yml)
|
||||||
- [Gradle (`Gradle.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml)
|
- [Gradle (`Gradle.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml)
|
||||||
- [Grails (`Grails.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Grails.gitlab-ci.yml)
|
- [Grails (`Grails.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Grails.gitlab-ci.yml)
|
||||||
- [iOS with fastlane (`iOS-Fastlane.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml)
|
- [iOS with fastlane (`iOS-Fastlane.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml)
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ This format was originally developed for Java, but most coverage analysis framew
|
||||||
for other languages have plugins to add support for it, like:
|
for other languages have plugins to add support for it, like:
|
||||||
|
|
||||||
- [simplecov-cobertura](https://rubygems.org/gems/simplecov-cobertura) (Ruby)
|
- [simplecov-cobertura](https://rubygems.org/gems/simplecov-cobertura) (Ruby)
|
||||||
- [gocover-cobertura](https://github.com/boumenot/gocover-cobertura) (Golang)
|
- [gocover-cobertura](https://github.com/boumenot/gocover-cobertura) (Go)
|
||||||
|
|
||||||
Other coverage analysis frameworks support the format out of the box, for example:
|
Other coverage analysis frameworks support the format out of the box, for example:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
||||||
|
|
||||||
## RPCs
|
## RPCs
|
||||||
|
|
||||||
Gitaly is a wrapper around the `git` binary, running in a [Gitaly Cluster](../../../administration/gitaly/index.md). It provides managed access to the file system housing the `git` repositories, via Golang Remote Procedure Calls (RPCs). Other functions are access optimization, caching, and a form of pagination against the file system.
|
Gitaly is a wrapper around the `git` binary, running in a [Gitaly Cluster](../../../administration/gitaly/index.md). It provides managed access to the file system housing the `git` repositories, using Go Remote Procedure Calls (RPCs). Other functions are access optimization, caching, and a form of pagination against the file system.
|
||||||
|
|
||||||
The comprehensive [Beginner's guide to Gitaly contributions](https://gitlab.com/gitlab-org/gitaly/-/blob/master/doc/beginners_guide.md) is focused on making updates to Gitaly, and offers many insights into how to understand the Gitaly code.
|
The comprehensive [Beginner's guide to Gitaly contributions](https://gitlab.com/gitlab-org/gitaly/-/blob/master/doc/beginners_guide.md) is focused on making updates to Gitaly, and offers many insights into how to understand the Gitaly code.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ To learn about the reasoning behind our creation of `gitlab-sshd`, read the blog
|
||||||
|
|
||||||
### Gitaly touch points
|
### Gitaly touch points
|
||||||
|
|
||||||
Gitaly is a Golang RPC service which handles all the `git` calls made by GitLab.
|
Gitaly is a Go RPC service which handles all the `git` calls made by GitLab.
|
||||||
GitLab is not exposed directly, and all traffic comes through Create: Source Code.
|
GitLab is not exposed directly, and all traffic comes through Create: Source Code.
|
||||||
For more information, read [Gitaly touch points](gitaly_touch_points.md).
|
For more information, read [Gitaly touch points](gitaly_touch_points.md).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -741,7 +741,7 @@ Do not use **limitations**. Use **known issues** instead.
|
||||||
|
|
||||||
## log in, log on
|
## log in, log on
|
||||||
|
|
||||||
Do not use **log in** or **log on**. Use [sign in](#sign-in) instead. If the user interface has **Log in**, you can use it.
|
Do not use **log in** or **log on**. Use [sign in](#sign-in-sign-in) instead. If the user interface has **Log in**, you can use it.
|
||||||
|
|
||||||
## logged-in user, logged in user
|
## logged-in user, logged in user
|
||||||
|
|
||||||
|
|
@ -1154,14 +1154,17 @@ Use **setup** as a noun, and **set up** as a verb. For example:
|
||||||
- Your remote office setup is amazing.
|
- Your remote office setup is amazing.
|
||||||
- To set up your remote office correctly, consider the ergonomics of your work area.
|
- To set up your remote office correctly, consider the ergonomics of your work area.
|
||||||
|
|
||||||
## sign in
|
## sign in, sign-in
|
||||||
|
|
||||||
Use **sign in** or **sign in to**.
|
Use **sign in** or **sign in to** as a verb to describe the action of signing in.
|
||||||
|
|
||||||
Do not use **sign on** or **sign into**, or **log on**, **log in**, or **log into**.
|
Do not use **sign on** or **sign into**, or **log on**, **log in**, or **log into**.
|
||||||
|
|
||||||
If the user interface has different words, use those.
|
If the user interface has different words, use those.
|
||||||
|
|
||||||
|
You can use **sign-in** as a noun or adjective. For example, **sign-in page** or
|
||||||
|
**sign-in restrictions**.
|
||||||
|
|
||||||
You can use **single sign-on**.
|
You can use **single sign-on**.
|
||||||
|
|
||||||
## sign up
|
## sign up
|
||||||
|
|
|
||||||
|
|
@ -446,7 +446,7 @@ irb(main):001:0> require 'openssl'; OpenSSL.fips_mode
|
||||||
|
|
||||||
### Go
|
### Go
|
||||||
|
|
||||||
Google maintains a [`dev.boringcrypto` branch](https://github.com/golang/go/tree/dev.boringcrypto) in the Golang compiler
|
Google maintains a [`dev.boringcrypto` branch](https://github.com/golang/go/tree/dev.boringcrypto) in the Go compiler
|
||||||
that makes it possible to statically link BoringSSL, a FIPS-validated module forked from OpenSSL.
|
that makes it possible to statically link BoringSSL, a FIPS-validated module forked from OpenSSL.
|
||||||
However, BoringSSL is not intended for public use.
|
However, BoringSSL is not intended for public use.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -257,13 +257,13 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag.
|
||||||
|
|
||||||
1. Create a package scoped flag name:
|
1. Create a package scoped flag name:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
var findAllTagsFeatureFlag = "go-find-all-tags"
|
var findAllTagsFeatureFlag = "go-find-all-tags"
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Create a switch in the code using the `featureflag` package:
|
1. Create a switch in the code using the `featureflag` package:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
if featureflag.IsEnabled(ctx, findAllTagsFeatureFlag) {
|
if featureflag.IsEnabled(ctx, findAllTagsFeatureFlag) {
|
||||||
// go implementation
|
// go implementation
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -273,7 +273,7 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag.
|
||||||
|
|
||||||
1. Create Prometheus metrics:
|
1. Create Prometheus metrics:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
var findAllTagsRequests = prometheus.NewCounterVec(
|
var findAllTagsRequests = prometheus.NewCounterVec(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "gitaly_find_all_tags_requests_total",
|
Name: "gitaly_find_all_tags_requests_total",
|
||||||
|
|
@ -297,7 +297,7 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag.
|
||||||
|
|
||||||
1. Set headers in tests:
|
1. Set headers in tests:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
import (
|
import (
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ Ruby to build and test, but not to run.
|
||||||
GitLab Shell runs on `port 22` on an Omnibus installation. To use a regular SSH
|
GitLab Shell runs on `port 22` on an Omnibus installation. To use a regular SSH
|
||||||
service, configure it on an alternative port.
|
service, configure it on an alternative port.
|
||||||
|
|
||||||
Download and install the current version of Go from [golang.org](https://go.dev/dl/).
|
Download and install the [current version of Go](https://go.dev/dl/).
|
||||||
We follow the [Golang Release Policy](https://golang.org/doc/devel/release.html#policy)
|
We follow the [Go Release Policy](https://go.dev/doc/devel/release#policy)
|
||||||
and support:
|
and support:
|
||||||
|
|
||||||
- The current stable version.
|
- The current stable version.
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ by Distribution:
|
||||||
|
|
||||||
## Supporting multiple Go versions
|
## Supporting multiple Go versions
|
||||||
|
|
||||||
Individual Golang projects need to support multiple Go versions because:
|
Individual Go projects need to support multiple Go versions because:
|
||||||
|
|
||||||
- When a new version of Go is released, we should start integrating it into the CI pipelines to verify compatibility with the new compiler.
|
- When a new version of Go is released, we should start integrating it into the CI pipelines to verify compatibility with the new compiler.
|
||||||
- We must support the [official Omnibus GitLab Go version](#updating-go-version), which may be behind the latest minor release.
|
- We must support the [official Omnibus GitLab Go version](#updating-go-version), which may be behind the latest minor release.
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,7 @@ When comparing expected and actual values in tests, use
|
||||||
and others to improve readability when comparing structs, errors,
|
and others to improve readability when comparing structs, errors,
|
||||||
large portions of text, or JSON documents:
|
large portions of text, or JSON documents:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
type TestData struct {
|
type TestData struct {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
@ -291,7 +291,7 @@ easier to debug.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
// Wrap the error
|
// Wrap the error
|
||||||
return nil, fmt.Errorf("get cache %s: %w", f.Name, err)
|
return nil, fmt.Errorf("get cache %s: %w", f.Name, err)
|
||||||
|
|
||||||
|
|
@ -462,7 +462,7 @@ allocations.
|
||||||
|
|
||||||
**Don't:**
|
**Don't:**
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
var s2 []string
|
var s2 []string
|
||||||
for _, val := range s1 {
|
for _, val := range s1 {
|
||||||
s2 = append(s2, val)
|
s2 = append(s2, val)
|
||||||
|
|
@ -471,7 +471,7 @@ for _, val := range s1 {
|
||||||
|
|
||||||
**Do:**
|
**Do:**
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
s2 := make([]string, 0, len(s1))
|
s2 := make([]string, 0, len(s1))
|
||||||
for _, val := range s1 {
|
for _, val := range s1 {
|
||||||
s2 = append(s2, val)
|
s2 = append(s2, val)
|
||||||
|
|
@ -494,7 +494,7 @@ If the scanner report is small, less than 35 lines, then feel free to [inline th
|
||||||
|
|
||||||
The [go-cmp](https://github.com/google/go-cmp) package should be used when comparing large structs in tests. It makes it possible to output a specific diff where the two structs differ, rather than seeing the whole of both structs printed out in the test logs. Here is a small example:
|
The [go-cmp](https://github.com/google/go-cmp) package should be used when comparing large structs in tests. It makes it possible to output a specific diff where the two structs differ, rather than seeing the whole of both structs printed out in the test logs. Here is a small example:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
||||||
|
|
@ -542,11 +542,11 @@ print(p.join('log', '/etc/passwd', ''))
|
||||||
# renders the path to "/etc/passwd", which is not what we expect!
|
# renders the path to "/etc/passwd", which is not what we expect!
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Golang
|
#### Go
|
||||||
|
|
||||||
Golang has similar behavior with [`path.Clean`](https://pkg.go.dev/path#example-Clean). Remember that with many file systems, using `../../../../` traverses up to the root directory. Any remaining `../` are ignored. This example may give an attacker access to `/etc/passwd`:
|
Go has similar behavior with [`path.Clean`](https://pkg.go.dev/path#example-Clean). Remember that with many file systems, using `../../../../` traverses up to the root directory. Any remaining `../` are ignored. This example may give an attacker access to `/etc/passwd`:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
path.Clean("/../../etc/passwd")
|
path.Clean("/../../etc/passwd")
|
||||||
// renders the path to "etc/passwd"; the file path is relative to whatever the current directory is
|
// renders the path to "etc/passwd"; the file path is relative to whatever the current directory is
|
||||||
path.Clean("../../etc/passwd")
|
path.Clean("../../etc/passwd")
|
||||||
|
|
@ -601,7 +601,7 @@ Go has built-in protections that usually prevent an attacker from successfully i
|
||||||
|
|
||||||
Consider the following example:
|
Consider the following example:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -620,7 +620,7 @@ This echoes `"1; cat /etc/passwd"`.
|
||||||
|
|
||||||
**Do not** use `sh`, as it bypasses internal protections:
|
**Do not** use `sh`, as it bypasses internal protections:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
out, _ = exec.Command("sh", "-c", "echo 1 | cat /etc/passwd").Output()
|
out, _ = exec.Command("sh", "-c", "echo 1 | cat /etc/passwd").Output()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -646,15 +646,15 @@ And the following cipher suites (according to the [RFC 8446](https://datatracker
|
||||||
- `TLS_AES_128_GCM_SHA256`
|
- `TLS_AES_128_GCM_SHA256`
|
||||||
- `TLS_AES_256_GCM_SHA384`
|
- `TLS_AES_256_GCM_SHA384`
|
||||||
|
|
||||||
*Note*: **Golang** does [not support](https://github.com/golang/go/blob/go1.17/src/crypto/tls/cipher_suites.go#L676) all cipher suites with TLS 1.3.
|
*Note*: **Go** does [not support](https://github.com/golang/go/blob/go1.17/src/crypto/tls/cipher_suites.go#L676) all cipher suites with TLS 1.3.
|
||||||
|
|
||||||
##### Implementation examples
|
##### Implementation examples
|
||||||
|
|
||||||
##### TLS 1.3
|
##### TLS 1.3
|
||||||
|
|
||||||
For TLS 1.3, **Golang** only supports [3 cipher suites](https://github.com/golang/go/blob/go1.17/src/crypto/tls/cipher_suites.go#L676), as such we only need to set the TLS version:
|
For TLS 1.3, **Go** only supports [3 cipher suites](https://github.com/golang/go/blob/go1.17/src/crypto/tls/cipher_suites.go#L676), as such we only need to set the TLS version:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
cfg := &tls.Config{
|
cfg := &tls.Config{
|
||||||
MinVersion: tls.VersionTLS13,
|
MinVersion: tls.VersionTLS13,
|
||||||
}
|
}
|
||||||
|
|
@ -678,9 +678,9 @@ response = GitLab::HTTP.perform_request(Net::HTTP::Get, 'https://gitlab.com', ss
|
||||||
|
|
||||||
##### TLS 1.2
|
##### TLS 1.2
|
||||||
|
|
||||||
**Golang** does support multiple cipher suites that we do not want to use with TLS 1.2. We need to explicitly list authorized ciphers:
|
**Go** does support multiple cipher suites that we do not want to use with TLS 1.2. We need to explicitly list authorized ciphers:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
func secureCipherSuites() []uint16 {
|
func secureCipherSuites() []uint16 {
|
||||||
return []uint16{
|
return []uint16{
|
||||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
|
@ -692,7 +692,7 @@ func secureCipherSuites() []uint16 {
|
||||||
|
|
||||||
And then use `secureCipherSuites()` in `tls.Config`:
|
And then use `secureCipherSuites()` in `tls.Config`:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
tls.Config{
|
tls.Config{
|
||||||
(...),
|
(...),
|
||||||
CipherSuites: secureCipherSuites(),
|
CipherSuites: secureCipherSuites(),
|
||||||
|
|
@ -920,7 +920,7 @@ end
|
||||||
|
|
||||||
#### Go
|
#### Go
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
// unzip INSECURELY extracts source zip file to destination.
|
// unzip INSECURELY extracts source zip file to destination.
|
||||||
func unzip(src, dest string) error {
|
func unzip(src, dest string) error {
|
||||||
r, err := zip.OpenReader(src)
|
r, err := zip.OpenReader(src)
|
||||||
|
|
@ -1016,7 +1016,7 @@ end
|
||||||
|
|
||||||
You are encouraged to use the secure archive utilities provided by [LabSec](https://gitlab.com/gitlab-com/gl-security/appsec/labsec) which will handle Zip Slip and other types of vulnerabilities for you. The LabSec utilities are also context aware which makes it possible to cancel or timeout extractions:
|
You are encouraged to use the secure archive utilities provided by [LabSec](https://gitlab.com/gitlab-com/gl-security/appsec/labsec) which will handle Zip Slip and other types of vulnerabilities for you. The LabSec utilities are also context aware which makes it possible to cancel or timeout extractions:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "gitlab-com/gl-security/appsec/labsec/archive/zip"
|
import "gitlab-com/gl-security/appsec/labsec/archive/zip"
|
||||||
|
|
@ -1041,7 +1041,7 @@ func main() {
|
||||||
|
|
||||||
In case the LabSec utilities do not fit your needs, here is an example for extracting a zip file with protection against Zip Slip attacks:
|
In case the LabSec utilities do not fit your needs, here is an example for extracting a zip file with protection against Zip Slip attacks:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
// unzip extracts source zip file to destination with protection against Zip Slip attacks.
|
// unzip extracts source zip file to destination with protection against Zip Slip attacks.
|
||||||
func unzip(src, dest string) error {
|
func unzip(src, dest string) error {
|
||||||
r, err := zip.OpenReader(src)
|
r, err := zip.OpenReader(src)
|
||||||
|
|
@ -1118,7 +1118,7 @@ end
|
||||||
|
|
||||||
#### Go
|
#### Go
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
// printZipContents INSECURELY prints contents of files in a zip file.
|
// printZipContents INSECURELY prints contents of files in a zip file.
|
||||||
func printZipContents(src string) error {
|
func printZipContents(src string) error {
|
||||||
r, err := zip.OpenReader(src)
|
r, err := zip.OpenReader(src)
|
||||||
|
|
@ -1186,7 +1186,7 @@ You are encouraged to use the secure archive utilities provided by [LabSec](http
|
||||||
|
|
||||||
In case the LabSec utilities do not fit your needs, here is an example for extracting a zip file with protection against symlink attacks:
|
In case the LabSec utilities do not fit your needs, here is an example for extracting a zip file with protection against symlink attacks:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
// printZipContents prints contents of files in a zip file with protection against symlink attacks.
|
// printZipContents prints contents of files in a zip file with protection against symlink attacks.
|
||||||
func printZipContents(src string) error {
|
func printZipContents(src string) error {
|
||||||
r, err := zip.OpenReader(src)
|
r, err := zip.OpenReader(src)
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ They consist of:
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
u.route("PUT", apiProjectPattern+`packages/nuget/`, mimeMultipartUploader),
|
u.route("PUT", apiProjectPattern+`packages/nuget/`, mimeMultipartUploader),
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -227,8 +227,8 @@ The following Elasticsearch settings are available:
|
||||||
| `AWS Secret Access Key` | The AWS secret access key. |
|
| `AWS Secret Access Key` | The AWS secret access key. |
|
||||||
| `Maximum file size indexed` | See [the explanation in instance limits.](../../administration/instance_limits.md#maximum-file-size-indexed). |
|
| `Maximum file size indexed` | See [the explanation in instance limits.](../../administration/instance_limits.md#maximum-file-size-indexed). |
|
||||||
| `Maximum field length` | See [the explanation in instance limits.](../../administration/instance_limits.md#maximum-field-length). |
|
| `Maximum field length` | See [the explanation in instance limits.](../../administration/instance_limits.md#maximum-field-length). |
|
||||||
| `Maximum bulk request size (MiB)` | Used by the GitLab Ruby and Golang-based indexer processes. This setting indicates how much data must be collected (and stored in memory) in a given indexing process before submitting the payload to the Elasticsearch Bulk API. For the GitLab Golang-based indexer, you should use this setting with `Bulk request concurrency`. `Maximum bulk request size (MiB)` must accommodate the resource constraints of both the Elasticsearch hosts and the hosts running the GitLab Golang-based indexer from either the `gitlab-rake` command or the Sidekiq tasks. |
|
| `Maximum bulk request size (MiB)` | Used by the GitLab Ruby and Go-based indexer processes. This setting indicates how much data must be collected (and stored in memory) in a given indexing process before submitting the payload to the Elasticsearch Bulk API. For the GitLab Go-based indexer, you should use this setting with `Bulk request concurrency`. `Maximum bulk request size (MiB)` must accommodate the resource constraints of both the Elasticsearch hosts and the hosts running the GitLab Go-based indexer from either the `gitlab-rake` command or the Sidekiq tasks. |
|
||||||
| `Bulk request concurrency` | The Bulk request concurrency indicates how many of the GitLab Golang-based indexer processes (or threads) can run in parallel to collect data to subsequently submit to the Elasticsearch Bulk API. This increases indexing performance, but fills the Elasticsearch bulk requests queue faster. This setting should be used together with the Maximum bulk request size setting (see above) and needs to accommodate the resource constraints of both the Elasticsearch hosts and the hosts running the GitLab Golang-based indexer either from the `gitlab-rake` command or the Sidekiq tasks. |
|
| `Bulk request concurrency` | The Bulk request concurrency indicates how many of the GitLab Go-based indexer processes (or threads) can run in parallel to collect data to subsequently submit to the Elasticsearch Bulk API. This increases indexing performance, but fills the Elasticsearch bulk requests queue faster. This setting should be used together with the Maximum bulk request size setting (see above) and needs to accommodate the resource constraints of both the Elasticsearch hosts and the hosts running the GitLab Go-based indexer either from the `gitlab-rake` command or the Sidekiq tasks. |
|
||||||
| `Client request timeout` | Elasticsearch HTTP client request timeout value in seconds. `0` means using the system default timeout value, which depends on the libraries that GitLab application is built upon. |
|
| `Client request timeout` | Elasticsearch HTTP client request timeout value in seconds. `0` means using the system default timeout value, which depends on the libraries that GitLab application is built upon. |
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
|
|
|
||||||
|
|
@ -297,11 +297,11 @@ For API content, see:
|
||||||
- [Feature flags API](../api/feature_flags.md)
|
- [Feature flags API](../api/feature_flags.md)
|
||||||
- [Feature flag user lists API](../api/feature_flag_user_lists.md)
|
- [Feature flag user lists API](../api/feature_flag_user_lists.md)
|
||||||
|
|
||||||
### Golang application example
|
### Go application example
|
||||||
|
|
||||||
Here's an example of how to integrate feature flags in a Golang application:
|
Here's an example of how to integrate feature flags in a Go application:
|
||||||
|
|
||||||
```golang
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ You can use the following fuzzing engines to test the specified languages.
|
||||||
| Language | Fuzzing Engine | Example |
|
| Language | Fuzzing Engine | Example |
|
||||||
|---------------------------------------------|------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|
|
|---------------------------------------------|------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| C/C++ | [libFuzzer](https://llvm.org/docs/LibFuzzer.html) | [c-cpp-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/c-cpp-fuzzing-example) |
|
| C/C++ | [libFuzzer](https://llvm.org/docs/LibFuzzer.html) | [c-cpp-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/c-cpp-fuzzing-example) |
|
||||||
| GoLang | [go-fuzz (libFuzzer support)](https://github.com/dvyukov/go-fuzz) | [go-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/go-fuzzing-example) |
|
| Go | [go-fuzz (libFuzzer support)](https://github.com/dvyukov/go-fuzz) | [go-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/go-fuzzing-example) |
|
||||||
| Swift | [libFuzzer](https://github.com/apple/swift/blob/master/docs/libFuzzerIntegration.md) | [swift-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/swift-fuzzing-example) |
|
| Swift | [libFuzzer](https://github.com/apple/swift/blob/master/docs/libFuzzerIntegration.md) | [swift-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/swift-fuzzing-example) |
|
||||||
| Rust | [cargo-fuzz (libFuzzer support)](https://github.com/rust-fuzz/cargo-fuzz) | [rust-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/rust-fuzzing-example) |
|
| Rust | [cargo-fuzz (libFuzzer support)](https://github.com/rust-fuzz/cargo-fuzz) | [rust-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/rust-fuzzing-example) |
|
||||||
| Java | [Javafuzz](https://gitlab.com/gitlab-org/security-products/analyzers/fuzzers/javafuzz) (recommended) | [javafuzz-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/javafuzz-fuzzing-example) |
|
| Java | [Javafuzz](https://gitlab.com/gitlab-org/security-products/analyzers/fuzzers/javafuzz) (recommended) | [javafuzz-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/javafuzz-fuzzing-example) |
|
||||||
|
|
|
||||||
|
|
@ -281,7 +281,7 @@ Here are some other options you can use to reduce the Container Registry storage
|
||||||
|
|
||||||
If you see this error message, check the regex patterns to ensure they are valid.
|
If you see this error message, check the regex patterns to ensure they are valid.
|
||||||
|
|
||||||
GitLab uses [RE2 syntax](https://github.com/google/re2/wiki/Syntax) for regular expressions in the cleanup policy. You can test them with the [regex101 regex tester](https://regex101.com/) using the Golang flavor.
|
GitLab uses [RE2 syntax](https://github.com/google/re2/wiki/Syntax) for regular expressions in the cleanup policy. You can test them with the [regex101 regex tester](https://regex101.com/) using the `Golang` flavor.
|
||||||
View some common [regex pattern examples](#regex-pattern-examples).
|
View some common [regex pattern examples](#regex-pattern-examples).
|
||||||
|
|
||||||
### The cleanup policy doesn't delete any tags
|
### The cleanup policy doesn't delete any tags
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ go env -w GOPROXY='https://gitlab.example.com/api/v4/projects/1234/packages/go,h
|
||||||
With this configuration, Go fetches dependencies in this order:
|
With this configuration, Go fetches dependencies in this order:
|
||||||
|
|
||||||
1. Go attempts to fetch from the project-specific Go proxy.
|
1. Go attempts to fetch from the project-specific Go proxy.
|
||||||
1. Go attempts to fetch from [proxy.golang.org](https://proxy.golang.org).
|
1. Go attempts to fetch from [`proxy.golang.org`](https://proxy.golang.org).
|
||||||
1. Go fetches directly with version control system operations (like `git clone`,
|
1. Go fetches directly with version control system operations (like `git clone`,
|
||||||
`svn checkout`, and so on).
|
`svn checkout`, and so on).
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
|
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
stage: Create
|
||||||
|
group: Code Review
|
||||||
|
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
|
||||||
|
type: howto
|
||||||
|
---
|
||||||
|
|
||||||
|
# Saved replies **(FREE)**
|
||||||
|
|
||||||
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352956) in GitLab 14.9 [with a flag](../../administration/feature_flags.md) named `saved_replies`. Disabled by default.
|
||||||
|
|
||||||
|
With saved replies, create and reuse text for any text area in:
|
||||||
|
|
||||||
|
- Merge requests, including diffs.
|
||||||
|
- Issues, including design management comments.
|
||||||
|
- Epics.
|
||||||
|
- Work items.
|
||||||
|
|
||||||
|
Saved replies can be small, like approving a merge request and unassigning yourself from it,
|
||||||
|
or large, like chunks of boilerplate text you use frequently:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Use saved replies in a text area
|
||||||
|
|
||||||
|
To include the text of a saved reply in your comment:
|
||||||
|
|
||||||
|
1. In the editor toolbar for your comment, select **Saved replies** (**{symlink}**).
|
||||||
|
1. Select your desired saved reply.
|
||||||
|
|
||||||
|
## Create saved replies
|
||||||
|
|
||||||
|
To create a saved reply for future use:
|
||||||
|
|
||||||
|
1. On the top bar, in the upper-right corner, select your avatar.
|
||||||
|
1. From the dropdown list, select **Preferences**.
|
||||||
|
1. On the left sidebar, select **Saved replies** (**{symlink}**).
|
||||||
|
1. Provide a **Name** for your saved reply.
|
||||||
|
1. Enter the **Content** of your reply. You can use any formatting you use in
|
||||||
|
other GitLab text areas.
|
||||||
|
1. Select **Save**, and the page reloads with your saved reply shown.
|
||||||
|
|
||||||
|
## View your saved replies
|
||||||
|
|
||||||
|
To go to your saved replies:
|
||||||
|
|
||||||
|
1. On the top bar, in the upper-right corner, select your avatar.
|
||||||
|
1. From the dropdown list, select **Preferences**.
|
||||||
|
1. On the left sidebar, select **Saved replies** (**{symlink}**).
|
||||||
|
1. Scroll to **My saved replies**.
|
||||||
|
|
||||||
|
## Edit or delete saved replies
|
||||||
|
|
||||||
|
To edit or delete a previously saved reply:
|
||||||
|
|
||||||
|
1. On the top bar, in the upper-right corner, select your avatar.
|
||||||
|
1. From the dropdown list, select **Preferences**.
|
||||||
|
1. On the left sidebar, select **Saved replies** (**{symlink}**).
|
||||||
|
1. Scroll to **My saved replies**, and identify the saved reply you want to edit.
|
||||||
|
1. To edit, select **Edit** (**{pencil}**).
|
||||||
|
1. To delete, select **Delete** (**{remove}**), then select **Delete** again from the modal window.
|
||||||
|
|
@ -57,15 +57,11 @@ module API
|
||||||
end
|
end
|
||||||
|
|
||||||
def cache_client
|
def cache_client
|
||||||
if Feature.enabled?(:cache_client_with_metrics, user_project)
|
Gitlab::Cache::Client.build_with_metadata(
|
||||||
Gitlab::Cache::Client.build_with_metadata(
|
cache_identifier: 'API::Files#content_sha',
|
||||||
cache_identifier: 'API::Files#content_sha',
|
feature_category: :source_code_management,
|
||||||
feature_category: :source_code_management,
|
backing_resource: :gitaly
|
||||||
backing_resource: :gitaly
|
)
|
||||||
)
|
|
||||||
else
|
|
||||||
Rails.cache
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_blame_range(blame_params)
|
def fetch_blame_range(blame_params)
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ module Gitlab
|
||||||
return [] unless parsed_metadata['remediations']
|
return [] unless parsed_metadata['remediations']
|
||||||
|
|
||||||
parsed_metadata['remediations'].filter_map do |remediation|
|
parsed_metadata['remediations'].filter_map do |remediation|
|
||||||
next unless remediation
|
next unless remediation && remediation['diff'].present?
|
||||||
|
|
||||||
remediation.merge('checksum' => DiffFile.checksum(remediation['diff']))
|
remediation.merge('checksum' => DiffFile.checksum(remediation['diff']))
|
||||||
end.compact.uniq
|
end.compact.uniq
|
||||||
|
|
|
||||||
|
|
@ -7732,9 +7732,6 @@ msgstr ""
|
||||||
msgid "BulkImport|Import is finished. Pick another name for re-import"
|
msgid "BulkImport|Import is finished. Pick another name for re-import"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "BulkImport|Import selected"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "BulkImport|Import with projects"
|
msgid "BulkImport|Import with projects"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
"@gitlab/favicon-overlay": "2.0.0",
|
"@gitlab/favicon-overlay": "2.0.0",
|
||||||
"@gitlab/fonts": "^1.2.0",
|
"@gitlab/fonts": "^1.2.0",
|
||||||
"@gitlab/svgs": "3.24.0",
|
"@gitlab/svgs": "3.24.0",
|
||||||
"@gitlab/ui": "56.4.0",
|
"@gitlab/ui": "56.4.1",
|
||||||
"@gitlab/visual-review-tools": "1.7.3",
|
"@gitlab/visual-review-tools": "1.7.3",
|
||||||
"@gitlab/web-ide": "0.0.1-dev-20230223005157",
|
"@gitlab/web-ide": "0.0.1-dev-20230223005157",
|
||||||
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
|
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,13 @@ describe('Header Search Store Getters', () => {
|
||||||
MOCK_DEFAULT_SEARCH_OPTIONS,
|
MOCK_DEFAULT_SEARCH_OPTIONS,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns the correct array if issues path is false', () => {
|
||||||
|
mockGetters.scopedIssuesPath = undefined;
|
||||||
|
expect(getters.defaultSearchOptions(state, mockGetters)).toStrictEqual(
|
||||||
|
MOCK_DEFAULT_SEARCH_OPTIONS.slice(2, MOCK_DEFAULT_SEARCH_OPTIONS.length),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('scopedSearchOptions', () => {
|
describe('scopedSearchOptions', () => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { GlButton, GlIcon, GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
import { GlDropdown, GlIcon, GlDropdownItem } from '@gitlab/ui';
|
||||||
import { shallowMount } from '@vue/test-utils';
|
import { shallowMount } from '@vue/test-utils';
|
||||||
import ImportActionsCell from '~/import_entities/import_groups/components/import_actions_cell.vue';
|
import ImportActionsCell from '~/import_entities/import_groups/components/import_actions_cell.vue';
|
||||||
|
|
||||||
|
|
@ -8,7 +8,6 @@ describe('import actions cell', () => {
|
||||||
const createComponent = (props) => {
|
const createComponent = (props) => {
|
||||||
wrapper = shallowMount(ImportActionsCell, {
|
wrapper = shallowMount(ImportActionsCell, {
|
||||||
propsData: {
|
propsData: {
|
||||||
isProjectsImportEnabled: false,
|
|
||||||
isFinished: false,
|
isFinished: false,
|
||||||
isAvailableForImport: false,
|
isAvailableForImport: false,
|
||||||
isInvalid: false,
|
isInvalid: false,
|
||||||
|
|
@ -22,10 +21,10 @@ describe('import actions cell', () => {
|
||||||
createComponent({ isAvailableForImport: true });
|
createComponent({ isAvailableForImport: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders import button', () => {
|
it('renders import dropdown', () => {
|
||||||
const button = wrapper.findComponent(GlButton);
|
const dropdown = wrapper.findComponent(GlDropdown);
|
||||||
expect(button.exists()).toBe(true);
|
expect(dropdown.exists()).toBe(true);
|
||||||
expect(button.text()).toBe('Import');
|
expect(dropdown.props('text')).toBe('Import with projects');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not render icon with a hint', () => {
|
it('does not render icon with a hint', () => {
|
||||||
|
|
@ -38,10 +37,10 @@ describe('import actions cell', () => {
|
||||||
createComponent({ isAvailableForImport: false, isFinished: true });
|
createComponent({ isAvailableForImport: false, isFinished: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders re-import button', () => {
|
it('renders re-import dropdown', () => {
|
||||||
const button = wrapper.findComponent(GlButton);
|
const dropdown = wrapper.findComponent(GlDropdown);
|
||||||
expect(button.exists()).toBe(true);
|
expect(dropdown.exists()).toBe(true);
|
||||||
expect(button.text()).toBe('Re-import');
|
expect(dropdown.props('text')).toBe('Re-import with projects');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders icon with a hint', () => {
|
it('renders icon with a hint', () => {
|
||||||
|
|
@ -53,25 +52,25 @@ describe('import actions cell', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not render import button when group is not available for import', () => {
|
it('does not render import dropdown when group is not available for import', () => {
|
||||||
createComponent({ isAvailableForImport: false });
|
createComponent({ isAvailableForImport: false });
|
||||||
|
|
||||||
const button = wrapper.findComponent(GlButton);
|
const dropdown = wrapper.findComponent(GlDropdown);
|
||||||
expect(button.exists()).toBe(false);
|
expect(dropdown.exists()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders import button as disabled when group is invalid', () => {
|
it('renders import dropdown as disabled when group is invalid', () => {
|
||||||
createComponent({ isInvalid: true, isAvailableForImport: true });
|
createComponent({ isInvalid: true, isAvailableForImport: true });
|
||||||
|
|
||||||
const button = wrapper.findComponent(GlButton);
|
const dropdown = wrapper.findComponent(GlDropdown);
|
||||||
expect(button.props().disabled).toBe(true);
|
expect(dropdown.props().disabled).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('emits import-group event when import button is clicked', () => {
|
it('emits import-group event when import button is clicked', () => {
|
||||||
createComponent({ isAvailableForImport: true });
|
createComponent({ isAvailableForImport: true });
|
||||||
|
|
||||||
const button = wrapper.findComponent(GlButton);
|
const dropdown = wrapper.findComponent(GlDropdown);
|
||||||
button.vm.$emit('click');
|
dropdown.vm.$emit('click');
|
||||||
|
|
||||||
expect(wrapper.emitted('import-group')).toHaveLength(1);
|
expect(wrapper.emitted('import-group')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
@ -81,10 +80,10 @@ describe('import actions cell', () => {
|
||||||
${false} | ${'Import'}
|
${false} | ${'Import'}
|
||||||
${true} | ${'Re-import'}
|
${true} | ${'Re-import'}
|
||||||
`(
|
`(
|
||||||
'when import projects is enabled, group is available for import and finish status is $status',
|
'group is available for import and finish status is $isFinished',
|
||||||
({ isFinished, expectedAction }) => {
|
({ isFinished, expectedAction }) => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createComponent({ isProjectsImportEnabled: true, isAvailableForImport: true, isFinished });
|
createComponent({ isAvailableForImport: true, isFinished });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('render import dropdown', () => {
|
it('render import dropdown', () => {
|
||||||
|
|
|
||||||
|
|
@ -49,12 +49,12 @@ describe('import table', () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const findImportSelectedButton = () =>
|
|
||||||
wrapper.findAll('button').wrappers.find((w) => w.text() === 'Import selected');
|
|
||||||
const findImportSelectedDropdown = () =>
|
const findImportSelectedDropdown = () =>
|
||||||
wrapper.findAll('.gl-dropdown').wrappers.find((w) => w.text().includes('Import with projects'));
|
wrapper.find('[data-testid="import-selected-groups-dropdown"]');
|
||||||
const findImportButtons = () =>
|
const findRowImportDropdownAtIndex = (idx) =>
|
||||||
wrapper.findAll('button').wrappers.filter((w) => w.text() === 'Import');
|
wrapper.findAll('tbody td button').wrappers.filter((w) => w.text() === 'Import with projects')[
|
||||||
|
idx
|
||||||
|
];
|
||||||
const findPaginationDropdown = () => wrapper.find('[data-testid="page-size"]');
|
const findPaginationDropdown = () => wrapper.find('[data-testid="page-size"]');
|
||||||
const findTargetNamespaceDropdown = (rowWrapper) =>
|
const findTargetNamespaceDropdown = (rowWrapper) =>
|
||||||
rowWrapper.find('[data-testid="target-namespace-selector"]');
|
rowWrapper.find('[data-testid="target-namespace-selector"]');
|
||||||
|
|
@ -70,12 +70,7 @@ describe('import table', () => {
|
||||||
const findRowCheckbox = (idx) => wrapper.findAll('tbody td input[type=checkbox]').at(idx);
|
const findRowCheckbox = (idx) => wrapper.findAll('tbody td input[type=checkbox]').at(idx);
|
||||||
const selectRow = (idx) => findRowCheckbox(idx).setChecked(true);
|
const selectRow = (idx) => findRowCheckbox(idx).setChecked(true);
|
||||||
|
|
||||||
const createComponent = ({
|
const createComponent = ({ bulkImportSourceGroups, importGroups, defaultTargetNamespace }) => {
|
||||||
bulkImportSourceGroups,
|
|
||||||
importGroups,
|
|
||||||
defaultTargetNamespace,
|
|
||||||
glFeatures = {},
|
|
||||||
}) => {
|
|
||||||
apolloProvider = createMockApollo(
|
apolloProvider = createMockApollo(
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
|
|
@ -104,9 +99,6 @@ describe('import table', () => {
|
||||||
directives: {
|
directives: {
|
||||||
GlTooltip: createMockDirective('gl-tooltip'),
|
GlTooltip: createMockDirective('gl-tooltip'),
|
||||||
},
|
},
|
||||||
provide: {
|
|
||||||
glFeatures,
|
|
||||||
},
|
|
||||||
apolloProvider,
|
apolloProvider,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -130,7 +122,7 @@ describe('import table', () => {
|
||||||
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
|
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not renders loading icon when request is completed', async () => {
|
it('does not render loading icon when request is completed', async () => {
|
||||||
createComponent({
|
createComponent({
|
||||||
bulkImportSourceGroups: () => [],
|
bulkImportSourceGroups: () => [],
|
||||||
});
|
});
|
||||||
|
|
@ -241,12 +233,13 @@ describe('import table', () => {
|
||||||
|
|
||||||
await waitForPromises();
|
await waitForPromises();
|
||||||
|
|
||||||
await findImportButtons()[0].trigger('click');
|
await findRowImportDropdownAtIndex(0).trigger('click');
|
||||||
expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({
|
expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({
|
||||||
mutation: importGroupsMutation,
|
mutation: importGroupsMutation,
|
||||||
variables: {
|
variables: {
|
||||||
importRequests: [
|
importRequests: [
|
||||||
{
|
{
|
||||||
|
migrateProjects: true,
|
||||||
newName: FAKE_GROUP.lastImportTarget.newName,
|
newName: FAKE_GROUP.lastImportTarget.newName,
|
||||||
sourceGroupId: FAKE_GROUP.id,
|
sourceGroupId: FAKE_GROUP.id,
|
||||||
targetNamespace: AVAILABLE_NAMESPACES[0].fullPath,
|
targetNamespace: AVAILABLE_NAMESPACES[0].fullPath,
|
||||||
|
|
@ -269,7 +262,7 @@ describe('import table', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForPromises();
|
await waitForPromises();
|
||||||
await findImportButtons()[0].trigger('click');
|
await findRowImportDropdownAtIndex(0).trigger('click');
|
||||||
await waitForPromises();
|
await waitForPromises();
|
||||||
|
|
||||||
expect(createAlert).toHaveBeenCalledWith(
|
expect(createAlert).toHaveBeenCalledWith(
|
||||||
|
|
@ -294,7 +287,7 @@ describe('import table', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForPromises();
|
await waitForPromises();
|
||||||
await findImportButtons()[0].trigger('click');
|
await findRowImportDropdownAtIndex(0).trigger('click');
|
||||||
await waitForPromises();
|
await waitForPromises();
|
||||||
|
|
||||||
expect(createAlert).not.toHaveBeenCalled();
|
expect(createAlert).not.toHaveBeenCalled();
|
||||||
|
|
@ -472,7 +465,7 @@ describe('import table', () => {
|
||||||
});
|
});
|
||||||
await waitForPromises();
|
await waitForPromises();
|
||||||
|
|
||||||
expect(findImportSelectedButton().props().disabled).toBe(true);
|
expect(findImportSelectedDropdown().props().disabled).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('import selected button is enabled when groups were selected for import', async () => {
|
it('import selected button is enabled when groups were selected for import', async () => {
|
||||||
|
|
@ -487,7 +480,7 @@ describe('import table', () => {
|
||||||
|
|
||||||
await selectRow(0);
|
await selectRow(0);
|
||||||
|
|
||||||
expect(findImportSelectedButton().props().disabled).toBe(false);
|
expect(findImportSelectedDropdown().props().disabled).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not allow selecting already started groups', async () => {
|
it('does not allow selecting already started groups', async () => {
|
||||||
|
|
@ -505,7 +498,7 @@ describe('import table', () => {
|
||||||
await selectRow(0);
|
await selectRow(0);
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
expect(findImportSelectedButton().props().disabled).toBe(true);
|
expect(findImportSelectedDropdown().props().disabled).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not allow selecting groups with validation errors', async () => {
|
it('does not allow selecting groups with validation errors', async () => {
|
||||||
|
|
@ -530,10 +523,10 @@ describe('import table', () => {
|
||||||
await selectRow(0);
|
await selectRow(0);
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
expect(findImportSelectedButton().props().disabled).toBe(true);
|
expect(findImportSelectedDropdown().props().disabled).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('invokes importGroups mutation when import selected button is clicked', async () => {
|
it('invokes importGroups mutation when import selected dropdown is clicked', async () => {
|
||||||
const NEW_GROUPS = [
|
const NEW_GROUPS = [
|
||||||
generateFakeEntry({ id: 1, status: STATUSES.NONE }),
|
generateFakeEntry({ id: 1, status: STATUSES.NONE }),
|
||||||
generateFakeEntry({ id: 2, status: STATUSES.NONE }),
|
generateFakeEntry({ id: 2, status: STATUSES.NONE }),
|
||||||
|
|
@ -554,7 +547,7 @@ describe('import table', () => {
|
||||||
await selectRow(1);
|
await selectRow(1);
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
await findImportSelectedButton().trigger('click');
|
await findImportSelectedDropdown().find('button').trigger('click');
|
||||||
|
|
||||||
expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({
|
expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({
|
||||||
mutation: importGroupsMutation,
|
mutation: importGroupsMutation,
|
||||||
|
|
@ -675,7 +668,7 @@ describe('import table', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when import projects is enabled', () => {
|
describe('importing projects', () => {
|
||||||
const NEW_GROUPS = [
|
const NEW_GROUPS = [
|
||||||
generateFakeEntry({ id: 1, status: STATUSES.NONE }),
|
generateFakeEntry({ id: 1, status: STATUSES.NONE }),
|
||||||
generateFakeEntry({ id: 2, status: STATUSES.NONE }),
|
generateFakeEntry({ id: 2, status: STATUSES.NONE }),
|
||||||
|
|
@ -689,9 +682,6 @@ describe('import table', () => {
|
||||||
pageInfo: FAKE_PAGE_INFO,
|
pageInfo: FAKE_PAGE_INFO,
|
||||||
versionValidation: FAKE_VERSION_VALIDATION,
|
versionValidation: FAKE_VERSION_VALIDATION,
|
||||||
}),
|
}),
|
||||||
glFeatures: {
|
|
||||||
bulkImportProjects: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
jest.spyOn(apolloProvider.defaultClient, 'mutate');
|
jest.spyOn(apolloProvider.defaultClient, 'mutate');
|
||||||
return waitForPromises();
|
return waitForPromises();
|
||||||
|
|
|
||||||
|
|
@ -278,26 +278,30 @@ describe('tags list row', () => {
|
||||||
textSrOnly: true,
|
textSrOnly: true,
|
||||||
category: 'tertiary',
|
category: 'tertiary',
|
||||||
right: true,
|
right: true,
|
||||||
|
disabled: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each`
|
it('has the correct classes', () => {
|
||||||
canDelete | digest | disabled | buttonDisabled
|
mountComponent();
|
||||||
${true} | ${null} | ${true} | ${true}
|
|
||||||
${false} | ${'foo'} | ${true} | ${true}
|
|
||||||
${false} | ${null} | ${true} | ${true}
|
|
||||||
${true} | ${'foo'} | ${true} | ${true}
|
|
||||||
${true} | ${'foo'} | ${false} | ${false}
|
|
||||||
`(
|
|
||||||
'is $visible that is visible when canDelete is $canDelete and digest is $digest and disabled is $disabled',
|
|
||||||
({ canDelete, digest, disabled, buttonDisabled }) => {
|
|
||||||
mountComponent({ ...defaultProps, tag: { ...tag, canDelete, digest }, disabled });
|
|
||||||
|
|
||||||
expect(findAdditionalActionsMenu().props('disabled')).toBe(buttonDisabled);
|
expect(findAdditionalActionsMenu().classes('gl-opacity-0')).toBe(false);
|
||||||
expect(findAdditionalActionsMenu().classes('gl-opacity-0')).toBe(buttonDisabled);
|
expect(findAdditionalActionsMenu().classes('gl-pointer-events-none')).toBe(false);
|
||||||
expect(findAdditionalActionsMenu().classes('gl-pointer-events-none')).toBe(buttonDisabled);
|
});
|
||||||
},
|
|
||||||
);
|
it('is not rendered when tag.canDelete is false', () => {
|
||||||
|
mountComponent({ ...defaultProps, tag: { ...tag, canDelete: false } });
|
||||||
|
|
||||||
|
expect(findAdditionalActionsMenu().exists()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is hidden when disabled prop is set to true', () => {
|
||||||
|
mountComponent({ ...defaultProps, disabled: true });
|
||||||
|
|
||||||
|
expect(findAdditionalActionsMenu().props('disabled')).toBe(true);
|
||||||
|
expect(findAdditionalActionsMenu().classes('gl-opacity-0')).toBe(true);
|
||||||
|
expect(findAdditionalActionsMenu().classes('gl-pointer-events-none')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
describe('delete button', () => {
|
describe('delete button', () => {
|
||||||
it('exists and has the correct attrs', () => {
|
it('exists and has the correct attrs', () => {
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,10 @@ describe('Tags List', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('registry list', () => {
|
describe('registry list', () => {
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
mountComponent();
|
mountComponent();
|
||||||
fireFirstSortUpdate();
|
fireFirstSortUpdate();
|
||||||
return waitForApolloRequestRender();
|
await waitForApolloRequestRender();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a persisted search', () => {
|
it('has a persisted search', () => {
|
||||||
|
|
@ -94,6 +94,7 @@ describe('Tags List', () => {
|
||||||
pagination: tagsPageInfo,
|
pagination: tagsPageInfo,
|
||||||
items: tags,
|
items: tags,
|
||||||
idProperty: 'name',
|
idProperty: 'name',
|
||||||
|
hiddenDelete: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -182,12 +183,23 @@ describe('Tags List', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when the list of tags is empty', () => {
|
describe('when user does not have permission to delete list rows', () => {
|
||||||
beforeEach(() => {
|
it('sets registry list hiddenDelete prop to true', async () => {
|
||||||
resolver = jest.fn().mockResolvedValue(imageTagsMock([]));
|
resolver = jest.fn().mockResolvedValue(imageTagsMock({ canDelete: false }));
|
||||||
mountComponent();
|
mountComponent();
|
||||||
fireFirstSortUpdate();
|
fireFirstSortUpdate();
|
||||||
return waitForApolloRequestRender();
|
await waitForApolloRequestRender();
|
||||||
|
|
||||||
|
expect(findRegistryList().props('hiddenDelete')).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the list of tags is empty', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
resolver = jest.fn().mockResolvedValue(imageTagsMock({ nodes: [] }));
|
||||||
|
mountComponent();
|
||||||
|
fireFirstSortUpdate();
|
||||||
|
await waitForApolloRequestRender();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not show the loader', () => {
|
it('does not show the loader', () => {
|
||||||
|
|
|
||||||
|
|
@ -177,11 +177,12 @@ export const tagsMock = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const imageTagsMock = (nodes = tagsMock) => ({
|
export const imageTagsMock = ({ nodes = tagsMock, canDelete = true } = {}) => ({
|
||||||
data: {
|
data: {
|
||||||
containerRepository: {
|
containerRepository: {
|
||||||
id: containerRepositoryMock.id,
|
id: containerRepositoryMock.id,
|
||||||
tagsCount: nodes.length,
|
tagsCount: nodes.length,
|
||||||
|
canDelete,
|
||||||
tags: {
|
tags: {
|
||||||
nodes,
|
nodes,
|
||||||
pageInfo: { ...tagsPageInfo },
|
pageInfo: { ...tagsPageInfo },
|
||||||
|
|
|
||||||
|
|
@ -107,10 +107,21 @@ describe('Registry List', () => {
|
||||||
expect(findDeleteSelected().text()).toBe(component.i18n.deleteSelected);
|
expect(findDeleteSelected().text()).toBe(component.i18n.deleteSelected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('is hidden when hiddenDelete is true', () => {
|
describe('when hiddenDelete is true', () => {
|
||||||
mountComponent({ propsData: { ...defaultPropsData, hiddenDelete: true } });
|
beforeEach(() => {
|
||||||
|
mountComponent({ propsData: { ...defaultPropsData, hiddenDelete: true } });
|
||||||
|
});
|
||||||
|
|
||||||
expect(findDeleteSelected().exists()).toBe(false);
|
it('is hidden', () => {
|
||||||
|
expect(findDeleteSelected().exists()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('populates the first slot prop correctly', async () => {
|
||||||
|
expect(findScopedSlots().at(0).exists()).toBe(true);
|
||||||
|
|
||||||
|
// it's the first slot
|
||||||
|
expect(findScopedSlotFirstValue(0).text()).toBe('false');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('is disabled when isLoading is true', () => {
|
it('is disabled when isLoading is true', () => {
|
||||||
|
|
|
||||||
|
|
@ -829,6 +829,21 @@ RSpec.describe SearchHelper, feature_category: :global_search do
|
||||||
expect(header_search_context[:project_metadata]).to eq(project_metadata)
|
expect(header_search_context[:project_metadata]).to eq(project_metadata)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'feature issues is not available' do
|
||||||
|
let(:feature_available) { false }
|
||||||
|
let(:project_metadata) { { mr_path: project_merge_requests_path(project) } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(project).to receive(:feature_available?).and_call_original
|
||||||
|
allow(project).to receive(:feature_available?).with(:issues, current_user).and_return(feature_available)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds the :project and :project-metadata correctly to hash' do
|
||||||
|
expect(header_search_context[:project]).to eq({ id: project.id, name: project.name })
|
||||||
|
expect(header_search_context[:project_metadata]).to eq(project_metadata)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'with scope' do
|
context 'with scope' do
|
||||||
let(:scope) { 'issues' }
|
let(:scope) { 'issues' }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,18 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateRemediationsForVulnerabilityF
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with remediation with empty string as the diff key' do
|
||||||
|
let!(:finding) do
|
||||||
|
create_finding!(project1.id, scanner1.id, { remediations: [{ summary: 'summary', diff: '' }] })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create any remediation' do
|
||||||
|
expect(Gitlab::AppLogger).not_to receive(:error)
|
||||||
|
|
||||||
|
expect { perform_migration }.not_to change { vulnerability_remediations.count }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'with remediation equals to an array of duplicated elements' do
|
context 'with remediation equals to an array of duplicated elements' do
|
||||||
let!(:finding) do
|
let!(:finding) do
|
||||||
create_finding!(project1.id, scanner1.id, { remediations: [remediation_hash, remediation_hash] })
|
create_finding!(project1.id, scanner1.id, { remediations: [remediation_hash, remediation_hash] })
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,7 @@ RSpec.describe ScheduleMigrationForRemediation, :migration, feature_category: :v
|
||||||
it 'schedules a batched background migration' do
|
it 'schedules a batched background migration' do
|
||||||
migrate!
|
migrate!
|
||||||
|
|
||||||
expect(migration).to have_scheduled_batched_migration(
|
expect(migration).not_to have_scheduled_batched_migration
|
||||||
table_name: :vulnerability_occurrences,
|
|
||||||
column_name: :id,
|
|
||||||
interval: described_class::DELAY_INTERVAL,
|
|
||||||
batch_size: described_class::BATCH_SIZE,
|
|
||||||
sub_batch_size: described_class::SUB_BATCH_SIZE
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
require_migration!
|
||||||
|
|
||||||
|
RSpec.describe FinalizeCiBuildNeedsBigIntConversion, migration: :gitlab_ci, feature_category: :continuous_integration do
|
||||||
|
describe '#up' do
|
||||||
|
using RSpec::Parameterized::TableSyntax
|
||||||
|
|
||||||
|
where(:dot_com, :dev_or_test, :jh, :expectation) do
|
||||||
|
true | true | true | :not_to
|
||||||
|
true | false | true | :not_to
|
||||||
|
false | true | true | :not_to
|
||||||
|
false | false | true | :not_to
|
||||||
|
true | true | false | :to
|
||||||
|
true | false | false | :to
|
||||||
|
false | true | false | :to
|
||||||
|
false | false | false | :not_to
|
||||||
|
end
|
||||||
|
|
||||||
|
with_them do
|
||||||
|
it 'ensures the migration is completed for GitLab.com, dev, or test' do
|
||||||
|
allow(Gitlab).to receive(:com?).and_return(dot_com)
|
||||||
|
allow(Gitlab).to receive(:dev_or_test_env?).and_return(dev_or_test)
|
||||||
|
allow(Gitlab).to receive(:jh?).and_return(jh)
|
||||||
|
|
||||||
|
migration_arguments = {
|
||||||
|
job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
|
||||||
|
table_name: 'ci_build_needs',
|
||||||
|
column_name: 'id',
|
||||||
|
job_arguments: [['id'], ['id_convert_to_bigint']]
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(described_class).send(
|
||||||
|
expectation,
|
||||||
|
ensure_batched_background_migration_is_finished_for(migration_arguments)
|
||||||
|
)
|
||||||
|
|
||||||
|
migrate!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
require_migration!
|
||||||
|
|
||||||
|
RSpec.describe SwapColumnsCiBuildNeedsBigIntConversion, feature_category: :continuous_integration do
|
||||||
|
describe '#up' do
|
||||||
|
using RSpec::Parameterized::TableSyntax
|
||||||
|
|
||||||
|
where(:dot_com, :dev_or_test, :jh, :swap) do
|
||||||
|
true | true | true | false
|
||||||
|
true | false | true | false
|
||||||
|
false | true | true | false
|
||||||
|
false | false | true | false
|
||||||
|
true | true | false | true
|
||||||
|
true | false | false | true
|
||||||
|
false | true | false | true
|
||||||
|
false | false | false | false
|
||||||
|
end
|
||||||
|
|
||||||
|
with_them do
|
||||||
|
before do
|
||||||
|
connection = described_class.new.connection
|
||||||
|
connection.execute('ALTER TABLE ci_build_needs ALTER COLUMN id TYPE integer')
|
||||||
|
connection.execute('ALTER TABLE ci_build_needs ALTER COLUMN id_convert_to_bigint TYPE bigint')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'swaps the integer and bigint columns for GitLab.com, dev, or test' do
|
||||||
|
allow(Gitlab).to receive(:com?).and_return(dot_com)
|
||||||
|
allow(Gitlab).to receive(:dev_or_test_env?).and_return(dev_or_test)
|
||||||
|
allow(Gitlab).to receive(:jh?).and_return(jh)
|
||||||
|
|
||||||
|
ci_build_needs = table(:ci_build_needs)
|
||||||
|
|
||||||
|
disable_migrations_output do
|
||||||
|
reversible_migration do |migration|
|
||||||
|
migration.before -> {
|
||||||
|
ci_build_needs.reset_column_information
|
||||||
|
|
||||||
|
expect(ci_build_needs.columns.find { |c| c.name == 'id' }.sql_type).to eq('integer')
|
||||||
|
expect(ci_build_needs.columns.find { |c| c.name == 'id_convert_to_bigint' }.sql_type).to eq('bigint')
|
||||||
|
}
|
||||||
|
|
||||||
|
migration.after -> {
|
||||||
|
ci_build_needs.reset_column_information
|
||||||
|
|
||||||
|
if swap
|
||||||
|
expect(ci_build_needs.columns.find { |c| c.name == 'id' }.sql_type).to eq('bigint')
|
||||||
|
expect(ci_build_needs.columns.find { |c| c.name == 'id_convert_to_bigint' }.sql_type).to eq('integer')
|
||||||
|
else
|
||||||
|
expect(ci_build_needs.columns.find { |c| c.name == 'id' }.sql_type).to eq('integer')
|
||||||
|
expect(ci_build_needs.columns.find { |c| c.name == 'id_convert_to_bigint' }.sql_type).to eq('bigint')
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
require_migration!
|
||||||
|
|
||||||
|
RSpec.describe DeleteSecurityPolicyBotUsers, feature_category: :security_policy_management do
|
||||||
|
let(:users) { table(:users) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
users.create!(user_type: 10, projects_limit: 0, email: 'security_policy_bot@example.com')
|
||||||
|
users.create!(user_type: 1, projects_limit: 0, email: 'support_bot@example.com')
|
||||||
|
users.create!(projects_limit: 0, email: 'human@example.com')
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#up' do
|
||||||
|
it 'deletes security_policy_bot users' do
|
||||||
|
expect { migrate! }.to change { users.count }.by(-1)
|
||||||
|
|
||||||
|
expect(users.where(user_type: 10).count).to eq(0)
|
||||||
|
expect(users.where(user_type: 1).count).to eq(1)
|
||||||
|
expect(users.where(user_type: nil).count).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
require_migration!
|
||||||
|
|
||||||
|
RSpec.describe RescheduleMigrationForRemediation, :migration, feature_category: :vulnerability_management do
|
||||||
|
let(:migration) { described_class::MIGRATION }
|
||||||
|
|
||||||
|
describe '#up' do
|
||||||
|
it 'schedules a batched background migration' do
|
||||||
|
migrate!
|
||||||
|
|
||||||
|
expect(migration).to have_scheduled_batched_migration(
|
||||||
|
table_name: :vulnerability_occurrences,
|
||||||
|
column_name: :id,
|
||||||
|
interval: described_class::DELAY_INTERVAL,
|
||||||
|
batch_size: described_class::BATCH_SIZE,
|
||||||
|
sub_batch_size: described_class::SUB_BATCH_SIZE
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#down' do
|
||||||
|
it 'deletes all batched migration records' do
|
||||||
|
migrate!
|
||||||
|
schema_migrate_down!
|
||||||
|
|
||||||
|
expect(migration).not_to have_scheduled_batched_migration
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -6,7 +6,7 @@ RSpec.describe User, feature_category: :system_access do
|
||||||
specify 'types consistency checks', :aggregate_failures do
|
specify 'types consistency checks', :aggregate_failures do
|
||||||
expect(described_class::USER_TYPES.keys)
|
expect(described_class::USER_TYPES.keys)
|
||||||
.to match_array(%w[human ghost alert_bot project_bot support_bot service_user security_bot visual_review_bot
|
.to match_array(%w[human ghost alert_bot project_bot support_bot service_user security_bot visual_review_bot
|
||||||
migration_bot automation_bot admin_bot suggested_reviewers_bot service_account])
|
migration_bot automation_bot security_policy_bot admin_bot suggested_reviewers_bot service_account])
|
||||||
expect(described_class::USER_TYPES).to include(*described_class::BOT_USER_TYPES)
|
expect(described_class::USER_TYPES).to include(*described_class::BOT_USER_TYPES)
|
||||||
expect(described_class::USER_TYPES).to include(*described_class::NON_INTERNAL_USER_TYPES)
|
expect(described_class::USER_TYPES).to include(*described_class::NON_INTERNAL_USER_TYPES)
|
||||||
expect(described_class::USER_TYPES).to include(*described_class::INTERNAL_USER_TYPES)
|
expect(described_class::USER_TYPES).to include(*described_class::INTERNAL_USER_TYPES)
|
||||||
|
|
|
||||||
|
|
@ -157,27 +157,6 @@ RSpec.describe API::Files, feature_category: :source_code_management do
|
||||||
head api(route(file_path), current_user, **options), params: params
|
head api(route(file_path), current_user, **options), params: params
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when feature flag "cache_client_with_metrics" is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(cache_client_with_metrics: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'caches sha256 of the content', :use_clean_rails_redis_caching do
|
|
||||||
head api(route(file_path), current_user, **options), params: params
|
|
||||||
|
|
||||||
expect(Gitlab::Cache::Client).not_to receive(:build_with_metadata)
|
|
||||||
|
|
||||||
expect(Rails.cache.fetch("blob_content_sha256:#{project.full_path}:#{response.headers['X-Gitlab-Blob-Id']}"))
|
|
||||||
.to eq(content_sha256)
|
|
||||||
|
|
||||||
expect_next_instance_of(Gitlab::Git::Blob) do |instance|
|
|
||||||
expect(instance).not_to receive(:load_all_data!)
|
|
||||||
end
|
|
||||||
|
|
||||||
head api(route(file_path), current_user, **options), params: params
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns file by commit sha' do
|
it 'returns file by commit sha' do
|
||||||
# This file is deleted on HEAD
|
# This file is deleted on HEAD
|
||||||
file_path = 'files%2Fjs%2Fcommit%2Ejs%2Ecoffee'
|
file_path = 'files%2Fjs%2Fcommit%2Ejs%2Ecoffee'
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
let(:base_url) { "/groups/#{group.id}/issues" }
|
let(:base_url) { "/groups/#{group.id}/issues" }
|
||||||
|
|
||||||
shared_examples 'group issues statistics' do
|
shared_examples 'group issues statistics' do
|
||||||
it 'returns issues statistics' do
|
it 'returns issues statistics', :aggregate_failures do
|
||||||
get api("/groups/#{group.id}/issues_statistics", user), params: params
|
get api("/groups/#{group.id}/issues_statistics", user), params: params
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -346,7 +346,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
group_project.add_reporter(user)
|
group_project.add_reporter(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'exposes known attributes' do
|
it 'exposes known attributes', :aggregate_failures do
|
||||||
get api(base_url, admin)
|
get api(base_url, admin)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -355,7 +355,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns all group issues (including opened and closed)' do
|
it 'returns all group issues (including opened and closed)' do
|
||||||
get api(base_url, admin)
|
get api(base_url, admin, admin_mode: true)
|
||||||
|
|
||||||
expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id, group_issue.id])
|
expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id, group_issue.id])
|
||||||
end
|
end
|
||||||
|
|
@ -385,7 +385,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns group confidential issues for admin' do
|
it 'returns group confidential issues for admin' do
|
||||||
get api(base_url, admin), params: { state: :opened }
|
get api(base_url, admin, admin_mode: true), params: { state: :opened }
|
||||||
|
|
||||||
expect_paginated_array_response([group_confidential_issue.id, group_issue.id])
|
expect_paginated_array_response([group_confidential_issue.id, group_issue.id])
|
||||||
end
|
end
|
||||||
|
|
@ -403,7 +403,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'labels parameter' do
|
context 'labels parameter' do
|
||||||
it 'returns an array of labeled group issues' do
|
it 'returns an array of labeled group issues', :aggregate_failures do
|
||||||
get api(base_url, user), params: { labels: group_label.title }
|
get api(base_url, user), params: { labels: group_label.title }
|
||||||
|
|
||||||
expect_paginated_array_response(group_issue.id)
|
expect_paginated_array_response(group_issue.id)
|
||||||
|
|
@ -486,7 +486,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns an array of issues found by iids' do
|
it 'returns an array of issues found by iids', :aggregate_failures do
|
||||||
get api(base_url, user), params: { iids: [group_issue.iid] }
|
get api(base_url, user), params: { iids: [group_issue.iid] }
|
||||||
|
|
||||||
expect_paginated_array_response(group_issue.id)
|
expect_paginated_array_response(group_issue.id)
|
||||||
|
|
@ -505,14 +505,14 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect_paginated_array_response([])
|
expect_paginated_array_response([])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns an array of group issues with any label' do
|
it 'returns an array of group issues with any label', :aggregate_failures do
|
||||||
get api(base_url, user), params: { labels: IssuableFinder::Params::FILTER_ANY }
|
get api(base_url, user), params: { labels: IssuableFinder::Params::FILTER_ANY }
|
||||||
|
|
||||||
expect_paginated_array_response(group_issue.id)
|
expect_paginated_array_response(group_issue.id)
|
||||||
expect(json_response.first['id']).to eq(group_issue.id)
|
expect(json_response.first['id']).to eq(group_issue.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns an array of group issues with any label with labels param as array' do
|
it 'returns an array of group issues with any label with labels param as array', :aggregate_failures do
|
||||||
get api(base_url, user), params: { labels: [IssuableFinder::Params::FILTER_ANY] }
|
get api(base_url, user), params: { labels: [IssuableFinder::Params::FILTER_ANY] }
|
||||||
|
|
||||||
expect_paginated_array_response(group_issue.id)
|
expect_paginated_array_response(group_issue.id)
|
||||||
|
|
@ -555,7 +555,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect_paginated_array_response(group_closed_issue.id)
|
expect_paginated_array_response(group_closed_issue.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns an array of issues with no milestone' do
|
it 'returns an array of issues with no milestone', :aggregate_failures do
|
||||||
get api(base_url, user), params: { milestone: no_milestone_title }
|
get api(base_url, user), params: { milestone: no_milestone_title }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -688,28 +688,28 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
let!(:issue2) { create(:issue, author: user2, project: group_project, created_at: 2.days.ago) }
|
let!(:issue2) { create(:issue, author: user2, project: group_project, created_at: 2.days.ago) }
|
||||||
let!(:issue3) { create(:issue, author: user2, assignees: [assignee, another_assignee], project: group_project, created_at: 1.day.ago) }
|
let!(:issue3) { create(:issue, author: user2, assignees: [assignee, another_assignee], project: group_project, created_at: 1.day.ago) }
|
||||||
|
|
||||||
it 'returns issues with by assignee_username' do
|
it 'returns issues with by assignee_username', :aggregate_failures do
|
||||||
get api(base_url, user), params: { assignee_username: [assignee.username], scope: 'all' }
|
get api(base_url, user), params: { assignee_username: [assignee.username], scope: 'all' }
|
||||||
|
|
||||||
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
|
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
|
||||||
expect_paginated_array_response([issue3.id, group_confidential_issue.id])
|
expect_paginated_array_response([issue3.id, group_confidential_issue.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns issues by assignee_username as string' do
|
it 'returns issues by assignee_username as string', :aggregate_failures do
|
||||||
get api(base_url, user), params: { assignee_username: assignee.username, scope: 'all' }
|
get api(base_url, user), params: { assignee_username: assignee.username, scope: 'all' }
|
||||||
|
|
||||||
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
|
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
|
||||||
expect_paginated_array_response([issue3.id, group_confidential_issue.id])
|
expect_paginated_array_response([issue3.id, group_confidential_issue.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns error when multiple assignees are passed' do
|
it 'returns error when multiple assignees are passed', :aggregate_failures do
|
||||||
get api(base_url, user), params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
|
get api(base_url, user), params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
expect(json_response["error"]).to include("allows one value, but found 2")
|
expect(json_response["error"]).to include("allows one value, but found 2")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns error when assignee_username and assignee_id are passed together' do
|
it 'returns error when assignee_username and assignee_id are passed together', :aggregate_failures do
|
||||||
get api(base_url, user), params: { assignee_username: [assignee.username], assignee_id: another_assignee.id, scope: 'all' }
|
get api(base_url, user), params: { assignee_username: [assignee.username], assignee_id: another_assignee.id, scope: 'all' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
|
@ -719,7 +719,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#to_reference" do
|
describe "#to_reference" do
|
||||||
it 'exposes reference path in context of group' do
|
it 'exposes reference path in context of group', :aggregate_failures do
|
||||||
get api(base_url, user)
|
get api(base_url, user)
|
||||||
|
|
||||||
expect(json_response.first['references']['short']).to eq("##{group_closed_issue.iid}")
|
expect(json_response.first['references']['short']).to eq("##{group_closed_issue.iid}")
|
||||||
|
|
@ -735,7 +735,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
group_closed_issue.reload
|
group_closed_issue.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'exposes reference path in context of parent group' do
|
it 'exposes reference path in context of parent group', :aggregate_failures do
|
||||||
get api("/groups/#{parent_group.id}/issues")
|
get api("/groups/#{parent_group.id}/issues")
|
||||||
|
|
||||||
expect(json_response.first['references']['short']).to eq("##{group_closed_issue.iid}")
|
expect(json_response.first['references']['short']).to eq("##{group_closed_issue.iid}")
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples 'project issues statistics' do
|
shared_examples 'project issues statistics' do
|
||||||
it 'returns project issues statistics' do
|
it 'returns project issues statistics', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues_statistics", current_user), params: params
|
get api("/projects/#{project.id}/issues_statistics", current_user), params: params
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -317,7 +317,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns project confidential issues for admin' do
|
it 'returns project confidential issues for admin' do
|
||||||
get api("#{base_url}/issues", admin)
|
get api("#{base_url}/issues", admin, admin_mode: true)
|
||||||
|
|
||||||
expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue.id])
|
expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue.id])
|
||||||
end
|
end
|
||||||
|
|
@ -526,7 +526,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect_paginated_array_response([closed_issue.id, confidential_issue.id, issue.id])
|
expect_paginated_array_response([closed_issue.id, confidential_issue.id, issue.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'exposes known attributes' do
|
it 'exposes known attributes', :aggregate_failures do
|
||||||
get api("#{base_url}/issues", user)
|
get api("#{base_url}/issues", user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -607,28 +607,28 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
let!(:issue2) { create(:issue, author: user2, project: project, created_at: 2.days.ago) }
|
let!(:issue2) { create(:issue, author: user2, project: project, created_at: 2.days.ago) }
|
||||||
let!(:issue3) { create(:issue, author: user2, assignees: [assignee, another_assignee], project: project, created_at: 1.day.ago) }
|
let!(:issue3) { create(:issue, author: user2, assignees: [assignee, another_assignee], project: project, created_at: 1.day.ago) }
|
||||||
|
|
||||||
it 'returns issues by assignee_username' do
|
it 'returns issues by assignee_username', :aggregate_failures do
|
||||||
get api("/issues", user), params: { assignee_username: [assignee.username], scope: 'all' }
|
get api("/issues", user), params: { assignee_username: [assignee.username], scope: 'all' }
|
||||||
|
|
||||||
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
|
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
|
||||||
expect_paginated_array_response([confidential_issue.id, issue3.id])
|
expect_paginated_array_response([confidential_issue.id, issue3.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns issues by assignee_username as string' do
|
it 'returns issues by assignee_username as string', :aggregate_failures do
|
||||||
get api("/issues", user), params: { assignee_username: assignee.username, scope: 'all' }
|
get api("/issues", user), params: { assignee_username: assignee.username, scope: 'all' }
|
||||||
|
|
||||||
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
|
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
|
||||||
expect_paginated_array_response([confidential_issue.id, issue3.id])
|
expect_paginated_array_response([confidential_issue.id, issue3.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns error when multiple assignees are passed' do
|
it 'returns error when multiple assignees are passed', :aggregate_failures do
|
||||||
get api("/issues", user), params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
|
get api("/issues", user), params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
expect(json_response["error"]).to include("allows one value, but found 2")
|
expect(json_response["error"]).to include("allows one value, but found 2")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns error when assignee_username and assignee_id are passed together' do
|
it 'returns error when assignee_username and assignee_id are passed together', :aggregate_failures do
|
||||||
get api("/issues", user), params: { assignee_username: [assignee.username], assignee_id: another_assignee.id, scope: 'all' }
|
get api("/issues", user), params: { assignee_username: [assignee.username], assignee_id: another_assignee.id, scope: 'all' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
|
@ -646,7 +646,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'exposes known attributes' do
|
it 'exposes known attributes', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
|
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -686,7 +686,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'exposes the closed_at attribute' do
|
it 'exposes the closed_at attribute', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{closed_issue.iid}", user)
|
get api("/projects/#{project.id}/issues/#{closed_issue.iid}", user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -694,7 +694,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'links exposure' do
|
context 'links exposure' do
|
||||||
it 'exposes related resources full URIs' do
|
it 'exposes related resources full URIs', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
|
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
|
||||||
|
|
||||||
links = json_response['_links']
|
links = json_response['_links']
|
||||||
|
|
@ -706,7 +706,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a project issue by internal id' do
|
it 'returns a project issue by internal id', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
|
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -738,7 +738,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(response).to have_gitlab_http_status(:not_found)
|
expect(response).to have_gitlab_http_status(:not_found)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns confidential issue for project members' do
|
it 'returns confidential issue for project members', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", user)
|
get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -746,7 +746,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['iid']).to eq(confidential_issue.iid)
|
expect(json_response['iid']).to eq(confidential_issue.iid)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns confidential issue for author' do
|
it 'returns confidential issue for author', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", author)
|
get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", author)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -754,7 +754,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['iid']).to eq(confidential_issue.iid)
|
expect(json_response['iid']).to eq(confidential_issue.iid)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns confidential issue for assignee' do
|
it 'returns confidential issue for assignee', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", assignee)
|
get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", assignee)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -762,8 +762,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['iid']).to eq(confidential_issue.iid)
|
expect(json_response['iid']).to eq(confidential_issue.iid)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns confidential issue for admin' do
|
it 'returns confidential issue for admin', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", admin)
|
get api("/projects/#{project.id}/issues/#{confidential_issue.iid}", admin, admin_mode: true)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['title']).to eq(confidential_issue.title)
|
expect(json_response['title']).to eq(confidential_issue.title)
|
||||||
|
|
@ -829,7 +829,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
let!(:related_mr) { create_referencing_mr(user, project, issue) }
|
let!(:related_mr) { create_referencing_mr(user, project, issue) }
|
||||||
|
|
||||||
context 'when unauthenticated' do
|
context 'when unauthenticated' do
|
||||||
it 'return list of referenced merge requests from issue' do
|
it 'return list of referenced merge requests from issue', :aggregate_failures do
|
||||||
get_related_merge_requests(project.id, issue.iid)
|
get_related_merge_requests(project.id, issue.iid)
|
||||||
|
|
||||||
expect_paginated_array_response(related_mr.id)
|
expect_paginated_array_response(related_mr.id)
|
||||||
|
|
@ -898,8 +898,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'exposes known attributes' do
|
it 'exposes known attributes', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{issue.iid}/user_agent_detail", admin)
|
get api("/projects/#{project.id}/issues/#{issue.iid}/user_agent_detail", admin, admin_mode: true)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['user_agent']).to eq(user_agent_detail.user_agent)
|
expect(json_response['user_agent']).to eq(user_agent_detail.user_agent)
|
||||||
|
|
@ -936,7 +936,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a full list of participants' do
|
it 'returns a full list of participants', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{issue.iid}/participants", user)
|
get api("/projects/#{project.id}/issues/#{issue.iid}/participants", user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -945,7 +945,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user cannot see a confidential note' do
|
context 'when user cannot see a confidential note' do
|
||||||
it 'returns a limited list of participants' do
|
it 'returns a limited list of participants', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{issue.iid}/participants", create(:user))
|
get api("/projects/#{project.id}/issues/#{issue.iid}/participants", create(:user))
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples 'issues statistics' do
|
shared_examples 'issues statistics' do
|
||||||
it 'returns issues statistics' do
|
it 'returns issues statistics', :aggregate_failures do
|
||||||
get api("/issues_statistics", user), params: params
|
get api("/issues_statistics", user), params: params
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -109,8 +109,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
|
|
||||||
context 'as an admin' do
|
context 'as an admin' do
|
||||||
context 'when issue exists' do
|
context 'when issue exists' do
|
||||||
it 'returns the issue' do
|
it 'returns the issue', :aggregate_failures do
|
||||||
get api("/issues/#{issue.id}", admin)
|
get api("/issues/#{issue.id}", admin, admin_mode: true)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response.dig('author', 'id')).to eq(issue.author.id)
|
expect(json_response.dig('author', 'id')).to eq(issue.author.id)
|
||||||
|
|
@ -121,7 +121,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
|
|
||||||
context 'when issue does not exist' do
|
context 'when issue does not exist' do
|
||||||
it 'returns 404' do
|
it 'returns 404' do
|
||||||
get api("/issues/0", admin)
|
get api("/issues/0", admin, admin_mode: true)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:not_found)
|
expect(response).to have_gitlab_http_status(:not_found)
|
||||||
end
|
end
|
||||||
|
|
@ -132,7 +132,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
|
|
||||||
describe 'GET /issues' do
|
describe 'GET /issues' do
|
||||||
context 'when unauthenticated' do
|
context 'when unauthenticated' do
|
||||||
it 'returns an array of all issues' do
|
it 'returns an array of all issues', :aggregate_failures do
|
||||||
get api('/issues'), params: { scope: 'all' }
|
get api('/issues'), params: { scope: 'all' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -162,14 +162,14 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns an array of issues matching state in milestone' do
|
it 'returns an array of issues matching state in milestone', :aggregate_failures do
|
||||||
get api('/issues'), params: { milestone: 'foo', scope: 'all' }
|
get api('/issues'), params: { milestone: 'foo', scope: 'all' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect_paginated_array_response([])
|
expect_paginated_array_response([])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns an array of issues matching state in milestone' do
|
it 'returns an array of issues matching state in milestone', :aggregate_failures do
|
||||||
get api('/issues'), params: { milestone: milestone.title, scope: 'all' }
|
get api('/issues'), params: { milestone: milestone.title, scope: 'all' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -273,7 +273,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when authenticated' do
|
context 'when authenticated' do
|
||||||
it 'returns an array of issues' do
|
it 'returns an array of issues', :aggregate_failures do
|
||||||
get api('/issues', user)
|
get api('/issues', user)
|
||||||
|
|
||||||
expect_paginated_array_response([issue.id, closed_issue.id])
|
expect_paginated_array_response([issue.id, closed_issue.id])
|
||||||
|
|
@ -532,7 +532,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
context 'with incident issues' do
|
context 'with incident issues' do
|
||||||
let_it_be(:incident) { create(:incident, project: project) }
|
let_it_be(:incident) { create(:incident, project: project) }
|
||||||
|
|
||||||
it 'avoids N+1 queries' do
|
it 'avoids N+1 queries', :aggregate_failures do
|
||||||
get api('/issues', user) # warm up
|
get api('/issues', user) # warm up
|
||||||
|
|
||||||
control = ActiveRecord::QueryRecorder.new do
|
control = ActiveRecord::QueryRecorder.new do
|
||||||
|
|
@ -553,7 +553,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
context 'with issues closed as duplicates' do
|
context 'with issues closed as duplicates' do
|
||||||
let_it_be(:dup_issue_1) { create(:issue, :closed_as_duplicate, project: project) }
|
let_it_be(:dup_issue_1) { create(:issue, :closed_as_duplicate, project: project) }
|
||||||
|
|
||||||
it 'avoids N+1 queries' do
|
it 'avoids N+1 queries', :aggregate_failures do
|
||||||
get api('/issues', user) # warm up
|
get api('/issues', user) # warm up
|
||||||
|
|
||||||
control = ActiveRecord::QueryRecorder.new do
|
control = ActiveRecord::QueryRecorder.new do
|
||||||
|
|
@ -639,7 +639,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect_paginated_array_response([])
|
expect_paginated_array_response([])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns an array of labeled issues matching given state' do
|
it 'returns an array of labeled issues matching given state', :aggregate_failures do
|
||||||
get api('/issues', user), params: { labels: label.title, state: :opened }
|
get api('/issues', user), params: { labels: label.title, state: :opened }
|
||||||
|
|
||||||
expect_paginated_array_response(issue.id)
|
expect_paginated_array_response(issue.id)
|
||||||
|
|
@ -647,7 +647,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response.first['state']).to eq('opened')
|
expect(json_response.first['state']).to eq('opened')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns an array of labeled issues matching given state with labels param as array' do
|
it 'returns an array of labeled issues matching given state with labels param as array', :aggregate_failures do
|
||||||
get api('/issues', user), params: { labels: [label.title], state: :opened }
|
get api('/issues', user), params: { labels: [label.title], state: :opened }
|
||||||
|
|
||||||
expect_paginated_array_response(issue.id)
|
expect_paginated_array_response(issue.id)
|
||||||
|
|
@ -917,14 +917,14 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'matches V4 response schema' do
|
it 'matches V4 response schema', :aggregate_failures do
|
||||||
get api('/issues', user)
|
get api('/issues', user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(response).to match_response_schema('public_api/v4/issues')
|
expect(response).to match_response_schema('public_api/v4/issues')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a related merge request count of 0 if there are no related merge requests' do
|
it 'returns a related merge request count of 0 if there are no related merge requests', :aggregate_failures do
|
||||||
get api('/issues', user)
|
get api('/issues', user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -932,7 +932,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response.first).to include('merge_requests_count' => 0)
|
expect(json_response.first).to include('merge_requests_count' => 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a related merge request count > 0 if there are related merge requests' do
|
it 'returns a related merge request count > 0 if there are related merge requests', :aggregate_failures do
|
||||||
create(:merge_requests_closing_issues, issue: issue)
|
create(:merge_requests_closing_issues, issue: issue)
|
||||||
|
|
||||||
get api('/issues', user)
|
get api('/issues', user)
|
||||||
|
|
@ -1013,28 +1013,28 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
let!(:issue2) { create(:issue, author: user2, project: project, created_at: 2.days.ago) }
|
let!(:issue2) { create(:issue, author: user2, project: project, created_at: 2.days.ago) }
|
||||||
let!(:issue3) { create(:issue, author: user2, assignees: [assignee, another_assignee], project: project, created_at: 1.day.ago) }
|
let!(:issue3) { create(:issue, author: user2, assignees: [assignee, another_assignee], project: project, created_at: 1.day.ago) }
|
||||||
|
|
||||||
it 'returns issues with by assignee_username' do
|
it 'returns issues with by assignee_username', :aggregate_failures do
|
||||||
get api("/issues", user), params: { assignee_username: [assignee.username], scope: 'all' }
|
get api("/issues", user), params: { assignee_username: [assignee.username], scope: 'all' }
|
||||||
|
|
||||||
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
|
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
|
||||||
expect_paginated_array_response([confidential_issue.id, issue3.id])
|
expect_paginated_array_response([confidential_issue.id, issue3.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns issues by assignee_username as string' do
|
it 'returns issues by assignee_username as string', :aggregate_failures do
|
||||||
get api("/issues", user), params: { assignee_username: assignee.username, scope: 'all' }
|
get api("/issues", user), params: { assignee_username: assignee.username, scope: 'all' }
|
||||||
|
|
||||||
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
|
expect(issue3.reload.assignees.pluck(:id)).to match_array([assignee.id, another_assignee.id])
|
||||||
expect_paginated_array_response([confidential_issue.id, issue3.id])
|
expect_paginated_array_response([confidential_issue.id, issue3.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns error when multiple assignees are passed' do
|
it 'returns error when multiple assignees are passed', :aggregate_failures do
|
||||||
get api("/issues", user), params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
|
get api("/issues", user), params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
expect(json_response["error"]).to include("allows one value, but found 2")
|
expect(json_response["error"]).to include("allows one value, but found 2")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns error when assignee_username and assignee_id are passed together' do
|
it 'returns error when assignee_username and assignee_id are passed together', :aggregate_failures do
|
||||||
get api("/issues", user), params: { assignee_username: [assignee.username], assignee_id: another_assignee.id, scope: 'all' }
|
get api("/issues", user), params: { assignee_username: [assignee.username], assignee_id: another_assignee.id, scope: 'all' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
|
@ -1088,7 +1088,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET /projects/:id/issues/:issue_iid' do
|
describe 'GET /projects/:id/issues/:issue_iid' do
|
||||||
it 'exposes full reference path' do
|
it 'exposes full reference path', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
|
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -1106,7 +1106,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'user does not have permission to view new issue' do
|
context 'user does not have permission to view new issue' do
|
||||||
it 'does not return the issue as closed_as_duplicate_of' do
|
it 'does not return the issue as closed_as_duplicate_of', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{issue_closed_as_dup.iid}", user)
|
get api("/projects/#{project.id}/issues/#{issue_closed_as_dup.iid}", user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -1119,7 +1119,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
new_issue.project.add_guest(user)
|
new_issue.project.add_guest(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the issue as closed_as_duplicate_of' do
|
it 'returns the issue as closed_as_duplicate_of', :aggregate_failures do
|
||||||
get api("/projects/#{project.id}/issues/#{issue_closed_as_dup.iid}", user)
|
get api("/projects/#{project.id}/issues/#{issue_closed_as_dup.iid}", user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -1131,7 +1131,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /projects/:id/issues" do
|
describe "POST /projects/:id/issues" do
|
||||||
it 'creates a new project issue' do
|
it 'creates a new project issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user), params: { title: 'new issue' }
|
post api("/projects/#{project.id}/issues", user), params: { title: 'new issue' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:created)
|
expect(response).to have_gitlab_http_status(:created)
|
||||||
|
|
@ -1140,7 +1140,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when confidential is null' do
|
context 'when confidential is null' do
|
||||||
it 'responds with 400 error' do
|
it 'responds with 400 error', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user), params: { title: 'issue', confidential: nil }
|
post api("/projects/#{project.id}/issues", user), params: { title: 'issue', confidential: nil }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
|
@ -1155,7 +1155,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns and error message and status code from the service' do
|
it 'returns and error message and status code from the service', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user), params: { title: 'new issue' }
|
post api("/projects/#{project.id}/issues", user), params: { title: 'new issue' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:forbidden)
|
expect(response).to have_gitlab_http_status(:forbidden)
|
||||||
|
|
@ -1177,15 +1177,15 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
travel_to fixed_time
|
travel_to fixed_time
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows admins to set the timestamp' do
|
it 'allows admins to set the timestamp', :aggregate_failures do
|
||||||
put api("/projects/#{project.id}/issues/#{issue.iid}", admin), params: { labels: 'label1', updated_at: updated_at }
|
put api("/projects/#{project.id}/issues/#{issue.iid}", admin, admin_mode: true), params: { labels: 'label1', updated_at: updated_at }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(Time.parse(json_response['updated_at'])).to be_like_time(updated_at)
|
expect(Time.parse(json_response['updated_at'])).to be_like_time(updated_at)
|
||||||
expect(ResourceLabelEvent.last.created_at).to be_like_time(updated_at)
|
expect(ResourceLabelEvent.last.created_at).to be_like_time(updated_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not allow other users to set the timestamp' do
|
it 'does not allow other users to set the timestamp', :aggregate_failures do
|
||||||
reporter = create(:user)
|
reporter = create(:user)
|
||||||
project.add_developer(reporter)
|
project.add_developer(reporter)
|
||||||
|
|
||||||
|
|
@ -1268,7 +1268,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with valid params' do
|
context 'with valid params' do
|
||||||
it 'reorders issues and returns a successful 200 response' do
|
it 'reorders issues and returns a successful 200 response', :aggregate_failures do
|
||||||
put api("/projects/#{project.id}/issues/#{issue1.iid}/reorder", user), params: { move_after_id: issue2.id, move_before_id: issue3.id }
|
put api("/projects/#{project.id}/issues/#{issue1.iid}/reorder", user), params: { move_after_id: issue2.id, move_before_id: issue3.id }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -1295,7 +1295,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
let(:other_project) { create(:project, group: group) }
|
let(:other_project) { create(:project, group: group) }
|
||||||
let(:other_issue) { create(:issue, project: other_project, relative_position: 80) }
|
let(:other_issue) { create(:issue, project: other_project, relative_position: 80) }
|
||||||
|
|
||||||
it 'reorders issues and returns a successful 200 response' do
|
it 'reorders issues and returns a successful 200 response', :aggregate_failures do
|
||||||
put api("/projects/#{other_project.id}/issues/#{other_issue.iid}/reorder", user), params: { move_after_id: issue2.id, move_before_id: issue3.id }
|
put api("/projects/#{other_project.id}/issues/#{other_issue.iid}/reorder", user), params: { move_after_id: issue2.id, move_before_id: issue3.id }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
|
|
||||||
describe 'POST /projects/:id/issues' do
|
describe 'POST /projects/:id/issues' do
|
||||||
context 'support for deprecated assignee_id' do
|
context 'support for deprecated assignee_id' do
|
||||||
it 'creates a new project issue' do
|
it 'creates a new project issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: { title: 'new issue', assignee_id: user2.id }
|
params: { title: 'new issue', assignee_id: user2.id }
|
||||||
|
|
||||||
|
|
@ -85,7 +85,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['assignees'].first['name']).to eq(user2.name)
|
expect(json_response['assignees'].first['name']).to eq(user2.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a new project issue when assignee_id is empty' do
|
it 'creates a new project issue when assignee_id is empty', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: { title: 'new issue', assignee_id: '' }
|
params: { title: 'new issue', assignee_id: '' }
|
||||||
|
|
||||||
|
|
@ -96,7 +96,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'single assignee restrictions' do
|
context 'single assignee restrictions' do
|
||||||
it 'creates a new project issue with no more than one assignee' do
|
it 'creates a new project issue with no more than one assignee', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: { title: 'new issue', assignee_ids: [user2.id, guest.id] }
|
params: { title: 'new issue', assignee_ids: [user2.id, guest.id] }
|
||||||
|
|
||||||
|
|
@ -122,8 +122,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
|
|
||||||
context 'an internal ID is provided' do
|
context 'an internal ID is provided' do
|
||||||
context 'by an admin' do
|
context 'by an admin' do
|
||||||
it 'sets the internal ID on the new issue' do
|
it 'sets the internal ID on the new issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", admin),
|
post api("/projects/#{project.id}/issues", admin, admin_mode: true),
|
||||||
params: { title: 'new issue', iid: 9001 }
|
params: { title: 'new issue', iid: 9001 }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:created)
|
expect(response).to have_gitlab_http_status(:created)
|
||||||
|
|
@ -132,7 +132,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'by an owner' do
|
context 'by an owner' do
|
||||||
it 'sets the internal ID on the new issue' do
|
it 'sets the internal ID on the new issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: { title: 'new issue', iid: 9001 }
|
params: { title: 'new issue', iid: 9001 }
|
||||||
|
|
||||||
|
|
@ -145,7 +145,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
let(:group) { create(:group) }
|
let(:group) { create(:group) }
|
||||||
let(:group_project) { create(:project, :public, namespace: group) }
|
let(:group_project) { create(:project, :public, namespace: group) }
|
||||||
|
|
||||||
it 'sets the internal ID on the new issue' do
|
it 'sets the internal ID on the new issue', :aggregate_failures do
|
||||||
group.add_owner(user2)
|
group.add_owner(user2)
|
||||||
post api("/projects/#{group_project.id}/issues", user2),
|
post api("/projects/#{group_project.id}/issues", user2),
|
||||||
params: { title: 'new issue', iid: 9001 }
|
params: { title: 'new issue', iid: 9001 }
|
||||||
|
|
@ -156,7 +156,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'by another user' do
|
context 'by another user' do
|
||||||
it 'ignores the given internal ID' do
|
it 'ignores the given internal ID', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user2),
|
post api("/projects/#{project.id}/issues", user2),
|
||||||
params: { title: 'new issue', iid: 9001 }
|
params: { title: 'new issue', iid: 9001 }
|
||||||
|
|
||||||
|
|
@ -166,8 +166,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when an issue with the same IID exists on database' do
|
context 'when an issue with the same IID exists on database' do
|
||||||
it 'returns 409' do
|
it 'returns 409', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", admin),
|
post api("/projects/#{project.id}/issues", admin, admin_mode: true),
|
||||||
params: { title: 'new issue', iid: issue.iid }
|
params: { title: 'new issue', iid: issue.iid }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:conflict)
|
expect(response).to have_gitlab_http_status(:conflict)
|
||||||
|
|
@ -176,7 +176,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a new project issue' do
|
it 'creates a new project issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: { title: 'new issue', labels: 'label, label2', weight: 3, assignee_ids: [user2.id] }
|
params: { title: 'new issue', labels: 'label, label2', weight: 3, assignee_ids: [user2.id] }
|
||||||
|
|
||||||
|
|
@ -189,7 +189,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['assignees'].first['name']).to eq(user2.name)
|
expect(json_response['assignees'].first['name']).to eq(user2.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a new project issue with labels param as array' do
|
it 'creates a new project issue with labels param as array', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: { title: 'new issue', labels: %w(label label2), weight: 3, assignee_ids: [user2.id] }
|
params: { title: 'new issue', labels: %w(label label2), weight: 3, assignee_ids: [user2.id] }
|
||||||
|
|
||||||
|
|
@ -202,7 +202,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['assignees'].first['name']).to eq(user2.name)
|
expect(json_response['assignees'].first['name']).to eq(user2.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a new confidential project issue' do
|
it 'creates a new confidential project issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: { title: 'new issue', confidential: true }
|
params: { title: 'new issue', confidential: true }
|
||||||
|
|
||||||
|
|
@ -211,7 +211,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['confidential']).to be_truthy
|
expect(json_response['confidential']).to be_truthy
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a new confidential project issue with a different param' do
|
it 'creates a new confidential project issue with a different param', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: { title: 'new issue', confidential: 'y' }
|
params: { title: 'new issue', confidential: 'y' }
|
||||||
|
|
||||||
|
|
@ -220,7 +220,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['confidential']).to be_truthy
|
expect(json_response['confidential']).to be_truthy
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a public issue when confidential param is false' do
|
it 'creates a public issue when confidential param is false', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: { title: 'new issue', confidential: false }
|
params: { title: 'new issue', confidential: false }
|
||||||
|
|
||||||
|
|
@ -229,7 +229,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['confidential']).to be_falsy
|
expect(json_response['confidential']).to be_falsy
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a public issue when confidential param is invalid' do
|
it 'creates a public issue when confidential param is invalid', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: { title: 'new issue', confidential: 'foo' }
|
params: { title: 'new issue', confidential: 'foo' }
|
||||||
|
|
||||||
|
|
@ -242,7 +242,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows special label names' do
|
it 'allows special label names', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: {
|
params: {
|
||||||
title: 'new issue',
|
title: 'new issue',
|
||||||
|
|
@ -256,7 +256,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['labels']).to include '&'
|
expect(json_response['labels']).to include '&'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows special label names with labels param as array' do
|
it 'allows special label names with labels param as array', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: {
|
params: {
|
||||||
title: 'new issue',
|
title: 'new issue',
|
||||||
|
|
@ -270,7 +270,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['labels']).to include '&'
|
expect(json_response['labels']).to include '&'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns 400 if title is too long' do
|
it 'returns 400 if title is too long', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
params: { title: 'g' * 256 }
|
params: { title: 'g' * 256 }
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
|
@ -313,7 +313,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with due date' do
|
context 'with due date' do
|
||||||
it 'creates a new project issue' do
|
it 'creates a new project issue', :aggregate_failures do
|
||||||
due_date = 2.weeks.from_now.strftime('%Y-%m-%d')
|
due_date = 2.weeks.from_now.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
|
|
@ -336,8 +336,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'by an admin' do
|
context 'by an admin' do
|
||||||
it 'sets the creation time on the new issue' do
|
it 'sets the creation time on the new issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", admin), params: params
|
post api("/projects/#{project.id}/issues", admin, admin_mode: true), params: params
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:created)
|
expect(response).to have_gitlab_http_status(:created)
|
||||||
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
|
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
|
||||||
|
|
@ -346,7 +346,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'by a project owner' do
|
context 'by a project owner' do
|
||||||
it 'sets the creation time on the new issue' do
|
it 'sets the creation time on the new issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues", user), params: params
|
post api("/projects/#{project.id}/issues", user), params: params
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:created)
|
expect(response).to have_gitlab_http_status(:created)
|
||||||
|
|
@ -356,7 +356,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'by a group owner' do
|
context 'by a group owner' do
|
||||||
it 'sets the creation time on the new issue' do
|
it 'sets the creation time on the new issue', :aggregate_failures do
|
||||||
group = create(:group)
|
group = create(:group)
|
||||||
group_project = create(:project, :public, namespace: group)
|
group_project = create(:project, :public, namespace: group)
|
||||||
group.add_owner(user2)
|
group.add_owner(user2)
|
||||||
|
|
@ -370,7 +370,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'by another user' do
|
context 'by another user' do
|
||||||
it 'ignores the given creation time' do
|
it 'ignores the given creation time', :aggregate_failures do
|
||||||
project.add_developer(user2)
|
project.add_developer(user2)
|
||||||
|
|
||||||
post api("/projects/#{project.id}/issues", user2), params: params
|
post api("/projects/#{project.id}/issues", user2), params: params
|
||||||
|
|
@ -397,7 +397,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when request exceeds the rate limit' do
|
context 'when request exceeds the rate limit' do
|
||||||
it 'prevents users from creating more issues' do
|
it 'prevents users from creating more issues', :aggregate_failures do
|
||||||
allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
|
allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
|
||||||
|
|
||||||
post api("/projects/#{project.id}/issues", user),
|
post api("/projects/#{project.id}/issues", user),
|
||||||
|
|
@ -437,7 +437,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect { post_issue }.not_to change(Issue, :count)
|
expect { post_issue }.not_to change(Issue, :count)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns correct status and message' do
|
it 'returns correct status and message', :aggregate_failures do
|
||||||
post_issue
|
post_issue
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
|
@ -476,7 +476,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
let!(:target_project) { create(:project, creator_id: user.id, namespace: user.namespace) }
|
let!(:target_project) { create(:project, creator_id: user.id, namespace: user.namespace) }
|
||||||
let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace) }
|
let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace) }
|
||||||
|
|
||||||
it 'moves an issue' do
|
it 'moves an issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues/#{issue.iid}/move", user),
|
post api("/projects/#{project.id}/issues/#{issue.iid}/move", user),
|
||||||
params: { to_project_id: target_project.id }
|
params: { to_project_id: target_project.id }
|
||||||
|
|
||||||
|
|
@ -485,7 +485,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when source and target projects are the same' do
|
context 'when source and target projects are the same' do
|
||||||
it 'returns 400 when trying to move an issue' do
|
it 'returns 400 when trying to move an issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues/#{issue.iid}/move", user),
|
post api("/projects/#{project.id}/issues/#{issue.iid}/move", user),
|
||||||
params: { to_project_id: project.id }
|
params: { to_project_id: project.id }
|
||||||
|
|
||||||
|
|
@ -495,7 +495,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the user does not have the permission to move issues' do
|
context 'when the user does not have the permission to move issues' do
|
||||||
it 'returns 400 when trying to move an issue' do
|
it 'returns 400 when trying to move an issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues/#{issue.iid}/move", user),
|
post api("/projects/#{project.id}/issues/#{issue.iid}/move", user),
|
||||||
params: { to_project_id: target_project2.id }
|
params: { to_project_id: target_project2.id }
|
||||||
|
|
||||||
|
|
@ -504,8 +504,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'moves the issue to another namespace if I am admin' do
|
it 'moves the issue to another namespace if I am admin', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues/#{issue.iid}/move", admin),
|
post api("/projects/#{project.id}/issues/#{issue.iid}/move", admin, admin_mode: true),
|
||||||
params: { to_project_id: target_project2.id }
|
params: { to_project_id: target_project2.id }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:created)
|
expect(response).to have_gitlab_http_status(:created)
|
||||||
|
|
@ -513,7 +513,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when using the issue ID instead of iid' do
|
context 'when using the issue ID instead of iid' do
|
||||||
it 'returns 404 when trying to move an issue', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341520' do
|
it 'returns 404 when trying to move an issue', :aggregate_failures, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341520' do
|
||||||
post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
|
post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
|
||||||
params: { to_project_id: target_project.id }
|
params: { to_project_id: target_project.id }
|
||||||
|
|
||||||
|
|
@ -523,7 +523,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when issue does not exist' do
|
context 'when issue does not exist' do
|
||||||
it 'returns 404 when trying to move an issue' do
|
it 'returns 404 when trying to move an issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues/123/move", user),
|
post api("/projects/#{project.id}/issues/123/move", user),
|
||||||
params: { to_project_id: target_project.id }
|
params: { to_project_id: target_project.id }
|
||||||
|
|
||||||
|
|
@ -533,7 +533,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when source project does not exist' do
|
context 'when source project does not exist' do
|
||||||
it 'returns 404 when trying to move an issue' do
|
it 'returns 404 when trying to move an issue', :aggregate_failures do
|
||||||
post api("/projects/0/issues/#{issue.iid}/move", user),
|
post api("/projects/0/issues/#{issue.iid}/move", user),
|
||||||
params: { to_project_id: target_project.id }
|
params: { to_project_id: target_project.id }
|
||||||
|
|
||||||
|
|
@ -562,7 +562,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
|
|
||||||
context 'when user can admin the issue' do
|
context 'when user can admin the issue' do
|
||||||
context 'when the user can admin the target project' do
|
context 'when the user can admin the target project' do
|
||||||
it 'clones the issue' do
|
it 'clones the issue', :aggregate_failures do
|
||||||
expect do
|
expect do
|
||||||
post_clone_issue(user, issue, valid_target_project)
|
post_clone_issue(user, issue, valid_target_project)
|
||||||
end.to change { valid_target_project.issues.count }.by(1)
|
end.to change { valid_target_project.issues.count }.by(1)
|
||||||
|
|
@ -577,7 +577,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when target project is the same source project' do
|
context 'when target project is the same source project' do
|
||||||
it 'clones the issue' do
|
it 'clones the issue', :aggregate_failures do
|
||||||
expect do
|
expect do
|
||||||
post_clone_issue(user, issue, issue.project)
|
post_clone_issue(user, issue, issue.project)
|
||||||
end.to change { issue.reset.project.issues.count }.by(1)
|
end.to change { issue.reset.project.issues.count }.by(1)
|
||||||
|
|
@ -595,7 +595,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the user does not have the permission to clone issues' do
|
context 'when the user does not have the permission to clone issues' do
|
||||||
it 'returns 400' do
|
it 'returns 400', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues/#{issue.iid}/clone", user),
|
post api("/projects/#{project.id}/issues/#{issue.iid}/clone", user),
|
||||||
params: { to_project_id: invalid_target_project.id }
|
params: { to_project_id: invalid_target_project.id }
|
||||||
|
|
||||||
|
|
@ -605,7 +605,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when using the issue ID instead of iid' do
|
context 'when using the issue ID instead of iid' do
|
||||||
it 'returns 404', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341520' do
|
it 'returns 404', :aggregate_failures, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341520' do
|
||||||
post api("/projects/#{project.id}/issues/#{issue.id}/clone", user),
|
post api("/projects/#{project.id}/issues/#{issue.id}/clone", user),
|
||||||
params: { to_project_id: valid_target_project.id }
|
params: { to_project_id: valid_target_project.id }
|
||||||
|
|
||||||
|
|
@ -615,7 +615,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when issue does not exist' do
|
context 'when issue does not exist' do
|
||||||
it 'returns 404' do
|
it 'returns 404', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues/12300/clone", user),
|
post api("/projects/#{project.id}/issues/12300/clone", user),
|
||||||
params: { to_project_id: valid_target_project.id }
|
params: { to_project_id: valid_target_project.id }
|
||||||
|
|
||||||
|
|
@ -625,7 +625,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when source project does not exist' do
|
context 'when source project does not exist' do
|
||||||
it 'returns 404' do
|
it 'returns 404', :aggregate_failures do
|
||||||
post api("/projects/0/issues/#{issue.iid}/clone", user),
|
post api("/projects/0/issues/#{issue.iid}/clone", user),
|
||||||
params: { to_project_id: valid_target_project.id }
|
params: { to_project_id: valid_target_project.id }
|
||||||
|
|
||||||
|
|
@ -635,7 +635,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when target project does not exist' do
|
context 'when target project does not exist' do
|
||||||
it 'returns 404' do
|
it 'returns 404', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues/#{issue.iid}/clone", user),
|
post api("/projects/#{project.id}/issues/#{issue.iid}/clone", user),
|
||||||
params: { to_project_id: 0 }
|
params: { to_project_id: 0 }
|
||||||
|
|
||||||
|
|
@ -644,7 +644,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'clones the issue with notes when with_notes is true' do
|
it 'clones the issue with notes when with_notes is true', :aggregate_failures do
|
||||||
expect do
|
expect do
|
||||||
post api("/projects/#{project.id}/issues/#{issue.iid}/clone", user),
|
post api("/projects/#{project.id}/issues/#{issue.iid}/clone", user),
|
||||||
params: { to_project_id: valid_target_project.id, with_notes: true }
|
params: { to_project_id: valid_target_project.id, with_notes: true }
|
||||||
|
|
@ -661,7 +661,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'POST :id/issues/:issue_iid/subscribe' do
|
describe 'POST :id/issues/:issue_iid/subscribe' do
|
||||||
it 'subscribes to an issue' do
|
it 'subscribes to an issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues/#{issue.iid}/subscribe", user2)
|
post api("/projects/#{project.id}/issues/#{issue.iid}/subscribe", user2)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:created)
|
expect(response).to have_gitlab_http_status(:created)
|
||||||
|
|
@ -694,7 +694,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'POST :id/issues/:issue_id/unsubscribe' do
|
describe 'POST :id/issues/:issue_id/unsubscribe' do
|
||||||
it 'unsubscribes from an issue' do
|
it 'unsubscribes from an issue', :aggregate_failures do
|
||||||
post api("/projects/#{project.id}/issues/#{issue.iid}/unsubscribe", user)
|
post api("/projects/#{project.id}/issues/#{issue.iid}/unsubscribe", user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:created)
|
expect(response).to have_gitlab_http_status(:created)
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'PUT /projects/:id/issues/:issue_iid to update only title' do
|
describe 'PUT /projects/:id/issues/:issue_iid to update only title' do
|
||||||
it 'updates a project issue' do
|
it 'updates a project issue', :aggregate_failures do
|
||||||
put api_for_user, params: { title: updated_title }
|
put api_for_user, params: { title: updated_title }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -109,7 +109,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows special label names with labels param as array' do
|
it 'allows special label names with labels param as array', :aggregate_failures do
|
||||||
put api_for_user,
|
put api_for_user,
|
||||||
params: {
|
params: {
|
||||||
title: updated_title,
|
title: updated_title,
|
||||||
|
|
@ -135,42 +135,42 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(response).to have_gitlab_http_status(:forbidden)
|
expect(response).to have_gitlab_http_status(:forbidden)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates a confidential issue for project members' do
|
it 'updates a confidential issue for project members', :aggregate_failures do
|
||||||
put api(confidential_issue_path, user), params: { title: updated_title }
|
put api(confidential_issue_path, user), params: { title: updated_title }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['title']).to eq(updated_title)
|
expect(json_response['title']).to eq(updated_title)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates a confidential issue for author' do
|
it 'updates a confidential issue for author', :aggregate_failures do
|
||||||
put api(confidential_issue_path, author), params: { title: updated_title }
|
put api(confidential_issue_path, author), params: { title: updated_title }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['title']).to eq(updated_title)
|
expect(json_response['title']).to eq(updated_title)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates a confidential issue for admin' do
|
it 'updates a confidential issue for admin', :aggregate_failures do
|
||||||
put api(confidential_issue_path, admin), params: { title: updated_title }
|
put api(confidential_issue_path, admin, admin_mode: true), params: { title: updated_title }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['title']).to eq(updated_title)
|
expect(json_response['title']).to eq(updated_title)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets an issue to confidential' do
|
it 'sets an issue to confidential', :aggregate_failures do
|
||||||
put api_for_user, params: { confidential: true }
|
put api_for_user, params: { confidential: true }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['confidential']).to be_truthy
|
expect(json_response['confidential']).to be_truthy
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'makes a confidential issue public' do
|
it 'makes a confidential issue public', :aggregate_failures do
|
||||||
put api(confidential_issue_path, user), params: { confidential: false }
|
put api(confidential_issue_path, user), params: { confidential: false }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['confidential']).to be_falsy
|
expect(json_response['confidential']).to be_falsy
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not update a confidential issue with wrong confidential flag' do
|
it 'does not update a confidential issue with wrong confidential flag', :aggregate_failures do
|
||||||
put api(confidential_issue_path, user), params: { confidential: 'foo' }
|
put api(confidential_issue_path, user), params: { confidential: 'foo' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
|
@ -209,7 +209,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect { update_issue }.not_to change { issue.reload.title }
|
expect { update_issue }.not_to change { issue.reload.title }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns correct status and message' do
|
it 'returns correct status and message', :aggregate_failures do
|
||||||
update_issue
|
update_issue
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
|
@ -246,14 +246,14 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
|
|
||||||
describe 'PUT /projects/:id/issues/:issue_iid to update assignee' do
|
describe 'PUT /projects/:id/issues/:issue_iid to update assignee' do
|
||||||
context 'support for deprecated assignee_id' do
|
context 'support for deprecated assignee_id' do
|
||||||
it 'removes assignee' do
|
it 'removes assignee', :aggregate_failures do
|
||||||
put api_for_user, params: { assignee_id: 0 }
|
put api_for_user, params: { assignee_id: 0 }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['assignee']).to be_nil
|
expect(json_response['assignee']).to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates an issue with new assignee' do
|
it 'updates an issue with new assignee', :aggregate_failures do
|
||||||
put api_for_user, params: { assignee_id: user2.id }
|
put api_for_user, params: { assignee_id: user2.id }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -261,21 +261,21 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes assignee' do
|
it 'removes assignee', :aggregate_failures do
|
||||||
put api_for_user, params: { assignee_ids: [0] }
|
put api_for_user, params: { assignee_ids: [0] }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['assignees']).to be_empty
|
expect(json_response['assignees']).to be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates an issue with new assignee' do
|
it 'updates an issue with new assignee', :aggregate_failures do
|
||||||
put api_for_user, params: { assignee_ids: [user2.id] }
|
put api_for_user, params: { assignee_ids: [user2.id] }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['assignees'].first['name']).to eq(user2.name)
|
expect(json_response['assignees'].first['name']).to eq(user2.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'single assignee restrictions' do
|
context 'single assignee restrictions', :aggregate_failures do
|
||||||
it 'updates an issue with several assignees but only one has been applied' do
|
it 'updates an issue with several assignees but only one has been applied' do
|
||||||
put api_for_user, params: { assignee_ids: [user2.id, guest.id] }
|
put api_for_user, params: { assignee_ids: [user2.id, guest.id] }
|
||||||
|
|
||||||
|
|
@ -289,7 +289,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
let!(:label) { create(:label, title: 'dummy', project: project) }
|
let!(:label) { create(:label, title: 'dummy', project: project) }
|
||||||
let!(:label_link) { create(:label_link, label: label, target: issue) }
|
let!(:label_link) { create(:label_link, label: label, target: issue) }
|
||||||
|
|
||||||
it 'adds relevant labels' do
|
it 'adds relevant labels', :aggregate_failures do
|
||||||
put api_for_user, params: { add_labels: '1, 2' }
|
put api_for_user, params: { add_labels: '1, 2' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -300,14 +300,14 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
let!(:label2) { create(:label, title: 'a-label', project: project) }
|
let!(:label2) { create(:label, title: 'a-label', project: project) }
|
||||||
let!(:label_link2) { create(:label_link, label: label2, target: issue) }
|
let!(:label_link2) { create(:label_link, label: label2, target: issue) }
|
||||||
|
|
||||||
it 'removes relevant labels' do
|
it 'removes relevant labels', :aggregate_failures do
|
||||||
put api_for_user, params: { remove_labels: label2.title }
|
put api_for_user, params: { remove_labels: label2.title }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['labels']).to eq([label.title])
|
expect(json_response['labels']).to eq([label.title])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes all labels' do
|
it 'removes all labels', :aggregate_failures do
|
||||||
put api_for_user, params: { remove_labels: "#{label.title}, #{label2.title}" }
|
put api_for_user, params: { remove_labels: "#{label.title}, #{label2.title}" }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -315,14 +315,14 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not update labels if not present' do
|
it 'does not update labels if not present', :aggregate_failures do
|
||||||
put api_for_user, params: { title: updated_title }
|
put api_for_user, params: { title: updated_title }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['labels']).to eq([label.title])
|
expect(json_response['labels']).to eq([label.title])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes all labels and touches the record' do
|
it 'removes all labels and touches the record', :aggregate_failures do
|
||||||
travel_to(2.minutes.from_now) do
|
travel_to(2.minutes.from_now) do
|
||||||
put api_for_user, params: { labels: '' }
|
put api_for_user, params: { labels: '' }
|
||||||
end
|
end
|
||||||
|
|
@ -332,7 +332,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['updated_at']).to be > Time.current
|
expect(json_response['updated_at']).to be > Time.current
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes all labels and touches the record with labels param as array' do
|
it 'removes all labels and touches the record with labels param as array', :aggregate_failures do
|
||||||
travel_to(2.minutes.from_now) do
|
travel_to(2.minutes.from_now) do
|
||||||
put api_for_user, params: { labels: [''] }
|
put api_for_user, params: { labels: [''] }
|
||||||
end
|
end
|
||||||
|
|
@ -342,7 +342,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['updated_at']).to be > Time.current
|
expect(json_response['updated_at']).to be > Time.current
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates labels and touches the record' do
|
it 'updates labels and touches the record', :aggregate_failures do
|
||||||
travel_to(2.minutes.from_now) do
|
travel_to(2.minutes.from_now) do
|
||||||
put api_for_user, params: { labels: 'foo,bar' }
|
put api_for_user, params: { labels: 'foo,bar' }
|
||||||
end
|
end
|
||||||
|
|
@ -352,7 +352,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['updated_at']).to be > Time.current
|
expect(json_response['updated_at']).to be > Time.current
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates labels and touches the record with labels param as array' do
|
it 'updates labels and touches the record with labels param as array', :aggregate_failures do
|
||||||
travel_to(2.minutes.from_now) do
|
travel_to(2.minutes.from_now) do
|
||||||
put api_for_user, params: { labels: %w(foo bar) }
|
put api_for_user, params: { labels: %w(foo bar) }
|
||||||
end
|
end
|
||||||
|
|
@ -363,21 +363,21 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['updated_at']).to be > Time.current
|
expect(json_response['updated_at']).to be > Time.current
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows special label names' do
|
it 'allows special label names', :aggregate_failures do
|
||||||
put api_for_user, params: { labels: 'label:foo, label-bar,label_bar,label/bar,label?bar,label&bar,?,&' }
|
put api_for_user, params: { labels: 'label:foo, label-bar,label_bar,label/bar,label?bar,label&bar,?,&' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['labels']).to contain_exactly('label:foo', 'label-bar', 'label_bar', 'label/bar', 'label?bar', 'label&bar', '?', '&')
|
expect(json_response['labels']).to contain_exactly('label:foo', 'label-bar', 'label_bar', 'label/bar', 'label?bar', 'label&bar', '?', '&')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows special label names with labels param as array' do
|
it 'allows special label names with labels param as array', :aggregate_failures do
|
||||||
put api_for_user, params: { labels: ['label:foo', 'label-bar', 'label_bar', 'label/bar,label?bar,label&bar,?,&'] }
|
put api_for_user, params: { labels: ['label:foo', 'label-bar', 'label_bar', 'label/bar,label?bar,label&bar,?,&'] }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(json_response['labels']).to contain_exactly('label:foo', 'label-bar', 'label_bar', 'label/bar', 'label?bar', 'label&bar', '?', '&')
|
expect(json_response['labels']).to contain_exactly('label:foo', 'label-bar', 'label_bar', 'label/bar', 'label?bar', 'label&bar', '?', '&')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns 400 if title is too long' do
|
it 'returns 400 if title is too long', :aggregate_failures do
|
||||||
put api_for_user, params: { title: 'g' * 256 }
|
put api_for_user, params: { title: 'g' * 256 }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
|
@ -386,7 +386,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'PUT /projects/:id/issues/:issue_iid to update state and label' do
|
describe 'PUT /projects/:id/issues/:issue_iid to update state and label' do
|
||||||
it 'updates a project issue' do
|
it 'updates a project issue', :aggregate_failures do
|
||||||
put api_for_user, params: { labels: 'label2', state_event: 'close' }
|
put api_for_user, params: { labels: 'label2', state_event: 'close' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -394,7 +394,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(json_response['state']).to eq 'closed'
|
expect(json_response['state']).to eq 'closed'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'reopens a project isssue' do
|
it 'reopens a project isssue', :aggregate_failures do
|
||||||
put api(issue_path, user), params: { state_event: 'reopen' }
|
put api(issue_path, user), params: { state_event: 'reopen' }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -404,7 +404,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
|
|
||||||
describe 'PUT /projects/:id/issues/:issue_iid to update updated_at param' do
|
describe 'PUT /projects/:id/issues/:issue_iid to update updated_at param' do
|
||||||
context 'when reporter makes request' do
|
context 'when reporter makes request' do
|
||||||
it 'accepts the update date to be set' do
|
it 'accepts the update date to be set', :aggregate_failures do
|
||||||
update_time = 2.weeks.ago
|
update_time = 2.weeks.ago
|
||||||
|
|
||||||
put api_for_user, params: { title: 'some new title', updated_at: update_time }
|
put api_for_user, params: { title: 'some new title', updated_at: update_time }
|
||||||
|
|
@ -436,7 +436,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'accepts the update date to be set' do
|
it 'accepts the update date to be set', :aggregate_failures do
|
||||||
update_time = 2.weeks.ago
|
update_time = 2.weeks.ago
|
||||||
put api_for_owner, params: { title: 'some new title', updated_at: update_time }
|
put api_for_owner, params: { title: 'some new title', updated_at: update_time }
|
||||||
|
|
||||||
|
|
@ -448,7 +448,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'PUT /projects/:id/issues/:issue_iid to update due date' do
|
describe 'PUT /projects/:id/issues/:issue_iid to update due date' do
|
||||||
it 'creates a new project issue' do
|
it 'creates a new project issue', :aggregate_failures do
|
||||||
due_date = 2.weeks.from_now.strftime('%Y-%m-%d')
|
due_date = 2.weeks.from_now.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
put api_for_user, params: { due_date: due_date }
|
put api_for_user, params: { due_date: due_date }
|
||||||
|
|
|
||||||
|
|
@ -35,39 +35,39 @@ RSpec.describe "Internal Project Pages Access", feature_category: :pages do
|
||||||
|
|
||||||
describe "GET /projects/:id/pages_access" do
|
describe "GET /projects/:id/pages_access" do
|
||||||
context 'access depends on the level' do
|
context 'access depends on the level' do
|
||||||
where(:pages_access_level, :with_user, :expected_result) do
|
where(:pages_access_level, :with_user, :admin_mode, :expected_result) do
|
||||||
ProjectFeature::DISABLED | "admin" | 403
|
ProjectFeature::DISABLED | "admin" | true | 403
|
||||||
ProjectFeature::DISABLED | "owner" | 403
|
ProjectFeature::DISABLED | "owner" | false | 403
|
||||||
ProjectFeature::DISABLED | "master" | 403
|
ProjectFeature::DISABLED | "master" | false | 403
|
||||||
ProjectFeature::DISABLED | "developer" | 403
|
ProjectFeature::DISABLED | "developer" | false | 403
|
||||||
ProjectFeature::DISABLED | "reporter" | 403
|
ProjectFeature::DISABLED | "reporter" | false | 403
|
||||||
ProjectFeature::DISABLED | "guest" | 403
|
ProjectFeature::DISABLED | "guest" | false | 403
|
||||||
ProjectFeature::DISABLED | "user" | 403
|
ProjectFeature::DISABLED | "user" | false | 403
|
||||||
ProjectFeature::DISABLED | nil | 404
|
ProjectFeature::DISABLED | nil | false | 404
|
||||||
ProjectFeature::PUBLIC | "admin" | 200
|
ProjectFeature::PUBLIC | "admin" | false | 200
|
||||||
ProjectFeature::PUBLIC | "owner" | 200
|
ProjectFeature::PUBLIC | "owner" | false | 200
|
||||||
ProjectFeature::PUBLIC | "master" | 200
|
ProjectFeature::PUBLIC | "master" | false | 200
|
||||||
ProjectFeature::PUBLIC | "developer" | 200
|
ProjectFeature::PUBLIC | "developer" | false | 200
|
||||||
ProjectFeature::PUBLIC | "reporter" | 200
|
ProjectFeature::PUBLIC | "reporter" | false | 200
|
||||||
ProjectFeature::PUBLIC | "guest" | 200
|
ProjectFeature::PUBLIC | "guest" | false | 200
|
||||||
ProjectFeature::PUBLIC | "user" | 200
|
ProjectFeature::PUBLIC | "user" | false | 200
|
||||||
ProjectFeature::PUBLIC | nil | 404
|
ProjectFeature::PUBLIC | nil | false | 404
|
||||||
ProjectFeature::ENABLED | "admin" | 200
|
ProjectFeature::ENABLED | "admin" | false | 200
|
||||||
ProjectFeature::ENABLED | "owner" | 200
|
ProjectFeature::ENABLED | "owner" | false | 200
|
||||||
ProjectFeature::ENABLED | "master" | 200
|
ProjectFeature::ENABLED | "master" | false | 200
|
||||||
ProjectFeature::ENABLED | "developer" | 200
|
ProjectFeature::ENABLED | "developer" | false | 200
|
||||||
ProjectFeature::ENABLED | "reporter" | 200
|
ProjectFeature::ENABLED | "reporter" | false | 200
|
||||||
ProjectFeature::ENABLED | "guest" | 200
|
ProjectFeature::ENABLED | "guest" | false | 200
|
||||||
ProjectFeature::ENABLED | "user" | 200
|
ProjectFeature::ENABLED | "user" | false | 200
|
||||||
ProjectFeature::ENABLED | nil | 404
|
ProjectFeature::ENABLED | nil | false | 404
|
||||||
ProjectFeature::PRIVATE | "admin" | 200
|
ProjectFeature::PRIVATE | "admin" | true | 200
|
||||||
ProjectFeature::PRIVATE | "owner" | 200
|
ProjectFeature::PRIVATE | "owner" | false | 200
|
||||||
ProjectFeature::PRIVATE | "master" | 200
|
ProjectFeature::PRIVATE | "master" | false | 200
|
||||||
ProjectFeature::PRIVATE | "developer" | 200
|
ProjectFeature::PRIVATE | "developer" | false | 200
|
||||||
ProjectFeature::PRIVATE | "reporter" | 200
|
ProjectFeature::PRIVATE | "reporter" | false | 200
|
||||||
ProjectFeature::PRIVATE | "guest" | 200
|
ProjectFeature::PRIVATE | "guest" | false | 200
|
||||||
ProjectFeature::PRIVATE | "user" | 403
|
ProjectFeature::PRIVATE | "user" | false | 403
|
||||||
ProjectFeature::PRIVATE | nil | 404
|
ProjectFeature::PRIVATE | nil | false | 404
|
||||||
end
|
end
|
||||||
|
|
||||||
with_them do
|
with_them do
|
||||||
|
|
@ -77,7 +77,7 @@ RSpec.describe "Internal Project Pages Access", feature_category: :pages do
|
||||||
it "correct return value" do
|
it "correct return value" do
|
||||||
if !with_user.nil?
|
if !with_user.nil?
|
||||||
user = public_send(with_user)
|
user = public_send(with_user)
|
||||||
get api("/projects/#{project.id}/pages_access", user)
|
get api("/projects/#{project.id}/pages_access", user, admin_mode: admin_mode)
|
||||||
else
|
else
|
||||||
get api("/projects/#{project.id}/pages_access")
|
get api("/projects/#{project.id}/pages_access")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe API::Pages, feature_category: :pages do
|
RSpec.describe API::Pages, feature_category: :pages do
|
||||||
let_it_be(:project) { create(:project, path: 'my.project', pages_https_only: false) }
|
let_it_be_with_reload(:project) { create(:project, path: 'my.project', pages_https_only: false) }
|
||||||
let_it_be(:admin) { create(:admin) }
|
let_it_be(:admin) { create(:admin) }
|
||||||
let_it_be(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ RSpec.describe API::Pages, feature_category: :pages do
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like '404 response' do
|
it_behaves_like '404 response' do
|
||||||
let(:request) { delete api("/projects/#{project.id}/pages", admin) }
|
let(:request) { delete api("/projects/#{project.id}/pages", admin, admin_mode: true) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -30,13 +30,13 @@ RSpec.describe API::Pages, feature_category: :pages do
|
||||||
|
|
||||||
context 'when Pages are deployed' do
|
context 'when Pages are deployed' do
|
||||||
it 'returns 204' do
|
it 'returns 204' do
|
||||||
delete api("/projects/#{project.id}/pages", admin)
|
delete api("/projects/#{project.id}/pages", admin, admin_mode: true)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:no_content)
|
expect(response).to have_gitlab_http_status(:no_content)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes the pages' do
|
it 'removes the pages' do
|
||||||
delete api("/projects/#{project.id}/pages", admin)
|
delete api("/projects/#{project.id}/pages", admin, admin_mode: true)
|
||||||
|
|
||||||
expect(project.reload.pages_metadatum.deployed?).to be(false)
|
expect(project.reload.pages_metadatum.deployed?).to be(false)
|
||||||
end
|
end
|
||||||
|
|
@ -48,7 +48,7 @@ RSpec.describe API::Pages, feature_category: :pages do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns 204' do
|
it 'returns 204' do
|
||||||
delete api("/projects/#{project.id}/pages", admin)
|
delete api("/projects/#{project.id}/pages", admin, admin_mode: true)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:no_content)
|
expect(response).to have_gitlab_http_status(:no_content)
|
||||||
end
|
end
|
||||||
|
|
@ -58,7 +58,7 @@ RSpec.describe API::Pages, feature_category: :pages do
|
||||||
it 'returns 404' do
|
it 'returns 404' do
|
||||||
id = -1
|
id = -1
|
||||||
|
|
||||||
delete api("/projects/#{id}/pages", admin)
|
delete api("/projects/#{id}/pages", admin, admin_mode: true)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:not_found)
|
expect(response).to have_gitlab_http_status(:not_found)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -35,39 +35,39 @@ RSpec.describe "Private Project Pages Access", feature_category: :pages do
|
||||||
|
|
||||||
describe "GET /projects/:id/pages_access" do
|
describe "GET /projects/:id/pages_access" do
|
||||||
context 'access depends on the level' do
|
context 'access depends on the level' do
|
||||||
where(:pages_access_level, :with_user, :expected_result) do
|
where(:pages_access_level, :with_user, :admin_mode, :expected_result) do
|
||||||
ProjectFeature::DISABLED | "admin" | 403
|
ProjectFeature::DISABLED | "admin" | true | 403
|
||||||
ProjectFeature::DISABLED | "owner" | 403
|
ProjectFeature::DISABLED | "owner" | false | 403
|
||||||
ProjectFeature::DISABLED | "master" | 403
|
ProjectFeature::DISABLED | "master" | false | 403
|
||||||
ProjectFeature::DISABLED | "developer" | 403
|
ProjectFeature::DISABLED | "developer" | false | 403
|
||||||
ProjectFeature::DISABLED | "reporter" | 403
|
ProjectFeature::DISABLED | "reporter" | false | 403
|
||||||
ProjectFeature::DISABLED | "guest" | 403
|
ProjectFeature::DISABLED | "guest" | false | 403
|
||||||
ProjectFeature::DISABLED | "user" | 404
|
ProjectFeature::DISABLED | "user" | false | 404
|
||||||
ProjectFeature::DISABLED | nil | 404
|
ProjectFeature::DISABLED | nil | false | 404
|
||||||
ProjectFeature::PUBLIC | "admin" | 200
|
ProjectFeature::PUBLIC | "admin" | true | 200
|
||||||
ProjectFeature::PUBLIC | "owner" | 200
|
ProjectFeature::PUBLIC | "owner" | false | 200
|
||||||
ProjectFeature::PUBLIC | "master" | 200
|
ProjectFeature::PUBLIC | "master" | false | 200
|
||||||
ProjectFeature::PUBLIC | "developer" | 200
|
ProjectFeature::PUBLIC | "developer" | false | 200
|
||||||
ProjectFeature::PUBLIC | "reporter" | 200
|
ProjectFeature::PUBLIC | "reporter" | false | 200
|
||||||
ProjectFeature::PUBLIC | "guest" | 200
|
ProjectFeature::PUBLIC | "guest" | false | 200
|
||||||
ProjectFeature::PUBLIC | "user" | 404
|
ProjectFeature::PUBLIC | "user" | false | 404
|
||||||
ProjectFeature::PUBLIC | nil | 404
|
ProjectFeature::PUBLIC | nil | false | 404
|
||||||
ProjectFeature::ENABLED | "admin" | 200
|
ProjectFeature::ENABLED | "admin" | true | 200
|
||||||
ProjectFeature::ENABLED | "owner" | 200
|
ProjectFeature::ENABLED | "owner" | false | 200
|
||||||
ProjectFeature::ENABLED | "master" | 200
|
ProjectFeature::ENABLED | "master" | false | 200
|
||||||
ProjectFeature::ENABLED | "developer" | 200
|
ProjectFeature::ENABLED | "developer" | false | 200
|
||||||
ProjectFeature::ENABLED | "reporter" | 200
|
ProjectFeature::ENABLED | "reporter" | false | 200
|
||||||
ProjectFeature::ENABLED | "guest" | 200
|
ProjectFeature::ENABLED | "guest" | false | 200
|
||||||
ProjectFeature::ENABLED | "user" | 404
|
ProjectFeature::ENABLED | "user" | false | 404
|
||||||
ProjectFeature::ENABLED | nil | 404
|
ProjectFeature::ENABLED | nil | false | 404
|
||||||
ProjectFeature::PRIVATE | "admin" | 200
|
ProjectFeature::PRIVATE | "admin" | true | 200
|
||||||
ProjectFeature::PRIVATE | "owner" | 200
|
ProjectFeature::PRIVATE | "owner" | false | 200
|
||||||
ProjectFeature::PRIVATE | "master" | 200
|
ProjectFeature::PRIVATE | "master" | false | 200
|
||||||
ProjectFeature::PRIVATE | "developer" | 200
|
ProjectFeature::PRIVATE | "developer" | false | 200
|
||||||
ProjectFeature::PRIVATE | "reporter" | 200
|
ProjectFeature::PRIVATE | "reporter" | false | 200
|
||||||
ProjectFeature::PRIVATE | "guest" | 200
|
ProjectFeature::PRIVATE | "guest" | false | 200
|
||||||
ProjectFeature::PRIVATE | "user" | 404
|
ProjectFeature::PRIVATE | "user" | false | 404
|
||||||
ProjectFeature::PRIVATE | nil | 404
|
ProjectFeature::PRIVATE | nil | false | 404
|
||||||
end
|
end
|
||||||
|
|
||||||
with_them do
|
with_them do
|
||||||
|
|
@ -77,7 +77,7 @@ RSpec.describe "Private Project Pages Access", feature_category: :pages do
|
||||||
it "correct return value" do
|
it "correct return value" do
|
||||||
if !with_user.nil?
|
if !with_user.nil?
|
||||||
user = public_send(with_user)
|
user = public_send(with_user)
|
||||||
get api("/projects/#{project.id}/pages_access", user)
|
get api("/projects/#{project.id}/pages_access", user, admin_mode: admin_mode)
|
||||||
else
|
else
|
||||||
get api("/projects/#{project.id}/pages_access")
|
get api("/projects/#{project.id}/pages_access")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -35,39 +35,39 @@ RSpec.describe "Public Project Pages Access", feature_category: :pages do
|
||||||
|
|
||||||
describe "GET /projects/:id/pages_access" do
|
describe "GET /projects/:id/pages_access" do
|
||||||
context 'access depends on the level' do
|
context 'access depends on the level' do
|
||||||
where(:pages_access_level, :with_user, :expected_result) do
|
where(:pages_access_level, :with_user, :admin_mode, :expected_result) do
|
||||||
ProjectFeature::DISABLED | "admin" | 403
|
ProjectFeature::DISABLED | "admin" | false | 403
|
||||||
ProjectFeature::DISABLED | "owner" | 403
|
ProjectFeature::DISABLED | "owner" | false | 403
|
||||||
ProjectFeature::DISABLED | "master" | 403
|
ProjectFeature::DISABLED | "master" | false | 403
|
||||||
ProjectFeature::DISABLED | "developer" | 403
|
ProjectFeature::DISABLED | "developer" | false | 403
|
||||||
ProjectFeature::DISABLED | "reporter" | 403
|
ProjectFeature::DISABLED | "reporter" | false | 403
|
||||||
ProjectFeature::DISABLED | "guest" | 403
|
ProjectFeature::DISABLED | "guest" | false | 403
|
||||||
ProjectFeature::DISABLED | "user" | 403
|
ProjectFeature::DISABLED | "user" | false | 403
|
||||||
ProjectFeature::DISABLED | nil | 403
|
ProjectFeature::DISABLED | nil | false | 403
|
||||||
ProjectFeature::PUBLIC | "admin" | 200
|
ProjectFeature::PUBLIC | "admin" | false | 200
|
||||||
ProjectFeature::PUBLIC | "owner" | 200
|
ProjectFeature::PUBLIC | "owner" | false | 200
|
||||||
ProjectFeature::PUBLIC | "master" | 200
|
ProjectFeature::PUBLIC | "master" | false | 200
|
||||||
ProjectFeature::PUBLIC | "developer" | 200
|
ProjectFeature::PUBLIC | "developer" | false | 200
|
||||||
ProjectFeature::PUBLIC | "reporter" | 200
|
ProjectFeature::PUBLIC | "reporter" | false | 200
|
||||||
ProjectFeature::PUBLIC | "guest" | 200
|
ProjectFeature::PUBLIC | "guest" | false | 200
|
||||||
ProjectFeature::PUBLIC | "user" | 200
|
ProjectFeature::PUBLIC | "user" | false | 200
|
||||||
ProjectFeature::PUBLIC | nil | 200
|
ProjectFeature::PUBLIC | nil | false | 200
|
||||||
ProjectFeature::ENABLED | "admin" | 200
|
ProjectFeature::ENABLED | "admin" | false | 200
|
||||||
ProjectFeature::ENABLED | "owner" | 200
|
ProjectFeature::ENABLED | "owner" | false | 200
|
||||||
ProjectFeature::ENABLED | "master" | 200
|
ProjectFeature::ENABLED | "master" | false | 200
|
||||||
ProjectFeature::ENABLED | "developer" | 200
|
ProjectFeature::ENABLED | "developer" | false | 200
|
||||||
ProjectFeature::ENABLED | "reporter" | 200
|
ProjectFeature::ENABLED | "reporter" | false | 200
|
||||||
ProjectFeature::ENABLED | "guest" | 200
|
ProjectFeature::ENABLED | "guest" | false | 200
|
||||||
ProjectFeature::ENABLED | "user" | 200
|
ProjectFeature::ENABLED | "user" | false | 200
|
||||||
ProjectFeature::ENABLED | nil | 200
|
ProjectFeature::ENABLED | nil | false | 200
|
||||||
ProjectFeature::PRIVATE | "admin" | 200
|
ProjectFeature::PRIVATE | "admin" | true | 200
|
||||||
ProjectFeature::PRIVATE | "owner" | 200
|
ProjectFeature::PRIVATE | "owner" | false | 200
|
||||||
ProjectFeature::PRIVATE | "master" | 200
|
ProjectFeature::PRIVATE | "master" | false | 200
|
||||||
ProjectFeature::PRIVATE | "developer" | 200
|
ProjectFeature::PRIVATE | "developer" | false | 200
|
||||||
ProjectFeature::PRIVATE | "reporter" | 200
|
ProjectFeature::PRIVATE | "reporter" | false | 200
|
||||||
ProjectFeature::PRIVATE | "guest" | 200
|
ProjectFeature::PRIVATE | "guest" | false | 200
|
||||||
ProjectFeature::PRIVATE | "user" | 403
|
ProjectFeature::PRIVATE | "user" | false | 403
|
||||||
ProjectFeature::PRIVATE | nil | 403
|
ProjectFeature::PRIVATE | nil | false | 403
|
||||||
end
|
end
|
||||||
|
|
||||||
with_them do
|
with_them do
|
||||||
|
|
@ -77,7 +77,7 @@ RSpec.describe "Public Project Pages Access", feature_category: :pages do
|
||||||
it "correct return value" do
|
it "correct return value" do
|
||||||
if !with_user.nil?
|
if !with_user.nil?
|
||||||
user = public_send(with_user)
|
user = public_send(with_user)
|
||||||
get api("/projects/#{project.id}/pages_access", user)
|
get api("/projects/#{project.id}/pages_access", user, admin_mode: admin_mode)
|
||||||
else
|
else
|
||||||
get api("/projects/#{project.id}/pages_access")
|
get api("/projects/#{project.id}/pages_access")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -41,14 +41,14 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like '404 response' do
|
it_behaves_like '404 response' do
|
||||||
let(:request) { get api('/pages/domains', admin) }
|
let(:request) { get api('/pages/domains', admin, admin_mode: true) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when pages is enabled' do
|
context 'when pages is enabled' do
|
||||||
context 'when authenticated as an admin' do
|
context 'when authenticated as an admin' do
|
||||||
it 'returns paginated all pages domains' do
|
it 'returns paginated all pages domains', :aggregate_failures do
|
||||||
get api('/pages/domains', admin)
|
get api('/pages/domains', admin, admin_mode: true)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
expect(response).to match_response_schema('public_api/v4/pages_domain_basics')
|
expect(response).to match_response_schema('public_api/v4/pages_domain_basics')
|
||||||
|
|
@ -74,7 +74,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
|
|
||||||
describe 'GET /projects/:project_id/pages/domains' do
|
describe 'GET /projects/:project_id/pages/domains' do
|
||||||
shared_examples_for 'get pages domains' do
|
shared_examples_for 'get pages domains' do
|
||||||
it 'returns paginated pages domains' do
|
it 'returns paginated pages domains', :aggregate_failures do
|
||||||
get api(route, user)
|
get api(route, user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -145,7 +145,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
|
|
||||||
describe 'GET /projects/:project_id/pages/domains/:domain' do
|
describe 'GET /projects/:project_id/pages/domains/:domain' do
|
||||||
shared_examples_for 'get pages domain' do
|
shared_examples_for 'get pages domain' do
|
||||||
it 'returns pages domain' do
|
it 'returns pages domain', :aggregate_failures do
|
||||||
get api(route_domain, user)
|
get api(route_domain, user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -155,7 +155,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
expect(json_response['certificate']).to be_nil
|
expect(json_response['certificate']).to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns pages domain with project path' do
|
it 'returns pages domain with project path', :aggregate_failures do
|
||||||
get api(route_domain_path, user)
|
get api(route_domain_path, user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -165,7 +165,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
expect(json_response['certificate']).to be_nil
|
expect(json_response['certificate']).to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns pages domain with a certificate' do
|
it 'returns pages domain with a certificate', :aggregate_failures do
|
||||||
get api(route_secure_domain, user)
|
get api(route_secure_domain, user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -177,7 +177,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
expect(json_response['auto_ssl_enabled']).to be false
|
expect(json_response['auto_ssl_enabled']).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns pages domain with an expired certificate' do
|
it 'returns pages domain with an expired certificate', :aggregate_failures do
|
||||||
get api(route_expired_domain, user)
|
get api(route_expired_domain, user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -185,7 +185,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
expect(json_response['certificate']['expired']).to be true
|
expect(json_response['certificate']['expired']).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns pages domain with letsencrypt' do
|
it 'returns pages domain with letsencrypt', :aggregate_failures do
|
||||||
get api(route_letsencrypt_domain, user)
|
get api(route_letsencrypt_domain, user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
@ -258,7 +258,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
let(:params_secure) { pages_domain_secure_params.slice(:domain, :certificate, :key) }
|
let(:params_secure) { pages_domain_secure_params.slice(:domain, :certificate, :key) }
|
||||||
|
|
||||||
shared_examples_for 'post pages domains' do
|
shared_examples_for 'post pages domains' do
|
||||||
it 'creates a new pages domain' do
|
it 'creates a new pages domain', :aggregate_failures do
|
||||||
expect { post api(route, user), params: params }
|
expect { post api(route, user), params: params }
|
||||||
.to publish_event(PagesDomains::PagesDomainCreatedEvent)
|
.to publish_event(PagesDomains::PagesDomainCreatedEvent)
|
||||||
.with(
|
.with(
|
||||||
|
|
@ -279,7 +279,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
expect(pages_domain.auto_ssl_enabled).to be false
|
expect(pages_domain.auto_ssl_enabled).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a new secure pages domain' do
|
it 'creates a new secure pages domain', :aggregate_failures do
|
||||||
post api(route, user), params: params_secure
|
post api(route, user), params: params_secure
|
||||||
pages_domain = PagesDomain.find_by(domain: json_response['domain'])
|
pages_domain = PagesDomain.find_by(domain: json_response['domain'])
|
||||||
|
|
||||||
|
|
@ -291,7 +291,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
expect(pages_domain.auto_ssl_enabled).to be false
|
expect(pages_domain.auto_ssl_enabled).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates domain with letsencrypt enabled' do
|
it 'creates domain with letsencrypt enabled', :aggregate_failures do
|
||||||
post api(route, user), params: pages_domain_with_letsencrypt_params
|
post api(route, user), params: pages_domain_with_letsencrypt_params
|
||||||
pages_domain = PagesDomain.find_by(domain: json_response['domain'])
|
pages_domain = PagesDomain.find_by(domain: json_response['domain'])
|
||||||
|
|
||||||
|
|
@ -301,7 +301,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
expect(pages_domain.auto_ssl_enabled).to be true
|
expect(pages_domain.auto_ssl_enabled).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates domain with letsencrypt enabled and provided certificate' do
|
it 'creates domain with letsencrypt enabled and provided certificate', :aggregate_failures do
|
||||||
post api(route, user), params: params_secure.merge(auto_ssl_enabled: true)
|
post api(route, user), params: params_secure.merge(auto_ssl_enabled: true)
|
||||||
pages_domain = PagesDomain.find_by(domain: json_response['domain'])
|
pages_domain = PagesDomain.find_by(domain: json_response['domain'])
|
||||||
|
|
||||||
|
|
@ -376,7 +376,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
let(:params_secure_nokey) { pages_domain_secure_params.slice(:certificate) }
|
let(:params_secure_nokey) { pages_domain_secure_params.slice(:certificate) }
|
||||||
|
|
||||||
shared_examples_for 'put pages domain' do
|
shared_examples_for 'put pages domain' do
|
||||||
it 'updates pages domain removing certificate' do
|
it 'updates pages domain removing certificate', :aggregate_failures do
|
||||||
put api(route_secure_domain, user), params: { certificate: nil, key: nil }
|
put api(route_secure_domain, user), params: { certificate: nil, key: nil }
|
||||||
pages_domain_secure.reload
|
pages_domain_secure.reload
|
||||||
|
|
||||||
|
|
@ -399,7 +399,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates pages domain adding certificate' do
|
it 'updates pages domain adding certificate', :aggregate_failures do
|
||||||
put api(route_domain, user), params: params_secure
|
put api(route_domain, user), params: params_secure
|
||||||
pages_domain.reload
|
pages_domain.reload
|
||||||
|
|
||||||
|
|
@ -409,7 +409,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
expect(pages_domain.key).to eq(params_secure[:key])
|
expect(pages_domain.key).to eq(params_secure[:key])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates pages domain adding certificate with letsencrypt' do
|
it 'updates pages domain adding certificate with letsencrypt', :aggregate_failures do
|
||||||
put api(route_domain, user), params: params_secure.merge(auto_ssl_enabled: true)
|
put api(route_domain, user), params: params_secure.merge(auto_ssl_enabled: true)
|
||||||
pages_domain.reload
|
pages_domain.reload
|
||||||
|
|
||||||
|
|
@ -420,7 +420,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
expect(pages_domain.auto_ssl_enabled).to be true
|
expect(pages_domain.auto_ssl_enabled).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates pages domain enabling letsencrypt' do
|
it 'updates pages domain enabling letsencrypt', :aggregate_failures do
|
||||||
put api(route_domain, user), params: { auto_ssl_enabled: true }
|
put api(route_domain, user), params: { auto_ssl_enabled: true }
|
||||||
pages_domain.reload
|
pages_domain.reload
|
||||||
|
|
||||||
|
|
@ -429,7 +429,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
expect(pages_domain.auto_ssl_enabled).to be true
|
expect(pages_domain.auto_ssl_enabled).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates pages domain disabling letsencrypt while preserving the certificate' do
|
it 'updates pages domain disabling letsencrypt while preserving the certificate', :aggregate_failures do
|
||||||
put api(route_letsencrypt_domain, user), params: { auto_ssl_enabled: false }
|
put api(route_letsencrypt_domain, user), params: { auto_ssl_enabled: false }
|
||||||
pages_domain_with_letsencrypt.reload
|
pages_domain_with_letsencrypt.reload
|
||||||
|
|
||||||
|
|
@ -440,7 +440,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
expect(pages_domain_with_letsencrypt.certificate).to be
|
expect(pages_domain_with_letsencrypt.certificate).to be
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates pages domain with expired certificate' do
|
it 'updates pages domain with expired certificate', :aggregate_failures do
|
||||||
put api(route_expired_domain, user), params: params_secure
|
put api(route_expired_domain, user), params: params_secure
|
||||||
pages_domain_expired.reload
|
pages_domain_expired.reload
|
||||||
|
|
||||||
|
|
@ -450,7 +450,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
|
||||||
expect(pages_domain_expired.key).to eq(params_secure[:key])
|
expect(pages_domain_expired.key).to eq(params_secure[:key])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates pages domain with expired certificate not updating key' do
|
it 'updates pages domain with expired certificate not updating key', :aggregate_failures do
|
||||||
put api(route_secure_domain, user), params: params_secure_nokey
|
put api(route_secure_domain, user), params: params_secure_nokey
|
||||||
pages_domain_secure.reload
|
pages_domain_secure.reload
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -362,20 +362,10 @@ RSpec.configure do |config|
|
||||||
./spec/requests/api/deploy_tokens_spec.rb
|
./spec/requests/api/deploy_tokens_spec.rb
|
||||||
./spec/requests/api/freeze_periods_spec.rb
|
./spec/requests/api/freeze_periods_spec.rb
|
||||||
./spec/requests/api/groups_spec.rb
|
./spec/requests/api/groups_spec.rb
|
||||||
./spec/requests/api/issues/get_group_issues_spec.rb
|
|
||||||
./spec/requests/api/issues/get_project_issues_spec.rb
|
|
||||||
./spec/requests/api/issues/issues_spec.rb
|
|
||||||
./spec/requests/api/issues/post_projects_issues_spec.rb
|
|
||||||
./spec/requests/api/issues/put_projects_issues_spec.rb
|
|
||||||
./spec/requests/api/keys_spec.rb
|
./spec/requests/api/keys_spec.rb
|
||||||
./spec/requests/api/merge_requests_spec.rb
|
./spec/requests/api/merge_requests_spec.rb
|
||||||
./spec/requests/api/namespaces_spec.rb
|
./spec/requests/api/namespaces_spec.rb
|
||||||
./spec/requests/api/notes_spec.rb
|
./spec/requests/api/notes_spec.rb
|
||||||
./spec/requests/api/pages/internal_access_spec.rb
|
|
||||||
./spec/requests/api/pages/pages_spec.rb
|
|
||||||
./spec/requests/api/pages/private_access_spec.rb
|
|
||||||
./spec/requests/api/pages/public_access_spec.rb
|
|
||||||
./spec/requests/api/pages_domains_spec.rb
|
|
||||||
./spec/requests/api/personal_access_tokens/self_information_spec.rb
|
./spec/requests/api/personal_access_tokens/self_information_spec.rb
|
||||||
./spec/requests/api/personal_access_tokens_spec.rb
|
./spec/requests/api/personal_access_tokens_spec.rb
|
||||||
./spec/requests/api/project_export_spec.rb
|
./spec/requests/api/project_export_spec.rb
|
||||||
|
|
|
||||||
|
|
@ -1237,10 +1237,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.24.0.tgz#bc8265919aa04b06cd08be91637471bad195936d"
|
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.24.0.tgz#bc8265919aa04b06cd08be91637471bad195936d"
|
||||||
integrity sha512-R4s5qJUFUIbPflknpw1aI/PchiNq65vY7LVsJZnQkY+vi+AgmsETdut/AdferbGWmeWMU0q2wuVu9phE8lDUgA==
|
integrity sha512-R4s5qJUFUIbPflknpw1aI/PchiNq65vY7LVsJZnQkY+vi+AgmsETdut/AdferbGWmeWMU0q2wuVu9phE8lDUgA==
|
||||||
|
|
||||||
"@gitlab/ui@56.4.0":
|
"@gitlab/ui@56.4.1":
|
||||||
version "56.4.0"
|
version "56.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-56.4.0.tgz#35e2ee19fff9ff803f8737300d957b6a9f6ade1f"
|
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-56.4.1.tgz#bdf6284053b092608f6f0b70c6abeb36fd352aa2"
|
||||||
integrity sha512-L+0lf5jXrE34RruiibeDOb4wiziSsUhB+d3CHTQIjtm6qdqcwgYT03AoNTgc/E/YVLshEKywTJnnyakreecAlw==
|
integrity sha512-WlEryaV/pwaJkHa+ioCcyB9slE+2RoPs15cmS+OtA9XsmWKqZTI1S00uZG9GcnA9hDKZM+HP2Apy7ku+myzLDQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@popperjs/core" "^2.11.2"
|
"@popperjs/core" "^2.11.2"
|
||||||
bootstrap-vue "2.23.1"
|
bootstrap-vue "2.23.1"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue