Commit b2442147 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents e37d80e2 762e049d
......@@ -447,6 +447,10 @@ class Issue < ApplicationRecord
issue_email_participants.pluck(IssueEmailParticipant.arel_table[:email].lower)
end
def issue_assignee_user_ids
issue_assignees.pluck(:user_id)
end
private
# Ensure that the metrics association is safely created and respecting the unique constraint on issue_id
......
......@@ -58,8 +58,10 @@ export default {
targetUrl: {
field: 'targetUrl',
label: s__('APIFuzzing|Target URL'),
description: s__('APIFuzzing|Base URL of API fuzzing target.'),
placeholder: __('Ex: Example.com'),
description: s__(
'APIFuzzing|Base URL of API testing target. For example, http://www.example.com.',
),
placeholder: __('http://www.example.com'),
value: '',
},
scanMode: {
......@@ -84,9 +86,9 @@ export default {
field: 'username',
label: s__('APIFuzzing|Username for basic authentication'),
description: s__(
'APIFuzzing|Instead of entering the username directly, enter the key of the CI variable set to the username.',
'APIFuzzing|Enter the name of the variable containing the username. For example, $VariableWithUsername.',
),
placeholder: s__('APIFuzzing|Ex: $TestUsername'),
placeholder: s__('APIFuzzing|$VariableWithUsername'),
value: '',
},
{
......@@ -94,18 +96,18 @@ export default {
field: 'password',
label: s__('APIFuzzing|Password for basic authentication'),
description: s__(
'APIFuzzing|Instead of entering the password directly, enter the key of the CI variable set to the password.',
'APIFuzzing|Enter the name of the variable containing the password. For example, $VariableWithPassword.',
),
placeholder: s__('APIFuzzing|Ex: $TestPassword'),
placeholder: s__('APIFuzzing|$VariableWithPassword'),
value: '',
},
],
scanProfile: {
field: 'scanProfile',
label: s__('APIFuzzing|Scan profile'),
description: 'Pre-defined profiles by GitLab.',
value: '',
defaultText: s__('APIFuzzing|Choose a profile'),
sectionHeader: s__('APIFuzzing|Predefined profiles'),
options: this.apiFuzzingCiConfiguration.scanProfiles.map(
({ name: value, description: text }) => ({
value,
......@@ -227,7 +229,7 @@ export default {
<gl-sprintf
:message="
s__(
'APIFuzzing|Authentication is handled by providing HTTP basic authentication token as a header or cookie. %{linkStart}More information%{linkEnd}.',
'APIFuzzing|Configure HTTP basic authentication values. Other authentication methods are supported. %{linkStart}Learn more%{linkEnd}.',
)
"
>
......
......@@ -10,11 +10,11 @@ export const SCAN_MODES = {
),
},
OPENAPI: {
scanModeLabel: __('Open API'),
label: __('Open API specification file path'),
placeholder: s__('APIFuzzing|Ex: Project_Test/File/example_fuzz.json'),
scanModeLabel: __('OpenAPI'),
label: __('OpenAPI specification file path'),
placeholder: s__('APIFuzzing|/folder/example_file.json'),
description: s__(
'APIFuzzing|We recommend that you review the JSON specifications file before adding it to a repository.',
'APIFuzzing|File path containing APIs to be tested. For example, /folder/example_file.json.',
),
},
POSTMAN: {
......
<script>
import { GlFormGroup, GlDropdown, GlDropdownItem, GlFormText, GlLink, GlSprintf } from '@gitlab/ui';
import {
GlFormGroup,
GlDropdown,
GlDropdownSectionHeader,
GlDropdownItem,
GlFormText,
GlLink,
GlSprintf,
} from '@gitlab/ui';
import { CUSTOM_VALUE_MESSAGE } from './constants';
export default {
components: {
GlFormGroup,
GlDropdown,
GlDropdownSectionHeader,
GlDropdownItem,
GlFormText,
GlLink,
......@@ -43,13 +52,19 @@ export default {
},
description: {
type: String,
required: true,
required: false,
default: '',
},
disabled: {
type: Boolean,
required: false,
default: false,
},
sectionHeader: {
type: String,
required: false,
default: '',
},
options: {
type: Array,
required: true,
......@@ -83,10 +98,17 @@ export default {
<gl-form-group :label-for="field">
<template #label>
{{ label }}
<gl-form-text class="gl-mt-3">{{ description }}</gl-form-text>
<gl-form-text v-if="description" class="gl-mt-3" data-testid="dropdown-input-description">{{
description
}}</gl-form-text>
</template>
<gl-dropdown :id="field" :text="text" :disabled="disabled">
<gl-dropdown-section-header
v-if="sectionHeader"
data-testid="dropdown-input-section-header"
>{{ sectionHeader }}</gl-dropdown-section-header
>
<gl-dropdown-item v-for="option in options" :key="option.value" @click="handleInput(option)">
{{ option.text }}
</gl-dropdown-item>
......
---
title: Update copies in API fuzzing configuration form
merge_request: 57753
author:
type: changed
---
title: Remove N+1 DB queries from indexing issues in Elasticsearch
merge_request: 57788
author:
type: performance
......@@ -28,6 +28,12 @@ module Elastic
search(query_hash, options)
end
# rubocop: disable CodeReuse/ActiveRecord
def preload_indexing_data(relation)
relation.includes(:issue_assignees, project: [:project_feature])
end
# rubocop: enable CodeReuse/ActiveRecord
private
# Builds an elasticsearch query that will select documents from a
......
......@@ -12,7 +12,11 @@ module Elastic
data[attr.to_s] = safely_read_attribute_for_elasticsearch(attr)
end
data['assignee_id'] = safely_read_attribute_for_elasticsearch(:assignee_ids)
# Load them through the issue_assignees table since calling
# assignee_ids can't be easily preloaded and does
# unnecessary joins
data['assignee_id'] = safely_read_attribute_for_elasticsearch(:issue_assignee_user_ids)
data['visibility_level'] = target.project.visibility_level
data['issues_access_level'] = safely_read_project_feature_for_elasticsearch(:issues)
......
......@@ -169,11 +169,11 @@ describe('EE - ApiFuzzingConfigurationForm', () => {
});
it('displays a dropdown option for each scan profile', () => {
findScanProfileDropdownInput()
.findAll('li')
.wrappers.forEach((item, index) => {
expect(item.text()).toBe(apiFuzzingCiConfiguration.scanProfiles[index].description);
});
const dropdownItems = findScanProfileDropdownInput().findAll('li').wrappers;
dropdownItems.shift(); // Skip section header
dropdownItems.forEach((item, index) => {
expect(item.text()).toBe(apiFuzzingCiConfiguration.scanProfiles[index].description);
});
});
it('by default, YAML viewer is not visible', () => {
......
import { GlDropdown, GlLink } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import GlDropdownInput from 'ee/security_configuration/components/dropdown_input.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
describe('DropdownInput component', () => {
let wrapper;
......@@ -26,17 +27,21 @@ describe('DropdownInput component', () => {
const newValue = 'foo';
const createComponent = ({ props = {} } = {}) => {
wrapper = mount(GlDropdownInput, {
propsData: {
...props,
},
});
wrapper = extendedWrapper(
mount(GlDropdownInput, {
propsData: {
...props,
},
}),
);
};
const findToggle = () => wrapper.find('button');
const findLabel = () => wrapper.find('label');
const findDescription = () => wrapper.findByTestId('dropdown-input-description');
const findInputComponent = () => wrapper.find(GlDropdown);
const findRestoreDefaultLink = () => wrapper.find(GlLink);
const findSectionHeader = () => wrapper.findByTestId('dropdown-input-section-header');
afterEach(() => {
wrapper.destroy();
......@@ -44,18 +49,55 @@ describe('DropdownInput component', () => {
});
describe('label', () => {
beforeEach(() => {
describe('with a description', () => {
beforeEach(() => {
createComponent({
props: testProps,
});
});
it('renders the label', () => {
expect(findLabel().text()).toContain(testProps.label);
});
it('renders the description', () => {
const description = findDescription();
expect(description.exists()).toBe(true);
expect(description.text()).toBe(testProps.description);
});
});
describe('without a description', () => {
beforeEach(() => {
createComponent({
props: { ...testProps, description: '' },
});
});
it('does not render the description', () => {
expect(findDescription().exists()).toBe(false);
});
});
});
describe('section header', () => {
it('does not render a section header by default', () => {
createComponent({
props: testProps,
});
});
it('renders the label', () => {
expect(findLabel().text()).toContain(testProps.label);
expect(findSectionHeader().exists()).toBe(false);
});
it('renders the description', () => {
expect(findLabel().text()).toContain(testProps.description);
it('renders a section header when passed a sectionHeader prop', () => {
const sectionHeader = 'Section header';
createComponent({
props: { ...testProps, sectionHeader },
});
expect(findSectionHeader().exists()).toBe(true);
expect(findSectionHeader().text()).toBe(sectionHeader);
});
});
......
......@@ -221,7 +221,7 @@ RSpec.describe Elastic::ProcessBookkeepingService, :clean_gitlab_redis_shared_st
described_class.track!(*notes)
control = ActiveRecord::QueryRecorder.new { described_class.new.execute }
control = ActiveRecord::QueryRecorder.new(skip_cached: false) { described_class.new.execute }
3.times do
notes << create(:note)
......@@ -234,6 +234,20 @@ RSpec.describe Elastic::ProcessBookkeepingService, :clean_gitlab_redis_shared_st
expect { described_class.new.execute }.not_to exceed_all_query_limit(control)
end
it 'does not have N+1 queries for issues' do
issues = create_list(:issue, 2)
described_class.track!(*issues)
control = ActiveRecord::QueryRecorder.new(skip_cached: false) { described_class.new.execute }
issues += create_list(:issue, 3)
described_class.track!(*issues)
expect { described_class.new.execute }.not_to exceed_all_query_limit(control)
end
end
def expect_processing(*refs, failures: [])
......
......@@ -1470,13 +1470,19 @@ msgstr ""
msgid "API version"
msgstr ""
msgid "APIFuzzing|API Fuzzing Configuration"
msgid "APIFuzzing|$VariableWithPassword"
msgstr ""
msgid "APIFuzzing|$VariableWithUsername"
msgstr ""
msgid "APIFuzzing|Authentication is handled by providing HTTP basic authentication token as a header or cookie. %{linkStart}More information%{linkEnd}."
msgid "APIFuzzing|/folder/example_file.json"
msgstr ""
msgid "APIFuzzing|Base URL of API fuzzing target."
msgid "APIFuzzing|API Fuzzing Configuration"
msgstr ""
msgid "APIFuzzing|Base URL of API testing target. For example, http://www.example.com."
msgstr ""
msgid "APIFuzzing|Choose a method"
......@@ -1488,6 +1494,9 @@ msgstr ""
msgid "APIFuzzing|Code snippet for the API Fuzzing configuration"
msgstr ""
msgid "APIFuzzing|Configure HTTP basic authentication values. Other authentication methods are supported. %{linkStart}Learn more%{linkEnd}."
msgstr ""
msgid "APIFuzzing|Copy code and open .gitlab-ci.yml file"
msgstr ""
......@@ -1500,10 +1509,10 @@ msgstr ""
msgid "APIFuzzing|Enable authentication"
msgstr ""
msgid "APIFuzzing|Ex: $TestPassword"
msgid "APIFuzzing|Enter the name of the variable containing the password. For example, $VariableWithPassword."
msgstr ""
msgid "APIFuzzing|Ex: $TestUsername"
msgid "APIFuzzing|Enter the name of the variable containing the username. For example, $VariableWithUsername."
msgstr ""
msgid "APIFuzzing|Ex: Project_Test/File/example_fuzz"
......@@ -1512,7 +1521,7 @@ msgstr ""
msgid "APIFuzzing|Ex: Project_Test/File/example_fuzz.har"
msgstr ""
msgid "APIFuzzing|Ex: Project_Test/File/example_fuzz.json"
msgid "APIFuzzing|File path containing APIs to be tested. For example, /folder/example_file.json."
msgstr ""
msgid "APIFuzzing|Generate code snippet"
......@@ -1521,12 +1530,6 @@ msgstr ""
msgid "APIFuzzing|HAR files may contain sensitive information such as authentication tokens, API keys, and session cookies. We recommend that you review the HAR files' contents before adding them to a repository."
msgstr ""
msgid "APIFuzzing|Instead of entering the password directly, enter the key of the CI variable set to the password."
msgstr ""
msgid "APIFuzzing|Instead of entering the username directly, enter the key of the CI variable set to the username."
msgstr ""
msgid "APIFuzzing|Make sure your credentials are secured"
msgstr ""
......@@ -1536,6 +1539,9 @@ msgstr ""
msgid "APIFuzzing|Postman collections are a group of saved requests you can organize into folders."
msgstr ""
msgid "APIFuzzing|Predefined profiles"
msgstr ""
msgid "APIFuzzing|Scan mode"
msgstr ""
......@@ -1566,9 +1572,6 @@ msgstr ""
msgid "APIFuzzing|Username for basic authentication"
msgstr ""
msgid "APIFuzzing|We recommend that you review the JSON specifications file before adding it to a repository."
msgstr ""
msgid "APIFuzzing|You may need a maintainer's help to secure your credentials."
msgstr ""
......@@ -12501,9 +12504,6 @@ msgstr ""
msgid "Evidence collection"
msgstr ""
msgid "Ex: Example.com"
msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
......@@ -21698,12 +21698,6 @@ msgstr ""
msgid "Open"
msgstr ""
msgid "Open API"
msgstr ""
msgid "Open API specification file path"
msgstr ""
msgid "Open Selection"
msgstr ""
......@@ -21734,6 +21728,12 @@ msgstr ""
msgid "Open: %{open}"
msgstr ""
msgid "OpenAPI"
msgstr ""
msgid "OpenAPI specification file path"
msgstr ""
msgid "Opened"
msgstr ""
......@@ -36107,6 +36107,9 @@ msgstr ""
msgid "http:"
msgstr ""
msgid "http://www.example.com"
msgstr ""
msgid "https://your-bitbucket-server"
msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment