diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-04 12:13:07 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-04 12:13:07 +0300 |
commit | 6cd4578a23ffe0fb94632f83a07a25d01f8d6821 (patch) | |
tree | d21e9881e4ceb8ae6f28451f0797acc59e7cd1e8 | |
parent | b6a194f6625042a09e083443c3326cc61aefc4c0 (diff) |
Add latest changes from gitlab-org/gitlab@master
69 files changed, 888 insertions, 1055 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 2d1e2d5918f..87641327d97 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -1420,6 +1420,10 @@ - <<: *if-force-ci when: manual allow_failure: true + - <<: *if-merge-request + changes: *code-patterns + when: manual + allow_failure: true .qa:rules:package-and-test-schedule: rules: @@ -1437,10 +1441,6 @@ rules: - !reference [".qa:rules:package-and-test-common", rules] - !reference [".qa:rules:package-and-test-schedule", rules] - - <<: *if-merge-request - changes: *code-patterns - when: manual - allow_failure: true .qa:rules:package-and-test-ce: rules: @@ -1465,9 +1465,6 @@ when: never - !reference [".qa:rules:package-and-test-common", rules] - !reference [".qa:rules:package-and-test-schedule", rules] - - <<: *if-merge-request - changes: *code-patterns - allow_failure: true .qa:rules:package-and-test-sidebar: rules: diff --git a/.rubocop.yml b/.rubocop.yml index c9c64a39288..6108b2981e9 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -151,6 +151,8 @@ RSpec/FilePath: - 'ee/spec/frontend/fixtures/*' - 'spec/requests/api/v3/*' - 'spec/fixtures/**/*' + CustomTransform: + HTTPartyBasicAuth: httparty_basic_auth # Configuration parameters: AllowSubject. RSpec/MultipleMemoizedHelpers: diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml index 205f633e9cb..966e80de174 100644 --- a/.rubocop_todo/rspec/missing_feature_category.yml +++ b/.rubocop_todo/rspec/missing_feature_category.yml @@ -5453,7 +5453,6 @@ RSpec/MissingFeatureCategory: - 'spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb' - 'spec/rubocop/cop/rspec/factory_bot/strategy_in_callback_spec.rb' - 'spec/rubocop/cop/rspec/have_gitlab_http_status_spec.rb' - - 'spec/rubocop/cop/rspec/htt_party_basic_auth_spec.rb' - 'spec/rubocop/cop/rspec/modify_sidekiq_middleware_spec.rb' - 'spec/rubocop/cop/rspec/top_level_describe_path_spec.rb' - 'spec/rubocop/cop/rspec/web_mock_enable_spec.rb' diff --git a/app/assets/javascripts/environments/components/stop_environment_modal.vue b/app/assets/javascripts/environments/components/stop_environment_modal.vue index dc0c5dc0f46..95ece2b653e 100644 --- a/app/assets/javascripts/environments/components/stop_environment_modal.vue +++ b/app/assets/javascripts/environments/components/stop_environment_modal.vue @@ -41,6 +41,9 @@ export default { text: __('Cancel'), }; }, + hasStopAction() { + return this.graphql ? this.environment.hasStopAction : this.environment.has_stop_action; + }, }, methods: { @@ -81,7 +84,7 @@ export default { <p>{{ s__('Environments|Are you sure you want to stop this environment?') }}</p> - <div v-if="!environment.has_stop_action" class="warning_message"> + <div v-if="!hasStopAction" class="warning_message"> <p> <gl-sprintf :message=" diff --git a/app/assets/javascripts/google_cloud/aiml/service_table.vue b/app/assets/javascripts/google_cloud/aiml/service_table.vue new file mode 100644 index 00000000000..b53baaa5c6f --- /dev/null +++ b/app/assets/javascripts/google_cloud/aiml/service_table.vue @@ -0,0 +1,115 @@ +<script> +import { GlButton, GlTable } from '@gitlab/ui'; +import { s__ } from '~/locale'; + +const KEY_VISION_AI = 'key-vision-ai'; +const KEY_NATURAL_LANGUAGE_AI = 'key-natural-language-ai'; +const KEY_TRANSLATION_AI = 'key-translation-ai'; + +const i18n = { + visionAi: s__('CloudSeed|Vision AI'), + visionAiDescription: s__( + 'CloudSeed|Derive insights from your images in the cloud or at the edge', + ), + naturalLanguageAi: s__('CloudSeed|Language AI'), + naturalLanguageAiDescription: s__( + 'CloudSeed|Derive insights from unstructured text using Google machine learning', + ), + translationAi: s__('CloudSeed|Translation AI'), + translationAiDescription: s__( + 'CloudSeed|Make your content and apps multilingual with fast, dynamic machine translation', + ), + aiml: s__('CloudSeed|AI / ML'), + aimlDescription: s__( + "CloudSeed|Google Cloud's AI tools are armed with the best of Google's research and technology to help developers focus exclusively on solving problems that matter", + ), + configureViaMergeRequest: s__('CloudSeed|Configure via Merge Request'), + service: s__('CloudSeed|Service'), + description: s__('CloudSeed|Description'), +}; + +export default { + components: { GlButton, GlTable }, + props: { + visionAiUrl: { + type: String, + required: true, + }, + languageAiUrl: { + type: String, + required: true, + }, + translationAiUrl: { + type: String, + required: true, + }, + }, + methods: { + actionUrl(key) { + switch (key) { + case KEY_VISION_AI: + return this.visionAiUrl; + case KEY_NATURAL_LANGUAGE_AI: + return this.languageAiUrl; + case KEY_TRANSLATION_AI: + return this.translationAiUrl; + default: + return '#'; + } + }, + }, + fields: [ + { key: 'title', label: i18n.service }, + { key: 'description', label: i18n.description }, + { key: 'action', label: '' }, + ], + items: [ + { + title: i18n.naturalLanguageAi, + description: i18n.naturalLanguageAiDescription, + action: { + key: KEY_NATURAL_LANGUAGE_AI, + testId: 'button-natural-language-ai', + title: i18n.configureViaMergeRequest, + }, + }, + { + title: i18n.translationAi, + description: i18n.translationAiDescription, + action: { + key: KEY_TRANSLATION_AI, + testId: 'button-translation-ai', + title: i18n.configureViaMergeRequest, + disabled: true, + }, + }, + { + title: i18n.visionAi, + description: i18n.visionAiDescription, + action: { + key: KEY_VISION_AI, + testId: 'button-vision-ai', + title: i18n.configureViaMergeRequest, + }, + }, + ], + i18n, +}; +</script> +<template> + <div class="gl-mx-3"> + <h2 class="gl-font-size-h2">{{ $options.i18n.aiml }}</h2> + <p>{{ $options.i18n.aimlDescription }}</p> + <gl-table :fields="$options.fields" :items="$options.items"> + <template #cell(action)="{ value }"> + <gl-button + :disabled="value.disabled" + :href="actionUrl(value.key)" + :data-testid="value.testId" + > + {{ value.title }} + </gl-button> + </template> + </gl-table> + </div> +</template> diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue index 55146610d5f..a4bbd87b34a 100644 --- a/app/assets/javascripts/super_sidebar/components/nav_item.vue +++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue @@ -129,7 +129,7 @@ export default { /> </slot> </div> - <div class="gl-pr-3 gl-text-gray-900 gl-truncate-end"> + <div class="gl-pr-8 gl-text-gray-900 gl-truncate-end"> {{ item.title }} <div v-if="item.subtitle" class="gl-font-sm gl-text-gray-500 gl-truncate-end"> {{ item.subtitle }} diff --git a/app/assets/javascripts/token_access/components/opt_in_jwt.vue b/app/assets/javascripts/token_access/components/opt_in_jwt.vue deleted file mode 100644 index 18636a26f46..00000000000 --- a/app/assets/javascripts/token_access/components/opt_in_jwt.vue +++ /dev/null @@ -1,125 +0,0 @@ -<script> -import { GlLink, GlLoadingIcon, GlSprintf, GlToggle } from '@gitlab/ui'; -import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; -import { createAlert } from '~/alert'; -import { __, s__ } from '~/locale'; -import updateOptInJwtMutation from '../graphql/mutations/update_opt_in_jwt.mutation.graphql'; -import getOptInJwtSettingQuery from '../graphql/queries/get_opt_in_jwt_setting.query.graphql'; -import { LIMIT_JWT_ACCESS_SNIPPET, OPT_IN_JWT_HELP_LINK } from '../constants'; - -export default { - i18n: { - labelText: s__('CICD|Limit JSON Web Token (JWT) access'), - helpText: s__( - `CICD|The JWT must be manually declared in each job that needs it. When disabled, the token is always available in all jobs in the pipeline. %{linkStart}Learn more.%{linkEnd}`, - ), - expandedText: s__( - 'CICD|Use the %{codeStart}secrets%{codeEnd} keyword to configure a job with a JWT.', - ), - copyToClipboard: __('Copy to clipboard'), - fetchError: s__('CICD|There was a problem fetching the token access settings.'), - updateError: s__('CICD|An error occurred while update the setting. Please try again.'), - }, - components: { - CodeInstruction, - GlLink, - GlLoadingIcon, - GlSprintf, - GlToggle, - }, - inject: ['fullPath'], - apollo: { - optInJwt: { - query: getOptInJwtSettingQuery, - variables() { - return { - fullPath: this.fullPath, - }; - }, - update({ - project: { - ciCdSettings: { optInJwt }, - }, - }) { - return optInJwt; - }, - error() { - createAlert({ message: this.$options.i18n.fetchError }); - }, - }, - }, - data() { - return { - optInJwt: null, - }; - }, - computed: { - isLoading() { - return this.$apollo.queries.optInJwt.loading; - }, - }, - methods: { - async updateOptInJwt() { - try { - const { - data: { - projectCiCdSettingsUpdate: { errors }, - }, - } = await this.$apollo.mutate({ - mutation: updateOptInJwtMutation, - variables: { - input: { - fullPath: this.fullPath, - optInJwt: this.optInJwt, - }, - }, - }); - - if (errors.length) { - throw new Error(errors[0]); - } - } catch (error) { - createAlert({ message: this.$options.i18n.updateError }); - } - }, - }, - OPT_IN_JWT_HELP_LINK, - LIMIT_JWT_ACCESS_SNIPPET, -}; -</script> -<template> - <div> - <gl-loading-icon v-if="isLoading" size="lg" class="gl-mt-5" /> - <template v-else> - <gl-toggle - v-model="optInJwt" - class="gl-mt-5" - :label="$options.i18n.labelText" - @change="updateOptInJwt" - > - <template #help> - <gl-sprintf :message="$options.i18n.helpText"> - <template #link="{ content }"> - <gl-link :href="$options.OPT_IN_JWT_HELP_LINK" class="inline-link" target="_blank"> - {{ content }} - </gl-link> - </template> - </gl-sprintf> - </template> - </gl-toggle> - <div v-if="optInJwt" class="gl-mt-5" data-testid="opt-in-jwt-expanded-section"> - <gl-sprintf :message="$options.i18n.expandedText"> - <template #code="{ content }"> - <code>{{ content }}</code> - </template> - </gl-sprintf> - <code-instruction - class="gl-mt-3" - :instruction="$options.LIMIT_JWT_ACCESS_SNIPPET" - :copy-text="$options.i18n.copyToClipboard" - multiline - /> - </div> - </template> - </div> -</template> diff --git a/app/assets/javascripts/token_access/components/token_access_app.vue b/app/assets/javascripts/token_access/components/token_access_app.vue index beaa564db51..167eebc8d9b 100644 --- a/app/assets/javascripts/token_access/components/token_access_app.vue +++ b/app/assets/javascripts/token_access/components/token_access_app.vue @@ -1,14 +1,12 @@ <script> import OutboundTokenAccess from './outbound_token_access.vue'; import InboundTokenAccess from './inbound_token_access.vue'; -import OptInJwt from './opt_in_jwt.vue'; export default { name: 'TokenAccessApp', components: { OutboundTokenAccess, InboundTokenAccess, - OptInJwt, }, }; </script> @@ -16,6 +14,5 @@ export default { <div> <inbound-token-access class="gl-pb-5" /> <outbound-token-access class="gl-py-5" /> - <opt-in-jwt /> </div> </template> diff --git a/app/assets/javascripts/token_access/constants.js b/app/assets/javascripts/token_access/constants.js deleted file mode 100644 index fb2128462f0..00000000000 --- a/app/assets/javascripts/token_access/constants.js +++ /dev/null @@ -1,14 +0,0 @@ -import { helpPagePath } from '~/helpers/help_page_helper'; - -export const LIMIT_JWT_ACCESS_SNIPPET = `job_name: - id_tokens: - ID_TOKEN_1: # or any other name - aud: "..." # sub-keyword to configure the token's audience - secrets: - TEST_SECRET: - vault: db/prod -`; - -export const OPT_IN_JWT_HELP_LINK = helpPagePath('ci/secrets/id_token_authentication', { - anchor: 'automatic-id-token-authentication-with-hashicorp-vault', -}); diff --git a/app/assets/javascripts/token_access/graphql/mutations/update_opt_in_jwt.mutation.graphql b/app/assets/javascripts/token_access/graphql/mutations/update_opt_in_jwt.mutation.graphql deleted file mode 100644 index 7f0a6bc7d6d..00000000000 --- a/app/assets/javascripts/token_access/graphql/mutations/update_opt_in_jwt.mutation.graphql +++ /dev/null @@ -1,8 +0,0 @@ -mutation updateOptInJwt($input: ProjectCiCdSettingsUpdateInput!) { - projectCiCdSettingsUpdate(input: $input) { - ciCdSettings { - optInJwt - } - errors - } -} diff --git a/app/assets/javascripts/token_access/graphql/queries/get_opt_in_jwt_setting.query.graphql b/app/assets/javascripts/token_access/graphql/queries/get_opt_in_jwt_setting.query.graphql deleted file mode 100644 index a1a216b7dc3..00000000000 --- a/app/assets/javascripts/token_access/graphql/queries/get_opt_in_jwt_setting.query.graphql +++ /dev/null @@ -1,8 +0,0 @@ -query getOptInJwtSetting($fullPath: ID!) { - project(fullPath: $fullPath) { - id - ciCdSettings { - optInJwt - } - } -} diff --git a/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue b/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue index 6c27d5a87f0..cb682e8b944 100644 --- a/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue +++ b/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue @@ -4,9 +4,8 @@ import Tracking from '~/tracking'; import { ASC } from '~/notes/constants'; import { __ } from '~/locale'; import { clearDraft } from '~/lib/utils/autosave'; -import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { getWorkItemQuery } from '../../utils'; import createNoteMutation from '../../graphql/notes/create_work_item_note.mutation.graphql'; +import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql'; import { TRACKING_CATEGORY_SHOW, i18n } from '../../constants'; import WorkItemNoteSignedOut from './work_item_note_signed_out.vue'; import WorkItemCommentLocked from './work_item_comment_locked.vue'; @@ -21,7 +20,7 @@ export default { WorkItemCommentLocked, WorkItemCommentForm, }, - mixins: [glFeatureFlagMixin(), Tracking.mixin()], + mixins: [Tracking.mixin()], props: { workItemId: { type: String, @@ -31,11 +30,6 @@ export default { type: String, required: true, }, - fetchByIid: { - type: Boolean, - required: false, - default: false, - }, queryVariables: { type: Object, required: true, @@ -89,17 +83,15 @@ export default { }, apollo: { workItem: { - query() { - return getWorkItemQuery(this.fetchByIid); - }, + query: workItemByIidQuery, variables() { return this.queryVariables; }, update(data) { - return this.fetchByIid ? data.workspace.workItems.nodes[0] : data.workItem; + return data.workspace.workItems.nodes[0]; }, skip() { - return !this.queryVariables.id && !this.queryVariables.iid; + return !this.queryVariables.iid; }, error() { this.$emit('error', i18n.fetchError); diff --git a/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue b/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue index b1ed5cb6b3a..e40c6296229 100644 --- a/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue +++ b/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue @@ -35,11 +35,6 @@ export default { type: String, required: true, }, - fetchByIid: { - type: Boolean, - required: false, - default: false, - }, discussion: { type: Array, required: true, @@ -168,7 +163,6 @@ export default { :work-item-id="workItemId" :query-variables="queryVariables" :full-path="fullPath" - :fetch-by-iid="fetchByIid" @startReplying="showReplyForm" @deleteNote="$emit('deleteNote', note)" @reportAbuse="$emit('reportAbuse', note)" @@ -201,7 +195,6 @@ export default { :can-set-work-item-metadata="canSetWorkItemMetadata" :query-variables="queryVariables" :full-path="fullPath" - :fetch-by-iid="fetchByIid" @startReplying="showReplyForm" @deleteNote="$emit('deleteNote', note)" @reportAbuse="$emit('reportAbuse', note)" @@ -229,7 +222,6 @@ export default { :can-set-work-item-metadata="canSetWorkItemMetadata" :query-variables="queryVariables" :full-path="fullPath" - :fetch-by-iid="fetchByIid" @startReplying="showReplyForm" @deleteNote="$emit('deleteNote', reply)" @reportAbuse="$emit('reportAbuse', reply)" @@ -244,7 +236,6 @@ export default { :query-variables="queryVariables" :full-path="fullPath" :work-item-id="workItemId" - :fetch-by-iid="fetchByIid" :discussion-id="discussionId" :work-item-type="workItemType" :sort-order="sortOrder" diff --git a/app/assets/javascripts/work_items/components/notes/work_item_note.vue b/app/assets/javascripts/work_items/components/notes/work_item_note.vue index 949dca72318..a6ab8a371de 100644 --- a/app/assets/javascripts/work_items/components/notes/work_item_note.vue +++ b/app/assets/javascripts/work_items/components/notes/work_item_note.vue @@ -9,7 +9,6 @@ import { updateDraft, clearDraft } from '~/lib/utils/autosave'; import { renderMarkdown } from '~/notes/utils'; import { getLocationHash } from '~/lib/utils/url_utility'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import { getWorkItemQuery } from '~/work_items/utils'; import EditedAt from '~/issues/show/components/edited.vue'; import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; import NoteBody from '~/work_items/components/notes/work_item_note_body.vue'; @@ -17,6 +16,7 @@ import NoteHeader from '~/notes/components/note_header.vue'; import NoteActions from '~/work_items/components/notes/work_item_note_actions.vue'; import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql'; import updateWorkItemNoteMutation from '../../graphql/notes/update_work_item_note.mutation.graphql'; +import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql'; import WorkItemCommentForm from './work_item_comment_form.vue'; export default { @@ -37,11 +37,6 @@ export default { type: String, required: true, }, - fetchByIid: { - type: Boolean, - required: false, - default: false, - }, queryVariables: { type: Object, required: true, @@ -161,17 +156,15 @@ export default { }, apollo: { workItem: { - query() { - return getWorkItemQuery(this.fetchByIid); - }, + query: workItemByIidQuery, variables() { return this.queryVariables; }, update(data) { - return this.fetchByIid ? data.workspace.workItems.nodes[0] : data.workItem; + return data.workspace?.workItems?.nodes[0]; }, skip() { - return !this.queryVariables.id && !this.queryVariables.iid; + return !this.queryVariables.iid; }, error() { this.$emit('error', i18n.fetchError); diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue index 5abc4da38ee..ddf7c789c09 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -733,7 +733,6 @@ export default { :work-item-iid="workItem.iid" :query-variables="queryVariables" :full-path="fullPath" - :fetch-by-iid="fetchByIid" :work-item-type="workItemType" :is-modal="isModal" :assignees="workItemAssignees && workItemAssignees.assignees.nodes" diff --git a/app/assets/javascripts/work_items/components/work_item_notes.vue b/app/assets/javascripts/work_items/components/work_item_notes.vue index e34578e1f46..42afde76a00 100644 --- a/app/assets/javascripts/work_items/components/work_item_notes.vue +++ b/app/assets/javascripts/work_items/components/work_item_notes.vue @@ -15,11 +15,7 @@ import { WORK_ITEM_NOTES_FILTER_ONLY_HISTORY, } from '~/work_items/constants'; import { ASC, DESC } from '~/notes/constants'; -import { - getWorkItemNotesQuery, - autocompleteDataSources, - markdownPreviewPath, -} from '~/work_items/utils'; +import { autocompleteDataSources, markdownPreviewPath } from '~/work_items/utils'; import { updateCacheAfterCreatingNote, updateCacheAfterDeletingNote, @@ -31,6 +27,7 @@ import workItemNoteCreatedSubscription from '~/work_items/graphql/notes/work_ite import workItemNoteUpdatedSubscription from '~/work_items/graphql/notes/work_item_note_updated.subscription.graphql'; import workItemNoteDeletedSubscription from '~/work_items/graphql/notes/work_item_note_deleted.subscription.graphql'; import deleteNoteMutation from '../graphql/notes/delete_work_item_notes.mutation.graphql'; +import workItemNotesByIidQuery from '../graphql/notes/work_item_notes_by_iid.query.graphql'; import WorkItemAddNote from './notes/work_item_add_note.vue'; export default { @@ -69,11 +66,6 @@ export default { type: String, required: true, }, - fetchByIid: { - type: Boolean, - required: false, - default: false, - }, isModal: { type: Boolean, required: false, @@ -133,7 +125,6 @@ export default { queryVariables: this.queryVariables, fullPath: this.fullPath, workItemId: this.workItemId, - fetchByIid: this.fetchByIid, workItemType: this.workItemType, sortOrder: this.sortOrder, isNewDiscussion: true, @@ -172,9 +163,7 @@ export default { }, apollo: { workItemNotes: { - query() { - return getWorkItemNotesQuery(this.fetchByIid); - }, + query: workItemNotesByIidQuery, context: { isSingleRequest: true, }, @@ -186,15 +175,11 @@ export default { }; }, update(data) { - const workItemWidgets = this.fetchByIid - ? data.workspace?.workItems?.nodes[0]?.widgets - : data.workItem?.widgets; - const discussionNodes = - workItemWidgets.find((widget) => widget.type === 'NOTES')?.discussions || []; - return discussionNodes; + const widgets = data.workspace?.workItems?.nodes[0]?.widgets; + return widgets?.find((widget) => widget.type === 'NOTES')?.discussions || []; }, skip() { - return !this.queryVariables.id && !this.queryVariables.iid; + return !this.queryVariables.iid; }, error() { this.$emit('error', i18n.fetchError); @@ -214,7 +199,7 @@ export default { { document: workItemNoteCreatedSubscription, updateQuery(previousResult, { subscriptionData }) { - return updateCacheAfterCreatingNote(previousResult, subscriptionData, this.fetchByIid); + return updateCacheAfterCreatingNote(previousResult, subscriptionData); }, variables() { return { @@ -228,7 +213,7 @@ export default { { document: workItemNoteDeletedSubscription, updateQuery(previousResult, { subscriptionData }) { - return updateCacheAfterDeletingNote(previousResult, subscriptionData, this.fetchByIid); + return updateCacheAfterDeletingNote(previousResult, subscriptionData); }, variables() { return { @@ -377,7 +362,6 @@ export default { :query-variables="queryVariables" :full-path="fullPath" :work-item-id="workItemId" - :fetch-by-iid="fetchByIid" :work-item-type="workItemType" :is-modal="isModal" :autocomplete-data-sources="autocompleteDataSources" diff --git a/app/assets/javascripts/work_items/graphql/cache_utils.js b/app/assets/javascripts/work_items/graphql/cache_utils.js index 95d68b69745..455d8b8ae7b 100644 --- a/app/assets/javascripts/work_items/graphql/cache_utils.js +++ b/app/assets/javascripts/work_items/graphql/cache_utils.js @@ -3,22 +3,12 @@ import { WIDGET_TYPE_NOTES } from '~/work_items/constants'; const isNotesWidget = (widget) => widget.type === WIDGET_TYPE_NOTES; -const getNotesWidgetFromSourceData = (draftData, fetchByIid) => { - return fetchByIid - ? draftData.workspace.workItems.nodes[0].widgets.find(isNotesWidget) - : draftData.workItem.widgets.find(isNotesWidget); -}; - -const updateNotesWidgetDataInDraftData = (draftData, notesWidget, fetchByIid) => { - const noteWidgetIndex = fetchByIid - ? draftData.workspace.workItems.nodes[0].widgets.findIndex(isNotesWidget) - : draftData.workItem.widgets.findIndex(isNotesWidget); +const getNotesWidgetFromSourceData = (draftData) => + draftData?.workspace?.workItems?.nodes[0]?.widgets.find(isNotesWidget); - if (fetchByIid) { - draftData.workspace.workItems.nodes[0].widgets[noteWidgetIndex] = notesWidget; - } else { - draftData.workItem.widgets[noteWidgetIndex] = notesWidget; - } +const updateNotesWidgetDataInDraftData = (draftData, notesWidget) => { + const noteWidgetIndex = draftData.workspace.workItems.nodes[0].widgets.findIndex(isNotesWidget); + draftData.workspace.workItems.nodes[0].widgets[noteWidgetIndex] = notesWidget; }; /** @@ -26,17 +16,16 @@ const updateNotesWidgetDataInDraftData = (draftData, notesWidget, fetchByIid) => * * @param currentNotes * @param subscriptionData - * @param fetchByIid */ -export const updateCacheAfterCreatingNote = (currentNotes, subscriptionData, fetchByIid) => { +export const updateCacheAfterCreatingNote = (currentNotes, subscriptionData) => { if (!subscriptionData.data?.workItemNoteCreated) { return currentNotes; } const newNote = subscriptionData.data.workItemNoteCreated; return produce(currentNotes, (draftData) => { - const notesWidget = getNotesWidgetFromSourceData(draftData, fetchByIid); + const notesWidget = getNotesWidgetFromSourceData(draftData); if (!notesWidget.discussions) { return; @@ -50,7 +39,7 @@ export const updateCacheAfterCreatingNote = (currentNotes, subscriptionData, fet } notesWidget.discussions.nodes.push(newNote.discussion); - updateNotesWidgetDataInDraftData(draftData, notesWidget, fetchByIid); + updateNotesWidgetDataInDraftData(draftData, notesWidget); }); }; @@ -59,10 +48,9 @@ export const updateCacheAfterCreatingNote = (currentNotes, subscriptionData, fet * * @param currentNotes * @param subscriptionData - * @param fetchByIid */ -export const updateCacheAfterDeletingNote = (currentNotes, subscriptionData, fetchByIid) => { +export const updateCacheAfterDeletingNote = (currentNotes, subscriptionData) => { if (!subscriptionData.data?.workItemNoteDeleted) { return currentNotes; } @@ -70,7 +58,7 @@ export const updateCacheAfterDeletingNote = (currentNotes, subscriptionData, fet const { id, discussionId, lastDiscussionNote } = deletedNote; return produce(currentNotes, (draftData) => { - const notesWidget = getNotesWidgetFromSourceData(draftData, fetchByIid); + const notesWidget = getNotesWidgetFromSourceData(draftData); if (!notesWidget.discussions) { return; @@ -95,6 +83,6 @@ export const updateCacheAfterDeletingNote = (currentNotes, subscriptionData, fet notesWidget.discussions.nodes[discussionIndex] = deletedThreadDiscussion; } - updateNotesWidgetDataInDraftData(draftData, notesWidget, fetchByIid); + updateNotesWidgetDataInDraftData(draftData, notesWidget); }); }; diff --git a/app/assets/javascripts/work_items/graphql/notes/work_item_notes.query.graphql b/app/assets/javascripts/work_items/graphql/notes/work_item_notes.query.graphql deleted file mode 100644 index 56dc175109f..00000000000 --- a/app/assets/javascripts/work_items/graphql/notes/work_item_notes.query.graphql +++ /dev/null @@ -1,27 +0,0 @@ -#import "~/graphql_shared/fragments/page_info.fragment.graphql" -#import "./work_item_note.fragment.graphql" - -query workItemNotes($id: WorkItemID!, $after: String, $pageSize: Int) { - workItem(id: $id) { - id - iid - widgets { - ... on WorkItemWidgetNotes { - type - discussions(first: $pageSize, after: $after, filter: ALL_NOTES) { - pageInfo { - ...PageInfo - } - nodes { - id - notes { - nodes { - ...WorkItemNote - } - } - } - } - } - } - } -} diff --git a/app/assets/javascripts/work_items/utils.js b/app/assets/javascripts/work_items/utils.js index 110e0f98c10..c5b937ef132 100644 --- a/app/assets/javascripts/work_items/utils.js +++ b/app/assets/javascripts/work_items/utils.js @@ -1,17 +1,11 @@ import { WIDGET_TYPE_HIERARCHY } from '~/work_items/constants'; import workItemQuery from './graphql/work_item.query.graphql'; import workItemByIidQuery from './graphql/work_item_by_iid.query.graphql'; -import workItemNotesIdQuery from './graphql/notes/work_item_notes.query.graphql'; -import workItemNotesByIidQuery from './graphql/notes/work_item_notes_by_iid.query.graphql'; export function getWorkItemQuery(isFetchedByIid) { return isFetchedByIid ? workItemByIidQuery : workItemQuery; } -export function getWorkItemNotesQuery(isFetchedByIid) { - return isFetchedByIid ? workItemNotesByIidQuery : workItemNotesIdQuery; -} - export const findHierarchyWidgets = (widgets) => widgets?.find((widget) => widget.type === WIDGET_TYPE_HIERARCHY); diff --git a/app/mailers/emails/service_desk.rb b/app/mailers/emails/service_desk.rb index e75882073f2..b9d8735494a 100644 --- a/app/mailers/emails/service_desk.rb +++ b/app/mailers/emails/service_desk.rb @@ -157,6 +157,7 @@ module Emails .gsub(/%\{\s*ISSUE_ID\s*\}/, issue_id) .gsub(/%\{\s*ISSUE_PATH\s*\}/, issue_path) .gsub(/%\{\s*NOTE_TEXT\s*\}/, note_text) + .gsub(/%\{\s*ISSUE_DESCRIPTION\s*\}/, issue_description) .gsub(/%\{\s*SYSTEM_HEADER\s*\}/, text_header_message.to_s) .gsub(/%\{\s*SYSTEM_FOOTER\s*\}/, text_footer_message.to_s) .gsub(/%\{\s*UNSUBSCRIBE_URL\s*\}/, unsubscribe_sent_notification_url(@sent_notification)) @@ -175,6 +176,10 @@ module Emails @note&.note.to_s end + def issue_description + @issue.description_html.to_s + end + def subject_base "#{@issue.title} (##{@issue.iid})" end diff --git a/app/models/ci/pipeline_variable.rb b/app/models/ci/pipeline_variable.rb index 8e83b41cd0b..f2457af0074 100644 --- a/app/models/ci/pipeline_variable.rb +++ b/app/models/ci/pipeline_variable.rb @@ -6,6 +6,9 @@ module Ci include Ci::HasVariable include Ci::RawVariable + include IgnorableColumns + ignore_column :id_convert_to_bigint, remove_with: '16.3', remove_after: '2023-08-22' + belongs_to :pipeline partitionable scope: :pipeline diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index a81afa5f450..13d628ec9a4 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -3,6 +3,7 @@ - add_page_specific_style 'page_bundles/projects_edit' - expanded = expanded_by_default? - reduce_visibility_form_id = 'reduce-visibility-form' +- @force_desktop_expanded_sidebar = true = render_if_exists 'shared/ultimate_feature_removal_banner', project: @project diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml index 35214ad38dc..4d71161c96e 100644 --- a/app/views/projects/hooks/index.html.haml +++ b/app/views/projects/hooks/index.html.haml @@ -1,5 +1,6 @@ - breadcrumb_title _('Webhook Settings') - page_title _('Webhooks') +- @force_desktop_expanded_sidebar = true .row.gl-mt-3.js-search-settings-section .col-lg-4 diff --git a/app/views/projects/settings/access_tokens/index.html.haml b/app/views/projects/settings/access_tokens/index.html.haml index b581ccaceec..26c08fcdfe4 100644 --- a/app/views/projects/settings/access_tokens/index.html.haml +++ b/app/views/projects/settings/access_tokens/index.html.haml @@ -2,6 +2,7 @@ - page_title _('Project Access Tokens') - type = _('project access token') - type_plural = _('project access tokens') +- @force_desktop_expanded_sidebar = true .row.gl-mt-3.js-search-settings-section .col-lg-4 diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index d8e26c7ad72..c7bb6a7f5da 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -1,5 +1,6 @@ - page_title _("CI/CD Settings") - page_title _("CI/CD") +- @force_desktop_expanded_sidebar = true - expanded = expanded_by_default? - general_expanded = @project.errors.empty? ? expanded : true diff --git a/app/views/projects/settings/integrations/index.html.haml b/app/views/projects/settings/integrations/index.html.haml index ed65cce5acb..59cdda5bb92 100644 --- a/app/views/projects/settings/integrations/index.html.haml +++ b/app/views/projects/settings/integrations/index.html.haml @@ -1,5 +1,6 @@ - breadcrumb_title _('Integration Settings') - page_title _('Integrations') +- @force_desktop_expanded_sidebar = true = render 'shared/integrations/slack_notifications_deprecation_alert' diff --git a/app/views/projects/settings/merge_requests/show.html.haml b/app/views/projects/settings/merge_requests/show.html.haml index 6cc5dfd8c90..ef528d17fc9 100644 --- a/app/views/projects/settings/merge_requests/show.html.haml +++ b/app/views/projects/settings/merge_requests/show.html.haml @@ -1,5 +1,6 @@ - breadcrumb_title _('Merge requests') - page_title _('Merge requests') +- @force_desktop_expanded_sidebar = true %section.rspec-merge-request-settings.settings.merge-requests-feature.no-animate#js-merge-request-settings.expanded{ class: [('hidden' if @project.project_feature.send(:merge_requests_access_level) == 0)], data: { qa_selector: 'merge_request_settings_content' } } .settings-header diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml index bfa637ee35e..d44ebf1eb83 100644 --- a/app/views/projects/settings/operations/show.html.haml +++ b/app/views/projects/settings/operations/show.html.haml @@ -1,5 +1,6 @@ - page_title _('Monitor Settings') - breadcrumb_title _('Monitor Settings') +- @force_desktop_expanded_sidebar = true - if Feature.disabled?(:remove_monitor_metrics) = render 'projects/settings/operations/metrics_dashboard' diff --git a/app/views/projects/settings/packages_and_registries/show.html.haml b/app/views/projects/settings/packages_and_registries/show.html.haml index 22385677192..6f38a3ace92 100644 --- a/app/views/projects/settings/packages_and_registries/show.html.haml +++ b/app/views/projects/settings/packages_and_registries/show.html.haml @@ -1,4 +1,5 @@ - breadcrumb_title _('Packages and registries settings') - page_title _('Packages and registries settings') +- @force_desktop_expanded_sidebar = true #js-registry-settings{ data: settings_data } diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml index c532c19e0d1..12404180362 100644 --- a/app/views/projects/settings/repository/show.html.haml +++ b/app/views/projects/settings/repository/show.html.haml @@ -1,6 +1,7 @@ - breadcrumb_title _("Repository Settings") - page_title _("Repository") - deploy_token_description = s_('DeployTokens|Deploy tokens allow access to packages, your repository, and registry images.') +- @force_desktop_expanded_sidebar = true = render "projects/branch_defaults/show" - if Feature.enabled?(:branch_rules, @project) diff --git a/app/views/projects/usage_quotas/index.html.haml b/app/views/projects/usage_quotas/index.html.haml index 5bfe6b650d1..1dd179f7b38 100644 --- a/app/views/projects/usage_quotas/index.html.haml +++ b/app/views/projects/usage_quotas/index.html.haml @@ -1,5 +1,6 @@ - page_title s_("UsageQuota|Usage") - add_page_specific_style 'page_bundles/projects_usage_quotas' +- @force_desktop_expanded_sidebar = true = render_if_exists 'shared/ultimate_feature_removal_banner', project: @project diff --git a/config/feature_flags/development/jira_dvcs_end_of_life_amnesty.yml b/config/feature_flags/development/jira_dvcs_end_of_life_amnesty.yml index 1d0f364580a..dd72f9e13dd 100644 --- a/config/feature_flags/development/jira_dvcs_end_of_life_amnesty.yml +++ b/config/feature_flags/development/jira_dvcs_end_of_life_amnesty.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/408148 milestone: '16.0' type: development group: group::import and integrate -default_enabled: true +default_enabled: false diff --git a/config/feature_flags/development/use_replica_for_mailers.yml b/config/feature_flags/development/use_replica_for_mailers.yml deleted file mode 100644 index 06dd2d699c7..00000000000 --- a/config/feature_flags/development/use_replica_for_mailers.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: use_replica_for_mailers -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/116357 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/408785 -milestone: '16.0' -type: development -group: group::scalability -default_enabled: false diff --git a/config/initializers/mailer_retries.rb b/config/initializers/mailer_retries.rb index cf41ecf1952..6812d3e5742 100644 --- a/config/initializers/mailer_retries.rb +++ b/config/initializers/mailer_retries.rb @@ -3,5 +3,5 @@ Rails.application.config.after_initialize do ActionMailer::MailDeliveryJob.sidekiq_options retry: 3 ActionMailer::MailDeliveryJob.include(WorkerAttributes) - ActionMailer::MailDeliveryJob.data_consistency :delayed, feature_flag: :use_replica_for_mailers + ActionMailer::MailDeliveryJob.data_consistency :delayed end diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index ea3260d8260..f0917c9265b 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -113,6 +113,8 @@ - 1 - - compliance_management_chain_of_custody_report - 1 +- - compliance_management_framework_export_mailer + - 1 - - compliance_management_merge_requests_compliance_violations - 1 - - compliance_management_update_default_framework diff --git a/db/docs/analytics_cycle_analytics_project_stages.yml b/db/docs/deleted_tables/analytics_cycle_analytics_project_stages.yml index 4d6baab5a00..75e1d307d59 100644 --- a/db/docs/analytics_cycle_analytics_project_stages.yml +++ b/db/docs/deleted_tables/analytics_cycle_analytics_project_stages.yml @@ -7,3 +7,5 @@ description: Persists project level value stream analytics stages. Scheduled for introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/15061 milestone: '12.2' gitlab_schema: gitlab_main +removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118980 +removed_in_milestone: '16.0' diff --git a/db/docs/analytics_cycle_analytics_project_value_streams.yml b/db/docs/deleted_tables/analytics_cycle_analytics_project_value_streams.yml index 839b49f1bed..8fbaf68ec7f 100644 --- a/db/docs/analytics_cycle_analytics_project_value_streams.yml +++ b/db/docs/deleted_tables/analytics_cycle_analytics_project_value_streams.yml @@ -7,3 +7,5 @@ description: Used to store the value stream configurations for projects. Schedul introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60925 milestone: '13.12' gitlab_schema: gitlab_main +removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118980 +removed_in_milestone: '16.0' diff --git a/db/migrate/20230427065641_initialize_conversion_of_ci_pipeline_variables.rb b/db/migrate/20230427065641_initialize_conversion_of_ci_pipeline_variables.rb new file mode 100644 index 00000000000..1c986dee3d2 --- /dev/null +++ b/db/migrate/20230427065641_initialize_conversion_of_ci_pipeline_variables.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class InitializeConversionOfCiPipelineVariables < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + TABLE = :ci_pipeline_variables + COLUMNS = %i[id] + + def up + initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS) + end + + def down + revert_initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS) + end +end diff --git a/db/post_migrate/20230427065942_backfill_ci_pipeline_variables_for_bigint_conversion.rb b/db/post_migrate/20230427065942_backfill_ci_pipeline_variables_for_bigint_conversion.rb new file mode 100644 index 00000000000..c73c29bbbad --- /dev/null +++ b/db/post_migrate/20230427065942_backfill_ci_pipeline_variables_for_bigint_conversion.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class BackfillCiPipelineVariablesForBigintConversion < Gitlab::Database::Migration[2.1] + restrict_gitlab_migration gitlab_schema: :gitlab_ci + + TABLE = :ci_pipeline_variables + COLUMNS = %i[id] + + def up + backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS, sub_batch_size: 500) + end + + def down + revert_backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS) + end +end diff --git a/db/post_migrate/20230427190005_drop_foreign_keys_from_cycle_analytics_unused_tables.rb b/db/post_migrate/20230427190005_drop_foreign_keys_from_cycle_analytics_unused_tables.rb new file mode 100644 index 00000000000..a0a6679f136 --- /dev/null +++ b/db/post_migrate/20230427190005_drop_foreign_keys_from_cycle_analytics_unused_tables.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +class DropForeignKeysFromCycleAnalyticsUnusedTables < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + with_lock_retries do + remove_foreign_key :analytics_cycle_analytics_project_stages, column: :stage_event_hash_id + remove_foreign_key :analytics_cycle_analytics_project_stages, column: :project_id + remove_foreign_key :analytics_cycle_analytics_project_stages, column: :start_event_label_id + remove_foreign_key :analytics_cycle_analytics_project_stages, column: :end_event_label_id + remove_foreign_key :analytics_cycle_analytics_project_stages, column: :project_value_stream_id + + remove_foreign_key :analytics_cycle_analytics_project_value_streams, column: :project_id + end + end + + def down + add_concurrent_foreign_key(:analytics_cycle_analytics_project_stages, + :analytics_cycle_analytics_stage_event_hashes, + name: "fk_c3339bdfc9", + column: :stage_event_hash_id, + target_column: :id, + on_delete: :cascade + ) + + add_concurrent_foreign_key(:analytics_cycle_analytics_project_stages, + :labels, + name: "fk_rails_1722574860", + column: :start_event_label_id, + target_column: :id, + on_delete: :cascade + ) + + add_concurrent_foreign_key(:analytics_cycle_analytics_project_stages, + :projects, + name: "fk_rails_3829e49b66", + column: :project_id, + target_column: :id, + on_delete: :cascade + ) + + add_concurrent_foreign_key(:analytics_cycle_analytics_project_stages, + :labels, + name: "fk_rails_3ec9fd7912", + column: :end_event_label_id, + target_column: :id, + on_delete: :cascade + ) + + add_concurrent_foreign_key(:analytics_cycle_analytics_project_stages, + :analytics_cycle_analytics_project_value_streams, + name: "fk_rails_796a7dbc9c", + column: :project_value_stream_id, + target_column: :id, + on_delete: :cascade + ) + + add_concurrent_foreign_key(:analytics_cycle_analytics_project_value_streams, + :projects, + name: "fk_rails_669f4ba293", + column: :project_id, + target_column: :id, + on_delete: :cascade + ) + end +end diff --git a/db/post_migrate/20230427194552_drop_cycle_analytics_unused_tables.rb b/db/post_migrate/20230427194552_drop_cycle_analytics_unused_tables.rb new file mode 100644 index 00000000000..7c10238a1bc --- /dev/null +++ b/db/post_migrate/20230427194552_drop_cycle_analytics_unused_tables.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +# See https://docs.gitlab.com/ee/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class DropCycleAnalyticsUnusedTables < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + drop_table :analytics_cycle_analytics_project_stages + drop_table :analytics_cycle_analytics_project_value_streams + end + + def down + create_analytics_cycle_analytics_project_value_streams_table + create_analytics_cycle_analytics_project_stages_table + end + + def create_analytics_cycle_analytics_project_value_streams_table + index_name = 'index_analytics_ca_project_value_streams_on_project_id_and_name' + + # rubocop:disable Migration/SchemaAdditionMethodsNoPost + # rubocop:disable Migration/AddLimitToTextColumns + create_table :analytics_cycle_analytics_project_value_streams do |t| + t.timestamps_with_timezone + t.references(:project, + null: false, + index: false, + foreign_key: { to_table: :projects, on_delete: :cascade } + ) + t.text :name, null: false + t.index [:project_id, :name], unique: true, name: index_name + end + # rubocop:enable Migration/SchemaAdditionMethodsNoPost + # rubocop:enable Migration/AddLimitToTextColumns + + add_text_limit :analytics_cycle_analytics_project_value_streams, :name, 100 + end + + def create_analytics_cycle_analytics_project_stages_table + index_prefix = 'index_analytics_ca_project_stages_' + + # rubocop:disable Migration/SchemaAdditionMethodsNoPost + create_table :analytics_cycle_analytics_project_stages do |t| + t.timestamps_with_timezone + t.integer :relative_position + t.integer :start_event_identifier, null: false + t.integer :end_event_identifier, null: false + t.references(:project, null: false, + foreign_key: { to_table: :projects, on_delete: :cascade }, + index: { name: "#{index_prefix}on_project_id" } + ) + t.references(:start_event_label, + foreign_key: { to_table: :labels, on_delete: :cascade }, + index: { name: "#{index_prefix}on_start_event_label_id" } + ) + t.references(:end_event_label, + foreign_key: { to_table: :labels, on_delete: :cascade }, + index: { name: "#{index_prefix}on_end_event_label_id" } + ) + t.boolean :hidden, default: false, null: false + t.boolean :custom, default: true, null: false + t.string :name, null: false, limit: 255 # rubocop: disable Migration/PreventStrings + t.references(:project_value_stream, null: false, + foreign_key: { to_table: :analytics_cycle_analytics_project_value_streams, on_delete: :cascade }, + index: { name: "#{index_prefix}on_value_stream_id" } + ) + t.references(:stage_event_hash, + foreign_key: { to_table: :analytics_cycle_analytics_stage_event_hashes, on_delete: :cascade }, + index: { name: 'index_project_stages_on_stage_event_hash_id' } + ) + end + # rubocop:enable Migration/SchemaAdditionMethodsNoPost + + add_check_constraint :analytics_cycle_analytics_project_stages, 'stage_event_hash_id IS NOT NULL', + 'check_8f6019de1e' + + add_index :analytics_cycle_analytics_project_stages, [:project_id, :name], unique: true, + name: "#{index_prefix}on_project_id_and_name" + add_index :analytics_cycle_analytics_project_stages, [:relative_position], + name: "#{index_prefix}on_relative_position" + end +end diff --git a/db/schema_migrations/20230427065641 b/db/schema_migrations/20230427065641 new file mode 100644 index 00000000000..4a202ea0bc7 --- /dev/null +++ b/db/schema_migrations/20230427065641 @@ -0,0 +1 @@ +69f768f9b0eed0f81e206b819f403b3d3b9b2f16d415fcfbb4d9eafc920b55d6
\ No newline at end of file diff --git a/db/schema_migrations/20230427065942 b/db/schema_migrations/20230427065942 new file mode 100644 index 00000000000..6bb8571c4f4 --- /dev/null +++ b/db/schema_migrations/20230427065942 @@ -0,0 +1 @@ +88617cc027cd260d56db9d2c44724e4ffba0b01604cb1c444ee9af2e36919cc8
\ No newline at end of file diff --git a/db/schema_migrations/20230427190005 b/db/schema_migrations/20230427190005 new file mode 100644 index 00000000000..514c2c9071d --- /dev/null +++ b/db/schema_migrations/20230427190005 @@ -0,0 +1 @@ +b704051dd0fd69f22a1f33739fd8d969acd0c56e4b56c8711daf10a735bc2027
\ No newline at end of file diff --git a/db/schema_migrations/20230427194552 b/db/schema_migrations/20230427194552 new file mode 100644 index 00000000000..10d4b9e687e --- /dev/null +++ b/db/schema_migrations/20230427194552 @@ -0,0 +1 @@ +e0b4dc1848c0d4c5880366861b9806f98747d5bcddc270c14c7757c4d5398819
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index a5bf3bd3829..d2cabbf6636 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -237,6 +237,15 @@ RETURN NULL; END $$; +CREATE FUNCTION trigger_023e82d8e257() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW."id_convert_to_bigint" := NEW."id"; + RETURN NEW; +END; +$$; + CREATE FUNCTION trigger_080e73845bfd() RETURNS trigger LANGUAGE plpgsql AS $$ @@ -11142,51 +11151,6 @@ CREATE SEQUENCE analytics_cycle_analytics_group_value_streams_id_seq ALTER SEQUENCE analytics_cycle_analytics_group_value_streams_id_seq OWNED BY analytics_cycle_analytics_group_value_streams.id; -CREATE TABLE analytics_cycle_analytics_project_stages ( - id bigint NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - relative_position integer, - start_event_identifier integer NOT NULL, - end_event_identifier integer NOT NULL, - project_id bigint NOT NULL, - start_event_label_id bigint, - end_event_label_id bigint, - hidden boolean DEFAULT false NOT NULL, - custom boolean DEFAULT true NOT NULL, - name character varying(255) NOT NULL, - project_value_stream_id bigint NOT NULL, - stage_event_hash_id bigint, - CONSTRAINT check_8f6019de1e CHECK ((stage_event_hash_id IS NOT NULL)) -); - -CREATE SEQUENCE analytics_cycle_analytics_project_stages_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - -ALTER SEQUENCE analytics_cycle_analytics_project_stages_id_seq OWNED BY analytics_cycle_analytics_project_stages.id; - -CREATE TABLE analytics_cycle_analytics_project_value_streams ( - id bigint NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - project_id bigint NOT NULL, - name text NOT NULL, - CONSTRAINT check_9b1970a898 CHECK ((char_length(name) <= 100)) -); - -CREATE SEQUENCE analytics_cycle_analytics_project_value_streams_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - -ALTER SEQUENCE analytics_cycle_analytics_project_value_streams_id_seq OWNED BY analytics_cycle_analytics_project_value_streams.id; - CREATE TABLE analytics_cycle_analytics_stage_event_hashes ( id bigint NOT NULL, hash_sha256 bytea @@ -13654,7 +13618,8 @@ CREATE TABLE ci_pipeline_variables ( pipeline_id integer NOT NULL, variable_type smallint DEFAULT 1 NOT NULL, partition_id bigint DEFAULT 100 NOT NULL, - raw boolean DEFAULT false NOT NULL + raw boolean DEFAULT false NOT NULL, + id_convert_to_bigint bigint DEFAULT 0 NOT NULL ); CREATE SEQUENCE ci_pipeline_variables_id_seq @@ -24820,10 +24785,6 @@ ALTER TABLE ONLY analytics_cycle_analytics_group_stages ALTER COLUMN id SET DEFA ALTER TABLE ONLY analytics_cycle_analytics_group_value_streams ALTER COLUMN id SET DEFAULT nextval('analytics_cycle_analytics_group_value_streams_id_seq'::regclass); -ALTER TABLE ONLY analytics_cycle_analytics_project_stages ALTER COLUMN id SET DEFAULT nextval('analytics_cycle_analytics_project_stages_id_seq'::regclass); - -ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams ALTER COLUMN id SET DEFAULT nextval('analytics_cycle_analytics_project_value_streams_id_seq'::regclass); - ALTER TABLE ONLY analytics_cycle_analytics_stage_event_hashes ALTER COLUMN id SET DEFAULT nextval('analytics_cycle_analytics_stage_event_hashes_id_seq'::regclass); ALTER TABLE ONLY analytics_dashboards_pointers ALTER COLUMN id SET DEFAULT nextval('analytics_dashboards_pointers_id_seq'::regclass); @@ -26554,12 +26515,6 @@ ALTER TABLE ONLY analytics_cycle_analytics_group_stages ALTER TABLE ONLY analytics_cycle_analytics_group_value_streams ADD CONSTRAINT analytics_cycle_analytics_group_value_streams_pkey PRIMARY KEY (id); -ALTER TABLE ONLY analytics_cycle_analytics_project_stages - ADD CONSTRAINT analytics_cycle_analytics_project_stages_pkey PRIMARY KEY (id); - -ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams - ADD CONSTRAINT analytics_cycle_analytics_project_value_streams_pkey PRIMARY KEY (id); - ALTER TABLE ONLY analytics_cycle_analytics_stage_event_hashes ADD CONSTRAINT analytics_cycle_analytics_stage_event_hashes_pkey PRIMARY KEY (id); @@ -29776,20 +29731,6 @@ CREATE INDEX index_analytics_ca_group_stages_on_value_stream_id ON analytics_cyc CREATE UNIQUE INDEX index_analytics_ca_group_value_streams_on_group_id_and_name ON analytics_cycle_analytics_group_value_streams USING btree (group_id, name); -CREATE INDEX index_analytics_ca_project_stages_on_end_event_label_id ON analytics_cycle_analytics_project_stages USING btree (end_event_label_id); - -CREATE INDEX index_analytics_ca_project_stages_on_project_id ON analytics_cycle_analytics_project_stages USING btree (project_id); - -CREATE UNIQUE INDEX index_analytics_ca_project_stages_on_project_id_and_name ON analytics_cycle_analytics_project_stages USING btree (project_id, name); - -CREATE INDEX index_analytics_ca_project_stages_on_relative_position ON analytics_cycle_analytics_project_stages USING btree (relative_position); - -CREATE INDEX index_analytics_ca_project_stages_on_start_event_label_id ON analytics_cycle_analytics_project_stages USING btree (start_event_label_id); - -CREATE INDEX index_analytics_ca_project_stages_on_value_stream_id ON analytics_cycle_analytics_project_stages USING btree (project_value_stream_id); - -CREATE UNIQUE INDEX index_analytics_ca_project_value_streams_on_project_id_and_name ON analytics_cycle_analytics_project_value_streams USING btree (project_id, name); - CREATE INDEX index_analytics_cycle_analytics_group_stages_custom_only ON analytics_cycle_analytics_group_stages USING btree (id) WHERE (custom = true); CREATE UNIQUE INDEX index_analytics_dashboards_pointers_on_namespace_id ON analytics_dashboards_pointers USING btree (namespace_id); @@ -32054,8 +31995,6 @@ CREATE INDEX index_project_settings_on_project_id_partially ON project_settings CREATE UNIQUE INDEX index_project_settings_on_push_rule_id ON project_settings USING btree (push_rule_id); -CREATE INDEX index_project_stages_on_stage_event_hash_id ON analytics_cycle_analytics_project_stages USING btree (stage_event_hash_id); - CREATE INDEX index_project_states_failed_verification ON project_states USING btree (verification_retry_at NULLS FIRST) WHERE (verification_state = 3); CREATE INDEX index_project_states_needs_verification ON project_states USING btree (verification_state) WHERE ((verification_state = 0) OR (verification_state = 3)); @@ -34596,6 +34535,8 @@ CREATE TRIGGER push_rules_loose_fk_trigger AFTER DELETE ON push_rules REFERENCIN CREATE TRIGGER tags_loose_fk_trigger AFTER DELETE ON tags REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records(); +CREATE TRIGGER trigger_023e82d8e257 BEFORE INSERT OR UPDATE ON ci_pipeline_variables FOR EACH ROW EXECUTE FUNCTION trigger_023e82d8e257(); + CREATE TRIGGER trigger_080e73845bfd BEFORE INSERT OR UPDATE ON notes FOR EACH ROW EXECUTE FUNCTION trigger_080e73845bfd(); CREATE TRIGGER trigger_0e214b8a14f2 BEFORE INSERT OR UPDATE ON vulnerability_user_mentions FOR EACH ROW EXECUTE FUNCTION trigger_0e214b8a14f2(); @@ -35424,9 +35365,6 @@ ALTER TABLE ONLY packages_packages ALTER TABLE ONLY sbom_occurrences ADD CONSTRAINT fk_c2a5562923 FOREIGN KEY (source_id) REFERENCES sbom_sources(id) ON DELETE CASCADE; -ALTER TABLE ONLY analytics_cycle_analytics_project_stages - ADD CONSTRAINT fk_c3339bdfc9 FOREIGN KEY (stage_event_hash_id) REFERENCES analytics_cycle_analytics_stage_event_hashes(id) ON DELETE CASCADE; - ALTER TABLE ONLY user_group_callouts ADD CONSTRAINT fk_c366e12ec3 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; @@ -35853,9 +35791,6 @@ ALTER TABLE ONLY catalog_resources ALTER TABLE ONLY project_deploy_tokens ADD CONSTRAINT fk_rails_170e03cbaf FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; -ALTER TABLE ONLY analytics_cycle_analytics_project_stages - ADD CONSTRAINT fk_rails_1722574860 FOREIGN KEY (start_event_label_id) REFERENCES labels(id) ON DELETE CASCADE; - ALTER TABLE ONLY security_orchestration_policy_rule_schedules ADD CONSTRAINT fk_rails_17ade83f17 FOREIGN KEY (security_orchestration_policy_configuration_id) REFERENCES security_orchestration_policy_configurations(id) ON DELETE CASCADE; @@ -36081,9 +36016,6 @@ ALTER TABLE ONLY merge_request_reviewers ALTER TABLE ONLY group_merge_request_approval_settings ADD CONSTRAINT fk_rails_37b6b4cdba FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE; -ALTER TABLE ONLY analytics_cycle_analytics_project_stages - ADD CONSTRAINT fk_rails_3829e49b66 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; - ALTER TABLE ONLY packages_debian_project_distribution_keys ADD CONSTRAINT fk_rails_3834a11264 FOREIGN KEY (distribution_id) REFERENCES packages_debian_project_distributions(id) ON DELETE CASCADE; @@ -36123,9 +36055,6 @@ ALTER TABLE ONLY snippet_user_mentions ALTER TABLE ONLY epic_user_mentions ADD CONSTRAINT fk_rails_3eaf4d88cc FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE CASCADE; -ALTER TABLE ONLY analytics_cycle_analytics_project_stages - ADD CONSTRAINT fk_rails_3ec9fd7912 FOREIGN KEY (end_event_label_id) REFERENCES labels(id) ON DELETE CASCADE; - ALTER TABLE ONLY issuable_resource_links ADD CONSTRAINT fk_rails_3f0ec6b1cf FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE; @@ -36417,9 +36346,6 @@ ALTER TABLE ONLY namespace_admin_notes ALTER TABLE ONLY ci_runner_machines ADD CONSTRAINT fk_rails_666b61f04f FOREIGN KEY (runner_id) REFERENCES ci_runners(id) ON DELETE CASCADE; -ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams - ADD CONSTRAINT fk_rails_669f4ba293 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; - ALTER TABLE ONLY jira_imports ADD CONSTRAINT fk_rails_675d38c03b FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE SET NULL; @@ -36537,9 +36463,6 @@ ALTER TABLE ONLY packages_debian_group_distribution_keys ALTER TABLE ONLY terraform_states ADD CONSTRAINT fk_rails_78f54ca485 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; -ALTER TABLE ONLY analytics_cycle_analytics_project_stages - ADD CONSTRAINT fk_rails_796a7dbc9c FOREIGN KEY (project_value_stream_id) REFERENCES analytics_cycle_analytics_project_value_streams(id) ON DELETE CASCADE; - ALTER TABLE ONLY software_license_policies ADD CONSTRAINT fk_rails_7a7a2a92de FOREIGN KEY (software_license_id) REFERENCES software_licenses(id) ON DELETE CASCADE; diff --git a/doc/ci/secrets/id_token_authentication.md b/doc/ci/secrets/id_token_authentication.md index c5b19797a05..c1a6282447b 100644 --- a/doc/ci/secrets/id_token_authentication.md +++ b/doc/ci/secrets/id_token_authentication.md @@ -130,7 +130,13 @@ manual_authentication: You can use ID tokens to automatically fetch secrets from HashiCorp Vault with the [`secrets`](../yaml/index.md#secrets) keyword. -### Enable automatic ID token authentication +<!--- start_remove The following content will be removed on remove_date: '2023-05-11' --> + +### Enable automatic ID token authentication (deprecated) + +WARNING: +This setting was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/391886) in GitLab 16.0. +ID token authentication is now always available, and JSON Web Token access is always limited. To enable automatic ID token authentication: @@ -139,6 +145,8 @@ To enable automatic ID token authentication: 1. Expand **Token Access**. 1. Toggle **Limit JSON Web Token (JWT) access** to enabled. +<!--- end_remove --> + ### Configure automatic ID Token authentication If one ID token is defined, the `secrets` keyword automatically uses it to authenticate with Vault. For example: diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 39f979d98d5..9cf718573ec 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -3831,9 +3831,6 @@ job: Use `secrets:token` to explicitly select a token to use when authenticating with Vault by referencing the token's CI/CD variable. -This keyword has no effect if [**Limit JSON Web Token (JWT) access**](../secrets/id_token_authentication.md#enable-automatic-id-token-authentication) -is disabled. - **Keyword type**: Job keyword. You can use it only as part of a job. **Possible inputs**: @@ -3857,8 +3854,8 @@ job: **Additional details**: -- When the `token` keyword is not set and **Limit JSON Web Token (JWT) access** enabled, the first ID token - is used to authenticate. +- When the `token` keyword is not set, the first ID token is used to authenticate. +- In GitLab 15.8 to 15.11, you must enable [**Limit JSON Web Token (JWT) access**](../secrets/id_token_authentication.md#enable-automatic-id-token-authentication-deprecated) for this keyword to be available. - When **Limit JSON Web Token (JWT) access** is disabled, the `token` keyword is ignored and the `CI_JOB_JWT` CI/CD variable is used to authenticate. diff --git a/doc/development/documentation/topic_types/tutorial.md b/doc/development/documentation/topic_types/tutorial.md index 52f715cfcf3..91f426147b5 100644 --- a/doc/development/documentation/topic_types/tutorial.md +++ b/doc/development/documentation/topic_types/tutorial.md @@ -27,8 +27,10 @@ In general, you might consider using a tutorial when: ## Tutorial file name and location -For tutorial Markdown files, create a subfolder under `doc/tutorials`. -The tutorial file should be `index.md`. +For tutorial Markdown files, you can either: + +- Save the file in a directory with the product documentation. +- Create a subfolder under `doc/tutorials` and name the file `index.md`. ## Tutorial format diff --git a/doc/integration/jira/development_panel.md b/doc/integration/jira/development_panel.md index e4fe94476bd..009e620f121 100644 --- a/doc/integration/jira/development_panel.md +++ b/doc/integration/jira/development_panel.md @@ -92,3 +92,8 @@ For more information about how Smart Commits work and what commands are availabl - [Process issues with Smart Commits](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/) - [Using Smart Commits](https://confluence.atlassian.com/fisheye/using-smart-commits-960155400.html) + +## Troubleshooting + +To troubleshoot the Jira development panel on your own server, see the +[Atlassian documentation](https://confluence.atlassian.com/jirakb/troubleshoot-the-development-panel-in-jira-server-574685212.html). diff --git a/doc/integration/jira/dvcs/index.md b/doc/integration/jira/dvcs/index.md index dc2b488e043..580086e15ff 100644 --- a/doc/integration/jira/dvcs/index.md +++ b/doc/integration/jira/dvcs/index.md @@ -78,8 +78,3 @@ can refresh the data manually from the Jira interface: 1. Select **DVCS accounts**. 1. In the table, for the repository you want to refresh, in the **Last Activity** column, select the icon. - -## Troubleshooting - -To troubleshoot the Jira development panel on your own server, see the -[Atlassian documentation](https://confluence.atlassian.com/jirakb/troubleshoot-the-development-panel-in-jira-server-574685212.html). diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index 766fc4504cb..0ef87adfb89 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -773,34 +773,6 @@ As a result, it only supports Java 8, 11, and 17. Dependency scanning for Gradle projects and auto-remediation for Yarn projects are not supported in FIPS mode. -## Interacting with the vulnerabilities - -Once a vulnerability is found, you can interact with it. Read more on how to -[address the vulnerabilities](../vulnerabilities/index.md). - -## Solutions for vulnerabilities - -Some vulnerabilities can be fixed by applying the solution that GitLab -automatically generates. Read more about the -[solutions for vulnerabilities](../vulnerabilities/index.md#resolve-a-vulnerability). - -## Security Dashboard - -The Security Dashboard is a good place to get an overview of all the security -vulnerabilities in your groups, projects and pipelines. Read more about the -[Security Dashboard](../security_dashboard/index.md). - -## Vulnerabilities database update - -For more information about the vulnerabilities database update, see the -[maintenance table](../index.md#vulnerability-scanner-maintenance). - -## Dependency List - -An additional benefit of dependency scanning is the ability to view your -project's dependencies and their known vulnerabilities. Read more about -the [Dependency List](../dependency_list/index.md). - ## Reports JSON format The dependency scanning tool emits a JSON report file. For more information, see the diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index 191695bc7ab..4e71a5fdca8 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -342,17 +342,21 @@ the server continues to reset the TTL, regardless of whether 2FA is installed. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/20340) in GitLab 13.1. -When you sign in, two cookies are set: +When you sign in, three cookies are set: - A session cookie called `_gitlab_session`. This cookie has no set expiration date. However, it expires based on its `session_expire_delay`. +- A session cookied called `about_gitlab_active_user`. + This cookie is used by the [marketing site](https://about.gitlab.com/) to determine if a user has an active GitLab session. No user information is passed to the cookie and it expires with the session. - A persistent cookie called `remember_user_token`, which is set only if you selected **Remember me** on the sign-in page. -When you close your browser, the `_gitlab_session` cookie is usually cleared client-side. -When it expires or isn't available, GitLab uses the `remember_user_token`cookie to get you -a new `_gitlab_session` cookie and keep you signed in, even if you close your browser. +When you close your browser, the `_gitlab_session` and `about_gitlab_active_user` cookies are usually cleared client-side. +When it expires or isn't available, GitLab: -When both cookies are gone or expired, you must sign in again. +- Uses the `remember_user_token`cookie to get you a new `_gitlab_session` cookie and keep you signed in, even if you close your browser. +- Sets the `about_gitlab_active_user` to `true`. + +When both the `remember_user_token` and `_gitlab_session` cookies are gone or expired, you must sign in again. NOTE: When any session is signed out, or when a session is revoked diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 77ad8d29440..64579d44aa3 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -8421,9 +8421,6 @@ msgstr "" msgid "CICD|Allow access to this project with a CI_JOB_TOKEN" msgstr "" -msgid "CICD|An error occurred while update the setting. Please try again." -msgstr "" - msgid "CICD|Auto DevOps" msgstr "" @@ -8463,9 +8460,6 @@ msgstr "" msgid "CICD|Limit CI_JOB_TOKEN access" msgstr "" -msgid "CICD|Limit JSON Web Token (JWT) access" -msgstr "" - msgid "CICD|Manage which projects can use their CI_JOB_TOKEN to access this project. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more.%{linkEnd}" msgstr "" @@ -8481,24 +8475,15 @@ msgstr "" msgid "CICD|The Auto DevOps pipeline runs if no alternative CI configuration file is found." msgstr "" -msgid "CICD|The JWT must be manually declared in each job that needs it. When disabled, the token is always available in all jobs in the pipeline. %{linkStart}Learn more.%{linkEnd}" -msgstr "" - msgid "CICD|There are several CI/CD limits in place." msgstr "" -msgid "CICD|There was a problem fetching the token access settings." -msgstr "" - msgid "CICD|Unprotected branches will not have access to the cache from protected branches." msgstr "" msgid "CICD|Use separate caches for protected branches" msgstr "" -msgid "CICD|Use the %{codeStart}secrets%{codeEnd} keyword to configure a job with a JWT." -msgstr "" - msgid "CICD|group enabled" msgstr "" @@ -9816,6 +9801,9 @@ msgstr "" msgid "Cloud Storage" msgstr "" +msgid "CloudSeed|AI / ML" +msgstr "" + msgid "CloudSeed|All" msgstr "" @@ -9852,6 +9840,9 @@ msgstr "" msgid "CloudSeed|Configuration" msgstr "" +msgid "CloudSeed|Configure via Merge Request" +msgstr "" + msgid "CloudSeed|Create MySQL Instance" msgstr "" @@ -9882,6 +9873,12 @@ msgstr "" msgid "CloudSeed|Deployments" msgstr "" +msgid "CloudSeed|Derive insights from unstructured text using Google machine learning" +msgstr "" + +msgid "CloudSeed|Derive insights from your images in the cloud or at the edge" +msgstr "" + msgid "CloudSeed|Description" msgstr "" @@ -9921,18 +9918,27 @@ msgstr "" msgid "CloudSeed|Google Cloud project" msgstr "" +msgid "CloudSeed|Google Cloud's AI tools are armed with the best of Google's research and technology to help developers focus exclusively on solving problems that matter" +msgstr "" + msgid "CloudSeed|I accept Google Cloud pricing and responsibilities involved with managing database instances" msgstr "" msgid "CloudSeed|Instances" msgstr "" +msgid "CloudSeed|Language AI" +msgstr "" + msgid "CloudSeed|Learn more about pricing for %{cloudsqlPricingStart}Cloud SQL%{cloudsqlPricingEnd}, %{alloydbPricingStart}Alloy DB%{alloydbPricingEnd}, %{memorystorePricingStart}Memorystore%{memorystorePricingEnd} and %{firestorePricingStart}Firestore%{firestorePricingEnd}." msgstr "" msgid "CloudSeed|Machine type" msgstr "" +msgid "CloudSeed|Make your content and apps multilingual with fast, dynamic machine translation" +msgstr "" + msgid "CloudSeed|Memorystore for Redis" msgstr "" @@ -9960,9 +9966,15 @@ msgstr "" msgid "CloudSeed|There are no instances to display." msgstr "" +msgid "CloudSeed|Translation AI" +msgstr "" + msgid "CloudSeed|Version" msgstr "" +msgid "CloudSeed|Vision AI" +msgstr "" + msgid "Cluster" msgstr "" @@ -11146,6 +11158,9 @@ msgstr "" msgid "ComplianceFrameworks|Cancel" msgstr "" +msgid "ComplianceFrameworks|Compliance Frameworks Export" +msgstr "" + msgid "ComplianceFrameworks|Compliance framework created" msgstr "" @@ -11230,6 +11245,12 @@ msgstr "" msgid "ComplianceFrameworks|You are about to permanently delete the compliance framework %{framework} from all projects which currently have it applied, which may remove other functionality. This cannot be undone." msgstr "" +msgid "ComplianceFrameworks|Your Compliance Frameworks CSV export for the group \"%{group_name}\" has been attached to this email." +msgstr "" + +msgid "ComplianceFrameworks|Your Compliance Frameworks CSV export for the group %{group_link} has been attached to this email." +msgstr "" + msgid "ComplianceFrameworks|default" msgstr "" diff --git a/scripts/verify-tff-mapping b/scripts/verify-tff-mapping index bab3afd4e4e..c37ccafa02c 100755 --- a/scripts/verify-tff-mapping +++ b/scripts/verify-tff-mapping @@ -66,6 +66,12 @@ tests = [ }, { + explanation: 'Map RuboCop related files to respective specs', + source: 'rubocop/cop/gettext/static_identifier.rb', + expected: ['spec/rubocop/cop/gettext/static_identifier_spec.rb'] + }, + + { explanation: 'Initializers should map to respective spec', source: 'config/initializers/action_mailer_hooks.rb', expected: ['spec/initializers/action_mailer_hooks_spec.rb'] diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 788bf6477b1..3bb4e93dc64 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -175,7 +175,7 @@ RSpec.describe 'Environments page', :js, feature_category: :projects do context 'when builds and manual actions are present' do let!(:pipeline) { create(:ci_pipeline, project: project) } - let!(:build) { create(:ci_build, pipeline: pipeline) } + let!(:build) { create(:ci_build, :success, pipeline: pipeline) } let!(:action) do create(:ci_build, :manual, pipeline: pipeline, name: 'deploy to production') @@ -207,8 +207,14 @@ RSpec.describe 'Environments page', :js, feature_category: :projects do .not_to change { Ci::Pipeline.count } end - it 'shows a stop button' do + it 'shows a stop button and dialog' do expect(page).to have_selector(stop_button_selector) + + click_button(_('Stop')) + + within('.modal-body') do + expect(page).to have_css('.warning_message') + end end it 'does not show external link button' do @@ -216,7 +222,6 @@ RSpec.describe 'Environments page', :js, feature_category: :projects do end it 'does not show terminal button' do - expect(page).not_to have_button(_('More actions')) expect(page).not_to have_terminal_button end @@ -242,8 +247,14 @@ RSpec.describe 'Environments page', :js, feature_category: :projects do on_stop: 'close_app') end - it 'shows a stop button' do + it 'shows a stop button and dialog' do expect(page).to have_selector(stop_button_selector) + + click_button(_('Stop')) + + within('.modal-body') do + expect(page).not_to have_css('.warning_message') + end end context 'when user is a reporter' do @@ -273,7 +284,6 @@ RSpec.describe 'Environments page', :js, feature_category: :projects do let(:role) { :developer } it 'does not show terminal button' do - expect(page).not_to have_button(_('More actions')) expect(page).not_to have_terminal_button end end diff --git a/spec/frontend/google_cloud/aiml/service_table_spec.js b/spec/frontend/google_cloud/aiml/service_table_spec.js new file mode 100644 index 00000000000..b14db064a7f --- /dev/null +++ b/spec/frontend/google_cloud/aiml/service_table_spec.js @@ -0,0 +1,34 @@ +import { GlTable } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import ServiceTable from '~/google_cloud/aiml/service_table.vue'; + +describe('google_cloud/aiml/service_table', () => { + let wrapper; + + const findTable = () => wrapper.findComponent(GlTable); + + beforeEach(() => { + const propsData = { + visionAiUrl: '#url-vision-ai', + languageAiUrl: '#url-language-ai', + translationAiUrl: '#url-translate-ai', + }; + wrapper = mountExtended(ServiceTable, { propsData }); + }); + + it('should contain a table', () => { + expect(findTable().exists()).toBe(true); + }); + + it.each` + name | testId | url + ${'key-vision-ai'} | ${'button-vision-ai'} | ${'#url-vision-ai'} + ${'key-natural-language-ai'} | ${'button-natural-language-ai'} | ${'#url-language-ai'} + ${'key-translation-ai'} | ${'button-translation-ai'} | ${'#url-translate-ai'} + `('renders $name button with correct url', ({ testId, url }) => { + const button = wrapper.findByTestId(testId); + + expect(button.exists()).toBe(true); + expect(button.attributes('href')).toBe(url); + }); +}); diff --git a/spec/frontend/search/sidebar/components/scope_new_navigation_spec.js b/spec/frontend/search/sidebar/components/scope_new_navigation_spec.js index 105beae8638..c7dd6e931b1 100644 --- a/spec/frontend/search/sidebar/components/scope_new_navigation_spec.js +++ b/spec/frontend/search/sidebar/components/scope_new_navigation_spec.js @@ -42,7 +42,7 @@ describe('ScopeNewNavigation', () => { const findNavItems = () => wrapper.findAllComponents(NavItem); const findNavItemActive = () => wrapper.find('[aria-current=page]'); const findNavItemActiveLabel = () => - findNavItemActive().find('[class="gl-pr-3 gl-text-gray-900 gl-truncate-end"]'); + findNavItemActive().find('[class="gl-pr-8 gl-text-gray-900 gl-truncate-end"]'); describe('scope navigation', () => { beforeEach(() => { diff --git a/spec/frontend/token_access/mock_data.js b/spec/frontend/token_access/mock_data.js index 67ee197b831..a4b5532108a 100644 --- a/spec/frontend/token_access/mock_data.js +++ b/spec/frontend/token_access/mock_data.js @@ -121,32 +121,6 @@ export const mockFields = [ }, ]; -export const optInJwtQueryResponse = (optInJwt) => ({ - data: { - project: { - id: '1', - ciCdSettings: { - optInJwt, - __typename: 'ProjectCiCdSetting', - }, - __typename: 'Project', - }, - }, -}); - -export const optInJwtMutationResponse = (optInJwt) => ({ - data: { - projectCiCdSettingsUpdate: { - ciCdSettings: { - optInJwt, - __typename: 'ProjectCiCdSetting', - }, - errors: [], - __typename: 'ProjectCiCdSettingsUpdatePayload', - }, - }, -}); - export const inboundJobTokenScopeEnabledResponse = { data: { project: { diff --git a/spec/frontend/token_access/opt_in_jwt_spec.js b/spec/frontend/token_access/opt_in_jwt_spec.js deleted file mode 100644 index cdb385aa743..00000000000 --- a/spec/frontend/token_access/opt_in_jwt_spec.js +++ /dev/null @@ -1,144 +0,0 @@ -import { GlLink, GlLoadingIcon, GlToggle, GlSprintf } from '@gitlab/ui'; -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import waitForPromises from 'helpers/wait_for_promises'; -import { createAlert } from '~/alert'; -import { OPT_IN_JWT_HELP_LINK } from '~/token_access/constants'; -import OptInJwt from '~/token_access/components/opt_in_jwt.vue'; -import getOptInJwtSettingQuery from '~/token_access/graphql/queries/get_opt_in_jwt_setting.query.graphql'; -import updateOptInJwtMutation from '~/token_access/graphql/mutations/update_opt_in_jwt.mutation.graphql'; -import { optInJwtMutationResponse, optInJwtQueryResponse } from './mock_data'; - -const errorMessage = 'An error occurred'; -const error = new Error(errorMessage); - -Vue.use(VueApollo); - -jest.mock('~/alert'); - -describe('OptInJwt component', () => { - let wrapper; - - const failureHandler = jest.fn().mockRejectedValue(error); - const enabledOptInJwtHandler = jest.fn().mockResolvedValue(optInJwtQueryResponse(true)); - const disabledOptInJwtHandler = jest.fn().mockResolvedValue(optInJwtQueryResponse(false)); - const updateOptInJwtHandler = jest.fn().mockResolvedValue(optInJwtMutationResponse(true)); - - const findHelpLink = () => wrapper.findComponent(GlLink); - const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); - const findToggle = () => wrapper.findComponent(GlToggle); - const findOptInJwtExpandedSection = () => wrapper.findByTestId('opt-in-jwt-expanded-section'); - - const createMockApolloProvider = (requestHandlers) => { - return createMockApollo(requestHandlers); - }; - - const createComponent = (requestHandlers, mountFn = shallowMountExtended, options = {}) => { - wrapper = mountFn(OptInJwt, { - provide: { - fullPath: 'root/my-repo', - }, - apolloProvider: createMockApolloProvider(requestHandlers), - data() { - return { - targetProjectPath: 'root/test', - }; - }, - ...options, - }); - }; - - const createShallowComponent = (requestHandlers, options = {}) => - createComponent(requestHandlers, shallowMountExtended, options); - const createFullComponent = (requestHandlers, options = {}) => - createComponent(requestHandlers, mountExtended, options); - - describe('loading state', () => { - it('shows loading state and hides toggle while waiting on query to resolve', async () => { - createShallowComponent([[getOptInJwtSettingQuery, enabledOptInJwtHandler]]); - - expect(findLoadingIcon().exists()).toBe(true); - expect(findToggle().exists()).toBe(false); - - await waitForPromises(); - - expect(findLoadingIcon().exists()).toBe(false); - expect(findToggle().exists()).toBe(true); - }); - }); - - describe('template', () => { - it('renders help link', async () => { - createShallowComponent([[getOptInJwtSettingQuery, enabledOptInJwtHandler]], { - stubs: { - GlToggle, - GlSprintf, - GlLink, - }, - }); - await waitForPromises(); - - expect(findHelpLink().exists()).toBe(true); - expect(findHelpLink().attributes('href')).toBe(OPT_IN_JWT_HELP_LINK); - }); - }); - - describe('toggle JWT token access', () => { - it('code instruction is visible when toggle is enabled', async () => { - createShallowComponent([[getOptInJwtSettingQuery, enabledOptInJwtHandler]]); - - await waitForPromises(); - - expect(findToggle().props('value')).toBe(true); - expect(findOptInJwtExpandedSection().exists()).toBe(true); - }); - - it('code instruction is hidden when toggle is disabled', async () => { - createShallowComponent([[getOptInJwtSettingQuery, disabledOptInJwtHandler]]); - - await waitForPromises(); - - expect(findToggle().props('value')).toBe(false); - expect(findOptInJwtExpandedSection().exists()).toBe(false); - }); - - describe('update JWT token access', () => { - it('calls updateOptInJwtMutation with correct arguments', async () => { - createFullComponent([ - [getOptInJwtSettingQuery, disabledOptInJwtHandler], - [updateOptInJwtMutation, updateOptInJwtHandler], - ]); - - await waitForPromises(); - - findToggle().vm.$emit('change', true); - - expect(updateOptInJwtHandler).toHaveBeenCalledWith({ - input: { - fullPath: 'root/my-repo', - optInJwt: true, - }, - }); - }); - - it('handles update error', async () => { - createFullComponent([ - [getOptInJwtSettingQuery, enabledOptInJwtHandler], - [updateOptInJwtMutation, failureHandler], - ]); - - await waitForPromises(); - - findToggle().vm.$emit('change', false); - - await waitForPromises(); - - expect(createAlert).toHaveBeenCalledWith({ - message: 'An error occurred while update the setting. Please try again.', - }); - }); - }); - }); -}); diff --git a/spec/frontend/token_access/token_access_app_spec.js b/spec/frontend/token_access/token_access_app_spec.js index cff16fd125c..77356f1b839 100644 --- a/spec/frontend/token_access/token_access_app_spec.js +++ b/spec/frontend/token_access/token_access_app_spec.js @@ -1,7 +1,6 @@ import { shallowMount } from '@vue/test-utils'; import OutboundTokenAccess from '~/token_access/components/outbound_token_access.vue'; import InboundTokenAccess from '~/token_access/components/inbound_token_access.vue'; -import OptInJwt from '~/token_access/components/opt_in_jwt.vue'; import TokenAccessApp from '~/token_access/components/token_access_app.vue'; describe('TokenAccessApp component', () => { @@ -9,7 +8,6 @@ describe('TokenAccessApp component', () => { const findOutboundTokenAccess = () => wrapper.findComponent(OutboundTokenAccess); const findInboundTokenAccess = () => wrapper.findComponent(InboundTokenAccess); - const findOptInJwt = () => wrapper.findComponent(OptInJwt); const createComponent = () => { wrapper = shallowMount(TokenAccessApp); @@ -20,10 +18,6 @@ describe('TokenAccessApp component', () => { createComponent(); }); - it('renders the opt in jwt component', () => { - expect(findOptInJwt().exists()).toBe(true); - }); - it('renders the outbound token access component', () => { expect(findOutboundTokenAccess().exists()).toBe(true); }); diff --git a/spec/frontend/work_items/components/notes/work_item_add_note_spec.js b/spec/frontend/work_items/components/notes/work_item_add_note_spec.js index a97164f9dce..fbcf759d50f 100644 --- a/spec/frontend/work_items/components/notes/work_item_add_note_spec.js +++ b/spec/frontend/work_items/components/notes/work_item_add_note_spec.js @@ -5,21 +5,16 @@ import createMockApollo from 'helpers/mock_apollo_helper'; import { mockTracking } from 'helpers/tracking_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { clearDraft } from '~/lib/utils/autosave'; -import { config } from '~/graphql_shared/issuable_client'; import WorkItemAddNote from '~/work_items/components/notes/work_item_add_note.vue'; import WorkItemCommentLocked from '~/work_items/components/notes/work_item_comment_locked.vue'; import WorkItemCommentForm from '~/work_items/components/notes/work_item_comment_form.vue'; import createNoteMutation from '~/work_items/graphql/notes/create_work_item_note.mutation.graphql'; import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants'; -import workItemQuery from '~/work_items/graphql/work_item.query.graphql'; -import workItemNotesQuery from '~/work_items/graphql/notes/work_item_notes.query.graphql'; import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql'; import { - workItemResponseFactory, - workItemQueryResponse, - projectWorkItemResponse, createWorkItemNoteResponse, - mockWorkItemNotesResponse, + workItemByIidResponseFactory, + workItemQueryResponse, } from '../../mock_data'; jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'); @@ -33,7 +28,6 @@ describe('Work item add note', () => { Vue.use(VueApollo); const mutationSuccessHandler = jest.fn().mockResolvedValue(createWorkItemNoteResponse); - const workItemByIidResponseHandler = jest.fn().mockResolvedValue(projectWorkItemResponse); let workItemResponseHandler; const findCommentForm = () => wrapper.findComponent(WorkItemCommentForm); @@ -42,9 +36,8 @@ describe('Work item add note', () => { const createComponent = async ({ mutationHandler = mutationSuccessHandler, canUpdate = true, - workItemResponse = workItemResponseFactory({ canUpdate }), - queryVariables = { id: workItemId }, - fetchByIid = false, + workItemResponse = workItemByIidResponseFactory({ canUpdate }), + queryVariables = { iid: '1' }, signedIn = true, isEditing = true, workItemType = 'Task', @@ -55,24 +48,10 @@ describe('Work item add note', () => { window.gon.current_user_avatar_url = 'avatar.png'; } - const apolloProvider = createMockApollo( - [ - [workItemQuery, workItemResponseHandler], - [createNoteMutation, mutationHandler], - [workItemByIidQuery, workItemByIidResponseHandler], - ], - {}, - { ...config.cacheConfig }, - ); - - apolloProvider.clients.defaultClient.writeQuery({ - query: workItemNotesQuery, - variables: { - id: workItemId, - pageSize: 100, - }, - data: mockWorkItemNotesResponse.data, - }); + const apolloProvider = createMockApollo([ + [workItemByIidQuery, workItemResponseHandler], + [createNoteMutation, mutationHandler], + ]); const { id } = workItemQueryResponse.data.workItem; wrapper = shallowMountExtended(WorkItemAddNote, { @@ -81,7 +60,6 @@ describe('Work item add note', () => { workItemId: id, fullPath: 'test-project-path', queryVariables, - fetchByIid, workItemType, markdownPreviewPath: '/group/project/preview_markdown?target_type=WorkItem', autocompleteDataSources: {}, @@ -139,9 +117,7 @@ describe('Work item add note', () => { await createComponent({ isEditing: true, signedIn: true, - queryVariables: { - id: mockWorkItemNotesResponse.data.workItem.id, - }, + queryVariables: { iid: '1' }, }); findCommentForm().vm.$emit('submitForm', 'some text'); @@ -244,23 +220,14 @@ describe('Work item add note', () => { }); }); - it('calls the global ID work item query when `fetchByIid` prop is false', async () => { - createComponent({ fetchByIid: false }); - await waitForPromises(); + it('calls the work item query', async () => { + await createComponent(); expect(workItemResponseHandler).toHaveBeenCalled(); - expect(workItemByIidResponseHandler).not.toHaveBeenCalled(); - }); - - it('calls the IID work item query when when `fetchByIid` prop is true', async () => { - await createComponent({ fetchByIid: true, isEditing: false }); - - expect(workItemResponseHandler).not.toHaveBeenCalled(); - expect(workItemByIidResponseHandler).toHaveBeenCalled(); }); - it('skips calling the handlers when missing the needed queryVariables', async () => { - await createComponent({ queryVariables: {}, fetchByIid: false, isEditing: false }); + it('skips calling the work item query when missing queryVariables', async () => { + await createComponent({ queryVariables: {}, isEditing: false }); expect(workItemResponseHandler).not.toHaveBeenCalled(); }); diff --git a/spec/frontend/work_items/components/notes/work_item_discussion_spec.js b/spec/frontend/work_items/components/notes/work_item_discussion_spec.js index 568b76150c4..28c8ea13a23 100644 --- a/spec/frontend/work_items/components/notes/work_item_discussion_spec.js +++ b/spec/frontend/work_items/components/notes/work_item_discussion_spec.js @@ -11,7 +11,7 @@ import { } from 'jest/work_items/mock_data'; import { WIDGET_TYPE_NOTES } from '~/work_items/constants'; -const mockWorkItemNotesWidgetResponseWithComments = mockWorkItemNotesResponseWithComments.data.workItem.widgets.find( +const mockWorkItemNotesWidgetResponseWithComments = mockWorkItemNotesResponseWithComments.data.workspace.workItems.nodes[0].widgets.find( (widget) => widget.type === WIDGET_TYPE_NOTES, ); @@ -28,8 +28,7 @@ describe('Work Item Discussion', () => { const createComponent = ({ discussion = [mockWorkItemCommentNote], workItemId = mockWorkItemId, - queryVariables = { id: workItemId }, - fetchByIid = false, + queryVariables = { iid: '1' }, fullPath = 'gitlab-org', workItemType = 'Task', } = {}) => { @@ -38,7 +37,6 @@ describe('Work Item Discussion', () => { discussion, workItemId, queryVariables, - fetchByIid, fullPath, workItemType, markdownPreviewPath: '/group/project/preview_markdown?target_type=WorkItem', diff --git a/spec/frontend/work_items/components/notes/work_item_note_spec.js b/spec/frontend/work_items/components/notes/work_item_note_spec.js index f8be2f5667b..956b52bb74c 100644 --- a/spec/frontend/work_items/components/notes/work_item_note_spec.js +++ b/spec/frontend/work_items/components/notes/work_item_note_spec.js @@ -11,13 +11,14 @@ import NoteBody from '~/work_items/components/notes/work_item_note_body.vue'; import NoteHeader from '~/notes/components/note_header.vue'; import NoteActions from '~/work_items/components/notes/work_item_note_actions.vue'; import WorkItemCommentForm from '~/work_items/components/notes/work_item_comment_form.vue'; -import workItemQuery from '~/work_items/graphql/work_item.query.graphql'; import updateWorkItemNoteMutation from '~/work_items/graphql/notes/update_work_item_note.mutation.graphql'; import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql'; +import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql'; import { mockAssignees, mockWorkItemCommentNote, updateWorkItemMutationResponse, + workItemByIidResponseFactory, workItemQueryResponse, } from 'jest/work_items/mock_data'; import { i18n, TRACKING_CATEGORY_SHOW } from '~/work_items/constants'; @@ -45,7 +46,7 @@ describe('Work Item Note', () => { }, }); - const workItemResponseHandler = jest.fn().mockResolvedValue(workItemQueryResponse); + const workItemResponseHandler = jest.fn().mockResolvedValue(workItemByIidResponseFactory()); const updateWorkItemMutationSuccessHandler = jest .fn() @@ -68,8 +69,7 @@ describe('Work Item Note', () => { workItemId = mockWorkItemId, updateWorkItemMutationHandler = updateWorkItemMutationSuccessHandler, assignees = mockAssignees, - queryVariables = { id: mockWorkItemId }, - fetchByIid = false, + queryVariables = { iid: '1' }, } = {}) => { wrapper = shallowMount(WorkItemNote, { propsData: { @@ -81,11 +81,10 @@ describe('Work Item Note', () => { autocompleteDataSources: {}, assignees, queryVariables, - fetchByIid, fullPath: 'test-project-path', }, apolloProvider: mockApollo([ - [workItemQuery, workItemResponseHandler], + [workItemByIidQuery, workItemResponseHandler], [updateWorkItemNoteMutation, updateNoteMutationHandler], [updateWorkItemMutation, updateWorkItemMutationHandler], ]), diff --git a/spec/frontend/work_items/components/work_item_notes_spec.js b/spec/frontend/work_items/components/work_item_notes_spec.js index 9cbe8283468..be5861ba1e2 100644 --- a/spec/frontend/work_items/components/work_item_notes_spec.js +++ b/spec/frontend/work_items/components/work_item_notes_spec.js @@ -10,7 +10,6 @@ import WorkItemNotes from '~/work_items/components/work_item_notes.vue'; import WorkItemDiscussion from '~/work_items/components/notes/work_item_discussion.vue'; import WorkItemAddNote from '~/work_items/components/notes/work_item_add_note.vue'; import WorkItemNotesActivityHeader from '~/work_items/components/notes/work_item_notes_activity_header.vue'; -import workItemNotesQuery from '~/work_items/graphql/notes/work_item_notes.query.graphql'; import workItemNotesByIidQuery from '~/work_items/graphql/notes/work_item_notes_by_iid.query.graphql'; import deleteWorkItemNoteMutation from '~/work_items/graphql/notes/delete_work_item_notes.mutation.graphql'; import workItemNoteCreatedSubscription from '~/work_items/graphql/notes/work_item_note_created.subscription.graphql'; @@ -36,15 +35,11 @@ const mockNotesWidgetResponse = mockWorkItemNotesResponse.data.workItem.widgets. (widget) => widget.type === WIDGET_TYPE_NOTES, ); -const mockNotesByIidWidgetResponse = mockWorkItemNotesByIidResponse.data.workspace.workItems.nodes[0].widgets.find( +const mockMoreNotesWidgetResponse = mockMoreWorkItemNotesResponse.data.workspace.workItems.nodes[0].widgets.find( (widget) => widget.type === WIDGET_TYPE_NOTES, ); -const mockMoreNotesWidgetResponse = mockMoreWorkItemNotesResponse.data.workItem.widgets.find( - (widget) => widget.type === WIDGET_TYPE_NOTES, -); - -const mockWorkItemNotesWidgetResponseWithComments = mockWorkItemNotesResponseWithComments.data.workItem.widgets.find( +const mockWorkItemNotesWidgetResponseWithComments = mockWorkItemNotesResponseWithComments.data.workspace.workItems.nodes[0].widgets.find( (widget) => widget.type === WIDGET_TYPE_NOTES, ); @@ -61,7 +56,6 @@ describe('WorkItemNotes component', () => { const findAllSystemNotes = () => wrapper.findAllComponents(SystemNote); const findAllListItems = () => wrapper.findAll('ul.timeline > *'); - const findWorkItemAddNote = () => wrapper.findComponent(WorkItemAddNote); const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader); const findActivityHeader = () => wrapper.findComponent(WorkItemNotesActivityHeader); const findSystemNoteAtIndex = (index) => findAllSystemNotes().at(index); @@ -69,10 +63,7 @@ describe('WorkItemNotes component', () => { const findWorkItemCommentNoteAtIndex = (index) => findAllWorkItemCommentNotes().at(index); const findDeleteNoteModal = () => wrapper.findComponent(GlModal); - const workItemNotesQueryHandler = jest.fn().mockResolvedValue(mockWorkItemNotesResponse); - const workItemNotesByIidQueryHandler = jest - .fn() - .mockResolvedValue(mockWorkItemNotesByIidResponse); + const workItemNotesQueryHandler = jest.fn().mockResolvedValue(mockWorkItemNotesByIidResponse); const workItemMoreNotesQueryHandler = jest.fn().mockResolvedValue(mockMoreWorkItemNotesResponse); const workItemNotesWithCommentsQueryHandler = jest .fn() @@ -93,7 +84,6 @@ describe('WorkItemNotes component', () => { const createComponent = ({ workItemId = mockWorkItemId, - fetchByIid = false, workItemIid = mockWorkItemIid, defaultWorkItemNotesQueryHandler = workItemNotesQueryHandler, deleteWINoteMutationHandler = deleteWorkItemNoteMutationSuccessHandler, @@ -101,8 +91,7 @@ describe('WorkItemNotes component', () => { } = {}) => { wrapper = shallowMount(WorkItemNotes, { apolloProvider: createMockApollo([ - [workItemNotesQuery, defaultWorkItemNotesQueryHandler], - [workItemNotesByIidQuery, workItemNotesByIidQueryHandler], + [workItemNotesByIidQuery, defaultWorkItemNotesQueryHandler], [deleteWorkItemNoteMutation, deleteWINoteMutationHandler], [workItemNoteCreatedSubscription, notesCreateSubscriptionHandler], [workItemNoteUpdatedSubscription, notesUpdateSubscriptionHandler], @@ -111,11 +100,8 @@ describe('WorkItemNotes component', () => { propsData: { workItemId, workItemIid, - queryVariables: { - id: workItemId, - }, + queryVariables: { iid: '1' }, fullPath: 'test-path', - fetchByIid, workItemType: 'task', reportAbusePath: '/report/abuse/path', isModal, @@ -134,17 +120,6 @@ describe('WorkItemNotes component', () => { expect(findActivityHeader().exists()).toBe(true); }); - it('passes correct props to comment form component', async () => { - createComponent({ - workItemId: mockWorkItemId, - fetchByIid: false, - defaultWorkItemNotesQueryHandler: workItemNotesByIidQueryHandler, - }); - await waitForPromises(); - - expect(findWorkItemAddNote().props('fetchByIid')).toEqual(false); - }); - describe('when notes are loading', () => { it('renders skeleton loader', () => { expect(findSkeletonLoader().exists()).toBe(true); @@ -162,28 +137,15 @@ describe('WorkItemNotes component', () => { it('renders system notes to the length of the response', async () => { await waitForPromises(); + expect(workItemNotesQueryHandler).toHaveBeenCalledWith({ + after: undefined, + iid: '1', + pageSize: 30, + }); expect(findAllSystemNotes()).toHaveLength(mockNotesWidgetResponse.discussions.nodes.length); }); }); - describe('when the notes are fetched by `iid`', () => { - beforeEach(async () => { - createComponent({ workItemId: mockWorkItemId, fetchByIid: true }); - await waitForPromises(); - }); - - it('renders the notes list to the length of the response', () => { - expect(workItemNotesByIidQueryHandler).toHaveBeenCalled(); - expect(findAllSystemNotes()).toHaveLength( - mockNotesByIidWidgetResponse.discussions.nodes.length, - ); - }); - - it('passes correct props to comment form component', () => { - expect(findWorkItemAddNote().props('fetchByIid')).toEqual(true); - }); - }); - describe('Pagination', () => { describe('When there is no next page', () => { it('fetch more notes is not called', async () => { @@ -202,14 +164,14 @@ describe('WorkItemNotes component', () => { it('fetch more notes should be called', async () => { expect(workItemMoreNotesQueryHandler).toHaveBeenCalledWith({ pageSize: DEFAULT_PAGE_SIZE_NOTES, - id: 'gid://gitlab/WorkItem/1', + iid: '1', }); await nextTick(); expect(workItemMoreNotesQueryHandler).toHaveBeenCalledWith({ pageSize: DEFAULT_PAGE_SIZE_NOTES, - id: 'gid://gitlab/WorkItem/1', + iid: '1', after: mockMoreNotesWidgetResponse.discussions.pageInfo.endCursor, }); }); diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js index 36e8d713789..2ab31908577 100644 --- a/spec/frontend/work_items/mock_data.js +++ b/spec/frontend/work_items/mock_data.js @@ -2235,186 +2235,197 @@ export const mockWorkItemNotesByIidResponse = { }; export const mockMoreWorkItemNotesResponse = { data: { - workItem: { - id: 'gid://gitlab/WorkItem/600', - iid: '60', - widgets: [ - { - __typename: 'WorkItemWidgetIteration', - }, - { - __typename: 'WorkItemWidgetWeight', - }, - { - __typename: 'WorkItemWidgetAssignees', - }, - { - __typename: 'WorkItemWidgetLabels', - }, - { - __typename: 'WorkItemWidgetDescription', - }, - { - __typename: 'WorkItemWidgetHierarchy', - }, - { - __typename: 'WorkItemWidgetStartAndDueDate', - }, - { - __typename: 'WorkItemWidgetMilestone', - }, - { - type: 'NOTES', - discussions: { - pageInfo: { - hasNextPage: true, - hasPreviousPage: false, - startCursor: null, - endCursor: 'endCursor', - __typename: 'PageInfo', - }, - nodes: [ + workspace: { + id: 'gid://gitlab/Project/6', + workItems: { + nodes: [ + { + id: 'gid://gitlab/WorkItem/600', + iid: '60', + widgets: [ { - id: 'gid://gitlab/Discussion/8bbc4890b6ff0f2cde93a5a0947cd2b8a13d3b6e', - notes: { - nodes: [ - { - id: 'gid://gitlab/Note/2428', - body: 'added #31 as parent issue', - bodyHtml: - '<p data-sourcepos="1:1-1:25" dir="auto">added <a href="/flightjs/Flight/-/issues/31" data-reference-type="issue" data-original="#31" data-link="false" data-link-reference="false" data-project="6" data-issue="224" data-project-path="flightjs/Flight" data-iid="31" data-issue-type="issue" data-container=body data-placement="top" title="Perferendis est quae totam quia laborum tempore ut voluptatem." class="gfm gfm-issue">#31</a> as parent issue</p>', - systemNoteIconName: 'link', - createdAt: '2022-11-14T04:18:59Z', - lastEditedAt: null, - url: - 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191', - lastEditedBy: null, - system: true, - internal: false, - discussion: { - id: 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da1112356a59e', - }, - userPermissions: { - adminNote: false, - awardEmoji: true, - readNote: true, - createNote: true, - resolveNote: true, - repositionNote: true, - __typename: 'NotePermissions', - }, - author: { - avatarUrl: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - id: 'gid://gitlab/User/1', - name: 'Administrator', - username: 'root', - webUrl: 'http://127.0.0.1:3000/root', - __typename: 'UserCore', - }, - __typename: 'Note', - }, - ], - __typename: 'NoteConnection', - }, - __typename: 'Discussion', + __typename: 'WorkItemWidgetIteration', }, { - id: 'gid://gitlab/Discussion/7b08b89a728a5ceb7de8334246837ba1d07270dc', - notes: { - nodes: [ - { - id: 'gid://gitlab/MilestoneNote/0f2f195ec0d1ef95ee9d5b10446b8e96a7d83823', - body: 'changed milestone to %v4.0', - bodyHtml: - '<p data-sourcepos="1:1-1:23" dir="auto">changed milestone to <a href="/flightjs/Flight/-/milestones/5" data-reference-type="milestone" data-original="%5" data-link="false" data-link-reference="false" data-project="6" data-milestone="30" data-container=body data-placement="top" title="" class="gfm gfm-milestone has-tooltip">%v4.0</a></p>', - systemNoteIconName: 'clock', - createdAt: '2022-11-14T04:18:59Z', - lastEditedAt: null, - url: - 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191', - lastEditedBy: null, - system: true, - internal: false, - discussion: { - id: 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da1272356a59e', - }, - userPermissions: { - adminNote: false, - awardEmoji: true, - readNote: true, - createNote: true, - resolveNote: true, - repositionNote: true, - __typename: 'NotePermissions', - }, - author: { - avatarUrl: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - id: 'gid://gitlab/User/1', - name: 'Administrator', - username: 'root', - webUrl: 'http://127.0.0.1:3000/root', - __typename: 'UserCore', - }, - __typename: 'Note', - }, - ], - __typename: 'NoteConnection', - }, - __typename: 'Discussion', + __typename: 'WorkItemWidgetWeight', }, { - id: 'gid://gitlab/Discussion/0f2f195ec0d1ef95ee9d5b10446b8e96a7d83864', - notes: { + __typename: 'WorkItemWidgetAssignees', + }, + { + __typename: 'WorkItemWidgetLabels', + }, + { + __typename: 'WorkItemWidgetDescription', + }, + { + __typename: 'WorkItemWidgetHierarchy', + }, + { + __typename: 'WorkItemWidgetStartAndDueDate', + }, + { + __typename: 'WorkItemWidgetMilestone', + }, + { + type: 'NOTES', + discussions: { + pageInfo: { + hasNextPage: true, + hasPreviousPage: false, + startCursor: null, + endCursor: 'endCursor', + __typename: 'PageInfo', + }, nodes: [ { - id: 'gid://gitlab/WeightNote/0f2f195ec0d1ef95ee9d5b10446b8e96a7d83864', - body: 'changed weight to **89**', - bodyHtml: '<p dir="auto">changed weight to <strong>89</strong></p>', - systemNoteIconName: 'weight', - createdAt: '2022-11-25T07:16:20Z', - lastEditedAt: null, - url: - 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191', - lastEditedBy: null, - system: true, - internal: false, - discussion: { - id: 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723569876', + id: 'gid://gitlab/Discussion/8bbc4890b6ff0f2cde93a5a0947cd2b8a13d3b6e', + notes: { + nodes: [ + { + id: 'gid://gitlab/Note/2428', + body: 'added #31 as parent issue', + bodyHtml: + '<p data-sourcepos="1:1-1:25" dir="auto">added <a href="/flightjs/Flight/-/issues/31" data-reference-type="issue" data-original="#31" data-link="false" data-link-reference="false" data-project="6" data-issue="224" data-project-path="flightjs/Flight" data-iid="31" data-issue-type="issue" data-container=body data-placement="top" title="Perferendis est quae totam quia laborum tempore ut voluptatem." class="gfm gfm-issue">#31</a> as parent issue</p>', + systemNoteIconName: 'link', + createdAt: '2022-11-14T04:18:59Z', + lastEditedAt: null, + url: + 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191', + lastEditedBy: null, + system: true, + internal: false, + discussion: { + id: + 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da1112356a59e', + }, + userPermissions: { + adminNote: false, + awardEmoji: true, + readNote: true, + createNote: true, + resolveNote: true, + repositionNote: true, + __typename: 'NotePermissions', + }, + author: { + avatarUrl: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + id: 'gid://gitlab/User/1', + name: 'Administrator', + username: 'root', + webUrl: 'http://127.0.0.1:3000/root', + __typename: 'UserCore', + }, + __typename: 'Note', + }, + ], + __typename: 'NoteConnection', }, - userPermissions: { - adminNote: false, - awardEmoji: true, - readNote: true, - createNote: true, - resolveNote: true, - repositionNote: true, - __typename: 'NotePermissions', + __typename: 'Discussion', + }, + { + id: 'gid://gitlab/Discussion/7b08b89a728a5ceb7de8334246837ba1d07270dc', + notes: { + nodes: [ + { + id: + 'gid://gitlab/MilestoneNote/0f2f195ec0d1ef95ee9d5b10446b8e96a7d83823', + body: 'changed milestone to %v4.0', + bodyHtml: + '<p data-sourcepos="1:1-1:23" dir="auto">changed milestone to <a href="/flightjs/Flight/-/milestones/5" data-reference-type="milestone" data-original="%5" data-link="false" data-link-reference="false" data-project="6" data-milestone="30" data-container=body data-placement="top" title="" class="gfm gfm-milestone has-tooltip">%v4.0</a></p>', + systemNoteIconName: 'clock', + createdAt: '2022-11-14T04:18:59Z', + lastEditedAt: null, + url: + 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191', + lastEditedBy: null, + system: true, + internal: false, + discussion: { + id: + 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da1272356a59e', + }, + userPermissions: { + adminNote: false, + awardEmoji: true, + readNote: true, + createNote: true, + resolveNote: true, + repositionNote: true, + __typename: 'NotePermissions', + }, + author: { + avatarUrl: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + id: 'gid://gitlab/User/1', + name: 'Administrator', + username: 'root', + webUrl: 'http://127.0.0.1:3000/root', + __typename: 'UserCore', + }, + __typename: 'Note', + }, + ], + __typename: 'NoteConnection', }, - author: { - avatarUrl: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - id: 'gid://gitlab/User/1', - name: 'Administrator', - username: 'root', - webUrl: 'http://127.0.0.1:3000/root', - __typename: 'UserCore', + __typename: 'Discussion', + }, + { + id: 'gid://gitlab/Discussion/0f2f195ec0d1ef95ee9d5b10446b8e96a7d83864', + notes: { + nodes: [ + { + id: 'gid://gitlab/WeightNote/0f2f195ec0d1ef95ee9d5b10446b8e96a7d83864', + body: 'changed weight to **89**', + bodyHtml: '<p dir="auto">changed weight to <strong>89</strong></p>', + systemNoteIconName: 'weight', + createdAt: '2022-11-25T07:16:20Z', + lastEditedAt: null, + url: + 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191', + lastEditedBy: null, + system: true, + internal: false, + discussion: { + id: + 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723569876', + }, + userPermissions: { + adminNote: false, + awardEmoji: true, + readNote: true, + createNote: true, + resolveNote: true, + repositionNote: true, + __typename: 'NotePermissions', + }, + author: { + avatarUrl: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + id: 'gid://gitlab/User/1', + name: 'Administrator', + username: 'root', + webUrl: 'http://127.0.0.1:3000/root', + __typename: 'UserCore', + }, + __typename: 'Note', + }, + ], + __typename: 'NoteConnection', }, - __typename: 'Note', + __typename: 'Discussion', }, ], - __typename: 'NoteConnection', + __typename: 'DiscussionConnection', }, - __typename: 'Discussion', + __typename: 'WorkItemWidgetNotes', }, ], - __typename: 'DiscussionConnection', + __typename: 'WorkItem', }, - __typename: 'WorkItemWidgetNotes', - }, - ], - __typename: 'WorkItem', + ], + }, }, }, }; @@ -2514,177 +2525,187 @@ export const mockWorkItemCommentNote = { export const mockWorkItemNotesResponseWithComments = { data: { - workItem: { - id: 'gid://gitlab/WorkItem/600', - iid: '60', - widgets: [ - { - __typename: 'WorkItemWidgetIteration', - }, - { - __typename: 'WorkItemWidgetWeight', - }, - { - __typename: 'WorkItemWidgetAssignees', - }, - { - __typename: 'WorkItemWidgetLabels', - }, - { - __typename: 'WorkItemWidgetDescription', - }, - { - __typename: 'WorkItemWidgetHierarchy', - }, - { - __typename: 'WorkItemWidgetStartAndDueDate', - }, - { - __typename: 'WorkItemWidgetMilestone', - }, - { - type: 'NOTES', - discussions: { - pageInfo: { - hasNextPage: false, - hasPreviousPage: false, - startCursor: null, - endCursor: null, - __typename: 'PageInfo', - }, - nodes: [ + workspace: { + id: 'gid://gitlab/Project/6', + workItems: { + nodes: [ + { + id: 'gid://gitlab/WorkItem/600', + iid: '60', + widgets: [ { - id: 'gid://gitlab/Discussion/8bbc4890b6ff0f2cde93a5a0947cd2b8a13d3b6e', - notes: { - nodes: [ - { - id: 'gid://gitlab/DiscussionNote/174', - body: 'Separate thread', - bodyHtml: '<p data-sourcepos="1:1-1:15" dir="auto">Separate thread</p>', - system: false, - internal: false, - systemNoteIconName: null, - createdAt: '2023-01-12T07:47:40Z', - lastEditedAt: null, - url: - 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191', - lastEditedBy: null, - discussion: { - id: 'gid://gitlab/Discussion/2bb1162fd0d39297d1a68fdd7d4083d3780af0f3', - __typename: 'Discussion', - }, - author: { - id: 'gid://gitlab/User/1', - avatarUrl: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - name: 'Administrator', - username: 'root', - webUrl: 'http://127.0.0.1:3000/root', - __typename: 'UserCore', - }, - userPermissions: { - adminNote: true, - awardEmoji: true, - readNote: true, - createNote: true, - resolveNote: true, - repositionNote: true, - __typename: 'NotePermissions', - }, - __typename: 'Note', - }, - { - id: 'gid://gitlab/DiscussionNote/235', - body: 'Thread comment', - bodyHtml: '<p data-sourcepos="1:1-1:15" dir="auto">Thread comment</p>', - system: false, - internal: false, - systemNoteIconName: null, - createdAt: '2023-01-18T09:09:54Z', - lastEditedAt: null, - url: - 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191', - lastEditedBy: null, - discussion: { - id: 'gid://gitlab/Discussion/2bb1162fd0d39297d1a68fdd7d4083d3780af0f3', - __typename: 'Discussion', - }, - author: { - id: 'gid://gitlab/User/1', - avatarUrl: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - name: 'Administrator', - username: 'root', - webUrl: 'http://127.0.0.1:3000/root', - __typename: 'UserCore', - }, - userPermissions: { - adminNote: true, - awardEmoji: true, - readNote: true, - createNote: true, - resolveNote: true, - repositionNote: true, - __typename: 'NotePermissions', - }, - __typename: 'Note', - }, - ], - __typename: 'NoteConnection', - }, - __typename: 'Discussion', + __typename: 'WorkItemWidgetIteration', }, { - id: 'gid://gitlab/Discussion/0f2f195ec0d1ef95ee9d5b10446b8e96a7d83864', - notes: { + __typename: 'WorkItemWidgetWeight', + }, + { + __typename: 'WorkItemWidgetAssignees', + }, + { + __typename: 'WorkItemWidgetLabels', + }, + { + __typename: 'WorkItemWidgetDescription', + }, + { + __typename: 'WorkItemWidgetHierarchy', + }, + { + __typename: 'WorkItemWidgetStartAndDueDate', + }, + { + __typename: 'WorkItemWidgetMilestone', + }, + { + type: 'NOTES', + discussions: { + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + startCursor: null, + endCursor: null, + __typename: 'PageInfo', + }, nodes: [ { - id: 'gid://gitlab/WeightNote/0f2f195ec0d1ef95ee9d5b10446b8e96a9883864', - body: 'Main thread 2', - bodyHtml: '<p data-sourcepos="1:1-1:15" dir="auto">Main thread 2</p>', - systemNoteIconName: 'weight', - createdAt: '2022-11-25T07:16:20Z', - lastEditedAt: null, - url: - 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191', - lastEditedBy: null, - system: false, - internal: false, - discussion: { - id: 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723560987', - }, - userPermissions: { - adminNote: false, - awardEmoji: true, - readNote: true, - createNote: true, - resolveNote: true, - repositionNote: true, - __typename: 'NotePermissions', + id: 'gid://gitlab/Discussion/8bbc4890b6ff0f2cde93a5a0947cd2b8a13d3b6e', + notes: { + nodes: [ + { + id: 'gid://gitlab/DiscussionNote/174', + body: 'Separate thread', + bodyHtml: '<p data-sourcepos="1:1-1:15" dir="auto">Separate thread</p>', + system: false, + internal: false, + systemNoteIconName: null, + createdAt: '2023-01-12T07:47:40Z', + lastEditedAt: null, + url: + 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191', + lastEditedBy: null, + discussion: { + id: + 'gid://gitlab/Discussion/2bb1162fd0d39297d1a68fdd7d4083d3780af0f3', + __typename: 'Discussion', + }, + author: { + id: 'gid://gitlab/User/1', + avatarUrl: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + name: 'Administrator', + username: 'root', + webUrl: 'http://127.0.0.1:3000/root', + __typename: 'UserCore', + }, + userPermissions: { + adminNote: true, + awardEmoji: true, + readNote: true, + createNote: true, + resolveNote: true, + repositionNote: true, + __typename: 'NotePermissions', + }, + __typename: 'Note', + }, + { + id: 'gid://gitlab/DiscussionNote/235', + body: 'Thread comment', + bodyHtml: '<p data-sourcepos="1:1-1:15" dir="auto">Thread comment</p>', + system: false, + internal: false, + systemNoteIconName: null, + createdAt: '2023-01-18T09:09:54Z', + lastEditedAt: null, + url: + 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191', + lastEditedBy: null, + discussion: { + id: + 'gid://gitlab/Discussion/2bb1162fd0d39297d1a68fdd7d4083d3780af0f3', + __typename: 'Discussion', + }, + author: { + id: 'gid://gitlab/User/1', + avatarUrl: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + name: 'Administrator', + username: 'root', + webUrl: 'http://127.0.0.1:3000/root', + __typename: 'UserCore', + }, + userPermissions: { + adminNote: true, + awardEmoji: true, + readNote: true, + createNote: true, + resolveNote: true, + repositionNote: true, + __typename: 'NotePermissions', + }, + __typename: 'Note', + }, + ], + __typename: 'NoteConnection', }, - author: { - avatarUrl: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - id: 'gid://gitlab/User/1', - name: 'Administrator', - username: 'root', - webUrl: 'http://127.0.0.1:3000/root', - __typename: 'UserCore', + __typename: 'Discussion', + }, + { + id: 'gid://gitlab/Discussion/0f2f195ec0d1ef95ee9d5b10446b8e96a7d83864', + notes: { + nodes: [ + { + id: 'gid://gitlab/WeightNote/0f2f195ec0d1ef95ee9d5b10446b8e96a9883864', + body: 'Main thread 2', + bodyHtml: '<p data-sourcepos="1:1-1:15" dir="auto">Main thread 2</p>', + systemNoteIconName: 'weight', + createdAt: '2022-11-25T07:16:20Z', + lastEditedAt: null, + url: + 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191', + lastEditedBy: null, + system: false, + internal: false, + discussion: { + id: + 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723560987', + }, + userPermissions: { + adminNote: false, + awardEmoji: true, + readNote: true, + createNote: true, + resolveNote: true, + repositionNote: true, + __typename: 'NotePermissions', + }, + author: { + avatarUrl: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + id: 'gid://gitlab/User/1', + name: 'Administrator', + username: 'root', + webUrl: 'http://127.0.0.1:3000/root', + __typename: 'UserCore', + }, + __typename: 'Note', + }, + ], + __typename: 'NoteConnection', }, - __typename: 'Note', + __typename: 'Discussion', }, ], - __typename: 'NoteConnection', + __typename: 'DiscussionConnection', }, - __typename: 'Discussion', + __typename: 'WorkItemWidgetNotes', }, ], - __typename: 'DiscussionConnection', + __typename: 'WorkItem', }, - __typename: 'WorkItemWidgetNotes', - }, - ], - __typename: 'WorkItem', + ], + }, }, }, }; diff --git a/spec/mailers/emails/service_desk_spec.rb b/spec/mailers/emails/service_desk_spec.rb index 76036fcd0b3..521cbe469d9 100644 --- a/spec/mailers/emails/service_desk_spec.rb +++ b/spec/mailers/emails/service_desk_spec.rb @@ -13,7 +13,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } - let_it_be(:issue) { create(:issue, project: project) } + let_it_be(:issue) { create(:issue, project: project, description: 'Some **issue** description') } let_it_be(:email) { 'someone@gitlab.com' } let_it_be(:expected_unsubscribe_url) { unsubscribe_sent_notification_url('b7721fc7e8419911a8bea145236a0519') } let_it_be(:credential) { create(:service_desk_custom_email_credential, project: project) } @@ -171,6 +171,13 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do it_behaves_like 'handle template content', 'thank_you' end + + context 'when issue description placeholder is used' do + let(:template_content) { 'thank you, your new issue has been created. %{ISSUE_DESCRIPTION}' } + let(:expected_body) { "<p dir=\"auto\">thank you, your new issue has been created. </p>#{issue.description_html}" } + + it_behaves_like 'handle template content', 'thank_you' + end end end diff --git a/spec/rubocop/cop/rspec/htt_party_basic_auth_spec.rb b/spec/rubocop/cop/rspec/httparty_basic_auth_spec.rb index 537a7a9a7e9..c7f4c61b501 100644 --- a/spec/rubocop/cop/rspec/htt_party_basic_auth_spec.rb +++ b/spec/rubocop/cop/rspec/httparty_basic_auth_spec.rb @@ -4,7 +4,7 @@ require 'rubocop_spec_helper' require_relative '../../../../rubocop/cop/rspec/httparty_basic_auth' -RSpec.describe RuboCop::Cop::RSpec::HTTPartyBasicAuth do +RSpec.describe RuboCop::Cop::RSpec::HTTPartyBasicAuth, feature_category: :shared do context 'when passing `basic_auth: { user: ... }`' do it 'registers an offense and corrects', :aggregate_failures do expect_offense(<<~SOURCE, 'spec/foo.rb') diff --git a/tests.yml b/tests.yml index 26433e5c4fa..e4c84789459 100644 --- a/tests.yml +++ b/tests.yml @@ -40,6 +40,10 @@ mapping: - source: tooling/(.+)\.rb test: spec/tooling/%s_spec.rb + # RuboCop related specs + - source: rubocop/(.+)\.rb + test: spec/rubocop/%s_spec.rb + # Initializers should map to respective spec - source: config/initializers/(.+)\.rb test: spec/initializers/%s_spec.rb |