From efe8d313905f207ceae5fd53edb02a4e4c911804 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 30 Apr 2025 06:11:52 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- GITALY_SERVER_VERSION | 2 +- .../issuable/components/issuable_by_email.vue | 3 +- app/assets/javascripts/issues/list/index.js | 3 +- .../shared/work_item_link_child_contents.vue | 1 + .../work_item_change_type_modal.vue | 5 +- .../work_item_links/work_item_link_child.vue | 6 +- .../work_item_relationship_list.vue | 2 +- app/helpers/issues_helper.rb | 13 +- config/sidekiq_queues.yml | 4 + db/docs/design_management_versions.yml | 12 +- ...e_ai_troubleshoot_job_events_project_fk.rb | 26 ++ ...requirement_status_namespace_project_id.rb | 17 + ...irement_status_namespace_requirement_id.rb | 17 + ...quirement_status_namespace_framework_id.rb | 17 + ...rsions_namespace_id_not_null_constraint.rb | 14 + ..._traversal_ids_vul_namespace_statistics.rb | 16 + db/schema_migrations/20250422044141 | 1 + db/schema_migrations/20250423150108 | 1 + db/schema_migrations/20250428204842 | 1 + db/schema_migrations/20250429094554 | 1 + db/schema_migrations/20250429094649 | 1 + db/schema_migrations/20250429094811 | 1 + db/structure.sql | 13 +- doc/api/graphql/reference/_index.md | 29 +- doc/development/utilities.md | 2 +- doc/update/versions/gitlab_17_changes.md | 329 +++++++++++------- lib/gitlab/utils/merge_hash.rb | 10 +- locale/gitlab.pot | 2 +- .../work_item_change_type_modal_spec.js | 37 +- .../work_item_link_child_spec.js | 2 +- .../work_item_relationship_list_spec.js | 3 +- spec/frontend/work_items/mock_data.js | 79 +++++ spec/helpers/issues_helper_spec.rb | 59 +++- spec/lib/gitlab/utils/merge_hash_spec.rb | 14 +- spec/models/design_management/version_spec.rb | 2 +- 35 files changed, 566 insertions(+), 179 deletions(-) create mode 100644 db/migrate/20250428204842_update_ai_troubleshoot_job_events_project_fk.rb create mode 100644 db/migrate/20250429094554_add_index_to_project_requirement_status_namespace_project_id.rb create mode 100644 db/migrate/20250429094649_add_index_to_project_requirement_status_namespace_requirement_id.rb create mode 100644 db/migrate/20250429094811_add_index_to_project_requirement_status_namespace_framework_id.rb create mode 100644 db/post_migrate/20250422044141_add_design_management_versions_namespace_id_not_null_constraint.rb create mode 100644 db/post_migrate/20250423150108_add_btree_index_on_traversal_ids_vul_namespace_statistics.rb create mode 100644 db/schema_migrations/20250422044141 create mode 100644 db/schema_migrations/20250423150108 create mode 100644 db/schema_migrations/20250428204842 create mode 100644 db/schema_migrations/20250429094554 create mode 100644 db/schema_migrations/20250429094649 create mode 100644 db/schema_migrations/20250429094811 diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 8899d408ef3..5115cfb2637 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -4586553167d1b1ce3932b76ea5b8721e860dada2 +d389b47d972835c6b4f5dbacf07f7965361d0c8f diff --git a/app/assets/javascripts/issuable/components/issuable_by_email.vue b/app/assets/javascripts/issuable/components/issuable_by_email.vue index 0a87add240f..d98ce6ae55e 100644 --- a/app/assets/javascripts/issuable/components/issuable_by_email.vue +++ b/app/assets/javascripts/issuable/components/issuable_by_email.vue @@ -128,10 +128,11 @@ export default { + diff --git a/app/assets/javascripts/issues/list/index.js b/app/assets/javascripts/issues/list/index.js index a8bec8539ba..149a823b337 100644 --- a/app/assets/javascripts/issues/list/index.js +++ b/app/assets/javascripts/issues/list/index.js @@ -116,6 +116,7 @@ export async function mountIssuesListApp() { wiNewCommentTemplatePaths, hasLinkedItemsEpicsFeature, timeTrackingLimitToHours, + hasSubepicsFeature, } = el.dataset; return new Vue({ @@ -200,7 +201,7 @@ export async function mountIssuesListApp() { issuesListPath: wiIssuesListPath, labelsManagePath: wiLabelsManagePath, reportAbusePath: wiReportAbusePath, - hasSubepicsFeature: false, + hasSubepicsFeature: parseBoolean(hasSubepicsFeature), hasLinkedItemsEpicsFeature: parseBoolean(hasLinkedItemsEpicsFeature), commentTemplatePaths: JSON.parse(wiNewCommentTemplatePaths), timeTrackingLimitToHours: parseBoolean(timeTrackingLimitToHours), diff --git a/app/assets/javascripts/work_items/components/shared/work_item_link_child_contents.vue b/app/assets/javascripts/work_items/components/shared/work_item_link_child_contents.vue index 22cdf31d177..d491418682d 100644 --- a/app/assets/javascripts/work_items/components/shared/work_item_link_child_contents.vue +++ b/app/assets/javascripts/work_items/components/shared/work_item_link_child_contents.vue @@ -261,6 +261,7 @@ export default { :work-item-iid="childItemIid" :work-item-web-url="childItemWebUrl" /> + import { GlButton, GlTooltipDirective } from '@gitlab/ui'; +import WorkItemLinkChildContents from 'ee_else_ce/work_items/components/shared/work_item_link_child_contents.vue'; import { __, s__ } from '~/locale'; import { createAlert } from '~/alert'; import { WORK_ITEM_TYPE_NAME_TASK } from '../../constants'; @@ -7,7 +8,6 @@ import { findHierarchyWidget, getDefaultHierarchyChildrenCount, getItems } from import toggleHierarchyTreeChildMutation from '../../graphql/client/toggle_hierarchy_tree_child.mutation.graphql'; import isExpandedHierarchyTreeChildQuery from '../../graphql/client/is_expanded_hierarchy_tree_child.query.graphql'; import getWorkItemTreeQuery from '../../graphql/work_item_tree.query.graphql'; -import WorkItemLinkChildContents from '../shared/work_item_link_child_contents.vue'; import WorkItemChildrenLoadMore from '../shared/work_item_children_load_more.vue'; export default { @@ -283,13 +283,9 @@ export default { :child-item="childItem" :can-update="canUpdate" :class="childItemClass" - :parent-work-item-id="issuableGid" - :work-item-type="workItemType" :show-labels="showLabels" - :show-closed="showClosed" :work-item-full-path="workItemFullPath" :show-weight="shouldShowWeight" - :is-active="isActive" @click="$emit('click', $event)" @removeChild="$emit('removeChild', childItem)" /> diff --git a/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationship_list.vue b/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationship_list.vue index dd19845b89a..f4872af155a 100644 --- a/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationship_list.vue +++ b/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationship_list.vue @@ -8,7 +8,7 @@ import { defaultSortableOptions, DRAG_DELAY } from '~/sortable/constants'; import * as Sentry from '~/sentry/sentry_browser_wrapper'; import { sortableStart, sortableEnd } from '~/sortable/utils'; -import WorkItemLinkChildContents from '../shared/work_item_link_child_contents.vue'; +import WorkItemLinkChildContents from 'ee_else_ce/work_items/components/shared/work_item_link_child_contents.vue'; import removeLinkedItemsMutation from '../../graphql/remove_linked_items.mutation.graphql'; import addLinkedItemsMutation from '../../graphql/add_linked_items.mutation.graphql'; diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 1461a3528f6..00ef48561c2 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -145,10 +145,21 @@ module IssuesHelper is_signed_in: current_user.present?.to_s, rss_path: url_for(safe_params.merge(rss_url_options)), sign_in_path: new_user_session_path, - wi: work_items_data(namespace, current_user) + wi: work_items_data(namespace, current_user), + has_subepics_feature: has_subepics_feature?(namespace).to_s } end + def has_subepics_feature?(namespace) + if namespace.is_a?(Group) + return namespace.licensed_feature_available?(:subepics) + elsif namespace.respond_to?(:group) && namespace.group + return namespace.group.licensed_feature_available?(:subepics) + end + + false + end + def has_issue_date_filter_feature?(namespace, current_user) enabled_for_user = Feature.enabled?(:issue_date_filter, current_user) return true if enabled_for_user diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 130426dbc5e..7ab931956c6 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -1011,6 +1011,10 @@ - 1 - - vulnerabilities_namespace_statistics_adjustment - 1 +- - vulnerabilities_namespace_statistics_process_group_transfer_events + - 1 +- - vulnerabilities_namespace_statistics_process_project_transfer_events + - 1 - - vulnerabilities_process_archived_events - 1 - - vulnerabilities_process_bulk_dismissed_events diff --git a/db/docs/design_management_versions.yml b/db/docs/design_management_versions.yml index 7e6b4345d31..3a6f12c39a9 100644 --- a/db/docs/design_management_versions.yml +++ b/db/docs/design_management_versions.yml @@ -9,14 +9,6 @@ description: The SHA referencing changes to individual designs made using the De introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9801 milestone: '11.10' gitlab_schema: gitlab_main_cell -desired_sharding_key: - namespace_id: - references: namespaces - backfill_via: - parent: - foreign_key: issue_id - table: issues - sharding_key: namespace_id - belongs_to: issue -desired_sharding_key_migration_job_name: BackfillDesignManagementVersionsNamespaceId +sharding_key: + namespace_id: namespaces table_size: small diff --git a/db/migrate/20250428204842_update_ai_troubleshoot_job_events_project_fk.rb b/db/migrate/20250428204842_update_ai_troubleshoot_job_events_project_fk.rb new file mode 100644 index 00000000000..7dd524d597d --- /dev/null +++ b/db/migrate/20250428204842_update_ai_troubleshoot_job_events_project_fk.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class UpdateAiTroubleshootJobEventsProjectFk < Gitlab::Database::Migration[2.3] + include Gitlab::Database::PartitioningMigrationHelpers::ForeignKeyHelpers + + disable_ddl_transaction! + milestone '18.0' + + def up + with_lock_retries do + remove_foreign_key_if_exists(:ai_troubleshoot_job_events, column: :project_id, reverse_lock_order: true) + end + + add_concurrent_partitioned_foreign_key :ai_troubleshoot_job_events, :projects, column: :project_id, + on_delete: :cascade, reverse_lock_order: true + end + + def down + with_lock_retries do + remove_foreign_key_if_exists(:ai_troubleshoot_job_events, column: :project_id, reverse_lock_order: true) + end + + add_concurrent_partitioned_foreign_key :ai_troubleshoot_job_events, :projects, column: :project_id, on_delete: nil, + reverse_lock_order: true + end +end diff --git a/db/migrate/20250429094554_add_index_to_project_requirement_status_namespace_project_id.rb b/db/migrate/20250429094554_add_index_to_project_requirement_status_namespace_project_id.rb new file mode 100644 index 00000000000..42c14cf2db5 --- /dev/null +++ b/db/migrate/20250429094554_add_index_to_project_requirement_status_namespace_project_id.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddIndexToProjectRequirementStatusNamespaceProjectId < Gitlab::Database::Migration[2.3] + milestone '18.0' + disable_ddl_transaction! + + INDEX_NAMESPACE_PROJECT_ID_DESC = 'i_project_requirement_statuses_on_namespace_id_project_id' + + def up + add_concurrent_index :project_requirement_compliance_statuses, [:namespace_id, :project_id, :id], + order: { project_id: :asc, id: :asc }, using: :btree, name: INDEX_NAMESPACE_PROJECT_ID_DESC + end + + def down + remove_concurrent_index_by_name :project_requirement_compliance_statuses, INDEX_NAMESPACE_PROJECT_ID_DESC + end +end diff --git a/db/migrate/20250429094649_add_index_to_project_requirement_status_namespace_requirement_id.rb b/db/migrate/20250429094649_add_index_to_project_requirement_status_namespace_requirement_id.rb new file mode 100644 index 00000000000..c7d97857ea6 --- /dev/null +++ b/db/migrate/20250429094649_add_index_to_project_requirement_status_namespace_requirement_id.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddIndexToProjectRequirementStatusNamespaceRequirementId < Gitlab::Database::Migration[2.3] + milestone '18.0' + disable_ddl_transaction! + + INDEX_NAMESPACE_REQUIREMENT_ID_DESC = 'i_project_requirement_statuses_on_namespace_id_requirement_id' + + def up + add_concurrent_index :project_requirement_compliance_statuses, [:namespace_id, :compliance_requirement_id, :id], + order: { compliance_requirement_id: :asc, id: :asc }, using: :btree, name: INDEX_NAMESPACE_REQUIREMENT_ID_DESC + end + + def down + remove_concurrent_index_by_name :project_requirement_compliance_statuses, INDEX_NAMESPACE_REQUIREMENT_ID_DESC + end +end diff --git a/db/migrate/20250429094811_add_index_to_project_requirement_status_namespace_framework_id.rb b/db/migrate/20250429094811_add_index_to_project_requirement_status_namespace_framework_id.rb new file mode 100644 index 00000000000..9c8d569c501 --- /dev/null +++ b/db/migrate/20250429094811_add_index_to_project_requirement_status_namespace_framework_id.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddIndexToProjectRequirementStatusNamespaceFrameworkId < Gitlab::Database::Migration[2.3] + milestone '18.0' + disable_ddl_transaction! + + INDEX_NAMESPACE_FRAMEWORK_ID_DESC = 'i_project_requirement_statuses_on_namespace_id_framework_id' + + def up + add_concurrent_index :project_requirement_compliance_statuses, [:namespace_id, :compliance_framework_id, :id], + order: { compliance_framework_id: :asc, id: :asc }, using: :btree, name: INDEX_NAMESPACE_FRAMEWORK_ID_DESC + end + + def down + remove_concurrent_index_by_name :project_requirement_compliance_statuses, INDEX_NAMESPACE_FRAMEWORK_ID_DESC + end +end diff --git a/db/post_migrate/20250422044141_add_design_management_versions_namespace_id_not_null_constraint.rb b/db/post_migrate/20250422044141_add_design_management_versions_namespace_id_not_null_constraint.rb new file mode 100644 index 00000000000..3776e5db73f --- /dev/null +++ b/db/post_migrate/20250422044141_add_design_management_versions_namespace_id_not_null_constraint.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class AddDesignManagementVersionsNamespaceIdNotNullConstraint < Gitlab::Database::Migration[2.2] + disable_ddl_transaction! + milestone '18.0' + + def up + add_not_null_constraint :design_management_versions, :namespace_id + end + + def down + remove_not_null_constraint :design_management_versions, :namespace_id + end +end diff --git a/db/post_migrate/20250423150108_add_btree_index_on_traversal_ids_vul_namespace_statistics.rb b/db/post_migrate/20250423150108_add_btree_index_on_traversal_ids_vul_namespace_statistics.rb new file mode 100644 index 00000000000..c44ed8f5ed1 --- /dev/null +++ b/db/post_migrate/20250423150108_add_btree_index_on_traversal_ids_vul_namespace_statistics.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddBtreeIndexOnTraversalIdsVulNamespaceStatistics < Gitlab::Database::Migration[2.2] + disable_ddl_transaction! + milestone '18.0' + + INDEX_NAME = 'index_vuln_namespace_statistics_btree_traversal_ids' + + def up + add_concurrent_index :vulnerability_namespace_statistics, :traversal_ids, unique: true, name: INDEX_NAME + end + + def down + remove_concurrent_index_by_name :vulnerability_namespace_statistics, name: INDEX_NAME + end +end diff --git a/db/schema_migrations/20250422044141 b/db/schema_migrations/20250422044141 new file mode 100644 index 00000000000..bcfe7f60192 --- /dev/null +++ b/db/schema_migrations/20250422044141 @@ -0,0 +1 @@ +3edbf476db3fbbc6005ce957db22db0f6b6060e38c1d5ac77c1d3d73f1343d0e \ No newline at end of file diff --git a/db/schema_migrations/20250423150108 b/db/schema_migrations/20250423150108 new file mode 100644 index 00000000000..45856d9cb05 --- /dev/null +++ b/db/schema_migrations/20250423150108 @@ -0,0 +1 @@ +51c23d9955d9a94bc84e9e40e5f7a7b24b35b6e5f02596c29c4da2693e1fc8ff \ No newline at end of file diff --git a/db/schema_migrations/20250428204842 b/db/schema_migrations/20250428204842 new file mode 100644 index 00000000000..0648696697d --- /dev/null +++ b/db/schema_migrations/20250428204842 @@ -0,0 +1 @@ +03f74b80d288bf570e1e8547bab8e992031c4b100a73e4606652bf9f3fd124e9 \ No newline at end of file diff --git a/db/schema_migrations/20250429094554 b/db/schema_migrations/20250429094554 new file mode 100644 index 00000000000..c9acf4b879a --- /dev/null +++ b/db/schema_migrations/20250429094554 @@ -0,0 +1 @@ +c25d19ea0cc74680d5dd48331e2cd8540492bedf4940a775bd33b43abadfed4d \ No newline at end of file diff --git a/db/schema_migrations/20250429094649 b/db/schema_migrations/20250429094649 new file mode 100644 index 00000000000..013725b024c --- /dev/null +++ b/db/schema_migrations/20250429094649 @@ -0,0 +1 @@ +32c7f68dee6145388f4deac62338aa9c37d36d0bcfc827e4054491f006f5bb85 \ No newline at end of file diff --git a/db/schema_migrations/20250429094811 b/db/schema_migrations/20250429094811 new file mode 100644 index 00000000000..686dd91fa13 --- /dev/null +++ b/db/schema_migrations/20250429094811 @@ -0,0 +1 @@ +a214e8bff3d1aca417ea6f0b6c6c50114830486286966dea83bfceea1df4eeb3 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 2a1b4b772ee..928e232d371 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -13753,7 +13753,8 @@ CREATE TABLE design_management_versions ( issue_id bigint, created_at timestamp with time zone NOT NULL, author_id bigint, - namespace_id bigint + namespace_id bigint, + CONSTRAINT check_1d0291f47a CHECK ((namespace_id IS NOT NULL)) ); CREATE SEQUENCE design_management_versions_id_seq @@ -32990,6 +32991,12 @@ CREATE UNIQUE INDEX i_pm_package_versions_on_package_id_and_version ON pm_packag CREATE UNIQUE INDEX i_pm_packages_purl_type_and_name ON pm_packages USING btree (purl_type, name); +CREATE INDEX i_project_requirement_statuses_on_namespace_id_framework_id ON project_requirement_compliance_statuses USING btree (namespace_id, compliance_framework_id, id); + +CREATE INDEX i_project_requirement_statuses_on_namespace_id_project_id ON project_requirement_compliance_statuses USING btree (namespace_id, project_id, id); + +CREATE INDEX i_project_requirement_statuses_on_namespace_id_requirement_id ON project_requirement_compliance_statuses USING btree (namespace_id, compliance_requirement_id, id); + CREATE INDEX i_project_requirement_statuses_on_namespace_id_updated_at_id ON project_requirement_compliance_statuses USING btree (namespace_id, updated_at DESC, id DESC); CREATE INDEX i_protected_branch_unprotect_access_levels_protected_branch_nam ON protected_branch_unprotect_access_levels USING btree (protected_branch_namespace_id); @@ -37746,6 +37753,8 @@ CREATE INDEX index_vuln_namespace_hist_statistics_for_traversal_ids_update ON vu CREATE UNIQUE INDEX index_vuln_namespace_historical_statistics_traversal_ids_date ON vulnerability_namespace_historical_statistics USING btree (traversal_ids, date); +CREATE UNIQUE INDEX index_vuln_namespace_statistics_btree_traversal_ids ON vulnerability_namespace_statistics USING btree (traversal_ids); + CREATE INDEX index_vuln_namespace_statistics_gin_traversal_ids ON vulnerability_namespace_statistics USING gin (traversal_ids); CREATE UNIQUE INDEX index_vuln_namespace_statistics_on_namespace_id ON vulnerability_namespace_statistics USING btree (namespace_id); @@ -44146,7 +44155,7 @@ ALTER TABLE ONLY external_status_checks ADD CONSTRAINT fk_rails_1f5a8aa809 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; ALTER TABLE ai_troubleshoot_job_events - ADD CONSTRAINT fk_rails_1fb7e812da FOREIGN KEY (project_id) REFERENCES projects(id); + ADD CONSTRAINT fk_rails_1fb7e812da FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; ALTER TABLE ONLY dora_daily_metrics ADD CONSTRAINT fk_rails_1fd07aff6f FOREIGN KEY (environment_id) REFERENCES environments(id) ON DELETE CASCADE; diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md index 21179cc68c4..15e2b25e857 100644 --- a/doc/api/graphql/reference/_index.md +++ b/doc/api/graphql/reference/_index.md @@ -13015,7 +13015,8 @@ Input type: `WorkspaceCreateInput` | `maxHoursBeforeTermination` {{< icon name="warning-solid" >}} | [`Int`](#int) | **Deprecated:** Field is not used. Deprecated in GitLab 17.9. | | `projectId` | [`ProjectID!`](#projectid) | ID of the project that will provide the Devfile for the created workspace. | | `projectRef` | [`String`](#string) | Project repo git ref. | -| `variables` | [`[WorkspaceVariableInput!]`](#workspacevariableinput) | Variables to inject into the workspace. | +| `variables` {{< icon name="warning-solid" >}} | [`[WorkspaceVariableInput!]`](#workspacevariableinput) | **Deprecated:** Argument is renamed to workspace_variables. Deprecated in GitLab 18.0. | +| `workspaceVariables` {{< icon name="warning-solid" >}} | [`[WorkspaceVariableInput!]`](#workspacevariableinput) | **Deprecated:** **Status**: Experiment. Introduced in GitLab 18.0. | #### Fields @@ -28696,7 +28697,7 @@ four standard [pagination arguments](#pagination-arguments): ##### `Group.projectComplianceRequirementsStatus` -Compliance standards adherence for the projects in a group and its subgroups. +Compliance statuses for the projects in a group and its subgroups. {{< details >}} **Introduced** in GitLab 17.10. @@ -28714,6 +28715,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | `filters` | [`GroupProjectRequirementComplianceStatusInput`](#groupprojectrequirementcompliancestatusinput) | Filters applied when retrieving compliance requirement statuses. | +| `orderBy` | [`ProjectComplianceRequirementStatusOrderBy`](#projectcompliancerequirementstatusorderby) | Field used to sort compliance requirement statuses. | ##### `Group.projectComplianceStandardsAdherence` @@ -30502,7 +30504,18 @@ Represents an instance-level LDAP link. | `cn` | [`String`](#string) | Common Name (CN) of the LDAP group. | | `filter` | [`String`](#string) | Search filter for the LDAP group. | | `id` | [`ID!`](#id) | ID of the LDAP link. | -| `provider` | [`String!`](#string) | LDAP provider for the LDAP link. | +| `provider` | [`LdapProvider!`](#ldapprovider) | LDAP provider for the LDAP link. | + +### `LdapProvider` + +Represents a LDAP provider. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `id` | [`String`](#string) | ID of the LDAP provider. | +| `label` | [`String`](#string) | Display name of the LDAP provider. | ### `LfsObjectRegistry` @@ -44994,6 +45007,16 @@ Compliance status of the project control. | `PASS` | Pass. | | `PENDING` | Pending. | +### `ProjectComplianceRequirementStatusOrderBy` + +Values for order_by field for project requirement statuses. + +| Value | Description | +| ----- | ----------- | +| `FRAMEWORK` | Order by frameworks. | +| `PROJECT` | Order by projects. | +| `REQUIREMENT` | Order by requirements. | + ### `ProjectFeatureAccessLevel` Access level of a project feature. diff --git a/doc/development/utilities.md b/doc/development/utilities.md index 96e61342848..75a6e1b0b57 100644 --- a/doc/development/utilities.md +++ b/doc/development/utilities.md @@ -11,7 +11,7 @@ We have developed a number of utilities to help ease development: Refer to [`merge_hash.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/utils/merge_hash.rb): -- Deep merges an array of hashes: +- Deep merges an array of elements which can be hashes, arrays, or other objects: ```ruby Gitlab::Utils::MergeHash.merge( diff --git a/doc/update/versions/gitlab_17_changes.md b/doc/update/versions/gitlab_17_changes.md index d93f338e06f..3b17b221ea3 100644 --- a/doc/update/versions/gitlab_17_changes.md +++ b/doc/update/versions/gitlab_17_changes.md @@ -235,8 +235,7 @@ For more information, see [issue 480328](https://gitlab.com/gitlab-org/gitlab/-/ ## Issues to be aware of when upgrading to 17.8 - In GitLab 17.8, three new secrets have been added to support the new encryption framework (started to be used in 17.9). - If you have a multi-node configuration, follow the steps relevant to your installation from - the [17.8.0](#1780) section below. + If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets). - Migration failures when upgrading to GitLab 17.8. @@ -266,8 +265,7 @@ For more information, see [issue 480328](https://gitlab.com/gitlab-org/gitlab/-/ ## Issues to be aware of when upgrading to 17.9 - In GitLab 17.8, three new secrets have been added to support the new encryption framework (started to be used in 17.9). - If you have a multi-node configuration, follow the steps relevant to your installation from - the [17.9.0](#1790) section below. + If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets). - Runner tags missing when upgrading to GitLab 17.9 @@ -289,14 +287,12 @@ For more information, see [issue 480328](https://gitlab.com/gitlab-org/gitlab/-/ ## Issues to be aware of when upgrading to 17.10 - In GitLab 17.8, three new secrets have been added to support the new encryption framework (started to be used in 17.9). - If you have a multi-node configuration, follow the steps relevant to your installation from - the [17.9.0](#1790) section below. + If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets). ## Issues to be aware of when upgrading to 17.11 - In GitLab 17.8, three new secrets have been added to support the new encryption framework (started to be used in 17.9). - If you have a multi-node configuration, follow the steps relevant to your installation from - the [17.9.0](#1790) section below. + If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets). ## 17.11.0 @@ -308,9 +304,7 @@ In GitLab 17.8, three new secrets have been added to support the new encryption - `active_record_encryption_deterministic_key` - `active_record_encryption_key_derivation_salt` -**If you have a multi-node configuration, you must ensure these secrets are the same on all nodes.** Otherwise, the application automatically generates the missing secrets at startup. - -Follow the steps relevant to your installation from the [17.9.0](#1790) section below. +If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets). ## 17.10.0 @@ -322,9 +316,7 @@ In GitLab 17.8, three new secrets have been added to support the new encryption - `active_record_encryption_deterministic_key` - `active_record_encryption_key_derivation_salt` -**If you have a multi-node configuration, you should ensure these secrets are the same on all nodes.** Otherwise, the application automatically generates the missing secrets at startup. - -Follow the steps relevant to your installation from the [17.9.0](#1790) section below. +If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets). ## 17.9.0 @@ -336,71 +328,7 @@ In GitLab 17.8, three new secrets have been added to support the new encryption - `active_record_encryption_deterministic_key` - `active_record_encryption_key_derivation_salt` -**If you have a multi-node configuration, you should ensure these secrets are the same on all nodes.** Otherwise, the application automatically generates the missing secrets at startup. - -{{< tabs >}} - -{{< tab title="Linux package (Omnibus)" >}} - -1. Optional. If possible, put your instance in [maintenance mode](../../administration/maintenance_mode/_index.md) (otherwise if the steps 3 and 6 return `true`, it's fine). -1. Delete all `CloudConnector::Keys` records: - - ```shell - gitlab-rails r 'CloudConnector::Keys.delete_all' - ``` - -1. Check if any records with encrypted attributes exist: - - ```shell - gitlab-rails r 'Rails.application.eager_load!; puts ApplicationRecord.descendants.select { |d| d.encrypted_attributes.present? }.index_with { |model| model.count }.values.all?(&:zero?)' - ``` - - If the result is `true`, you can proceed to the next step. Otherwise, we need to check what are the existing records - and decide if we can delete them before proceeding further. - -1. Pick one Sidekiq or Rails node as a reference node from which you will copy - `/etc/gitlab/gitlab-secrets.json` to all other Sidekiq and Rails nodes. -1. On all Sidekiq and Rails nodes (except the reference node): - - 1. Back up your [configuration files](https://docs.gitlab.com/omnibus/settings/backups/#backup-and-restore-configuration-on-a-linux-package-installation): - - ```shell - sudo gitlab-ctl backup-etc - ``` - - 1. Copy `/etc/gitlab/gitlab-secrets.json` from the reference node, and replace the file of the - same name on the current node. - 1. Reconfigure GitLab: - - ```shell - sudo gitlab-ctl reconfigure - ``` - -1. Check again if any records with encrypted attributes exist (to ensure no records were created while - you performed the previous steps). The return value should be `true`: - - ```shell - gitlab-rails r 'Rails.application.eager_load!; puts ApplicationRecord.descendants.select { |d| d.encrypted_attributes.present? }.index_with { |model| model.count }.values.all?(&:zero?)' - ``` - -1. Create a new Cloud Connector key: `gitlab-rake cloud_connector:keys:create` -1. Optional. On all Sidekiq and Rails nodes, check that the encrypted attributes can be read - (if no `ActiveRecord::Encryption::Errors::Decryption` exception is raised, it's good): - - ```shell - gitlab-rails r 'CloudConnector::Keys.first.secret_key; nil' - ``` - -{{< /tab >}} - -{{< tab title="Helm chart (Kubernetes)" >}} - -If you disabled the [shared-secrets chart](https://docs.gitlab.com/charts/charts/shared-secrets/), -you need to [manually create these secrets](https://docs.gitlab.com/charts/releases/8_0.html). - -{{< /tab >}} - -{{< /tabs >}} +If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets). ## 17.8.0 @@ -412,57 +340,7 @@ In GitLab 17.8, three new secrets have been added to support the new encryption - `active_record_encryption_deterministic_key` - `active_record_encryption_key_derivation_salt` -**If you have a multi-node configuration, you should ensure these secrets are the same on all nodes.** Otherwise, the application automatically generates the missing secrets at startup. - -{{< tabs >}} - -{{< tab title="Linux package (Omnibus)" >}} - -1. Optional. If possible, put your instance in [maintenance mode](../../administration/maintenance_mode/_index.md) (otherwise if the steps 2 and 5 return `true`, it's fine). -1. Check if any records with encrypted attributes exist: - - ```shell - gitlab-rails r 'Rails.application.eager_load!; puts ApplicationRecord.descendants.select { |d| d.encrypted_attributes.present? }.index_with { |model| model.count }.values.all?(&:zero?)' - ``` - - If the result is `true`, proceed to the next step. Otherwise, we need to check what are the existing records - and decide if we can delete them before proceeding further. - -1. Pick one Sidekiq or Rails node as a reference node from which you will copy - `/etc/gitlab/gitlab-secrets.json` to all other Sidekiq and Rails nodes. -1. On all Sidekiq and Rails nodes (except the reference node): - - 1. Back up your [configuration files](https://docs.gitlab.com/omnibus/settings/backups/#backup-and-restore-configuration-on-a-linux-package-installation): - - ```shell - sudo gitlab-ctl backup-etc - ``` - - 1. Copy `/etc/gitlab/gitlab-secrets.json` from the reference node, and replace the file of the - same name on the current node. - 1. Reconfigure GitLab: - - ```shell - sudo gitlab-ctl reconfigure - ``` - -1. Check again if any records with encrypted attributes exist (to ensure no records were created while - you performed the previous steps). The return value should be `true`: - - ```shell - gitlab-rails r 'Rails.application.eager_load!; puts ApplicationRecord.descendants.select { |d| d.encrypted_attributes.present? }.index_with { |model| model.count }.values.all?(&:zero?)' - ``` - -{{< /tab >}} - -{{< tab title="Helm chart (Kubernetes)" >}} - -If you disabled the [shared-secrets chart](https://docs.gitlab.com/charts/charts/shared-secrets/), -you need to [manually create these secrets](https://docs.gitlab.com/charts/releases/8_0.html). - -{{< /tab >}} - -{{< /tabs >}} +If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets). ### Change to the GitLab agent server for Kubernetes @@ -809,3 +687,194 @@ Feedback about this conditional stop on the upgrade path can be provided [in the | 17.1 | All | 17.1.7 | | 17.2 | All | 17.2.5 | | 17.3 | All | 17.3.1 | + +## Unify new encryption secrets + +[GitLab 17.8 introduced three new secrets](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175154) to support the new encryption framework, [which was introduced in GitLab 17.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/179559): + +- `active_record_encryption_primary_key` +- `active_record_encryption_deterministic_key` +- `active_record_encryption_key_derivation_salt` + +If you have a multi-node configuration, you must ensure these secrets are the same on all nodes. Otherwise, the application automatically generates the missing secrets at startup. + +{{< tabs >}} + +{{< tab title="Linux package (Omnibus)" >}} + +1. If possible, [enable maintenance mode](../../administration/maintenance_mode/_index.md#enable-maintenance-mode). +1. (Only for GitLab >= 17.9) Delete all `CloudConnector::Keys` records: + + ```shell + gitlab-rails runner 'CloudConnector::Keys.delete_all' + ``` + +1. On all Sidekiq and GitLab application nodes, gather information about encryption keys and their usage. + + On GitLab >= 18.0.0, >= 17.11.2, >= 17.10.6, or >= 17.9.8, run: + + ```shell + gitlab-rake gitlab:doctor:encryption_keys + ``` + + If you're using other versions, check the content of [`encryption_keys.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/doctor/encryption_keys.rb) and + then download it: + + ```shell + wget -O encryption_keys.rb https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/doctor/encryption_keys.rb?inline=false + ``` + + Then run the file: + + ```shell + gitlab-rails runner 'require_relative Pathname(Dir.pwd).join("encryption_keys.rb"); Gitlab::Doctor::EncryptionKeys.new(Logger.new($stdout)).run!' + ``` + + The output of the command indicates which one of three possible processes you must follow. + + - If all "Encryption keys usage for \" report `NONE`, select any Sidekiq or GitLab application node as a reference + node from which to copy `/etc/gitlab/gitlab-secrets.json` to all other Sidekiq and GitLab application nodes. + - If all reported keys usage are for the same key ID, select the node where the key exists as a reference node from which to copy `/etc/gitlab/gitlab-secrets.json` to all + other Sidekiq and GitLab application nodes. For example, let's say you get the following output on node 1: + + ```shell + Gathering existing encryption keys: + - active_record_encryption_primary_key: ID => `bb32`; truncated secret => `bEt...eBU` + - active_record_encryption_deterministic_key: ID => `445f`; truncated secret => `MJo...yg5` + + [... snipped for brevity ...] + + Encryption keys usage for VirtualRegistries::Packages::Maven::Upstream: NONE + Encryption keys usage for Ai::ActiveContext::Connection: NONE + Encryption keys usage for CloudConnector::Keys: NONE + Encryption keys usage for DependencyProxy::GroupSetting: + - `bb32` => 8 + Encryption keys usage for Ci::PipelineScheduleInput: + - `bb32` => 1 + ``` + + And let's say you get the following output on node 2 (the `(UNKNOWN KEY!)` is fine as long as a single key ID is used. For example, `bb32` here): + + ```shell + Gathering existing encryption keys: + - active_record_encryption_primary_key: ID => `83kf`; truncated secret => `pKq...ikC` + - active_record_encryption_deterministic_key: ID => `b722`; truncated secret => `Lma...iJ7` + + [... snipped for brevity ...] + + Encryption keys usage for VirtualRegistries::Packages::Maven::Upstream: NONE + Encryption keys usage for Ai::ActiveContext::Connection: NONE + Encryption keys usage for CloudConnector::Keys: NONE + Encryption keys usage for DependencyProxy::GroupSetting: + - `bb32` (UNKNOWN KEY!) => 8 + Encryption keys usage for Ci::PipelineScheduleInput: + - `bb32` (UNKNOWN KEY!) => 1 + ``` + + With the above examples, you would pick node 1 as the reference node. + 1. Not all reported keys usage are for the same key ID. For instance, if node 1 shows `` -`bb32` => 1 `` and node 2 shows + `` - `83kf` => 1 ``. In that case, the resolution is more complex as it involves re-encrypting all data with a single encryption key. + Alternatively, if you're ok losing some data, you can delete records so that all remaining records use the same key ID. + Contact [support](https://about.gitlab.com/support/) for further assistance. + +1. After deciding which node is the reference node, decide which of the reference node's secrets must be copied to the other nodes. +1. On all Sidekiq and Rails nodes except the reference node: + + 1. Back up your [configuration files](https://docs.gitlab.com/omnibus/settings/backups/#backup-and-restore-configuration-on-a-linux-package-installation): + + ```shell + sudo gitlab-ctl backup-etc + ``` + + 1. Copy `/etc/gitlab/gitlab-secrets.json` from the reference node, and replace the file of the + same name on the current node. + 1. Reconfigure GitLab: + + ```shell + sudo gitlab-ctl reconfigure + ``` + + 1. Check again encryption keys and their usage with one of the following command depending on your version: + + On GitLab >= 18.0.0, >= 17.11.2, >= 17.10.6, or >= 17.9.8, run: + + ```shell + gitlab-rake gitlab:doctor:encryption_keys + ``` + + If you're using other versions: + + ```shell + gitlab-rails runner 'require_relative Pathname(Dir.pwd).join("encryption_keys.rb"); Gitlab::Doctor::EncryptionKeys.new(Logger.new($stdout)).run!' + ``` + + All reported keys usage are for the same key ID. For example, on node 1: + + ```shell + Gathering existing encryption keys: + - active_record_encryption_primary_key: ID => `bb32`; truncated secret => `bEt...eBU` + - active_record_encryption_deterministic_key: ID => `445f`; truncated secret => `MJo...yg5` + + [... snipped for brevity ...] + + Encryption keys usage for VirtualRegistries::Packages::Maven::Upstream: NONE + Encryption keys usage for Ai::ActiveContext::Connection: NONE + Encryption keys usage for CloudConnector::Keys: + - `bb32` => 1 + Encryption keys usage for DependencyProxy::GroupSetting: + - `bb32` => 8 + Encryption keys usage for Ci::PipelineScheduleInput: + - `bb32` => 1 + ``` + + And for example, on node 2 (you should not see any `(UNKNOWN KEY!)` this time): + + ```shell + Gathering existing encryption keys: + - active_record_encryption_primary_key: ID => `bb32`; truncated secret => `bEt...eBU` + - active_record_encryption_deterministic_key: ID => `445f`; truncated secret => `MJo...yg5` + + [... snipped for brevity ...] + + Encryption keys usage for VirtualRegistries::Packages::Maven::Upstream: NONE + Encryption keys usage for Ai::ActiveContext::Connection: NONE + Encryption keys usage for CloudConnector::Keys: + - `bb32` => 1 + Encryption keys usage for DependencyProxy::GroupSetting: + - `bb32` => 8 + Encryption keys usage for Ci::PipelineScheduleInput: + - `bb32` => 1 + ``` + + 1. Remove the `encryption_keys.rb` file if you downloaded it previously: + + ```shell + rm encryption_keys.rb + ``` + +1. Create a new Cloud Connector key: + + For GitLab >= 17.10: + + ```shell + gitlab-rake cloud_connector:keys:create + ``` + + For GitLab 17.9: + + ```shell + gitlab-rails runner 'CloudConnector::Keys.create!(secret_key: OpenSSL::PKey::RSA.new(2048).to_pem)' + ``` + +1. [Disable maintenance mode](../../administration/maintenance_mode/_index.md#disable-maintenance-mode). + +{{< /tab >}} + +{{< tab title="Helm chart (Kubernetes)" >}} + +If you disabled the [shared-secrets chart](https://docs.gitlab.com/charts/charts/shared-secrets/), +you need to [manually create these secrets](https://docs.gitlab.com/charts/releases/8_0.html). + +{{< /tab >}} + +{{< /tabs >}} diff --git a/lib/gitlab/utils/merge_hash.rb b/lib/gitlab/utils/merge_hash.rb index 48ba13b8561..24e6860e2cd 100644 --- a/lib/gitlab/utils/merge_hash.rb +++ b/lib/gitlab/utils/merge_hash.rb @@ -6,7 +6,7 @@ module Gitlab module Utils module MergeHash extend self - # Deep merges an array of hashes + # Deep merges an array of elements which can be hashes, arrays, or other objects. # # [{ hello: ["world"] }, # { hello: "Everyone" }, @@ -49,8 +49,14 @@ module Gitlab def merge_hash_into_array(array, new_hash) crushed_new_hash = crush_hash(new_hash) + # Merge the hash into an existing element of the array if there is overlap - if mergeable_index = array.index { |element| crushable?(element) && (crush(element) & crushed_new_hash).any? } + mergeable_index = array.index do |element| + crushed_element = crushable?(element) ? crush(element) : Array.wrap(element) + (crushed_element & crushed_new_hash).any? + end + + if mergeable_index array[mergeable_index] = merge_hash_tree(array[mergeable_index], new_hash) else array << new_hash diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 242a9721a47..bf18a2e072a 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -61196,7 +61196,7 @@ msgstr "" msgid "The subdomain setting." msgstr "" -msgid "The subject will be used as the title of the new issue, and the message will be the description. %{quickActionsLinkStart}Quick actions%{quickActionsLinkEnd} and styling with %{markdownLinkStart}Markdown%{markdownLinkEnd} are supported." +msgid "The subject will be used as the title of the new %{name}, and the message will be the description. %{quickActionsLinkStart}Quick actions%{quickActionsLinkEnd} and styling with %{markdownLinkStart}Markdown%{markdownLinkEnd} are supported." msgstr "" msgid "The tag name can't be changed for an existing release." diff --git a/spec/frontend/work_items/components/work_item_change_type_modal_spec.js b/spec/frontend/work_items/components/work_item_change_type_modal_spec.js index eb7f08f5b76..9c8e2fa1a6a 100644 --- a/spec/frontend/work_items/components/work_item_change_type_modal_spec.js +++ b/spec/frontend/work_items/components/work_item_change_type_modal_spec.js @@ -22,6 +22,7 @@ import { convertWorkItemMutationResponse, workItemChangeTypeWidgets, workItemQueryResponse, + workItemWithEpicParentQueryResponse, } from '../mock_data'; import { designCollectionResponse, mockDesign } from './design_management/mock_data'; @@ -78,6 +79,7 @@ describe('WorkItemChangeTypeModal component', () => { convertWorkItemMutationHandler = convertWorkItemMutationSuccessHandler, designQueryHandler = noDesignQueryHandler, allowedConversionTypesEE = [], + hasSubepicsFeature = true, } = {}) => { wrapper = mountExtended(WorkItemChangeTypeModal, { apolloProvider: createMockApollo([ @@ -100,6 +102,7 @@ describe('WorkItemChangeTypeModal component', () => { glFeatures: { workItemsAlpha, }, + hasSubepicsFeature, }, stubs: { GlModal: stubComponent(GlModal, { @@ -141,20 +144,34 @@ describe('WorkItemChangeTypeModal component', () => { expect(findGlFormSelect().findAll('option')).toHaveLength(2); }); - it('does not allow to change type and disables `Change type` button when the work item has a parent', async () => { - createComponent({ hasParent: true, widgets: workItemQueryResponse.data.workItem.widgets }); + describe('work item type change tests', () => { + it.each` + scenario | widgets | hasSubepicsFeature | btnDisabled | parentType + ${'epic parent with subepics enabled'} | ${workItemWithEpicParentQueryResponse.data.workItem.widgets} | ${true} | ${false} | ${''} + ${'epic parent with subepics disabled'} | ${workItemWithEpicParentQueryResponse.data.workItem.widgets} | ${false} | ${true} | ${'epic'} + ${'non-epic parent with subepics enabled'} | ${workItemQueryResponse.data.workItem.widgets} | ${true} | ${true} | ${'issue'} + ${'non-epic parent with subepics disabled'} | ${workItemQueryResponse.data.workItem.widgets} | ${false} | ${true} | ${'issue'} + `('$scenario', async ({ widgets, hasSubepicsFeature, btnDisabled, parentType }) => { + createComponent({ + hasParent: true, + widgets, + hasSubepicsFeature, + }); - await waitForPromises(); + await waitForPromises(); - findGlFormSelect().vm.$emit('change', issueTypeId); + findGlFormSelect().vm.$emit('change', issueTypeId); - await nextTick(); + await nextTick(); - expect(findWarningAlert().text()).toBe( - 'Parent item type issue is not supported on issue. Remove the parent item to change type.', - ); - - expect(findChangeTypeModal().props('actionPrimary').attributes.disabled).toBe(true); + const hasWarning = parentType !== ''; + expect(findWarningAlert().exists()).toBe(hasWarning); + if (hasWarning) { + const warningText = `Parent item type ${parentType} is not supported on issue. Remove the parent item to change type.`; + expect(findWarningAlert().text()).toBe(warningText); + } + expect(findChangeTypeModal().props('actionPrimary').attributes.disabled).toBe(btnDisabled); + }); }); it('does not allow to change type and disables `Change type` button when the work item has child items', async () => { diff --git a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js index 47acac1a862..ac8316fee6e 100644 --- a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js +++ b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js @@ -10,7 +10,7 @@ import getWorkItemTreeQuery from '~/work_items/graphql/work_item_tree.query.grap import isExpandedHierarchyTreeChildQuery from '~/work_items/graphql/client/is_expanded_hierarchy_tree_child.query.graphql'; import WorkItemLinkChild from '~/work_items/components/work_item_links/work_item_link_child.vue'; import WorkItemChildrenWrapper from '~/work_items/components/work_item_links/work_item_children_wrapper.vue'; -import WorkItemLinkChildContents from '~/work_items/components/shared/work_item_link_child_contents.vue'; +import WorkItemLinkChildContents from 'ee_else_ce/work_items/components/shared/work_item_link_child_contents.vue'; import { WIDGET_TYPE_HIERARCHY, WORK_ITEM_TYPE_NAME_OBJECTIVE, diff --git a/spec/frontend/work_items/components/work_item_relationships/work_item_relationship_list_spec.js b/spec/frontend/work_items/components/work_item_relationships/work_item_relationship_list_spec.js index 3e76645551d..ccc2dee353f 100644 --- a/spec/frontend/work_items/components/work_item_relationships/work_item_relationship_list_spec.js +++ b/spec/frontend/work_items/components/work_item_relationships/work_item_relationship_list_spec.js @@ -4,7 +4,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { visitUrl } from '~/lib/utils/url_utility'; import WorkItemRelationshipList from '~/work_items/components/work_item_relationships/work_item_relationship_list.vue'; -import WorkItemLinkChildContents from '~/work_items/components/shared/work_item_link_child_contents.vue'; +import WorkItemLinkChildContents from 'ee_else_ce/work_items/components/shared/work_item_link_child_contents.vue'; import removeLinkedItemsMutation from '~/work_items/graphql/remove_linked_items.mutation.graphql'; import addLinkedItemsMutation from '~/work_items/graphql/add_linked_items.mutation.graphql'; @@ -87,7 +87,6 @@ describe('WorkItemRelationshipList', () => { childItem: mockLinkedItems[0].workItem, canUpdate: true, workItemFullPath, - showWeight: true, }); }); diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js index cc1c8db966c..0df050ce097 100644 --- a/spec/frontend/work_items/mock_data.js +++ b/spec/frontend/work_items/mock_data.js @@ -333,6 +333,85 @@ export const workItemQueryResponse = { }, }; +export const workItemWithEpicParentQueryResponse = { + data: { + workItem: { + __typename: 'WorkItem', + id: 'gid://gitlab/WorkItem/1', + iid: '1', + archived: false, + title: 'Test', + movedToWorkItemUrl: null, + duplicatedToWorkItemUrl: null, + promotedToEpicUrl: null, + state: 'OPEN', + description: 'description', + confidential: false, + createdAt: '2022-08-03T12:41:54Z', + updatedAt: null, + closedAt: null, + author: { + avatarUrl: 'http://127.0.0.1:3000/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + id: 'gid://gitlab/User/1', + name: 'Administrator', + username: 'root', + webUrl: 'http://127.0.0.1:3000/root', + webPath: '/root', + __typename: 'UserCore', + }, + project: { + id: 'gid://gitlab/Project/7', + __typename: 'Project', + }, + namespace: { + __typename: 'Project', + id: '1', + fullPath: 'test-project-path', + name: 'Project name', + fullName: 'Group name', + }, + workItemType: { + __typename: 'WorkItemType', + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + iconName: 'issue-type-task', + }, + userPermissions: { + adminParentLink: false, + adminWorkItemLink: true, + deleteWorkItem: false, + createNote: false, + markNoteAsInternal: true, + moveWorkItem: false, + reportSpam: false, + setWorkItemMetadata: false, + summarizeComments: false, + updateWorkItem: false, + __typename: 'WorkItemPermissions', + }, + widgets: [ + { + __typename: 'WorkItemWidgetHierarchy', + type: 'HIERARCHY', + hasChildren: true, + parent: { + id: 'gid://gitlab/WorkItem/3', + title: 'Work Item Epic', + webUrl: 'http://127.0.0.1:3000/groups/gitlab-org/-/work_items/130', + __typename: 'WorkItem', + workItemType: { + id: 'gid://gitlab/WorkItems::Type/6', + name: 'Epic', + iconName: 'issue-type-epic', + __typename: 'WorkItemType', + }, + }, + }, + ], + }, + }, +}; + export const updateWorkItemMutationResponse = { data: { workItemUpdate: { diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 588e33fe30d..a29153f66e5 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -289,6 +289,7 @@ RSpec.describe IssuesHelper, feature_category: :team_planning do allow(helper).to receive(:can?).with(current_user, :read_crm_organization, group.crm_group).and_return(false) allow(helper).to receive(:image_path).and_return('#') allow(helper).to receive(:url_for).and_return('#') + allow(helper).to receive(:licensed_feature_available?).with(:subepics).and_return(false) assign(:has_issues, false) assign(:has_projects, true) @@ -307,7 +308,8 @@ RSpec.describe IssuesHelper, feature_category: :team_planning do rss_path: '#', sign_in_path: new_user_session_path, group_id: group.id, - time_tracking_limit_to_hours: "false" + time_tracking_limit_to_hours: "false", + has_subepics_feature: "false" } expect(helper.group_issues_list_data(group, current_user)).to include(expected) @@ -498,4 +500,59 @@ RSpec.describe IssuesHelper, feature_category: :team_planning do end end end + + describe '#has_subepics_feature?' do + subject(:has_subepics_feature) { helper.has_subepics_feature?(namespace) } + + context 'when namespace is a group project' do + let_it_be(:namespace) { create(:project, namespace: group) } + + context 'when subepics feature is licensed and available for group' do + before do + allow(namespace.group).to receive(:licensed_feature_available?).with(:subepics).and_return(true) + end + + it { is_expected.to be_truthy } + end + + context 'when subepics feature is not licensed for group' do + before do + allow(namespace.group).to receive(:licensed_feature_available?).with(:subepics).and_return(false) + end + + it { is_expected.to be_falsey } + end + end + + context 'when namespace is a group' do + let_it_be(:namespace) { group } + + context 'when subepics feature is licensed and available' do + before do + allow(namespace).to receive(:licensed_feature_available?).with(:subepics).and_return(true) + end + + it { is_expected.to be_truthy } + end + + context 'when subepics feature is not licensed' do + before do + allow(namespace).to receive(:licensed_feature_available?).with(:subepics).and_return(false) + end + + it { is_expected.to be_falsey } + end + end + + context 'when namespace is neither a group nor has a group' do + let_it_be(:namespace) { create(:namespace) } + + before do + allow(namespace).to receive(:is_a?).with(Group).and_return(false) + allow(namespace).to receive(:respond_to?).with(:group).and_return(false) + end + + it { is_expected.to be_falsey } + end + end end diff --git a/spec/lib/gitlab/utils/merge_hash_spec.rb b/spec/lib/gitlab/utils/merge_hash_spec.rb index 4eec6e83be2..de39f4a60d3 100644 --- a/spec/lib/gitlab/utils/merge_hash_spec.rb +++ b/spec/lib/gitlab/utils/merge_hash_spec.rb @@ -13,10 +13,15 @@ RSpec.describe Gitlab::Utils::MergeHash do describe '.elements' do it 'deep merges an array of elements' do - input = [{ hello: ["world"] }, - { hello: "Everyone" }, - { hello: { greetings: ['Bonjour', 'Hello', 'Hallo', 'Dzień dobry'] } }, - "Goodbye", "Hallo"] + input = [ + :hello, + "Howdy", + { hello: ["world"] }, + { hello: "Everyone" }, + { hello: { greetings: ['Bonjour', 'Hello', 'Hallo', 'Dzień dobry'] } }, + "Goodbye", + "Hallo" + ] expected_output = [ { hello: @@ -26,6 +31,7 @@ RSpec.describe Gitlab::Utils::MergeHash do { greetings: ['Bonjour', 'Hello', 'Hallo', 'Dzień dobry'] } ] }, + "Howdy", "Goodbye" ] diff --git a/spec/models/design_management/version_spec.rb b/spec/models/design_management/version_spec.rb index e06daac64fb..cda4ab1e9ce 100644 --- a/spec/models/design_management/version_spec.rb +++ b/spec/models/design_management/version_spec.rb @@ -103,7 +103,7 @@ RSpec.describe DesignManagement::Version do it 'has an appropriate cause' do expect { call_with_empty_actions } - .to raise_error(have_attributes(cause: ActiveRecord::RecordInvalid)) + .to raise_error(have_attributes(cause: ActiveRecord::StatementInvalid)) end it 'provides extra data sentry can consume' do