diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-30 00:09:52 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-30 00:09:52 +0300 |
commit | b86a07e9b7332bf5d2815716b2220299ffd7a223 (patch) | |
tree | 29206bd07a8af3885fe6c9d35f2e51daafadc38e | |
parent | 12a62cd94406f23fc3782e4914ca282196eb25f6 (diff) |
Add latest changes from gitlab-org/gitlab@master
48 files changed, 592 insertions, 97 deletions
diff --git a/.gitlab/issue_templates/Feature proposal.md b/.gitlab/issue_templates/Feature proposal.md index 589310b4cef..dc32ce1288d 100644 --- a/.gitlab/issue_templates/Feature proposal.md +++ b/.gitlab/issue_templates/Feature proposal.md @@ -87,4 +87,18 @@ In which enterprise tier should this feature go? See https://about.gitlab.com/ha ### Links / references +<!-- Label reminders - you should have one of each of the following labels if you can find the correct ones! + +Type - for example ~feature ~bug ~documentation ~meta /label ~"feature::addition" ~"feature::maintenance" ~tooling ~"tooling::pipelines" ~"tooling::workflow" per https://docs.gitlab.com/ee/development/contributing/issue_workflow.html#type-labels + +DevOps stage - for example ~"devops::secure" + +Group - for example ~"group::composition analysis" + +Category - for example ~"Category:Dependency Scanning" +<!-- Label reminders - you should have one of each of the following labels if you can figure out the correct ones! --> +/label ~"devops:: +/label ~"group:: +/label ~"Category: + /label ~feature 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 18c9f82f052..56ecf0d9c55 100644 --- a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue +++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue @@ -435,7 +435,7 @@ export default { data-testid="alert-settings-select" @change="resetFormValues" /> - <span class="gl-text-gray-400"> + <span class="gl-text-gray-200"> <gl-sprintf :message="$options.i18n.integrationsInfo"> <template #link="{ content }"> <gl-link @@ -474,7 +474,7 @@ export default { :placeholder="baseUrlPlaceholder" :disabled="!selectedService.active" /> - <span class="gl-text-gray-400"> + <span class="gl-text-gray-200"> {{ $options.i18n.apiBaseUrlHelpText }} </span> </gl-form-group> @@ -489,7 +489,7 @@ export default { /> </template> </gl-form-input-group> - <span class="gl-text-gray-400"> + <span class="gl-text-gray-200"> {{ prometheusInfo }} </span> </gl-form-group> diff --git a/app/assets/javascripts/clusters_list/components/clusters.vue b/app/assets/javascripts/clusters_list/components/clusters.vue index 7e9b720d269..09d7c0329a9 100644 --- a/app/assets/javascripts/clusters_list/components/clusters.vue +++ b/app/assets/javascripts/clusters_list/components/clusters.vue @@ -231,7 +231,7 @@ export default { <gl-skeleton-loading v-else-if="loadingNodes" :lines="1" :class="contentAlignClasses" /> - <small v-else class="gl-font-sm gl-font-style-italic gl-text-gray-400">{{ + <small v-else class="gl-font-sm gl-font-style-italic gl-text-gray-200">{{ __('Unknown') }}</small> </template> diff --git a/app/assets/javascripts/error_tracking/components/stacktrace_entry.vue b/app/assets/javascripts/error_tracking/components/stacktrace_entry.vue index c22f34b5a8d..c6825d7af45 100644 --- a/app/assets/javascripts/error_tracking/components/stacktrace_entry.vue +++ b/app/assets/javascripts/error_tracking/components/stacktrace_entry.vue @@ -99,7 +99,7 @@ export default { <gl-sprintf v-if="errorFn" :message="__('%{spanStart}in%{spanEnd} %{errorFn}')"> <template #span="{content}"> - <span class="gl-text-gray-400">{{ content }} </span> + <span class="gl-text-gray-200">{{ content }} </span> </template> <template #errorFn> <strong>{{ errorFn }} </strong> @@ -108,7 +108,7 @@ export default { <gl-sprintf :message="__('%{spanStart}at line%{spanEnd} %{errorLine}%{errorColumn}')"> <template #span="{content}"> - <span class="gl-text-gray-400">{{ content }} </span> + <span class="gl-text-gray-200">{{ content }} </span> </template> <template #errorLine> <strong>{{ errorLine }}</strong> diff --git a/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue b/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue index 027848db6e9..7b37fddd630 100644 --- a/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue +++ b/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue @@ -135,7 +135,7 @@ export default { </template> </gl-form-input-group> - <div class="gl-text-gray-400 gl-pt-2"> + <div class="gl-text-gray-200 gl-pt-2"> <gl-sprintf :message="$options.i18n.webhookUrl.helpText"> <template #docsLink> <gl-link diff --git a/app/assets/javascripts/mr_notes/init_notes.js b/app/assets/javascripts/mr_notes/init_notes.js index 0fd4b1e7f4a..2be7cc951fc 100644 --- a/app/assets/javascripts/mr_notes/init_notes.js +++ b/app/assets/javascripts/mr_notes/init_notes.js @@ -3,7 +3,7 @@ import Vue from 'vue'; import { mapActions, mapState, mapGetters } from 'vuex'; import store from '~/mr_notes/stores'; import notesApp from '../notes/components/notes_app.vue'; -import discussionKeyboardNavigator from '../notes/components/discussion_keyboard_navigator.vue'; +import discussionNavigator from '../notes/components/discussion_navigator.vue'; import initWidget from '../vue_merge_request_widget'; import { parseBoolean } from '~/lib/utils/common_utils'; @@ -71,11 +71,11 @@ export default () => { }, }, render(createElement) { - // NOTE: Even though `discussionKeyboardNavigator` is added to the `notes-app`, + // NOTE: Even though `discussionNavigator` is added to the `notes-app`, // it adds a global key listener so it works on the diffs tab as well. // If we create a single Vue app for all of the MR tabs, we should move this // up the tree, to the root. - return createElement(discussionKeyboardNavigator, [ + return createElement(discussionNavigator, [ createElement('notes-app', { props: { noteableData: this.noteableData, diff --git a/app/assets/javascripts/notes/components/discussion_keyboard_navigator.vue b/app/assets/javascripts/notes/components/discussion_navigator.vue index facc53e27a6..facc53e27a6 100644 --- a/app/assets/javascripts/notes/components/discussion_keyboard_navigator.vue +++ b/app/assets/javascripts/notes/components/discussion_navigator.vue diff --git a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue index caf13bc898b..e11b0cd67cc 100644 --- a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue +++ b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue @@ -38,13 +38,6 @@ export default { }, ); }, - heightStyle() { - return { - minHeight: '32px', - width: '0px', - visibility: 'hidden', - }; - }, iconClasses() { return `${this.iconClass} ic-${this.iconName}`; }, @@ -60,7 +53,9 @@ export default { }" class="item-body d-flex align-items-center py-2 px-3" > - <div class="item-contents d-flex align-items-center flex-wrap flex-grow-1 flex-xl-nowrap"> + <div + class="item-contents gl-display-flex gl-align-items-center gl-flex-wrap gl-flex-grow-1 flex-xl-nowrap gl-min-h-7" + > <!-- Title area: Status icon (XL) and title --> <div class="item-title d-flex align-items-xl-center mb-xl-0"> <div ref="iconElementXL"> @@ -159,9 +154,5 @@ export default { > <icon :size="16" class="btn-item-remove-icon" name="close" /> </button> - - <!-- This element serves to set the issue card's height at a minimum of 32 px. --> - <!-- It fixes #59594: when the remove button is missing, issues have inconsistent heights. --> - <span :style="heightStyle"></span> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_custom_renderer.js b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_custom_renderer.js index 70d29b5b3df..bd374a54d98 100644 --- a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_custom_renderer.js +++ b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_custom_renderer.js @@ -5,12 +5,14 @@ import renderIdentifierInstanceText from './renderers/render_identifier_instance import renderIdentifierParagraph from './renderers/render_identifier_paragraph'; import renderEmbeddedRubyText from './renderers/render_embedded_ruby_text'; import renderFontAwesomeHtmlInline from './renderers/render_font_awesome_html_inline'; +import renderSoftbreak from './renderers/render_softbreak'; const htmlInlineRenderers = [renderFontAwesomeHtmlInline]; const htmlBlockRenderers = [renderBlockHtml]; const listRenderers = [renderKramdownList]; const paragraphRenderers = [renderIdentifierParagraph]; const textRenderers = [renderKramdownText, renderEmbeddedRubyText, renderIdentifierInstanceText]; +const softbreakRenderers = [renderSoftbreak]; const executeRenderer = (renderers, node, context) => { const availableRenderer = renderers.find(renderer => renderer.canRender(node, context)); @@ -29,7 +31,14 @@ const buildCustomRendererFunctions = (customRenderers, defaults) => { }; const buildCustomHTMLRenderer = ( - customRenderers = { htmlBlock: [], htmlInline: [], list: [], paragraph: [], text: [] }, + customRenderers = { + htmlBlock: [], + htmlInline: [], + list: [], + paragraph: [], + text: [], + softbreak: [], + }, ) => { const defaults = { htmlBlock(node, context) { @@ -57,6 +66,11 @@ const buildCustomHTMLRenderer = ( return executeRenderer(allTextRenderers, node, context); }, + softbreak(node, context) { + const allSoftbreakRenderers = [...customRenderers.softbreak, ...softbreakRenderers]; + + return executeRenderer(allSoftbreakRenderers, node, context); + }, }; return { diff --git a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer.js b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer.js index 89a0df395d3..868ede9426e 100644 --- a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer.js +++ b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer.js @@ -69,7 +69,7 @@ const buildHTMLToMarkdownRender = (baseRenderer, formattingPreferences = {}) => [orderedListItemNode](node, subContent) { const baseResult = baseRenderer.convert(node, subContent); - return incrementListMarker ? baseResult : baseResult.replace(/^(\s*)\d\./, '$11.'); + return incrementListMarker ? baseResult : baseResult.replace(/^(\s*)\d+?\./, '$11.'); }, [emphasisNode](node, subContent) { const result = baseRenderer.convert(node, subContent); diff --git a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/renderers/render_softbreak.js b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/renderers/render_softbreak.js new file mode 100644 index 00000000000..389ade5f27a --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/renderers/render_softbreak.js @@ -0,0 +1,7 @@ +const canRender = node => ['emph', 'strong'].includes(node.parent?.type); +const render = () => ({ + type: 'text', + content: ' ', +}); + +export default { canRender, render }; diff --git a/app/assets/stylesheets/components/dashboard_skeleton.scss b/app/assets/stylesheets/components/dashboard_skeleton.scss index a35222bca95..1f257d96d94 100644 --- a/app/assets/stylesheets/components/dashboard_skeleton.scss +++ b/app/assets/stylesheets/components/dashboard_skeleton.scss @@ -41,7 +41,7 @@ } &-extra { - background-color: $gray-400; + background-color: $gray-200; font-size: 10px; line-height: $gl-line-height; width: $gl-padding; diff --git a/app/assets/stylesheets/components/related_items_list.scss b/app/assets/stylesheets/components/related_items_list.scss index dd749b4df1a..499aa6440da 100644 --- a/app/assets/stylesheets/components/related_items_list.scss +++ b/app/assets/stylesheets/components/related_items_list.scss @@ -104,7 +104,7 @@ $item-remove-button-space: 42px; .bullet-separator { font-size: 9px; - color: $gray-400; + color: $gray-200; } } diff --git a/app/assets/stylesheets/components/rich_content_editor.scss b/app/assets/stylesheets/components/rich_content_editor.scss index 8d31b386d9e..425c67ee188 100644 --- a/app/assets/stylesheets/components/rich_content_editor.scss +++ b/app/assets/stylesheets/components/rich_content_editor.scss @@ -20,11 +20,11 @@ .tui-popup-wrapper { @include gl-overflow-hidden; @include gl-rounded-base; - @include gl-border-gray-400; + @include gl-border-gray-200; hr { @include gl-m-0; - @include gl-bg-gray-400; + @include gl-bg-gray-200; } button { diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index f92db122209..0c1f19e9420 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -74,7 +74,7 @@ .hint { font-style: italic; - color: $gl-gray-400; + color: $gl-gray-200; } .light { color: $gl-text-color; } @@ -168,7 +168,7 @@ table { } p.time { - color: $gl-gray-400; + color: $gl-gray-200; font-size: 90%; margin: 30px 3px 3px 2px; } diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index b002ced9393..8758fe15870 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -89,10 +89,10 @@ background-color: $gray-10; border-width: 1px; border-style: solid; - border-color: $gray-100 $gray-100 $gray-400; + border-color: $gray-100 $gray-100 $gray-200; border-image: none; border-radius: 3px; - box-shadow: 0 -1px 0 $gray-400 inset; + box-shadow: 0 -1px 0 $gray-200 inset; } h1 { diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index f7c0617a135..11a948eb2e5 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -166,7 +166,7 @@ $gray-50: #f0f0f0 !default; $gray-100: #dbdbdb !default; $gray-200: #bfbfbf !default; $gray-300: #999 !default; -$gray-400: #bababa !default; +$gray-400: #868686 !default; $gray-500: #a7a7a7 !default; $gray-600: #919191 !default; $gray-700: #707070 !default; diff --git a/app/assets/stylesheets/pages/alert_management/severity-icons.scss b/app/assets/stylesheets/pages/alert_management/severity-icons.scss index 0e00d78e53a..6004697b3e1 100644 --- a/app/assets/stylesheets/pages/alert_management/severity-icons.scss +++ b/app/assets/stylesheets/pages/alert_management/severity-icons.scss @@ -21,6 +21,6 @@ } .icon-unknown { - color: $gray-400; + color: $gray-200; } } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 5abf8e1c64f..5e4c43d5b1e 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -825,7 +825,7 @@ } .repository-language-bar-tooltip-share { - color: $gray-400; + color: $gray-200; } pre.light-well { diff --git a/app/graphql/mutations/notes/update/base.rb b/app/graphql/mutations/notes/update/base.rb index 9a53337f253..8a2a78a29ec 100644 --- a/app/graphql/mutations/notes/update/base.rb +++ b/app/graphql/mutations/notes/update/base.rb @@ -40,7 +40,7 @@ module Mutations end def note_params(_note, args) - { note: args[:body] }.compact + { note: args[:body], confidential: args[:confidential] }.compact end end end diff --git a/app/graphql/mutations/notes/update/note.rb b/app/graphql/mutations/notes/update/note.rb index 03a174fc8d9..ca97dad6ded 100644 --- a/app/graphql/mutations/notes/update/note.rb +++ b/app/graphql/mutations/notes/update/note.rb @@ -8,9 +8,14 @@ module Mutations argument :body, GraphQL::STRING_TYPE, - required: true, + required: false, description: copy_field_description(Types::Notes::NoteType, :body) + argument :confidential, + GraphQL::BOOLEAN_TYPE, + required: false, + description: 'The confidentiality flag of a note. Default is false.' + private def pre_update_checks!(note, _args) diff --git a/app/serializers/merge_request_poll_widget_entity.rb b/app/serializers/merge_request_poll_widget_entity.rb index c27d571245b..99d6211b487 100644 --- a/app/serializers/merge_request_poll_widget_entity.rb +++ b/app/serializers/merge_request_poll_widget_entity.rb @@ -20,7 +20,7 @@ class MergeRequestPollWidgetEntity < Grape::Entity expose :merge_user, using: UserEntity expose :actual_head_pipeline, as: :pipeline, if: -> (mr, _) { presenter(mr).can_read_pipeline? } do |merge_request, options| - if Feature.enabled?(:merge_request_short_pipeline_serializer, merge_request.project) + if Feature.enabled?(:merge_request_short_pipeline_serializer, merge_request.project, default_enabled: true) MergeRequests::PipelineEntity.represent(merge_request.actual_head_pipeline, options) else PipelineDetailsEntity.represent(merge_request.actual_head_pipeline, options) @@ -28,7 +28,7 @@ class MergeRequestPollWidgetEntity < Grape::Entity end expose :merge_pipeline, if: ->(mr, _) { mr.merged? && can?(request.current_user, :read_pipeline, mr.target_project)} do |merge_request, options| - if Feature.enabled?(:merge_request_short_pipeline_serializer, merge_request.project) + if Feature.enabled?(:merge_request_short_pipeline_serializer, merge_request.project, default_enabled: true) MergeRequests::PipelineEntity.represent(merge_request.merge_pipeline, options) else PipelineDetailsEntity.represent(merge_request.merge_pipeline, options) diff --git a/changelogs/unreleased/207472-api-update-note-conf.yml b/changelogs/unreleased/207472-api-update-note-conf.yml new file mode 100644 index 00000000000..4b2df2e4fe2 --- /dev/null +++ b/changelogs/unreleased/207472-api-update-note-conf.yml @@ -0,0 +1,5 @@ +--- +title: Add confidential attribute to public API for notes update +merge_request: 37932 +author: +type: added diff --git a/changelogs/unreleased/207473-graphl-update-note-conf.yml b/changelogs/unreleased/207473-graphl-update-note-conf.yml new file mode 100644 index 00000000000..e49f2805ae0 --- /dev/null +++ b/changelogs/unreleased/207473-graphl-update-note-conf.yml @@ -0,0 +1,5 @@ +--- +title: Add confidential attribute to graphQL for notes update +merge_request: 37920 +author: +type: added diff --git a/changelogs/unreleased/218526-remove-again-gitlab-issue-tracker-service.yml b/changelogs/unreleased/218526-remove-again-gitlab-issue-tracker-service.yml new file mode 100644 index 00000000000..93094d8623e --- /dev/null +++ b/changelogs/unreleased/218526-remove-again-gitlab-issue-tracker-service.yml @@ -0,0 +1,5 @@ +--- +title: Remove GitlabIssueTrackerService database records +merge_request: 37931 +author: +type: other diff --git a/changelogs/unreleased/224119-replace-gray-400-value-replace-usages-with-gray-200.yml b/changelogs/unreleased/224119-replace-gray-400-value-replace-usages-with-gray-200.yml new file mode 100644 index 00000000000..f46f4675041 --- /dev/null +++ b/changelogs/unreleased/224119-replace-gray-400-value-replace-usages-with-gray-200.yml @@ -0,0 +1,5 @@ +--- +title: Update $gray-400 hex and replace instances of $gray-400 with $gray-200 +merge_request: 37813 +author: +type: other diff --git a/changelogs/unreleased/fix-ordered-lists-single-marker-bug.yml b/changelogs/unreleased/fix-ordered-lists-single-marker-bug.yml new file mode 100644 index 00000000000..9ef32919ab8 --- /dev/null +++ b/changelogs/unreleased/fix-ordered-lists-single-marker-bug.yml @@ -0,0 +1,6 @@ +--- +title: 'Static Site Editor: Fix ordered list formatting bug and rendering bug in strong + and emphasis nodes with softbreaks' +merge_request: 37964 +author: +type: fixed diff --git a/changelogs/unreleased/id-enable-pipelines-serializer-ff.yml b/changelogs/unreleased/id-enable-pipelines-serializer-ff.yml new file mode 100644 index 00000000000..2e7587f2662 --- /dev/null +++ b/changelogs/unreleased/id-enable-pipelines-serializer-ff.yml @@ -0,0 +1,5 @@ +--- +title: Serialize fewer pipeline fields for MR widget +merge_request: 38215 +author: +type: performance diff --git a/db/post_migrate/20200727100631_remove_again_gitlab_issue_tracker_service_records.rb b/db/post_migrate/20200727100631_remove_again_gitlab_issue_tracker_service_records.rb new file mode 100644 index 00000000000..b61da82aef8 --- /dev/null +++ b/db/post_migrate/20200727100631_remove_again_gitlab_issue_tracker_service_records.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class RemoveAgainGitlabIssueTrackerServiceRecords < ActiveRecord::Migration[6.0] + DOWNTIME = false + BATCH_SIZE = 5000 + + disable_ddl_transaction! + + class Service < ActiveRecord::Base + include EachBatch + + self.table_name = 'services' + + def self.gitlab_issue_tracker_service + where(type: 'GitlabIssueTrackerService') + end + end + + def up + Service.each_batch(of: BATCH_SIZE) do |services| + services.gitlab_issue_tracker_service.delete_all + end + end + + def down + # no-op + end +end diff --git a/db/schema_migrations/20200727100631 b/db/schema_migrations/20200727100631 new file mode 100644 index 00000000000..aa724c13995 --- /dev/null +++ b/db/schema_migrations/20200727100631 @@ -0,0 +1 @@ +83c1dca01d4e56c22f6c1cda249c9b162eb7c31e2b164629bf51ea9aa9dd8fb5
\ No newline at end of file diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index ec602222f3b..c86f405dfb9 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -5664,6 +5664,11 @@ type Group { ): VulnerabilitiesCountByDayAndSeverityConnection """ + Represents vulnerable project counts for each grade + """ + vulnerabilityGrades: [VulnerableProjectsByGrade!]! + + """ Vulnerability scanners reported on the project vulnerabilties of the group and its subgroups """ vulnerabilityScanners( @@ -5817,6 +5822,11 @@ type InstanceSecurityDashboard { ): ProjectConnection! """ + Represents vulnerable project counts for each grade + """ + vulnerabilityGrades: [VulnerableProjectsByGrade!]! + + """ Vulnerability scanners reported on the vulnerabilties from projects selected in Instance Security Dashboard """ vulnerabilityScanners( @@ -14312,7 +14322,7 @@ input UpdateNoteInput { """ Content of the note """ - body: String! + body: String """ A unique identifier for the client performing the mutation. @@ -14320,6 +14330,11 @@ input UpdateNoteInput { clientMutationId: String """ + The confidentiality flag of a note. Default is false. + """ + confidential: Boolean + + """ The global id of the note to update """ id: ID! @@ -15041,6 +15056,17 @@ type VulnerabilityEdge { } """ +The grade of the vulnerable project +""" +enum VulnerabilityGrade { + A + B + C + D + F +} + +""" Represents a vulnerability identifier. """ type VulnerabilityIdentifier { @@ -15480,4 +15506,44 @@ type VulnerablePackage { The name of the vulnerable package """ name: String +} + +""" +Represents vulnerability letter grades with associated projects +""" +type VulnerableProjectsByGrade { + """ + Number of projects within this grade + """ + count: Int! + + """ + Grade based on the highest severity vulnerability present + """ + grade: VulnerabilityGrade! + + """ + Projects within this grade + """ + projects( + """ + Returns the elements in the list that come after the specified cursor. + """ + after: String + + """ + Returns the elements in the list that come before the specified cursor. + """ + before: String + + """ + Returns the first _n_ elements from the list. + """ + first: Int + + """ + Returns the last _n_ elements from the list. + """ + last: Int + ): ProjectConnection! }
\ No newline at end of file diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index 838bd1cc0a2..773367b3783 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -15569,6 +15569,32 @@ "deprecationReason": null }, { + "name": "vulnerabilityGrades", + "description": "Represents vulnerable project counts for each grade", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "VulnerableProjectsByGrade", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "vulnerabilityScanners", "description": "Vulnerability scanners reported on the project vulnerabilties of the group and its subgroups", "args": [ @@ -16021,6 +16047,32 @@ "deprecationReason": null }, { + "name": "vulnerabilityGrades", + "description": "Represents vulnerable project counts for each grade", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "VulnerableProjectsByGrade", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "vulnerabilityScanners", "description": "Vulnerability scanners reported on the vulnerabilties from projects selected in Instance Security Dashboard", "args": [ @@ -42306,13 +42358,19 @@ "name": "body", "description": "Content of the note", "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "confidential", + "description": "The confidentiality flag of a note. Default is false.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null }, "defaultValue": null }, @@ -44349,6 +44407,47 @@ "possibleTypes": null }, { + "kind": "ENUM", + "name": "VulnerabilityGrade", + "description": "The grade of the vulnerable project", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "A", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "B", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "C", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "D", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "F", + "description": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { "kind": "OBJECT", "name": "VulnerabilityIdentifier", "description": "Represents a vulnerability identifier.", @@ -45708,6 +45807,112 @@ }, { "kind": "OBJECT", + "name": "VulnerableProjectsByGrade", + "description": "Represents vulnerability letter grades with associated projects", + "fields": [ + { + "name": "count", + "description": "Number of projects within this grade", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "grade", + "description": "Grade based on the highest severity vulnerability present", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "VulnerabilityGrade", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "projects", + "description": "Projects within this grade", + "args": [ + { + "name": "after", + "description": "Returns the elements in the list that come after the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "before", + "description": "Returns the elements in the list that come before the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "first", + "description": "Returns the first _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "last", + "description": "Returns the last _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "ProjectConnection", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", "name": "__Directive", "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.", "fields": [ diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 73df0156fe6..5eaff5350eb 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -862,6 +862,7 @@ Autogenerated return type of EpicTreeReorder | `twoFactorGracePeriod` | Int | Time before two-factor authentication is enforced | | `userPermissions` | GroupPermissions! | Permissions for the current user on the resource | | `visibility` | String | Visibility of the namespace | +| `vulnerabilityGrades` | VulnerableProjectsByGrade! => Array | Represents vulnerable project counts for each grade | | `webUrl` | String! | Web URL of the group | ## GroupMember @@ -884,6 +885,12 @@ Represents a Group Member | --- | ---- | ---------- | | `readGroup` | Boolean! | Indicates the user can perform `read_group` on this resource | +## InstanceSecurityDashboard + +| Name | Type | Description | +| --- | ---- | ---------- | +| `vulnerabilityGrades` | VulnerableProjectsByGrade! => Array | Represents vulnerable project counts for each grade | + ## Issue | Name | Type | Description | @@ -2395,3 +2402,12 @@ Represents a vulnerable package. Used in vulnerability dependency data | Name | Type | Description | | --- | ---- | ---------- | | `name` | String | The name of the vulnerable package | + +## VulnerableProjectsByGrade + +Represents vulnerability letter grades with associated projects + +| Name | Type | Description | +| --- | ---- | ---------- | +| `count` | Int! | Number of projects within this grade | +| `grade` | VulnerabilityGrade! | Grade based on the highest severity vulnerability present | diff --git a/doc/api/notes.md b/doc/api/notes.md index 6b25494c577..3a68454507a 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -145,10 +145,11 @@ PUT /projects/:id/issues/:issue_iid/notes/:note_id Parameters: -- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) -- `issue_iid` (required) - The IID of an issue -- `note_id` (required) - The ID of a note -- `body` (required) - The content of a note. Limited to 1,000,000 characters. +- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). +- `issue_iid` (required) - The IID of an issue. +- `note_id` (required) - The ID of a note. +- `body` (optional) - The content of a note. Limited to 1,000,000 characters. +- `confidential` (optional) - The confidential flag of a note. ```shell curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/issues/11/notes?body=note" diff --git a/doc/university/training/topics/explore_gitlab.md b/doc/university/training/topics/explore_gitlab.md index 4ca931d0e26..8678f8fd9eb 100644 --- a/doc/university/training/topics/explore_gitlab.md +++ b/doc/university/training/topics/explore_gitlab.md @@ -1,12 +1,5 @@ --- -comments: false +redirect_to: '../../../gitlab-basics/README.md' --- -# Explore GitLab projects - -- Dashboard -- User Preferences -- Issues -- Milestones and Labels -- Manage project members -- Project settings +This document was moved to [another location](../../../gitlab-basics/README.md). diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb index f88624ed63e..f61bcfe963e 100644 --- a/lib/api/helpers/notes_helpers.rb +++ b/lib/api/helpers/notes_helpers.rb @@ -17,8 +17,9 @@ module API authorize! :admin_note, note opts = { - note: params[:body] - } + note: params[:body], + confidential: params[:confidential] + }.compact parent = noteable_parent(noteable) project = parent if parent.is_a?(Project) diff --git a/lib/api/notes.rb b/lib/api/notes.rb index bfd09dcd496..e4989243f3d 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -101,7 +101,8 @@ module API params do requires :noteable_id, type: Integer, desc: 'The ID of the noteable' requires :note_id, type: Integer, desc: 'The ID of a note' - requires :body, type: String, desc: 'The content of a note' + optional :body, type: String, allow_blank: false, desc: 'The content of a note' + optional :confidential, type: Boolean, desc: 'Confidentiality note flag' end put ":id/#{noteables_str}/:noteable_id/notes/:note_id" do noteable = find_noteable(noteable_type, params[:noteable_id]) diff --git a/package.json b/package.json index 0ec8aade436..17d74efec32 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,8 @@ "@babel/plugin-syntax-import-meta": "^7.10.1", "@babel/preset-env": "^7.10.1", "@gitlab/at.js": "1.5.5", - "@gitlab/svgs": "1.155.0", - "@gitlab/ui": "17.37.0", + "@gitlab/svgs": "1.156.0", + "@gitlab/ui": "17.39.0", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "^6.0.3-1", "@sentry/browser": "^5.10.2", diff --git a/spec/frontend/alert_settings/__snapshots__/alert_settings_form_spec.js.snap b/spec/frontend/alert_settings/__snapshots__/alert_settings_form_spec.js.snap index 1f5c3a80fbb..96c5cc5b980 100644 --- a/spec/frontend/alert_settings/__snapshots__/alert_settings_form_spec.js.snap +++ b/spec/frontend/alert_settings/__snapshots__/alert_settings_form_spec.js.snap @@ -13,14 +13,14 @@ exports[`AlertsSettingsForm with default values renders the initial template 1`] </div> <gl-form-stub> <gl-form-group-stub label=\\"Integrations\\" label-for=\\"integrations\\" label-class=\\"label-bold\\"> - <gl-form-select-stub options=\\"[object Object],[object Object],[object Object]\\" data-testid=\\"alert-settings-select\\" value=\\"generic\\"></gl-form-select-stub> <span class=\\"gl-text-gray-400\\"><gl-sprintf-stub message=\\"Learn more about our %{linkStart}upcoming integrations%{linkEnd}\\"></gl-sprintf-stub></span> + <gl-form-select-stub options=\\"[object Object],[object Object],[object Object]\\" data-testid=\\"alert-settings-select\\" value=\\"generic\\"></gl-form-select-stub> <span class=\\"gl-text-gray-200\\"><gl-sprintf-stub message=\\"Learn more about our %{linkStart}upcoming integrations%{linkEnd}\\"></gl-sprintf-stub></span> </gl-form-group-stub> <gl-form-group-stub label=\\"Active\\" label-for=\\"activated\\" label-class=\\"label-bold\\"> <toggle-button-stub id=\\"activated\\"></toggle-button-stub> </gl-form-group-stub> <!----> <gl-form-group-stub label=\\"Webhook URL\\" label-for=\\"url\\" label-class=\\"label-bold\\"> - <gl-form-input-group-stub value=\\"/alerts/notify.json\\" predefinedoptions=\\"[object Object]\\" id=\\"url\\" readonly=\\"\\"></gl-form-input-group-stub> <span class=\\"gl-text-gray-400\\"> + <gl-form-input-group-stub value=\\"/alerts/notify.json\\" predefinedoptions=\\"[object Object]\\" id=\\"url\\" readonly=\\"\\"></gl-form-input-group-stub> <span class=\\"gl-text-gray-200\\"> </span> </gl-form-group-stub> diff --git a/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap b/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap index 17ada722034..4ac20275319 100644 --- a/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap +++ b/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap @@ -35,7 +35,7 @@ exports[`Alert integration settings form should match the default snapshot 1`] = /> <div - class="gl-text-gray-400 gl-pt-2" + class="gl-text-gray-200 gl-pt-2" > <gl-sprintf-stub message="Create a GitLab issue for each PagerDuty incident by %{docsLink}" diff --git a/spec/frontend/notes/components/discussion_keyboard_navigator_spec.js b/spec/frontend/notes/components/discussion_navigator_spec.js index fd0383f3b9d..122814b8e3f 100644 --- a/spec/frontend/notes/components/discussion_keyboard_navigator_spec.js +++ b/spec/frontend/notes/components/discussion_navigator_spec.js @@ -2,10 +2,10 @@ import 'mousetrap'; import Vue from 'vue'; import { shallowMount, createLocalVue } from '@vue/test-utils'; -import DiscussionKeyboardNavigator from '~/notes/components/discussion_keyboard_navigator.vue'; +import DiscussionNavigator from '~/notes/components/discussion_navigator.vue'; import eventHub from '~/notes/event_hub'; -describe('notes/components/discussion_keyboard_navigator', () => { +describe('notes/components/discussion_navigator', () => { const localVue = createLocalVue(); let wrapper; @@ -13,7 +13,7 @@ describe('notes/components/discussion_keyboard_navigator', () => { let jumpToPreviousDiscussion; const createComponent = () => { - wrapper = shallowMount(DiscussionKeyboardNavigator, { + wrapper = shallowMount(DiscussionNavigator, { mixins: [ localVue.extend({ methods: { @@ -43,7 +43,7 @@ describe('notes/components/discussion_keyboard_navigator', () => { beforeEach(() => { onSpy = jest.spyOn(eventHub, '$on'); - vm = new (Vue.extend(DiscussionKeyboardNavigator))(); + vm = new (Vue.extend(DiscussionNavigator))(); }); it('listens for jumpToFirstUnresolvedDiscussion events', () => { diff --git a/spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_integration_spec.js b/spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_integration_spec.js index 70317d333d9..b9b93b274d2 100644 --- a/spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_integration_spec.js +++ b/spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_integration_spec.js @@ -1,5 +1,6 @@ import Editor from '@toast-ui/editor'; import { registerHTMLToMarkdownRenderer } from '~/vue_shared/components/rich_content_editor/services/editor_service'; +import buildMarkdownToHTMLRenderer from '~/vue_shared/components/rich_content_editor/services/build_custom_renderer'; describe('vue_shared/components/rich_content_editor', () => { let editor; @@ -7,6 +8,7 @@ describe('vue_shared/components/rich_content_editor', () => { const buildEditor = () => { editor = new Editor({ el: document.body, + customHTMLRenderer: buildMarkdownToHTMLRenderer(), }); registerHTMLToMarkdownRenderer(editor); @@ -49,4 +51,19 @@ describe('vue_shared/components/rich_content_editor', () => { expect(markdown).toBe('**strong text**_emphasis text_'); }); }); + + describe('Markdown to HTML', () => { + it.each` + input | output + ${'markdown with _emphasized\ntext_'} | ${'<p>markdown with <em>emphasized text</em></p>\n'} + ${'markdown with **strong\ntext**'} | ${'<p>markdown with <strong>strong text</strong></p>\n'} + `( + 'does not transform softbreaks inside (_) and strong (**) nodes into <br/> tags', + ({ input, output }) => { + editor.setMarkdown(input); + + expect(editor.getHtml()).toBe(output); + }, + ); + }); }); diff --git a/spec/frontend/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer_spec.js b/spec/frontend/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer_spec.js index 2bbd3572d4b..a90d3528d60 100644 --- a/spec/frontend/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer_spec.js +++ b/spec/frontend/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer_spec.js @@ -70,10 +70,11 @@ describe('HTMLToMarkdownRenderer', () => { describe('OL LI visitor', () => { it.each` - listItem | result | incrementListMarker | action - ${'2. list item'} | ${'1. list item'} | ${false} | ${'increments'} - ${' 3. list item'} | ${' 1. list item'} | ${false} | ${'increments'} - ${'3. list item'} | ${'3. list item'} | ${true} | ${'does not increment'} + listItem | result | incrementListMarker | action + ${'2. list item'} | ${'1. list item'} | ${false} | ${'increments'} + ${' 3. list item'} | ${' 1. list item'} | ${false} | ${'increments'} + ${' 123. list item'} | ${' 1. list item'} | ${false} | ${'increments'} + ${'3. list item'} | ${'3. list item'} | ${true} | ${'does not increment'} `( '$action a list item counter when incrementListMaker is $incrementListMarker', ({ listItem, result, incrementListMarker }) => { diff --git a/spec/frontend/vue_shared/components/rich_content_editor/services/renderers/render_softbreak_spec.js b/spec/frontend/vue_shared/components/rich_content_editor/services/renderers/render_softbreak_spec.js new file mode 100644 index 00000000000..3c3d2354cb9 --- /dev/null +++ b/spec/frontend/vue_shared/components/rich_content_editor/services/renderers/render_softbreak_spec.js @@ -0,0 +1,23 @@ +import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_softbreak'; + +describe('Render softbreak renderer', () => { + describe('canRender', () => { + it.each` + node | parentType | result + ${{ parent: { type: 'emph' } }} | ${'emph'} | ${true} + ${{ parent: { type: 'strong' } }} | ${'strong'} | ${true} + ${{ parent: { type: 'paragraph' } }} | ${'paragraph'} | ${false} + `('returns $result when node parent type is $parentType ', ({ node, result }) => { + expect(renderer.canRender(node)).toBe(result); + }); + }); + + describe('render', () => { + it('returns text node with a break line', () => { + expect(renderer.render()).toEqual({ + type: 'text', + content: ' ', + }); + }); + }); +}); diff --git a/spec/requests/api/graphql/mutations/notes/update/note_spec.rb b/spec/requests/api/graphql/mutations/notes/update/note_spec.rb index 38378310d9f..0d93afe9434 100644 --- a/spec/requests/api/graphql/mutations/notes/update/note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/update/note_spec.rb @@ -8,11 +8,9 @@ RSpec.describe 'Updating a Note' do let!(:note) { create(:note, note: original_body) } let(:original_body) { 'Initial body text' } let(:updated_body) { 'Updated body text' } + let(:params) { { body: updated_body, confidential: true } } let(:mutation) do - variables = { - id: GitlabSchema.id_from_object(note).to_s, - body: updated_body - } + variables = params.merge(id: GitlabSchema.id_from_object(note).to_s) graphql_mutation(:update_note, variables) end @@ -31,6 +29,7 @@ RSpec.describe 'Updating a Note' do post_graphql_mutation(mutation, current_user: current_user) expect(note.reload.note).to eq(original_body) + expect(note.confidential).to be_falsey end end @@ -43,12 +42,40 @@ RSpec.describe 'Updating a Note' do post_graphql_mutation(mutation, current_user: current_user) expect(note.reload.note).to eq(updated_body) + expect(note.confidential).to be_truthy end it 'returns the updated Note' do post_graphql_mutation(mutation, current_user: current_user) expect(mutation_response['note']['body']).to eq(updated_body) + expect(mutation_response['note']['confidential']).to be_truthy + end + + context 'when only confidential param is present' do + let(:params) { { confidential: true } } + + it 'updates only the note confidentiality' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(note.reload.note).to eq(original_body) + expect(note.confidential).to be_truthy + end + end + + context 'when only body param is present' do + let(:params) { { body: updated_body } } + + before do + note.update_column(:confidential, true) + end + + it 'updates only the note body' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(note.reload.note).to eq(updated_body) + expect(note.confidential).to be_truthy + end end context 'when there are ActiveRecord validation errors' do @@ -60,12 +87,14 @@ RSpec.describe 'Updating a Note' do post_graphql_mutation(mutation, current_user: current_user) expect(note.reload.note).to eq(original_body) + expect(note.confidential).to be_falsey end - it 'returns the Note with its original body' do + it 'returns the original Note' do post_graphql_mutation(mutation, current_user: current_user) expect(mutation_response['note']['body']).to eq(original_body) + expect(mutation_response['note']['confidential']).to be_falsey end end diff --git a/spec/services/notes/update_service_spec.rb b/spec/services/notes/update_service_spec.rb index 70dea99de4a..cfdfb10d65d 100644 --- a/spec/services/notes/update_service_spec.rb +++ b/spec/services/notes/update_service_spec.rb @@ -36,6 +36,16 @@ RSpec.describe Notes::UpdateService do end end + context 'with system note' do + before do + note.update_column(:system, true) + end + + it 'does not update the note' do + expect { update_note(note: 'new text') }.not_to change { note.reload.note } + end + end + context 'suggestions' do it 'refreshes note suggestions' do markdown = <<-MARKDOWN.strip_heredoc diff --git a/spec/support/shared_examples/requests/api/notes_shared_examples.rb b/spec/support/shared_examples/requests/api/notes_shared_examples.rb index 07d38d472cc..7066f803f9d 100644 --- a/spec/support/shared_examples/requests/api/notes_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/notes_shared_examples.rb @@ -277,12 +277,53 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name| end describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes/:note_id" do - it 'returns modified note' do - put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\ - "notes/#{note.id}", user), params: { body: 'Hello!' } + let(:params) { { body: 'Hello!', confidential: false } } - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['body']).to eq('Hello!') + subject do + put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{note.id}", user), params: params + end + + context 'when eveything is ok' do + before do + note.update!(confidential: true) + end + + context 'with multiple params present' do + before do + subject + end + + it 'returns modified note' do + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['body']).to eq('Hello!') + expect(json_response['confidential']).to be_falsey + end + + it 'updates the note' do + expect(note.reload.note).to eq('Hello!') + expect(note.confidential).to be_falsey + end + end + + context 'when only body param is present' do + let(:params) { { body: 'Hello!' } } + + it 'updates only the note text' do + expect { subject }.not_to change { note.reload.confidential } + + expect(note.note).to eq('Hello!') + end + end + + context 'when only confidential param is present' do + let(:params) { { confidential: false } } + + it 'updates only the note text' do + expect { subject }.not_to change { note.reload.note } + + expect(note.confidential).to be_falsey + end + end end it 'returns a 404 error when note id not found' do @@ -292,9 +333,9 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name| expect(response).to have_gitlab_http_status(:not_found) end - it 'returns a 400 bad request error if body not given' do + it 'returns a 400 bad request error if body is empty' do put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\ - "notes/#{note.id}", user) + "notes/#{note.id}", user), params: { body: '' } expect(response).to have_gitlab_http_status(:bad_request) end diff --git a/yarn.lock b/yarn.lock index d1a4a937692..7db20de8d49 100644 --- a/yarn.lock +++ b/yarn.lock @@ -843,15 +843,15 @@ eslint-plugin-vue "^6.2.1" vue-eslint-parser "^7.0.0" -"@gitlab/svgs@1.155.0": - version "1.155.0" - resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.155.0.tgz#c3b0abd868660486cf0e4b3a1bc018980812ca5b" - integrity sha512-gyu94CjiTOaEuwJ3GXtoKRwIOc79S+vh8o72jS4cH0h7SzLkloM9kEusPCWZsbhoLpbzTX0ncJBnyfwkWNvDZg== - -"@gitlab/ui@17.37.0": - version "17.37.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-17.37.0.tgz#1d6116b9820ff6d343886cb870ad90922ed354ce" - integrity sha512-Xq9WHhKchpr0EpCYTf+YZTMwRgRqyZ9V3byjGL54iY/a3iXfSm8EbRUIIjIe2nNLaXfJTUasms+e5WWUtWBl/w== +"@gitlab/svgs@1.156.0": + version "1.156.0" + resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.156.0.tgz#2af56246b5d71000ec81abb1281e811a921cdfd1" + integrity sha512-+b670Sxkjo80Wb4GKMZQ+xvuwu9sVvql8aS9nzw63FLn84QyqXS+jMjvyDqPAW5kly6B1Eg4Kljq0YawJ0ySBg== + +"@gitlab/ui@17.39.0": + version "17.39.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-17.39.0.tgz#04417471ac094323581482d354a33cdf0a21ec86" + integrity sha512-3KPrw+1cwF+ibY5zo01b6EsSOE2Kgflu7FGmrvJMvEgpK4w2shloGEts4vEJbPEGBpUzpjr3gQinNcoeIYu/JA== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0" |