Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
c41b66bd05
commit
261c96684a
34 changed files with 636 additions and 308 deletions
|
@ -16,9 +16,11 @@
|
|||
## Author's checklist (required)
|
||||
|
||||
- [ ] Follow the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide.html).
|
||||
- If you have `developer` access or higher (for example, GitLab team members or [Core Team](https://about.gitlab.com/community/core-team/) members)
|
||||
|
||||
- If you have **Developer** permissions or higher:
|
||||
- [ ] Ensure that the [product tier badge](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges) is added to doc's `h1`.
|
||||
- [ ] Apply the ~documentation label, plus:
|
||||
- The corresponding DevOps stage and group label, if applicable.
|
||||
- The corresponding DevOps stage and group labels, if applicable.
|
||||
- ~"development guidelines" when changing docs under `doc/development/*`, `CONTRIBUTING.md`, or `README.md`.
|
||||
- ~"development guidelines" and ~"Documentation guidelines" when changing docs under `development/documentation/*`.
|
||||
- ~"development guidelines" and ~"Description templates (.gitlab/\*)" when creating/updating issue and MR description templates.
|
||||
|
@ -30,10 +32,9 @@ When applicable:
|
|||
|
||||
- [ ] Update the [permissions table](https://docs.gitlab.com/ee/user/permissions.html).
|
||||
- [ ] Link docs to and from the higher-level index page, plus other related docs where helpful.
|
||||
- [ ] Add the [product tier badge](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges) accordingly.
|
||||
- [ ] Add [GitLab's version history note(s)](https://docs.gitlab.com/ee/development/documentation/styleguide.html#text-for-documentation-requiring-version-text).
|
||||
- [ ] Add the [product tier badge](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges).
|
||||
- [ ] Add/update the [feature flag section](https://docs.gitlab.com/ee/development/documentation/feature_flags.html).
|
||||
- [ ] If you're changing document headings, search `doc/*`, `app/views/*`, and `ee/app/views/*` for old headings replacing with the new ones to [avoid broken anchors](https://docs.gitlab.com/ee/development/documentation/styleguide.html#anchor-links).
|
||||
|
||||
## Review checklist
|
||||
|
||||
|
@ -46,8 +47,9 @@ All reviewers can help ensure accuracy, clarity, completeness, and adherence to
|
|||
**2. Technical Writer**
|
||||
|
||||
- [ ] Technical writer review. If not requested for this MR, must be scheduled post-merge. To request for this MR, assign the writer listed for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/product-categories/#devops-stages).
|
||||
- [ ] Ensure ~"Technical Writing", ~"documentation", and a `docs::` scoped label are added.
|
||||
- [ ] Add ~docs-only when the only files changed are under `doc/*`.
|
||||
- [ ] Ensure docs metadata are present and up-to-date.
|
||||
- [ ] Ensure ~"Technical Writing" and ~"documentation" are added.
|
||||
- [ ] Add the corresponding `docs::` scoped label.
|
||||
- [ ] Add ~"tw::doing" when starting work on the MR.
|
||||
- [ ] Add ~"tw::finished" if Technical Writing team work on the MR is complete but it remains open.
|
||||
|
||||
|
|
18
Gemfile.lock
18
Gemfile.lock
|
@ -516,7 +516,7 @@ GEM
|
|||
googleapis-common-protos-types (~> 1.0)
|
||||
gssapi (1.2.0)
|
||||
ffi (>= 1.0.1)
|
||||
guard (2.15.1)
|
||||
guard (2.16.2)
|
||||
formatador (>= 0.2.4)
|
||||
listen (>= 2.7, < 4.0)
|
||||
lumberjack (>= 1.0.12, < 2.0)
|
||||
|
@ -649,10 +649,9 @@ GEM
|
|||
xml-simple
|
||||
licensee (8.9.2)
|
||||
rugged (~> 0.24)
|
||||
listen (3.1.5)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
ruby_dep (~> 1.2)
|
||||
listen (3.2.1)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
locale (2.1.3)
|
||||
lockbox (0.3.3)
|
||||
lograge (0.11.2)
|
||||
|
@ -664,7 +663,7 @@ GEM
|
|||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
lru_redux (1.1.0)
|
||||
lumberjack (1.0.13)
|
||||
lumberjack (1.2.7)
|
||||
mail (2.7.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
marcel (0.3.3)
|
||||
|
@ -899,9 +898,9 @@ GEM
|
|||
rainbow (3.0.0)
|
||||
raindrops (0.19.1)
|
||||
rake (12.3.3)
|
||||
rb-fsevent (0.10.2)
|
||||
rb-inotify (0.9.10)
|
||||
ffi (>= 0.5.0, < 2)
|
||||
rb-fsevent (0.10.4)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rblineprof (0.3.6)
|
||||
debugger-ruby_core_source (~> 1.3)
|
||||
rbtrace (0.4.14)
|
||||
|
@ -1020,7 +1019,6 @@ GEM
|
|||
nokogiri (>= 1.5.10)
|
||||
ruby-statistics (2.1.2)
|
||||
ruby2_keywords (0.0.2)
|
||||
ruby_dep (1.5.0)
|
||||
ruby_parser (3.13.1)
|
||||
sexp_processor (~> 4.9)
|
||||
rubyntlm (0.6.2)
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
/* eslint-disable @gitlab/require-i18n-strings */
|
||||
import { __ } from '~/locale';
|
||||
|
||||
export const ANY_AUTHOR = 'Any';
|
||||
|
||||
export const NO_LABEL = 'No label';
|
||||
const DEFAULT_LABEL_NO_LABEL = { value: 'No label', text: __('No label') };
|
||||
export const DEFAULT_LABEL_NONE = { value: 'None', text: __('None') };
|
||||
export const DEFAULT_LABEL_ANY = { value: 'Any', text: __('Any') };
|
||||
|
||||
export const DEFAULT_LABELS = [DEFAULT_LABEL_NO_LABEL];
|
||||
|
||||
export const DEBOUNCE_DELAY = 200;
|
||||
|
||||
|
@ -11,13 +16,11 @@ export const SortDirection = {
|
|||
ascending: 'ascending',
|
||||
};
|
||||
|
||||
export const defaultMilestones = [
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
{ value: 'None', text: __('None') },
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
{ value: 'Any', text: __('Any') },
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
export const DEFAULT_MILESTONES = [
|
||||
DEFAULT_LABEL_NONE,
|
||||
DEFAULT_LABEL_ANY,
|
||||
{ value: 'Upcoming', text: __('Upcoming') },
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
{ value: 'Started', text: __('Started') },
|
||||
];
|
||||
|
||||
/* eslint-enable @gitlab/require-i18n-strings */
|
||||
|
|
|
@ -14,10 +14,9 @@ import { __ } from '~/locale';
|
|||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
|
||||
import { stripQuotes } from '../filtered_search_utils';
|
||||
import { NO_LABEL, DEBOUNCE_DELAY } from '../constants';
|
||||
import { DEFAULT_LABELS, DEBOUNCE_DELAY } from '../constants';
|
||||
|
||||
export default {
|
||||
noLabel: NO_LABEL,
|
||||
components: {
|
||||
GlToken,
|
||||
GlFilteredSearchToken,
|
||||
|
@ -38,6 +37,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
labels: this.config.initialLabels || [],
|
||||
defaultLabels: this.config.defaultLabels || DEFAULT_LABELS,
|
||||
loading: true,
|
||||
};
|
||||
},
|
||||
|
@ -105,9 +105,13 @@ export default {
|
|||
>
|
||||
</template>
|
||||
<template #suggestions>
|
||||
<gl-filtered-search-suggestion :value="$options.noLabel">{{
|
||||
__('No label')
|
||||
}}</gl-filtered-search-suggestion>
|
||||
<gl-filtered-search-suggestion
|
||||
v-for="label in defaultLabels"
|
||||
:key="label.value"
|
||||
:value="label.value"
|
||||
>
|
||||
{{ label.text }}
|
||||
</gl-filtered-search-suggestion>
|
||||
<gl-dropdown-divider />
|
||||
<gl-loading-icon v-if="loading" />
|
||||
<template v-else>
|
||||
|
|
|
@ -11,10 +11,9 @@ import createFlash from '~/flash';
|
|||
import { __ } from '~/locale';
|
||||
|
||||
import { stripQuotes } from '../filtered_search_utils';
|
||||
import { defaultMilestones, DEBOUNCE_DELAY } from '../constants';
|
||||
import { DEFAULT_MILESTONES, DEBOUNCE_DELAY } from '../constants';
|
||||
|
||||
export default {
|
||||
defaultMilestones,
|
||||
components: {
|
||||
GlFilteredSearchToken,
|
||||
GlFilteredSearchSuggestion,
|
||||
|
@ -34,6 +33,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
milestones: this.config.initialMilestones || [],
|
||||
defaultMilestones: this.config.defaultMilestones || DEFAULT_MILESTONES,
|
||||
loading: true,
|
||||
};
|
||||
},
|
||||
|
@ -89,11 +89,12 @@ export default {
|
|||
</template>
|
||||
<template #suggestions>
|
||||
<gl-filtered-search-suggestion
|
||||
v-for="milestone in $options.defaultMilestones"
|
||||
v-for="milestone in defaultMilestones"
|
||||
:key="milestone.value"
|
||||
:value="milestone.value"
|
||||
>{{ milestone.text }}</gl-filtered-search-suggestion
|
||||
>
|
||||
{{ milestone.text }}
|
||||
</gl-filtered-search-suggestion>
|
||||
<gl-dropdown-divider />
|
||||
<gl-loading-icon v-if="loading" />
|
||||
<template v-else>
|
||||
|
|
|
@ -16,14 +16,6 @@ module Mutations
|
|||
required: true,
|
||||
description: 'Title of the snippet'
|
||||
|
||||
argument :file_name, GraphQL::STRING_TYPE,
|
||||
required: false,
|
||||
description: 'File name of the snippet'
|
||||
|
||||
argument :content, GraphQL::STRING_TYPE,
|
||||
required: false,
|
||||
description: 'Content of the snippet'
|
||||
|
||||
argument :description, GraphQL::STRING_TYPE,
|
||||
required: false,
|
||||
description: 'Description of the snippet'
|
||||
|
|
|
@ -14,14 +14,6 @@ module Mutations
|
|||
required: false,
|
||||
description: 'Title of the snippet'
|
||||
|
||||
argument :file_name, GraphQL::STRING_TYPE,
|
||||
required: false,
|
||||
description: 'File name of the snippet'
|
||||
|
||||
argument :content, GraphQL::STRING_TYPE,
|
||||
required: false,
|
||||
description: 'Content of the snippet'
|
||||
|
||||
argument :description, GraphQL::STRING_TYPE,
|
||||
required: false,
|
||||
description: 'Description of the snippet'
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
module Ci
|
||||
class RetryBuildService < ::BaseService
|
||||
CLONE_ACCESSORS = %i[pipeline project ref tag options name
|
||||
allow_failure stage stage_id stage_idx trigger_request
|
||||
yaml_variables when environment coverage_regex
|
||||
description tag_list protected needs_attributes
|
||||
resource_group scheduling_type].freeze
|
||||
def self.clone_accessors
|
||||
%i[pipeline project ref tag options name
|
||||
allow_failure stage stage_id stage_idx trigger_request
|
||||
yaml_variables when environment coverage_regex
|
||||
description tag_list protected needs_attributes
|
||||
resource_group scheduling_type].freeze
|
||||
end
|
||||
|
||||
def execute(build)
|
||||
build.ensure_scheduling_type!
|
||||
|
@ -28,7 +30,7 @@ module Ci
|
|||
raise Gitlab::Access::AccessDeniedError
|
||||
end
|
||||
|
||||
attributes = CLONE_ACCESSORS.map do |attribute|
|
||||
attributes = self.class.clone_accessors.map do |attribute|
|
||||
[attribute, build.public_send(attribute)] # rubocop:disable GitlabSecurity/PublicSend
|
||||
end.to_h
|
||||
|
||||
|
@ -68,3 +70,5 @@ module Ci
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Ci::RetryBuildService.prepend_if_ee('EE::Ci::RetryBuildService')
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove file_name and content in snippet mutations
|
||||
merge_request: 40727
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Improve group search users scope performance
|
||||
merge_request: 38701
|
||||
author:
|
||||
type: performance
|
5
changelogs/unreleased/issue_34247.yml
Normal file
5
changelogs/unreleased/issue_34247.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove pipeline_id column from requirements_test_reports
|
||||
merge_request: 38924
|
||||
author:
|
||||
type: deprecated
|
5
changelogs/unreleased/usage_ping_kubernetes_agent.yml
Normal file
5
changelogs/unreleased/usage_ping_kubernetes_agent.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add kubernetes_agents usage metric
|
||||
merge_request: 40559
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemovePipelineIdFromTestReports < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
remove_column :requirements_management_test_reports, :pipeline_id
|
||||
end
|
||||
|
||||
def down
|
||||
add_column :requirements_management_test_reports, :pipeline_id, :integer
|
||||
|
||||
with_lock_retries do
|
||||
# rubocop:disable Migration/AddConcurrentForeignKey
|
||||
add_foreign_key :requirements_management_test_reports, :ci_pipelines, column: :pipeline_id, on_delete: :nullify
|
||||
# rubocop:enable Migration/AddConcurrentForeignKey
|
||||
end
|
||||
end
|
||||
end
|
1
db/schema_migrations/20200810191256
Normal file
1
db/schema_migrations/20200810191256
Normal file
|
@ -0,0 +1 @@
|
|||
66653e275889da8e695843f648af36c8a4e275b4d3215119eab4942db1b4b823
|
|
@ -15120,7 +15120,6 @@ CREATE TABLE public.requirements_management_test_reports (
|
|||
id bigint NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
requirement_id bigint NOT NULL,
|
||||
pipeline_id bigint,
|
||||
author_id bigint,
|
||||
state smallint NOT NULL,
|
||||
build_id bigint
|
||||
|
@ -20644,8 +20643,6 @@ CREATE INDEX index_requirements_management_test_reports_on_author_id ON public.r
|
|||
|
||||
CREATE INDEX index_requirements_management_test_reports_on_build_id ON public.requirements_management_test_reports USING btree (build_id);
|
||||
|
||||
CREATE INDEX index_requirements_management_test_reports_on_pipeline_id ON public.requirements_management_test_reports USING btree (pipeline_id);
|
||||
|
||||
CREATE INDEX index_requirements_management_test_reports_on_requirement_id ON public.requirements_management_test_reports USING btree (requirement_id);
|
||||
|
||||
CREATE INDEX index_requirements_on_author_id ON public.requirements USING btree (author_id);
|
||||
|
@ -22205,9 +22202,6 @@ ALTER TABLE ONLY public.service_desk_settings
|
|||
ALTER TABLE ONLY public.group_custom_attributes
|
||||
ADD CONSTRAINT fk_rails_246e0db83a FOREIGN KEY (group_id) REFERENCES public.namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY public.requirements_management_test_reports
|
||||
ADD CONSTRAINT fk_rails_24cecc1e68 FOREIGN KEY (pipeline_id) REFERENCES public.ci_pipelines(id) ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE ONLY public.cluster_agents
|
||||
ADD CONSTRAINT fk_rails_25e9fc2d5d FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
@ -2746,21 +2746,11 @@ input CreateSnippetInput {
|
|||
"""
|
||||
clientMutationId: String
|
||||
|
||||
"""
|
||||
Content of the snippet
|
||||
"""
|
||||
content: String
|
||||
|
||||
"""
|
||||
Description of the snippet
|
||||
"""
|
||||
description: String
|
||||
|
||||
"""
|
||||
File name of the snippet
|
||||
"""
|
||||
fileName: String
|
||||
|
||||
"""
|
||||
The project full path the snippet is associated with
|
||||
"""
|
||||
|
@ -2959,6 +2949,66 @@ type DastScannerProfileEdge {
|
|||
node: DastScannerProfile
|
||||
}
|
||||
|
||||
"""
|
||||
Identifier of DastScannerProfile
|
||||
"""
|
||||
scalar DastScannerProfileID
|
||||
|
||||
"""
|
||||
Autogenerated input type of DastScannerProfileUpdate
|
||||
"""
|
||||
input DastScannerProfileUpdateInput {
|
||||
"""
|
||||
A unique identifier for the client performing the mutation.
|
||||
"""
|
||||
clientMutationId: String
|
||||
|
||||
"""
|
||||
The project the scanner profile belongs to.
|
||||
"""
|
||||
fullPath: ID!
|
||||
|
||||
"""
|
||||
ID of the scanner profile to be updated.
|
||||
"""
|
||||
id: DastScannerProfileID!
|
||||
|
||||
"""
|
||||
The name of the scanner profile.
|
||||
"""
|
||||
profileName: String!
|
||||
|
||||
"""
|
||||
The maximum number of seconds allowed for the spider to traverse the site.
|
||||
"""
|
||||
spiderTimeout: Int!
|
||||
|
||||
"""
|
||||
The maximum number of seconds allowed for the site under test to respond to a request.
|
||||
"""
|
||||
targetTimeout: Int!
|
||||
}
|
||||
|
||||
"""
|
||||
Autogenerated return type of DastScannerProfileUpdate
|
||||
"""
|
||||
type DastScannerProfileUpdatePayload {
|
||||
"""
|
||||
A unique identifier for the client performing the mutation.
|
||||
"""
|
||||
clientMutationId: String
|
||||
|
||||
"""
|
||||
Errors encountered during execution of the mutation.
|
||||
"""
|
||||
errors: [String!]!
|
||||
|
||||
"""
|
||||
ID of the scanner profile.
|
||||
"""
|
||||
id: DastScannerProfileID
|
||||
}
|
||||
|
||||
"""
|
||||
Represents a DAST Site Profile.
|
||||
"""
|
||||
|
@ -9757,6 +9807,7 @@ type Mutation {
|
|||
createSnippet(input: CreateSnippetInput!): CreateSnippetPayload
|
||||
dastOnDemandScanCreate(input: DastOnDemandScanCreateInput!): DastOnDemandScanCreatePayload
|
||||
dastScannerProfileCreate(input: DastScannerProfileCreateInput!): DastScannerProfileCreatePayload
|
||||
dastScannerProfileUpdate(input: DastScannerProfileUpdateInput!): DastScannerProfileUpdatePayload
|
||||
dastSiteProfileCreate(input: DastSiteProfileCreateInput!): DastSiteProfileCreatePayload
|
||||
dastSiteProfileDelete(input: DastSiteProfileDeleteInput!): DastSiteProfileDeletePayload
|
||||
dastSiteProfileUpdate(input: DastSiteProfileUpdateInput!): DastSiteProfileUpdatePayload
|
||||
|
@ -16566,21 +16617,11 @@ input UpdateSnippetInput {
|
|||
"""
|
||||
clientMutationId: String
|
||||
|
||||
"""
|
||||
Content of the snippet
|
||||
"""
|
||||
content: String
|
||||
|
||||
"""
|
||||
Description of the snippet
|
||||
"""
|
||||
description: String
|
||||
|
||||
"""
|
||||
File name of the snippet
|
||||
"""
|
||||
fileName: String
|
||||
|
||||
"""
|
||||
The global id of the snippet to update
|
||||
"""
|
||||
|
|
|
@ -7390,26 +7390,6 @@
|
|||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "fileName",
|
||||
"description": "File name of the snippet",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "content",
|
||||
"description": "Content of the snippet",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"description": "Description of the snippet",
|
||||
|
@ -8016,6 +7996,174 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "SCALAR",
|
||||
"name": "DastScannerProfileID",
|
||||
"description": "Identifier of DastScannerProfile",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "DastScannerProfileUpdateInput",
|
||||
"description": "Autogenerated input type of DastScannerProfileUpdate",
|
||||
"fields": null,
|
||||
"inputFields": [
|
||||
{
|
||||
"name": "fullPath",
|
||||
"description": "The project the scanner profile belongs to.",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"description": "ID of the scanner profile to be updated.",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DastScannerProfileID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "profileName",
|
||||
"description": "The name of the scanner profile.",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "spiderTimeout",
|
||||
"description": "The maximum number of seconds allowed for the spider to traverse the site.",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "targetTimeout",
|
||||
"description": "The maximum number of seconds allowed for the site under test to respond to a request.",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "clientMutationId",
|
||||
"description": "A unique identifier for the client performing the mutation.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "DastScannerProfileUpdatePayload",
|
||||
"description": "Autogenerated return type of DastScannerProfileUpdate",
|
||||
"fields": [
|
||||
{
|
||||
"name": "clientMutationId",
|
||||
"description": "A unique identifier for the client performing the mutation.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "errors",
|
||||
"description": "Errors encountered during execution of the mutation.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"description": "ID of the scanner profile.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DastScannerProfileID",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "DastSiteProfile",
|
||||
|
@ -27986,6 +28134,33 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "dastScannerProfileUpdate",
|
||||
"description": null,
|
||||
"args": [
|
||||
{
|
||||
"name": "input",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "DastScannerProfileUpdateInput",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "DastScannerProfileUpdatePayload",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "dastSiteProfileCreate",
|
||||
"description": null,
|
||||
|
@ -48753,26 +48928,6 @@
|
|||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "fileName",
|
||||
"description": "File name of the snippet",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "content",
|
||||
"description": "Content of the snippet",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"description": "Description of the snippet",
|
||||
|
|
|
@ -502,6 +502,16 @@ Autogenerated return type of DastScannerProfileCreate
|
|||
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
|
||||
| `id` | ID | ID of the scanner profile. |
|
||||
|
||||
## DastScannerProfileUpdatePayload
|
||||
|
||||
Autogenerated return type of DastScannerProfileUpdate
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | ---- | ---------- |
|
||||
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
|
||||
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
|
||||
| `id` | DastScannerProfileID | ID of the scanner profile. |
|
||||
|
||||
## DastSiteProfile
|
||||
|
||||
Represents a DAST Site Profile.
|
||||
|
|
|
@ -15,7 +15,7 @@ Elasticsearch is enabled, you'll have the benefit of fast search response times
|
|||
and the advantage of the following special searches:
|
||||
|
||||
- [Advanced Search](../user/search/advanced_global_search.md)
|
||||
- [Advanced Syntax Search](../user/search/advanced_search_syntax.md)
|
||||
- [Advanced Search Syntax](../user/search/advanced_search_syntax.md)
|
||||
|
||||
## Version requirements
|
||||
|
||||
|
@ -746,6 +746,17 @@ Here are some common pitfalls and how to overcome them:
|
|||
|
||||
You can run `sudo gitlab-rake gitlab:elastic:projects_not_indexed` to display projects that aren't indexed.
|
||||
|
||||
- **No new data is added to the Elasticsearch index when I push code**
|
||||
|
||||
NOTE: **Note:**
|
||||
This was [fixed in GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35936) and the Rake task is not available for versions greater than that.
|
||||
|
||||
When performing the initial indexing of blobs, we lock all projects until the project finishes indexing. It could happen that an error during the process causes one or multiple projects to remain locked. In order to unlock them, run:
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake gitlab:elastic:clear_locked_projects
|
||||
```
|
||||
|
||||
- **"Can't specify parent if no parent field has been configured"**
|
||||
|
||||
If you enabled Elasticsearch before GitLab 8.12 and have not rebuilt indexes you will get
|
||||
|
|
|
@ -57,7 +57,7 @@ With GitLab Enterprise Edition, you can also:
|
|||
- [Multiple Issue Boards](project/issue_board.md#multiple-issue-boards).
|
||||
- Create formal relationships between issues with [Related Issues](project/issues/related_issues.md).
|
||||
- Use [Burndown Charts](project/milestones/burndown_charts.md) to track progress during a sprint or while working on a new version of their software.
|
||||
- Leverage [Elasticsearch](../integration/elasticsearch.md) with [Advanced Search](search/advanced_global_search.md) and [Advanced Syntax Search](search/advanced_search_syntax.md) for faster, more advanced code search across your entire GitLab instance.
|
||||
- Leverage [Elasticsearch](../integration/elasticsearch.md) with [Advanced Search](search/advanced_global_search.md) and [Advanced Search Syntax](search/advanced_search_syntax.md) for faster, more advanced code search across your entire GitLab instance.
|
||||
- [Authenticate users with Kerberos](../integration/kerberos.md).
|
||||
- [Mirror a repository](project/repository/repository_mirroring.md) from elsewhere on your local server.
|
||||
- [Export issues as CSV](project/issues/csv_export.md).
|
||||
|
|
|
@ -60,7 +60,7 @@ project you have access to.
|
|||
|
||||
![Advanced Search](img/advanced_global_search.png)
|
||||
|
||||
You can also use the [Advanced Syntax Search](advanced_search_syntax.md) which
|
||||
You can also use the [Advanced Search Syntax](advanced_search_syntax.md) which
|
||||
provides some useful queries.
|
||||
|
||||
NOTE: **Note:**
|
||||
|
|
|
@ -5,7 +5,7 @@ info: "To determine the technical writer assigned to the Stage/Group associated
|
|||
type: reference
|
||||
---
|
||||
|
||||
# Advanced Syntax Search **(STARTER)**
|
||||
# Advanced Search Syntax **(STARTER)**
|
||||
|
||||
> - Introduced in [GitLab Enterprise Starter](https://about.gitlab.com/pricing/) 9.2
|
||||
|
||||
|
@ -19,7 +19,7 @@ visit the [administrator documentation](../../integration/elasticsearch.md).
|
|||
|
||||
## Overview
|
||||
|
||||
The Advanced Syntax Search is a subset of the
|
||||
The Advanced Search Syntax is a subset of the
|
||||
[Advanced Search](advanced_global_search.md), which you can use if you
|
||||
want to have more specific search results.
|
||||
|
||||
|
@ -38,9 +38,9 @@ not so sure.
|
|||
In that case, using the advanced search syntax in your query will yield much
|
||||
better results.
|
||||
|
||||
## Using the Advanced Syntax Search
|
||||
## Using the Advanced Search Syntax
|
||||
|
||||
The Advanced Syntax Search supports fuzzy or exact search queries with prefixes,
|
||||
The Advanced Search Syntax supports fuzzy or exact search queries with prefixes,
|
||||
boolean operators, and much more.
|
||||
|
||||
Full details can be found in the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/5.3/query-dsl-simple-query-string-query.html#_simple_query_string_syntax), but
|
||||
|
@ -57,7 +57,7 @@ here's a quick guide:
|
|||
|
||||
### Syntax search filters
|
||||
|
||||
The Advanced Syntax Search also supports the use of filters. The available filters are:
|
||||
The Advanced Search Syntax also supports the use of filters. The available filters are:
|
||||
|
||||
- filename: Filters by filename. You can use the glob (`*`) operator for fuzzy matching.
|
||||
- path: Filters by path. You can use the glob (`*`) operator for fuzzy matching.
|
||||
|
|
|
@ -215,8 +215,8 @@ GitLab instance.
|
|||
|
||||
[Learn how to use the Advanced Search.](advanced_global_search.md)
|
||||
|
||||
## Advanced Syntax Search **(STARTER)**
|
||||
## Advanced Search Syntax **(STARTER)**
|
||||
|
||||
Use advanced queries for more targeted search results.
|
||||
|
||||
[Learn how to use the Advanced Syntax Search.](advanced_search_syntax.md)
|
||||
[Learn how to use the Advanced Search Syntax.](advanced_search_syntax.md)
|
||||
|
|
|
@ -12,20 +12,24 @@ module Gitlab
|
|||
|
||||
# rubocop:disable CodeReuse/ActiveRecord
|
||||
def users
|
||||
# 1: get all groups the current user has access to
|
||||
groups = GroupsFinder.new(current_user).execute.joins(:users)
|
||||
# get all groups the current user has access to
|
||||
# ignore order inherited from GroupsFinder to improve performance
|
||||
current_user_groups = GroupsFinder.new(current_user).execute.unscope(:order)
|
||||
|
||||
# 2: Get the group's whole hierarchy
|
||||
group_users = @group.direct_and_indirect_users
|
||||
# the hierarchy of the current group
|
||||
group_groups = @group.self_and_hierarchy.unscope(:order)
|
||||
|
||||
# 3: get all users the current user has access to (->
|
||||
# `SearchResults#users`), which also applies the query.
|
||||
# the groups where the above hierarchies intersect
|
||||
intersect_groups = group_groups.where(id: current_user_groups)
|
||||
|
||||
# members of @group hierarchy where the user has access to the groups
|
||||
members = GroupMember.where(group: intersect_groups).non_invite
|
||||
|
||||
# get all users the current user has access to (-> `SearchResults#users`), which also applies the query
|
||||
users = super
|
||||
|
||||
# 4: filter for users that belong to the previously selected groups
|
||||
users
|
||||
.where(id: group_users.select('id'))
|
||||
.where(id: groups.select('members.user_id'))
|
||||
# filter users that belong to the previously selected groups
|
||||
users.where(id: members.select(:user_id))
|
||||
end
|
||||
# rubocop:enable CodeReuse/ActiveRecord
|
||||
|
||||
|
|
|
@ -111,6 +111,7 @@ module Gitlab
|
|||
clusters_applications_jupyter: count(::Clusters::Applications::Jupyter.available),
|
||||
clusters_applications_cilium: count(::Clusters::Applications::Cilium.available),
|
||||
clusters_management_project: count(::Clusters::Cluster.with_management_project),
|
||||
kubernetes_agents: count(::Clusters::Agent),
|
||||
in_review_folder: count(::Environment.in_review_folder),
|
||||
grafana_integrated_projects: count(GrafanaIntegration.enabled),
|
||||
groups: count(Group),
|
||||
|
|
|
@ -28080,6 +28080,9 @@ msgstr ""
|
|||
msgid "You are not authorized to perform this action"
|
||||
msgstr ""
|
||||
|
||||
msgid "You are not authorized to update this scanner profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "You are now impersonating %{username}"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -65,6 +65,9 @@ FactoryBot.define do
|
|||
create(:alert_management_alert, issue: alert_bot_issues[0], project: projects[0])
|
||||
create(:self_managed_prometheus_alert_event, related_issues: [issues[1]], project: projects[0])
|
||||
|
||||
# Kubernetes agents
|
||||
create(:cluster_agent, project: projects[0])
|
||||
|
||||
# Enabled clusters
|
||||
gcp_cluster = create(:cluster_provider_gcp, :created).cluster
|
||||
create(:cluster_provider_aws, :created)
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import { GlFilteredSearchToken, GlFilteredSearchTokenSegment } from '@gitlab/ui';
|
||||
import {
|
||||
GlFilteredSearchToken,
|
||||
GlFilteredSearchSuggestion,
|
||||
GlFilteredSearchTokenSegment,
|
||||
} from '@gitlab/ui';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import {
|
||||
|
@ -9,13 +13,32 @@ import {
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import {
|
||||
DEFAULT_LABELS,
|
||||
DEFAULT_LABEL_NONE,
|
||||
DEFAULT_LABEL_ANY,
|
||||
} from '~/vue_shared/components/filtered_search_bar/constants';
|
||||
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
|
||||
|
||||
import { mockLabelToken } from '../mock_data';
|
||||
|
||||
jest.mock('~/flash');
|
||||
const defaultStubs = {
|
||||
Portal: true,
|
||||
GlFilteredSearchSuggestionList: {
|
||||
template: '<div></div>',
|
||||
methods: {
|
||||
getValue: () => '=',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const createComponent = ({ config = mockLabelToken, value = { data: '' }, active = false } = {}) =>
|
||||
const createComponent = ({
|
||||
config = mockLabelToken,
|
||||
value = { data: '' },
|
||||
active = false,
|
||||
stubs = defaultStubs,
|
||||
} = {}) =>
|
||||
mount(LabelToken, {
|
||||
propsData: {
|
||||
config,
|
||||
|
@ -26,15 +49,7 @@ const createComponent = ({ config = mockLabelToken, value = { data: '' }, active
|
|||
portalName: 'fake target',
|
||||
alignSuggestions: function fakeAlignSuggestions() {},
|
||||
},
|
||||
stubs: {
|
||||
Portal: true,
|
||||
GlFilteredSearchSuggestionList: {
|
||||
template: '<div></div>',
|
||||
methods: {
|
||||
getValue: () => '=',
|
||||
},
|
||||
},
|
||||
},
|
||||
stubs,
|
||||
});
|
||||
|
||||
describe('LabelToken', () => {
|
||||
|
@ -43,7 +58,6 @@ describe('LabelToken', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
wrapper = createComponent();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -96,6 +110,10 @@ describe('LabelToken', () => {
|
|||
});
|
||||
|
||||
describe('methods', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = createComponent();
|
||||
});
|
||||
|
||||
describe('fetchLabelBySearchTerm', () => {
|
||||
it('calls `config.fetchLabels` with provided searchTerm param', () => {
|
||||
jest.spyOn(wrapper.vm.config, 'fetchLabels');
|
||||
|
@ -138,6 +156,8 @@ describe('LabelToken', () => {
|
|||
});
|
||||
|
||||
describe('template', () => {
|
||||
const defaultLabels = [DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY];
|
||||
|
||||
beforeEach(async () => {
|
||||
wrapper = createComponent({ value: { data: `"${mockRegularLabel.title}"` } });
|
||||
|
||||
|
@ -164,5 +184,43 @@ describe('LabelToken', () => {
|
|||
.attributes('style'),
|
||||
).toBe('background-color: rgb(186, 218, 85); color: rgb(255, 255, 255);');
|
||||
});
|
||||
|
||||
it('renders provided defaultLabels as suggestions', async () => {
|
||||
wrapper = createComponent({
|
||||
active: true,
|
||||
config: { ...mockLabelToken, defaultLabels },
|
||||
stubs: { Portal: true },
|
||||
});
|
||||
const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
|
||||
const suggestionsSegment = tokenSegments.at(2);
|
||||
suggestionsSegment.vm.$emit('activate');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
|
||||
|
||||
expect(suggestions).toHaveLength(defaultLabels.length);
|
||||
defaultLabels.forEach((label, index) => {
|
||||
expect(suggestions.at(index).text()).toBe(label.text);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders `DEFAULT_LABELS` as default suggestions', async () => {
|
||||
wrapper = createComponent({
|
||||
active: true,
|
||||
config: { ...mockLabelToken },
|
||||
stubs: { Portal: true },
|
||||
});
|
||||
const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
|
||||
const suggestionsSegment = tokenSegments.at(2);
|
||||
suggestionsSegment.vm.$emit('activate');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
|
||||
|
||||
expect(suggestions).toHaveLength(DEFAULT_LABELS.length);
|
||||
DEFAULT_LABELS.forEach((label, index) => {
|
||||
expect(suggestions.at(index).text()).toBe(label.text);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import { GlFilteredSearchToken, GlFilteredSearchTokenSegment } from '@gitlab/ui';
|
||||
import {
|
||||
GlFilteredSearchToken,
|
||||
GlFilteredSearchSuggestion,
|
||||
GlFilteredSearchTokenSegment,
|
||||
} from '@gitlab/ui';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
import createFlash from '~/flash';
|
||||
import { DEFAULT_MILESTONES } from '~/vue_shared/components/filtered_search_bar/constants';
|
||||
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
|
||||
|
||||
import {
|
||||
|
@ -16,10 +21,21 @@ import {
|
|||
|
||||
jest.mock('~/flash');
|
||||
|
||||
const defaultStubs = {
|
||||
Portal: true,
|
||||
GlFilteredSearchSuggestionList: {
|
||||
template: '<div></div>',
|
||||
methods: {
|
||||
getValue: () => '=',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const createComponent = ({
|
||||
config = mockMilestoneToken,
|
||||
value = { data: '' },
|
||||
active = false,
|
||||
stubs = defaultStubs,
|
||||
} = {}) =>
|
||||
mount(MilestoneToken, {
|
||||
propsData: {
|
||||
|
@ -31,15 +47,7 @@ const createComponent = ({
|
|||
portalName: 'fake target',
|
||||
alignSuggestions: function fakeAlignSuggestions() {},
|
||||
},
|
||||
stubs: {
|
||||
Portal: true,
|
||||
GlFilteredSearchSuggestionList: {
|
||||
template: '<div></div>',
|
||||
methods: {
|
||||
getValue: () => '=',
|
||||
},
|
||||
},
|
||||
},
|
||||
stubs,
|
||||
});
|
||||
|
||||
describe('MilestoneToken', () => {
|
||||
|
@ -126,6 +134,8 @@ describe('MilestoneToken', () => {
|
|||
});
|
||||
|
||||
describe('template', () => {
|
||||
const defaultMilestones = [{ text: 'foo', value: 'foo' }, { text: 'bar', value: 'baz' }];
|
||||
|
||||
beforeEach(async () => {
|
||||
wrapper = createComponent({ value: { data: `"${mockRegularMilestone.title}"` } });
|
||||
|
||||
|
@ -146,5 +156,43 @@ describe('MilestoneToken', () => {
|
|||
expect(tokenSegments).toHaveLength(3); // Milestone, =, '%"4.0"'
|
||||
expect(tokenSegments.at(2).text()).toBe(`%"${mockRegularMilestone.title}"`); // "4.0 RC1"
|
||||
});
|
||||
|
||||
it('renders provided defaultMilestones as suggestions', async () => {
|
||||
wrapper = createComponent({
|
||||
active: true,
|
||||
config: { ...mockMilestoneToken, defaultMilestones },
|
||||
stubs: { Portal: true },
|
||||
});
|
||||
const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
|
||||
const suggestionsSegment = tokenSegments.at(2);
|
||||
suggestionsSegment.vm.$emit('activate');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
|
||||
|
||||
expect(suggestions).toHaveLength(defaultMilestones.length);
|
||||
defaultMilestones.forEach((milestone, index) => {
|
||||
expect(suggestions.at(index).text()).toBe(milestone.text);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders `DEFAULT_MILESTONES` as default suggestions', async () => {
|
||||
wrapper = createComponent({
|
||||
active: true,
|
||||
config: { ...mockMilestoneToken },
|
||||
stubs: { Portal: true },
|
||||
});
|
||||
const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
|
||||
const suggestionsSegment = tokenSegments.at(2);
|
||||
suggestionsSegment.vm.$emit('activate');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
|
||||
|
||||
expect(suggestions).toHaveLength(DEFAULT_MILESTONES.length);
|
||||
DEFAULT_MILESTONES.forEach((milestone, index) => {
|
||||
expect(suggestions.at(index).text()).toBe(milestone.text);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GroupSearchResults do
|
||||
# group creation calls GroupFinder, so need to create the group
|
||||
# before so expect(GroupsFinder) check works
|
||||
let_it_be(:group) { create(:group) }
|
||||
let(:user) { create(:user) }
|
||||
let(:group) { create(:group) }
|
||||
|
||||
subject(:results) { described_class.new(user, 'gob', anything, group: group) }
|
||||
|
||||
|
@ -60,6 +62,19 @@ RSpec.describe Gitlab::GroupSearchResults do
|
|||
|
||||
is_expected.to be_empty
|
||||
end
|
||||
|
||||
it 'does not return the user invited to the group' do
|
||||
user = create(:user, username: 'gob_bluth')
|
||||
create(:group_member, :invited, :developer, user: user, group: group)
|
||||
|
||||
is_expected.to be_empty
|
||||
end
|
||||
|
||||
it 'calls GroupFinder during execution' do
|
||||
expect(GroupsFinder).to receive(:new).with(user).and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
describe "#issuable_params" do
|
||||
|
|
|
@ -447,6 +447,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
expect(count_data[:clusters_applications_jupyter]).to eq(1)
|
||||
expect(count_data[:clusters_applications_cilium]).to eq(1)
|
||||
expect(count_data[:clusters_management_project]).to eq(1)
|
||||
expect(count_data[:kubernetes_agents]).to eq(1)
|
||||
|
||||
expect(count_data[:deployments]).to eq(4)
|
||||
expect(count_data[:successful_deployments]).to eq(2)
|
||||
|
|
|
@ -7,22 +7,24 @@ RSpec.describe 'Creating a Snippet' do
|
|||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let(:content) { 'Initial content' }
|
||||
|
||||
let(:description) { 'Initial description' }
|
||||
let(:title) { 'Initial title' }
|
||||
let(:file_name) { 'Initial file_name' }
|
||||
let(:visibility_level) { 'public' }
|
||||
let(:action) { :create }
|
||||
let(:file_1) { { filePath: 'example_file1', content: 'This is the example file 1' }}
|
||||
let(:file_2) { { filePath: 'example_file2', content: 'This is the example file 2' }}
|
||||
let(:actions) { [{ action: action }.merge(file_1), { action: action }.merge(file_2)] }
|
||||
let(:project_path) { nil }
|
||||
let(:uploaded_files) { nil }
|
||||
let(:mutation_vars) do
|
||||
{
|
||||
content: content,
|
||||
description: description,
|
||||
visibility_level: visibility_level,
|
||||
file_name: file_name,
|
||||
title: title,
|
||||
project_path: project_path,
|
||||
uploaded_files: uploaded_files
|
||||
uploaded_files: uploaded_files,
|
||||
blob_actions: actions
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -62,68 +64,6 @@ RSpec.describe 'Creating a Snippet' do
|
|||
context 'when the user has permission' do
|
||||
let(:current_user) { user }
|
||||
|
||||
context 'with PersonalSnippet' do
|
||||
it 'creates the Snippet' do
|
||||
expect do
|
||||
subject
|
||||
end.to change { Snippet.count }.by(1)
|
||||
end
|
||||
|
||||
it 'returns the created Snippet' do
|
||||
subject
|
||||
|
||||
expect(mutation_response['snippet']['blob']['richData']).to be_nil
|
||||
expect(mutation_response['snippet']['blob']['plainData']).to match(content)
|
||||
expect(mutation_response['snippet']['title']).to eq(title)
|
||||
expect(mutation_response['snippet']['description']).to eq(description)
|
||||
expect(mutation_response['snippet']['fileName']).to eq(file_name)
|
||||
expect(mutation_response['snippet']['visibilityLevel']).to eq(visibility_level)
|
||||
expect(mutation_response['snippet']['project']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with ProjectSnippet' do
|
||||
let(:project_path) { project.full_path }
|
||||
|
||||
before do
|
||||
project.add_developer(current_user)
|
||||
end
|
||||
|
||||
it 'creates the Snippet' do
|
||||
expect do
|
||||
subject
|
||||
end.to change { Snippet.count }.by(1)
|
||||
end
|
||||
|
||||
it 'returns the created Snippet' do
|
||||
subject
|
||||
|
||||
expect(mutation_response['snippet']['blob']['richData']).to be_nil
|
||||
expect(mutation_response['snippet']['blob']['plainData']).to match(content)
|
||||
expect(mutation_response['snippet']['title']).to eq(title)
|
||||
expect(mutation_response['snippet']['description']).to eq(description)
|
||||
expect(mutation_response['snippet']['fileName']).to eq(file_name)
|
||||
expect(mutation_response['snippet']['visibilityLevel']).to eq(visibility_level)
|
||||
expect(mutation_response['snippet']['project']['fullPath']).to eq(project_path)
|
||||
end
|
||||
|
||||
context 'when the project path is invalid' do
|
||||
let(:project_path) { 'foobar' }
|
||||
|
||||
it_behaves_like 'a mutation that returns top-level errors',
|
||||
errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
|
||||
end
|
||||
|
||||
context 'when the feature is disabled' do
|
||||
before do
|
||||
project.project_feature.update_attribute(:snippets_access_level, ProjectFeature::DISABLED)
|
||||
end
|
||||
|
||||
it_behaves_like 'a mutation that returns top-level errors',
|
||||
errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'does not create snippet' do
|
||||
it 'does not create the Snippet' do
|
||||
expect do
|
||||
|
@ -138,29 +78,11 @@ RSpec.describe 'Creating a Snippet' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when snippet is created using the files param' do
|
||||
let(:action) { :create }
|
||||
let(:file_1) { { filePath: 'example_file1', content: 'This is the example file 1' }}
|
||||
let(:file_2) { { filePath: 'example_file2', content: 'This is the example file 2' }}
|
||||
let(:actions) { [{ action: action }.merge(file_1), { action: action }.merge(file_2)] }
|
||||
let(:mutation_vars) do
|
||||
{
|
||||
description: description,
|
||||
visibility_level: visibility_level,
|
||||
project_path: project_path,
|
||||
title: title,
|
||||
blob_actions: actions
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates the Snippet' do
|
||||
shared_examples 'creates snippet' do
|
||||
it 'returns the created Snippet' do
|
||||
expect do
|
||||
subject
|
||||
end.to change { Snippet.count }.by(1)
|
||||
end
|
||||
|
||||
it 'returns the created Snippet' do
|
||||
subject
|
||||
|
||||
expect(mutation_response['snippet']['title']).to eq(title)
|
||||
expect(mutation_response['snippet']['description']).to eq(description)
|
||||
|
@ -179,6 +101,36 @@ RSpec.describe 'Creating a Snippet' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with PersonalSnippet' do
|
||||
it_behaves_like 'creates snippet'
|
||||
end
|
||||
|
||||
context 'with ProjectSnippet' do
|
||||
let(:project_path) { project.full_path }
|
||||
|
||||
before do
|
||||
project.add_developer(current_user)
|
||||
end
|
||||
|
||||
it_behaves_like 'creates snippet'
|
||||
|
||||
context 'when the project path is invalid' do
|
||||
let(:project_path) { 'foobar' }
|
||||
|
||||
it_behaves_like 'a mutation that returns top-level errors',
|
||||
errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
|
||||
end
|
||||
|
||||
context 'when the feature is disabled' do
|
||||
before do
|
||||
project.project_feature.update_attribute(:snippets_access_level, ProjectFeature::DISABLED)
|
||||
end
|
||||
|
||||
it_behaves_like 'a mutation that returns top-level errors',
|
||||
errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are ActiveRecord validation errors' do
|
||||
let(:title) { '' }
|
||||
|
||||
|
@ -187,7 +139,7 @@ RSpec.describe 'Creating a Snippet' do
|
|||
end
|
||||
|
||||
context 'when there non ActiveRecord errors' do
|
||||
let(:file_name) { 'invalid://file/path' }
|
||||
let(:file_1) { { filePath: 'invalid://file/path', content: 'foobar' }}
|
||||
|
||||
it_behaves_like 'a mutation that returns errors in the response', errors: ['Repository Error creating the snippet - Invalid file name']
|
||||
it_behaves_like 'does not create snippet'
|
||||
|
|
|
@ -12,18 +12,20 @@ RSpec.describe 'Updating a Snippet' do
|
|||
let(:updated_content) { 'Updated content' }
|
||||
let(:updated_description) { 'Updated description' }
|
||||
let(:updated_title) { 'Updated_title' }
|
||||
let(:updated_file_name) { 'Updated file_name' }
|
||||
let(:current_user) { snippet.author }
|
||||
|
||||
let(:updated_file) { 'CHANGELOG' }
|
||||
let(:deleted_file) { 'README' }
|
||||
let(:snippet_gid) { GitlabSchema.id_from_object(snippet).to_s }
|
||||
let(:mutation_vars) do
|
||||
{
|
||||
id: snippet_gid,
|
||||
content: updated_content,
|
||||
description: updated_description,
|
||||
visibility_level: 'public',
|
||||
file_name: updated_file_name,
|
||||
title: updated_title
|
||||
title: updated_title,
|
||||
blob_actions: [
|
||||
{ action: :update, filePath: updated_file, content: updated_content },
|
||||
{ action: :delete, filePath: deleted_file }
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -50,21 +52,32 @@ RSpec.describe 'Updating a Snippet' do
|
|||
end
|
||||
|
||||
context 'when the user has permission' do
|
||||
it 'updates the Snippet' do
|
||||
it 'updates the snippet record' do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
expect(snippet.reload.title).to eq(updated_title)
|
||||
end
|
||||
|
||||
it 'returns the updated Snippet' do
|
||||
it 'updates the Snippet' do
|
||||
blob_to_update = blob_at(updated_file)
|
||||
blob_to_delete = blob_at(deleted_file)
|
||||
|
||||
expect(blob_to_update.data).not_to eq updated_content
|
||||
expect(blob_to_delete).to be_present
|
||||
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
expect(mutation_response['snippet']['blob']['richData']).to be_nil
|
||||
expect(mutation_response['snippet']['blob']['plainData']).to match(updated_content)
|
||||
expect(mutation_response['snippet']['title']).to eq(updated_title)
|
||||
expect(mutation_response['snippet']['description']).to eq(updated_description)
|
||||
expect(mutation_response['snippet']['fileName']).to eq(updated_file_name)
|
||||
expect(mutation_response['snippet']['visibilityLevel']).to eq('public')
|
||||
blob_to_update = blob_at(updated_file)
|
||||
blob_to_delete = blob_at(deleted_file)
|
||||
|
||||
aggregate_failures do
|
||||
expect(blob_to_update.data).to eq updated_content
|
||||
expect(blob_to_delete).to be_nil
|
||||
expect(blob_in_mutation_response(updated_file)['plainData']).to match(updated_content)
|
||||
expect(mutation_response['snippet']['title']).to eq(updated_title)
|
||||
expect(mutation_response['snippet']['description']).to eq(updated_description)
|
||||
expect(mutation_response['snippet']['visibilityLevel']).to eq('public')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are ActiveRecord validation errors' do
|
||||
|
@ -79,16 +92,29 @@ RSpec.describe 'Updating a Snippet' do
|
|||
end
|
||||
|
||||
it 'returns the Snippet with its original values' do
|
||||
blob_to_update = blob_at(updated_file)
|
||||
blob_to_delete = blob_at(deleted_file)
|
||||
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
expect(mutation_response['snippet']['blob']['richData']).to be_nil
|
||||
expect(mutation_response['snippet']['blob']['plainData']).to match(original_content)
|
||||
expect(mutation_response['snippet']['title']).to eq(original_title)
|
||||
expect(mutation_response['snippet']['description']).to eq(original_description)
|
||||
expect(mutation_response['snippet']['fileName']).to eq(original_file_name)
|
||||
expect(mutation_response['snippet']['visibilityLevel']).to eq('private')
|
||||
aggregate_failures do
|
||||
expect(blob_at(updated_file).data).to eq blob_to_update.data
|
||||
expect(blob_at(deleted_file).data).to eq blob_to_delete.data
|
||||
expect(blob_in_mutation_response(deleted_file)['plainData']).not_to be_nil
|
||||
expect(mutation_response['snippet']['title']).to eq(original_title)
|
||||
expect(mutation_response['snippet']['description']).to eq(original_description)
|
||||
expect(mutation_response['snippet']['visibilityLevel']).to eq('private')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def blob_in_mutation_response(filename)
|
||||
mutation_response['snippet']['blobs'].select { |blob| blob['name'] == filename }[0]
|
||||
end
|
||||
|
||||
def blob_at(filename)
|
||||
snippet.repository.blob_at('HEAD', filename)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -96,6 +122,7 @@ RSpec.describe 'Updating a Snippet' do
|
|||
let(:snippet) do
|
||||
create(:personal_snippet,
|
||||
:private,
|
||||
:repository,
|
||||
file_name: original_file_name,
|
||||
title: original_title,
|
||||
content: original_content,
|
||||
|
@ -111,6 +138,7 @@ RSpec.describe 'Updating a Snippet' do
|
|||
let(:snippet) do
|
||||
create(:project_snippet,
|
||||
:private,
|
||||
:repository,
|
||||
project: project,
|
||||
author: create(:user),
|
||||
file_name: original_file_name,
|
||||
|
@ -149,40 +177,4 @@ RSpec.describe 'Updating a Snippet' do
|
|||
|
||||
it_behaves_like 'when the snippet is not found'
|
||||
end
|
||||
|
||||
context 'when using the files params' do
|
||||
let!(:snippet) { create(:personal_snippet, :private, :repository) }
|
||||
let(:updated_content) { 'updated_content' }
|
||||
let(:updated_file) { 'CHANGELOG' }
|
||||
let(:deleted_file) { 'README' }
|
||||
let(:mutation_vars) do
|
||||
{
|
||||
id: snippet_gid,
|
||||
blob_actions: [
|
||||
{ action: :update, filePath: updated_file, content: updated_content },
|
||||
{ action: :delete, filePath: deleted_file }
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
it 'updates the Snippet' do
|
||||
blob_to_update = blob_at(updated_file)
|
||||
expect(blob_to_update.data).not_to eq updated_content
|
||||
|
||||
blob_to_delete = blob_at(deleted_file)
|
||||
expect(blob_to_delete).to be_present
|
||||
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
blob_to_update = blob_at(updated_file)
|
||||
expect(blob_to_update.data).to eq updated_content
|
||||
|
||||
blob_to_delete = blob_at(deleted_file)
|
||||
expect(blob_to_delete).to be_nil
|
||||
end
|
||||
|
||||
def blob_at(filename)
|
||||
snippet.repository.blob_at('HEAD', filename)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,7 +22,7 @@ RSpec.describe Ci::RetryBuildService do
|
|||
described_class.new(project, user)
|
||||
end
|
||||
|
||||
clone_accessors = described_class::CLONE_ACCESSORS
|
||||
clone_accessors = described_class.clone_accessors
|
||||
|
||||
reject_accessors =
|
||||
%i[id status user token token_encrypted coverage trace runner
|
||||
|
@ -143,6 +143,8 @@ RSpec.describe Ci::RetryBuildService do
|
|||
Ci::Build.reflect_on_all_associations.map(&:name) +
|
||||
[:tag_list, :needs_attributes]
|
||||
|
||||
current_accessors << :secrets if Gitlab.ee?
|
||||
|
||||
current_accessors.uniq!
|
||||
|
||||
expect(current_accessors).to include(*processed_accessors)
|
||||
|
|
Loading…
Reference in a new issue