diff --git a/app/assets/javascripts/observability/mock_traces.json b/app/assets/javascripts/observability/mock_traces.json
new file mode 100644
index 00000000000..7b472081a24
--- /dev/null
+++ b/app/assets/javascripts/observability/mock_traces.json
@@ -0,0 +1,147 @@
+{
+ "data": [
+ {
+ "traceID": "668ec7d464968a87",
+ "date": "Mon, 03 Jul 2023 14:35:37 GMT",
+ "service": "HealthCheck",
+ "operation": "/grpc.health.v1.Health/Check",
+ "duration": 100,
+ "method": "GET",
+ "status": 200,
+ "spans": [
+
+ ],
+ "warnings": null
+ },
+ {
+ "traceID": "668ec7d464968a87",
+ "date": "Mon, 03 Jul 2023 14:35:37 GMT",
+ "service": "HealthCheck",
+ "operation": "/grpc.health.v1.Health/Check",
+ "duration": 343,
+ "method": "GET",
+ "status": 200,
+ "spans": [
+
+ ],
+ "warnings": null
+ },
+ {
+ "traceID": "668ec7d464968a87",
+ "date": "Mon, 03 Jul 2023 14:35:37 GMT",
+ "service": "HealthCheck",
+ "operation": "/grpc.health.v1.Health/Check",
+ "duration": 343,
+ "method": "GET",
+ "status": 200,
+ "spans": [
+
+ ],
+ "warnings": null
+ },
+ {
+ "traceID": "668ec7d464968a87",
+ "date": "Mon, 03 Jul 2023 14:35:37 GMT",
+ "service": "HealthCheck",
+ "operation": "/grpc.health.v1.Health/Check",
+ "duration": 343,
+ "method": "GET",
+ "status": 200,
+ "spans": [
+
+ ],
+ "warnings": null
+ },
+ {
+ "traceID": "668ec7d464968a87",
+ "date": "Mon, 03 Jul 2023 14:35:37 GMT",
+ "service": "HealthCheck",
+ "operation": "/grpc.health.v1.Health/Check",
+ "duration": 343,
+ "method": "GET",
+ "status": 200,
+ "spans": [
+
+ ],
+ "warnings": null
+ },
+ {
+ "traceID": "668ec7d464968a87",
+ "date": "Mon, 03 Jul 2023 14:35:37 GMT",
+ "service": "HealthCheck",
+ "operation": "/grpc.health.v1.Health/Check",
+ "duration": 343,
+ "method": "GET",
+ "status": 200,
+ "spans": [
+
+ ],
+ "warnings": null
+ },
+ {
+ "traceID": "668ec7d464968a87",
+ "date": "Mon, 03 Jul 2023 14:35:37 GMT",
+ "service": "HealthCheck",
+ "operation": "/grpc.health.v1.Health/Check",
+ "duration": 343,
+ "method": "GET",
+ "status": 200,
+ "spans": [
+
+ ],
+ "warnings": null
+ },
+ {
+ "traceID": "668ec7d464968a87",
+ "date": "Mon, 03 Jul 2023 14:35:37 GMT",
+ "service": "HealthCheck",
+ "operation": "/grpc.health.v1.Health/Check",
+ "duration": 343,
+ "method": "GET",
+ "status": 200,
+ "spans": [
+
+ ],
+ "warnings": null
+ },
+ {
+ "traceID": "668ec7d464968a87",
+ "date": "Mon, 03 Jul 2023 14:35:37 GMT",
+ "service": "HealthCheck",
+ "operation": "/grpc.health.v1.Health/Check",
+ "duration": 343,
+ "method": "GET",
+ "status": 200,
+ "spans": [
+
+ ],
+ "warnings": null
+ },
+ {
+ "traceID": "668ec7d464968a87",
+ "date": "Mon, 03 Jul 2023 14:35:37 GMT",
+ "service": "HealthCheck",
+ "operation": "/grpc.health.v1.Health/Check",
+ "duration": 343,
+ "method": "GET",
+ "status": 200,
+ "spans": [
+
+ ],
+ "warnings": null
+ },
+ {
+ "traceID": "668ec7d464968a87",
+ "date": "Mon, 03 Jul 2023 14:35:37 GMT",
+ "service": "HealthCheck",
+ "operation": "/grpc.health.v1.Health/Check",
+ "duration": 343,
+ "method": "GET",
+ "status": 200,
+ "spans": [
+
+ ],
+ "warnings": null
+ }
+ ]
+}
diff --git a/app/assets/javascripts/pages/projects/tracing/index/index.js b/app/assets/javascripts/pages/projects/tracing/index/index.js
new file mode 100644
index 00000000000..64ca303f8ba
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/tracing/index/index.js
@@ -0,0 +1,4 @@
+import { initSimpleApp } from '~/helpers/init_simple_app_helper';
+import ListIndex from '~/tracing/list_index.vue';
+
+initSimpleApp('#js-tracing', ListIndex);
diff --git a/app/assets/javascripts/tracing/components/tracing_list.vue b/app/assets/javascripts/tracing/components/tracing_list.vue
new file mode 100644
index 00000000000..38acda8a1b4
--- /dev/null
+++ b/app/assets/javascripts/tracing/components/tracing_list.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
diff --git a/app/assets/javascripts/tracing/list_index.vue b/app/assets/javascripts/tracing/list_index.vue
new file mode 100644
index 00000000000..432fbb81506
--- /dev/null
+++ b/app/assets/javascripts/tracing/list_index.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 904c1a51471..7b8d9281148 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -549,3 +549,16 @@ li.note {
See https://gitlab.com/gitlab-org/gitlab/issues/36857 for more details.
**/
.gl-line-height-14 { line-height: $gl-line-height-14; }
+
+// TODO: To be removed once `split` option for new dropdowns is implemented.
+// See issue at https://gitlab.com/gitlab-org/gitlab-ui/-/issues/2263
+.gl-new-dropdown.split:nth-child(n+2) {
+ .gl-new-dropdown-toggle {
+ margin-left: 1px;
+
+ &.btn-tertiary,
+ &.disabled {
+ margin-left: -1px;
+ }
+ }
+}
diff --git a/app/controllers/projects/tracing_controller.rb b/app/controllers/projects/tracing_controller.rb
index 71ca03deb8c..d1218ebf344 100644
--- a/app/controllers/projects/tracing_controller.rb
+++ b/app/controllers/projects/tracing_controller.rb
@@ -8,10 +8,7 @@ module Projects
before_action :check_tracing_enabled
- def index
- # TODO frontend changes coming separately https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125014
- render html: helpers.tag.strong('Tracing')
- end
+ def index; end
private
diff --git a/app/helpers/projects/observability_helper.rb b/app/helpers/projects/observability_helper.rb
new file mode 100644
index 00000000000..24bc1928a36
--- /dev/null
+++ b/app/helpers/projects/observability_helper.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Projects
+ module ObservabilityHelper
+ def observability_tracing_view_model(project)
+ Gitlab::Json.generate({
+ tracingUrl: Gitlab::Observability.tracing_url(project),
+ provisioningUrl: Gitlab::Observability.provisioning_url(project),
+ oauthUrl: Gitlab::Observability.oauth_url
+ })
+ end
+ end
+end
diff --git a/app/models/concerns/protected_ref_deploy_key_access.rb b/app/models/concerns/protected_ref_deploy_key_access.rb
index a62cce1368d..4275476a1ff 100644
--- a/app/models/concerns/protected_ref_deploy_key_access.rb
+++ b/app/models/concerns/protected_ref_deploy_key_access.rb
@@ -24,7 +24,7 @@ module ProtectedRefDeployKeyAccess
end
def humanize
- return "Deploy key" if deploy_key.present?
+ return deploy_key.title if deploy_key?
super
end
diff --git a/app/views/projects/tracing/index.html.haml b/app/views/projects/tracing/index.html.haml
new file mode 100644
index 00000000000..ae6608cf343
--- /dev/null
+++ b/app/views/projects/tracing/index.html.haml
@@ -0,0 +1,4 @@
+- page_title _('Tracing')
+
+#js-tracing{ data: { view_model: observability_tracing_view_model(@project) } }
+
diff --git a/db/database_connections/ci.yaml b/db/database_connections/ci.yaml
index daa155dcb00..5331765214e 100644
--- a/db/database_connections/ci.yaml
+++ b/db/database_connections/ci.yaml
@@ -4,6 +4,11 @@ gitlab_schemas:
- gitlab_internal
- gitlab_shared
- gitlab_ci
+lock_gitlab_schemas:
+ - gitlab_main
+ - gitlab_main_clusterwide
+ - gitlab_main_cell
+ - gitlab_pm
klass: Ci::ApplicationRecord
# if CI database is not configured, use this database
fallback_database: main
diff --git a/db/database_connections/main.yaml b/db/database_connections/main.yaml
index dfdd50eb085..9eadd26ec26 100644
--- a/db/database_connections/main.yaml
+++ b/db/database_connections/main.yaml
@@ -6,6 +6,8 @@ gitlab_schemas:
- gitlab_main
- gitlab_main_cell
- gitlab_pm
+lock_gitlab_schemas:
+ - gitlab_ci
# Note that we use ActiveRecord::Base here and not ApplicationRecord.
# This is deliberate, as:
# - the load balancer must be enabled for _all_ models
diff --git a/db/docs/sbom_vulnerable_component_versions.yml b/db/docs/deleted_tables/sbom_vulnerable_component_versions.yml
similarity index 75%
rename from db/docs/sbom_vulnerable_component_versions.yml
rename to db/docs/deleted_tables/sbom_vulnerable_component_versions.yml
index 8747b6c6588..7642cb5ea53 100644
--- a/db/docs/sbom_vulnerable_component_versions.yml
+++ b/db/docs/deleted_tables/sbom_vulnerable_component_versions.yml
@@ -7,4 +7,6 @@ feature_categories:
description: Stores information about vulnerable SBoM components
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95622
milestone: '15.4'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125426
+removed_in_milestone: '16.2'
gitlab_schema: gitlab_main
diff --git a/db/docs/vulnerability_advisories.yml b/db/docs/deleted_tables/vulnerability_advisories.yml
similarity index 75%
rename from db/docs/vulnerability_advisories.yml
rename to db/docs/deleted_tables/vulnerability_advisories.yml
index 6ce7f30aa7c..613ab678f35 100644
--- a/db/docs/vulnerability_advisories.yml
+++ b/db/docs/deleted_tables/vulnerability_advisories.yml
@@ -8,4 +8,6 @@ feature_categories:
description: Stores vulnerability advisories
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95622
milestone: '15.4'
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125426
+removed_in_milestone: '16.2'
gitlab_schema: gitlab_main
diff --git a/db/post_migrate/20230705141703_rollback_vulnerability_advisories_foreign_key_on_vulnerable_component_versions.rb b/db/post_migrate/20230705141703_rollback_vulnerability_advisories_foreign_key_on_vulnerable_component_versions.rb
new file mode 100644
index 00000000000..92feca76511
--- /dev/null
+++ b/db/post_migrate/20230705141703_rollback_vulnerability_advisories_foreign_key_on_vulnerable_component_versions.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class RollbackVulnerabilityAdvisoriesForeignKeyOnVulnerableComponentVersions < Gitlab::Database::Migration[2.1]
+ SOURCE_TABLE = :sbom_vulnerable_component_versions
+ TARGET_TABLE = :vulnerability_advisories
+ COLUMN = :vulnerability_advisory_id
+
+ disable_ddl_transaction!
+
+ def up
+ # Foreign key is removed when the table is dropped in the next migration.
+ end
+
+ def down
+ add_concurrent_foreign_key SOURCE_TABLE, TARGET_TABLE, column: COLUMN, on_delete: :cascade
+ end
+end
diff --git a/db/post_migrate/20230705141733_rollback_component_version_foreign_key_on_vulnerable_component_versions.rb b/db/post_migrate/20230705141733_rollback_component_version_foreign_key_on_vulnerable_component_versions.rb
new file mode 100644
index 00000000000..c54d4ebd1e3
--- /dev/null
+++ b/db/post_migrate/20230705141733_rollback_component_version_foreign_key_on_vulnerable_component_versions.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class RollbackComponentVersionForeignKeyOnVulnerableComponentVersions < Gitlab::Database::Migration[2.1]
+ SOURCE_TABLE = :sbom_vulnerable_component_versions
+ TARGET_TABLE = :sbom_component_versions
+ COLUMN = :sbom_component_version_id
+
+ disable_ddl_transaction!
+
+ def up
+ # Foreign key is removed when the table is dropped in the next migration.
+ end
+
+ def down
+ add_concurrent_foreign_key SOURCE_TABLE, TARGET_TABLE, column: COLUMN, on_delete: :cascade
+ end
+end
diff --git a/db/post_migrate/20230705142241_drop_vulnerable_component_versions.rb b/db/post_migrate/20230705142241_drop_vulnerable_component_versions.rb
new file mode 100644
index 00000000000..10432f6b515
--- /dev/null
+++ b/db/post_migrate/20230705142241_drop_vulnerable_component_versions.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class DropVulnerableComponentVersions < Gitlab::Database::Migration[2.1]
+ ADVISORY_INDEX_NAME = "index_vulnerable_component_versions_on_vulnerability_advisory"
+ SBOM_COMPONENT_INDEX_NAME = "index_vulnerable_component_versions_on_sbom_component_version"
+
+ def up
+ drop_table :sbom_vulnerable_component_versions
+ end
+
+ def down
+ create_table :sbom_vulnerable_component_versions do |t|
+ t.references :vulnerability_advisory,
+ index: { name: ADVISORY_INDEX_NAME }
+
+ t.references :sbom_component_version,
+ index: { name: SBOM_COMPONENT_INDEX_NAME }
+
+ t.timestamps_with_timezone null: false
+ end
+ end
+end
diff --git a/db/post_migrate/20230705142334_drop_vulnerabilities_advisories.rb b/db/post_migrate/20230705142334_drop_vulnerabilities_advisories.rb
new file mode 100644
index 00000000000..e6bee52eb0c
--- /dev/null
+++ b/db/post_migrate/20230705142334_drop_vulnerabilities_advisories.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class DropVulnerabilitiesAdvisories < Gitlab::Database::Migration[2.1]
+ def up
+ drop_table :vulnerability_advisories
+ end
+
+ def down
+ create_table :vulnerability_advisories, id: false do |t|
+ t.uuid :uuid, null: false
+ t.timestamps_with_timezone null: false
+ t.primary_key :id
+ t.date :created_date, null: false
+ t.date :published_date, null: false
+ t.text :description, limit: 2048
+ t.text :title, limit: 2048
+ t.text :component_name, limit: 2048
+ t.text :solution, limit: 2048
+ t.text :not_impacted, limit: 2048
+ t.text :cvss_v2, limit: 128
+ t.text :cvss_v3, limit: 128
+ t.text :affected_range, limit: 32
+ t.text :identifiers, array: true, default: []
+ t.text :fixed_versions, array: true, default: []
+ t.text :urls, array: true, default: []
+ t.text :links, array: true, default: []
+ end
+ end
+end
diff --git a/db/schema_migrations/20230705141703 b/db/schema_migrations/20230705141703
new file mode 100644
index 00000000000..51c3cd350c1
--- /dev/null
+++ b/db/schema_migrations/20230705141703
@@ -0,0 +1 @@
+dafb3395a28180da275eceddb87af4deb0008b2d0793dd0ea3f34d2ae8bd5c10
\ No newline at end of file
diff --git a/db/schema_migrations/20230705141733 b/db/schema_migrations/20230705141733
new file mode 100644
index 00000000000..2b5870f2ba5
--- /dev/null
+++ b/db/schema_migrations/20230705141733
@@ -0,0 +1 @@
+2cea22d62a5a08a643b3043bea1e14e4965f57201db559995cab8616d7586f55
\ No newline at end of file
diff --git a/db/schema_migrations/20230705142241 b/db/schema_migrations/20230705142241
new file mode 100644
index 00000000000..4d4ee24d798
--- /dev/null
+++ b/db/schema_migrations/20230705142241
@@ -0,0 +1 @@
+ae094cd61e252b30c1ebe0e5369ff2c061aa96079bbc1addde160003e2263886
\ No newline at end of file
diff --git a/db/schema_migrations/20230705142334 b/db/schema_migrations/20230705142334
new file mode 100644
index 00000000000..ace38aed2f7
--- /dev/null
+++ b/db/schema_migrations/20230705142334
@@ -0,0 +1 @@
+33de9f678eb493070ceaae0e50461cffbcdbb5a542740b9fc595cba2c8c32808
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 239970ac959..633b2943d1a 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -22361,23 +22361,6 @@ CREATE SEQUENCE sbom_sources_id_seq
ALTER SEQUENCE sbom_sources_id_seq OWNED BY sbom_sources.id;
-CREATE TABLE sbom_vulnerable_component_versions (
- id bigint NOT NULL,
- vulnerability_advisory_id bigint,
- sbom_component_version_id bigint,
- created_at timestamp with time zone NOT NULL,
- updated_at timestamp with time zone NOT NULL
-);
-
-CREATE SEQUENCE sbom_vulnerable_component_versions_id_seq
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-ALTER SEQUENCE sbom_vulnerable_component_versions_id_seq OWNED BY sbom_vulnerable_component_versions.id;
-
CREATE TABLE scan_result_policies (
id bigint NOT NULL,
security_orchestration_policy_configuration_id bigint NOT NULL,
@@ -24116,44 +24099,6 @@ CREATE SEQUENCE vulnerabilities_id_seq
ALTER SEQUENCE vulnerabilities_id_seq OWNED BY vulnerabilities.id;
-CREATE TABLE vulnerability_advisories (
- uuid uuid NOT NULL,
- created_at timestamp with time zone NOT NULL,
- updated_at timestamp with time zone NOT NULL,
- id bigint NOT NULL,
- created_date date NOT NULL,
- published_date date NOT NULL,
- description text,
- title text,
- component_name text,
- solution text,
- not_impacted text,
- cvss_v2 text,
- cvss_v3 text,
- affected_range text,
- identifiers text[] DEFAULT '{}'::text[],
- fixed_versions text[] DEFAULT '{}'::text[],
- urls text[] DEFAULT '{}'::text[],
- links text[] DEFAULT '{}'::text[],
- CONSTRAINT check_3ab0544d19 CHECK ((char_length(title) <= 2048)),
- CONSTRAINT check_3b57023409 CHECK ((char_length(affected_range) <= 32)),
- CONSTRAINT check_4d5cd7be9c CHECK ((char_length(component_name) <= 2048)),
- CONSTRAINT check_962f256a51 CHECK ((char_length(solution) <= 2048)),
- CONSTRAINT check_aae93955fb CHECK ((char_length(cvss_v3) <= 128)),
- CONSTRAINT check_b8a17497f3 CHECK ((char_length(cvss_v2) <= 128)),
- CONSTRAINT check_c05a35f418 CHECK ((char_length(not_impacted) <= 2048)),
- CONSTRAINT check_ff9f6483b6 CHECK ((char_length(description) <= 2048))
-);
-
-CREATE SEQUENCE vulnerability_advisories_id_seq
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-ALTER SEQUENCE vulnerability_advisories_id_seq OWNED BY vulnerability_advisories.id;
-
CREATE TABLE vulnerability_exports (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -25935,8 +25880,6 @@ ALTER TABLE ONLY sbom_occurrences ALTER COLUMN id SET DEFAULT nextval('sbom_occu
ALTER TABLE ONLY sbom_sources ALTER COLUMN id SET DEFAULT nextval('sbom_sources_id_seq'::regclass);
-ALTER TABLE ONLY sbom_vulnerable_component_versions ALTER COLUMN id SET DEFAULT nextval('sbom_vulnerable_component_versions_id_seq'::regclass);
-
ALTER TABLE ONLY scan_result_policies ALTER COLUMN id SET DEFAULT nextval('scan_result_policies_id_seq'::regclass);
ALTER TABLE ONLY schema_inconsistencies ALTER COLUMN id SET DEFAULT nextval('schema_inconsistencies_id_seq'::regclass);
@@ -26083,8 +26026,6 @@ ALTER TABLE ONLY value_stream_dashboard_counts ALTER COLUMN id SET DEFAULT nextv
ALTER TABLE ONLY vulnerabilities ALTER COLUMN id SET DEFAULT nextval('vulnerabilities_id_seq'::regclass);
-ALTER TABLE ONLY vulnerability_advisories ALTER COLUMN id SET DEFAULT nextval('vulnerability_advisories_id_seq'::regclass);
-
ALTER TABLE ONLY vulnerability_exports ALTER COLUMN id SET DEFAULT nextval('vulnerability_exports_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_external_issue_links ALTER COLUMN id SET DEFAULT nextval('vulnerability_external_issue_links_id_seq'::regclass);
@@ -28383,9 +28324,6 @@ ALTER TABLE ONLY sbom_occurrences
ALTER TABLE ONLY sbom_sources
ADD CONSTRAINT sbom_sources_pkey PRIMARY KEY (id);
-ALTER TABLE ONLY sbom_vulnerable_component_versions
- ADD CONSTRAINT sbom_vulnerable_component_versions_pkey PRIMARY KEY (id);
-
ALTER TABLE ONLY scan_result_policies
ADD CONSTRAINT scan_result_policies_pkey PRIMARY KEY (id);
@@ -28650,9 +28588,6 @@ ALTER TABLE ONLY verification_codes
ALTER TABLE ONLY vulnerabilities
ADD CONSTRAINT vulnerabilities_pkey PRIMARY KEY (id);
-ALTER TABLE ONLY vulnerability_advisories
- ADD CONSTRAINT vulnerability_advisories_pkey PRIMARY KEY (id);
-
ALTER TABLE ONLY vulnerability_exports
ADD CONSTRAINT vulnerability_exports_pkey PRIMARY KEY (id);
@@ -33529,10 +33464,6 @@ CREATE UNIQUE INDEX index_vulnerability_statistics_on_unique_project_id ON vulne
CREATE UNIQUE INDEX index_vulnerability_user_mentions_on_note_id ON vulnerability_user_mentions USING btree (note_id) WHERE (note_id IS NOT NULL);
-CREATE INDEX index_vulnerable_component_versions_on_sbom_component_version ON sbom_vulnerable_component_versions USING btree (sbom_component_version_id);
-
-CREATE INDEX index_vulnerable_component_versions_on_vulnerability_advisory ON sbom_vulnerable_component_versions USING btree (vulnerability_advisory_id);
-
CREATE UNIQUE INDEX index_vulns_user_mentions_on_vulnerability_id ON vulnerability_user_mentions USING btree (vulnerability_id) WHERE (note_id IS NULL);
CREATE UNIQUE INDEX index_vulns_user_mentions_on_vulnerability_id_and_note_id ON vulnerability_user_mentions USING btree (vulnerability_id, note_id);
@@ -35906,9 +35837,6 @@ ALTER TABLE ONLY issues
ALTER TABLE ONLY ci_build_trace_chunks
ADD CONSTRAINT fk_89e29fa5ee_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
-ALTER TABLE ONLY sbom_vulnerable_component_versions
- ADD CONSTRAINT fk_8a2a1197f9 FOREIGN KEY (sbom_component_version_id) REFERENCES sbom_component_versions(id) ON DELETE CASCADE;
-
ALTER TABLE ONLY protected_branch_merge_access_levels
ADD CONSTRAINT fk_8a3072ccb3 FOREIGN KEY (protected_branch_id) REFERENCES protected_branches(id) ON DELETE CASCADE;
@@ -36242,9 +36170,6 @@ ALTER TABLE ONLY lists
ALTER TABLE ONLY agent_activity_events
ADD CONSTRAINT fk_d6f785c9fc FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
-ALTER TABLE ONLY sbom_vulnerable_component_versions
- ADD CONSTRAINT fk_d720a1959a FOREIGN KEY (vulnerability_advisory_id) REFERENCES vulnerability_advisories(id) ON DELETE CASCADE;
-
ALTER TABLE ONLY user_achievements
ADD CONSTRAINT fk_d7653ef780 FOREIGN KEY (revoked_by_user_id) REFERENCES users(id) ON DELETE SET NULL;
diff --git a/doc/administration/settings/index.md b/doc/administration/settings/index.md
index 8f349c13bb6..cf7640c75c1 100644
--- a/doc/administration/settings/index.md
+++ b/doc/administration/settings/index.md
@@ -70,7 +70,7 @@ The **CI/CD** settings contain:
## Security and Compliance settings
-- [License compliance settings](../../user/admin_area/settings/security_and_compliance.md#choose-package-registry-metadata-to-sync): Enable or disable synchronization of package metadata by a registry type.
+- [License compliance settings](security_and_compliance.md#choose-package-registry-metadata-to-sync): Enable or disable synchronization of package metadata by a registry type.
### Geo **(PREMIUM SELF)**
diff --git a/doc/administration/settings/security_and_compliance.md b/doc/administration/settings/security_and_compliance.md
new file mode 100644
index 00000000000..2237866ad9c
--- /dev/null
+++ b/doc/administration/settings/security_and_compliance.md
@@ -0,0 +1,25 @@
+---
+stage: Secure
+group: Composition Analysis
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: howto
+---
+
+# Security and Compliance Admin Area settings **(ULTIMATE SELF)**
+
+The settings for package metadata synchronization are located in the [Admin Area](index.md).
+
+## Choose package registry metadata to sync
+
+WARNING:
+The full package metadata sync can add up to 30 GB to the PostgreSQL database. Ensure you have provisioned enough disk space for the database before enabling this feature.
+We are actively working on reducing this data size in [epic 10415](https://gitlab.com/groups/gitlab-org/-/epics/10415).
+
+To choose the packages you want to synchronize with the GitLab License Database for [License Compliance](../../user/compliance/license_scanning_of_cyclonedx_files/index.md):
+
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Settings > Security and Compliance**.
+1. Expand **License Compliance**.
+1. Select or clear checkboxes for the package registries that you want to sync.
+1. Select **Save changes**.
diff --git a/doc/administration/settings/sidekiq_job_limits.md b/doc/administration/settings/sidekiq_job_limits.md
new file mode 100644
index 00000000000..d5cd24c5237
--- /dev/null
+++ b/doc/administration/settings/sidekiq_job_limits.md
@@ -0,0 +1,35 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: reference
+---
+
+# Sidekiq job size limits **(FREE SELF)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68982) in GitLab 14.3.
+
+[Sidekiq](../sidekiq/index.md) jobs get stored in
+Redis. To avoid excessive memory for Redis, we:
+
+- Compress job arguments before storing them in Redis.
+- Reject jobs that exceed the specified threshold limit after compression.
+
+To access Sidekiq job size limits:
+
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Settings > Preferences**.
+1. Expand **Sidekiq job size limits**.
+1. Adjust the compression threshold or size limit. The compression can
+ be disabled by selecting the **Track** mode.
+
+## Available settings
+
+| Setting | Default | Description |
+|-------------------------------------------|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Limiting mode | Compress | This mode compresses the jobs at the specified threshold and rejects them if they exceed the specified limit after compression. |
+| Sidekiq job compression threshold (bytes) | 100 000 (100 KB) | When the size of arguments exceeds this threshold, they are compressed before being stored in Redis. |
+| Sidekiq job size limit (bytes) | 0 | The jobs exceeding this size after compression are rejected. This avoids excessive memory usage in Redis leading to instability. Setting it to 0 prevents rejecting jobs. |
+
+After changing these values, [restart Sidekiq](../restart_gitlab.md).
diff --git a/doc/user/admin_area/settings/security_and_compliance.md b/doc/user/admin_area/settings/security_and_compliance.md
index 54fd04f6521..8c1e514c575 100644
--- a/doc/user/admin_area/settings/security_and_compliance.md
+++ b/doc/user/admin_area/settings/security_and_compliance.md
@@ -1,25 +1,11 @@
---
-stage: Secure
-group: Composition Analysis
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
-type: howto
+redirect_to: '../../../administration/settings/security_and_compliance.md'
+remove_date: '2023-10-13'
---
-# Security and Compliance Admin Area settings **(ULTIMATE SELF)**
+This document was moved to [another location](../../../administration/settings/security_and_compliance.md).
-The settings for package metadata synchronization are located in the [Admin Area](index.md).
-
-## Choose package registry metadata to sync
-
-WARNING:
-The full package metadata sync can add up to 30 GB to the PostgreSQL database. Ensure you have provisioned enough disk space for the database before enabling this feature.
-We are actively working on reducing this data size in [epic 10415](https://gitlab.com/groups/gitlab-org/-/epics/10415).
-
-To choose the packages you want to synchronize with the GitLab License Database for [License Compliance](../../compliance/license_scanning_of_cyclonedx_files/index.md):
-
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Settings > Security and Compliance**.
-1. Expand **License Compliance**.
-1. Select or clear checkboxes for the package registries that you want to sync.
-1. Select **Save changes**.
+
+
+
+
diff --git a/doc/user/admin_area/settings/sidekiq_job_limits.md b/doc/user/admin_area/settings/sidekiq_job_limits.md
index 08c3ced4c4e..81be26ec8e0 100644
--- a/doc/user/admin_area/settings/sidekiq_job_limits.md
+++ b/doc/user/admin_area/settings/sidekiq_job_limits.md
@@ -1,35 +1,11 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
-type: reference
+redirect_to: '../../../administration/settings/sidekiq_job_limits.md'
+remove_date: '2023-10-13'
---
-# Sidekiq job size limits **(FREE SELF)**
+This document was moved to [another location](../../../administration/settings/sidekiq_job_limits.md).
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68982) in GitLab 14.3.
-
-[Sidekiq](../../../administration/sidekiq/index.md) jobs get stored in
-Redis. To avoid excessive memory for Redis, we:
-
-- Compress job arguments before storing them in Redis.
-- Reject jobs that exceed the specified threshold limit after compression.
-
-To access Sidekiq job size limits:
-
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Settings > Preferences**.
-1. Expand **Sidekiq job size limits**.
-1. Adjust the compression threshold or size limit. The compression can
- be disabled by selecting the **Track** mode.
-
-## Available settings
-
-| Setting | Default | Description |
-|-------------------------------------------|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Limiting mode | Compress | This mode compresses the jobs at the specified threshold and rejects them if they exceed the specified limit after compression. |
-| Sidekiq job compression threshold (bytes) | 100 000 (100 KB) | When the size of arguments exceeds this threshold, they are compressed before being stored in Redis. |
-| Sidekiq job size limit (bytes) | 0 | The jobs exceeding this size after compression are rejected. This avoids excessive memory usage in Redis leading to instability. Setting it to 0 prevents rejecting jobs. |
-
-After changing these values, [restart Sidekiq](../../../administration/restart_gitlab.md).
+
+
+
+
diff --git a/doc/user/compliance/license_scanning_of_cyclonedx_files/index.md b/doc/user/compliance/license_scanning_of_cyclonedx_files/index.md
index 22defd593cd..1fbe67a62b2 100644
--- a/doc/user/compliance/license_scanning_of_cyclonedx_files/index.md
+++ b/doc/user/compliance/license_scanning_of_cyclonedx_files/index.md
@@ -23,7 +23,7 @@ Licenses not in the SPDX list are reported as "Unknown". License information can
Prerequisites:
-- On GitLab self-managed only, enable [Synchronization with the GitLab License Database](../../admin_area/settings/security_and_compliance.md#choose-package-registry-metadata-to-sync) in Admin Area for the GitLab instance. On GitLab SaaS this step has already been completed.
+- On GitLab self-managed only, enable [Synchronization with the GitLab License Database](../../../administration/settings/security_and_compliance.md#choose-package-registry-metadata-to-sync) in Admin Area for the GitLab instance. On GitLab SaaS this step has already been completed.
- Enable [Dependency Scanning](../../application_security/dependency_scanning/index.md#configuration)
and ensure that its prerequisites are met.
diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md
index f854dc418d6..eb67223c61f 100644
--- a/doc/user/group/import/index.md
+++ b/doc/user/group/import/index.md
@@ -208,10 +208,10 @@ Group items that are migrated to the destination GitLab instance include:
| Boards | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18938) |
| Board lists | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24863) |
| Epics
1 | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/250281) |
-| Group labels | [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/292429) |
+| Group labels
2 | [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/292429) |
| Iterations | [GitLab 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/292428) |
| Iteration cadences | [GitLab 15.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96570) |
-| Members
2 | [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/299415) |
+| Members
3 | [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/299415) |
| Group milestones | [GitLab 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/292427) |
| Namespace settings | [GitLab 14.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85128) |
| Release milestones | [GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/339422) |
@@ -222,6 +222,8 @@ Group items that are migrated to the destination GitLab instance include:
associations [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62074) in GitLab 13.12, state and
state ID [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28203) in GitLab 13.7, and system note
metadata [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63551) in GitLab 14.0.
+1. Group Labels cannot retain any associated Label Priorities during import. These labels will need to be re-prioritized manually
+ once the relevant Project is migrated to the destination instance.
1. Group members are associated with the imported group if the user:
- Already exists in the destination GitLab instance.
- Has a public email in the source GitLab instance that matches a confirmed email in the destination GitLab instance.
@@ -490,7 +492,7 @@ for your version of GitLab to check which items can be imported to the destinati
Group items that are exported include:
- Milestones
-- Labels
+- Group Labels (_without_ associated label priorities)
- Boards and Board Lists
- Badges
- Subgroups (including all the aforementioned data)
diff --git a/lib/api/ml_model_packages.rb b/lib/api/ml_model_packages.rb
index 43a61537aa2..35d231d9fe1 100644
--- a/lib/api/ml_model_packages.rb
+++ b/lib/api/ml_model_packages.rb
@@ -6,7 +6,7 @@ module API
include ::API::Helpers::Authentication
ML_MODEL_PACKAGES_REQUIREMENTS = {
- package_name: API::NO_SLASH_URL_PART_REGEX,
+ model_name: API::NO_SLASH_URL_PART_REGEX,
file_name: API::NO_SLASH_URL_PART_REGEX
}.freeze
@@ -47,15 +47,15 @@ module API
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/packages/ml_models' do
params do
- requires :package_name, type: String, desc: 'Package name', regexp: Gitlab::Regex.ml_model_name_regex,
+ requires :model_name, type: String, desc: 'Model name', regexp: Gitlab::Regex.ml_model_name_regex,
file_path: true
- requires :package_version, type: String, desc: 'Package version',
+ requires :model_version, type: String, desc: 'Model version',
regexp: Gitlab::Regex.ml_model_version_regex
requires :file_name, type: String, desc: 'Package file name',
regexp: Gitlab::Regex.ml_model_file_name_regex, file_path: true
optional :status, type: String, values: ALLOWED_STATUSES, desc: 'Package status'
end
- namespace ':package_name/*package_version/:file_name', requirements: ML_MODEL_PACKAGES_REQUIREMENTS do
+ namespace ':model_name/*model_version/:file_name', requirements: ML_MODEL_PACKAGES_REQUIREMENTS do
desc 'Workhorse authorize model package file' do
detail 'Introduced in GitLab 16.1'
success code: 200
@@ -91,7 +91,12 @@ module API
bad_request!('File is too large') if max_file_size_exceeded?
- create_package_file_params = declared(params).merge(build: current_authenticated_job)
+ create_package_file_params = declared(params).merge(
+ build: current_authenticated_job,
+ package_name: params[:model_name],
+ package_version: params[:model_version]
+ )
+
package_file = ::Packages::MlModel::CreatePackageFileService
.new(project, current_user, create_package_file_params)
.execute
@@ -119,7 +124,7 @@ module API
authorize_read_package!(project)
package = ::Packages::MlModel::PackageFinder.new(project)
- .execute!(params[:package_name], params[:package_version])
+ .execute!(params[:model_name], params[:model_version])
package_file = ::Packages::PackageFileFinder.new(package, params[:file_name]).execute!
present_package_file!(package_file)
diff --git a/lib/gitlab/database/database_connection_info.rb b/lib/gitlab/database/database_connection_info.rb
index 57ecbcd64ae..f0cafcf041b 100644
--- a/lib/gitlab/database/database_connection_info.rb
+++ b/lib/gitlab/database/database_connection_info.rb
@@ -6,6 +6,7 @@ module Gitlab
:name,
:description,
:gitlab_schemas,
+ :lock_gitlab_schemas,
:klass,
:fallback_database,
:db_dir,
@@ -20,6 +21,7 @@ module Gitlab
self.name = name.to_sym
self.gitlab_schemas = gitlab_schemas.map(&:to_sym)
self.klass = klass.constantize
+ self.lock_gitlab_schemas = (lock_gitlab_schemas || []).map(&:to_sym)
self.fallback_database = fallback_database&.to_sym
self.db_dir = Rails.root.join(db_dir || 'db')
end
diff --git a/lib/gitlab/database/gitlab_schema.rb b/lib/gitlab/database/gitlab_schema.rb
index 9b58284b389..0bd357b7730 100644
--- a/lib/gitlab/database/gitlab_schema.rb
+++ b/lib/gitlab/database/gitlab_schema.rb
@@ -23,6 +23,21 @@ module Gitlab
tables.map { |table| table_schema!(table) }.to_set
end
+ # Mainly used for test tables
+ # It maps table names prefixes to gitlab_schemas.
+ # The order of keys matter. Prefixes that contain other prefixes should come first.
+ IMPLICIT_GITLAB_SCHEMAS = {
+ '_test_gitlab_main_clusterwide_' => :gitlab_main_clusterwide,
+ '_test_gitlab_main_cell_' => :gitlab_main_cell,
+ '_test_gitlab_main_' => :gitlab_main,
+ '_test_gitlab_ci_' => :gitlab_ci,
+ '_test_gitlab_embedding_' => :gitlab_embedding,
+ '_test_gitlab_geo_' => :gitlab_geo,
+ '_test_gitlab_pm_' => :gitlab_pm,
+ '_test_' => :gitlab_shared,
+ 'pg_' => :gitlab_internal
+ }.freeze
+
# rubocop:disable Metrics/CyclomaticComplexity
def self.table_schema(name)
schema_name, table_name = name.split('.', 2) # Strip schema name like: `public.`
@@ -54,19 +69,11 @@ module Gitlab
# All tables from `information_schema.` are marked as `internal`
return :gitlab_internal if schema_name == 'information_schema'
- return :gitlab_main if table_name.start_with?('_test_gitlab_main_')
+ IMPLICIT_GITLAB_SCHEMAS.each do |prefix, gitlab_schema|
+ return gitlab_schema if table_name.start_with?(prefix)
+ end
- return :gitlab_ci if table_name.start_with?('_test_gitlab_ci_')
-
- return :gitlab_embedding if table_name.start_with?('_test_gitlab_embedding_')
-
- return :gitlab_geo if table_name.start_with?('_test_gitlab_geo_')
-
- # All tables that start with `_test_` without a following schema are shared and ignored
- return :gitlab_shared if table_name.start_with?('_test_')
-
- # All `pg_` tables are marked as `internal`
- return :gitlab_internal if table_name.start_with?('pg_')
+ nil
end
# rubocop:enable Metrics/CyclomaticComplexity
diff --git a/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables.rb b/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables.rb
index 55c4fd6a7af..fe456fab505 100644
--- a/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables.rb
+++ b/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables.rb
@@ -11,7 +11,9 @@ module Gitlab
end
def exec_migration(connection, direction)
- return super if %w[main ci].exclude?(Gitlab::Database.db_config_name(connection))
+ db_config_name = Gitlab::Database.db_config_name(connection)
+ db_info = Gitlab::Database.all_database_connections.fetch(db_config_name)
+ return super if db_info.lock_gitlab_schemas.empty?
return super if automatic_lock_on_writes_disabled?
# This compares the tables only on the `public` schema. Partitions are not affected
@@ -20,7 +22,7 @@ module Gitlab
new_tables = connection.tables - tables
new_tables.each do |table_name|
- lock_writes_on_table(connection, table_name) if should_lock_writes_on_table?(table_name)
+ lock_writes_on_table(connection, table_name) if should_lock_writes_on_table?(db_info, table_name)
end
end
@@ -39,16 +41,17 @@ module Gitlab
end
end
- def should_lock_writes_on_table?(table_name)
- # currently gitlab_schema represents only present existing tables, this is workaround for deleted tables
- # that should be skipped as they will be removed in a future migration.
+ def should_lock_writes_on_table?(db_info, table_name)
+ # We skip locking writes on tables that are scheduled for deletion in a future migration
return false if Gitlab::Database::GitlabSchema.deleted_tables_to_schema[table_name]
table_schema = Gitlab::Database::GitlabSchema.table_schema!(table_name.to_s)
- return false unless %i[gitlab_main gitlab_ci].include?(table_schema)
-
- Gitlab::Database.gitlab_schemas_for_connection(connection).exclude?(table_schema)
+ # This takes into consideration which database mode is used.
+ # In single-db and single-db-ci-connection the main database includes gitlab_ci tables,
+ # so we don't lock them there.
+ Gitlab::Database.gitlab_schemas_for_connection(connection).exclude?(table_schema) &&
+ db_info.lock_gitlab_schemas.include?(table_schema)
end
# with_retries creates new a transaction. So we set it to false if the connection is
diff --git a/lib/gitlab/import_export/group/import_export.yml b/lib/gitlab/import_export/group/import_export.yml
index c2a1a1f8575..7a91cfb340a 100644
--- a/lib/gitlab/import_export/group/import_export.yml
+++ b/lib/gitlab/import_export/group/import_export.yml
@@ -7,12 +7,10 @@ tree:
group:
- :milestones
- :badges
- - labels:
- - :priorities
+ - :labels
- boards:
- lists:
- - label:
- - :priorities
+ - :label
- :board
- members:
- :user
@@ -126,8 +124,7 @@ ee:
- boards:
- :board_assignee
- :milestone
- - labels:
- - :priorities
+ - :labels
- lists:
- milestone:
- events:
diff --git a/lib/gitlab/import_export/group/relation_factory.rb b/lib/gitlab/import_export/group/relation_factory.rb
index 1b8436c4ed9..664ef5358ef 100644
--- a/lib/gitlab/import_export/group/relation_factory.rb
+++ b/lib/gitlab/import_export/group/relation_factory.rb
@@ -6,7 +6,6 @@ module Gitlab
class RelationFactory < Base::RelationFactory
OVERRIDES = {
labels: :group_labels,
- priorities: :label_priorities,
label: :group_label,
parent: :epic,
iterations_cadences: 'Iterations::Cadence'
diff --git a/lib/gitlab/observability.rb b/lib/gitlab/observability.rb
index 0e6089e1d21..b500df86363 100644
--- a/lib/gitlab/observability.rb
+++ b/lib/gitlab/observability.rb
@@ -27,6 +27,15 @@ module Gitlab
"#{Gitlab::Observability.observability_url}/v1/auth/start"
end
+ def tracing_url(project)
+ "#{Gitlab::Observability.observability_url}/query/#{project.group.id}/#{project.id}/v1/traces"
+ end
+
+ def provisioning_url(_project)
+ # TODO Change to correct endpoint when API is ready
+ Gitlab::Observability.observability_url.to_s
+ end
+
# Returns true if the GitLab Observability UI (GOUI) feature flag is enabled
#
# @deprecated
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 21d81d0968c..035a173e9e0 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -881,6 +881,9 @@ msgstr ""
msgid "%{linkStart} Learn more%{linkEnd}."
msgstr ""
+msgid "%{linkStart}%{linkEnd} review summary"
+msgstr ""
+
msgid "%{listToShow}, and %{awardsListLength} more"
msgstr ""
@@ -11353,6 +11356,9 @@ msgstr ""
msgid "Comment templates can be used when creating comments inside issues, merge requests, and epics."
msgstr ""
+msgid "Comment type"
+msgstr ""
+
msgid "Comment/Reply (quoting selected text)"
msgstr ""
diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb
index 84cc481945f..900dc62f6b7 100644
--- a/qa/qa/page/component/note.rb
+++ b/qa/qa/page/component/note.rb
@@ -148,7 +148,7 @@ module QA
def start_discussion(text)
fill_element :comment_field, text
- within_element(:comment_button) { click_button(class: 'dropdown-toggle-split') }
+ within_element(:comment_button) { click_button(class: 'gl-new-dropdown-toggle') }
click_element :discussion_menu_item
click_element :comment_button
diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb
index d1e00d730b7..146e4d1265a 100644
--- a/spec/features/merge_request/user_posts_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_notes_spec.rb
@@ -46,8 +46,8 @@ RSpec.describe 'Merge request > User posts notes', :js, feature_category: :code_
it 'has enable submit button, preview button and saves content to local storage' do
page.within('.js-main-target-form') do
page.within('[data-testid="comment-button"]') do
- expect(page).to have_css('.split-content-button')
- expect(page).not_to have_css('.split-content-button[disabled]')
+ expect(page).to have_css('.gl-button')
+ expect(page).not_to have_css('.disabled')
end
expect(page).to have_css('.js-md-preview-button', visible: true)
end
diff --git a/spec/frontend/notes/components/comment_type_dropdown_spec.js b/spec/frontend/notes/components/comment_type_dropdown_spec.js
index b891c1f553d..053542a421c 100644
--- a/spec/frontend/notes/components/comment_type_dropdown_spec.js
+++ b/spec/frontend/notes/components/comment_type_dropdown_spec.js
@@ -1,4 +1,4 @@
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { GlButton, GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import CommentTypeDropdown from '~/notes/components/comment_type_dropdown.vue';
@@ -8,9 +8,9 @@ import { COMMENT_FORM } from '~/notes/i18n';
describe('CommentTypeDropdown component', () => {
let wrapper;
- const findCommentGlDropdown = () => wrapper.findComponent(GlDropdown);
- const findCommentDropdownOption = () => wrapper.findAllComponents(GlDropdownItem).at(0);
- const findDiscussionDropdownOption = () => wrapper.findAllComponents(GlDropdownItem).at(1);
+ const findCommentButton = () => wrapper.findComponent(GlButton);
+ const findCommentListboxOption = () => wrapper.findAllComponents(GlListboxItem).at(0);
+ const findDiscussionListboxOption = () => wrapper.findAllComponents(GlListboxItem).at(1);
const mountComponent = ({ props = {} } = {}) => {
wrapper = extendedWrapper(
@@ -20,6 +20,10 @@ describe('CommentTypeDropdown component', () => {
noteType: constants.COMMENT,
...props,
},
+ stubs: {
+ GlCollapsibleListbox,
+ GlListboxItem,
+ },
}),
);
};
@@ -33,15 +37,15 @@ describe('CommentTypeDropdown component', () => {
({ isInternalNote, buttonText }) => {
mountComponent({ props: { noteType: constants.COMMENT, isInternalNote } });
- expect(findCommentGlDropdown().props()).toMatchObject({ text: buttonText });
+ expect(findCommentButton().text()).toBe(buttonText);
},
);
it('Should set correct dropdown item checked when comment is selected', () => {
mountComponent({ props: { noteType: constants.COMMENT } });
- expect(findCommentDropdownOption().props()).toMatchObject({ isChecked: true });
- expect(findDiscussionDropdownOption().props()).toMatchObject({ isChecked: false });
+ expect(findCommentListboxOption().props('isSelected')).toBe(true);
+ expect(findDiscussionListboxOption().props('isSelected')).toBe(false);
});
it.each`
@@ -53,32 +57,22 @@ describe('CommentTypeDropdown component', () => {
({ isInternalNote, buttonText }) => {
mountComponent({ props: { noteType: constants.DISCUSSION, isInternalNote } });
- expect(findCommentGlDropdown().props()).toMatchObject({ text: buttonText });
+ expect(findCommentButton().text()).toBe(buttonText);
},
);
it('Should set correct dropdown item option checked when discussion is selected', () => {
mountComponent({ props: { noteType: constants.DISCUSSION } });
- expect(findCommentDropdownOption().props()).toMatchObject({ isChecked: false });
- expect(findDiscussionDropdownOption().props()).toMatchObject({ isChecked: true });
+ expect(findCommentListboxOption().props('isSelected')).toBe(false);
+ expect(findDiscussionListboxOption().props('isSelected')).toBe(true);
});
it('Should emit `change` event when clicking on an alternate dropdown option', () => {
mountComponent({ props: { noteType: constants.DISCUSSION } });
- const event = {
- type: 'click',
- stopPropagation: jest.fn(),
- preventDefault: jest.fn(),
- };
-
- findCommentDropdownOption().vm.$emit('click', event);
- findDiscussionDropdownOption().vm.$emit('click', event);
-
- // ensure the native events don't trigger anything
- expect(event.stopPropagation).toHaveBeenCalledTimes(2);
- expect(event.preventDefault).toHaveBeenCalledTimes(2);
+ findCommentListboxOption().trigger('click');
+ findDiscussionListboxOption().trigger('click');
expect(wrapper.emitted('change')[0]).toEqual([constants.COMMENT]);
expect(wrapper.emitted('change').length).toEqual(1);
@@ -87,7 +81,7 @@ describe('CommentTypeDropdown component', () => {
it('Should emit `click` event when clicking on the action button', () => {
mountComponent({ props: { noteType: constants.DISCUSSION } });
- findCommentGlDropdown().vm.$emit('click');
+ findCommentButton().vm.$emit('click');
expect(wrapper.emitted('click').length > 0).toBe(true);
});
diff --git a/spec/frontend/observability/observability_container_spec.js b/spec/frontend/observability/observability_container_spec.js
new file mode 100644
index 00000000000..1152df072d4
--- /dev/null
+++ b/spec/frontend/observability/observability_container_spec.js
@@ -0,0 +1,134 @@
+import { nextTick } from 'vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { stubComponent } from 'helpers/stub_component';
+import ObservabilityContainer from '~/observability/components/observability_container.vue';
+import ObservabilitySkeleton from '~/observability/components/skeleton/index.vue';
+import { buildClient } from '~/observability/client';
+
+jest.mock('~/observability/client');
+
+describe('ObservabilityContainer', () => {
+ let wrapper;
+
+ const mockSkeletonOnContentLoaded = jest.fn();
+ const mockSkeletonOnError = jest.fn();
+
+ const OAUTH_URL = 'https://example.com/oauth';
+ const TRACING_URL = 'https://example.com/tracing';
+ const PROVISIONING_URL = 'https://example.com/provisioning';
+
+ beforeEach(() => {
+ jest.spyOn(console, 'error').mockImplementation();
+
+ buildClient.mockReturnValue({});
+
+ wrapper = shallowMountExtended(ObservabilityContainer, {
+ propsData: {
+ oauthUrl: OAUTH_URL,
+ tracingUrl: TRACING_URL,
+ provisioningUrl: PROVISIONING_URL,
+ },
+ stubs: {
+ ObservabilitySkeleton: stubComponent(ObservabilitySkeleton, {
+ methods: { onContentLoaded: mockSkeletonOnContentLoaded, onError: mockSkeletonOnError },
+ }),
+ },
+ slots: {
+ default: {
+ render(h) {
+ h(`
mockedComponent
`);
+ },
+ name: 'MockComponent',
+ props: {
+ observabilityClient: {
+ type: Object,
+ required: true,
+ },
+ },
+ },
+ },
+ });
+ });
+
+ const dispatchMessageEvent = (status, origin) =>
+ window.dispatchEvent(
+ new MessageEvent('message', {
+ data: {
+ type: 'AUTH_COMPLETION',
+ status,
+ },
+ origin: origin ?? new URL(OAUTH_URL).origin,
+ }),
+ );
+
+ const findIframe = () => wrapper.findByTestId('observability-oauth-iframe');
+ const findSlotComponent = () => wrapper.findComponent({ name: 'MockComponent' });
+
+ it('should render the oauth iframe', () => {
+ const iframe = findIframe();
+ expect(iframe.exists()).toBe(true);
+ expect(iframe.attributes('hidden')).toBe('hidden');
+ expect(iframe.attributes('src')).toBe(OAUTH_URL);
+ expect(iframe.attributes('sandbox')).toBe('allow-same-origin allow-forms allow-scripts');
+ });
+
+ it('should render the ObservabilitySkeleton', () => {
+ const skeleton = wrapper.findComponent(ObservabilitySkeleton);
+ expect(skeleton.exists()).toBe(true);
+ });
+
+ it('should not render the default slot', () => {
+ expect(findSlotComponent().exists()).toBe(false);
+ });
+
+ it('renders the slot content and removes the iframe on oauth success message', async () => {
+ dispatchMessageEvent('success');
+
+ await nextTick();
+
+ expect(mockSkeletonOnContentLoaded).toHaveBeenCalledTimes(1);
+
+ const slotComponent = findSlotComponent();
+ expect(slotComponent.exists()).toBe(true);
+ expect(buildClient).toHaveBeenCalledWith({
+ provisioningUrl: PROVISIONING_URL,
+ tracingUrl: TRACING_URL,
+ });
+ expect(findIframe().exists()).toBe(false);
+ });
+
+ it('does not render the slot content and removes the iframe on oauth error message', async () => {
+ dispatchMessageEvent('error');
+
+ await nextTick();
+
+ expect(mockSkeletonOnError).toHaveBeenCalledTimes(1);
+
+ expect(findSlotComponent().exists()).toBe(false);
+ expect(findIframe().exists()).toBe(false);
+ expect(buildClient).not.toHaveBeenCalled();
+ });
+
+ it('handles oauth message only once', () => {
+ dispatchMessageEvent('success');
+ dispatchMessageEvent('success');
+
+ expect(mockSkeletonOnContentLoaded).toHaveBeenCalledTimes(1);
+ });
+
+ it('only handles messages from the oauth url', () => {
+ dispatchMessageEvent('success', 'www.fake-url.com');
+
+ expect(mockSkeletonOnContentLoaded).toHaveBeenCalledTimes(0);
+ expect(findSlotComponent().exists()).toBe(false);
+ expect(findIframe().exists()).toBe(true);
+ });
+
+ it('does not handle messages if the component has been destroyed', () => {
+ wrapper.destroy();
+
+ dispatchMessageEvent('success');
+
+ expect(mockSkeletonOnContentLoaded).toHaveBeenCalledTimes(0);
+ });
+});
diff --git a/spec/frontend/observability/skeleton_spec.js b/spec/frontend/observability/skeleton_spec.js
index 65dbb003743..cf9ed814c9f 100644
--- a/spec/frontend/observability/skeleton_spec.js
+++ b/spec/frontend/observability/skeleton_spec.js
@@ -19,7 +19,7 @@ describe('Skeleton component', () => {
const SKELETON_VARIANTS = Object.values(SKELETON_VARIANTS_BY_ROUTE);
- const findContentWrapper = () => wrapper.findByTestId('observability-wrapper');
+ const findContentWrapper = () => wrapper.findByTestId('content-wrapper');
const findExploreSkeleton = () => wrapper.findComponent(ExploreSkeleton);
@@ -42,8 +42,8 @@ describe('Skeleton component', () => {
mountComponent({ variant: 'explore' });
});
- describe('loading timers', () => {
- it('show Skeleton if content is not loaded within CONTENT_WAIT_MS', async () => {
+ describe('showing content', () => {
+ it('shows the skeleton if content is not loaded within CONTENT_WAIT_MS', async () => {
expect(findExploreSkeleton().exists()).toBe(false);
expect(findContentWrapper().isVisible()).toBe(false);
@@ -55,7 +55,7 @@ describe('Skeleton component', () => {
expect(findContentWrapper().isVisible()).toBe(false);
});
- it('does not show the skeleton if content has loaded within CONTENT_WAIT_MS', async () => {
+ it('does not show the skeleton if content loads within CONTENT_WAIT_MS', async () => {
expect(findExploreSkeleton().exists()).toBe(false);
expect(findContentWrapper().isVisible()).toBe(false);
@@ -73,9 +73,25 @@ describe('Skeleton component', () => {
expect(findContentWrapper().isVisible()).toBe(true);
expect(findExploreSkeleton().exists()).toBe(false);
});
+
+ it('hides the skeleton after content loads', async () => {
+ jest.advanceTimersByTime(DEFAULT_TIMERS.CONTENT_WAIT_MS);
+
+ await nextTick();
+
+ expect(findExploreSkeleton().exists()).toBe(true);
+ expect(findContentWrapper().isVisible()).toBe(false);
+
+ wrapper.vm.onContentLoaded();
+
+ await nextTick();
+
+ expect(findContentWrapper().isVisible()).toBe(true);
+ expect(findExploreSkeleton().exists()).toBe(false);
+ });
});
- describe('error timeout', () => {
+ describe('error handling', () => {
it('shows the error dialog if content has not loaded within TIMEOUT_MS', async () => {
expect(findAlert().exists()).toBe(false);
jest.advanceTimersByTime(DEFAULT_TIMERS.TIMEOUT_MS);
@@ -86,6 +102,17 @@ describe('Skeleton component', () => {
expect(findContentWrapper().isVisible()).toBe(false);
});
+ it('shows the error dialog if content fails to load', async () => {
+ expect(findAlert().exists()).toBe(false);
+
+ wrapper.vm.onError();
+
+ await nextTick();
+
+ expect(findAlert().exists()).toBe(true);
+ expect(findContentWrapper().isVisible()).toBe(false);
+ });
+
it('does not show the error dialog if content has loaded within TIMEOUT_MS', async () => {
wrapper.vm.onContentLoaded();
jest.advanceTimersByTime(DEFAULT_TIMERS.TIMEOUT_MS);
diff --git a/spec/frontend/tracing/list_index_spec.js b/spec/frontend/tracing/list_index_spec.js
new file mode 100644
index 00000000000..a5759035c2f
--- /dev/null
+++ b/spec/frontend/tracing/list_index_spec.js
@@ -0,0 +1,37 @@
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import ListIndex from '~/tracing/list_index.vue';
+import TracingList from '~/tracing/components/tracing_list.vue';
+import ObservabilityContainer from '~/observability/components/observability_container.vue';
+
+describe('ListIndex', () => {
+ const props = {
+ oauthUrl: 'https://example.com/oauth',
+ tracingUrl: 'https://example.com/tracing',
+ provisioningUrl: 'https://example.com/provisioning',
+ };
+
+ let wrapper;
+
+ const mountComponent = () => {
+ wrapper = shallowMountExtended(ListIndex, {
+ propsData: props,
+ });
+ };
+
+ it('renders ObservabilityContainer component', () => {
+ mountComponent();
+
+ const observabilityContainer = wrapper.findComponent(ObservabilityContainer);
+ expect(observabilityContainer.exists()).toBe(true);
+ expect(observabilityContainer.props('oauthUrl')).toBe(props.oauthUrl);
+ expect(observabilityContainer.props('tracingUrl')).toBe(props.tracingUrl);
+ expect(observabilityContainer.props('provisioningUrl')).toBe(props.provisioningUrl);
+ });
+
+ it('renders TracingList component inside ObservabilityContainer', () => {
+ mountComponent();
+
+ const observabilityContainer = wrapper.findComponent(ObservabilityContainer);
+ expect(observabilityContainer.findComponent(TracingList).exists()).toBe(true);
+ });
+});
diff --git a/spec/helpers/projects/observability_helper_spec.rb b/spec/helpers/projects/observability_helper_spec.rb
new file mode 100644
index 00000000000..65b6ddf04ec
--- /dev/null
+++ b/spec/helpers/projects/observability_helper_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'json'
+
+RSpec.describe Projects::ObservabilityHelper, type: :helper, feature_category: :tracing do
+ describe '#observability_tracing_view_model' do
+ let_it_be(:group) { build_stubbed(:group) }
+ let_it_be(:project) { build_stubbed(:project, group: group) }
+
+ it 'generates the correct JSON' do
+ expected_json = {
+ tracingUrl: Gitlab::Observability.tracing_url(project),
+ provisioningUrl: Gitlab::Observability.provisioning_url(project),
+ oauthUrl: Gitlab::Observability.oauth_url
+ }.to_json
+
+ expect(helper.observability_tracing_view_model(project)).to eq(expected_json)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/gitlab_schema_spec.rb b/spec/lib/gitlab/database/gitlab_schema_spec.rb
index 48f5cdb995b..1c864239ae6 100644
--- a/spec/lib/gitlab/database/gitlab_schema_spec.rb
+++ b/spec/lib/gitlab/database/gitlab_schema_spec.rb
@@ -29,6 +29,9 @@ RSpec.describe Gitlab::Database::GitlabSchema, feature_category: :database do
'audit_events_part_5fc467ac26' | :gitlab_main
'_test_gitlab_main_table' | :gitlab_main
'_test_gitlab_ci_table' | :gitlab_ci
+ '_test_gitlab_main_clusterwide_table' | :gitlab_main_clusterwide
+ '_test_gitlab_main_cell_table' | :gitlab_main_cell
+ '_test_gitlab_pm_table' | :gitlab_pm
'_test_my_table' | :gitlab_shared
'pg_attribute' | :gitlab_internal
end
diff --git a/spec/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables_spec.rb b/spec/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables_spec.rb
index e4241348b54..577bf00ba2f 100644
--- a/spec/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers/automatic_lock_writes_on_tables_spec.rb
@@ -9,7 +9,10 @@ RSpec.describe Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables,
let(:schema_class) { Class.new(Gitlab::Database::Migration[2.1]) }
let(:skip_automatic_lock_on_writes) { false }
let(:gitlab_main_table_name) { :_test_gitlab_main_table }
+ let(:gitlab_main_clusterwide_table_name) { :_test_gitlab_main_clusterwide_table }
+ let(:gitlab_main_cell_table_name) { :_test_gitlab_main_cell_table }
let(:gitlab_ci_table_name) { :_test_gitlab_ci_table }
+ let(:gitlab_pm_table_name) { :_test_gitlab_pm_table }
let(:gitlab_geo_table_name) { :_test_gitlab_geo_table }
let(:gitlab_shared_table_name) { :_test_table }
@@ -24,8 +27,11 @@ RSpec.describe Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables,
# Drop the created test tables, because we use non-transactional tests
after do
drop_table_if_exists(gitlab_main_table_name)
+ drop_table_if_exists(gitlab_main_clusterwide_table_name)
+ drop_table_if_exists(gitlab_main_cell_table_name)
drop_table_if_exists(gitlab_ci_table_name)
drop_table_if_exists(gitlab_geo_table_name)
+ drop_table_if_exists(gitlab_pm_table_name)
drop_table_if_exists(gitlab_shared_table_name)
drop_table_if_exists(renamed_gitlab_main_table_name)
drop_table_if_exists(renamed_gitlab_ci_table_name)
@@ -82,8 +88,12 @@ RSpec.describe Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables,
context 'when single database' do
let(:config_model) { Gitlab::Database.database_base_models[:main] }
let(:create_gitlab_main_table_migration_class) { create_table_migration(gitlab_main_table_name) }
+ let(:create_gitlab_main_cell_table_migration_class) { create_table_migration(gitlab_main_cell_table_name) }
let(:create_gitlab_ci_table_migration_class) { create_table_migration(gitlab_ci_table_name) }
let(:create_gitlab_shared_table_migration_class) { create_table_migration(gitlab_shared_table_name) }
+ let(:create_gitlab_main_clusterwide_table_migration_class) do
+ create_table_migration(gitlab_main_clusterwide_table_name)
+ end
before do
skip_if_database_exists(:ci)
@@ -93,13 +103,19 @@ RSpec.describe Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables,
expect(Gitlab::Database::LockWritesManager).not_to receive(:new)
create_gitlab_main_table_migration_class.migrate(:up)
+ create_gitlab_main_cell_table_migration_class.migrate(:up)
+ create_gitlab_main_clusterwide_table_migration_class.migrate(:up)
create_gitlab_ci_table_migration_class.migrate(:up)
create_gitlab_shared_table_migration_class.migrate(:up)
expect do
create_gitlab_main_table_migration_class.connection.execute("DELETE FROM #{gitlab_main_table_name}")
+ create_gitlab_main_cell_table_migration_class.connection.execute("DELETE FROM #{gitlab_main_cell_table_name}")
create_gitlab_ci_table_migration_class.connection.execute("DELETE FROM #{gitlab_ci_table_name}")
create_gitlab_shared_table_migration_class.connection.execute("DELETE FROM #{gitlab_shared_table_name}")
+ create_gitlab_main_clusterwide_table_migration_class.connection.execute(
+ "DELETE FROM #{gitlab_main_clusterwide_table_name}"
+ )
end.not_to raise_error
end
end
@@ -163,6 +179,27 @@ RSpec.describe Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables,
end
end
+ context 'for creating a gitlab_main_clusterwide table' do
+ let(:table_name) { gitlab_main_clusterwide_table_name }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
+ it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:ci]
+ end
+
+ context 'for creating a gitlab_main_cell table' do
+ let(:table_name) { gitlab_main_cell_table_name }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
+ it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:ci]
+ end
+
+ context 'for creating a gitlab_pm table' do
+ let(:table_name) { gitlab_pm_table_name }
+
+ it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
+ it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:ci]
+ end
+
context 'for creating a gitlab_ci table' do
let(:table_name) { gitlab_ci_table_name }
diff --git a/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb
index 495cefa002a..9852f6c9652 100644
--- a/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb
@@ -71,20 +71,22 @@ RSpec.describe Gitlab::ImportExport::Group::RelationTreeRestorer, feature_catego
before do
allow(shared.logger).to receive(:info).and_call_original
allow(relation_reader).to receive(:consume_relation).and_call_original
-
- allow(relation_reader)
- .to receive(:consume_relation)
- .with(importable_name, 'labels')
- .and_return([[label, 0]])
end
context 'when relation object is new' do
+ before do
+ allow(relation_reader)
+ .to receive(:consume_relation)
+ .with(importable_name, 'boards')
+ .and_return([[board, 0]])
+ end
+
context 'when relation object has invalid subrelations' do
- let(:label) do
+ let(:board) do
{
- 'title' => 'test',
- 'priorities' => [LabelPriority.new, LabelPriority.new],
- 'type' => 'GroupLabel'
+ 'name' => 'test',
+ 'lists' => [List.new, List.new],
+ 'group_id' => importable.id
}
end
@@ -94,26 +96,33 @@ RSpec.describe Gitlab::ImportExport::Group::RelationTreeRestorer, feature_catego
.with(
message: '[Project/Group Import] Invalid subrelation',
group_id: importable.id,
- relation_key: 'labels',
- error_messages: "Project can't be blank, Priority can't be blank, and Priority is not a number"
+ relation_key: 'boards',
+ error_messages: "Label can't be blank, Position can't be blank, and Position is not a number"
)
subject
- label = importable.labels.first
+ board = importable.boards.last
failure = importable.import_failures.first
expect(importable.import_failures.count).to eq(2)
- expect(label.title).to eq('test')
+ expect(board.name).to eq('test')
expect(failure.exception_class).to eq('ActiveRecord::RecordInvalid')
expect(failure.source).to eq('RelationTreeRestorer#save_relation_object')
expect(failure.exception_message)
- .to eq("Project can't be blank, Priority can't be blank, and Priority is not a number")
+ .to eq("Label can't be blank, Position can't be blank, and Position is not a number")
end
end
end
context 'when relation object is persisted' do
+ before do
+ allow(relation_reader)
+ .to receive(:consume_relation)
+ .with(importable_name, 'labels')
+ .and_return([[label, 0]])
+ end
+
context 'when relation object is invalid' do
let(:label) { create(:group_label, group: group, title: 'test') }
diff --git a/spec/lib/gitlab/observability_spec.rb b/spec/lib/gitlab/observability_spec.rb
index 84d591e2520..61f69a0171a 100644
--- a/spec/lib/gitlab/observability_spec.rb
+++ b/spec/lib/gitlab/observability_spec.rb
@@ -3,6 +3,9 @@
require 'spec_helper'
RSpec.describe Gitlab::Observability, feature_category: :error_tracking do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+
describe '.observability_url' do
let(:gitlab_url) { 'https://example.com' }
@@ -37,6 +40,18 @@ RSpec.describe Gitlab::Observability, feature_category: :error_tracking do
it { is_expected.to eq("#{described_class.observability_url}/v1/auth/start") }
end
+ describe '.tracing_url' do
+ subject { described_class.tracing_url(project) }
+
+ it { is_expected.to eq("#{described_class.observability_url}/query/#{group.id}/#{project.id}/v1/traces") }
+ end
+
+ describe '.provisioning_url' do
+ subject { described_class.provisioning_url(project) }
+
+ it { is_expected.to eq(described_class.observability_url.to_s) }
+ end
+
describe '.build_full_url' do
let_it_be(:group) { build_stubbed(:group, id: 123) }
let(:observability_url) { described_class.observability_url }
diff --git a/spec/models/bulk_imports/file_transfer/group_config_spec.rb b/spec/models/bulk_imports/file_transfer/group_config_spec.rb
index e50f52c728f..9e1e7cf6d6e 100644
--- a/spec/models/bulk_imports/file_transfer/group_config_spec.rb
+++ b/spec/models/bulk_imports/file_transfer/group_config_spec.rb
@@ -40,7 +40,12 @@ RSpec.describe BulkImports::FileTransfer::GroupConfig, feature_category: :import
describe '#top_relation_tree' do
it 'returns relation tree of a top level relation' do
- expect(subject.top_relation_tree('labels')).to eq('priorities' => {})
+ expect(subject.top_relation_tree('boards')).to include(
+ 'lists' => a_hash_including({
+ 'board' => anything,
+ 'label' => anything
+ })
+ )
end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 7cf3981969c..a91ac7a2c20 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -1510,52 +1510,22 @@ RSpec.describe Issue, feature_category: :team_planning do
end
describe '#publicly_visible?' do
- context 'using a public project' do
- let(:project) { create(:project, :public) }
+ let(:project) { build(:project, project_visiblity) }
+ let(:issue) { build(:issue, confidential: confidential, project: project) }
- it 'returns true for a regular issue' do
- issue = build(:issue, project: project)
+ subject { issue.send(:publicly_visible?) }
- expect(issue).to be_truthy
- end
-
- it 'returns false for a confidential issue' do
- issue = build(:issue, :confidential, project: project)
-
- expect(issue).not_to be_falsy
- end
+ where(:project_visiblity, :confidential, :expected_value) do
+ :public | false | true
+ :public | true | false
+ :internal | false | false
+ :internal | true | false
+ :private | false | false
+ :private | true | false
end
- context 'using an internal project' do
- let(:project) { create(:project, :internal) }
-
- it 'returns false for a regular issue' do
- issue = build(:issue, project: project)
-
- expect(issue).not_to be_falsy
- end
-
- it 'returns false for a confidential issue' do
- issue = build(:issue, :confidential, project: project)
-
- expect(issue).not_to be_falsy
- end
- end
-
- context 'using a private project' do
- let(:project) { create(:project, :private) }
-
- it 'returns false for a regular issue' do
- issue = build(:issue, project: project)
-
- expect(issue).not_to be_falsy
- end
-
- it 'returns false for a confidential issue' do
- issue = build(:issue, :confidential, project: project)
-
- expect(issue).not_to be_falsy
- end
+ with_them do
+ it { is_expected.to eq(expected_value) }
end
end
diff --git a/spec/requests/api/ml_model_packages_spec.rb b/spec/requests/api/ml_model_packages_spec.rb
index a559f1642e8..3166298b430 100644
--- a/spec/requests/api/ml_model_packages_spec.rb
+++ b/spec/requests/api/ml_model_packages_spec.rb
@@ -124,18 +124,18 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
project.send("add_#{user_role}", user) if member && user_role != :anonymous
end
- describe 'PUT /api/v4/projects/:id/packages/ml_models/:package_name/:package_version/:file_name/authorize' do
+ describe 'PUT /api/v4/projects/:id/packages/ml_models/:model_name/:model_version/:file_name/authorize' do
include_context 'ml model authorize permissions table'
let(:token) { tokens[:personal_access_token] }
let(:user_headers) { { 'HTTP_AUTHORIZATION' => token } }
let(:headers) { user_headers.merge(workhorse_headers) }
let(:request) { authorize_upload_file(headers) }
- let(:package_name) { 'my_package' }
+ let(:model_name) { 'my_package' }
let(:file_name) { 'myfile.tar.gz' }
subject(:api_response) do
- url = "/projects/#{project.id}/packages/ml_models/#{package_name}/0.0.1/#{file_name}/authorize"
+ url = "/projects/#{project.id}/packages/ml_models/#{model_name}/0.0.1/#{file_name}/authorize"
put api(url), headers: headers
@@ -162,7 +162,7 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
end
describe 'application security' do
- where(:package_name, :file_name) do
+ where(:model_name, :file_name) do
'my-package/../' | 'myfile.tar.gz'
'my-package%2f%2e%2e%2f' | 'myfile.tar.gz'
'my_package' | '../.ssh%2fauthorized_keys'
@@ -177,7 +177,7 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
end
end
- describe 'PUT /api/v4/projects/:id/packages/ml_models/:package_name/:package_version/:file_name' do
+ describe 'PUT /api/v4/projects/:id/packages/ml_models/:model_name/:model_version/:file_name' do
include_context 'ml model authorize permissions table'
let_it_be(:file_name) { 'model.md5' }
@@ -188,10 +188,10 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
let(:params) { { file: temp_file(file_name) } }
let(:file_key) { :file }
let(:send_rewritten_field) { true }
- let(:package_name) { 'my_package' }
+ let(:model_name) { 'my_package' }
subject(:api_response) do
- url = "/projects/#{project.id}/packages/ml_models/#{package_name}/0.0.1/#{file_name}"
+ url = "/projects/#{project.id}/packages/ml_models/#{model_name}/0.0.1/#{file_name}"
workhorse_finalize(
api(url),
@@ -236,14 +236,14 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
end
end
- describe 'GET /api/v4/projects/:project_id/packages/ml_models/:package_name/:package_version/:file_name' do
+ describe 'GET /api/v4/projects/:project_id/packages/ml_models/:model_name/:model_version/:file_name' do
include_context 'ml model authorize permissions table'
let_it_be(:package) { create(:ml_model_package, project: project, name: 'model', version: '0.0.1') }
let_it_be(:package_file) { create(:package_file, :generic, package: package, file_name: 'model.md5') }
- let(:package_name) { package.name }
- let(:package_version) { package.version }
+ let(:model_name) { package.name }
+ let(:model_version) { package.version }
let(:file_name) { package_file.file_name }
let(:token) { tokens[:personal_access_token] }
@@ -251,7 +251,7 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
let(:headers) { user_headers.merge(workhorse_headers) }
subject(:api_response) do
- url = "/projects/#{project.id}/packages/ml_models/#{package_name}/#{package_version}/#{file_name}"
+ url = "/projects/#{project.id}/packages/ml_models/#{model_name}/#{model_version}/#{file_name}"
get api(url), headers: headers
diff --git a/spec/requests/api/protected_branches_spec.rb b/spec/requests/api/protected_branches_spec.rb
index 04d5f7ac20a..ad3a69fec9d 100644
--- a/spec/requests/api/protected_branches_spec.rb
+++ b/spec/requests/api/protected_branches_spec.rb
@@ -121,7 +121,7 @@ RSpec.describe API::ProtectedBranches, feature_category: :source_code_management
get api(route, user)
expect(json_response['push_access_levels']).to include(
- a_hash_including('access_level_description' => 'Deploy key', 'deploy_key_id' => deploy_key.id)
+ a_hash_including('access_level_description' => deploy_key.title, 'deploy_key_id' => deploy_key.id)
)
end
end
diff --git a/spec/requests/api/protected_tags_spec.rb b/spec/requests/api/protected_tags_spec.rb
index c6398e624f8..4e7227b2294 100644
--- a/spec/requests/api/protected_tags_spec.rb
+++ b/spec/requests/api/protected_tags_spec.rb
@@ -95,7 +95,7 @@ RSpec.describe API::ProtectedTags, feature_category: :source_code_management do
get api(route, user)
expect(json_response['create_access_levels']).to include(
- a_hash_including('access_level_description' => 'Deploy key', 'deploy_key_id' => deploy_key.id)
+ a_hash_including('access_level_description' => deploy_key.title, 'deploy_key_id' => deploy_key.id)
)
end
end
diff --git a/spec/requests/projects/tracing_controller_spec.rb b/spec/requests/projects/tracing_controller_spec.rb
index 520e99b120a..eecaa0d962a 100644
--- a/spec/requests/projects/tracing_controller_spec.rb
+++ b/spec/requests/projects/tracing_controller_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
RSpec.describe Projects::TracingController, feature_category: :tracing do
- let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
let_it_be(:user) { create(:user) }
let(:path) { nil }
let(:observability_tracing_ff) { true }
@@ -44,6 +45,17 @@ RSpec.describe Projects::TracingController, feature_category: :tracing do
expect(subject).to have_gitlab_http_status(:ok)
end
+ it 'renders the js-tracing element correctly' do
+ element = Nokogiri::HTML.parse(subject.body).at_css('#js-tracing')
+
+ expected_view_model = {
+ tracingUrl: Gitlab::Observability.tracing_url(project),
+ provisioningUrl: Gitlab::Observability.provisioning_url(project),
+ oauthUrl: Gitlab::Observability.oauth_url
+ }.to_json
+ expect(element.attributes['data-view-model'].value).to eq(expected_view_model)
+ end
+
context 'when feature is disabled' do
let(:observability_tracing_ff) { false }
diff --git a/spec/support/shared_examples/features/discussion_comments_shared_example.rb b/spec/support/shared_examples/features/discussion_comments_shared_example.rb
index ac8055138d7..ba520d0c609 100644
--- a/spec/support/shared_examples/features/discussion_comments_shared_example.rb
+++ b/spec/support/shared_examples/features/discussion_comments_shared_example.rb
@@ -3,8 +3,8 @@
RSpec.shared_examples 'thread comments for commit and snippet' do |resource_name|
let(:form_selector) { '.js-main-target-form' }
let(:dropdown_selector) { "#{form_selector} .comment-type-dropdown" }
- let(:toggle_selector) { "#{dropdown_selector} .gl-dropdown-toggle" }
- let(:menu_selector) { "#{dropdown_selector} .dropdown-menu" }
+ let(:toggle_selector) { "#{dropdown_selector} .gl-new-dropdown-toggle" }
+ let(:menu_selector) { "#{dropdown_selector} .gl-new-dropdown-contents" }
let(:submit_selector) { "#{form_selector} .js-comment-submit-button > button:first-child" }
let(:close_selector) { "#{form_selector} .btn-comment-and-close" }
let(:comments_selector) { '.timeline > .note.timeline-entry:not(.being-posted)' }
@@ -63,33 +63,6 @@ RSpec.shared_examples 'thread comments for commit and snippet' do |resource_name
expect(page).not_to have_selector menu_selector
end
- it 'clicking the ul padding or divider should not change the text' do
- execute_script("document.querySelector('#{menu_selector}').click()")
-
- # on issues page, the menu closes when clicking anywhere, on other pages it will
- # remain open if clicking divider or menu padding, but should not change button action
- #
- # if dropdown menu is not toggled (and also not present),
- # it's "issue-type" dropdown
- if first(menu_selector, minimum: 0).nil?
- expect(find(dropdown_selector)).to have_content 'Comment'
-
- find(toggle_selector).click
- execute_script("document.querySelector('#{menu_selector} .dropdown-divider').click()")
- else
- execute_script("document.querySelector('#{menu_selector}').click()")
-
- expect(page).to have_selector menu_selector
- expect(find(dropdown_selector)).to have_content 'Comment'
-
- execute_script("document.querySelector('#{menu_selector} .dropdown-divider').click()")
-
- expect(page).to have_selector menu_selector
- end
-
- expect(find(dropdown_selector)).to have_content 'Comment'
- end
-
describe 'when selecting "Start thread"' do
before do
find("#{menu_selector} li", match: :first)
@@ -178,10 +151,10 @@ end
RSpec.shared_examples 'thread comments for issue, epic and merge request' do |resource_name|
let(:form_selector) { '.js-main-target-form' }
- let(:dropdown_selector) { "#{form_selector} [data-testid='comment-button']" }
- let(:submit_button_selector) { "#{dropdown_selector} .split-content-button" }
- let(:toggle_selector) { "#{dropdown_selector} .dropdown-toggle-split" }
- let(:menu_selector) { "#{dropdown_selector} .dropdown-menu" }
+ let(:dropdown_selector) { "#{form_selector} .comment-type-dropdown" }
+ let(:toggle_selector) { "#{dropdown_selector} .gl-new-dropdown-toggle" }
+ let(:menu_selector) { "#{dropdown_selector} .gl-new-dropdown-contents" }
+ let(:submit_selector) { "#{form_selector} .js-comment-submit-button > button:first-child" }
let(:close_selector) { "#{form_selector} .btn-comment-and-close" }
let(:comments_selector) { '.timeline > .note.timeline-entry:not(.being-posted)' }
let(:comment) { 'My comment' }
@@ -191,7 +164,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
find("#{form_selector} .note-textarea").send_keys(comment)
- find(submit_button_selector).click
+ find(submit_selector).click
wait_for_all_requests
@@ -260,7 +233,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
describe 'creating a thread' do
before do
- find(submit_button_selector).click
+ find(submit_selector).click
wait_for_requests
find(comments_selector, match: :first)
@@ -366,7 +339,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
end
it 'updates the submit button text and closes the dropdown' do
- button = find(submit_button_selector)
+ button = find(submit_selector)
expect(button).to have_content 'Comment'
diff --git a/spec/tasks/gitlab/feature_categories_rake_spec.rb b/spec/tasks/gitlab/feature_categories_rake_spec.rb
index 33f4bca4c85..f495c7e8911 100644
--- a/spec/tasks/gitlab/feature_categories_rake_spec.rb
+++ b/spec/tasks/gitlab/feature_categories_rake_spec.rb
@@ -40,7 +40,8 @@ RSpec.describe 'gitlab:feature_categories:index', :silence_stdout, feature_categ
)
),
'database_tables' => a_hash_including(
- 'container_scanning' => a_collection_including('vulnerability_advisories')
+ 'continuous_integration' => a_collection_including('ci_pipelines'),
+ 'user_profile' => a_collection_including('users')
)
}
diff --git a/vendor/project_templates/typo3_distribution.tar.gz b/vendor/project_templates/typo3_distribution.tar.gz
index 13c158d1462..1de92d781af 100644
Binary files a/vendor/project_templates/typo3_distribution.tar.gz and b/vendor/project_templates/typo3_distribution.tar.gz differ