diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-31 09:08:58 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-31 09:08:58 +0300 |
commit | 4fbd3e553515d506bd82b42e1ffbb516cf0f178a (patch) | |
tree | e4fe69014d6a660a63e2b31928b5f7fd20615da6 | |
parent | 89cd4b410196971a1259463e6d1121ba85d45a6f (diff) |
Add latest changes from gitlab-org/gitlab@master
17 files changed, 140 insertions, 68 deletions
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue index c0e13d665c3..5ffaebae6fd 100644 --- a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue +++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue @@ -223,6 +223,10 @@ export default { testAlertModal() { return this.isFormDirty ? testAlertModalId : null; }, + prometheusUrlInvalidFeedback() { + const { blankUrlError, invalidUrlError } = i18n.integrationFormSteps.prometheusFormUrl; + return this.integrationForm.apiUrl?.length ? invalidUrlError : blankUrlError; + }, }, watch: { tabIndex(val) { @@ -288,6 +292,9 @@ export default { if (this.isHttp) { this.validationState.apiUrl = true; this.validateName(); + if (!this.validationState.name) { + this.$refs.integrationName.$el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } } else if (this.isPrometheus) { this.validationState.name = true; this.validateApiUrl(); @@ -300,6 +307,11 @@ export default { this.$emit('save-and-test-alert-payload', this.dataForSave, this.testAlertPayload); }, submit(testAfterSubmit = false) { + this.triggerValidation(); + + if (!this.isFormValid) { + return; + } const event = this.currentIntegration ? 'update-integration' : 'create-new-integration'; this.$emit(event, this.dataForSave, testAfterSubmit); }, @@ -412,7 +424,6 @@ export default { :disabled="isSelectDisabled" class="gl-max-w-full" :options="integrationTypesOptions" - @change="triggerValidation" /> <alert-settings-form-help-block @@ -439,6 +450,7 @@ export default { > <gl-form-input id="name-integration" + ref="integrationName" v-model="integrationForm.name" type="text" :placeholder="$options.i18n.integrationFormSteps.nameIntegration.placeholder" @@ -473,7 +485,7 @@ export default { class="gl-my-4" :label="$options.i18n.integrationFormSteps.prometheusFormUrl.label" label-for="api-url" - :invalid-feedback="$options.i18n.integrationFormSteps.prometheusFormUrl.error" + :invalid-feedback="prometheusUrlInvalidFeedback" :state="validationState.apiUrl" > <gl-form-input diff --git a/app/assets/javascripts/alerts_settings/constants.js b/app/assets/javascripts/alerts_settings/constants.js index d64ac55ff98..c73804d1722 100644 --- a/app/assets/javascripts/alerts_settings/constants.js +++ b/app/assets/javascripts/alerts_settings/constants.js @@ -70,7 +70,8 @@ export const i18n = { prometheusFormUrl: { label: s__('AlertSettings|Prometheus API base URL'), help: s__('AlertSettings|URL cannot be blank and must start with http or https'), - error: s__('AlertSettings|URL is invalid.'), + blankUrlError: __('URL cannot be blank'), + invalidUrlError: __('URL is invalid'), }, restKeyInfo: { label: s__( diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue index 6d928337396..c6836efc32d 100644 --- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue +++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue @@ -69,6 +69,7 @@ export default { <board-editable-item ref="sidebarItem" class="board-sidebar-due-date" + data-testid="sidebar-due-date" :title="$options.i18n.dueDate" :loading="loading" @open="openDatePicker" diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue index 829f1c72806..cce3172d985 100644 --- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue +++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue @@ -113,7 +113,8 @@ export default { ref="sidebarItem" :title="$options.i18n.milestone" :loading="loading" - @open="handleOpen()" + data-testid="sidebar-milestones" + @open="handleOpen" @close="handleClose" > <template v-if="hasMilestone" #collapsed> diff --git a/app/models/milestone.rb b/app/models/milestone.rb index aa4ddfede99..4cf0e423a15 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -89,6 +89,10 @@ class Milestone < ApplicationRecord .order(:project_id, :group_id, :due_date).select('DISTINCT ON (project_id, group_id) id') end + def self.with_web_entity_associations + preload(:group, project: [:project_feature, group: [:parent], namespace: :route]) + end + def participants User.joins(assigned_issues: :milestone).where("milestones.id = ?", id).distinct end diff --git a/app/presenters/search_service_presenter.rb b/app/presenters/search_service_presenter.rb index a5e6ffd55b3..ca5c3094509 100644 --- a/app/presenters/search_service_presenter.rb +++ b/app/presenters/search_service_presenter.rb @@ -10,7 +10,8 @@ class SearchServicePresenter < Gitlab::View::Presenter::Delegated issues: :with_web_entity_associations, merge_requests: :with_web_entity_associations, epics: :with_web_entity_associations, - notes: :with_web_entity_associations + notes: :with_web_entity_associations, + milestones: :with_web_entity_associations }.freeze SORT_ENABLED_SCOPES = %w(issues merge_requests).freeze diff --git a/changelogs/unreleased/21044-controller-profiles-slackscontroller-edit-executes-more-than-100-sq.yml b/changelogs/unreleased/21044-controller-profiles-slackscontroller-edit-executes-more-than-100-sq.yml new file mode 100644 index 00000000000..0d7848d58b3 --- /dev/null +++ b/changelogs/unreleased/21044-controller-profiles-slackscontroller-edit-executes-more-than-100-sq.yml @@ -0,0 +1,5 @@ +--- +title: Reduce number of SQL queries in Profiles::SlacksController#edit +merge_request: 57674 +author: +type: performance diff --git a/changelogs/unreleased/227383-fix-search-n-plus-1.yml b/changelogs/unreleased/227383-fix-search-n-plus-1.yml new file mode 100644 index 00000000000..a06d4df1460 --- /dev/null +++ b/changelogs/unreleased/227383-fix-search-n-plus-1.yml @@ -0,0 +1,5 @@ +--- +title: Fix N+1 for searching milestone scope +merge_request: 57715 +author: +type: performance diff --git a/changelogs/unreleased/325820-update-validation-trigger.yml b/changelogs/unreleased/325820-update-validation-trigger.yml new file mode 100644 index 00000000000..1b5905cb4f6 --- /dev/null +++ b/changelogs/unreleased/325820-update-validation-trigger.yml @@ -0,0 +1,5 @@ +--- +title: Update validation trigger flow on the alerts integration form +merge_request: 57697 +author: +type: changed diff --git a/doc/user/application_security/sast/analyzers.md b/doc/user/application_security/sast/analyzers.md index 83f85951388..12d61a4921c 100644 --- a/doc/user/application_security/sast/analyzers.md +++ b/doc/user/application_security/sast/analyzers.md @@ -43,6 +43,18 @@ dedicated containers for each analysis. SAST is pre-configured with a set of **default images** that are maintained by GitLab, but users can also integrate their own **custom images**. +## SAST analyzer features + +For an analyzer to be considered Generally Available, it is expected to minimally +support the following features: + +- [Customizable configuration](index.md#available-variables) +- [Customizable rulesets](index.md#customize-rulesets) +- [Scan projects](index.md#supported-languages-and-frameworks) +- [Multi-project support](index.md#multi-project-support) +- [Offline support](index.md#running-sast-in-an-offline-environment) +- [Emits JSON report format](index.md#reports-json-format) + ## Official default analyzers Any custom change to the official analyzers can be achieved by using a diff --git a/doc/user/project/issues/related_issues.md b/doc/user/project/issues/related_issues.md index 91c26d49532..43939da4c4a 100644 --- a/doc/user/project/issues/related_issues.md +++ b/doc/user/project/issues/related_issues.md @@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Related issues **(FREE)** -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1797) in [GitLab Starter](https://about.gitlab.com/pricing/) 9.4. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1797) in GitLab 9.4. > - The simple "relates to" relationship [moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212329) to [GitLab Free](https://about.gitlab.com/pricing/) in 13.4. Related issues are a bi-directional relationship between any two issues @@ -16,8 +16,8 @@ and projects. You can set any issue as: - Related to another issue -- Blocking another issue **(STARTER)** -- Blocked by another issue **(STARTER)** +- Blocking another issue **(PREMIUM)** +- Blocked by another issue **(PREMIUM)** The relationship only shows up in the UI if the user can see both issues. @@ -28,28 +28,30 @@ To manage related issues through our API, visit the [issue links API documentati ## Adding a related issue -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2035) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.8. -> - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/34239) to warn when attempting to close an issue that is blocked by others in [GitLab Starter](https://about.gitlab.com/pricing/) 13.0. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2035) in GitLab 12.8. +> - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/34239) to warn when attempting to close an issue that is blocked by others in GitLab 13.0. > When you try to close an issue with open blockers, you see a warning that you can dismiss. 1. Relate one issue to another by clicking the related issues "+" button in the header of the related issue block. +1. Select the relationship the between the two issues. Either: + - **relates to**. + - **blocks**. **(PREMIUM)** + - **is blocked by**. **(PREMIUM)** 1. Input the issue reference number or paste in the full URL of the issue. -1. **(STARTER)** Select whether the current issue relates to, blocks, or is blocked by the issues being entered. + ![Adding a related issue](img/related_issues_add_v12_8.png) - ![Adding a related issue](img/related_issues_add_v12_8.png) + Issues of the same project can be specified just by the reference number. + Issues from a different project require additional information like the + group and the project name. For example: - Issues of the same project can be specified just by the reference number. - Issues from a different project require additional information like the - group and the project name. For example: + - The same project: `#44` + - The same group: `project#44` + - Different group: `group/project#44` - - same project: `#44` - - same group: `project#44` - - different group: `group/project#44` - - Valid references are added to a temporary list that you can review. + Valid references are added to a temporary list that you can review. 1. When you have added all the related issues, click **Add** to submit. @@ -60,7 +62,7 @@ them categorized so their relationships can be better understood visually. ## Removing a related issue -In the related issues block, click the "x" icon on the right-side of each issue +In the related issues block, click the remove button (**{close}**) on the right-side of each issue token that you wish to remove. Due to the bi-directional relationship, it no longer appears in either issue. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index c130b2f1f8b..5ac5e30310b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3005,9 +3005,6 @@ msgstr "" msgid "AlertSettings|URL cannot be blank and must start with http or https" msgstr "" -msgid "AlertSettings|URL is invalid." -msgstr "" - msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint." msgstr "" @@ -8726,6 +8723,12 @@ msgstr "" msgid "Could not load usage counts. Please refresh the page to try again." msgstr "" +msgid "Could not remove %{user} from %{group}. Cannot remove last group owner." +msgstr "" + +msgid "Could not remove %{user} from %{group}. User is not a group member." +msgstr "" + msgid "Could not remove the trigger." msgstr "" @@ -32403,6 +32406,12 @@ msgstr "" msgid "URL" msgstr "" +msgid "URL cannot be blank" +msgstr "" + +msgid "URL is invalid" +msgstr "" + msgid "URL is required" msgstr "" @@ -33165,6 +33174,9 @@ msgstr "" msgid "User %{username} was successfully removed." msgstr "" +msgid "User %{user} was removed from %{group}." +msgstr "" + msgid "User ID" msgstr "" diff --git a/spec/features/boards/sidebar_due_date_spec.rb b/spec/features/boards/sidebar_due_date_spec.rb index 52ec51c317e..03d6c8dfaf9 100644 --- a/spec/features/boards/sidebar_due_date_spec.rb +++ b/spec/features/boards/sidebar_due_date_spec.rb @@ -17,8 +17,6 @@ RSpec.describe 'Project issue boards sidebar due date', :js do end before do - stub_feature_flags(graphql_board_lists: false) - project.add_maintainer(user) sign_in(user) @@ -31,10 +29,10 @@ RSpec.describe 'Project issue boards sidebar due date', :js do it 'updates due date' do click_card(card) - page.within('.due_date') do + page.within('[data-testid="sidebar-due-date"]') do today = Date.today.day - click_link 'Edit' + click_button 'Edit' click_button today.to_s diff --git a/spec/features/boards/sidebar_labels_spec.rb b/spec/features/boards/sidebar_labels_spec.rb index 37de561e689..7d89ef63f6c 100644 --- a/spec/features/boards/sidebar_labels_spec.rb +++ b/spec/features/boards/sidebar_labels_spec.rb @@ -18,8 +18,6 @@ RSpec.describe 'Project issue boards sidebar labels', :js do let(:card) { find('.board:nth-child(2)').first('.board-card') } before do - stub_feature_flags(graphql_board_lists: false) - project.add_maintainer(user) sign_in(user) @@ -29,7 +27,8 @@ RSpec.describe 'Project issue boards sidebar labels', :js do end context 'labels' do - it 'shows current labels when editing' do + # https://gitlab.com/gitlab-org/gitlab/-/issues/322725 + xit 'shows current labels when editing' do click_card(card) page.within('.labels') do @@ -49,15 +48,15 @@ RSpec.describe 'Project issue boards sidebar labels', :js do click_card(card) page.within('.labels') do - click_link 'Edit' + click_button 'Edit' wait_for_requests click_link bug.title - wait_for_requests + find('[data-testid="close-icon"]').click - find('.dropdown-menu-close-icon').click + wait_for_requests page.within('.value') do expect(page).to have_selector('.gl-label-text', count: 3) @@ -74,19 +73,17 @@ RSpec.describe 'Project issue boards sidebar labels', :js do click_card(card) page.within('.labels') do - click_link 'Edit' + click_button 'Edit' wait_for_requests click_link bug.title - wait_for_requests - click_link regression.title - wait_for_requests + find('[data-testid="close-icon"]').click - find('.dropdown-menu-close-icon').click + wait_for_requests page.within('.value') do expect(page).to have_selector('.gl-label-text', count: 4) @@ -105,17 +102,15 @@ RSpec.describe 'Project issue boards sidebar labels', :js do click_card(card) page.within('.labels') do - click_link 'Edit' + click_button 'Edit' wait_for_requests - within('.dropdown-menu-labels') do - click_link stretch.title - end + click_link stretch.title - wait_for_requests + find('[data-testid="close-icon"]').click - find('.dropdown-menu-close-icon').click + wait_for_requests page.within('.value') do expect(page).to have_selector('.gl-label-text', count: 1) @@ -128,7 +123,8 @@ RSpec.describe 'Project issue boards sidebar labels', :js do expect(card).not_to have_content(stretch.title) end - it 'creates project label' do + # https://gitlab.com/gitlab-org/gitlab/-/issues/324290 + xit 'creates project label' do click_card(card) page.within('.labels') do @@ -146,7 +142,8 @@ RSpec.describe 'Project issue boards sidebar labels', :js do expect(page).to have_selector('.board', count: 3) end - it 'creates project label and list' do + # https://gitlab.com/gitlab-org/gitlab/-/issues/324290 + xit 'creates project label and list' do click_card(card) page.within('.labels') do diff --git a/spec/features/boards/sidebar_milestones_spec.rb b/spec/features/boards/sidebar_milestones_spec.rb index d815d60d5b0..a8d8d20c10b 100644 --- a/spec/features/boards/sidebar_milestones_spec.rb +++ b/spec/features/boards/sidebar_milestones_spec.rb @@ -16,8 +16,6 @@ RSpec.describe 'Project issue boards sidebar milestones', :js do let(:card2) { find('.board:nth-child(1) .board-card:nth-of-type(2)') } before do - stub_feature_flags(graphql_board_lists: false) - project.add_maintainer(user) sign_in(user) @@ -30,12 +28,12 @@ RSpec.describe 'Project issue boards sidebar milestones', :js do it 'adds a milestone' do click_card(card1) - page.within('.milestone') do - click_link 'Edit' + page.within('[data-testid="sidebar-milestones"]') do + click_button 'Edit' wait_for_requests - click_link milestone.title + click_button milestone.title wait_for_requests @@ -48,12 +46,12 @@ RSpec.describe 'Project issue boards sidebar milestones', :js do it 'removes a milestone' do click_card(card2) - page.within('.milestone') do - click_link 'Edit' + page.within('[data-testid="sidebar-milestones"]') do + click_button 'Edit' wait_for_requests - click_link "No milestone" + click_button "No milestone" wait_for_requests diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index 45fe5ab8376..57e88fac013 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -13,8 +13,6 @@ RSpec.describe 'Project issue boards sidebar', :js do let(:card) { find('.board:nth-child(1)').first('.board-card') } before do - stub_feature_flags(graphql_board_lists: false) - project.add_maintainer(user) sign_in(user) @@ -44,7 +42,7 @@ RSpec.describe 'Project issue boards sidebar', :js do expect(page).to have_selector('.issue-boards-sidebar') - find('.gutter-toggle').click + find("[data-testid='sidebar-drawer'] .gl-drawer-close-button").click expect(page).not_to have_selector('.issue-boards-sidebar') end diff --git a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js index 00f2fa60360..9912ac433a5 100644 --- a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js +++ b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js @@ -10,6 +10,9 @@ import alertFields from '../mocks/alert_fields.json'; import parsedMapping from '../mocks/parsed_mapping.json'; import { defaultAlertSettingsConfig } from './util'; +const scrollIntoViewMock = jest.fn(); +HTMLElement.prototype.scrollIntoView = scrollIntoViewMock; + describe('AlertsSettingsForm', () => { let wrapper; const mockToastShow = jest.fn(); @@ -410,33 +413,35 @@ describe('AlertsSettingsForm', () => { createComponent(); }); - it('should not be able to submit when no integration type is selected', () => { - selectOptionAtIndex(0); + it('should not be able to submit when no integration type is selected', async () => { + await selectOptionAtIndex(0); expect(findSubmitButton().attributes('disabled')).toBe('disabled'); }); - it('should not be able to submit when HTTP integration form is invalid', () => { - selectOptionAtIndex(1); - + it('should not be able to submit when HTTP integration form is invalid', async () => { + await selectOptionAtIndex(1); + await findFormFields().at(0).vm.$emit('input', ''); expect(findSubmitButton().attributes('disabled')).toBe('disabled'); }); it('should be able to submit when HTTP integration form is valid', async () => { await selectOptionAtIndex(1); - await findFormFields().at(0).setValue('Name'); + await findFormFields().at(0).vm.$emit('input', 'Name'); expect(findSubmitButton().attributes('disabled')).toBe(undefined); }); - it('should not be able to submit when Prometheus integration form is invalid', () => { - selectOptionAtIndex(2); + it('should not be able to submit when Prometheus integration form is invalid', async () => { + await selectOptionAtIndex(2); + await findFormFields().at(0).vm.$emit('input', ''); expect(findSubmitButton().attributes('disabled')).toBe('disabled'); }); it('should be able to submit when Prometheus integration form is valid', async () => { await selectOptionAtIndex(2); - await findFormFields().at(0).setValue('http://valid.url'); + await findFormFields().at(0).vm.$emit('input', 'http://valid.url'); + expect(findSubmitButton().attributes('disabled')).toBe(undefined); }); @@ -445,7 +450,7 @@ describe('AlertsSettingsForm', () => { currentIntegration: { type: typeSet.http, name: 'Existing integration' }, }); await nextTick(); - await findFormFields().at(0).setValue('Updated name'); + await findFormFields().at(0).vm.$emit('input', 'Updated name'); expect(findSubmitButton().attributes('disabled')).toBe(undefined); }); @@ -458,5 +463,20 @@ describe('AlertsSettingsForm', () => { expect(findSubmitButton().attributes('disabled')).toBe('disabled'); }); + + it('should disable submit button after click on validation failure', async () => { + await selectOptionAtIndex(1); + findSubmitButton().trigger('click'); + await nextTick(); + + expect(findSubmitButton().attributes('disabled')).toBe('disabled'); + }); + + it('should scroll to invalid field on validation failure', async () => { + await selectOptionAtIndex(1); + findSubmitButton().trigger('click'); + + expect(scrollIntoViewMock).toHaveBeenCalledWith({ behavior: 'smooth', block: 'center' }); + }); }); }); |