Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9e83c35c6a
commit
df2358a5f7
|
|
@ -332,7 +332,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/graphql/sample_issue_boards.md @msedlakjakubowski
|
||||
/doc/api/group_access_tokens.md @eread
|
||||
/doc/api/group_activity_analytics.md @fneill
|
||||
/doc/api/group_badges.md @eread
|
||||
/doc/api/group_badges.md @fneill
|
||||
/doc/api/group_boards.md @msedlakjakubowski
|
||||
/doc/api/group_clusters.md @sselhorn
|
||||
/doc/api/group_import_export.md @ngaskill
|
||||
|
|
@ -344,7 +344,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/group_relations_export.md @ngaskill
|
||||
/doc/api/group_repository_storage_moves.md @aqualls
|
||||
/doc/api/group_wikis.md @aqualls
|
||||
/doc/api/groups.md @eread
|
||||
/doc/api/groups.md @fneill
|
||||
/doc/api/import.md @ngaskill
|
||||
/doc/api/index.md @kpaizee
|
||||
/doc/api/instance_clusters.md @sselhorn
|
||||
|
|
@ -705,7 +705,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/group/epics/index.md @msedlakjakubowski
|
||||
/doc/user/group/epics/linked_epics.md @msedlakjakubowski
|
||||
/doc/user/group/epics/manage_epics.md @msedlakjakubowski
|
||||
/doc/user/group/index.md @eread
|
||||
/doc/user/group/index.md @fneill
|
||||
/doc/user/group/import/index.md @ngaskill
|
||||
/doc/user/group/insights/index.md @fneill
|
||||
/doc/user/group/issues_analytics/index.md @msedlakjakubowski
|
||||
|
|
@ -718,7 +718,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/group/saml_sso/scim_setup.md @eread
|
||||
/doc/user/group/settings/group_access_tokens.md @eread
|
||||
/doc/user/group/settings/import_export.md @ngaskill
|
||||
/doc/user/group/subgroups/index.md @eread
|
||||
/doc/user/group/subgroups/index.md @fneill
|
||||
/doc/user/group/value_stream_analytics/index.md @fneill
|
||||
/doc/user/infrastructure/clusters/ @sselhorn
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/apparmor.md @ngaskill
|
||||
|
|
@ -761,7 +761,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/project/issues/ @msedlakjakubowski
|
||||
/doc/user/project/issues/csv_import.md @ngaskill
|
||||
/doc/user/project/labels.md @msedlakjakubowski
|
||||
/doc/user/project/members/index.md @eread
|
||||
/doc/user/project/members/index.md @fneill
|
||||
/doc/user/project/members/share_project_with_groups.md @fneill
|
||||
/doc/user/project/merge_requests/ @aqualls
|
||||
/doc/user/project/merge_requests/accessibility_testing.md @eread
|
||||
|
|
|
|||
|
|
@ -113,17 +113,6 @@ Lint/RedundantCopDisableDirective:
|
|||
Lint/UselessMethodDefinition:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 11
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: lowercase, uppercase
|
||||
Naming/HeredocDelimiterCase:
|
||||
Exclude:
|
||||
- 'spec/lib/gitlab/diff/parser_spec.rb'
|
||||
- 'spec/lib/json_web_token/rsa_token_spec.rb'
|
||||
- 'spec/models/commit_spec.rb'
|
||||
- 'spec/support/helpers/repo_helpers.rb'
|
||||
- 'spec/support/helpers/seed_repo.rb'
|
||||
|
||||
# Offense count: 321
|
||||
# Configuration parameters: ForbiddenDelimiters.
|
||||
# ForbiddenDelimiters: (?-mix:(^|\s)(EO[A-Z]{1}|END)(\s|$))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
Naming/HeredocDelimiterCase:
|
||||
Exclude:
|
||||
- 'spec/lib/gitlab/diff/parser_spec.rb'
|
||||
- 'spec/lib/json_web_token/rsa_token_spec.rb'
|
||||
- 'spec/models/commit_spec.rb'
|
||||
- 'spec/support/helpers/repo_helpers.rb'
|
||||
- 'spec/support/helpers/seed_repo.rb'
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query accessTokensGetProjects(
|
||||
$search: String = ""
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
#import "../fragments/count.fragment.graphql"
|
||||
|
||||
query getCount($identifier: MeasurementIdentifier!, $first: Int, $after: String) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
#import "../fragments/count.fragment.graphql"
|
||||
|
||||
query getUsersCount($first: Int, $after: String) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query boardsGetGroupProjects($fullPath: ID!, $search: String, $after: String) {
|
||||
group(fullPath: $fullPath) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
#import "../fragments/cluster_agent_token.fragment.graphql"
|
||||
|
||||
query getClusterAgent(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
#import "../fragments/cluster_agent.fragment.graphql"
|
||||
|
||||
query getAgents(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query getProjects(
|
||||
$search: String!
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
SEARCH_BOX_INDEX,
|
||||
SEARCH_INPUT_DESCRIPTION,
|
||||
SEARCH_RESULTS_DESCRIPTION,
|
||||
SEARCH_SHORTCUTS_MIN_CHARACTERS,
|
||||
} from '../constants';
|
||||
import HeaderSearchAutocompleteItems from './header_search_autocomplete_items.vue';
|
||||
import HeaderSearchDefaultItems from './header_search_default_items.vue';
|
||||
|
|
@ -50,7 +51,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
...mapState(['search', 'loading']),
|
||||
...mapGetters(['searchQuery', 'searchOptions']),
|
||||
...mapGetters(['searchQuery', 'searchOptions', 'autocompleteGroupedSearchOptions']),
|
||||
searchText: {
|
||||
get() {
|
||||
return this.search;
|
||||
|
|
@ -66,14 +67,20 @@ export default {
|
|||
return this.currentFocusedOption?.html_id;
|
||||
},
|
||||
isLoggedIn() {
|
||||
return gon?.current_username;
|
||||
return Boolean(gon?.current_username);
|
||||
},
|
||||
showSearchDropdown() {
|
||||
return this.showDropdown && this.isLoggedIn;
|
||||
const hasResultsUnderMinCharacters =
|
||||
this.searchText?.length === 1 ? this?.autocompleteGroupedSearchOptions?.length > 0 : true;
|
||||
|
||||
return this.showDropdown && this.isLoggedIn && hasResultsUnderMinCharacters;
|
||||
},
|
||||
showDefaultItems() {
|
||||
return !this.searchText;
|
||||
},
|
||||
showShortcuts() {
|
||||
return this.searchText && this.searchText?.length >= SEARCH_SHORTCUTS_MIN_CHARACTERS;
|
||||
},
|
||||
defaultIndex() {
|
||||
if (this.showDefaultItems) {
|
||||
return SEARCH_BOX_INDEX;
|
||||
|
|
@ -182,7 +189,10 @@ export default {
|
|||
:current-focused-option="currentFocusedOption"
|
||||
/>
|
||||
<template v-else>
|
||||
<header-search-scoped-items :current-focused-option="currentFocusedOption" />
|
||||
<header-search-scoped-items
|
||||
v-if="showShortcuts"
|
||||
:current-focused-option="currentFocusedOption"
|
||||
/>
|
||||
<header-search-autocomplete-items :current-focused-option="currentFocusedOption" />
|
||||
</template>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<template v-if="!loading">
|
||||
<div v-for="option in autocompleteGroupedSearchOptions" :key="option.category">
|
||||
<gl-dropdown-divider />
|
||||
<div v-for="(option, index) in autocompleteGroupedSearchOptions" :key="option.category">
|
||||
<gl-dropdown-divider v-if="index > 0" />
|
||||
<gl-dropdown-section-header>{{ option.category }}</gl-dropdown-section-header>
|
||||
<gl-dropdown-item
|
||||
v-for="data in option.data"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlDropdownItem } from '@gitlab/ui';
|
||||
import { GlDropdownItem, GlDropdownDivider } from '@gitlab/ui';
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import { __, sprintf } from '~/locale';
|
||||
|
||||
|
|
@ -7,6 +7,7 @@ export default {
|
|||
name: 'HeaderSearchScopedItems',
|
||||
components: {
|
||||
GlDropdownItem,
|
||||
GlDropdownDivider,
|
||||
},
|
||||
props: {
|
||||
currentFocusedOption: {
|
||||
|
|
@ -17,7 +18,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
...mapState(['search']),
|
||||
...mapGetters(['scopedSearchOptions']),
|
||||
...mapGetters(['scopedSearchOptions', 'autocompleteGroupedSearchOptions']),
|
||||
},
|
||||
methods: {
|
||||
isOptionFocused(option) {
|
||||
|
|
@ -53,5 +54,6 @@ export default {
|
|||
<span v-if="option.scope" class="gl-font-style-italic">{{ option.scope }}</span>
|
||||
</span>
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-divider v-if="autocompleteGroupedSearchOptions.length > 0" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ export const FIRST_DROPDOWN_INDEX = 0;
|
|||
|
||||
export const SEARCH_BOX_INDEX = -1;
|
||||
|
||||
export const SEARCH_SHORTCUTS_MIN_CHARACTERS = 2;
|
||||
|
||||
export const SEARCH_INPUT_DESCRIPTION = 'search-input-description';
|
||||
|
||||
export const SEARCH_RESULTS_DESCRIPTION = 'search-results-description';
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ export const fetchAutocompleteOptions = ({ commit, getters }) => {
|
|||
commit(types.REQUEST_AUTOCOMPLETE);
|
||||
return axios
|
||||
.get(getters.autocompleteQuery)
|
||||
.then(({ data }) => commit(types.RECEIVE_AUTOCOMPLETE_SUCCESS, data))
|
||||
.then(({ data }) => {
|
||||
commit(types.RECEIVE_AUTOCOMPLETE_SUCCESS, data);
|
||||
})
|
||||
.catch(() => {
|
||||
commit(types.RECEIVE_AUTOCOMPLETE_ERROR);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -190,7 +190,6 @@ export const autocompleteGroupedSearchOptions = (state) => {
|
|||
results.push(groupedOptions[option.category]);
|
||||
}
|
||||
});
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
#import "./issue.fragment.graphql"
|
||||
|
||||
query getIssues(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query jiraGetProjects(
|
||||
$search: String!
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ function deferredInitialisation() {
|
|||
// In case the user started searching before we bootstrapped, let's pass the search along.
|
||||
const initialSearchValue = searchInputBox.value;
|
||||
await initHeaderSearchApp(initialSearchValue);
|
||||
// this is new #search input element. We need to re-find it.
|
||||
document.querySelector('#search').focus();
|
||||
})
|
||||
.catch(() => {});
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import { convertObjectPropsToCamelCase } from '../../lib/utils/common_utils';
|
|||
import { s__, sprintf } from '../../locale';
|
||||
import { ENVIRONMENT_AVAILABLE_STATE, OVERVIEW_DASHBOARD_PATH, VARIABLE_TYPES } from '../constants';
|
||||
import trackDashboardLoad from '../monitoring_tracking_helper';
|
||||
import getAnnotations from '../queries/getAnnotations.query.graphql';
|
||||
import getDashboardValidationWarnings from '../queries/getDashboardValidationWarnings.query.graphql';
|
||||
import getEnvironments from '../queries/getEnvironments.query.graphql';
|
||||
import getAnnotations from '../queries/get_annotations.query.graphql';
|
||||
import getDashboardValidationWarnings from '../queries/get_dashboard_validation_warnings.query.graphql';
|
||||
import getEnvironments from '../queries/get_environments.query.graphql';
|
||||
import { getDashboard, getPrometheusQueryData } from '../requests';
|
||||
|
||||
import * as types from './mutation_types';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query getContainerRepositoryTags(
|
||||
$id: ID!
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query getDependencyProxyDetails(
|
||||
$fullPath: ID!
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#import "~/packages_and_registries/package_registry/graphql/fragments/package_data.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query getPackages(
|
||||
$fullPath: ID!
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query getPipelineJobs($fullPath: ID!, $iid: ID!, $after: String) {
|
||||
project(fullPath: $fullPath) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query getRunnerJobs($id: CiRunnerID!, $first: Int, $last: Int, $before: String, $after: String) {
|
||||
runner(id: $id) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query getRunnerProjects(
|
||||
$id: CiRunnerID!
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#import "~/runner/graphql/list/list_item.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query getRunners(
|
||||
$before: String
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#import "~/runner/graphql/list/list_item.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query getGroupRunners(
|
||||
$groupFullPath: ID!
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import sidebarDetailsIssueQuery from 'ee_else_ce/sidebar/queries/sidebarDetails.query.graphql';
|
||||
import sidebarDetailsIssueQuery from 'ee_else_ce/sidebar/queries/sidebar_details.query.graphql';
|
||||
import { TYPE_USER } from '~/graphql_shared/constants';
|
||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||
import createGqClient, { fetchPolicies } from '~/lib/graphql';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import reviewerRereviewMutation from '../queries/reviewer_rereview.mutation.graphql';
|
||||
import sidebarDetailsMRQuery from '../queries/sidebarDetailsMR.query.graphql';
|
||||
import sidebarDetailsMRQuery from '../queries/sidebar_details_mr.query.graphql';
|
||||
import toggleAttentionRequestedMutation from '../queries/toggle_attention_requested.mutation.graphql';
|
||||
|
||||
const queries = {
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ import TitleField from '~/vue_shared/components/form/title.vue';
|
|||
|
||||
import { SNIPPET_CREATE_MUTATION_ERROR, SNIPPET_UPDATE_MUTATION_ERROR } from '../constants';
|
||||
import { getSnippetMixin } from '../mixins/snippets';
|
||||
import CreateSnippetMutation from '../mutations/createSnippet.mutation.graphql';
|
||||
import UpdateSnippetMutation from '../mutations/updateSnippet.mutation.graphql';
|
||||
import CreateSnippetMutation from '../mutations/create_snippet.mutation.graphql';
|
||||
import UpdateSnippetMutation from '../mutations/update_snippet.mutation.graphql';
|
||||
import { markBlobPerformance } from '../utils/blob';
|
||||
import { getErrorMessage } from '../utils/error';
|
||||
|
||||
|
|
@ -238,9 +238,9 @@ export default {
|
|||
>
|
||||
</template>
|
||||
<template #append>
|
||||
<gl-button type="cancel" data-testid="snippet-cancel-btn" :href="cancelButtonHref">{{
|
||||
__('Cancel')
|
||||
}}</gl-button>
|
||||
<gl-button type="cancel" data-testid="snippet-cancel-btn" :href="cancelButtonHref">
|
||||
{{ __('Cancel') }}
|
||||
</gl-button>
|
||||
</template>
|
||||
</form-footer-actions>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { __, s__, sprintf } from '~/locale';
|
|||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import createFlash, { FLASH_TYPES } from '~/flash';
|
||||
|
||||
import DeleteSnippetMutation from '../mutations/deleteSnippet.mutation.graphql';
|
||||
import DeleteSnippetMutation from '../mutations/delete_snippet.mutation.graphql';
|
||||
|
||||
export const i18n = {
|
||||
snippetSpamSuccess: sprintf(
|
||||
|
|
@ -294,9 +294,9 @@ export default {
|
|||
<gl-modal ref="deleteModal" modal-id="delete-modal" title="Example title">
|
||||
<template #modal-title>{{ __('Delete snippet?') }}</template>
|
||||
|
||||
<gl-alert v-if="errorMessage" variant="danger" class="mb-2" @dismiss="errorMessage = ''">
|
||||
{{ errorMessage }}
|
||||
</gl-alert>
|
||||
<gl-alert v-if="errorMessage" variant="danger" class="mb-2" @dismiss="errorMessage = ''">{{
|
||||
errorMessage
|
||||
}}</gl-alert>
|
||||
|
||||
<gl-sprintf :message="__('Are you sure you want to delete %{name}?')">
|
||||
<template #name>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#import "../fragments/state.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query getStates($projectPath: ID!, $first: Int, $last: Int, $before: String, $after: String) {
|
||||
project(fullPath: $projectPath) {
|
||||
|
|
|
|||
|
|
@ -186,6 +186,13 @@ module SortingHelper
|
|||
}
|
||||
end
|
||||
|
||||
def runners_sort_options_hash
|
||||
{
|
||||
sort_value_created_date => sort_title_created_date,
|
||||
sort_value_contacted_date => sort_title_contacted_date
|
||||
}
|
||||
end
|
||||
|
||||
def starrers_sort_options_hash
|
||||
{
|
||||
sort_value_name => sort_title_name,
|
||||
|
|
|
|||
|
|
@ -38,8 +38,6 @@ module WorkhorseHelper
|
|||
# Send an entry from artifacts through Workhorse
|
||||
def send_artifacts_entry(file, entry)
|
||||
headers.store(*Gitlab::Workhorse.send_artifacts_entry(file, entry))
|
||||
headers.store(*Gitlab::Workhorse.detect_content_type)
|
||||
|
||||
head :ok
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -661,10 +661,6 @@ class Namespace < ApplicationRecord
|
|||
# Use SHA2 of `traversal_ids` to account for moving a namespace within the same root ancestor hierarchy.
|
||||
"namespaces:{#{traversal_ids.first}}:first_auto_devops_config:#{group_id}:#{Digest::SHA2.hexdigest(traversal_ids.join(' '))}"
|
||||
end
|
||||
|
||||
def allow_serialization?(options = nil)
|
||||
Feature.disabled?(:block_namespace_serialization, self, default_enabled: :yaml) || super
|
||||
end
|
||||
end
|
||||
|
||||
Namespace.prepend_mod_with('Namespace')
|
||||
|
|
|
|||
|
|
@ -50,6 +50,12 @@ module Auth
|
|||
access_token(['pull'], names)
|
||||
end
|
||||
|
||||
def self.pull_nested_repositories_access_token(name)
|
||||
name = name.chomp('/') if name.end_with?('/')
|
||||
paths = [name, "#{name}/*"]
|
||||
access_token(['pull'], paths)
|
||||
end
|
||||
|
||||
def self.access_token(actions, names, type = 'repository')
|
||||
names = names.flatten
|
||||
registry = Gitlab.config.registry
|
||||
|
|
|
|||
|
|
@ -1,10 +1,3 @@
|
|||
- sorted_by = sort_options_hash[@sort] || sort_title_created_date
|
||||
- runners_sort_options = runners_sort_options_hash.map { |value, text| { value: value, text: text, href: page_filter_path(sort: value) } }
|
||||
|
||||
.dropdown.inline.gl-ml-3
|
||||
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } }
|
||||
= sorted_by
|
||||
= sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
|
||||
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
|
||||
%li
|
||||
= sortable_item(sort_title_created_date, page_filter_path(sort: sort_value_created_date), sorted_by)
|
||||
= sortable_item(sort_title_contacted_date, page_filter_path(sort: sort_value_contacted_date), sorted_by)
|
||||
= gl_redirect_listbox_tag runners_sort_options, @sort, class: 'gl-ml-3', data: { display: 'static' }
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: block_namespace_serialization
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82661
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/355553
|
||||
milestone: '14.9'
|
||||
type: development
|
||||
group: group::global search
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: enforce_security_report_validation
|
||||
introduced_by_url:
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351000
|
||||
milestone: '14.9'
|
||||
type: development
|
||||
group: group::threat insights
|
||||
default_enabled: false
|
||||
|
|
@ -14,7 +14,7 @@ full list of reference architectures, see
|
|||
> - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA)
|
||||
> - **Estimated Costs:** [See cost table](index.md#cost-to-run)
|
||||
> - **Cloud Native Hybrid Alternative:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
|
||||
> - **Performance tested daily with the [GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance)**:
|
||||
> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant
|
||||
> - **Test requests per second (RPS) rates:** API: 200 RPS, Web: 20 RPS, Git (Pull): 20 RPS, Git (Push): 4 RPS
|
||||
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/10k)**
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ many organizations.
|
|||
> - **Estimated Costs:** [See cost table](index.md#cost-to-run)
|
||||
> - **Cloud Native Hybrid:** No. For a cloud native hybrid environment, you
|
||||
> can follow a [modified hybrid reference architecture](#cloud-native-hybrid-reference-architecture-with-helm-charts).
|
||||
> - **Performance tested daily with the [GitLab Performance Tool (GPT)](https://gitlab.com/gitlab-org/quality/performance)**:
|
||||
> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant
|
||||
> - **Test requests per second (RPS) rates:** API: 20 RPS, Web: 2 RPS, Git (Pull): 2 RPS, Git (Push): 1 RPS
|
||||
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/1k)**
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ full list of reference architectures, see
|
|||
> - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA)
|
||||
> - **Estimated Costs:** [See cost table](index.md#cost-to-run)
|
||||
> - **Cloud Native Hybrid Alternative:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
|
||||
> - **Performance tested weekly with the [GitLab Performance Tool (GPT)](https://gitlab.com/gitlab-org/quality/performance)**:
|
||||
> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant
|
||||
> - **Test requests per second (RPS) rates:** API: 500 RPS, Web: 50 RPS, Git (Pull): 50 RPS, Git (Push): 10 RPS
|
||||
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/25k)**
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ For a full list of reference architectures, see
|
|||
> - **Supported users (approximate):** 2,000
|
||||
> - **High Availability:** No. For a highly-available environment, you can
|
||||
> follow a modified [3K reference architecture](3k_users.md#supported-modifications-for-lower-user-counts-ha).
|
||||
> - **Estimated Costs:** [See cost table](index.md#cost-to-run)
|
||||
> - **Estimated Costs:** [See cost table](index.md#cost-to-run)
|
||||
> - **Cloud Native Hybrid:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
|
||||
> - **Performance tested daily with the [GitLab Performance Tool (GPT)](https://gitlab.com/gitlab-org/quality/performance)**:
|
||||
> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant
|
||||
> - **Test requests per second (RPS) rates:** API: 40 RPS, Web: 4 RPS, Git (Pull): 4 RPS, Git (Push): 1 RPS
|
||||
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/2k)**
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ For a full list of reference architectures, see
|
|||
> - **High Availability:** Yes, although [Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution
|
||||
> - **Estimated Costs:** [See cost table](index.md#cost-to-run)
|
||||
> - **Cloud Native Hybrid Alternative:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
|
||||
> - **Performance tested weekly with the [GitLab Performance Tool (GPT)](https://gitlab.com/gitlab-org/quality/performance)**:
|
||||
> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant
|
||||
> - **Test requests per second (RPS) rates:** API: 60 RPS, Web: 6 RPS, Git (Pull): 6 RPS, Git (Push): 1 RPS
|
||||
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/3k)**
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ full list of reference architectures, see
|
|||
> - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA)
|
||||
> - **Estimated Costs:** [See cost table](index.md#cost-to-run)
|
||||
> - **Cloud Native Hybrid Alternative:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
|
||||
> - **Performance tested weekly with the [GitLab Performance Tool (GPT)](https://gitlab.com/gitlab-org/quality/performance)**:
|
||||
> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant
|
||||
> - **Test requests per second (RPS) rates:** API: 1000 RPS, Web: 100 RPS, Git (Pull): 100 RPS, Git (Push): 20 RPS
|
||||
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/50k)**
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ costly-to-operate environment by using the
|
|||
> - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA)
|
||||
> - **Estimated Costs:** [See cost table](index.md#cost-to-run)
|
||||
> - **Cloud Native Hybrid Alternative:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
|
||||
> - **Performance tested weekly with the [GitLab Performance Tool (GPT)](https://gitlab.com/gitlab-org/quality/performance)**:
|
||||
> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant
|
||||
> - **Test requests per second (RPS) rates:** API: 100 RPS, Web: 10 RPS, Git (Pull): 10 RPS, Git (Push): 2 RPS
|
||||
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/5k)**
|
||||
|
||||
|
|
|
|||
|
|
@ -18,20 +18,6 @@ you scale GitLab accordingly.
|
|||

|
||||
<!-- Internal link: https://docs.google.com/spreadsheets/d/1obYP4fLKkVVDOljaI3-ozhmCiPtEeMblbBKkf2OADKs/edit#gid=1403207183 -->
|
||||
|
||||
Testing on these reference architectures was performed with the
|
||||
[GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance)
|
||||
at specific coded workloads, and the throughputs used for testing were
|
||||
calculated based on sample customer data. Select the
|
||||
[reference architecture](#available-reference-architectures) that matches your scale.
|
||||
|
||||
Each endpoint type is tested with the following number of requests per second (RPS)
|
||||
per 1,000 users:
|
||||
|
||||
- API: 20 RPS
|
||||
- Web: 2 RPS
|
||||
- Git (Pull): 2 RPS
|
||||
- Git (Push): 0.4 RPS (rounded to nearest integer)
|
||||
|
||||
For GitLab instances with less than 2,000 users, it's recommended that you use
|
||||
the [default setup](#automated-backups) by
|
||||
[installing GitLab](../../install/index.md) on a single machine to minimize
|
||||
|
|
@ -48,7 +34,8 @@ When scaling GitLab, there are several factors to consider:
|
|||
- A load balancer is added in front to distribute traffic across the application nodes.
|
||||
- The application nodes connects to a shared file server and PostgreSQL and Redis services on the backend.
|
||||
|
||||
NOTE:
|
||||
## Available reference architectures
|
||||
|
||||
Depending on your workflow, the following recommended reference architectures
|
||||
may need to be adapted accordingly. Your workload is influenced by factors
|
||||
including how active your users are, how much automation you use, mirroring,
|
||||
|
|
@ -57,12 +44,10 @@ provided by [GCP machine types](https://cloud.google.com/compute/docs/machine-ty
|
|||
For different cloud vendors, attempt to select options that best match the
|
||||
provided architecture.
|
||||
|
||||
## Available reference architectures
|
||||
|
||||
The following reference architectures are available.
|
||||
|
||||
### GitLab package (Omnibus)
|
||||
|
||||
The following reference architectures, where the GitLab package is used, are available:
|
||||
|
||||
- [Up to 1,000 users](1k_users.md)
|
||||
- [Up to 2,000 users](2k_users.md)
|
||||
- [Up to 3,000 users](3k_users.md)
|
||||
|
|
@ -87,17 +72,53 @@ to get assistance from Support with troubleshooting the [2,000 users](2k_users.m
|
|||
and higher reference architectures.
|
||||
[Read more about our definition of scaled architectures](https://about.gitlab.com/support/#definition-of-scaled-architecture).
|
||||
|
||||
### Validation and test results
|
||||
## Validation and test results
|
||||
|
||||
The [Quality Engineering - Enablement team](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/) does regular smoke and performance tests for the reference architectures to ensure they remain compliant.
|
||||
The [Quality Engineering team](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/)
|
||||
does regular smoke and performance tests for the reference architectures to ensure they
|
||||
remain compliant.
|
||||
|
||||
- Testing occurs against all reference architectures and cloud providers in an automated and ad-hoc fashion. This is done by two tools:
|
||||
- The [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) for building the environments.
|
||||
- The [GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance) for performance testing.
|
||||
- Network latency on the test environments between components on all Cloud Providers were measured at <5ms. Note that this is shared as an observation and not as an implicit recommendation.
|
||||
- We aim to have a "test smart" approach where architectures tested have a good range that can also apply to others. Testing focuses on 10k Omnibus on GCP as the testing has shown this is a good bellwether for the other architectures and cloud providers as well as Cloud Native Hybrids.
|
||||
- Testing is done publicly and all results are shared.
|
||||
- For more information about performance testing at GitLab, read [how our QA team leverages GitLab’s performance testing tool (and you can too)](https://about.gitlab.com/blog/2020/02/18/how-were-building-up-performance-testing-of-gitlab/).
|
||||
### Why we perform the tests
|
||||
|
||||
The Quality Department has a focus on measuring and improving the performance
|
||||
of GitLab, as well as creating and validating reference architectures that
|
||||
self-managed customers can rely on as performant configurations.
|
||||
|
||||
For more information, see our [handbook page](https://about.gitlab.com/handbook/engineering/quality/performance-and-scalability/).
|
||||
|
||||
### How we perform the tests
|
||||
|
||||
Testing occurs against all reference architectures and cloud providers in an automated and ad-hoc fashion. This is done by two tools:
|
||||
|
||||
- The [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) for building the environments.
|
||||
- The [GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance) for performance testing.
|
||||
|
||||
Network latency on the test environments between components on all Cloud Providers were measured at <5ms. Note that this is shared as an observation and not as an implicit recommendation.
|
||||
|
||||
We aim to have a "test smart" approach where architectures tested have a good range that can also apply to others. Testing focuses on 10k Omnibus on GCP as the testing has shown this is a good bellwether for the other architectures and cloud providers as well as Cloud Native Hybrids.
|
||||
|
||||
The Standard Reference Architectures are designed to be platform agnostic, with everything being run on VMs via [Omnibus GitLab](https://docs.gitlab.com/omnibus/). While testing occurs primarily on GCP, ad-hoc testing has shown that they perform similarly on equivalently specced hardware on other Cloud Providers or if run on premises (bare-metal).
|
||||
|
||||
Testing on these reference architectures is performed with the
|
||||
[GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance)
|
||||
at specific coded workloads, and the throughputs used for testing are
|
||||
calculated based on sample customer data. Select the
|
||||
[reference architecture](#available-reference-architectures) that matches your scale.
|
||||
|
||||
Each endpoint type is tested with the following number of requests per second (RPS)
|
||||
per 1,000 users:
|
||||
|
||||
- API: 20 RPS
|
||||
- Web: 2 RPS
|
||||
- Git (Pull): 2 RPS
|
||||
- Git (Push): 0.4 RPS (rounded to nearest integer)
|
||||
|
||||
### How to interpret the results
|
||||
|
||||
NOTE:
|
||||
Read our blog post on [how our QA team leverages GitLab’s performance testing tool](https://about.gitlab.com/blog/2020/02/18/how-were-building-up-performance-testing-of-gitlab/).
|
||||
|
||||
Testing is done publicly and all results are shared.
|
||||
|
||||
The following table details the testing done against the reference architectures along with the frequency and results. Additional testing is continuously evaluated, and the table is updated accordingly.
|
||||
|
||||
|
|
@ -192,9 +213,7 @@ table.test-coverage th {
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
The Standard Reference Architectures are designed to be platform agnostic, with everything being run on VMs via [Omnibus GitLab](https://docs.gitlab.com/omnibus/). While testing occurs primarily on GCP, ad-hoc testing has shown that they perform similarly on equivalently specced hardware on other Cloud Providers or if run on premises (bare-metal).
|
||||
|
||||
### Cost to run
|
||||
## Cost to run
|
||||
|
||||
<table class="test-coverage">
|
||||
<col>
|
||||
|
|
@ -271,7 +290,7 @@ The Standard Reference Architectures are designed to be platform agnostic, with
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
### Recommended cloud providers and services
|
||||
## Recommended cloud providers and services
|
||||
|
||||
NOTE:
|
||||
The following lists are non exhaustive. Generally, other cloud providers not listed
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Manage
|
||||
group: Authentication and Authorization
|
||||
group: Workspace
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Manage
|
||||
group: Authentication and Authorization
|
||||
group: Workspace
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -25,10 +25,11 @@ Branch pipelines:
|
|||
Merge request pipelines:
|
||||
|
||||
- Run when you:
|
||||
- Create a new merge request.
|
||||
- Create a new merge request from a source branch with one or more commits.
|
||||
- Push a new commit to the source branch for a merge request.
|
||||
- Select **Run pipeline** from the **Pipelines** tab in a merge request. This option
|
||||
is only available when merge request pipelines are configured for the pipeline.
|
||||
is only available when merge request pipelines are configured for the pipeline
|
||||
and the source branch has at least one commit.
|
||||
- Do not run by default. The jobs in the CI/CD configuration file [must be configured](#prerequisites)
|
||||
to run in merge request pipelines.
|
||||
- Have access to [more predefined variables](#available-predefined-variables).
|
||||
|
|
|
|||
|
|
@ -583,7 +583,7 @@ we want to fetch after or before a given endpoint.
|
|||
For example, here we're fetching 10 designs after a cursor (let us call this `projectQuery`):
|
||||
|
||||
```javascript
|
||||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query {
|
||||
project(fullPath: "root/my-project") {
|
||||
|
|
@ -606,7 +606,7 @@ query {
|
|||
}
|
||||
```
|
||||
|
||||
Note that we are using the [`pageInfo.fragment.graphql`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/graphql_shared/fragments/pageInfo.fragment.graphql) to populate the `pageInfo` information.
|
||||
Note that we are using the [`page_info.fragment.graphql`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/graphql_shared/fragments/page_info.fragment.graphql) to populate the `pageInfo` information.
|
||||
|
||||
#### Using `fetchMore` method in components
|
||||
|
||||
|
|
@ -869,7 +869,7 @@ You'd then be able to retrieve the data without providing any pagination-specifi
|
|||
Here's an example of a query using the `@connection` directive:
|
||||
|
||||
```graphql
|
||||
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
query DastSiteProfiles($fullPath: ID!, $after: String, $before: String, $first: Int, $last: Int) {
|
||||
project(fullPath: $fullPath) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
type: reference, howto
|
||||
stage: Manage
|
||||
group: Authentication and Authorization
|
||||
group: Workspace
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Manage
|
||||
group: Authentication and Authorization
|
||||
group: Workspace
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Manage
|
||||
group: Authentication and Authorization
|
||||
group: Workspace
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -707,7 +707,6 @@ module API
|
|||
|
||||
def send_artifacts_entry(file, entry)
|
||||
header(*Gitlab::Workhorse.send_artifacts_entry(file, entry))
|
||||
header(*Gitlab::Workhorse.detect_content_type)
|
||||
|
||||
body ''
|
||||
end
|
||||
|
|
|
|||
|
|
@ -87,19 +87,80 @@ module Gitlab
|
|||
end
|
||||
|
||||
def initialize(report_type, report_data, report_version = nil)
|
||||
@report_type = report_type
|
||||
@report_type = report_type&.to_sym
|
||||
@report_data = report_data
|
||||
@report_version = report_version
|
||||
@errors = []
|
||||
@warnings = []
|
||||
|
||||
populate_errors
|
||||
populate_warnings
|
||||
end
|
||||
|
||||
def valid?
|
||||
errors.empty?
|
||||
end
|
||||
|
||||
def errors
|
||||
@errors ||= schema.validate(report_data).map { |error| JSONSchemer::Errors.pretty(error) }
|
||||
def populate_errors
|
||||
if Feature.enabled?(:enforce_security_report_validation)
|
||||
@errors += schema.validate(report_data).map { |error| JSONSchemer::Errors.pretty(error) }
|
||||
else
|
||||
@warnings += schema.validate(report_data).map { |error| JSONSchemer::Errors.pretty(error) }
|
||||
end
|
||||
end
|
||||
|
||||
def populate_warnings
|
||||
add_deprecated_report_version_message if report_uses_deprecated_schema_version?
|
||||
add_unsupported_report_version_message if !report_uses_supported_schema_version? && !report_uses_deprecated_schema_version?
|
||||
end
|
||||
|
||||
def add_deprecated_report_version_message
|
||||
message = "Version #{report_version} for report type #{report_type} has been deprecated, supported versions for this report type are: #{supported_schema_versions}"
|
||||
add_message_as(level: :warning, message: message)
|
||||
end
|
||||
|
||||
def add_unsupported_report_version_message
|
||||
if Feature.enabled?(:enforce_security_report_validation)
|
||||
handle_unsupported_report_version(treat_as: :error)
|
||||
else
|
||||
handle_unsupported_report_version(treat_as: :warning)
|
||||
end
|
||||
end
|
||||
|
||||
def report_uses_deprecated_schema_version?
|
||||
DEPRECATED_VERSIONS[report_type].include?(report_version)
|
||||
end
|
||||
|
||||
def report_uses_supported_schema_version?
|
||||
SUPPORTED_VERSIONS[report_type].include?(report_version)
|
||||
end
|
||||
|
||||
def handle_unsupported_report_version(treat_as:)
|
||||
if report_version.nil?
|
||||
message = "Report version not provided, #{report_type} report type supports versions: #{supported_schema_versions}"
|
||||
add_message_as(level: treat_as, message: message)
|
||||
else
|
||||
message = "Version #{report_version} for report type #{report_type} is unsupported, supported versions for this report type are: #{supported_schema_versions}"
|
||||
end
|
||||
|
||||
add_message_as(level: treat_as, message: message)
|
||||
end
|
||||
|
||||
def supported_schema_versions
|
||||
SUPPORTED_VERSIONS[report_type].join(", ")
|
||||
end
|
||||
|
||||
def add_message_as(level:, message:)
|
||||
case level
|
||||
when :error
|
||||
@errors << message
|
||||
when :warning
|
||||
@warnings << message
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :errors, :warnings
|
||||
|
||||
private
|
||||
|
||||
attr_reader :report_type, :report_data, :report_version
|
||||
|
|
|
|||
|
|
@ -226,13 +226,6 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def detect_content_type
|
||||
[
|
||||
Gitlab::Workhorse::DETECT_HEADER,
|
||||
'true'
|
||||
]
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This is the outermost encoding of a senddata: header. It is safe for
|
||||
|
|
|
|||
|
|
@ -323,7 +323,6 @@ RSpec.describe Projects::ArtifactsController do
|
|||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.headers['Gitlab-Workhorse-Detect-Content-Type']).to eq('true')
|
||||
expect(send_data).to start_with('artifacts-entry:')
|
||||
|
||||
expect(params.keys).to eq(%w(Archive Entry))
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
MOCK_USERNAME,
|
||||
MOCK_DEFAULT_SEARCH_OPTIONS,
|
||||
MOCK_SCOPED_SEARCH_OPTIONS,
|
||||
MOCK_SORTED_AUTOCOMPLETE_OPTIONS,
|
||||
} from '../mock_data';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
|
@ -108,6 +109,11 @@ describe('HeaderSearchApp', () => {
|
|||
search | showDefault | showScoped | showAutocomplete | showDropdownNavigation
|
||||
${null} | ${true} | ${false} | ${false} | ${true}
|
||||
${''} | ${true} | ${false} | ${false} | ${true}
|
||||
${'1'} | ${false} | ${false} | ${false} | ${false}
|
||||
${')'} | ${false} | ${false} | ${false} | ${false}
|
||||
${'t'} | ${false} | ${false} | ${true} | ${true}
|
||||
${'te'} | ${false} | ${true} | ${true} | ${true}
|
||||
${'tes'} | ${false} | ${true} | ${true} | ${true}
|
||||
${MOCK_SEARCH} | ${false} | ${true} | ${true} | ${true}
|
||||
`(
|
||||
'Header Search Dropdown Items',
|
||||
|
|
@ -115,7 +121,13 @@ describe('HeaderSearchApp', () => {
|
|||
describe(`when search is ${search}`, () => {
|
||||
beforeEach(() => {
|
||||
window.gon.current_username = MOCK_USERNAME;
|
||||
createComponent({ search });
|
||||
createComponent(
|
||||
{ search },
|
||||
{
|
||||
autocompleteGroupedSearchOptions: () =>
|
||||
search.match(/^[A-Za-z]+$/g) ? MOCK_SORTED_AUTOCOMPLETE_OPTIONS : [],
|
||||
},
|
||||
);
|
||||
findHeaderSearchInput().vm.$emit('click');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GlDropdownItem, GlLoadingIcon, GlAvatar, GlAlert } from '@gitlab/ui';
|
||||
import { GlDropdownItem, GlLoadingIcon, GlAvatar, GlAlert, GlDropdownDivider } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
|
|
@ -9,7 +9,14 @@ import {
|
|||
PROJECTS_CATEGORY,
|
||||
SMALL_AVATAR_PX,
|
||||
} from '~/header_search/constants';
|
||||
import { MOCK_GROUPED_AUTOCOMPLETE_OPTIONS, MOCK_SORTED_AUTOCOMPLETE_OPTIONS } from '../mock_data';
|
||||
import {
|
||||
MOCK_GROUPED_AUTOCOMPLETE_OPTIONS,
|
||||
MOCK_SORTED_AUTOCOMPLETE_OPTIONS,
|
||||
MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_SETTINGS_HELP,
|
||||
MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_HELP,
|
||||
MOCK_SEARCH,
|
||||
MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_2,
|
||||
} from '../mock_data';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
|
|
@ -41,6 +48,7 @@ describe('HeaderSearchAutocompleteItems', () => {
|
|||
});
|
||||
|
||||
const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
|
||||
const findGlDropdownDividers = () => wrapper.findAllComponents(GlDropdownDivider);
|
||||
const findFirstDropdownItem = () => findDropdownItems().at(0);
|
||||
const findDropdownItemTitles = () => findDropdownItems().wrappers.map((w) => w.text());
|
||||
const findDropdownItemLinks = () => findDropdownItems().wrappers.map((w) => w.attributes('href'));
|
||||
|
|
@ -140,6 +148,34 @@ describe('HeaderSearchAutocompleteItems', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
search | items | dividerCount
|
||||
${null} | ${[]} | ${0}
|
||||
${''} | ${[]} | ${0}
|
||||
${'1'} | ${[]} | ${0}
|
||||
${')'} | ${[]} | ${0}
|
||||
${'t'} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_SETTINGS_HELP} | ${1}
|
||||
${'te'} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_HELP} | ${0}
|
||||
${'tes'} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_2} | ${1}
|
||||
${MOCK_SEARCH} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_2} | ${1}
|
||||
`('Header Search Dropdown Dividers', ({ search, items, dividerCount }) => {
|
||||
describe(`when search is ${search}`, () => {
|
||||
beforeEach(() => {
|
||||
createComponent(
|
||||
{ search },
|
||||
{
|
||||
autocompleteGroupedSearchOptions: () => items,
|
||||
},
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
it(`component should have ${dividerCount} dividers`, () => {
|
||||
expect(findGlDropdownDividers()).toHaveLength(dividerCount);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('watchers', () => {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,21 @@
|
|||
import { GlDropdownItem } from '@gitlab/ui';
|
||||
import { GlDropdownItem, GlDropdownDivider } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import { trimText } from 'helpers/text_helper';
|
||||
import HeaderSearchScopedItems from '~/header_search/components/header_search_scoped_items.vue';
|
||||
import { MOCK_SEARCH, MOCK_SCOPED_SEARCH_OPTIONS } from '../mock_data';
|
||||
import {
|
||||
MOCK_SEARCH,
|
||||
MOCK_SCOPED_SEARCH_OPTIONS,
|
||||
MOCK_GROUPED_AUTOCOMPLETE_OPTIONS,
|
||||
} from '../mock_data';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
describe('HeaderSearchScopedItems', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (initialState, props) => {
|
||||
const createComponent = (initialState, mockGetters, props) => {
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
search: MOCK_SEARCH,
|
||||
|
|
@ -19,6 +23,8 @@ describe('HeaderSearchScopedItems', () => {
|
|||
},
|
||||
getters: {
|
||||
scopedSearchOptions: () => MOCK_SCOPED_SEARCH_OPTIONS,
|
||||
autocompleteGroupedSearchOptions: () => MOCK_GROUPED_AUTOCOMPLETE_OPTIONS,
|
||||
...mockGetters,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -35,6 +41,7 @@ describe('HeaderSearchScopedItems', () => {
|
|||
});
|
||||
|
||||
const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
|
||||
const findGlDropdownDivider = () => wrapper.findComponent(GlDropdownDivider);
|
||||
const findFirstDropdownItem = () => findDropdownItems().at(0);
|
||||
const findDropdownItemTitles = () => findDropdownItems().wrappers.map((w) => trimText(w.text()));
|
||||
const findDropdownItemAriaLabels = () =>
|
||||
|
|
@ -79,7 +86,7 @@ describe('HeaderSearchScopedItems', () => {
|
|||
`('isOptionFocused', ({ currentFocusedOption, isFocused, ariaSelected }) => {
|
||||
describe(`when currentFocusedOption.html_id is ${currentFocusedOption?.html_id}`, () => {
|
||||
beforeEach(() => {
|
||||
createComponent({}, { currentFocusedOption });
|
||||
createComponent({}, {}, { currentFocusedOption });
|
||||
});
|
||||
|
||||
it(`should${isFocused ? '' : ' not'} have gl-bg-gray-50 applied`, () => {
|
||||
|
|
@ -91,5 +98,21 @@ describe('HeaderSearchScopedItems', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
autosuggestResults | showDivider
|
||||
${[]} | ${false}
|
||||
${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS} | ${true}
|
||||
`('scoped search items', ({ autosuggestResults, showDivider }) => {
|
||||
describe(`when when we have ${autosuggestResults.length} auto-sugest results`, () => {
|
||||
beforeEach(() => {
|
||||
createComponent({}, { autocompleteGroupedSearchOptions: () => autosuggestResults }, {});
|
||||
});
|
||||
|
||||
it(`divider should${showDivider ? '' : ' not'} be shown`, () => {
|
||||
expect(findGlDropdownDivider().exists()).toBe(showDivider);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -226,3 +226,98 @@ export const MOCK_SORTED_AUTOCOMPLETE_OPTIONS = [
|
|||
url: 'help/gitlab',
|
||||
},
|
||||
];
|
||||
|
||||
export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_HELP = [
|
||||
{
|
||||
category: 'Help',
|
||||
data: [
|
||||
{
|
||||
html_id: 'autocomplete-Help-1',
|
||||
category: 'Help',
|
||||
label: 'Rake Tasks Help',
|
||||
url: '/help/raketasks/index',
|
||||
},
|
||||
{
|
||||
html_id: 'autocomplete-Help-2',
|
||||
category: 'Help',
|
||||
label: 'System Hooks Help',
|
||||
url: '/help/system_hooks/system_hooks',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_SETTINGS_HELP = [
|
||||
{
|
||||
category: 'Settings',
|
||||
data: [
|
||||
{
|
||||
html_id: 'autocomplete-Settings-0',
|
||||
category: 'Settings',
|
||||
label: 'User settings',
|
||||
url: '/-/profile',
|
||||
},
|
||||
{
|
||||
html_id: 'autocomplete-Settings-3',
|
||||
category: 'Settings',
|
||||
label: 'Admin Section',
|
||||
url: '/admin',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Help',
|
||||
data: [
|
||||
{
|
||||
html_id: 'autocomplete-Help-1',
|
||||
category: 'Help',
|
||||
label: 'Rake Tasks Help',
|
||||
url: '/help/raketasks/index',
|
||||
},
|
||||
{
|
||||
html_id: 'autocomplete-Help-2',
|
||||
category: 'Help',
|
||||
label: 'System Hooks Help',
|
||||
url: '/help/system_hooks/system_hooks',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_2 = [
|
||||
{
|
||||
category: 'Groups',
|
||||
data: [
|
||||
{
|
||||
html_id: 'autocomplete-Groups-0',
|
||||
category: 'Groups',
|
||||
id: 148,
|
||||
label: 'Jashkenas / Test Subgroup / test-subgroup',
|
||||
url: '/jashkenas/test-subgroup/test-subgroup',
|
||||
avatar_url: '',
|
||||
},
|
||||
{
|
||||
html_id: 'autocomplete-Groups-1',
|
||||
category: 'Groups',
|
||||
id: 147,
|
||||
label: 'Jashkenas / Test Subgroup',
|
||||
url: '/jashkenas/test-subgroup',
|
||||
avatar_url: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Projects',
|
||||
data: [
|
||||
{
|
||||
html_id: 'autocomplete-Projects-2',
|
||||
category: 'Projects',
|
||||
id: 1,
|
||||
value: 'Gitlab Test',
|
||||
label: 'Gitlab Org / Gitlab Test',
|
||||
url: '/gitlab-org/gitlab-test',
|
||||
avatar_url: '/uploads/-/system/project/avatar/1/icons8-gitlab-512.png',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import * as commonUtils from '~/lib/utils/common_utils';
|
|||
import statusCodes from '~/lib/utils/http_status';
|
||||
import { ENVIRONMENT_AVAILABLE_STATE } from '~/monitoring/constants';
|
||||
|
||||
import getAnnotations from '~/monitoring/queries/getAnnotations.query.graphql';
|
||||
import getDashboardValidationWarnings from '~/monitoring/queries/getDashboardValidationWarnings.query.graphql';
|
||||
import getEnvironments from '~/monitoring/queries/getEnvironments.query.graphql';
|
||||
import getAnnotations from '~/monitoring/queries/get_annotations.query.graphql';
|
||||
import getDashboardValidationWarnings from '~/monitoring/queries/get_dashboard_validation_warnings.query.graphql';
|
||||
import getEnvironments from '~/monitoring/queries/get_environments.query.graphql';
|
||||
import { createStore } from '~/monitoring/stores';
|
||||
import {
|
||||
setGettingStartedEmptyState,
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ import {
|
|||
SNIPPET_VISIBILITY_INTERNAL,
|
||||
SNIPPET_VISIBILITY_PUBLIC,
|
||||
} from '~/snippets/constants';
|
||||
import CreateSnippetMutation from '~/snippets/mutations/createSnippet.mutation.graphql';
|
||||
import UpdateSnippetMutation from '~/snippets/mutations/updateSnippet.mutation.graphql';
|
||||
import CreateSnippetMutation from '~/snippets/mutations/create_snippet.mutation.graphql';
|
||||
import UpdateSnippetMutation from '~/snippets/mutations/update_snippet.mutation.graphql';
|
||||
import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue';
|
||||
import TitleField from '~/vue_shared/components/form/title.vue';
|
||||
import { testEntries, createGQLSnippetsQueryResponse, createGQLSnippet } from '../test_utils';
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import waitForPromises from 'helpers/wait_for_promises';
|
|||
import { Blob, BinaryBlob } from 'jest/blob/components/mock_data';
|
||||
import { differenceInMilliseconds } from '~/lib/utils/datetime_utility';
|
||||
import SnippetHeader, { i18n } from '~/snippets/components/snippet_header.vue';
|
||||
import DeleteSnippetMutation from '~/snippets/mutations/deleteSnippet.mutation.graphql';
|
||||
import DeleteSnippetMutation from '~/snippets/mutations/delete_snippet.mutation.graphql';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import createFlash, { FLASH_TYPES } from '~/flash';
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
|
||||
let(:validator) { described_class.new(report_type, report_data, report_version) }
|
||||
|
||||
describe 'SUPPORTED_VERSIONS' do
|
||||
schema_path = Rails.root.join("lib", "gitlab", "ci", "parsers", "security", "validators", "schemas")
|
||||
|
||||
|
|
@ -47,48 +49,484 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
|
|||
end
|
||||
end
|
||||
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
describe '#valid?' do
|
||||
subject { validator.valid? }
|
||||
|
||||
where(:report_type, :report_version, :expected_errors, :valid_data) do
|
||||
'sast' | '10.0.0' | ['root is missing required keys: vulnerabilities'] | { 'version' => '10.0.0', 'vulnerabilities' => [] }
|
||||
:sast | '10.0.0' | ['root is missing required keys: vulnerabilities'] | { 'version' => '10.0.0', 'vulnerabilities' => [] }
|
||||
:secret_detection | '10.0.0' | ['root is missing required keys: vulnerabilities'] | { 'version' => '10.0.0', 'vulnerabilities' => [] }
|
||||
end
|
||||
context 'when given a supported schema version' do
|
||||
let(:report_type) { :dast }
|
||||
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
|
||||
|
||||
with_them do
|
||||
let(:validator) { described_class.new(report_type, report_data, report_version) }
|
||||
|
||||
describe '#valid?' do
|
||||
subject { validator.valid? }
|
||||
|
||||
context 'when given data is invalid according to the schema' do
|
||||
let(:report_data) { {} }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'when given data is valid according to the schema' do
|
||||
let(:report_data) { valid_data }
|
||||
context 'and the report is valid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version,
|
||||
'vulnerabilities' => []
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when no report_version is provided' do
|
||||
let(:report_version) { nil }
|
||||
let(:report_data) { valid_data }
|
||||
context 'and the report is invalid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version
|
||||
}
|
||||
end
|
||||
|
||||
it 'does not fail' do
|
||||
expect { subject }.not_to raise_error
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given a deprecated schema version' do
|
||||
let(:report_type) { :dast }
|
||||
let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
|
||||
|
||||
context 'and the report passes schema validation' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => '10.0.0',
|
||||
'vulnerabilities' => []
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'and the report does not pass schema validation' do
|
||||
context 'and enforce_security_report_validation is enabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_security_report_validation: true)
|
||||
end
|
||||
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => 'V2.7.0'
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'and enforce_security_report_validation is disabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_security_report_validation: false)
|
||||
end
|
||||
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => 'V2.7.0'
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#errors' do
|
||||
let(:report_data) { { 'version' => '10.0.0' } }
|
||||
context 'when given an unsupported schema version' do
|
||||
let(:report_type) { :dast }
|
||||
let(:report_version) { "12.37.0" }
|
||||
|
||||
subject { validator.errors }
|
||||
context 'if enforce_security_report_validation is enabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_security_report_validation: true)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(expected_errors) }
|
||||
context 'and the report is valid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version,
|
||||
'vulnerabilities' => []
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'and the report is invalid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
context 'if enforce_security_report_validation is disabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_security_report_validation: false)
|
||||
end
|
||||
|
||||
context 'and the report is valid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version,
|
||||
'vulnerabilities' => []
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'and the report is invalid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#errors' do
|
||||
subject { validator.errors }
|
||||
|
||||
context 'when given a supported schema version' do
|
||||
let(:report_type) { :dast }
|
||||
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
|
||||
|
||||
context 'and the report is valid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version,
|
||||
'vulnerabilities' => []
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_errors) { [] }
|
||||
|
||||
it { is_expected.to match_array(expected_errors) }
|
||||
end
|
||||
|
||||
context 'and the report is invalid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_errors) do
|
||||
[
|
||||
'root is missing required keys: vulnerabilities'
|
||||
]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(expected_errors) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given a deprecated schema version' do
|
||||
let(:report_type) { :dast }
|
||||
let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
|
||||
|
||||
context 'and the report passes schema validation' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => '10.0.0',
|
||||
'vulnerabilities' => []
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_errors) { [] }
|
||||
|
||||
it { is_expected.to match_array(expected_errors) }
|
||||
end
|
||||
|
||||
context 'and the report does not pass schema validation' do
|
||||
context 'and enforce_security_report_validation is enabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_security_report_validation: true)
|
||||
end
|
||||
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => 'V2.7.0'
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_errors) do
|
||||
[
|
||||
"property '/version' does not match pattern: ^[0-9]+\\.[0-9]+\\.[0-9]+$",
|
||||
"root is missing required keys: vulnerabilities"
|
||||
]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(expected_errors) }
|
||||
end
|
||||
|
||||
context 'and enforce_security_report_validation is disabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_security_report_validation: false)
|
||||
end
|
||||
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => 'V2.7.0'
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_errors) { [] }
|
||||
|
||||
it { is_expected.to match_array(expected_errors) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given an unsupported schema version' do
|
||||
let(:report_type) { :dast }
|
||||
let(:report_version) { "12.37.0" }
|
||||
|
||||
context 'if enforce_security_report_validation is enabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_security_report_validation: true)
|
||||
end
|
||||
|
||||
context 'and the report is valid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version,
|
||||
'vulnerabilities' => []
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_errors) do
|
||||
[
|
||||
"Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0"
|
||||
]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(expected_errors) }
|
||||
end
|
||||
|
||||
context 'and the report is invalid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_errors) do
|
||||
[
|
||||
"Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0",
|
||||
"root is missing required keys: vulnerabilities"
|
||||
]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(expected_errors) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'if enforce_security_report_validation is disabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_security_report_validation: false)
|
||||
end
|
||||
|
||||
context 'and the report is valid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version,
|
||||
'vulnerabilities' => []
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_errors) { [] }
|
||||
|
||||
it { is_expected.to match_array(expected_errors) }
|
||||
end
|
||||
|
||||
context 'and the report is invalid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_errors) { [] }
|
||||
|
||||
it { is_expected.to match_array(expected_errors) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#warnings' do
|
||||
subject { validator.warnings }
|
||||
|
||||
context 'when given a supported schema version' do
|
||||
let(:report_type) { :dast }
|
||||
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
|
||||
|
||||
context 'and the report is valid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version,
|
||||
'vulnerabilities' => []
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_warnings) { [] }
|
||||
|
||||
it { is_expected.to match_array(expected_warnings) }
|
||||
end
|
||||
|
||||
context 'and the report is invalid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_warnings) { [] }
|
||||
|
||||
it { is_expected.to match_array(expected_warnings) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given a deprecated schema version' do
|
||||
let(:report_type) { :dast }
|
||||
let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
|
||||
|
||||
context 'and the report passes schema validation' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'vulnerabilities' => []
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_warnings) do
|
||||
[
|
||||
"Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0"
|
||||
]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(expected_warnings) }
|
||||
end
|
||||
|
||||
context 'and the report does not pass schema validation' do
|
||||
context 'and enforce_security_report_validation is enabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_security_report_validation: true)
|
||||
end
|
||||
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => 'V2.7.0'
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_warnings) do
|
||||
[
|
||||
"Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0"
|
||||
]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(expected_warnings) }
|
||||
end
|
||||
|
||||
context 'and enforce_security_report_validation is disabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_security_report_validation: false)
|
||||
end
|
||||
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => 'V2.7.0'
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_warnings) do
|
||||
[
|
||||
"Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0",
|
||||
"property '/version' does not match pattern: ^[0-9]+\\.[0-9]+\\.[0-9]+$",
|
||||
"root is missing required keys: vulnerabilities"
|
||||
]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(expected_warnings) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given an unsupported schema version' do
|
||||
let(:report_type) { :dast }
|
||||
let(:report_version) { "12.37.0" }
|
||||
|
||||
context 'if enforce_security_report_validation is enabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_security_report_validation: true)
|
||||
end
|
||||
|
||||
context 'and the report is valid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version,
|
||||
'vulnerabilities' => []
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_warnings) { [] }
|
||||
|
||||
it { is_expected.to match_array(expected_warnings) }
|
||||
end
|
||||
|
||||
context 'and the report is invalid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_warnings) { [] }
|
||||
|
||||
it { is_expected.to match_array(expected_warnings) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'if enforce_security_report_validation is disabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_security_report_validation: false)
|
||||
end
|
||||
|
||||
context 'and the report is valid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version,
|
||||
'vulnerabilities' => []
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_warnings) do
|
||||
[
|
||||
"Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0"
|
||||
]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(expected_warnings) }
|
||||
end
|
||||
|
||||
context 'and the report is invalid' do
|
||||
let(:report_data) do
|
||||
{
|
||||
'version' => report_version
|
||||
}
|
||||
end
|
||||
|
||||
let(:expected_warnings) do
|
||||
[
|
||||
"Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0",
|
||||
"root is missing required keys: vulnerabilities"
|
||||
]
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(expected_warnings) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -448,14 +448,6 @@ RSpec.describe Gitlab::Workhorse do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.detect_content_type' do
|
||||
subject { described_class.detect_content_type }
|
||||
|
||||
it 'returns array setting detect content type in workhorse' do
|
||||
expect(subject).to eq(%w[Gitlab-Workhorse-Detect-Content-Type true])
|
||||
end
|
||||
end
|
||||
|
||||
describe '.send_git_blob' do
|
||||
include FakeBlobHelpers
|
||||
|
||||
|
|
|
|||
|
|
@ -2235,13 +2235,5 @@ RSpec.describe Namespace do
|
|||
let(:object) { build(:namespace) }
|
||||
|
||||
it_behaves_like 'blocks unsafe serialization'
|
||||
|
||||
context 'when feature flag block_namespace_serialization is disabled' do
|
||||
before do
|
||||
stub_feature_flags(block_namespace_serialization: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'allows unsafe serialization'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -556,8 +556,7 @@ RSpec.describe API::Ci::JobArtifacts do
|
|||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.headers.to_h)
|
||||
.to include('Content-Type' => 'application/json',
|
||||
'Gitlab-Workhorse-Send-Data' => /artifacts-entry/,
|
||||
'Gitlab-Workhorse-Detect-Content-Type' => 'true')
|
||||
'Gitlab-Workhorse-Send-Data' => /artifacts-entry/)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -627,8 +626,7 @@ RSpec.describe API::Ci::JobArtifacts do
|
|||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.headers.to_h)
|
||||
.to include('Content-Type' => 'application/json',
|
||||
'Gitlab-Workhorse-Send-Data' => /artifacts-entry/,
|
||||
'Gitlab-Workhorse-Detect-Content-Type' => 'true')
|
||||
'Gitlab-Workhorse-Send-Data' => /artifacts-entry/)
|
||||
expect(response.parsed_body).to be_empty
|
||||
end
|
||||
end
|
||||
|
|
@ -646,8 +644,7 @@ RSpec.describe API::Ci::JobArtifacts do
|
|||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.headers.to_h)
|
||||
.to include('Content-Type' => 'application/json',
|
||||
'Gitlab-Workhorse-Send-Data' => /artifacts-entry/,
|
||||
'Gitlab-Workhorse-Detect-Content-Type' => 'true')
|
||||
'Gitlab-Workhorse-Send-Data' => /artifacts-entry/)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -204,6 +204,46 @@ RSpec.shared_examples 'a container registry auth service' do
|
|||
it_behaves_like 'not a container repository factory'
|
||||
end
|
||||
|
||||
describe '.pull_nested_repositories_access_token' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
let(:token) { described_class.pull_nested_repositories_access_token(project.full_path) }
|
||||
let(:access) do
|
||||
[
|
||||
{
|
||||
'type' => 'repository',
|
||||
'name' => project.full_path,
|
||||
'actions' => ['pull']
|
||||
},
|
||||
{
|
||||
'type' => 'repository',
|
||||
'name' => "#{project.full_path}/*",
|
||||
'actions' => ['pull']
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
subject { { token: token } }
|
||||
|
||||
it 'has the correct scope' do
|
||||
expect(payload).to include('access' => access)
|
||||
end
|
||||
|
||||
it_behaves_like 'a valid token'
|
||||
it_behaves_like 'not a container repository factory'
|
||||
|
||||
context 'with path ending with a slash' do
|
||||
let(:token) { described_class.pull_nested_repositories_access_token("#{project.full_path}/") }
|
||||
|
||||
it 'has the correct scope' do
|
||||
expect(payload).to include('access' => access)
|
||||
end
|
||||
|
||||
it_behaves_like 'a valid token'
|
||||
it_behaves_like 'not a container repository factory'
|
||||
end
|
||||
end
|
||||
|
||||
context 'user authorization' do
|
||||
let_it_be(:current_user) { create(:user) }
|
||||
|
||||
|
|
|
|||
|
|
@ -4,27 +4,22 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe 'groups/runners/sort_dropdown.html.haml' do
|
||||
describe 'render' do
|
||||
let_it_be(:sort_options_hash) { { by_title: 'Title' } }
|
||||
let_it_be(:sort_title_created_date) { 'Created date' }
|
||||
|
||||
before do
|
||||
allow(view).to receive(:sort).and_return('by_title')
|
||||
end
|
||||
|
||||
describe 'when a sort option is not selected' do
|
||||
it 'renders a default sort option' do
|
||||
render 'groups/runners/sort_dropdown', sort_options_hash: sort_options_hash, sort_title_created_date: sort_title_created_date
|
||||
render 'groups/runners/sort_dropdown'
|
||||
|
||||
expect(rendered).to have_content 'Created date'
|
||||
expect(rendered).to have_content _('Created date')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when a sort option is selected' do
|
||||
it 'renders the selected sort option' do
|
||||
@sort = :by_title
|
||||
render 'groups/runners/sort_dropdown', sort_options_hash: sort_options_hash, sort_title_created_date: sort_title_created_date
|
||||
before do
|
||||
assign(:sort, 'contacted_asc')
|
||||
render 'groups/runners/sort_dropdown'
|
||||
end
|
||||
|
||||
expect(rendered).to have_content 'Title'
|
||||
it 'renders the selected sort option' do
|
||||
expect(rendered).to have_content _('Last Contact')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue