Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-04-06 03:14:55 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-04-06 03:14:55 +0300
commitd39c778244590f478537df87ed01dde2705350a8 (patch)
tree5792def7c7c01effeeea50eb7fba02d0a53b0169
parentb7d0ee2a31d4d8b8037c07cb1df7c123d2e754b5 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop_todo/rspec/missing_feature_category.yml2
-rw-r--r--app/assets/javascripts/super_sidebar/components/user_bar.vue16
-rw-r--r--app/assets/javascripts/super_sidebar/super_sidebar_bundle.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/entity_select/entity_select.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/entity_select/init_project_selects.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/entity_select/project_select.vue15
-rw-r--r--app/helpers/sidebars_helper.rb8
-rw-r--r--app/models/work_items/resource_link_event.rb14
-rw-r--r--app/views/clusters/clusters/_advanced_settings.html.haml1
-rw-r--r--db/docs/resource_link_events.yml10
-rw-r--r--db/migrate/20230313031351_create_resource_link_events.rb15
-rw-r--r--db/migrate/20230329085754_add_foreign_key_to_resource_link_events_on_user.rb15
-rw-r--r--db/migrate/20230403164454_add_fork_storage_size_columns_to_root_storage_statistics.rb11
-rw-r--r--db/schema_migrations/202303130313511
-rw-r--r--db/schema_migrations/202303290857541
-rw-r--r--db/schema_migrations/202304031644541
-rw-r--r--db/structure.sql43
-rw-r--r--lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb52
-rw-r--r--lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb4
-rw-r--r--locale/gitlab.pot3
-rw-r--r--package.json1
-rw-r--r--qa/qa/page/group/menu.rb10
-rw-r--r--qa/qa/page/group/sub_menus/super_sidebar/build.rb25
-rw-r--r--qa/qa/page/group/sub_menus/super_sidebar/common.rb24
-rw-r--r--qa/qa/page/group/sub_menus/super_sidebar/settings.rb8
-rw-r--r--qa/qa/page/project/menu.rb4
-rw-r--r--qa/qa/page/project/sub_menus/issues.rb2
-rw-r--r--qa/qa/page/project/sub_menus/super_sidebar/analyze.rb45
-rw-r--r--qa/qa/page/project/sub_menus/super_sidebar/build.rb8
-rw-r--r--qa/qa/page/project/sub_menus/super_sidebar/code.rb8
-rw-r--r--qa/qa/page/project/sub_menus/super_sidebar/common.rb24
-rw-r--r--qa/qa/page/project/sub_menus/super_sidebar/manage.rb45
-rw-r--r--qa/qa/page/project/sub_menus/super_sidebar/monitor.rb8
-rw-r--r--qa/qa/page/project/sub_menus/super_sidebar/operate.rb8
-rw-r--r--qa/qa/page/project/sub_menus/super_sidebar/plan.rb22
-rw-r--r--qa/qa/page/project/sub_menus/super_sidebar/project.rb8
-rw-r--r--qa/qa/page/project/sub_menus/super_sidebar/secure.rb12
-rw-r--r--qa/qa/page/project/sub_menus/super_sidebar/settings.rb8
-rw-r--r--qa/qa/page/sub_menus/common.rb39
-rw-r--r--qa/qa/page/sub_menus/super_sidebar/manage.rb43
-rw-r--r--qa/qa/page/sub_menus/super_sidebar/plan.rb39
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb2
-rw-r--r--spec/factories/work_items/resource_link_events.rb10
-rw-r--r--spec/frontend/super_sidebar/components/user_bar_spec.js38
-rw-r--r--spec/frontend/super_sidebar/mock_data.js1
-rw-r--r--spec/frontend/vue_shared/components/entity_select/project_select_spec.js13
-rw-r--r--spec/helpers/sidebars_helper_spec.rb17
-rw-r--r--spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb115
-rw-r--r--spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb5
-rw-r--r--spec/models/work_items/resource_link_event_spec.rb16
-rw-r--r--spec/support/shared_examples/models/resource_event_shared_examples.rb40
51 files changed, 577 insertions, 297 deletions
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml
index d59f5f4c47d..dfcc1e48ddd 100644
--- a/.rubocop_todo/rspec/missing_feature_category.yml
+++ b/.rubocop_todo/rspec/missing_feature_category.yml
@@ -3663,7 +3663,6 @@ RSpec/MissingFeatureCategory:
- 'spec/lib/gitlab/database/migrations/test_background_runner_spec.rb'
- 'spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb'
- 'spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb'
- - 'spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb'
- 'spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb'
- 'spec/lib/gitlab/database/partitioning/monthly_strategy_spec.rb'
- 'spec/lib/gitlab/database/partitioning/partition_manager_spec.rb'
@@ -3674,7 +3673,6 @@ RSpec/MissingFeatureCategory:
- 'spec/lib/gitlab/database/partitioning/time_partition_spec.rb'
- 'spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb'
- 'spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb'
- - 'spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb'
- 'spec/lib/gitlab/database/partitioning_spec.rb'
- 'spec/lib/gitlab/database/pg_class_spec.rb'
- 'spec/lib/gitlab/database/postgres_constraint_spec.rb'
diff --git a/app/assets/javascripts/super_sidebar/components/user_bar.vue b/app/assets/javascripts/super_sidebar/components/user_bar.vue
index 750ec0aa0c6..e03c587567e 100644
--- a/app/assets/javascripts/super_sidebar/components/user_bar.vue
+++ b/app/assets/javascripts/super_sidebar/components/user_bar.vue
@@ -42,13 +42,14 @@ export default {
false,
),
todoList: __('To-Do list'),
+ stopImpersonating: __('Stop impersonating'),
},
directives: {
GlTooltip: GlTooltipDirective,
GlModal: GlModalDirective,
SafeHtml,
},
- inject: ['rootPath'],
+ inject: ['rootPath', 'isImpersonating'],
props: {
sidebarData: {
type: Object,
@@ -114,6 +115,19 @@ export default {
<search-modal />
<user-menu :data="sidebarData" />
+
+ <gl-button
+ v-if="isImpersonating"
+ v-gl-tooltip
+ :href="sidebarData.stop_impersonation_path"
+ :title="$options.i18n.stopImpersonating"
+ :aria-label="$options.i18n.stopImpersonating"
+ icon="incognito"
+ variant="confirm"
+ category="tertiary"
+ data-method="delete"
+ data-testid="stop-impersonation-btn"
+ />
</div>
<div class="gl-display-flex gl-justify-content-space-between gl-px-3 gl-py-2 gl-gap-2">
<counter
diff --git a/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js b/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js
index c5e8c68b940..58b49f218ad 100644
--- a/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js
+++ b/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils';
import { initStatusTriggers } from '../header';
import createStore from './components/global_search/store';
import {
@@ -29,6 +29,7 @@ export const initSuperSidebar = () => {
const searchData = convertObjectPropsToCamelCase(sidebarData.search);
const { searchPath, issuesPath, mrPath, autocompletePath, searchContext } = searchData;
+ const isImpersonating = parseBoolean(sidebarData.is_impersonating);
return new Vue({
el,
@@ -37,6 +38,7 @@ export const initSuperSidebar = () => {
provide: {
rootPath,
toggleNewNavEndpoint,
+ isImpersonating,
},
store: createStore({
searchPath,
diff --git a/app/assets/javascripts/vue_shared/components/entity_select/entity_select.vue b/app/assets/javascripts/vue_shared/components/entity_select/entity_select.vue
index 45c50dce8ce..9b45e969c90 100644
--- a/app/assets/javascripts/vue_shared/components/entity_select/entity_select.vue
+++ b/app/assets/javascripts/vue_shared/components/entity_select/entity_select.vue
@@ -13,6 +13,11 @@ export default {
GlCollapsibleListbox,
},
props: {
+ block: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
label: {
type: String,
required: true,
@@ -176,6 +181,7 @@ export default {
<gl-collapsible-listbox
ref="listbox"
v-model="selected"
+ :block="block"
:header-text="headerText"
:reset-button-label="resetButtonLabel"
:toggle-text="toggleText"
diff --git a/app/assets/javascripts/vue_shared/components/entity_select/init_project_selects.js b/app/assets/javascripts/vue_shared/components/entity_select/init_project_selects.js
index 1afbeda74c4..12db70d8e9c 100644
--- a/app/assets/javascripts/vue_shared/components/entity_select/init_project_selects.js
+++ b/app/assets/javascripts/vue_shared/components/entity_select/init_project_selects.js
@@ -20,6 +20,8 @@ export const initProjectSelects = () => {
orderBy,
selected: initialSelection,
} = el.dataset;
+ const block = parseBoolean(el.dataset.block);
+ const withShared = parseBoolean(el.dataset.withShared);
const includeSubgroups = parseBoolean(el.dataset.includeSubgroups);
const membership = parseBoolean(el.dataset.membership);
const hasHtmlLabel = parseBoolean(el.dataset.hasHtmlLabel);
@@ -37,6 +39,8 @@ export const initProjectSelects = () => {
groupId,
userId,
orderBy,
+ block,
+ withShared,
includeSubgroups,
membership,
initialSelection,
diff --git a/app/assets/javascripts/vue_shared/components/entity_select/project_select.vue b/app/assets/javascripts/vue_shared/components/entity_select/project_select.vue
index 393991d746e..7af3819f2a5 100644
--- a/app/assets/javascripts/vue_shared/components/entity_select/project_select.vue
+++ b/app/assets/javascripts/vue_shared/components/entity_select/project_select.vue
@@ -20,6 +20,11 @@ export default {
SafeHtml,
},
props: {
+ block: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
label: {
type: String,
required: true,
@@ -47,6 +52,11 @@ export default {
required: false,
default: null,
},
+ withShared: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
includeSubgroups: {
type: Boolean,
required: false,
@@ -86,7 +96,7 @@ export default {
if (this.groupId) {
return Api.groupProjects(this.groupId, searchString, {
...commonParams,
- with_shared: true,
+ with_shared: this.withShared,
include_subgroups: this.includeSubgroups,
simple: true,
});
@@ -99,7 +109,7 @@ export default {
this.userId,
searchString,
{
- with_shared: true,
+ with_shared: this.withShared,
include_subgroups: this.includeSubgroups,
},
(res) => ({ data: res }),
@@ -154,6 +164,7 @@ export default {
:default-toggle-text="$options.i18n.searchForProject"
:fetch-items="fetchProjects"
:fetch-initial-selection-text="fetchProjectName"
+ :block="block"
clearable
>
<template v-if="hasHtmlLabel" #label>
diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb
index 6a19943afa6..341937e78de 100644
--- a/app/helpers/sidebars_helper.rb
+++ b/app/helpers/sidebars_helper.rb
@@ -87,7 +87,9 @@ module SidebarsHelper
search: search_data,
pinned_items: user.pinned_nav_items[panel_type] || [],
panel_type: panel_type,
- update_pins_url: pins_url
+ update_pins_url: pins_url,
+ is_impersonating: impersonating?,
+ stop_impersonation_path: admin_impersonation_path
}
end
@@ -323,6 +325,10 @@ module SidebarsHelper
count.to_s
end
end
+
+ def impersonating?
+ !!session[:impersonator_id]
+ end
end
SidebarsHelper.prepend_mod_with('SidebarsHelper')
diff --git a/app/models/work_items/resource_link_event.rb b/app/models/work_items/resource_link_event.rb
new file mode 100644
index 00000000000..64d51b2743c
--- /dev/null
+++ b/app/models/work_items/resource_link_event.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module WorkItems
+ class ResourceLinkEvent < ResourceEvent
+ belongs_to :child_work_item, class_name: 'WorkItem'
+
+ validates :child_work_item, presence: true
+
+ enum action: {
+ add: 1,
+ remove: 2
+ }
+ end
+end
diff --git a/app/views/clusters/clusters/_advanced_settings.html.haml b/app/views/clusters/clusters/_advanced_settings.html.haml
index b49f1aa061a..a818f8a5c26 100644
--- a/app/views/clusters/clusters/_advanced_settings.html.haml
+++ b/app/views/clusters/clusters/_advanced_settings.html.haml
@@ -24,6 +24,7 @@
order_by: 'last_activity_at',
group_id: group_id,
user_id: user_id,
+ with_shared: true.to_s,
include_subgroups: true.to_s,
membership: true.to_s,
selected: @cluster.management_project_id } }
diff --git a/db/docs/resource_link_events.yml b/db/docs/resource_link_events.yml
new file mode 100644
index 00000000000..cfa04aa522c
--- /dev/null
+++ b/db/docs/resource_link_events.yml
@@ -0,0 +1,10 @@
+---
+table_name: resource_link_events
+classes:
+- WorkItems::ResourceLinkEvent
+feature_categories:
+- planning_analytics
+description: Records the change of parent link on work items along with timestamps
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/114394
+milestone: '15.11'
+gitlab_schema: gitlab_main
diff --git a/db/migrate/20230313031351_create_resource_link_events.rb b/db/migrate/20230313031351_create_resource_link_events.rb
new file mode 100644
index 00000000000..03f00c9416b
--- /dev/null
+++ b/db/migrate/20230313031351_create_resource_link_events.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class CreateResourceLinkEvents < Gitlab::Database::Migration[2.1]
+ def change
+ create_table :resource_link_events do |t|
+ t.integer :action, limit: 2, null: false
+ t.bigint :user_id, null: false
+ t.references :issue, index: true, null: false, foreign_key: { on_delete: :cascade }
+ t.references :child_work_item, index: true, null: false, foreign_key: { to_table: :issues, on_delete: :cascade }
+ t.datetime_with_timezone :created_at, null: false
+
+ t.index :user_id
+ end
+ end
+end
diff --git a/db/migrate/20230329085754_add_foreign_key_to_resource_link_events_on_user.rb b/db/migrate/20230329085754_add_foreign_key_to_resource_link_events_on_user.rb
new file mode 100644
index 00000000000..6a167f232ae
--- /dev/null
+++ b/db/migrate/20230329085754_add_foreign_key_to_resource_link_events_on_user.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddForeignKeyToResourceLinkEventsOnUser < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :resource_link_events, :users, column: :user_id, on_delete: :nullify, validate: true
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists :resource_link_events, column: :user_id
+ end
+ end
+end
diff --git a/db/migrate/20230403164454_add_fork_storage_size_columns_to_root_storage_statistics.rb b/db/migrate/20230403164454_add_fork_storage_size_columns_to_root_storage_statistics.rb
new file mode 100644
index 00000000000..3428fba669f
--- /dev/null
+++ b/db/migrate/20230403164454_add_fork_storage_size_columns_to_root_storage_statistics.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddForkStorageSizeColumnsToRootStorageStatistics < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def change
+ add_column :namespace_root_storage_statistics, :public_forks_storage_size, :bigint, default: 0, null: false
+ add_column :namespace_root_storage_statistics, :internal_forks_storage_size, :bigint, default: 0, null: false
+ add_column :namespace_root_storage_statistics, :private_forks_storage_size, :bigint, default: 0, null: false
+ end
+end
diff --git a/db/schema_migrations/20230313031351 b/db/schema_migrations/20230313031351
new file mode 100644
index 00000000000..37a57006cae
--- /dev/null
+++ b/db/schema_migrations/20230313031351
@@ -0,0 +1 @@
+44dc97ac36a6edcd0c0dba76f6b60204b72c005da7bd793af4ac7832d949bd0b \ No newline at end of file
diff --git a/db/schema_migrations/20230329085754 b/db/schema_migrations/20230329085754
new file mode 100644
index 00000000000..fd2687b225f
--- /dev/null
+++ b/db/schema_migrations/20230329085754
@@ -0,0 +1 @@
+52c5c662dc46313dece9ed9228af5ea2734f0fc4872ba0f6a762e77437b9564e \ No newline at end of file
diff --git a/db/schema_migrations/20230403164454 b/db/schema_migrations/20230403164454
new file mode 100644
index 00000000000..0283a3b1612
--- /dev/null
+++ b/db/schema_migrations/20230403164454
@@ -0,0 +1 @@
+ff04f9ef9bb479b85223e361b96c921e25b436a86a0041627b595c3635848a5b \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 0266b37c320..06e4541147b 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -18623,7 +18623,10 @@ CREATE TABLE namespace_root_storage_statistics (
dependency_proxy_size bigint DEFAULT 0 NOT NULL,
notification_level smallint DEFAULT 100 NOT NULL,
container_registry_size bigint DEFAULT 0 NOT NULL,
- registry_size_estimated boolean DEFAULT false NOT NULL
+ registry_size_estimated boolean DEFAULT false NOT NULL,
+ public_forks_storage_size bigint DEFAULT 0 NOT NULL,
+ internal_forks_storage_size bigint DEFAULT 0 NOT NULL,
+ private_forks_storage_size bigint DEFAULT 0 NOT NULL
);
CREATE TABLE namespace_settings (
@@ -21740,6 +21743,24 @@ CREATE SEQUENCE resource_label_events_id_seq
ALTER SEQUENCE resource_label_events_id_seq OWNED BY resource_label_events.id;
+CREATE TABLE resource_link_events (
+ id bigint NOT NULL,
+ action smallint NOT NULL,
+ user_id bigint NOT NULL,
+ issue_id bigint NOT NULL,
+ child_work_item_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL
+);
+
+CREATE SEQUENCE resource_link_events_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE resource_link_events_id_seq OWNED BY resource_link_events.id;
+
CREATE TABLE resource_milestone_events (
id bigint NOT NULL,
user_id bigint,
@@ -25341,6 +25362,8 @@ ALTER TABLE ONLY resource_iteration_events ALTER COLUMN id SET DEFAULT nextval('
ALTER TABLE ONLY resource_label_events ALTER COLUMN id SET DEFAULT nextval('resource_label_events_id_seq'::regclass);
+ALTER TABLE ONLY resource_link_events ALTER COLUMN id SET DEFAULT nextval('resource_link_events_id_seq'::regclass);
+
ALTER TABLE ONLY resource_milestone_events ALTER COLUMN id SET DEFAULT nextval('resource_milestone_events_id_seq'::regclass);
ALTER TABLE ONLY resource_state_events ALTER COLUMN id SET DEFAULT nextval('resource_state_events_id_seq'::regclass);
@@ -27703,6 +27726,9 @@ ALTER TABLE ONLY resource_iteration_events
ALTER TABLE ONLY resource_label_events
ADD CONSTRAINT resource_label_events_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY resource_link_events
+ ADD CONSTRAINT resource_link_events_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY resource_milestone_events
ADD CONSTRAINT resource_milestone_events_pkey PRIMARY KEY (id);
@@ -31928,6 +31954,12 @@ CREATE INDEX index_resource_label_events_on_merge_request_id_label_id_action ON
CREATE INDEX index_resource_label_events_on_user_id ON resource_label_events USING btree (user_id);
+CREATE INDEX index_resource_link_events_on_child_work_item_id ON resource_link_events USING btree (child_work_item_id);
+
+CREATE INDEX index_resource_link_events_on_issue_id ON resource_link_events USING btree (issue_id);
+
+CREATE INDEX index_resource_link_events_on_user_id ON resource_link_events USING btree (user_id);
+
CREATE INDEX index_resource_milestone_events_created_at ON resource_milestone_events USING btree (created_at);
CREATE INDEX index_resource_milestone_events_on_issue_id ON resource_milestone_events USING btree (issue_id);
@@ -34950,6 +34982,9 @@ ALTER TABLE ONLY namespace_bans
ALTER TABLE ONLY gitlab_subscriptions
ADD CONSTRAINT fk_bd0c4019c3 FOREIGN KEY (hosted_plan_id) REFERENCES plans(id) ON DELETE CASCADE;
+ALTER TABLE ONLY resource_link_events
+ ADD CONSTRAINT fk_bd4ae15ce4 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
+
ALTER TABLE ONLY metrics_users_starred_dashboards
ADD CONSTRAINT fk_bd6ae32fac FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
@@ -35310,6 +35345,9 @@ ALTER TABLE ONLY audit_events_external_audit_event_destinations
ALTER TABLE ONLY operations_user_lists
ADD CONSTRAINT fk_rails_0c716e079b FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY resource_link_events
+ ADD CONSTRAINT fk_rails_0cea73eba5 FOREIGN KEY (child_work_item_id) REFERENCES issues(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY geo_node_statuses
ADD CONSTRAINT fk_rails_0ecc699c2a FOREIGN KEY (geo_node_id) REFERENCES geo_nodes(id) ON DELETE CASCADE;
@@ -36696,6 +36734,9 @@ ALTER TABLE ONLY merge_request_reviewers
ALTER TABLE ONLY ci_running_builds
ADD CONSTRAINT fk_rails_da45cfa165_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
+ALTER TABLE ONLY resource_link_events
+ ADD CONSTRAINT fk_rails_da5dd8a56f FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY jira_imports
ADD CONSTRAINT fk_rails_da617096ce FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
diff --git a/lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb b/lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb
index f6cec311225..afca2368126 100644
--- a/lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb
+++ b/lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb
@@ -8,6 +8,8 @@ module Gitlab
SQL_STATEMENT_SEPARATOR = ";\n\n"
+ PARTITIONING_CONSTRAINT_NAME = 'partitioning_constraint'
+
attr_reader :partitioning_column, :table_name, :parent_table_name, :zero_partition_value
def initialize(
@@ -23,10 +25,10 @@ module Gitlab
@lock_tables = Array.wrap(lock_tables)
end
- def prepare_for_partitioning
+ def prepare_for_partitioning(async: false)
assert_existing_constraints_partitionable
- add_partitioning_check_constraint
+ add_partitioning_check_constraint(async: async)
end
def revert_preparation_for_partitioning
@@ -121,16 +123,17 @@ module Gitlab
constraints_on_column = Gitlab::Database::PostgresConstraint
.by_table_identifier(table_identifier)
.check_constraints
- .valid
.including_column(partitioning_column)
- constraints_on_column.to_a.find do |constraint|
- constraint.definition == "CHECK ((#{partitioning_column} = #{zero_partition_value}))"
+ check_body = "CHECK ((#{partitioning_column} = #{zero_partition_value}))"
+
+ constraints_on_column.find do |constraint|
+ constraint.definition.start_with?(check_body)
end
end
def assert_partitioning_constraint_present
- return if partitioning_constraint
+ return if partitioning_constraint&.constraint_valid?
raise UnableToPartition, <<~MSG
Table #{table_name} is not ready for partitioning.
@@ -138,14 +141,43 @@ module Gitlab
MSG
end
- def add_partitioning_check_constraint
- return if partitioning_constraint.present?
+ def add_partitioning_check_constraint(async: false)
+ return validate_partitioning_constraint_synchronously if partitioning_constraint.present?
check_body = "#{partitioning_column} = #{connection.quote(zero_partition_value)}"
# Any constraint name would work. The constraint is found based on its definition before partitioning
- migration_context.add_check_constraint(table_name, check_body, 'partitioning_constraint')
+ migration_context.add_check_constraint(
+ table_name, check_body, PARTITIONING_CONSTRAINT_NAME,
+ validate: !async
+ )
+
+ if async
+ migration_context.prepare_async_check_constraint_validation(
+ table_name, name: PARTITIONING_CONSTRAINT_NAME
+ )
+ end
+
+ return if partitioning_constraint.present?
+
+ raise UnableToPartition, <<~MSG
+ Error adding partitioning constraint `#{PARTITIONING_CONSTRAINT_NAME}` for `#{table_name}`
+ MSG
+ end
+
+ def validate_partitioning_constraint_synchronously
+ if partitioning_constraint.constraint_valid?
+ return Gitlab::AppLogger.info <<~MSG
+ Nothing to do, the partitioning constraint exists and is valid for `#{table_name}`
+ MSG
+ end
+
+ # Async validations are executed only on .com, we need to validate synchronously for self-managed
+ migration_context.validate_check_constraint(table_name, partitioning_constraint.name)
+ return if partitioning_constraint.constraint_valid?
- raise UnableToPartition, 'Error adding partitioning constraint' unless partitioning_constraint.present?
+ raise UnableToPartition, <<~MSG
+ Error validating partitioning constraint `#{partitioning_constraint.name}` for `#{table_name}`
+ MSG
end
def create_parent_table
diff --git a/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb b/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb
index 3477f4846b6..5a942577006 100644
--- a/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb
+++ b/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb
@@ -252,7 +252,7 @@ module Gitlab
create_sync_trigger(source_table_name, trigger_name, function_name)
end
- def prepare_constraint_for_list_partitioning(table_name:, partitioning_column:, parent_table_name:, initial_partitioning_value:)
+ def prepare_constraint_for_list_partitioning(table_name:, partitioning_column:, parent_table_name:, initial_partitioning_value:, async: false)
validate_not_in_transaction!(:prepare_constraint_for_list_partitioning)
Gitlab::Database::Partitioning::ConvertTableToFirstListPartition
@@ -261,7 +261,7 @@ module Gitlab
parent_table_name: parent_table_name,
partitioning_column: partitioning_column,
zero_partition_value: initial_partitioning_value
- ).prepare_for_partitioning
+ ).prepare_for_partitioning(async: async)
end
def revert_preparing_constraint_for_list_partitioning(table_name:, partitioning_column:, parent_table_name:, initial_partitioning_value:)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 114fc340132..56e0c0cd5f2 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -42294,6 +42294,9 @@ msgstr ""
msgid "Stop Terminal"
msgstr ""
+msgid "Stop impersonating"
+msgstr ""
+
msgid "Stop impersonation"
msgstr ""
diff --git a/package.json b/package.json
index 673c49fb344..37765ae6204 100644
--- a/package.json
+++ b/package.json
@@ -60,6 +60,7 @@
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20230323132525",
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
+ "@popperjs/core": "^2.11.2",
"@rails/actioncable": "6.1.4-7",
"@rails/ujs": "6.1.4-7",
"@sourcegraph/code-host-integration": "0.0.84",
diff --git a/qa/qa/page/group/menu.rb b/qa/qa/page/group/menu.rb
index c0af4af464b..46228926311 100644
--- a/qa/qa/page/group/menu.rb
+++ b/qa/qa/page/group/menu.rb
@@ -5,7 +5,13 @@ module QA
module Group
class Menu < Page::Base
include SubMenus::Common
- include SubMenus::SuperSidebar::Settings if Runtime::Env.super_sidebar_enabled?
+
+ if Runtime::Env.super_sidebar_enabled?
+ prepend Page::SubMenus::SuperSidebar::Manage
+ prepend Page::SubMenus::SuperSidebar::Plan
+ prepend SubMenus::SuperSidebar::Settings
+ prepend SubMenus::SuperSidebar::Build
+ end
def click_group_members_item
hover_group_information do
@@ -16,6 +22,8 @@ module QA
end
def click_subgroup_members_item
+ return go_to_members if Runtime::Env.super_sidebar_enabled?
+
hover_subgroup_information do
within_submenu do
click_element(:sidebar_menu_item_link, menu_item: 'Members')
diff --git a/qa/qa/page/group/sub_menus/super_sidebar/build.rb b/qa/qa/page/group/sub_menus/super_sidebar/build.rb
new file mode 100644
index 00000000000..704548c1dd0
--- /dev/null
+++ b/qa/qa/page/group/sub_menus/super_sidebar/build.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Group
+ module SubMenus
+ module SuperSidebar
+ module Build
+ extend QA::Page::PageConcern
+
+ def go_to_runners
+ open_build_submenu("Runners")
+ end
+
+ private
+
+ def open_build_submenu(sub_menu)
+ open_submenu("Build", "#build", sub_menu)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/group/sub_menus/super_sidebar/common.rb b/qa/qa/page/group/sub_menus/super_sidebar/common.rb
deleted file mode 100644
index dc1975c0044..00000000000
--- a/qa/qa/page/group/sub_menus/super_sidebar/common.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Group
- module SubMenus
- module SuperSidebar
- module Common
- private
-
- def open_submenu(parent_menu_name, parent_section_id, sub_menu)
- click_element(:sidebar_menu_link, menu_item: parent_menu_name)
-
- # TODO: it's not possible to add qa-selectors to sub-menu containers at the moment
- within(parent_section_id) do
- click_element(:sidebar_menu_link, menu_item: sub_menu)
- end
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/group/sub_menus/super_sidebar/settings.rb b/qa/qa/page/group/sub_menus/super_sidebar/settings.rb
index b0501454413..4478b4b93b3 100644
--- a/qa/qa/page/group/sub_menus/super_sidebar/settings.rb
+++ b/qa/qa/page/group/sub_menus/super_sidebar/settings.rb
@@ -8,14 +8,6 @@ module QA
module Settings
extend QA::Page::PageConcern
- def self.included(base)
- super
-
- base.class_eval do
- include Common
- end
- end
-
def go_to_general_settings
open_settings_submenu("General")
end
diff --git a/qa/qa/page/project/menu.rb b/qa/qa/page/project/menu.rb
index 0b2ea479f17..08bd20d5468 100644
--- a/qa/qa/page/project/menu.rb
+++ b/qa/qa/page/project/menu.rb
@@ -16,16 +16,14 @@ module QA
include SubMenus::Packages
if Runtime::Env.super_sidebar_enabled?
+ include Page::SubMenus::SuperSidebar::Manage
include SubMenus::SuperSidebar::Project
- include SubMenus::SuperSidebar::Manage
include SubMenus::SuperSidebar::Plan
include SubMenus::SuperSidebar::Settings
include SubMenus::SuperSidebar::Code
include SubMenus::SuperSidebar::Build
- include SubMenus::SuperSidebar::Secure
include SubMenus::SuperSidebar::Operate
include SubMenus::SuperSidebar::Monitor
- include SubMenus::SuperSidebar::Analyze
end
def click_merge_requests
diff --git a/qa/qa/page/project/sub_menus/issues.rb b/qa/qa/page/project/sub_menus/issues.rb
index 7fa19063653..48840c29635 100644
--- a/qa/qa/page/project/sub_menus/issues.rb
+++ b/qa/qa/page/project/sub_menus/issues.rb
@@ -27,7 +27,7 @@ module QA
end
end
- def go_to_boards
+ def go_to_issue_boards
hover_issues do
within_submenu do
click_element(:sidebar_menu_item_link, menu_item: 'Boards')
diff --git a/qa/qa/page/project/sub_menus/super_sidebar/analyze.rb b/qa/qa/page/project/sub_menus/super_sidebar/analyze.rb
deleted file mode 100644
index 0a0735ee3eb..00000000000
--- a/qa/qa/page/project/sub_menus/super_sidebar/analyze.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Project
- module SubMenus
- module SuperSidebar
- module Analyze
- extend QA::Page::PageConcern
-
- def self.included(base)
- super
-
- base.class_eval do
- include QA::Page::Project::SubMenus::SuperSidebar::Common
- end
- end
-
- def go_to_value_stream_analytics
- open_analyze_submenu('Value stream analytics')
- end
-
- def go_to_contributor_statistics
- open_analyze_submenu('Contributor statistics')
- end
-
- def go_to_ci_cd_analytics
- open_analyze_submenu('CI/CD analytics')
- end
-
- def go_to_repository_analytics
- open_analyze_submenu('Repository analytics')
- end
-
- private
-
- def open_analyze_submenu(sub_menu)
- open_submenu('Analyze', '#analyze', sub_menu)
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/project/sub_menus/super_sidebar/build.rb b/qa/qa/page/project/sub_menus/super_sidebar/build.rb
index 117aade7b1d..f5052051d6a 100644
--- a/qa/qa/page/project/sub_menus/super_sidebar/build.rb
+++ b/qa/qa/page/project/sub_menus/super_sidebar/build.rb
@@ -8,14 +8,6 @@ module QA
module Build
extend QA::Page::PageConcern
- def self.included(base)
- super
-
- base.class_eval do
- include QA::Page::Project::SubMenus::SuperSidebar::Common
- end
- end
-
def go_to_pipelines
open_build_submenu('Pipelines')
end
diff --git a/qa/qa/page/project/sub_menus/super_sidebar/code.rb b/qa/qa/page/project/sub_menus/super_sidebar/code.rb
index 77b87da545e..8263ada5c23 100644
--- a/qa/qa/page/project/sub_menus/super_sidebar/code.rb
+++ b/qa/qa/page/project/sub_menus/super_sidebar/code.rb
@@ -8,14 +8,6 @@ module QA
module Code
extend QA::Page::PageConcern
- def self.included(base)
- super
-
- base.class_eval do
- include QA::Page::Project::SubMenus::SuperSidebar::Common
- end
- end
-
def go_to_repository
open_code_submenu('Repository')
end
diff --git a/qa/qa/page/project/sub_menus/super_sidebar/common.rb b/qa/qa/page/project/sub_menus/super_sidebar/common.rb
deleted file mode 100644
index 7cb14f4189e..00000000000
--- a/qa/qa/page/project/sub_menus/super_sidebar/common.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Project
- module SubMenus
- module SuperSidebar
- module Common
- private
-
- def open_submenu(parent_menu_name, parent_section_id, sub_menu)
- click_element(:sidebar_menu_link, menu_item: parent_menu_name)
-
- # TODO: it's not possible to add qa-selectors to sub-menu container
- within(parent_section_id) do
- click_element(:sidebar_menu_link, menu_item: sub_menu)
- end
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/project/sub_menus/super_sidebar/manage.rb b/qa/qa/page/project/sub_menus/super_sidebar/manage.rb
deleted file mode 100644
index 40bbd3b6618..00000000000
--- a/qa/qa/page/project/sub_menus/super_sidebar/manage.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Project
- module SubMenus
- module SuperSidebar
- module Manage
- extend QA::Page::PageConcern
-
- def self.included(base)
- super
-
- base.class_eval do
- include QA::Page::Project::SubMenus::SuperSidebar::Common
- end
- end
-
- def go_to_activity
- open_manage_submenu('Activity')
- end
-
- def go_to_members
- open_manage_submenu('Members')
- end
-
- def go_to_labels
- open_manage_submenu('Labels')
- end
-
- def go_to_milestones
- open_manage_submenu('Milestones')
- end
-
- private
-
- def open_manage_submenu(sub_menu)
- open_submenu('Manage', '#manage', sub_menu)
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/project/sub_menus/super_sidebar/monitor.rb b/qa/qa/page/project/sub_menus/super_sidebar/monitor.rb
index 2a578ba2e80..391cde6887b 100644
--- a/qa/qa/page/project/sub_menus/super_sidebar/monitor.rb
+++ b/qa/qa/page/project/sub_menus/super_sidebar/monitor.rb
@@ -8,14 +8,6 @@ module QA
module Monitor
extend QA::Page::PageConcern
- def self.included(base)
- super
-
- base.class_eval do
- include QA::Page::Project::SubMenus::SuperSidebar::Common
- end
- end
-
def go_to_metrics
open_monitor_submenu('Metrics')
end
diff --git a/qa/qa/page/project/sub_menus/super_sidebar/operate.rb b/qa/qa/page/project/sub_menus/super_sidebar/operate.rb
index f2a131d915d..5e4306c77d0 100644
--- a/qa/qa/page/project/sub_menus/super_sidebar/operate.rb
+++ b/qa/qa/page/project/sub_menus/super_sidebar/operate.rb
@@ -8,14 +8,6 @@ module QA
module Operate
extend QA::Page::PageConcern
- def self.included(base)
- super
-
- base.class_eval do
- include QA::Page::Project::SubMenus::SuperSidebar::Common
- end
- end
-
def go_to_package_registry
open_operate_submenu('Package Registry')
end
diff --git a/qa/qa/page/project/sub_menus/super_sidebar/plan.rb b/qa/qa/page/project/sub_menus/super_sidebar/plan.rb
index 4ed9faeffc0..ca81837b2e2 100644
--- a/qa/qa/page/project/sub_menus/super_sidebar/plan.rb
+++ b/qa/qa/page/project/sub_menus/super_sidebar/plan.rb
@@ -6,32 +6,16 @@ module QA
module SubMenus
module SuperSidebar
module Plan
- extend QA::Page::PageConcern
-
def self.included(base)
super
base.class_eval do
- include QA::Page::Project::SubMenus::SuperSidebar::Common
+ include QA::Page::SubMenus::SuperSidebar::Plan
end
end
- def go_to_boards
- open_plan_submenu("Issue boards")
- end
-
- def go_to_service_desk
- open_plan_submenu("Service Desk")
- end
-
- def go_to_wiki
- open_plan_submenu("Wiki")
- end
-
- private
-
- def open_plan_submenu(sub_menu)
- open_submenu("Plan", "#plan", sub_menu)
+ def go_to_requirements
+ open_plan_submenu("Requirements")
end
end
end
diff --git a/qa/qa/page/project/sub_menus/super_sidebar/project.rb b/qa/qa/page/project/sub_menus/super_sidebar/project.rb
index ef3b8cc9596..0d2df959548 100644
--- a/qa/qa/page/project/sub_menus/super_sidebar/project.rb
+++ b/qa/qa/page/project/sub_menus/super_sidebar/project.rb
@@ -8,14 +8,6 @@ module QA
module Project
extend QA::Page::PageConcern
- def self.included(base)
- super
-
- base.class_eval do
- include QA::Page::Project::SubMenus::Common
- end
- end
-
def click_project
within_sidebar do
click_element(:sidebar_menu_link, menu_item: 'Project overview')
diff --git a/qa/qa/page/project/sub_menus/super_sidebar/secure.rb b/qa/qa/page/project/sub_menus/super_sidebar/secure.rb
index 4fea95a5456..1b9e35d3d37 100644
--- a/qa/qa/page/project/sub_menus/super_sidebar/secure.rb
+++ b/qa/qa/page/project/sub_menus/super_sidebar/secure.rb
@@ -8,18 +8,6 @@ module QA
module Secure
extend QA::Page::PageConcern
- def self.included(base)
- super
-
- base.class_eval do
- include QA::Page::Project::SubMenus::SuperSidebar::Common
- end
- end
-
- def go_to_audit_events
- open_secure_submenu('Audit events')
- end
-
def go_to_security_configuration
open_secure_submenu('Security configuration')
end
diff --git a/qa/qa/page/project/sub_menus/super_sidebar/settings.rb b/qa/qa/page/project/sub_menus/super_sidebar/settings.rb
index eead4f9b147..f2833239966 100644
--- a/qa/qa/page/project/sub_menus/super_sidebar/settings.rb
+++ b/qa/qa/page/project/sub_menus/super_sidebar/settings.rb
@@ -8,14 +8,6 @@ module QA
module Settings
extend QA::Page::PageConcern
- def self.included(base)
- super
-
- base.class_eval do
- include QA::Page::Project::SubMenus::SuperSidebar::Common
- end
- end
-
def go_to_general_settings
open_settings_submenu('General')
end
diff --git a/qa/qa/page/sub_menus/common.rb b/qa/qa/page/sub_menus/common.rb
index 518b3b4e84e..2e3e53ac793 100644
--- a/qa/qa/page/sub_menus/common.rb
+++ b/qa/qa/page/sub_menus/common.rb
@@ -15,38 +15,41 @@ module QA
end
end
- def within_sidebar
+ def within_sidebar(&block)
wait_for_requests
- within_element(sidebar_element) do
- yield
- end
+ within_element(sidebar_element, &block)
end
- def within_submenu(element = nil)
+ def within_submenu(element = nil, &block)
if element
- within_element(element) do
- yield
- end
+ within_element(element, &block)
else
- within_submenu_without_element do
- yield
- end
+ within_submenu_without_element(&block)
end
end
private
- def within_submenu_without_element
- if has_css?('.fly-out-list')
- within('.fly-out-list') do
- yield
- end
- else
- yield
+ # Implementation for super-sidebar, will replace within_submenu
+ #
+ # @param [String] parent_menu_name
+ # @param [String] parent_section_id
+ # @param [String] sub_menu
+ # @return [void]
+ def open_submenu(parent_menu_name, parent_section_id, sub_menu)
+ click_element(:sidebar_menu_link, menu_item: parent_menu_name)
+
+ # TODO: it's not possible to add qa-selectors to sub-menu container
+ within(parent_section_id) do
+ click_element(:sidebar_menu_link, menu_item: sub_menu)
end
end
+ def within_submenu_without_element(&block)
+ has_css?('.fly-out-list') ? within('.fly-out-list', &block) : yield
+ end
+
def sidebar_element
raise NotImplementedError
end
diff --git a/qa/qa/page/sub_menus/super_sidebar/manage.rb b/qa/qa/page/sub_menus/super_sidebar/manage.rb
new file mode 100644
index 00000000000..ddab1b373cf
--- /dev/null
+++ b/qa/qa/page/sub_menus/super_sidebar/manage.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module SubMenus
+ module SuperSidebar
+ module Manage
+ extend QA::Page::PageConcern
+
+ def self.included(base)
+ super
+
+ base.class_eval do
+ include QA::Page::SubMenus::Common
+ end
+ end
+
+ def go_to_activity
+ open_manage_submenu('Activity')
+ end
+
+ def go_to_members
+ open_manage_submenu('Members')
+ end
+
+ def go_to_labels
+ open_manage_submenu('Labels')
+ end
+
+ def go_to_milestones
+ open_manage_submenu('Milestones')
+ end
+
+ private
+
+ def open_manage_submenu(sub_menu)
+ open_submenu('Manage', '#manage', sub_menu)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/sub_menus/super_sidebar/plan.rb b/qa/qa/page/sub_menus/super_sidebar/plan.rb
new file mode 100644
index 00000000000..7e966450e19
--- /dev/null
+++ b/qa/qa/page/sub_menus/super_sidebar/plan.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module SubMenus
+ module SuperSidebar
+ module Plan
+ extend QA::Page::PageConcern
+
+ def self.included(base)
+ super
+
+ base.class_eval do
+ include QA::Page::SubMenus::Common
+ end
+ end
+
+ def go_to_issue_boards
+ open_plan_submenu("Issue boards")
+ end
+
+ def go_to_service_desk
+ open_plan_submenu("Service Desk")
+ end
+
+ def go_to_wiki
+ open_plan_submenu("Wiki")
+ end
+
+ private
+
+ def open_plan_submenu(sub_menu)
+ open_submenu("Plan", "#plan", sub_menu)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb
index 83e178ae4c3..7377b0ff8af 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb
@@ -16,7 +16,7 @@ module QA
it 'focuses on issue board', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347996' do
project.visit!
- Page::Project::Menu.perform(&:go_to_boards)
+ Page::Project::Menu.perform(&:go_to_issue_boards)
Page::Component::IssueBoard::Show.perform do |show|
show.click_focus_mode_button
diff --git a/spec/factories/work_items/resource_link_events.rb b/spec/factories/work_items/resource_link_events.rb
new file mode 100644
index 00000000000..696f6dcc43f
--- /dev/null
+++ b/spec/factories/work_items/resource_link_events.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :resource_link_event, class: 'WorkItems::ResourceLinkEvent' do
+ action { :add }
+ issue { association(:issue) }
+ user { issue&.author || association(:user) }
+ child_work_item { association(:work_item, :task) }
+ end
+end
diff --git a/spec/frontend/super_sidebar/components/user_bar_spec.js b/spec/frontend/super_sidebar/components/user_bar_spec.js
index e078d5a943c..f62104f4622 100644
--- a/spec/frontend/super_sidebar/components/user_bar_spec.js
+++ b/spec/frontend/super_sidebar/components/user_bar_spec.js
@@ -25,6 +25,7 @@ describe('UserBar component', () => {
const findBrandLogo = () => wrapper.findByTestId('brand-header-custom-logo');
const findSearchButton = () => wrapper.findByTestId('super-sidebar-search-button');
const findSearchModal = () => wrapper.findComponent(SearchModal);
+ const findStopImpersonationButton = () => wrapper.findByTestId('stop-impersonation-btn');
Vue.use(Vuex);
@@ -33,7 +34,7 @@ describe('UserBar component', () => {
searchOptions: () => MOCK_DEFAULT_SEARCH_OPTIONS,
},
});
- const createWrapper = (extraSidebarData = {}) => {
+ const createWrapper = ({ extraSidebarData = {}, provideOverrides = {} } = {}) => {
wrapper = shallowMountExtended(UserBar, {
propsData: {
sidebarData: { ...sidebarData, ...extraSidebarData },
@@ -41,6 +42,8 @@ describe('UserBar component', () => {
provide: {
rootPath: '/',
toggleNewNavEndpoint: '/-/profile/preferences',
+ isImpersonating: false,
+ ...provideOverrides,
},
directives: {
GlTooltip: createMockDirective('gl-tooltip'),
@@ -95,12 +98,16 @@ describe('UserBar component', () => {
expect(findBrandLogo().exists()).toBe(true);
expect(findBrandLogo().attributes('src')).toBe(sidebarData.logo_url);
});
+
+ it('does not render the "Stop impersonating" button', () => {
+ expect(findStopImpersonationButton().exists()).toBe(false);
+ });
});
describe('GitLab Next badge', () => {
describe('when on canary', () => {
it('should render a badge to switch off GitLab Next', () => {
- createWrapper({ gitlab_com_and_canary: true });
+ createWrapper({ extraSidebarData: { gitlab_com_and_canary: true } });
const badge = wrapper.findComponent(GlBadge);
expect(badge.text()).toBe('Next');
expect(badge.attributes('href')).toBe(sidebarData.canary_toggle_com_url);
@@ -109,7 +116,7 @@ describe('UserBar component', () => {
describe('when not on canary', () => {
it('should not render the GitLab Next badge', () => {
- createWrapper({ gitlab_com_and_canary: false });
+ createWrapper({ extraSidebarData: { gitlab_com_and_canary: false } });
const badge = wrapper.findComponent(GlBadge);
expect(badge.exists()).toBe(false);
});
@@ -135,4 +142,29 @@ describe('UserBar component', () => {
expect(findSearchModal().exists()).toBe(true);
});
});
+
+ describe('While impersonating a user', () => {
+ beforeEach(() => {
+ createWrapper({ provideOverrides: { isImpersonating: true } });
+ });
+
+ it('renders the "Stop impersonating" button', () => {
+ expect(findStopImpersonationButton().exists()).toBe(true);
+ });
+
+ it('sets the correct label on the button', () => {
+ const btn = findStopImpersonationButton();
+ const label = __('Stop impersonating');
+
+ expect(btn.attributes('title')).toBe(label);
+ expect(btn.attributes('aria-label')).toBe(label);
+ });
+
+ it('sets the href and data-method attributes', () => {
+ const btn = findStopImpersonationButton();
+
+ expect(btn.attributes('href')).toBe(sidebarData.stop_impersonation_path);
+ expect(btn.attributes('data-method')).toBe('delete');
+ });
+ });
});
diff --git a/spec/frontend/super_sidebar/mock_data.js b/spec/frontend/super_sidebar/mock_data.js
index fdf88e910e3..1d8f595a400 100644
--- a/spec/frontend/super_sidebar/mock_data.js
+++ b/spec/frontend/super_sidebar/mock_data.js
@@ -103,6 +103,7 @@ export const sidebarData = {
pinned_items: [],
panel_type: 'your_work',
update_pins_url: 'path/to/pins',
+ stop_impersonation_path: '/admin/impersonation',
};
export const userMenuMockStatus = {
diff --git a/spec/frontend/vue_shared/components/entity_select/project_select_spec.js b/spec/frontend/vue_shared/components/entity_select/project_select_spec.js
index 32ce2155494..0a174c98efb 100644
--- a/spec/frontend/vue_shared/components/entity_select/project_select_spec.js
+++ b/spec/frontend/vue_shared/components/entity_select/project_select_spec.js
@@ -97,6 +97,7 @@ describe('ProjectSelect', () => {
${'defaultToggleText'} | ${PROJECT_TOGGLE_TEXT}
${'headerText'} | ${PROJECT_HEADER_TEXT}
${'clearable'} | ${true}
+ ${'block'} | ${false}
`('passes the $prop prop to entity-select', ({ prop, expectedValue }) => {
expect(findEntitySelect().props(prop)).toBe(expectedValue);
});
@@ -136,6 +137,18 @@ describe('ProjectSelect', () => {
expect(mock.history.get[0].params.include_subgroups).toBe(true);
});
+ it('does not include shared projects if withShared prop is false', async () => {
+ createComponent({
+ props: {
+ withShared: false,
+ },
+ });
+ openListbox();
+ await waitForPromises();
+
+ expect(mock.history.get[0].params.with_shared).toBe(false);
+ });
+
it('fetches projects globally if no group ID is provided', async () => {
createComponent({
props: {
diff --git a/spec/helpers/sidebars_helper_spec.rb b/spec/helpers/sidebars_helper_spec.rb
index 76e87667e25..30357aef601 100644
--- a/spec/helpers/sidebars_helper_spec.rb
+++ b/spec/helpers/sidebars_helper_spec.rb
@@ -331,6 +331,23 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do
end
end
end
+
+ describe 'impersonation data' do
+ it 'sets is_impersonating to `false` when not impersonating' do
+ expect(subject[:is_impersonating]).to be(false)
+ end
+
+ it 'passes the stop_impersonation_path property' do
+ expect(subject[:stop_impersonation_path]).to eq(admin_impersonation_path)
+ end
+
+ describe 'when impersonating' do
+ it 'sets is_impersonating to `true`' do
+ expect(helper).to receive(:session).and_return({ impersonator_id: 1 })
+ expect(subject[:is_impersonating]).to be(true)
+ end
+ end
+ end
end
describe '#super_sidebar_nav_panel' do
diff --git a/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb b/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb
index 42a43392f9f..f4b13033270 100644
--- a/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb
+++ b/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Database::Partitioning::ConvertTableToFirstListPartition do
+RSpec.describe Gitlab::Database::Partitioning::ConvertTableToFirstListPartition, feature_category: :database do
include Gitlab::Database::DynamicModelHelpers
include Database::TableSchemaHelpers
@@ -77,7 +77,9 @@ RSpec.describe Gitlab::Database::Partitioning::ConvertTableToFirstListPartition
end
describe "#prepare_for_partitioning" do
- subject(:prepare) { converter.prepare_for_partitioning }
+ subject(:prepare) { converter.prepare_for_partitioning(async: async) }
+
+ let(:async) { false }
it 'adds a check constraint' do
expect { prepare }.to change {
@@ -87,9 +89,100 @@ RSpec.describe Gitlab::Database::Partitioning::ConvertTableToFirstListPartition
.count
}.from(0).to(1)
end
+
+ context 'when it fails to add constraint' do
+ before do
+ allow(migration_context).to receive(:add_check_constraint)
+ end
+
+ it 'raises UnableToPartition error' do
+ expect { prepare }
+ .to raise_error(described_class::UnableToPartition)
+ .and change {
+ Gitlab::Database::PostgresConstraint
+ .check_constraints
+ .by_table_identifier(table_identifier)
+ .count
+ }.by(0)
+ end
+ end
+
+ context 'when async' do
+ let(:async) { true }
+
+ it 'adds a NOT VALID check constraint' do
+ expect { prepare }.to change {
+ Gitlab::Database::PostgresConstraint
+ .check_constraints
+ .by_table_identifier(table_identifier)
+ .count
+ }.from(0).to(1)
+
+ constraint =
+ Gitlab::Database::PostgresConstraint
+ .check_constraints
+ .by_table_identifier(table_identifier)
+ .last
+
+ expect(constraint.definition).to end_with('NOT VALID')
+ end
+
+ it 'adds a PostgresAsyncConstraintValidation record' do
+ expect { prepare }.to change {
+ Gitlab::Database::AsyncConstraints::PostgresAsyncConstraintValidation.count
+ }.from(0).to(1)
+
+ record = Gitlab::Database::AsyncConstraints::PostgresAsyncConstraintValidation.last
+ expect(record.name).to eq described_class::PARTITIONING_CONSTRAINT_NAME
+ expect(record).to be_check_constraint
+ end
+
+ context 'when constraint exists but is not valid' do
+ before do
+ converter.prepare_for_partitioning(async: true)
+ end
+
+ it 'validates the check constraint' do
+ expect { prepare }.to change {
+ Gitlab::Database::PostgresConstraint
+ .check_constraints
+ .by_table_identifier(table_identifier).first.constraint_valid?
+ }.from(false).to(true)
+ end
+
+ context 'when it fails to validate constraint' do
+ before do
+ allow(migration_context).to receive(:validate_check_constraint)
+ end
+
+ it 'raises UnableToPartition error' do
+ expect { prepare }
+ .to raise_error(described_class::UnableToPartition,
+ starting_with('Error validating partitioning constraint'))
+ .and change {
+ Gitlab::Database::PostgresConstraint
+ .check_constraints
+ .by_table_identifier(table_identifier)
+ .count
+ }.by(0)
+ end
+ end
+ end
+
+ context 'when constraint exists and is valid' do
+ before do
+ converter.prepare_for_partitioning(async: false)
+ end
+
+ it 'raises UnableToPartition error' do
+ expect(Gitlab::AppLogger).to receive(:info).with(starting_with('Nothing to do'))
+ prepare
+ end
+ end
+ end
end
- describe '#revert_prepare_for_partitioning' do
+ describe '#revert_preparation_for_partitioning' do
before do
converter.prepare_for_partitioning
end
@@ -109,8 +202,10 @@ RSpec.describe Gitlab::Database::Partitioning::ConvertTableToFirstListPartition
describe "#partition" do
subject(:partition) { converter.partition }
+ let(:async) { false }
+
before do
- converter.prepare_for_partitioning
+ converter.prepare_for_partitioning(async: async)
end
context 'when the primary key is incorrect' do
@@ -134,7 +229,15 @@ RSpec.describe Gitlab::Database::Partitioning::ConvertTableToFirstListPartition
end
it 'throws a reasonable error message' do
- expect { partition }.to raise_error(described_class::UnableToPartition, /constraint /)
+ expect { partition }.to raise_error(described_class::UnableToPartition, /is not ready for partitioning./)
+ end
+ end
+
+ context 'when supporting check constraint is not valid' do
+ let(:async) { true }
+
+ it 'throws a reasonable error message' do
+ expect { partition }.to raise_error(described_class::UnableToPartition, /is not ready for partitioning./)
end
end
@@ -207,7 +310,7 @@ RSpec.describe Gitlab::Database::Partitioning::ConvertTableToFirstListPartition
proc do
allow(migration_context.connection).to receive(:add_foreign_key).and_call_original
expect(migration_context.connection).to receive(:add_foreign_key).with(from_table, to_table, any_args)
- .and_wrap_original(&fail_first_time)
+ .and_wrap_original(&fail_first_time)
end
end
diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
index e76b1da3834..06c40f18b43 100644
--- a/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers do
+RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers, feature_category: :database do
include Database::PartitioningHelpers
include Database::TriggerHelpers
include Database::TableSchemaHelpers
@@ -98,7 +98,8 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHe
migration.prepare_constraint_for_list_partitioning(table_name: source_table,
partitioning_column: partition_column,
parent_table_name: partitioned_table,
- initial_partitioning_value: min_date)
+ initial_partitioning_value: min_date,
+ async: false)
end
end
end
diff --git a/spec/models/work_items/resource_link_event_spec.rb b/spec/models/work_items/resource_link_event_spec.rb
new file mode 100644
index 00000000000..67ca9e72bbc
--- /dev/null
+++ b/spec/models/work_items/resource_link_event_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::ResourceLinkEvent, type: :model, feature_category: :team_planning do
+ it_behaves_like 'a resource event'
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:work_item) }
+ it { is_expected.to belong_to(:child_work_item) }
+ end
+
+ describe 'validation' do
+ it { is_expected.to validate_presence_of(:child_work_item) }
+ end
+end
diff --git a/spec/support/shared_examples/models/resource_event_shared_examples.rb b/spec/support/shared_examples/models/resource_event_shared_examples.rb
index 038ff33c68a..1409f7caea8 100644
--- a/spec/support/shared_examples/models/resource_event_shared_examples.rb
+++ b/spec/support/shared_examples/models/resource_event_shared_examples.rb
@@ -10,6 +10,8 @@ RSpec.shared_examples 'a resource event' do
let_it_be(:issue2) { create(:issue, author: user1) }
let_it_be(:issue3) { create(:issue, author: user2) }
+ let(:resource_event) { described_class.name.demodulize.underscore.to_sym }
+
describe 'importable' do
it { is_expected.to respond_to(:importing?) }
it { is_expected.to respond_to(:imported?) }
@@ -36,9 +38,9 @@ RSpec.shared_examples 'a resource event' do
let!(:created_at2) { 2.days.ago }
let!(:created_at3) { 3.days.ago }
- let!(:event1) { create(described_class.name.underscore.to_sym, issue: issue1, created_at: created_at1) }
- let!(:event2) { create(described_class.name.underscore.to_sym, issue: issue2, created_at: created_at2) }
- let!(:event3) { create(described_class.name.underscore.to_sym, issue: issue2, created_at: created_at3) }
+ let!(:event1) { create(resource_event, issue: issue1, created_at: created_at1) }
+ let!(:event2) { create(resource_event, issue: issue2, created_at: created_at2) }
+ let!(:event3) { create(resource_event, issue: issue2, created_at: created_at3) }
it 'returns the expected events' do
events = described_class.created_after(created_at3)
@@ -62,9 +64,10 @@ RSpec.shared_examples 'a resource event for issues' do
let_it_be(:issue2) { create(:issue, author: user1) }
let_it_be(:issue3) { create(:issue, author: user2) }
- let_it_be(:event1) { create(described_class.name.underscore.to_sym, issue: issue1) }
- let_it_be(:event2) { create(described_class.name.underscore.to_sym, issue: issue2) }
- let_it_be(:event3) { create(described_class.name.underscore.to_sym, issue: issue1) }
+ let_it_be(:resource_event) { described_class.name.demodulize.underscore.to_sym }
+ let_it_be(:event1) { create(resource_event, issue: issue1) }
+ let_it_be(:event2) { create(resource_event, issue: issue2) }
+ let_it_be(:event3) { create(resource_event, issue: issue1) }
describe 'associations' do
it { is_expected.to belong_to(:issue) }
@@ -93,9 +96,9 @@ RSpec.shared_examples 'a resource event for issues' do
end
describe '.by_created_at_earlier_or_equal_to' do
- let_it_be(:event1) { create(described_class.name.underscore.to_sym, issue: issue1, created_at: '2020-03-10') }
- let_it_be(:event2) { create(described_class.name.underscore.to_sym, issue: issue2, created_at: '2020-03-10') }
- let_it_be(:event3) { create(described_class.name.underscore.to_sym, issue: issue1, created_at: '2020-03-12') }
+ let_it_be(:event1) { create(resource_event, issue: issue1, created_at: '2020-03-10') }
+ let_it_be(:event2) { create(resource_event, issue: issue2, created_at: '2020-03-10') }
+ let_it_be(:event3) { create(resource_event, issue: issue1, created_at: '2020-03-12') }
it 'returns the expected events' do
events = described_class.by_created_at_earlier_or_equal_to('2020-03-11 23:59:59')
@@ -112,7 +115,7 @@ RSpec.shared_examples 'a resource event for issues' do
if described_class.method_defined?(:issuable)
describe '#issuable' do
- let_it_be(:event1) { create(described_class.name.underscore.to_sym, issue: issue2) }
+ let_it_be(:event1) { create(resource_event, issue: issue2) }
it 'returns the expected issuable' do
expect(event1.issuable).to eq(issue2)
@@ -125,6 +128,7 @@ RSpec.shared_examples 'a resource event for merge requests' do
let_it_be(:user1) { create(:user) }
let_it_be(:user2) { create(:user) }
+ let_it_be(:resource_event) { described_class.name.demodulize.underscore.to_sym }
let_it_be(:merge_request1) { create(:merge_request, author: user1) }
let_it_be(:merge_request2) { create(:merge_request, author: user1) }
let_it_be(:merge_request3) { create(:merge_request, author: user2) }
@@ -134,9 +138,9 @@ RSpec.shared_examples 'a resource event for merge requests' do
end
describe '.by_merge_request' do
- let_it_be(:event1) { create(described_class.name.underscore.to_sym, merge_request: merge_request1) }
- let_it_be(:event2) { create(described_class.name.underscore.to_sym, merge_request: merge_request2) }
- let_it_be(:event3) { create(described_class.name.underscore.to_sym, merge_request: merge_request1) }
+ let_it_be(:event1) { create(resource_event, merge_request: merge_request1) }
+ let_it_be(:event2) { create(resource_event, merge_request: merge_request2) }
+ let_it_be(:event3) { create(resource_event, merge_request: merge_request1) }
it 'returns the expected records for an issue with events' do
events = described_class.by_merge_request(merge_request1)
@@ -153,7 +157,7 @@ RSpec.shared_examples 'a resource event for merge requests' do
if described_class.method_defined?(:issuable)
describe '#issuable' do
- let_it_be(:event1) { create(described_class.name.underscore.to_sym, merge_request: merge_request2) }
+ let_it_be(:event1) { create(resource_event, merge_request: merge_request2) }
it 'returns the expected issuable' do
expect(event1.issuable).to eq(merge_request2)
@@ -163,7 +167,7 @@ RSpec.shared_examples 'a resource event for merge requests' do
context 'on callbacks' do
it 'does not trigger note created subscription' do
- event = build(described_class.name.underscore.to_sym, merge_request: merge_request1)
+ event = build(resource_event, merge_request: merge_request1)
expect(GraphqlTriggers).not_to receive(:work_item_note_created)
expect(event).not_to receive(:trigger_note_subscription_create)
@@ -177,15 +181,17 @@ RSpec.shared_examples 'a note for work item resource event' do
let_it_be(:project) { create(:project) }
let_it_be(:work_item) { create(:work_item, :task, project: project, author: user) }
+ let(:resource_event) { described_class.name.demodulize.underscore.to_sym }
+
it 'builds synthetic note with correct synthetic_note_class' do
- event = build(described_class.name.underscore.to_sym, issue: work_item)
+ event = build(resource_event, issue: work_item)
expect(event.work_item_synthetic_system_note.class.name).to eq(event.synthetic_note_class.name)
end
context 'on callbacks' do
it 'triggers note created subscription' do
- event = build(described_class.name.underscore.to_sym, issue: work_item)
+ event = build(resource_event, issue: work_item)
expect(GraphqlTriggers).to receive(:work_item_note_created)
expect(event).to receive(:trigger_note_subscription_create).and_call_original