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>2024-01-22 21:10:33 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2024-01-22 21:10:33 +0300
commita9a2f9257eae40935e03ca4185d5263bcb7ba45f (patch)
treef12875873819442e10ab04bd15fd975bf4bb7b64
parent917d93d86da4dffd96abcfcf3aa83b0d6fa45286 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/issue_templates/AI Project Proposal.md2
-rw-r--r--.gitlab/issue_templates/Design Sprint.md38
-rw-r--r--.gitlab/issue_templates/Documentation.md2
-rw-r--r--.gitlab/issue_templates/Feature Proposal - lean.md38
-rw-r--r--.gitlab/issue_templates/Feature proposal - detailed.md40
-rw-r--r--.gitlab/issue_templates/Fulfillment Group UX Issue.md38
-rw-r--r--.gitlab/issue_templates/Navigation Proposals.md2
-rw-r--r--.gitlab/issue_templates/Pipeline Security issue implementation.md2
-rw-r--r--.gitlab/issue_templates/Problem Validation.md2
-rw-r--r--.rubocop_todo/layout/line_length.yml1
-rw-r--r--.rubocop_todo/rspec/feature_category.yml1
-rw-r--r--.rubocop_todo/style/keyword_parameters_order.yml1
-rw-r--r--app/assets/javascripts/diffs/components/app.vue19
-rw-r--r--app/assets/javascripts/diffs/components/diff_inline_findings.vue32
-rw-r--r--app/assets/javascripts/diffs/components/diff_inline_findings_item.vue39
-rw-r--r--app/assets/javascripts/diffs/components/diff_line.vue32
-rw-r--r--app/assets/javascripts/diffs/components/diff_row.vue14
-rw-r--r--app/assets/javascripts/diffs/components/diff_view.vue34
-rw-r--r--app/assets/javascripts/diffs/components/inline_findings.vue41
-rw-r--r--app/assets/javascripts/diffs/index.js1
-rw-r--r--app/assets/javascripts/diffs/store/modules/index.js2
-rw-r--r--app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue41
-rw-r--r--app/assets/javascripts/vue_shared/components/registry/title_area.vue1
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/controllers/projects_controller.rb8
-rw-r--r--app/finders/groups/accepting_project_creations_finder.rb4
-rw-r--r--app/finders/groups/accepting_project_transfers_finder.rb2
-rw-r--r--app/graphql/mutations/snippets/create.rb6
-rw-r--r--app/graphql/mutations/snippets/update.rb6
-rw-r--r--app/models/group.rb8
-rw-r--r--app/models/projects/branch_rule.rb11
-rw-r--r--app/views/notify/new_review_email.html.haml49
-rw-r--r--app/workers/click_house/event_paths_consistency_cron_worker.rb4
-rw-r--r--config/feature_flags/wip/redis_hll_property_name_tracking.yml (renamed from config/feature_flags/gitlab_com_derisk/enhanced_review_email.yml)13
-rw-r--r--config/initializers/7_redis.rb1
-rw-r--r--db/docs/catalog_resource_components.yml11
-rw-r--r--db/docs/catalog_resource_versions.yml10
-rw-r--r--db/docs/catalog_resources.yml10
-rw-r--r--db/post_migrate/20240117070119_remove_index_protected_environments_on_project_id.rb17
-rw-r--r--db/schema_migrations/202401170701191
-rw-r--r--db/structure.sql2
-rw-r--r--doc/architecture/blueprints/ci_builds_runner_fleet_metrics/img/current_page.pngbin132200 -> 34877 bytes
-rw-r--r--doc/architecture/blueprints/remote_development/index.md2
-rw-r--r--doc/ci/runners/img/runner_fleet_dashboard.pngbin348380 -> 118249 bytes
-rw-r--r--doc/ci/yaml/index.md11
-rw-r--r--doc/development/contributing/first_contribution.md20
-rw-r--r--doc/development/contributing/img/ui_color_theme_after.pngbin0 -> 13246 bytes
-rw-r--r--doc/development/contributing/img/ui_color_theme_before.pngbin0 -> 11897 bytes
-rw-r--r--doc/development/contributing/img/ui_text_after.pngbin4446 -> 0 bytes
-rw-r--r--doc/development/contributing/img/ui_text_before.pngbin5243 -> 0 bytes
-rw-r--r--doc/development/merge_request_concepts/diffs/frontend.md2
-rw-r--r--doc/user/ai_features.md2
-rw-r--r--doc/user/clusters/agent/gitops/example_repository_structure.md2
-rw-r--r--doc/user/clusters/agent/gitops/flux_oci_tutorial.md2
-rw-r--r--doc/user/clusters/agent/gitops/flux_tutorial.md2
-rw-r--r--doc/user/clusters/agent/index.md2
-rw-r--r--doc/user/project/merge_requests/changes.md2
-rw-r--r--lib/api/commits.rb3
-rw-r--r--lib/gitlab/instrumentation/redis_helper.rb26
-rw-r--r--lib/gitlab/patch/redis_client.rb18
-rw-r--r--lib/gitlab/usage_data_counters/editor_unique_counter.rb55
-rw-r--r--lib/gitlab/usage_data_counters/hll_redis_counter.rb56
-rw-r--r--lib/gitlab/usage_data_counters/hll_redis_key_overrides.yml128
-rw-r--r--locale/gitlab.pot20
-rw-r--r--spec/features/groups/members/sort_members_spec.rb2
-rw-r--r--spec/features/projects/members/sorting_spec.rb2
-rw-r--r--spec/finders/groups/accepting_project_transfers_finder_spec.rb2
-rw-r--r--spec/frontend/diffs/components/app_spec.js4
-rw-r--r--spec/frontend/diffs/components/diff_inline_findings_item_spec.js38
-rw-r--r--spec/frontend/diffs/components/diff_inline_findings_spec.js33
-rw-r--r--spec/frontend/diffs/components/diff_line_spec.js65
-rw-r--r--spec/frontend/diffs/components/diff_view_spec.js45
-rw-r--r--spec/frontend/diffs/components/inline_findings_spec.js33
-rw-r--r--spec/frontend/diffs/utils/diff_line_spec.js30
-rw-r--r--spec/frontend/members/components/filter_sort/sort_dropdown_spec.js65
-rw-r--r--spec/lib/gitlab/instrumentation/redis_client_middleware_spec.rb6
-rw-r--r--spec/lib/gitlab/instrumentation/redis_helper_spec.rb52
-rw-r--r--spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb2
-rw-r--r--spec/lib/gitlab/patch/redis_client_spec.rb38
-rw-r--r--spec/lib/gitlab/process_supervisor_spec.rb14
-rw-r--r--spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb80
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb235
-rw-r--r--spec/models/group_spec.rb52
-rw-r--r--spec/models/projects/branch_rule_spec.rb24
-rw-r--r--spec/requests/api/commits_spec.rb9
-rw-r--r--spec/requests/api/graphql/mutations/snippets/create_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/snippets/update_spec.rb18
-rw-r--r--spec/spec_helper.rb3
-rw-r--r--spec/support/helpers/database/duplicate_indexes.yml3
-rw-r--r--spec/support/rspec_order_todo.yml1
-rw-r--r--spec/support/shared_examples/controllers/internal_event_tracking_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb12
92 files changed, 896 insertions, 918 deletions
diff --git a/.gitlab/issue_templates/AI Project Proposal.md b/.gitlab/issue_templates/AI Project Proposal.md
index 412d8d138fe..b7a518c7441 100644
--- a/.gitlab/issue_templates/AI Project Proposal.md
+++ b/.gitlab/issue_templates/AI Project Proposal.md
@@ -24,7 +24,7 @@ _Why do you believe this AI solution is a good way to solve this problem?_
_What assumptions are you making about this problem and the solution?_
### Personas
-_What [personas](https://about.gitlab.com/handbook/product/personas/#list-of-user-personas) have this problem, who is the intended user?_
+_What [personas](https://handbook.gitlab.com/handbook/product/personas/#list-of-user-personas) have this problem, who is the intended user?_
## Proposal
<!-- Explain the proposed changes, including details around usage and business drivers. -->
diff --git a/.gitlab/issue_templates/Design Sprint.md b/.gitlab/issue_templates/Design Sprint.md
index 2ad3bac0a05..7b072d92ff2 100644
--- a/.gitlab/issue_templates/Design Sprint.md
+++ b/.gitlab/issue_templates/Design Sprint.md
@@ -95,25 +95,25 @@ If you enjoy taking notes using Post-it notes make sure you have some of them as
Deciding which persona we are focusing on will be part of the Day 1 discussions in the workshop. The personas we are going to consider are:
-<!-- Choose which personas could be target users, and choose from this list during the Sprint. Personas are described at https://about.gitlab.com/handbook/product/personas/
-
-* [Parker (Product Manager)](https://about.gitlab.com/handbook/product/personas/#parker-product-manager)
-* [Delaney (Development Team Lead)](https://about.gitlab.com/handbook/product/personas/#delaney-development-team-lead)
-* [Presley (Product Designer)](https://about.gitlab.com/handbook/product/personas/#presley-product-designer)
-* [Sasha (Software Developer)](https://about.gitlab.com/handbook/product/personas/#sasha-software-developer)
-* [Priyanka (Platform Engineer)](https://about.gitlab.com/handbook/product/personas/#priyanka-platform-engineer)
-* [Sidney (Systems Administrator)](https://about.gitlab.com/handbook/product/personas/#sidney-systems-administrator)
-* [Rachel (Release Manager)](https://about.gitlab.com/handbook/product/personas/#rachel-release-manager)
-* [Simone (Software Engineer in Test)](https://about.gitlab.com/handbook/product/personas/#simone-software-engineer-in-test)
-* [Allison (Application Ops)](https://about.gitlab.com/handbook/product/personas/#allison-application-ops)
-* [Ingrid (Infrastructure Operator)](https://about.gitlab.com/handbook/product/personas/#ingrid-infrastructure-operator)
-* [Dakota (Application Development Director)](https://about.gitlab.com/handbook/product/personas/#dakota-application-development-director)
-* [Dana (Data Analyst)](https://about.gitlab.com/handbook/product/personas/#dana-data-analyst)
-* [Eddie (Content Editor)](https://about.gitlab.com/handbook/product/personas/#eddie-content-editor)
-* [Amy (Application Security Engineer)](https://about.gitlab.com/handbook/product/personas/#amy-application-security-engineer)
-* [Isaac (Infrastructure Engineer)](https://about.gitlab.com/handbook/product/personas/#isaac-infrastructure-security-engineer)
-* [Alex (Security Operations Engineer)](https://about.gitlab.com/handbook/product/personas/#alex-security-operations-engineer)
-* [Cameron (Compliance Manager)](https://about.gitlab.com/handbook/product/personas/#cameron-compliance-manager)
+<!-- Choose which personas could be target users, and choose from this list during the Sprint. Personas are described at https://handbook.gitlab.com/handbook/product/personas/
+
+* [Parker (Product Manager)](https://handbook.gitlab.com/handbook/product/personas/#parker-product-manager)
+* [Delaney (Development Team Lead)](https://handbook.gitlab.com/handbook/product/personas/#delaney-development-team-lead)
+* [Presley (Product Designer)](https://handbook.gitlab.com/handbook/product/personas/#presley-product-designer)
+* [Sasha (Software Developer)](https://handbook.gitlab.com/handbook/product/personas/#sasha-software-developer)
+* [Priyanka (Platform Engineer)](https://handbook.gitlab.com/handbook/product/personas/#priyanka-platform-engineer)
+* [Sidney (Systems Administrator)](https://handbook.gitlab.com/handbook/product/personas/#sidney-systems-administrator)
+* [Rachel (Release Manager)](https://handbook.gitlab.com/handbook/product/personas/#rachel-release-manager)
+* [Simone (Software Engineer in Test)](https://handbook.gitlab.com/handbook/product/personas/#simone-software-engineer-in-test)
+* [Allison (Application Ops)](https://handbook.gitlab.com/handbook/product/personas/#allison-application-ops)
+* [Ingrid (Infrastructure Operator)](https://handbook.gitlab.com/handbook/product/personas/#ingrid-infrastructure-operator)
+* [Dakota (Application Development Director)](https://handbook.gitlab.com/handbook/product/personas/#dakota-application-development-director)
+* [Dana (Data Analyst)](https://handbook.gitlab.com/handbook/product/personas/#dana-data-analyst)
+* [Eddie (Content Editor)](https://handbook.gitlab.com/handbook/product/personas/#eddie-content-editor)
+* [Amy (Application Security Engineer)](https://handbook.gitlab.com/handbook/product/personas/#amy-application-security-engineer)
+* [Isaac (Infrastructure Engineer)](https://handbook.gitlab.com/handbook/product/personas/#isaac-infrastructure-security-engineer)
+* [Alex (Security Operations Engineer)](https://handbook.gitlab.com/handbook/product/personas/#alex-security-operations-engineer)
+* [Cameron (Compliance Manager)](https://handbook.gitlab.com/handbook/product/personas/#cameron-compliance-manager)
-->
diff --git a/.gitlab/issue_templates/Documentation.md b/.gitlab/issue_templates/Documentation.md
index 82de14717a6..1a808d943e8 100644
--- a/.gitlab/issue_templates/Documentation.md
+++ b/.gitlab/issue_templates/Documentation.md
@@ -24,7 +24,7 @@
* Any concepts, procedures, reference info we could add to make it easier to successfully use GitLab?
* Include use cases, benefits, and/or goals for this work.
* If adding content: What audience is it intended for? (What roles and scenarios?)
- For ideas, see personas at https://about.gitlab.com/handbook/product/personas/ or the persona labels at
+ For ideas, see personas at https://handbook.gitlab.com/handbook/product/personas/ or the persona labels at
https://gitlab.com/groups/gitlab-org/-/labels?subscribed=&search=persona%3A
-->
diff --git a/.gitlab/issue_templates/Feature Proposal - lean.md b/.gitlab/issue_templates/Feature Proposal - lean.md
index 102c79aafd1..67a201b86d2 100644
--- a/.gitlab/issue_templates/Feature Proposal - lean.md
+++ b/.gitlab/issue_templates/Feature Proposal - lean.md
@@ -18,25 +18,25 @@ The goal of this template is brevity for quick/smaller iterations. For a more th
<!-- Who will use this feature? If known, include any of the following: types of users (e.g. Developer), personas, or specific company roles (e.g. Release Manager). It's okay to write "Unknown" and fill this field in later.
-Personas are described at https://about.gitlab.com/handbook/product/personas/
-
-* [Parker (Product Manager)](https://about.gitlab.com/handbook/product/personas/#parker-product-manager)
-* [Delaney (Development Team Lead)](https://about.gitlab.com/handbook/product/personas/#delaney-development-team-lead)
-* [Presley (Product Designer)](https://about.gitlab.com/handbook/product/personas/#presley-product-designer)
-* [Sasha (Software Developer)](https://about.gitlab.com/handbook/product/personas/#sasha-software-developer)
-* [Priyanka (Platform Engineer)](https://about.gitlab.com/handbook/product/personas/#priyanka-platform-engineer)
-* [Sidney (Systems Administrator)](https://about.gitlab.com/handbook/product/personas/#sidney-systems-administrator)
-* [Rachel (Release Manager)](https://about.gitlab.com/handbook/product/personas/#rachel-release-manager)
-* [Simone (Software Engineer in Test)](https://about.gitlab.com/handbook/product/personas/#simone-software-engineer-in-test)
-* [Allison (Application Ops)](https://about.gitlab.com/handbook/product/personas/#allison-application-ops)
-* [Ingrid (Infrastructure Operator)](https://about.gitlab.com/handbook/product/personas/#ingrid-infrastructure-operator)
-* [Dakota (Application Development Director)](https://about.gitlab.com/handbook/product/personas/#dakota-application-development-director)
-* [Dana (Data Analyst)](https://about.gitlab.com/handbook/product/personas/#dana-data-analyst)
-* [Eddie (Content Editor)](https://about.gitlab.com/handbook/product/personas/#eddie-content-editor)
-* [Amy (Application Security Engineer)](https://about.gitlab.com/handbook/product/personas/#amy-application-security-engineer)
-* [Isaac (Infrastructure Engineer)](https://about.gitlab.com/handbook/product/personas/#isaac-infrastructure-security-engineer)
-* [Alex (Security Operations Engineer)](https://about.gitlab.com/handbook/product/personas/#alex-security-operations-engineer)
-* [Cameron (Compliance Manager)](https://about.gitlab.com/handbook/product/personas/#cameron-compliance-manager)
+Personas are described at https://handbook.gitlab.com/handbook/product/personas/
+
+* [Parker (Product Manager)](https://handbook.gitlab.com/handbook/product/personas/#parker-product-manager)
+* [Delaney (Development Team Lead)](https://handbook.gitlab.com/handbook/product/personas/#delaney-development-team-lead)
+* [Presley (Product Designer)](https://handbook.gitlab.com/handbook/product/personas/#presley-product-designer)
+* [Sasha (Software Developer)](https://handbook.gitlab.com/handbook/product/personas/#sasha-software-developer)
+* [Priyanka (Platform Engineer)](https://handbook.gitlab.com/handbook/product/personas/#priyanka-platform-engineer)
+* [Sidney (Systems Administrator)](https://handbook.gitlab.com/handbook/product/personas/#sidney-systems-administrator)
+* [Rachel (Release Manager)](https://handbook.gitlab.com/handbook/product/personas/#rachel-release-manager)
+* [Simone (Software Engineer in Test)](https://handbook.gitlab.com/handbook/product/personas/#simone-software-engineer-in-test)
+* [Allison (Application Ops)](https://handbook.gitlab.com/handbook/product/personas/#allison-application-ops)
+* [Ingrid (Infrastructure Operator)](https://handbook.gitlab.com/handbook/product/personas/#ingrid-infrastructure-operator)
+* [Dakota (Application Development Director)](https://handbook.gitlab.com/handbook/product/personas/#dakota-application-development-director)
+* [Dana (Data Analyst)](https://handbook.gitlab.com/handbook/product/personas/#dana-data-analyst)
+* [Eddie (Content Editor)](https://handbook.gitlab.com/handbook/product/personas/#eddie-content-editor)
+* [Amy (Application Security Engineer)](https://handbook.gitlab.com/handbook/product/personas/#amy-application-security-engineer)
+* [Isaac (Infrastructure Engineer)](https://handbook.gitlab.com/handbook/product/personas/#isaac-infrastructure-security-engineer)
+* [Alex (Security Operations Engineer)](https://handbook.gitlab.com/handbook/product/personas/#alex-security-operations-engineer)
+* [Cameron (Compliance Manager)](https://handbook.gitlab.com/handbook/product/personas/#cameron-compliance-manager)
-->
### Feature Usage Metrics
diff --git a/.gitlab/issue_templates/Feature proposal - detailed.md b/.gitlab/issue_templates/Feature proposal - detailed.md
index 3d0a78f7f96..3161ac7addc 100644
--- a/.gitlab/issue_templates/Feature proposal - detailed.md
+++ b/.gitlab/issue_templates/Feature proposal - detailed.md
@@ -12,25 +12,25 @@
<!-- Who will use this feature? If known, include any of the following: types of users (e.g. Developer), personas, or specific company roles (e.g. Release Manager). It's okay to write "Unknown" and fill this field in later.
-Personas are described at https://about.gitlab.com/handbook/product/personas/
-
-* [Parker (Product Manager)](https://about.gitlab.com/handbook/product/personas/#parker-product-manager)
-* [Delaney (Development Team Lead)](https://about.gitlab.com/handbook/product/personas/#delaney-development-team-lead)
-* [Presley (Product Designer)](https://about.gitlab.com/handbook/product/personas/#presley-product-designer)
-* [Sasha (Software Developer)](https://about.gitlab.com/handbook/product/personas/#sasha-software-developer)
-* [Priyanka (Platform Engineer)](https://about.gitlab.com/handbook/product/personas/#priyanka-platform-engineer)
-* [Sidney (Systems Administrator)](https://about.gitlab.com/handbook/product/personas/#sidney-systems-administrator)
-* [Rachel (Release Manager)](https://about.gitlab.com/handbook/product/personas/#rachel-release-manager)
-* [Simone (Software Engineer in Test)](https://about.gitlab.com/handbook/product/personas/#simone-software-engineer-in-test)
-* [Allison (Application Ops)](https://about.gitlab.com/handbook/product/personas/#allison-application-ops)
-* [Ingrid (Infrastructure Operator)](https://about.gitlab.com/handbook/product/personas/#ingrid-infrastructure-operator)
-* [Dakota (Application Development Director)](https://about.gitlab.com/handbook/product/personas/#dakota-application-development-director)
-* [Dana (Data Analyst)](https://about.gitlab.com/handbook/product/personas/#dana-data-analyst)
-* [Eddie (Content Editor)](https://about.gitlab.com/handbook/product/personas/#eddie-content-editor)
-* [Amy (Application Security Engineer)](https://about.gitlab.com/handbook/product/personas/#amy-application-security-engineer)
-* [Isaac (Infrastructure Engineer)](https://about.gitlab.com/handbook/product/personas/#isaac-infrastructure-security-engineer)
-* [Alex (Security Operations Engineer)](https://about.gitlab.com/handbook/product/personas/#alex-security-operations-engineer)
-* [Cameron (Compliance Manager)](https://about.gitlab.com/handbook/product/personas/#cameron-compliance-manager)
+Personas are described at https://handbook.gitlab.com/handbook/product/personas/
+
+* [Parker (Product Manager)](https://handbook.gitlab.com/handbook/product/personas/#parker-product-manager)
+* [Delaney (Development Team Lead)](https://handbook.gitlab.com/handbook/product/personas/#delaney-development-team-lead)
+* [Presley (Product Designer)](https://handbook.gitlab.com/handbook/product/personas/#presley-product-designer)
+* [Sasha (Software Developer)](https://handbook.gitlab.com/handbook/product/personas/#sasha-software-developer)
+* [Priyanka (Platform Engineer)](https://handbook.gitlab.com/handbook/product/personas/#priyanka-platform-engineer)
+* [Sidney (Systems Administrator)](https://handbook.gitlab.com/handbook/product/personas/#sidney-systems-administrator)
+* [Rachel (Release Manager)](https://handbook.gitlab.com/handbook/product/personas/#rachel-release-manager)
+* [Simone (Software Engineer in Test)](https://handbook.gitlab.com/handbook/product/personas/#simone-software-engineer-in-test)
+* [Allison (Application Ops)](https://handbook.gitlab.com/handbook/product/personas/#allison-application-ops)
+* [Ingrid (Infrastructure Operator)](https://handbook.gitlab.com/handbook/product/personas/#ingrid-infrastructure-operator)
+* [Dakota (Application Development Director)](https://handbook.gitlab.com/handbook/product/personas/#dakota-application-development-director)
+* [Dana (Data Analyst)](https://handbook.gitlab.com/handbook/product/personas/#dana-data-analyst)
+* [Eddie (Content Editor)](https://handbook.gitlab.com/handbook/product/personas/#eddie-content-editor)
+* [Amy (Application Security Engineer)](https://handbook.gitlab.com/handbook/product/personas/#amy-application-security-engineer)
+* [Isaac (Infrastructure Engineer)](https://handbook.gitlab.com/handbook/product/personas/#isaac-infrastructure-security-engineer)
+* [Alex (Security Operations Engineer)](https://handbook.gitlab.com/handbook/product/personas/#alex-security-operations-engineer)
+* [Cameron (Compliance Manager)](https://handbook.gitlab.com/handbook/product/personas/#cameron-compliance-manager)
-->
@@ -110,7 +110,7 @@ Explore (../../doc/development/internal_analytics/internal_event_instrumentation
### What is the type of buyer?
-<!-- What is the buyer persona for this feature? See https://about.gitlab.com/handbook/product/personas/buyer-persona/
+<!-- What is the buyer persona for this feature? See https://handbook.gitlab.com/handbook/product/personas/buyer-persona/
In which enterprise tier should this feature go? See https://about.gitlab.com/handbook/product/pricing/#three-tiers -->
### Is this a cross-stage feature?
diff --git a/.gitlab/issue_templates/Fulfillment Group UX Issue.md b/.gitlab/issue_templates/Fulfillment Group UX Issue.md
index 55bbc35089d..ce8dd4dc1b8 100644
--- a/.gitlab/issue_templates/Fulfillment Group UX Issue.md
+++ b/.gitlab/issue_templates/Fulfillment Group UX Issue.md
@@ -6,25 +6,25 @@ The goal of this template is to ensure we have captured all the information avai
<!-- If known, include any of the following: types of users (e.g. Developer), personas, or specific company roles (e.g. Release Manager). It's okay to write "Unknown" and fill this field in later.
-Personas are described at https://about.gitlab.com/handbook/product/personas/
-
-* [Parker (Product Manager)](https://about.gitlab.com/handbook/product/personas/#parker-product-manager)
-* [Delaney (Development Team Lead)](https://about.gitlab.com/handbook/product/personas/#delaney-development-team-lead)
-* [Presley (Product Designer)](https://about.gitlab.com/handbook/product/personas/#presley-product-designer)
-* [Sasha (Software Developer)](https://about.gitlab.com/handbook/product/personas/#sasha-software-developer)
-* [Priyanka (Platform Engineer)](https://about.gitlab.com/handbook/product/personas/#priyanka-platform-engineer)
-* [Sidney (Systems Administrator)](https://about.gitlab.com/handbook/product/personas/#sidney-systems-administrator)
-* [Rachel (Release Manager)](https://about.gitlab.com/handbook/product/personas/#rachel-release-manager)
-* [Simone (Software Engineer in Test)](https://about.gitlab.com/handbook/product/personas/#simone-software-engineer-in-test)
-* [Allison (Application Ops)](https://about.gitlab.com/handbook/product/personas/#allison-application-ops)
-* [Ingrid (Infrastructure Operator)](https://about.gitlab.com/handbook/product/personas/#ingrid-infrastructure-operator)
-* [Dakota (Application Development Director)](https://about.gitlab.com/handbook/product/personas/#dakota-application-development-director)
-* [Dana (Data Analyst)](https://about.gitlab.com/handbook/product/personas/#dana-data-analyst)
-* [Eddie (Content Editor)](https://about.gitlab.com/handbook/product/personas/#eddie-content-editor)
-* [Amy (Application Security Engineer)](https://about.gitlab.com/handbook/product/personas/#amy-application-security-engineer)
-* [Isaac (Infrastructure Engineer)](https://about.gitlab.com/handbook/product/personas/#isaac-infrastructure-security-engineer)
-* [Alex (Security Operations Engineer)](https://about.gitlab.com/handbook/product/personas/#alex-security-operations-engineer)
-* [Cameron (Compliance Manager)](https://about.gitlab.com/handbook/product/personas/#cameron-compliance-manager)
+Personas are described at https://handbook.gitlab.com/handbook/product/personas/
+
+* [Parker (Product Manager)](https://handbook.gitlab.com/handbook/product/personas/#parker-product-manager)
+* [Delaney (Development Team Lead)](https://handbook.gitlab.com/handbook/product/personas/#delaney-development-team-lead)
+* [Presley (Product Designer)](https://handbook.gitlab.com/handbook/product/personas/#presley-product-designer)
+* [Sasha (Software Developer)](https://handbook.gitlab.com/handbook/product/personas/#sasha-software-developer)
+* [Priyanka (Platform Engineer)](https://handbook.gitlab.com/handbook/product/personas/#priyanka-platform-engineer)
+* [Sidney (Systems Administrator)](https://handbook.gitlab.com/handbook/product/personas/#sidney-systems-administrator)
+* [Rachel (Release Manager)](https://handbook.gitlab.com/handbook/product/personas/#rachel-release-manager)
+* [Simone (Software Engineer in Test)](https://handbook.gitlab.com/handbook/product/personas/#simone-software-engineer-in-test)
+* [Allison (Application Ops)](https://handbook.gitlab.com/handbook/product/personas/#allison-application-ops)
+* [Ingrid (Infrastructure Operator)](https://handbook.gitlab.com/handbook/product/personas/#ingrid-infrastructure-operator)
+* [Dakota (Application Development Director)](https://handbook.gitlab.com/handbook/product/personas/#dakota-application-development-director)
+* [Dana (Data Analyst)](https://handbook.gitlab.com/handbook/product/personas/#dana-data-analyst)
+* [Eddie (Content Editor)](https://handbook.gitlab.com/handbook/product/personas/#eddie-content-editor)
+* [Amy (Application Security Engineer)](https://handbook.gitlab.com/handbook/product/personas/#amy-application-security-engineer)
+* [Isaac (Infrastructure Engineer)](https://handbook.gitlab.com/handbook/product/personas/#isaac-infrastructure-security-engineer)
+* [Alex (Security Operations Engineer)](https://handbook.gitlab.com/handbook/product/personas/#alex-security-operations-engineer)
+* [Cameron (Compliance Manager)](https://handbook.gitlab.com/handbook/product/personas/#cameron-compliance-manager)
-->
diff --git a/.gitlab/issue_templates/Navigation Proposals.md b/.gitlab/issue_templates/Navigation Proposals.md
index 934cf440006..4ed7a7c7756 100644
--- a/.gitlab/issue_templates/Navigation Proposals.md
+++ b/.gitlab/issue_templates/Navigation Proposals.md
@@ -18,7 +18,7 @@
- [ ] Ensure your UI suggestion align with the [Documentation Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide/)
- [ ] Engage ~"Technical Writing". They can help craft a term that best describes the feature(s) you’re proposing.
- [ ] Follow the [product development workflow](https://about.gitlab.com/handbook/product-development-flow/#validation-phase-2-problem-validation) validation process to ensure you are solving a well understood problem and that the proposed change is understandable and non-disruptive to users. Navigation-specific research is mandatory for additions or when restructuring.
-- [ ] Engage the [Foundations Product Manager](https://about.gitlab.com/handbook/product/categories/#foundations-group) for approval. The Foundations DRI (@cdybenko) will work with UX partners in product design, research, and technical writing, as applicable.
+- [ ] Engage the [Foundations Product Manager](https://about.gitlab.com/handbook/product/categories/#foundations-group) for approval. The Foundations DRI (@jtucker_gl) will work with UX partners in product design, research, and technical writing, as applicable.
- [ ] Consider whether you need to [communicate the change somehow](https://design.gitlab.com/patterns/navigation#messaging-changes-to-users), or if you will have an interim period in the UI where your item will live in more than one place.
- [ ] Ensure engineers are familiar with the [implementation steps for navigation](https://docs.gitlab.com/ee/development/navigation_sidebar.html#navigation-sidebar).
diff --git a/.gitlab/issue_templates/Pipeline Security issue implementation.md b/.gitlab/issue_templates/Pipeline Security issue implementation.md
index 72807e98012..3ecf6ef57db 100644
--- a/.gitlab/issue_templates/Pipeline Security issue implementation.md
+++ b/.gitlab/issue_templates/Pipeline Security issue implementation.md
@@ -23,7 +23,7 @@ A user story is a requirement for any functionality or feature and follows this
- _As a `<user role/customer>`, I want to `<JTBD>` so that I can `<achieve a benefit or result>`._
-Please try to include one user story for the main [persona](https://about.gitlab.com/handbook/product/personas/#list-of-user-personas) who needs this feature.
+Please try to include one user story for the main [persona](https://handbook.gitlab.com/handbook/product/personas/#list-of-user-personas) who needs this feature.
-->
diff --git a/.gitlab/issue_templates/Problem Validation.md b/.gitlab/issue_templates/Problem Validation.md
index dcd2283f4b6..1fc76fbaa29 100644
--- a/.gitlab/issue_templates/Problem Validation.md
+++ b/.gitlab/issue_templates/Problem Validation.md
@@ -8,7 +8,7 @@ Learn more about it in the handbook: https://about.gitlab.com/handbook/product-d
## Reach
-<!-- Please describe who suffers from this problem. Consider referring to our personas, which are described at https://about.gitlab.com/handbook/product/personas/ -->
+<!-- Please describe who suffers from this problem. Consider referring to our personas, which are described at https://handbook.gitlab.com/handbook/product/personas/ -->
<!-- Please also quantify the problem's reach using the following values, considering an aggregate across GitLab.com and self-managed:
diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml
index 378c8adc69c..17612d463e8 100644
--- a/.rubocop_todo/layout/line_length.yml
+++ b/.rubocop_todo/layout/line_length.yml
@@ -2681,7 +2681,6 @@ Layout/LineLength:
- 'lib/gitlab/usage_data.rb'
- 'lib/gitlab/usage_data/topology.rb'
- 'lib/gitlab/usage_data_counters/base_counter.rb'
- - 'lib/gitlab/usage_data_counters/editor_unique_counter.rb'
- 'lib/gitlab/usage_data_counters/gitlab_cli_activity_unique_counter.rb'
- 'lib/gitlab/usage_data_counters/hll_redis_counter.rb'
- 'lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb'
diff --git a/.rubocop_todo/rspec/feature_category.yml b/.rubocop_todo/rspec/feature_category.yml
index df29ac4c8bd..71ac3caba14 100644
--- a/.rubocop_todo/rspec/feature_category.yml
+++ b/.rubocop_todo/rspec/feature_category.yml
@@ -4052,7 +4052,6 @@ RSpec/FeatureCategory:
- 'spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/cycle_analytics_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/designs_counter_spec.rb'
- - 'spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/gitlab_cli_activity_unique_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter_spec.rb'
diff --git a/.rubocop_todo/style/keyword_parameters_order.yml b/.rubocop_todo/style/keyword_parameters_order.yml
index 8f27a2c9f45..567796e6906 100644
--- a/.rubocop_todo/style/keyword_parameters_order.yml
+++ b/.rubocop_todo/style/keyword_parameters_order.yml
@@ -19,5 +19,4 @@ Style/KeywordParametersOrder:
- 'lib/gitlab/email/smime/signer.rb'
- 'lib/gitlab/exclusive_lease.rb'
- 'lib/gitlab/merge_requests/mergeability/results_store.rb'
- - 'lib/gitlab/usage_data_counters/editor_unique_counter.rb'
- 'lib/microsoft_teams/notifier.rb'
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 978e6731e9d..50266e2c434 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -171,14 +171,10 @@ export default {
return true;
}
- return (
- !this.sastReportsInInlineDiff ||
- (!this.codequalityReportAvailable && !this.sastReportAvailable)
- );
+ return !this.codequalityReportAvailable && !this.sastReportAvailable;
},
update(data) {
const { codequalityReportsComparer, sastReport } = data?.project?.mergeRequest || {};
-
this.activeProject = data?.project?.mergeRequest?.project;
if (
(sastReport?.status === FINDINGS_STATUS_PARSED || !this.sastReportAvailable) &&
@@ -307,9 +303,6 @@ export default {
resourceId() {
return convertToGraphQLId('MergeRequest', this.getNoteableData.id);
},
- sastReportsInInlineDiff() {
- return this.glFeatures.sastReportsInInlineDiff;
- },
},
watch: {
commit(newCommit, oldCommit) {
@@ -347,10 +340,6 @@ export default {
isLoading: 'adjustView',
},
mounted() {
- if (this.endpointCodequality) {
- this.setCodequalityEndpoint(this.endpointCodequality);
- }
-
if (this.shouldShow) {
this.fetchData();
}
@@ -423,14 +412,12 @@ export default {
...mapActions(['startTaskList']),
...mapActions('diffs', [
'moveToNeighboringCommit',
- 'setCodequalityEndpoint',
'fetchDiffFilesMeta',
'fetchDiffFilesBatch',
'fetchFileByFile',
'loadCollapsedDiff',
'setFileForcedOpen',
'fetchCoverageFiles',
- 'fetchCodequality',
'rereadNoteHash',
'startRenderDiffsQueue',
'assignDiscussionsToDiff',
@@ -576,10 +563,6 @@ export default {
this.fetchCoverageFiles();
}
- if (this.endpointCodequality && !this.sastReportsInInlineDiff) {
- this.fetchCodequality();
- }
-
if (!this.isNotesFetched) {
notesEventHub.$emit('fetchNotesData');
}
diff --git a/app/assets/javascripts/diffs/components/diff_inline_findings.vue b/app/assets/javascripts/diffs/components/diff_inline_findings.vue
deleted file mode 100644
index 59f92040776..00000000000
--- a/app/assets/javascripts/diffs/components/diff_inline_findings.vue
+++ /dev/null
@@ -1,32 +0,0 @@
-<script>
-import DiffInlineFindingsItem from './diff_inline_findings_item.vue';
-
-export default {
- components: { DiffInlineFindingsItem },
- props: {
- title: {
- type: String,
- required: true,
- },
- findings: {
- type: Array,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <div>
- <h4 data-testid="diff-inline-findings-heading" class="gl-my-0 gl-font-base gl-font-regular">
- {{ title }}
- </h4>
- <ul class="gl-list-style-none gl-mb-0 gl-p-0">
- <diff-inline-findings-item
- v-for="finding in findings"
- :key="finding.description"
- :finding="finding"
- />
- </ul>
- </div>
-</template>
diff --git a/app/assets/javascripts/diffs/components/diff_inline_findings_item.vue b/app/assets/javascripts/diffs/components/diff_inline_findings_item.vue
deleted file mode 100644
index 8a30d6ee7f8..00000000000
--- a/app/assets/javascripts/diffs/components/diff_inline_findings_item.vue
+++ /dev/null
@@ -1,39 +0,0 @@
-<script>
-import { GlIcon } from '@gitlab/ui';
-import { getSeverity } from '~/ci/reports/utils';
-
-export default {
- components: { GlIcon },
-
- props: {
- finding: {
- type: Object,
- required: true,
- },
- },
- computed: {
- enhancedFinding() {
- return getSeverity(this.finding);
- },
- listText() {
- return `${this.finding.severity} - ${this.finding.description}`;
- },
- },
-};
-</script>
-
-<template>
- <li class="gl-py-1 gl-font-regular gl-display-flex">
- <span class="gl-mr-3">
- <gl-icon
- :size="12"
- :name="enhancedFinding.name"
- :class="enhancedFinding.class"
- class="inline-findings-severity-icon"
- />
- </span>
- <span data-testid="description-plain-text" class="gl-display-flex">
- {{ listText }}
- </span>
- </li>
-</template>
diff --git a/app/assets/javascripts/diffs/components/diff_line.vue b/app/assets/javascripts/diffs/components/diff_line.vue
deleted file mode 100644
index 4867a21493f..00000000000
--- a/app/assets/javascripts/diffs/components/diff_line.vue
+++ /dev/null
@@ -1,32 +0,0 @@
-<script>
-import InlineFindings from './inline_findings.vue';
-
-export default {
- components: {
- InlineFindings,
- },
- props: {
- line: {
- type: Object,
- required: true,
- },
- },
- computed: {
- parsedCodeQuality() {
- return (this.line.left ?? this.line.right)?.codequality;
- },
- codeQualityLineNumber() {
- return this.parsedCodeQuality[0]?.line;
- },
- },
- methods: {
- hideInlineFindings() {
- this.$emit('hideInlineFindings', this.codeQualityLineNumber);
- },
- },
-};
-</script>
-
-<template>
- <inline-findings :code-quality="parsedCodeQuality" @hideInlineFindings="hideInlineFindings" />
-</template>
diff --git a/app/assets/javascripts/diffs/components/diff_row.vue b/app/assets/javascripts/diffs/components/diff_row.vue
index a9da77104d4..58049929ac8 100644
--- a/app/assets/javascripts/diffs/components/diff_row.vue
+++ b/app/assets/javascripts/diffs/components/diff_row.vue
@@ -24,8 +24,8 @@ import * as utils from './diff_row_utils';
export default {
DiffGutterAvatars,
- InlineFindingsFlagSwitcher: () =>
- import('ee_component/diffs/components/inline_findings_flag_switcher.vue'),
+ InlineFindingsGutterIconDropdown: () =>
+ import('ee_component/diffs/components/inline_findings_gutter_icon_dropdown.vue'),
// Temporary mixin for migration from Vue.js 2 to @vue/compat
mixins: [compatFunctionalMixin],
@@ -80,11 +80,6 @@ export default {
type: Function,
required: true,
},
- inlineFindingsExpanded: {
- type: Boolean,
- required: false,
- default: false,
- },
},
classNameMap: memoize(
(props) => {
@@ -336,9 +331,8 @@ export default {
:class="$options.parallelViewLeftLineType(props)"
>
<component
- :is="$options.InlineFindingsFlagSwitcher"
+ :is="$options.InlineFindingsGutterIconDropdown"
v-if="$options.showCodequalityLeft(props) || $options.showSecurityLeft(props)"
- :inline-findings-expanded="props.inlineFindingsExpanded"
:code-quality="props.line.left.codequality"
:sast="props.line.left.sast"
:file-path="props.filePath"
@@ -478,7 +472,7 @@ export default {
:class="$options.classNameMapCellRight(props)"
>
<component
- :is="$options.InlineFindingsFlagSwitcher"
+ :is="$options.InlineFindingsGutterIconDropdown"
v-if="$options.showCodequalityRight(props) || $options.showSecurityRight(props)"
:code-quality="props.line.right.codequality"
:sast="props.line.right.sast"
diff --git a/app/assets/javascripts/diffs/components/diff_view.vue b/app/assets/javascripts/diffs/components/diff_view.vue
index 641616a34f5..83984cdf0d0 100644
--- a/app/assets/javascripts/diffs/components/diff_view.vue
+++ b/app/assets/javascripts/diffs/components/diff_view.vue
@@ -10,7 +10,6 @@ import { getCommentedLines } from '~/notes/components/multiline_comment_utils';
import { hide } from '~/tooltips';
import { pickDirection } from '../utils/diff_line';
import DiffCommentCell from './diff_comment_cell.vue';
-import DiffLine from './diff_line.vue';
import DiffExpansionCell from './diff_expansion_cell.vue';
import DiffRow from './diff_row.vue';
import { isHighlighted } from './diff_row_utils';
@@ -19,7 +18,6 @@ export default {
components: {
DiffExpansionCell,
DiffRow,
- DiffLine,
DiffCommentCell,
DraftNote,
},
@@ -48,11 +46,6 @@ export default {
default: false,
},
},
- data() {
- return {
- inlineFindingsExpandedLines: [],
- };
- },
idState() {
return {
dragStart: null,
@@ -87,9 +80,6 @@ export default {
this.sastDiff?.added?.length > 0
);
},
- sastReportsInInlineDiff() {
- return this.glFeatures.sastReportsInInlineDiff;
- },
},
created() {
this.onDragOverThrottled = throttle((line) => this.onDragOver(line), 100, { leading: true });
@@ -109,19 +99,6 @@ export default {
}
this.idState.dragStart = line;
},
- hideInlineFindings(line) {
- const index = this.inlineFindingsExpandedLines.indexOf(line);
- if (index > -1) {
- this.inlineFindingsExpandedLines.splice(index, 1);
- }
- },
- toggleCodeQualityFindings(line) {
- if (!this.inlineFindingsExpandedLines.includes(line)) {
- this.inlineFindingsExpandedLines.push(line);
- } else {
- this.hideInlineFindings(line);
- }
- },
onDragOver(line) {
if (line.chunk !== this.idState.dragStart.chunk) return;
@@ -266,12 +243,10 @@ export default {
:is-last-highlighted-line="isLastHighlightedLine(line) || index === commentedLines.endLine"
:inline="inline"
:index="index"
- :inline-findings-expanded="inlineFindingsExpandedLines.includes(getCodeQualityLine(line))"
:file-line-coverage="fileLineCoverage"
:coverage-loaded="coverageLoaded"
@showCommentForm="(code) => singleLineComment(code, line)"
@setHighlightedRow="setHighlightedRow"
- @toggleCodeQualityFindings="toggleCodeQualityFindings"
@toggleLineDiscussions="
({ lineCode, expanded }) =>
toggleLineDiscussions({ lineCode, fileHash: diffFile.file_hash, expanded })
@@ -280,15 +255,6 @@ export default {
@startdragging="onStartDragging"
@stopdragging="onStopDragging"
/>
- <!-- Don't display InlineFindings expanded section when sastReportsInInlineDiff is false -->
- <diff-line
- v-if="
- inlineFindingsExpandedLines.includes(getCodeQualityLine(line)) && !sastReportsInInlineDiff
- "
- :key="line.line_code"
- :line="line"
- @hideInlineFindings="hideInlineFindings"
- />
<div
v-if="line.renderCommentRow"
:key="`dcr-${line.line_code || index}`"
diff --git a/app/assets/javascripts/diffs/components/inline_findings.vue b/app/assets/javascripts/diffs/components/inline_findings.vue
deleted file mode 100644
index efceedd1141..00000000000
--- a/app/assets/javascripts/diffs/components/inline_findings.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-<script>
-import { GlButton } from '@gitlab/ui';
-import { NEW_CODE_QUALITY_FINDINGS, NEW_SAST_FINDINGS } from '../i18n';
-import DiffInlineFindings from './diff_inline_findings.vue';
-
-export default {
- i18n: {
- newCodeQualityFindings: NEW_CODE_QUALITY_FINDINGS,
- newSastFindings: NEW_SAST_FINDINGS,
- },
- components: { GlButton, DiffInlineFindings },
- props: {
- codeQuality: {
- type: Array,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <div
- data-testid="inline-findings"
- class="gl-relative inline-findings-list gl-border-top-1 gl-border-bottom-1 gl-bg-gray-10 gl-text-black-normal gl-pl-5 gl-pt-4 gl-pb-4"
- >
- <diff-inline-findings
- v-if="codeQuality.length"
- :title="$options.i18n.newCodeQualityFindings"
- :findings="codeQuality"
- />
-
- <gl-button
- data-testid="inline-findings-close"
- category="tertiary"
- size="small"
- icon="close"
- class="gl-absolute gl-right-2 gl-top-2"
- @click="$emit('hideInlineFindings')"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js
index 303f010e56f..9acfda93303 100644
--- a/app/assets/javascripts/diffs/index.js
+++ b/app/assets/javascripts/diffs/index.js
@@ -34,7 +34,6 @@ export default function initDiffsApp(store = notesStore) {
projectPath: dataset.projectPath || '',
iid: dataset.iid || '',
endpointCoverage: dataset.endpointCoverage || '',
- endpointCodequality: dataset.endpointCodequality || '',
codequalityReportAvailable: parseBoolean(dataset.codequalityReportAvailable),
sastReportAvailable: parseBoolean(dataset.sastReportAvailable),
helpPagePath: dataset.helpPagePath,
diff --git a/app/assets/javascripts/diffs/store/modules/index.js b/app/assets/javascripts/diffs/store/modules/index.js
index 169502a957b..95371eb8dbd 100644
--- a/app/assets/javascripts/diffs/store/modules/index.js
+++ b/app/assets/javascripts/diffs/store/modules/index.js
@@ -1,7 +1,7 @@
-import * as actions from 'ee_else_ce/diffs/store/actions';
import * as getters from 'ee_else_ce/diffs/store/getters';
import createState from 'ee_else_ce/diffs/store/modules/diff_state';
import mutations from 'ee_else_ce/diffs/store/mutations';
+import * as actions from '../actions';
export default () => ({
namespaced: true,
diff --git a/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue b/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
index b61b2bdd0c9..a318d013228 100644
--- a/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
+++ b/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSorting, GlSortingItem } from '@gitlab/ui';
+import { GlSorting } from '@gitlab/ui';
// eslint-disable-next-line no-restricted-imports
import { mapState } from 'vuex';
import { visitUrl } from '~/lib/utils/url_utility';
@@ -9,7 +9,7 @@ import { SORT_DIRECTION_UI } from '~/search/sort/constants';
export default {
name: 'SortDropdown',
- components: { GlSorting, GlSortingItem },
+ components: { GlSorting },
inject: ['namespace'],
computed: {
...mapState({
@@ -29,6 +29,9 @@ export default {
activeOptionLabel() {
return this.activeOption?.label;
},
+ activeOptionKey() {
+ return this.activeOption?.key;
+ },
isAscending() {
return !this.sort.sortDesc;
},
@@ -39,14 +42,8 @@ export default {
return FIELDS.filter(
(field) => this.tableSortableFields.includes(field.key) && field.sort,
).map((field) => ({
- key: field.key,
- label: field.label,
- href: buildSortHref({
- sortBy: field.key,
- sortDesc: false,
- filteredSearchBarTokens: this.filteredSearchBar.tokens,
- filteredSearchBarSearchParam: this.filteredSearchBar.searchParam,
- }),
+ text: field.label,
+ value: field.key,
}));
},
},
@@ -64,6 +61,16 @@ export default {
}),
);
},
+ handleSortingItemClick(value) {
+ visitUrl(
+ buildSortHref({
+ sortBy: value,
+ sortDesc: false,
+ filteredSearchBarTokens: this.filteredSearchBar.tokens,
+ filteredSearchBarSearchParam: this.filteredSearchBar.searchParam,
+ }),
+ );
+ },
},
};
</script>
@@ -76,15 +83,9 @@ export default {
:text="activeOptionLabel"
:is-ascending="isAscending"
:sort-direction-tool-tip="sortDirectionData.tooltip"
+ :sort-options="filteredOptions"
+ :sort-by="activeOptionKey"
+ @sortByChange="handleSortingItemClick"
@sortDirectionChange="handleSortDirectionChange"
- >
- <gl-sorting-item
- v-for="option in filteredOptions"
- :key="option.key"
- :href="option.href"
- :active="isActive(option.key)"
- >
- {{ option.label }}
- </gl-sorting-item>
- </gl-sorting>
+ />
</template>
diff --git a/app/assets/javascripts/vue_shared/components/registry/title_area.vue b/app/assets/javascripts/vue_shared/components/registry/title_area.vue
index 2db56343210..362840125b5 100644
--- a/app/assets/javascripts/vue_shared/components/registry/title_area.vue
+++ b/app/assets/javascripts/vue_shared/components/registry/title_area.vue
@@ -127,5 +127,6 @@ export default {
</gl-sprintf>
</span>
</p>
+ <slot></slot>
</div>
</template>
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index c3faec4c8ec..c937c1e83b6 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -40,7 +40,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
before_action only: [:show, :diffs] do
push_frontend_feature_flag(:core_security_mr_widget_counts, project)
push_frontend_feature_flag(:moved_mr_sidebar, project)
- push_frontend_feature_flag(:sast_reports_in_inline_diff, project)
push_frontend_feature_flag(:mr_experience_survey, project)
push_frontend_feature_flag(:ci_job_failures_in_mr, project)
push_frontend_feature_flag(:mr_pipelines_graphql, project)
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 29b0cb4c018..23c5e2ad28f 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -87,15 +87,15 @@ class ProjectsController < Projects::ApplicationController
@parent_group = Group.find_by(id: params[:namespace_id])
- manageable_groups_count = current_user.manageable_groups(include_groups_with_developer_maintainer_access: true).count
+ manageable_groups = ::Groups::AcceptingProjectCreationsFinder.new(current_user).execute.limit(2)
- if manageable_groups_count == 0 && !can?(current_user, :create_projects, current_user.namespace)
+ if manageable_groups.empty? && !can?(current_user, :create_projects, current_user.namespace)
return access_denied!
end
@current_user_group =
- if manageable_groups_count == 1
- current_user.manageable_groups(include_groups_with_developer_maintainer_access: true).first
+ if manageable_groups.one?
+ manageable_groups.first
end
@project = Project.new(namespace_id: @namespace&.id)
diff --git a/app/finders/groups/accepting_project_creations_finder.rb b/app/finders/groups/accepting_project_creations_finder.rb
index 76463086943..ad942d84096 100644
--- a/app/finders/groups/accepting_project_creations_finder.rb
+++ b/app/finders/groups/accepting_project_creations_finder.rb
@@ -11,9 +11,9 @@ module Groups
[
current_user
.manageable_groups(include_groups_with_developer_maintainer_access: true)
- .project_creation_allowed,
+ .project_creation_allowed(current_user),
owner_maintainer_groups_originating_from_group_shares
- .project_creation_allowed,
+ .project_creation_allowed(current_user),
*developer_groups_originating_from_group_shares
]
diff --git a/app/finders/groups/accepting_project_transfers_finder.rb b/app/finders/groups/accepting_project_transfers_finder.rb
index a3f58a78eca..ff19171ddc1 100644
--- a/app/finders/groups/accepting_project_transfers_finder.rb
+++ b/app/finders/groups/accepting_project_transfers_finder.rb
@@ -15,7 +15,7 @@ module Groups
groups = ::Group.from_union(groups_accepting_project_transfers)
- groups.project_creation_allowed
+ groups.project_creation_allowed(current_user)
end
private
diff --git a/app/graphql/mutations/snippets/create.rb b/app/graphql/mutations/snippets/create.rb
index 1c7dbfa751d..a5d97022198 100644
--- a/app/graphql/mutations/snippets/create.rb
+++ b/app/graphql/mutations/snippets/create.rb
@@ -53,7 +53,11 @@ module Mutations
# Only when the user is not an api user and the operation was successful
if !api_user? && service_response.success?
- ::Gitlab::UsageDataCounters::EditorUniqueCounter.track_snippet_editor_edit_action(author: current_user, project: project)
+ Gitlab::InternalEvents.track_event(
+ 'g_edit_by_snippet_ide',
+ user: current_user,
+ project: project
+ )
end
snippet = service_response.payload[:snippet]
diff --git a/app/graphql/mutations/snippets/update.rb b/app/graphql/mutations/snippets/update.rb
index 7faf9cf9019..3e241a8cf2b 100644
--- a/app/graphql/mutations/snippets/update.rb
+++ b/app/graphql/mutations/snippets/update.rb
@@ -42,7 +42,11 @@ module Mutations
# Only when the user is not an api user and the operation was successful
if !api_user? && service_response.success?
- ::Gitlab::UsageDataCounters::EditorUniqueCounter.track_snippet_editor_edit_action(author: current_user, project: snippet.project)
+ Gitlab::InternalEvents.track_event(
+ 'g_edit_by_snippet_ide',
+ user: current_user,
+ project: snippet.project
+ )
end
snippet = service_response.payload[:snippet]
diff --git a/app/models/group.rb b/app/models/group.rb
index 1d2cadc24f3..e44618f7a33 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -211,7 +211,11 @@ class Group < Namespace
where(project_creation_level: project_creation_levels)
end
- scope :project_creation_allowed, -> do
+ scope :excluding_restricted_visibility_levels_for_user, -> (user) do
+ user.can_admin_all_resources? ? all : where.not(visibility_level: Gitlab::CurrentSettings.restricted_visibility_levels)
+ end
+
+ scope :project_creation_allowed, -> (user) do
project_creation_allowed_on_levels = [
::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS,
::Gitlab::Access::MAINTAINER_PROJECT_ACCESS,
@@ -227,7 +231,7 @@ class Group < Namespace
project_creation_allowed_on_levels.delete(nil)
end
- with_project_creation_levels(project_creation_allowed_on_levels)
+ with_project_creation_levels(project_creation_allowed_on_levels).excluding_restricted_visibility_levels_for_user(user)
end
scope :shared_into_ancestors, -> (group) do
diff --git a/app/models/projects/branch_rule.rb b/app/models/projects/branch_rule.rb
index a32e981f791..a3e502faa38 100644
--- a/app/models/projects/branch_rule.rb
+++ b/app/models/projects/branch_rule.rb
@@ -2,12 +2,21 @@
module Projects
class BranchRule
+ include GlobalID::Identification
extend Forwardable
attr_reader :project, :protected_branch
alias_method :branch_protection, :protected_branch
- def_delegators(:protected_branch, :name, :group, :default_branch?, :created_at, :updated_at)
+ def_delegators(:protected_branch, :id, :name, :group, :default_branch?, :created_at, :updated_at)
+
+ def self.find(id)
+ protected_branch = ProtectedBranch.find(id)
+
+ new(protected_branch.project, protected_branch)
+ rescue ActiveRecord::RecordNotFound
+ raise ActiveRecord::RecordNotFound, "Couldn't find Projects::BranchRule with 'id'=#{id}"
+ end
def initialize(project, protected_branch)
@project = project
diff --git a/app/views/notify/new_review_email.html.haml b/app/views/notify/new_review_email.html.haml
index 5b870fe2214..2d98026f616 100644
--- a/app/views/notify/new_review_email.html.haml
+++ b/app/views/notify/new_review_email.html.haml
@@ -2,41 +2,16 @@
= content_for :head do
= stylesheet_link_tag 'mailers/highlighted_diff_email'
-- if Feature.enabled?(:enhanced_review_email, @project, type: :gitlab_com_derisk)
- %div{ style: "color:#333333;border-bottom:8px solid #ededed;font-weight:bold;line-height:1.4;padding: 20px 0;" }
- - mr_link = link_to(@merge_request.to_reference(@project), project_merge_request_url(@project, @merge_request))
- - mr_author_link = link_to(@author_name, user_url(@author))
- = _('Merge request %{mr_link} was reviewed by %{mr_author}').html_safe % { mr_link: mr_link, mr_author: mr_author_link }
+%div{ style: "color:#333333;border-bottom:8px solid #ededed;font-weight:bold;line-height:1.4;padding: 20px 0;" }
+ - mr_link = link_to(@merge_request.to_reference(@project), project_merge_request_url(@project, @merge_request))
+ - mr_author_link = link_to(@author_name, user_url(@author))
+ = _('Merge request %{mr_link} was reviewed by %{mr_author}').html_safe % { mr_link: mr_link, mr_author: mr_author_link }
- - @notes.each do |note|
- -# Get preloaded note discussion
- - discussion = @discussions[note.discussion_id] if note.part_of_discussion?
- -# Preload project for discussions first note
- - discussion.first_note.project = @project if discussion&.first_note
- - target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{note.id}")
- = render 'note_email', note: note, diff_limit: 3, target_url: target_url, note_style: "border-bottom:4px solid #ededed; padding-bottom: 1em;", include_stylesheet_link: false, discussion: discussion, author: @author
- = render_if_exists 'notify/review_summary'
-
-- else
-
- %table{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;margin:0 auto;border-collapse:separate;border-spacing:0;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;overflow:hidden;" }
- %table{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
- %tbody
- %tr
- %td{ style: "color:#333333;border-bottom:1px solid #ededed;font-weight:bold;line-height:1.4;padding: 20px 0;" }
- - mr_link = link_to(@merge_request.to_reference(@project), project_merge_request_url(@project, @merge_request))
- - mr_author_link = link_to(@author_name, user_url(@author))
- = _('Merge request %{mr_link} was reviewed by %{mr_author}').html_safe % { mr_link: mr_link, mr_author: mr_author_link }
- %tr
- %td{ style: "overflow:hidden;line-height:1.4;display:grid;" }
- - @notes.each do |note|
- -# Get preloaded note discussion
- - discussion = @discussions[note.discussion_id] if note.part_of_discussion?
- -# Preload project for discussions first note
- - discussion.first_note.project = @project if discussion&.first_note
- - target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{note.id}")
- = render 'note_email', note: note, diff_limit: 3, target_url: target_url, note_style: "border-bottom:1px solid #ededed; padding-bottom: 1em;", include_stylesheet_link: false, discussion: discussion, author: @author
- = render_if_exists 'notify/review_summary'
+- @notes.each do |note|
+ -# Get preloaded note discussion
+ - discussion = @discussions[note.discussion_id] if note.part_of_discussion?
+ -# Preload project for discussions first note
+ - discussion.first_note.project = @project if discussion&.first_note
+ - target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{note.id}")
+ = render 'note_email', note: note, diff_limit: 3, target_url: target_url, note_style: "border-bottom:4px solid #ededed; padding-bottom: 1em;", include_stylesheet_link: false, discussion: discussion, author: @author
+= render_if_exists 'notify/review_summary'
diff --git a/app/workers/click_house/event_paths_consistency_cron_worker.rb b/app/workers/click_house/event_paths_consistency_cron_worker.rb
index 71a1fb0995f..e115aa6b7e9 100644
--- a/app/workers/click_house/event_paths_consistency_cron_worker.rb
+++ b/app/workers/click_house/event_paths_consistency_cron_worker.rb
@@ -83,13 +83,13 @@ module ClickHouse
paths = id_paths.map(&:second).map { |value| "'#{value}'" }.join(',')
query = ClickHouse::Client::Query.new(
- raw_query: "DELETE FROM events WHERE path IN (#{paths})"
+ raw_query: "ALTER TABLE events DELETE WHERE path IN (#{paths})"
)
connection.execute(query)
query = ClickHouse::Client::Query.new(
- raw_query: 'DELETE FROM event_namespace_paths WHERE namespace_id IN ({namespace_ids:Array(UInt64)})',
+ raw_query: 'ALTER TABLE event_namespace_paths DELETE WHERE namespace_id IN ({namespace_ids:Array(UInt64)})',
placeholders: { namespace_ids: id_paths.map(&:first).to_json }
)
diff --git a/config/feature_flags/gitlab_com_derisk/enhanced_review_email.yml b/config/feature_flags/wip/redis_hll_property_name_tracking.yml
index 71bdc424ec2..853eb5ce96c 100644
--- a/config/feature_flags/gitlab_com_derisk/enhanced_review_email.yml
+++ b/config/feature_flags/wip/redis_hll_property_name_tracking.yml
@@ -1,9 +1,8 @@
---
-name: enhanced_review_email
-feature_issue_url:
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141187
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/437582
-milestone: '16.8'
-group: group::code review
-type: gitlab_com_derisk
+name: redis_hll_property_name_tracking
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137890
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/432866
+milestone: '16.9'
+type: wip
+group: group::analytics instrumentation
default_enabled: false
diff --git a/config/initializers/7_redis.rb b/config/initializers/7_redis.rb
index 0b908ce4b65..cac21aea9f9 100644
--- a/config/initializers/7_redis.rb
+++ b/config/initializers/7_redis.rb
@@ -29,6 +29,7 @@ Redis::Cluster.prepend(Gitlab::Patch::RedisCluster)
# this only instruments `RedisClient` used in `Sidekiq.redis`
RedisClient.register(Gitlab::Instrumentation::RedisClientMiddleware)
+RedisClient.prepend(Gitlab::Patch::RedisClient)
if Gitlab::Redis::Workhorse.params[:cluster].present?
raise "Do not configure workhorse with a Redis Cluster as pub/sub commands are not cluster-compatible."
diff --git a/db/docs/catalog_resource_components.yml b/db/docs/catalog_resource_components.yml
index 122e2f14265..0aa0d973bad 100644
--- a/db/docs/catalog_resource_components.yml
+++ b/db/docs/catalog_resource_components.yml
@@ -1,7 +1,16 @@
+---
table_name: catalog_resource_components
feature_categories:
- pipeline_composition
description: CI component available in the CI Catalog
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127775
milestone: '16.3'
-gitlab_schema: gitlab_main
+gitlab_schema: gitlab_main_cell
+allow_cross_joins:
+- gitlab_main_clusterwide
+allow_cross_transactions:
+- gitlab_main_clusterwide
+allow_cross_foreign_keys:
+- gitlab_main_clusterwide
+sharding_key:
+ project_id: projects
diff --git a/db/docs/catalog_resource_versions.yml b/db/docs/catalog_resource_versions.yml
index f01dcd8a2d6..9eead9c4eef 100644
--- a/db/docs/catalog_resource_versions.yml
+++ b/db/docs/catalog_resource_versions.yml
@@ -5,4 +5,12 @@ feature_categories:
description: Catalog resource versions that contain valid CI components.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124668
milestone: '16.2'
-gitlab_schema: gitlab_main
+gitlab_schema: gitlab_main_cell
+allow_cross_joins:
+- gitlab_main_clusterwide
+allow_cross_transactions:
+- gitlab_main_clusterwide
+allow_cross_foreign_keys:
+- gitlab_main_clusterwide
+sharding_key:
+ project_id: projects
diff --git a/db/docs/catalog_resources.yml b/db/docs/catalog_resources.yml
index 5bc689c308e..416463ed9fe 100644
--- a/db/docs/catalog_resources.yml
+++ b/db/docs/catalog_resources.yml
@@ -5,4 +5,12 @@ feature_categories:
description: Projects containing a catalog resource.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112482
milestone: '15.10'
-gitlab_schema: gitlab_main
+gitlab_schema: gitlab_main_cell
+allow_cross_joins:
+- gitlab_main_clusterwide
+allow_cross_transactions:
+- gitlab_main_clusterwide
+allow_cross_foreign_keys:
+- gitlab_main_clusterwide
+sharding_key:
+ project_id: projects
diff --git a/db/post_migrate/20240117070119_remove_index_protected_environments_on_project_id.rb b/db/post_migrate/20240117070119_remove_index_protected_environments_on_project_id.rb
new file mode 100644
index 00000000000..dc66a7bb84c
--- /dev/null
+++ b/db/post_migrate/20240117070119_remove_index_protected_environments_on_project_id.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class RemoveIndexProtectedEnvironmentsOnProjectId < Gitlab::Database::Migration[2.2]
+ disable_ddl_transaction!
+
+ milestone '16.9'
+
+ INDEX_NAME = 'index_protected_environments_on_project_id'
+
+ def up
+ remove_concurrent_index_by_name :protected_environments, name: INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :protected_environments, :project_id, name: INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20240117070119 b/db/schema_migrations/20240117070119
new file mode 100644
index 00000000000..a9909fe3d11
--- /dev/null
+++ b/db/schema_migrations/20240117070119
@@ -0,0 +1 @@
+1becea9a8277072f40d0fa7325338ae92e698b39e7c84b1be8ffe52832ef6794 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index d9ceb1e6a9a..763cf0dc349 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -35115,8 +35115,6 @@ CREATE INDEX index_protected_environments_on_approval_count_and_created_at ON pr
CREATE UNIQUE INDEX index_protected_environments_on_group_id_and_name ON protected_environments USING btree (group_id, name) WHERE (group_id IS NOT NULL);
-CREATE INDEX index_protected_environments_on_project_id ON protected_environments USING btree (project_id);
-
CREATE UNIQUE INDEX index_protected_environments_on_project_id_and_name ON protected_environments USING btree (project_id, name);
CREATE INDEX index_protected_tag_create_access ON protected_tag_create_access_levels USING btree (protected_tag_id);
diff --git a/doc/architecture/blueprints/ci_builds_runner_fleet_metrics/img/current_page.png b/doc/architecture/blueprints/ci_builds_runner_fleet_metrics/img/current_page.png
index 42b09d37785..0bc3a38b025 100644
--- a/doc/architecture/blueprints/ci_builds_runner_fleet_metrics/img/current_page.png
+++ b/doc/architecture/blueprints/ci_builds_runner_fleet_metrics/img/current_page.png
Binary files differ
diff --git a/doc/architecture/blueprints/remote_development/index.md b/doc/architecture/blueprints/remote_development/index.md
index cc66c3b5416..4f630ce234a 100644
--- a/doc/architecture/blueprints/remote_development/index.md
+++ b/doc/architecture/blueprints/remote_development/index.md
@@ -26,7 +26,7 @@ You can use the [Web IDE](../../../user/project/web_ide/index.md) to commit chan
## Long-term vision
-As a [new Software Developer to a team such as Sasha](https://about.gitlab.com/handbook/product/personas/#sasha-software-developer) with no local development environment, I should be able to:
+As a [new Software Developer to a team such as Sasha](https://handbook.gitlab.com/handbook/product/personas/#sasha-software-developer) with no local development environment, I should be able to:
- Go to a repository on GitLab.com or self-managed.
- Click a button that will provide a list of current workspaces for this repository.
diff --git a/doc/ci/runners/img/runner_fleet_dashboard.png b/doc/ci/runners/img/runner_fleet_dashboard.png
index 8c77b5a1aa9..dbab9bbc091 100644
--- a/doc/ci/runners/img/runner_fleet_dashboard.png
+++ b/doc/ci/runners/img/runner_fleet_dashboard.png
Binary files differ
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index 208da96c5ad..543416d1216 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -1998,10 +1998,10 @@ to select a specific site profile and scanner profile.
### `dependencies`
Use the `dependencies` keyword to define a list of specific jobs to fetch [artifacts](#artifacts)
-from. When `dependencies` is not defined in a job, all jobs in earlier stages are considered dependent
-and the job fetches all artifacts from those jobs.
+from. The specified jobs must all be in earlier stages. You can also set a job to download no artifacts at all.
-You can also set a job to download no artifacts at all.
+When `dependencies` is not defined in a job, all jobs in earlier stages are considered dependent
+and the job fetches all artifacts from those jobs.
**Keyword type**: Job keyword. You can use it only as part of a job.
@@ -2057,6 +2057,8 @@ the [stage](#stages) precedence.
- The job status does not matter. If a job fails or it's a manual job that isn't triggered, no error occurs.
- If the artifacts of a dependent job are [expired](#artifactsexpire_in) or
[deleted](../jobs/job_artifacts.md#delete-job-log-and-artifacts), then the job fails.
+- To fetch artifacts from a job in the same stage, you must use [`needs:artifacts`](#needsartifacts).
+ You should not combine `dependencies` with `needs` in the same job.
### `environment`
@@ -2917,8 +2919,7 @@ In this example:
**Additional details**:
-- In GitLab 12.6 and later, you can't combine the [`dependencies`](#dependencies) keyword
- with `needs`.
+- You should not combine `needs` with [`dependencies`](#dependencies) in the same job.
#### `needs:project` **(PREMIUM ALL)**
diff --git a/doc/development/contributing/first_contribution.md b/doc/development/contributing/first_contribution.md
index c6d097574e8..05f39e559ef 100644
--- a/doc/development/contributing/first_contribution.md
+++ b/doc/development/contributing/first_contribution.md
@@ -199,22 +199,16 @@ In this example, I found some UI text I'd like to change.
In the upper-right corner in GitLab, I selected my avatar and then **Preferences**.
I want to change this text:
-![UI text](img/ui_text_before.png)
+![UI text](img/ui_color_theme_before.png)
-Other settings on the page start with the word `Customize` and skip the `This setting allows you to` part.
-I'll update this phrase to match the others.
+Let's clarify `Customize the color of GitLab.` by changing the phrase to `Customize the color theme of the GitLab UI.`.
-NOTE:
-As this text has already been [changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/116472) when developing this tutorial, you can instead search for `Customize the appearance of the syntax` to find the files that were changed.
-
-1. Search the `gitlab-development-kit/gitlab` directory for the string `This setting allows you to customize`.
+1. Search the `gitlab-development-kit/gitlab` directory for the string `Customize the color`.
- The results show one `.haml` file, two `.md` files, one `.pot` file, and
- several `.po` files.
+ The results shows 1 `.haml` file, and several `.po` files.
-1. Open the `.haml` file. This file is where the UI text resides.
-1. Update the string. In this case, I'll remove the words before `customize`
- and start the word `customize` with a capital `C`.
+1. Open the `app/views/profiles/preferences/show.html.haml` file. This file is where the UI text resides.
+1. Update the string from `Customize the color of GitLab.` to `Customize the color theme of the GitLab UI.`.
1. Save the file.
You can check that you were successful:
@@ -229,7 +223,7 @@ You can check that you were successful:
- Refresh the web browser where you're viewing the GDK.
The changes should be displayed. Take a screenshot.
- ![UI text](img/ui_text_after.png)
+ ![UI text](img/ui_color_theme_after.png)
### Update the translation files
diff --git a/doc/development/contributing/img/ui_color_theme_after.png b/doc/development/contributing/img/ui_color_theme_after.png
new file mode 100644
index 00000000000..40e160b2693
--- /dev/null
+++ b/doc/development/contributing/img/ui_color_theme_after.png
Binary files differ
diff --git a/doc/development/contributing/img/ui_color_theme_before.png b/doc/development/contributing/img/ui_color_theme_before.png
new file mode 100644
index 00000000000..b0f6bd28c3e
--- /dev/null
+++ b/doc/development/contributing/img/ui_color_theme_before.png
Binary files differ
diff --git a/doc/development/contributing/img/ui_text_after.png b/doc/development/contributing/img/ui_text_after.png
deleted file mode 100644
index 3a54e7ef653..00000000000
--- a/doc/development/contributing/img/ui_text_after.png
+++ /dev/null
Binary files differ
diff --git a/doc/development/contributing/img/ui_text_before.png b/doc/development/contributing/img/ui_text_before.png
deleted file mode 100644
index afd75a11ac2..00000000000
--- a/doc/development/contributing/img/ui_text_before.png
+++ /dev/null
Binary files differ
diff --git a/doc/development/merge_request_concepts/diffs/frontend.md b/doc/development/merge_request_concepts/diffs/frontend.md
index aeb1ba452a3..ee17023dae5 100644
--- a/doc/development/merge_request_concepts/diffs/frontend.md
+++ b/doc/development/merge_request_concepts/diffs/frontend.md
@@ -90,7 +90,7 @@ This chart contains several types of items:
AB["batch_comments~~draft_note.vue"]
AC["diffs~~diff_comment_cell.vue"]
AD["diffs~~diff_gutter_avatars.vue"]
- AE["ee-diffs~~inline_findings_flag_switcher.vue"]
+ AE["ee-diffs~~inline_findings_gutter_icon_dropdown.vue"]
AF["notes~~noteable_note.vue"]
AG["notes~~note_actions.vue"]
AH["notes~~note_body.vue"]
diff --git a/doc/user/ai_features.md b/doc/user/ai_features.md
index 906fa9cc724..3e55661d00f 100644
--- a/doc/user/ai_features.md
+++ b/doc/user/ai_features.md
@@ -26,7 +26,7 @@ GitLab is creating AI-assisted features across our DevSecOps platform. These fea
| Helps you remediate vulnerabilities more efficiently, boost your skills, and write more secure code. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=6sDf73QOav8) | [Vulnerability summary](application_security/vulnerabilities/index.md#explaining-a-vulnerability) | **(ULTIMATE SAAS BETA)** |
| Generates a merge request containing the changes required to mitigate a vulnerability. | [Vulnerability resolution](application_security/vulnerabilities/index.md#explaining-a-vulnerability) | **(ULTIMATE SAAS EXPERIMENT)** |
| Helps you understand code by explaining it in English language. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=1izKaLmmaCA) | [Code explanation](#explain-code-in-the-web-ui-with-code-explanation) | **(ULTIMATE SAAS EXPERIMENT)** |
-| Processes and generates text and code in a conversational manner. Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation. | [GitLab Duo Chat](gitlab_duo_chat.md) | **(ULTIMATE BETA)** |
+| Processes and generates text and code in a conversational manner. Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation. | [GitLab Duo Chat](gitlab_duo_chat.md) | **(ULTIMATE ALL BETA)** |
| Assists you in determining the root cause for a pipeline failure and failed CI/CD build. | [Root cause analysis](#root-cause-analysis) | **(ULTIMATE SAAS EXPERIMENT)** |
| Assists you with predicting productivity metrics and identifying anomalies across your software development lifecycle. | [Value stream forecasting](#forecast-deployment-frequency-with-value-stream-forecasting) | **(ULTIMATE ALL EXPERIMENT)** |
| Processes and responds to your questions about your application's usage data. | [Product Analytics](product_analytics/index.md) | **(ULTIMATE SAAS EXPERIMENT)** |
diff --git a/doc/user/clusters/agent/gitops/example_repository_structure.md b/doc/user/clusters/agent/gitops/example_repository_structure.md
index 52855b9731c..998a221905d 100644
--- a/doc/user/clusters/agent/gitops/example_repository_structure.md
+++ b/doc/user/clusters/agent/gitops/example_repository_structure.md
@@ -1,7 +1,7 @@
---
stage: Deploy
group: Environments
-info: A tutorial for deploying a GitLab repository using Flux
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Tutorial: Deploy a Git repository using Flux **(FREE ALL)**
diff --git a/doc/user/clusters/agent/gitops/flux_oci_tutorial.md b/doc/user/clusters/agent/gitops/flux_oci_tutorial.md
index 09e844e448a..921555c166a 100644
--- a/doc/user/clusters/agent/gitops/flux_oci_tutorial.md
+++ b/doc/user/clusters/agent/gitops/flux_oci_tutorial.md
@@ -1,7 +1,7 @@
---
stage: Deploy
group: Environments
-info: A tutorial for deploying an OCI artifact using Flux
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Tutorial: Deploy an OCI artifact using Flux **(FREE ALL)**
diff --git a/doc/user/clusters/agent/gitops/flux_tutorial.md b/doc/user/clusters/agent/gitops/flux_tutorial.md
index 832f91691e8..025264cfa94 100644
--- a/doc/user/clusters/agent/gitops/flux_tutorial.md
+++ b/doc/user/clusters/agent/gitops/flux_tutorial.md
@@ -1,7 +1,7 @@
---
stage: Deploy
group: Environments
-info: A tutorial using Flux
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Tutorial: Set up Flux for GitOps **(FREE ALL)**
diff --git a/doc/user/clusters/agent/index.md b/doc/user/clusters/agent/index.md
index 20d3448aceb..166acf76334 100644
--- a/doc/user/clusters/agent/index.md
+++ b/doc/user/clusters/agent/index.md
@@ -63,9 +63,9 @@ GitLab in a Kubernetes cluster, you might need a different version of Kubernetes
You can upgrade your
Kubernetes version to a supported version at any time:
+- 1.29 (support ends when GitLab version 17.10 is released or when 1.32 becomes supported)
- 1.28 (support ends when GitLab version 17.5 is released or when 1.31 becomes supported)
- 1.27 (support ends when GitLab version 17.2 is released or when 1.30 becomes supported)
-- 1.26 (support ends when GitLab version 16.10 is released or when 1.29 becomes supported)
GitLab aims to support a new minor Kubernetes version three months after its initial release. GitLab supports at least three production-ready Kubernetes minor
versions at any given time.
diff --git a/doc/user/project/merge_requests/changes.md b/doc/user/project/merge_requests/changes.md
index f1fc1bfe233..b7b6db0d22a 100644
--- a/doc/user/project/merge_requests/changes.md
+++ b/doc/user/project/merge_requests/changes.md
@@ -83,7 +83,7 @@ To change the default collapse behavior for a file type:
docs/** gitlab-generated
# Do not collapse package-lock.json
- package-json -gitlab-generated
+ package-lock.json -gitlab-generated
```
1. Commit, push, and merge your changes into your default branch.
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 021b3a9437c..7570702444d 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -32,7 +32,8 @@ module API
return unless find_user_from_warden
Gitlab::UsageDataCounters::WebIdeCounter.increment_commits_count
- Gitlab::UsageDataCounters::EditorUniqueCounter.track_web_ide_edit_action(author: current_user, project: user_project)
+ Gitlab::InternalEvents.track_event('g_edit_by_web_ide', user: current_user, project: user_project)
+
namespace = user_project.namespace
Gitlab::Tracking.event(
diff --git a/lib/gitlab/instrumentation/redis_helper.rb b/lib/gitlab/instrumentation/redis_helper.rb
index 392a7ebe852..8061c3702d5 100644
--- a/lib/gitlab/instrumentation/redis_helper.rb
+++ b/lib/gitlab/instrumentation/redis_helper.rb
@@ -16,13 +16,17 @@ module Gitlab
yield
rescue ::Redis::BaseError, ::RedisClient::Error => ex
- if ex.message.start_with?('MOVED', 'ASK')
- instrumentation_class.instance_count_cluster_redirection(ex)
- else
- instrumentation_class.instance_count_exception(ex)
+ Thread.current[:redis_client_error_count] ||= 0
+
+ # skip instrumentation if the error is a connection error happening for the first time as instrumentation
+ # middlewares are called within `ensure_connected` blocks. Connection retries are not known to the middleware.
+ # Refer to https://github.com/redis-rb/redis-client/issues/119#issuecomment-1829703792
+ unless ex.is_a?(::RedisClient::ConnectionError) && Thread.current[:redis_client_error_count] == 0
+ instrument_errors(instrumentation_class, ex)
end
- instrumentation_class.log_exception(ex)
+ Thread.current[:redis_client_error_count] += 1 if ex.is_a?(::RedisClient::Error)
+
raise ex
ensure
duration = Gitlab::Metrics::System.monotonic_time - start
@@ -80,6 +84,18 @@ module Gitlab
def exclude_from_apdex?(commands)
commands.any? { |command| APDEX_EXCLUDE.include?(command.first.to_s.downcase) }
end
+
+ private
+
+ def instrument_errors(instrumentation_class, error)
+ if error.message.start_with?('MOVED', 'ASK')
+ instrumentation_class.instance_count_cluster_redirection(error)
+ else
+ instrumentation_class.instance_count_exception(error)
+ end
+
+ instrumentation_class.log_exception(error)
+ end
end
end
end
diff --git a/lib/gitlab/patch/redis_client.rb b/lib/gitlab/patch/redis_client.rb
new file mode 100644
index 00000000000..24fa4cf34b2
--- /dev/null
+++ b/lib/gitlab/patch/redis_client.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Patch
+ module RedisClient
+ # This patch resets the connection error tracker after each call to prevent state leak
+ # across calls and requests.
+ #
+ # The purpose of the tracker is to silence RedisClient::ConnectionErrors during reconnection attempts.
+ # More details found in https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/2564#note_1665334335
+ def ensure_connected(retryable: true)
+ super
+ ensure
+ Thread.current[:redis_client_error_count] = 0
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage_data_counters/editor_unique_counter.rb b/lib/gitlab/usage_data_counters/editor_unique_counter.rb
deleted file mode 100644
index 7955c19b7e6..00000000000
--- a/lib/gitlab/usage_data_counters/editor_unique_counter.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module UsageDataCounters
- module EditorUniqueCounter
- EDIT_BY_SNIPPET_EDITOR = 'g_edit_by_snippet_ide'
- EDIT_BY_SFE = 'g_edit_by_sfe'
- EDIT_BY_WEB_IDE = 'g_edit_by_web_ide'
- EDIT_CATEGORY = 'ide_edit'
-
- class << self
- def track_web_ide_edit_action(author:, project:)
- track_internal_event(EDIT_BY_WEB_IDE, author, project)
- end
-
- def count_web_ide_edit_actions(date_from:, date_to:)
- count_unique(EDIT_BY_WEB_IDE, date_from, date_to)
- end
-
- def track_sfe_edit_action(author:, project:)
- track_internal_event(EDIT_BY_SFE, author, project)
- end
-
- def count_sfe_edit_actions(date_from:, date_to:)
- count_unique(EDIT_BY_SFE, date_from, date_to)
- end
-
- def track_snippet_editor_edit_action(author:, project:)
- track_internal_event(EDIT_BY_SNIPPET_EDITOR, author, project)
- end
-
- def count_snippet_editor_edit_actions(date_from:, date_to:)
- count_unique(EDIT_BY_SNIPPET_EDITOR, date_from, date_to)
- end
-
- private
-
- def track_internal_event(event_name, author, project = nil)
- return unless author
-
- Gitlab::InternalEvents.track_event(
- event_name,
- user: author,
- project: project,
- namespace: project&.namespace
- )
- end
-
- def count_unique(actions, date_from, date_to)
- Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: actions, start_date: date_from, end_date: date_to)
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/usage_data_counters/hll_redis_counter.rb b/lib/gitlab/usage_data_counters/hll_redis_counter.rb
index 137b6f90545..97039a58e92 100644
--- a/lib/gitlab/usage_data_counters/hll_redis_counter.rb
+++ b/lib/gitlab/usage_data_counters/hll_redis_counter.rb
@@ -29,18 +29,20 @@ module Gitlab
#
# event_name - The event name.
# values - One or multiple values counted.
+ # property_name - Name of the values counted.
# time - Time of the action, set to Time.current.
- def track_event(event_name, values:, time: Time.current)
- track(values, event_name, time: time)
+ def track_event(event_name, values:, property_name: nil, time: Time.current)
+ track(values, event_name, property_name: property_name, time: time)
end
# Count unique events for a given time range.
#
# event_names - The list of the events to count.
+ # property_names - The list of the values for which the events are to be counted.
# start_date - The start date of the time range.
# end_date - The end date of the time range.
- def unique_events(event_names:, start_date:, end_date:)
- count_unique_events(event_names: event_names, start_date: start_date, end_date: end_date)
+ def unique_events(event_names:, start_date:, end_date:, property_name: nil)
+ count_unique_events(event_names: event_names, property_name: property_name, start_date: start_date, end_date: end_date)
end
def known_event?(event_name)
@@ -52,19 +54,19 @@ module Gitlab
end
def calculate_events_union(event_names:, start_date:, end_date:)
- count_unique_events(event_names: event_names, start_date: start_date, end_date: end_date)
+ count_unique_events(event_names: event_names, property_name: nil, start_date: start_date, end_date: end_date)
end
private
- def track(values, event_name, time: Time.zone.now)
+ def track(values, event_name, property_name:, time: Time.zone.now)
event = event_for(event_name)
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(UnknownEvent.new("Unknown event #{event_name}")) unless event.present?
return if event.blank?
return unless Feature.enabled?(:redis_hll_tracking, type: :ops)
- Gitlab::Redis::HLL.add(key: redis_key(event, time), value: values, expiry: KEY_EXPIRY_LENGTH)
+ Gitlab::Redis::HLL.add(key: redis_key(event_with_property_name(event, property_name), time), value: values, expiry: KEY_EXPIRY_LENGTH)
rescue StandardError => e
# Ignore any exceptions unless is dev or test env
@@ -72,8 +74,8 @@ module Gitlab
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
end
- def count_unique_events(event_names:, start_date:, end_date:)
- events = events_for(Array(event_names).map(&:to_s))
+ def count_unique_events(event_names:, start_date:, end_date:, property_name:)
+ events = events_with_property_names(event_names, property_name)
keys = keys_for_aggregation(events: events, start_date: start_date, end_date: end_date)
@@ -82,6 +84,19 @@ module Gitlab
redis_usage_data { Gitlab::Redis::HLL.count(keys: keys) }
end
+ def events_with_property_names(event_names, property_name)
+ event_names = Array(event_names).map(&:to_s)
+ known_events.filter_map do |event|
+ next unless event_names.include?(event[:name])
+
+ property_name ? event_with_property_name(event, property_name) : event
+ end
+ end
+
+ def event_with_property_name(event, property_name)
+ event.merge(property_name: property_name)
+ end
+
def load_events
events = Gitlab::Usage::MetricDefinition.all.map do |d|
next unless d.available?
@@ -102,21 +117,30 @@ module Gitlab
known_events.find { |event| event[:name] == event_name.to_s }
end
- def events_for(event_names)
- known_events.select { |event| event_names.include?(event[:name]) }
- end
-
def redis_key(event, time)
- key = redis_key_base(event[:name])
+ key = redis_key_base(event)
year_week = time.strftime('%G-%V')
"{#{REDIS_SLOT}}_#{key}-#{year_week}"
end
- def redis_key_base(event_name)
+ def redis_key_base(event)
+ event_name = event[:name]
+
raise UnknownEvent, "Unknown event #{event_name}" unless known_events_names.include?(event_name.to_s)
- key_overrides.fetch(event_name, event_name)
+ property_name = event[:property_name]
+ key = event_name
+ if Feature.enabled?(:redis_hll_property_name_tracking, type: :wip) && property_name
+ key = "#{key}-#{formatted_property_name(property_name)}"
+ end
+
+ key_overrides.fetch(key, key)
+ end
+
+ def formatted_property_name(property_name)
+ # simplify to format from EventDefinitions.unique_properties
+ property_name.to_s.split('.').first.to_sym
end
def key_overrides
diff --git a/lib/gitlab/usage_data_counters/hll_redis_key_overrides.yml b/lib/gitlab/usage_data_counters/hll_redis_key_overrides.yml
index 0967ef424bc..9adbb02ff2c 100644
--- a/lib/gitlab/usage_data_counters/hll_redis_key_overrides.yml
+++ b/lib/gitlab/usage_data_counters/hll_redis_key_overrides.yml
@@ -1 +1,127 @@
-{}
+# This file lists all of the internal events that need to be saved with their legacy HLL Redis keys
+#
+# This file has been generated using the script included in
+# the description of https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137890
+#
+# It is only safe to regenerate it using the same script if the
+# :redis_hll_property_name_tracking feature flag is disabled on prod environment.
+---
+agent_users_using_ci_tunnel-user: agent_users_using_ci_tunnel
+ci_template_included-project: ci_template_included
+exclude_anonymised_users-user: exclude_anonymised_users
+g_compliance_dashboard-user: g_compliance_dashboard
+g_edit_by_sfe-user: g_edit_by_sfe
+g_edit_by_snippet_ide-user: g_edit_by_snippet_ide
+g_edit_by_web_ide-user: g_edit_by_web_ide
+g_project_management_epic_blocked_added-user: g_project_management_epic_blocked_added
+g_project_management_epic_blocked_removed-user: g_project_management_epic_blocked_removed
+g_project_management_epic_blocking_added-user: g_project_management_epic_blocking_added
+g_project_management_epic_blocking_removed-user: g_project_management_epic_blocking_removed
+g_project_management_epic_closed-user: g_project_management_epic_closed
+g_project_management_epic_created-user: g_project_management_epic_created
+g_project_management_epic_cross_referenced-user: g_project_management_epic_cross_referenced
+g_project_management_epic_destroyed-user: g_project_management_epic_destroyed
+g_project_management_epic_issue_added-user: g_project_management_epic_issue_added
+g_project_management_epic_issue_moved_from_project-user: g_project_management_epic_issue_moved_from_project
+g_project_management_epic_issue_removed-user: g_project_management_epic_issue_removed
+g_project_management_epic_related_added-user: g_project_management_epic_related_added
+g_project_management_epic_related_removed-user: g_project_management_epic_related_removed
+g_project_management_epic_reopened-user: g_project_management_epic_reopened
+g_project_management_epic_users_changing_labels-user: g_project_management_epic_users_changing_labels
+g_project_management_issue_added_to_epic-user: g_project_management_issue_added_to_epic
+g_project_management_issue_assignee_changed-user: g_project_management_issue_assignee_changed
+g_project_management_issue_changed_epic-user: g_project_management_issue_changed_epic
+g_project_management_issue_cloned-user: g_project_management_issue_cloned
+g_project_management_issue_closed-user: g_project_management_issue_closed
+g_project_management_issue_comment_added-user: g_project_management_issue_comment_added
+g_project_management_issue_comment_edited-user: g_project_management_issue_comment_edited
+g_project_management_issue_comment_removed-user: g_project_management_issue_comment_removed
+g_project_management_issue_created-user: g_project_management_issue_created
+g_project_management_issue_cross_referenced-user: g_project_management_issue_cross_referenced
+g_project_management_issue_description_changed-user: g_project_management_issue_description_changed
+g_project_management_issue_design_comments_removed-user: g_project_management_issue_design_comments_removed
+g_project_management_issue_designs_added-user: g_project_management_issue_designs_added
+g_project_management_issue_designs_modified-user: g_project_management_issue_designs_modified
+g_project_management_issue_designs_removed-user: g_project_management_issue_designs_removed
+g_project_management_issue_due_date_changed-user: g_project_management_issue_due_date_changed
+g_project_management_issue_health_status_changed-user: g_project_management_issue_health_status_changed
+g_project_management_issue_iteration_changed-user: g_project_management_issue_iteration_changed
+g_project_management_issue_label_changed-user: g_project_management_issue_label_changed
+g_project_management_issue_locked-user: g_project_management_issue_locked
+g_project_management_issue_made_confidential-user: g_project_management_issue_made_confidential
+g_project_management_issue_made_visible-user: g_project_management_issue_made_visible
+g_project_management_issue_marked_as_duplicate-user: g_project_management_issue_marked_as_duplicate
+g_project_management_issue_milestone_changed-user: g_project_management_issue_milestone_changed
+g_project_management_issue_moved-user: g_project_management_issue_moved
+g_project_management_issue_promoted_to_epic-user: g_project_management_issue_promoted_to_epic
+g_project_management_issue_related-user: g_project_management_issue_related
+g_project_management_issue_removed_from_epic-user: g_project_management_issue_removed_from_epic
+g_project_management_issue_reopened-user: g_project_management_issue_reopened
+g_project_management_issue_time_estimate_changed-user: g_project_management_issue_time_estimate_changed
+g_project_management_issue_time_spent_changed-user: g_project_management_issue_time_spent_changed
+g_project_management_issue_title_changed-user: g_project_management_issue_title_changed
+g_project_management_issue_unlocked-user: g_project_management_issue_unlocked
+g_project_management_issue_unrelated-user: g_project_management_issue_unrelated
+g_project_management_issue_weight_changed-user: g_project_management_issue_weight_changed
+g_project_management_users_awarding_epic_emoji-user: g_project_management_users_awarding_epic_emoji
+g_project_management_users_creating_epic_notes-user: g_project_management_users_creating_epic_notes
+g_project_management_users_destroying_epic_notes-user: g_project_management_users_destroying_epic_notes
+g_project_management_users_epic_issue_added_from_epic-user: g_project_management_users_epic_issue_added_from_epic
+g_project_management_users_removing_epic_emoji-user: g_project_management_users_removing_epic_emoji
+g_project_management_users_setting_epic_confidential-user: g_project_management_users_setting_epic_confidential
+g_project_management_users_setting_epic_due_date_as_fixed-user: g_project_management_users_setting_epic_due_date_as_fixed
+g_project_management_users_setting_epic_due_date_as_inherited-user: g_project_management_users_setting_epic_due_date_as_inherited
+g_project_management_users_setting_epic_start_date_as_fixed-user: g_project_management_users_setting_epic_start_date_as_fixed
+g_project_management_users_setting_epic_start_date_as_inherited-user: g_project_management_users_setting_epic_start_date_as_inherited
+g_project_management_users_setting_epic_visible-user: g_project_management_users_setting_epic_visible
+g_project_management_users_updating_epic_descriptions-user: g_project_management_users_updating_epic_descriptions
+g_project_management_users_updating_epic_notes-user: g_project_management_users_updating_epic_notes
+g_project_management_users_updating_epic_parent-user: g_project_management_users_updating_epic_parent
+g_project_management_users_updating_epic_titles-user: g_project_management_users_updating_epic_titles
+g_project_management_users_updating_fixed_epic_due_date-user: g_project_management_users_updating_fixed_epic_due_date
+g_project_management_users_updating_fixed_epic_start_date-user: g_project_management_users_updating_fixed_epic_start_date
+geo_secondary_git_op_action-user: geo_secondary_git_op_action
+i_analytics_dev_ops_adoption-user: i_analytics_dev_ops_adoption
+i_analytics_dev_ops_score-user: i_analytics_dev_ops_score
+i_code_review_saved_replies_create-user: i_code_review_saved_replies_create
+i_code_review_saved_replies_use-user: i_code_review_saved_replies_use
+i_code_review_saved_replies_use_in_mr-user: i_code_review_saved_replies_use_in_mr
+i_code_review_saved_replies_use_in_other-user: i_code_review_saved_replies_use_in_other
+i_code_review_user_create_mr-user: i_code_review_user_create_mr
+i_quickactions_remove_email_multiple-user: i_quickactions_remove_email_multiple
+i_quickactions_remove_email_single-user: i_quickactions_remove_email_single
+insights_chart_item_clicked-user: insights_chart_item_clicked
+insights_issue_chart_item_clicked-user: insights_issue_chart_item_clicked
+k8s_api_proxy_requests_unique_users_via_ci_access-user: k8s_api_proxy_requests_unique_users_via_ci_access
+k8s_api_proxy_requests_unique_users_via_pat_access-user: k8s_api_proxy_requests_unique_users_via_pat_access
+k8s_api_proxy_requests_unique_users_via_user_access-user: k8s_api_proxy_requests_unique_users_via_user_access
+p_analytics_ci_cd_deployment_frequency-user: p_analytics_ci_cd_deployment_frequency
+p_analytics_ci_cd_lead_time-user: p_analytics_ci_cd_lead_time
+p_analytics_ci_cd_pipelines-user: p_analytics_ci_cd_pipelines
+project_management_users_checking_epic_task-user: project_management_users_checking_epic_task
+project_management_users_unchecking_epic_task-user: project_management_users_unchecking_epic_task
+unique_users_visiting_ci_catalog-user: unique_users_visiting_ci_catalog
+user_created_custom_dashboard-user: user_created_custom_dashboard
+user_created_custom_visualization-user: user_created_custom_visualization
+user_edited_cluster_configuration-user: user_edited_cluster_configuration
+user_edited_custom_dashboard-user: user_edited_custom_dashboard
+user_viewed_cluster_configuration-user: user_viewed_cluster_configuration
+user_viewed_custom_dashboard-user: user_viewed_custom_dashboard
+user_viewed_dashboard_designer-user: user_viewed_dashboard_designer
+user_viewed_dashboard_list-user: user_viewed_dashboard_list
+user_viewed_instrumentation_directions-user: user_viewed_instrumentation_directions
+user_viewed_visualization_designer-user: user_viewed_visualization_designer
+user_visited_dashboard-user: user_visited_dashboard
+value_streams_dashboard_change_failure_rate_link_clicked-user: value_streams_dashboard_change_failure_rate_link_clicked
+value_streams_dashboard_cycle_time_link_clicked-user: value_streams_dashboard_cycle_time_link_clicked
+value_streams_dashboard_deployment_frequency_link_clicked-user: value_streams_dashboard_deployment_frequency_link_clicked
+value_streams_dashboard_deploys_link_clicked-user: value_streams_dashboard_deploys_link_clicked
+value_streams_dashboard_issues_completed_link_clicked-user: value_streams_dashboard_issues_completed_link_clicked
+value_streams_dashboard_issues_link_clicked-user: value_streams_dashboard_issues_link_clicked
+value_streams_dashboard_lead_time_for_changes_link_clicked-user: value_streams_dashboard_lead_time_for_changes_link_clicked
+value_streams_dashboard_lead_time_link_clicked-user: value_streams_dashboard_lead_time_link_clicked
+value_streams_dashboard_merge_request_throughput_link_clicked-user: value_streams_dashboard_merge_request_throughput_link_clicked
+value_streams_dashboard_metric_link_clicked-user: value_streams_dashboard_metric_link_clicked
+value_streams_dashboard_time_to_restore_service_link_clicked-user: value_streams_dashboard_time_to_restore_service_link_clicked
+value_streams_dashboard_vulnerability_critical_link_clicked-user: value_streams_dashboard_vulnerability_critical_link_clicked
+value_streams_dashboard_vulnerability_high_link_clicked-user: value_streams_dashboard_vulnerability_high_link_clicked
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 697a2af7ba7..d925ac87c1d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1585,11 +1585,6 @@ msgstr ""
msgid "0 B"
msgstr ""
-msgid "1 Code Quality finding"
-msgid_plural "%d Code Quality findings"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "1 Day"
msgid_plural "%d Days"
msgstr[0] ""
@@ -5517,9 +5512,6 @@ msgstr ""
msgid "An unexpected error occurred while communicating with the Web Terminal."
msgstr ""
-msgid "An unexpected error occurred while loading the code quality diff."
-msgstr ""
-
msgid "An unexpected error occurred while starting the Web Terminal."
msgstr ""
@@ -23063,6 +23055,18 @@ msgstr ""
msgid "Google Play service account key."
msgstr ""
+msgid "GoogleArtifactRegistry|An error occurred while fetching the artifacts."
+msgstr ""
+
+msgid "GoogleArtifactRegistry|Open in Google Cloud"
+msgstr ""
+
+msgid "GoogleArtifactRegistry|Project ID"
+msgstr ""
+
+msgid "GoogleArtifactRegistry|Repository name"
+msgstr ""
+
msgid "GoogleCloud|Cancel"
msgstr ""
diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb
index 1cc9862ff3b..d1927d293b2 100644
--- a/spec/features/groups/members/sort_members_spec.rb
+++ b/spec/features/groups/members/sort_members_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe 'Groups > Members > Sort members', :js, feature_category: :groups
def expect_sort_by(text, sort_direction)
within_testid('members-sort-dropdown') do
- expect(page).to have_css('button[aria-haspopup="menu"]', text: text)
+ expect(page).to have_css('button[aria-haspopup="listbox"]', text: text)
expect(page).to have_button("Sort direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
end
end
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index 94c42c0f098..7457fbc6989 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -148,7 +148,7 @@ RSpec.describe 'Projects > Members > Sorting', :js, feature_category: :groups_an
def expect_sort_by(text, sort_direction)
within('[data-testid="members-sort-dropdown"]') do
- expect(page).to have_css('button[aria-haspopup="menu"]', text: text)
+ expect(page).to have_css('button[aria-haspopup="listbox"]', text: text)
expect(page).to have_button("Sort direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
end
end
diff --git a/spec/finders/groups/accepting_project_transfers_finder_spec.rb b/spec/finders/groups/accepting_project_transfers_finder_spec.rb
index bb6731abbba..47c293bf74b 100644
--- a/spec/finders/groups/accepting_project_transfers_finder_spec.rb
+++ b/spec/finders/groups/accepting_project_transfers_finder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::AcceptingProjectTransfersFinder do
+RSpec.describe Groups::AcceptingProjectTransfersFinder, feature_category: :groups_and_projects do
let_it_be(:user) { create(:user) }
let_it_be(:group_where_direct_owner) { create(:group) }
let_it_be(:subgroup_of_group_where_direct_owner) { create(:group, parent: group_where_direct_owner) }
diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js
index 4676f56c47e..5e0dfd8bd09 100644
--- a/spec/frontend/diffs/components/app_spec.js
+++ b/spec/frontend/diffs/components/app_spec.js
@@ -195,10 +195,6 @@ describe('diffs/components/app', () => {
describe('codequality diff', () => {
it('does not fetch code quality data on FOSS', () => {
createComponent();
- jest.spyOn(wrapper.vm, 'fetchCodequality');
- wrapper.vm.fetchData(false);
-
- expect(wrapper.vm.fetchCodequality).not.toHaveBeenCalled();
expect(codeQualityAndSastQueryHandlerSuccess).not.toHaveBeenCalled();
});
});
diff --git a/spec/frontend/diffs/components/diff_inline_findings_item_spec.js b/spec/frontend/diffs/components/diff_inline_findings_item_spec.js
deleted file mode 100644
index cda3273d51e..00000000000
--- a/spec/frontend/diffs/components/diff_inline_findings_item_spec.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import { GlIcon } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import DiffInlineFindingsItem from '~/diffs/components/diff_inline_findings_item.vue';
-import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/ci/reports/codequality_report/constants';
-import { multipleFindingsArrCodeQualityScale } from '../mock_data/inline_findings';
-
-let wrapper;
-
-const [codeQualityFinding] = multipleFindingsArrCodeQualityScale;
-const findIcon = () => wrapper.findComponent(GlIcon);
-const findDescriptionPlainText = () => wrapper.findByTestId('description-plain-text');
-
-describe('DiffCodeQuality', () => {
- const createWrapper = () => {
- return shallowMountExtended(DiffInlineFindingsItem, {
- propsData: {
- finding: codeQualityFinding,
- },
- });
- };
-
- it('shows icon for given degradation', () => {
- wrapper = createWrapper();
- expect(findIcon().exists()).toBe(true);
-
- expect(findIcon().attributes()).toMatchObject({
- class: `inline-findings-severity-icon ${SEVERITY_CLASSES[codeQualityFinding.severity]}`,
- name: SEVERITY_ICONS[codeQualityFinding.severity],
- size: '12',
- });
- });
-
- it('should render severity + description in plain text', () => {
- wrapper = createWrapper();
- expect(findDescriptionPlainText().text()).toContain(codeQualityFinding.severity);
- expect(findDescriptionPlainText().text()).toContain(codeQualityFinding.description);
- });
-});
diff --git a/spec/frontend/diffs/components/diff_inline_findings_spec.js b/spec/frontend/diffs/components/diff_inline_findings_spec.js
deleted file mode 100644
index f654a2e2d4f..00000000000
--- a/spec/frontend/diffs/components/diff_inline_findings_spec.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import DiffInlineFindings from '~/diffs/components/diff_inline_findings.vue';
-import DiffInlineFindingsItem from '~/diffs/components/diff_inline_findings_item.vue';
-import { NEW_CODE_QUALITY_FINDINGS } from '~/diffs/i18n';
-import { multipleFindingsArrCodeQualityScale } from '../mock_data/inline_findings';
-
-let wrapper;
-const heading = () => wrapper.findByTestId('diff-inline-findings-heading');
-const diffInlineFindingsItems = () => wrapper.findAllComponents(DiffInlineFindingsItem);
-
-describe('DiffInlineFindings', () => {
- const createWrapper = () => {
- return shallowMountExtended(DiffInlineFindings, {
- propsData: {
- title: NEW_CODE_QUALITY_FINDINGS,
- findings: multipleFindingsArrCodeQualityScale,
- },
- });
- };
-
- it('renders the title correctly', () => {
- wrapper = createWrapper();
- expect(heading().text()).toBe(NEW_CODE_QUALITY_FINDINGS);
- });
-
- it('renders the correct number of DiffInlineFindingsItem components with correct props', () => {
- wrapper = createWrapper();
- expect(diffInlineFindingsItems()).toHaveLength(multipleFindingsArrCodeQualityScale.length);
- expect(diffInlineFindingsItems().wrappers[0].props('finding')).toEqual(
- wrapper.props('findings')[0],
- );
- });
-});
diff --git a/spec/frontend/diffs/components/diff_line_spec.js b/spec/frontend/diffs/components/diff_line_spec.js
deleted file mode 100644
index 501bd0757c8..00000000000
--- a/spec/frontend/diffs/components/diff_line_spec.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import DiffLine from '~/diffs/components/diff_line.vue';
-import InlineFindings from '~/diffs/components/inline_findings.vue';
-
-const EXAMPLE_LINE_NUMBER = 3;
-const EXAMPLE_DESCRIPTION = 'example description';
-const EXAMPLE_SEVERITY = 'example severity';
-
-const left = {
- line: {
- left: {
- codequality: [
- {
- line: EXAMPLE_LINE_NUMBER,
- description: EXAMPLE_DESCRIPTION,
- severity: EXAMPLE_SEVERITY,
- },
- ],
- },
- },
-};
-
-const right = {
- line: {
- right: {
- codequality: [
- {
- line: EXAMPLE_LINE_NUMBER,
- description: EXAMPLE_DESCRIPTION,
- severity: EXAMPLE_SEVERITY,
- },
- ],
- },
- },
-};
-
-const mockData = [right, left];
-
-describe('DiffLine', () => {
- const createWrapper = (propsData) => {
- return shallowMount(DiffLine, { propsData });
- };
-
- it('should emit event when hideInlineFindings is called', () => {
- const wrapper = createWrapper(right);
-
- wrapper.findComponent(InlineFindings).vm.$emit('hideInlineFindings');
- expect(wrapper.emitted()).toEqual({
- hideInlineFindings: [[EXAMPLE_LINE_NUMBER]],
- });
- });
-
- mockData.forEach((element) => {
- it('should set correct props for InlineFindings', () => {
- const wrapper = createWrapper(element);
- expect(wrapper.findComponent(InlineFindings).props('codeQuality')).toEqual([
- {
- line: EXAMPLE_LINE_NUMBER,
- description: EXAMPLE_DESCRIPTION,
- severity: EXAMPLE_SEVERITY,
- },
- ]);
- });
- });
-});
diff --git a/spec/frontend/diffs/components/diff_view_spec.js b/spec/frontend/diffs/components/diff_view_spec.js
index 2c8f751804e..2d42c0780b5 100644
--- a/spec/frontend/diffs/components/diff_view_spec.js
+++ b/spec/frontend/diffs/components/diff_view_spec.js
@@ -1,11 +1,9 @@
import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
+import Vue from 'vue';
// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import { throttle } from 'lodash';
import DiffView from '~/diffs/components/diff_view.vue';
-import DiffLine from '~/diffs/components/diff_line.vue';
-import { diffCodeQuality } from '../mock_data/inline_findings';
jest.mock('lodash/throttle', () => jest.fn((fn) => fn));
const lodash = jest.requireActual('lodash');
@@ -19,7 +17,7 @@ describe('DiffView', () => {
const setSelectedCommentPosition = jest.fn();
const getDiffRow = (wrapper) => wrapper.findComponent(DiffRow).vm;
- const createWrapper = ({ props, flag = false } = {}) => {
+ const createWrapper = ({ props } = {}) => {
Vue.use(Vuex);
const batchComments = {
@@ -51,21 +49,10 @@ describe('DiffView', () => {
diffFile: { file_hash: '123' },
diffLines: [],
...props,
- provide: {
- glFeatures: {
- sastReportsInInlineDiff: flag,
- },
- },
- };
-
- const provide = {
- glFeatures: {
- sastReportsInInlineDiff: flag,
- },
};
const stubs = { DiffExpansionCell, DiffRow, DiffCommentCell, DraftNote };
- return shallowMount(DiffView, { propsData, provide, store, stubs });
+ return shallowMount(DiffView, { propsData, store, stubs });
};
beforeEach(() => {
@@ -76,32 +63,6 @@ describe('DiffView', () => {
throttle.mockReset();
});
- it('does not render a diff-line component when there is no finding', () => {
- const wrapper = createWrapper();
- expect(wrapper.findComponent(DiffLine).exists()).toBe(false);
- });
-
- it('does render a diff-line component with the correct props when there is a finding', async () => {
- const wrapper = createWrapper({ props: diffCodeQuality });
- wrapper.findComponent(DiffRow).vm.$emit('toggleCodeQualityFindings', 2);
- await nextTick();
- expect(wrapper.findComponent(DiffLine).props('line')).toBe(diffCodeQuality.diffLines[2]);
- });
-
- it('does not render a diff-line component when there is a finding and sastReportsInInlineDiff flag is true', async () => {
- const wrapper = createWrapper({ props: diffCodeQuality, flag: true });
- wrapper.findComponent(DiffRow).vm.$emit('toggleCodeQualityFindings', 2);
- await nextTick();
- expect(wrapper.findComponent(DiffLine).exists()).toBe(false);
- });
-
- it('does render a diff-line component when there is a finding and sastReportsInInlineDiff flag is false', async () => {
- const wrapper = createWrapper({ props: diffCodeQuality });
- wrapper.findComponent(DiffRow).vm.$emit('toggleCodeQualityFindings', 2);
- await nextTick();
- expect(wrapper.findComponent(DiffLine).exists()).toBe(true);
- });
-
it.each`
type | side | container | sides | total
${'parallel'} | ${'left'} | ${'.old'} | ${{ left: { lineDrafts: [], renderDiscussion: true }, right: { lineDrafts: [], renderDiscussion: true } }} | ${2}
diff --git a/spec/frontend/diffs/components/inline_findings_spec.js b/spec/frontend/diffs/components/inline_findings_spec.js
deleted file mode 100644
index 102287a23b6..00000000000
--- a/spec/frontend/diffs/components/inline_findings_spec.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import InlineFindings from '~/diffs/components/inline_findings.vue';
-import DiffInlineFindings from '~/diffs/components/diff_inline_findings.vue';
-import { NEW_CODE_QUALITY_FINDINGS } from '~/diffs/i18n';
-import { threeCodeQualityFindings } from '../mock_data/inline_findings';
-
-let wrapper;
-
-const diffInlineFindings = () => wrapper.findComponent(DiffInlineFindings);
-
-describe('InlineFindings', () => {
- const createWrapper = () => {
- return mountExtended(InlineFindings, {
- propsData: {
- codeQuality: threeCodeQualityFindings,
- },
- });
- };
-
- it('hides details and throws hideInlineFindings event on close click', async () => {
- wrapper = createWrapper();
- expect(wrapper.findByTestId('inline-findings').exists()).toBe(true);
-
- await wrapper.findByTestId('inline-findings-close').trigger('click');
- expect(wrapper.emitted('hideInlineFindings')).toHaveLength(1);
- });
-
- it('renders diff inline findings component with correct props for codequality array', () => {
- wrapper = createWrapper();
- expect(diffInlineFindings().props('title')).toBe(NEW_CODE_QUALITY_FINDINGS);
- expect(diffInlineFindings().props('findings')).toBe(threeCodeQualityFindings);
- });
-});
diff --git a/spec/frontend/diffs/utils/diff_line_spec.js b/spec/frontend/diffs/utils/diff_line_spec.js
deleted file mode 100644
index adcb4a4433c..00000000000
--- a/spec/frontend/diffs/utils/diff_line_spec.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { pickDirection } from '~/diffs/utils/diff_line';
-
-describe('diff_line utilities', () => {
- describe('pickDirection', () => {
- const left = {
- line_code: 'left',
- };
- const right = {
- line_code: 'right',
- };
- const defaultLine = {
- left,
- right,
- };
-
- it.each`
- code | pick | line | pickDescription
- ${'left'} | ${left} | ${defaultLine} | ${'the left line'}
- ${'right'} | ${right} | ${defaultLine} | ${'the right line'}
- ${'junk'} | ${left} | ${defaultLine} | ${'the default: the left line'}
- ${'junk'} | ${right} | ${{ right }} | ${"the right line if there's no left line to default to"}
- ${'right'} | ${left} | ${{ left }} | ${"the left line when there isn't a right line to match"}
- `(
- 'when provided a line and a line code `$code`, picks $pickDescription',
- ({ code, line, pick }) => {
- expect(pickDirection({ line, code })).toBe(pick);
- },
- );
- });
-});
diff --git a/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
index 849a84b1a6f..1f98d0e7ce0 100644
--- a/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
+++ b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
@@ -1,12 +1,12 @@
-import { GlSorting, GlSortingItem } from '@gitlab/ui';
+import { GlSorting } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import setWindowLocation from 'helpers/set_window_location_helper';
import * as urlUtilities from '~/lib/utils/url_utility';
import SortDropdown from '~/members/components/filter_sort/sort_dropdown.vue';
-import { MEMBER_TYPES } from '~/members/constants';
+import { MEMBER_TYPES, FIELD_KEY_MAX_ROLE } from '~/members/constants';
Vue.use(Vuex);
@@ -47,59 +47,46 @@ describe('SortDropdown', () => {
const findSortingComponent = () => wrapper.findComponent(GlSorting);
const findSortDirectionToggle = () =>
findSortingComponent().find('button[title^="Sort direction"]');
- const findDropdownToggle = () => wrapper.find('button[aria-haspopup="menu"]');
- const findDropdownItemByText = (text) =>
- wrapper
- .findAllComponents(GlSortingItem)
- .wrappers.find((dropdownItemWrapper) => dropdownItemWrapper.text() === text);
+ const findDropdownToggle = () => wrapper.find('button[aria-haspopup="listbox"]');
beforeEach(() => {
setWindowLocation(URL_HOST);
});
describe('dropdown options', () => {
- it('adds dropdown items for all the sortable fields', () => {
+ it('sets sort options', () => {
const URL_FILTER_PARAMS = '?two_factor=enabled&search=foobar';
- const EXPECTED_BASE_URL = `${URL_HOST}${URL_FILTER_PARAMS}&sort=`;
setWindowLocation(URL_FILTER_PARAMS);
- const expectedDropdownItems = [
+ const expectedSortOptions = [
{
- label: 'Account',
- url: `${EXPECTED_BASE_URL}name_asc`,
+ text: 'Account',
+ value: 'account',
},
{
- label: 'Access granted',
- url: `${EXPECTED_BASE_URL}last_joined`,
+ text: 'Access granted',
+ value: 'granted',
},
{
- label: 'Max role',
- url: `${EXPECTED_BASE_URL}access_level_asc`,
+ text: 'Max role',
+ value: 'maxRole',
},
{
- label: 'Last sign-in',
- url: `${EXPECTED_BASE_URL}recent_sign_in`,
+ text: 'Last sign-in',
+ value: 'lastSignIn',
},
];
createComponent();
- expectedDropdownItems.forEach((expectedDropdownItem) => {
- const dropdownItem = findDropdownItemByText(expectedDropdownItem.label);
-
- expect(dropdownItem).not.toBe(null);
- expect(dropdownItem.find('a').attributes('href')).toBe(expectedDropdownItem.url);
+ expect(findSortingComponent().props()).toMatchObject({
+ text: expectedSortOptions[0].text,
+ isAscending: true,
+ sortBy: expectedSortOptions[0].value,
+ sortOptions: expectedSortOptions,
});
});
-
- it('checks selected sort option', () => {
- setWindowLocation('?sort=access_level_asc');
-
- createComponent();
-
- expect(findDropdownItemByText('Max role').vm.$attrs.active).toBe(true);
- });
});
describe('dropdown toggle', () => {
@@ -117,6 +104,20 @@ describe('SortDropdown', () => {
expect(findDropdownToggle().text()).toBe('Max role');
});
+
+ describe('select new sort field', () => {
+ beforeEach(async () => {
+ jest.spyOn(urlUtilities, 'visitUrl').mockImplementation();
+ createComponent();
+
+ findSortingComponent().vm.$emit('sortByChange', FIELD_KEY_MAX_ROLE);
+ await nextTick();
+ });
+
+ it('sorts by new field', () => {
+ expect(urlUtilities.visitUrl).toHaveBeenCalledWith(`${URL_HOST}?sort=access_level_asc`);
+ });
+ });
});
describe('sort direction toggle', () => {
diff --git a/spec/lib/gitlab/instrumentation/redis_client_middleware_spec.rb b/spec/lib/gitlab/instrumentation/redis_client_middleware_spec.rb
index eca75d93c80..a8bded69696 100644
--- a/spec/lib/gitlab/instrumentation/redis_client_middleware_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_client_middleware_spec.rb
@@ -74,14 +74,14 @@ RSpec.describe Gitlab::Instrumentation::RedisClientMiddleware, :request_store, f
context 'when encountering exceptions' do
before do
allow(redis_client.instance_variable_get(:@raw_connection)).to receive(:call).and_raise(
- RedisClient::ConnectionError, 'Connection was closed or lost')
+ RedisClient::Error)
end
it 'counts exception' do
expect(instrumentation_class).to receive(:instance_count_exception)
- .with(instance_of(RedisClient::ConnectionError)).and_call_original
+ .with(instance_of(RedisClient::Error)).and_call_original
expect(instrumentation_class).to receive(:log_exception)
- .with(instance_of(RedisClient::ConnectionError)).and_call_original
+ .with(instance_of(RedisClient::Error)).and_call_original
expect(instrumentation_class).to receive(:instance_count_request).and_call_original
expect do
diff --git a/spec/lib/gitlab/instrumentation/redis_helper_spec.rb b/spec/lib/gitlab/instrumentation/redis_helper_spec.rb
index 54659ca2c02..84a5886bc4e 100644
--- a/spec/lib/gitlab/instrumentation/redis_helper_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_helper_spec.rb
@@ -37,20 +37,27 @@ RSpec.describe Gitlab::Instrumentation::RedisHelper, :request_store, feature_cat
subject(:minimal_test_class_instance) { MinimalTestClass.new }
describe '.instrument_call' do
+ let(:pipelined) { false }
+ let(:command) { [[:set, 'foo', 'bar']] }
+
+ subject(:instrumented_command) { minimal_test_class_instance.check_command(command, pipelined) }
+
it 'instruments request count' do
expect(Gitlab::Instrumentation::Redis::Cache).to receive(:instance_count_request).with(1)
expect(Gitlab::Instrumentation::Redis::Cache).not_to receive(:instance_count_pipelined_request)
- minimal_test_class_instance.check_command([[:set, 'foo', 'bar']], false)
+ instrumented_command
end
it 'performs cluster validation' do
expect(Gitlab::Instrumentation::Redis::Cache).to receive(:redis_cluster_validate!).once
- minimal_test_class_instance.check_command([[:set, 'foo', 'bar']], false)
+ instrumented_command
end
context 'when command is not valid for Redis Cluster' do
+ let(:command) { [[:mget, 'foo', 'bar']] }
+
before do
allow(Gitlab::Instrumentation::Redis::Cache).to receive(:redis_cluster_validate!).and_return(false)
end
@@ -58,7 +65,7 @@ RSpec.describe Gitlab::Instrumentation::RedisHelper, :request_store, feature_cat
it 'reports cross slot request' do
expect(Gitlab::Instrumentation::Redis::Cache).to receive(:increment_cross_slot_request_count).once
- minimal_test_class_instance.check_command([[:mget, 'foo', 'bar']], false)
+ instrumented_command
end
end
@@ -71,21 +78,52 @@ RSpec.describe Gitlab::Instrumentation::RedisHelper, :request_store, feature_cat
end
it 'ensures duration is tracked' do
- commands = [[:set, 'foo', 'bar']]
allow(Gitlab::Instrumentation::Redis::Cache).to receive(:instance_observe_duration).once
allow(Gitlab::Instrumentation::Redis::Cache).to receive(:increment_request_count).with(1).once
allow(Gitlab::Instrumentation::Redis::Cache).to receive(:add_duration).once
- allow(Gitlab::Instrumentation::Redis::Cache).to receive(:add_call_details).with(anything, commands).once
+ allow(Gitlab::Instrumentation::Redis::Cache).to receive(:add_call_details).with(anything, command).once
+
+ expect { instrumented_command }.to raise_error(StandardError)
+ end
+ end
+
+ context 'when a RedisClient::ConnectionError is raised' do
+ before do
+ allow(Gitlab::Instrumentation::Redis::Cache).to receive(:instance_count_request)
+ .and_raise(RedisClient::ConnectionError)
+ end
+
+ it 'silences connection errors raised during the first attempt' do
+ expect(Gitlab::Instrumentation::Redis::Cache).not_to receive(:log_exception).with(RedisClient::ConnectionError)
+
+ expect { instrumented_command }.to raise_error(StandardError)
- expect { minimal_test_class_instance.check_command(commands, false) }.to raise_error(StandardError)
+ expect(Thread.current[:redis_client_error_count]).to eq(1)
+ end
+
+ context 'when error is raised on the second attempt' do
+ before do
+ Thread.current[:redis_client_error_count] = 1
+ end
+
+ it 'instruments errors on second attempt' do
+ expect(Gitlab::Instrumentation::Redis::Cache).to receive(:log_exception).with(RedisClient::ConnectionError)
+
+ expect { instrumented_command }.to raise_error(StandardError)
+
+ expect(Thread.current[:redis_client_error_count]).to eq(2)
+ end
end
end
context 'when pipelined' do
+ let(:command) { [[:get, '{user1}:bar'], [:get, '{user1}:foo']] }
+ let(:pipelined) { true }
+
it 'instruments pipelined request count' do
expect(Gitlab::Instrumentation::Redis::Cache).to receive(:instance_count_pipelined_request)
- minimal_test_class_instance.check_command([[:get, '{user1}:bar'], [:get, '{user1}:foo']], true)
+ instrumented_command
end
end
end
diff --git a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
index e9bd0056e5f..2a160a9d316 100644
--- a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
@@ -117,6 +117,8 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :request_store, featur
expect do
redis_store_class.with { |redis| redis.call(:auth, 'foo', 'bar') }
end.to raise_exception(Redis::CommandError)
+
+ expect(Thread.current[:redis_client_error_count]).to eq(0)
end
end
end
diff --git a/spec/lib/gitlab/patch/redis_client_spec.rb b/spec/lib/gitlab/patch/redis_client_spec.rb
new file mode 100644
index 00000000000..af094e9e0d2
--- /dev/null
+++ b/spec/lib/gitlab/patch/redis_client_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Patch::RedisClient, feature_category: :redis do
+ include RedisHelpers
+
+ let_it_be(:redis_store_class) { define_helper_redis_store_class }
+ let_it_be(:redis_client) { RedisClient.new(redis_store_class.redis_client_params) }
+
+ before do
+ Thread.current[:redis_client_error_count] = 1
+ end
+
+ it 'resets tracking count after each call' do
+ expect { redis_client.call("ping") }
+ .to change { Thread.current[:redis_client_error_count] }
+ .from(1).to(0)
+ end
+
+ it 'resets tracking count after each blocking call' do
+ expect { redis_client.blocking_call(false, "ping") }
+ .to change { Thread.current[:redis_client_error_count] }
+ .from(1).to(0)
+ end
+
+ it 'resets tracking count after pipelined' do
+ expect { redis_client.pipelined { |p| p.call("ping") } }
+ .to change { Thread.current[:redis_client_error_count] }
+ .from(1).to(0)
+ end
+
+ it 'resets tracking count after multi' do
+ expect { redis_client.multi { |p| p.call("ping") } }
+ .to change { Thread.current[:redis_client_error_count] }
+ .from(1).to(0)
+ end
+end
diff --git a/spec/lib/gitlab/process_supervisor_spec.rb b/spec/lib/gitlab/process_supervisor_spec.rb
index 94535e96843..5b9878df456 100644
--- a/spec/lib/gitlab/process_supervisor_spec.rb
+++ b/spec/lib/gitlab/process_supervisor_spec.rb
@@ -42,7 +42,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
pids_killed = []
supervisor.supervise(process_ids) do |dead_pids|
- pids_killed = dead_pids
+ pids_killed += dead_pids
[]
end
@@ -60,7 +60,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
pids_killed = []
supervisor.supervise(process_ids) do |dead_pids|
- pids_killed = dead_pids
+ pids_killed += dead_pids
[42] # Fake starting a new process in place of the terminated one.
end
@@ -68,7 +68,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
Process.kill('TERM', process_ids.first)
await_condition(sleep_sec: health_check_interval_seconds) do
- pids_killed == [process_ids.first]
+ pids_killed.include?(process_ids.first)
end
expect(Gitlab::ProcessManagement.process_alive?(process_ids.first)).to be(false)
@@ -81,7 +81,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
pids_killed = []
supervisor.supervise(process_ids) do |dead_pids|
- pids_killed = dead_pids
+ pids_killed += dead_pids
# Fake a new process having the same pid as one that was just terminated.
[process_ids.last]
end
@@ -90,7 +90,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
Process.kill('TERM', process_ids.first)
await_condition(sleep_sec: health_check_interval_seconds) do
- pids_killed == [process_ids.first]
+ pids_killed.include?(process_ids.first)
end
expect(supervisor.supervised_pids).to contain_exactly(process_ids.last)
@@ -101,7 +101,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
pids_killed = []
supervisor.supervise(process_ids) do |dead_pids|
- pids_killed = dead_pids
+ pids_killed += dead_pids
42
end
@@ -109,7 +109,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
Process.kill('TERM', process_ids.first)
await_condition(sleep_sec: health_check_interval_seconds) do
- pids_killed == [process_ids.first]
+ pids_killed.include?(process_ids.first)
end
expect(supervisor.supervised_pids).to contain_exactly(42, process_ids.last)
diff --git a/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
deleted file mode 100644
index cbf4d3c8261..00000000000
--- a/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::UsageDataCounters::EditorUniqueCounter, :clean_gitlab_redis_shared_state do
- let(:user) { build(:user, id: 1) }
- let(:user2) { build(:user, id: 2) }
- let(:user3) { build(:user, id: 3) }
- let(:project) { build(:project) }
- let(:namespace) { project.namespace }
- let(:time) { Time.zone.now }
-
- shared_examples 'tracks and counts action' do
- subject { track_action(author: user, project: project) }
-
- before do
- stub_application_setting(usage_ping_enabled: true)
- end
-
- specify do
- aggregate_failures do
- track_action(author: user, project: project)
- track_action(author: user2, project: project)
- track_action(author: user3, project: project)
-
- expect(count_unique(date_from: time.beginning_of_week, date_to: 1.week.from_now)).to eq(3)
- end
- end
-
- it_behaves_like 'internal event tracking'
-
- it 'does not track edit actions if author is not present' do
- track_action(author: nil, project: project)
-
- expect(count_unique(date_from: time.beginning_of_week, date_to: 1.week.from_now)).to eq(0)
- end
- end
-
- context 'for web IDE edit actions' do
- let(:event) { described_class::EDIT_BY_WEB_IDE }
-
- it_behaves_like 'tracks and counts action' do
- def track_action(params)
- described_class.track_web_ide_edit_action(**params)
- end
-
- def count_unique(params)
- described_class.count_web_ide_edit_actions(**params)
- end
- end
- end
-
- context 'for SFE edit actions' do
- let(:event) { described_class::EDIT_BY_SFE }
-
- it_behaves_like 'tracks and counts action' do
- def track_action(params)
- described_class.track_sfe_edit_action(**params)
- end
-
- def count_unique(params)
- described_class.count_sfe_edit_actions(**params)
- end
- end
- end
-
- context 'for snippet editor edit actions' do
- let(:event) { described_class::EDIT_BY_SNIPPET_EDITOR }
-
- it_behaves_like 'tracks and counts action' do
- def track_action(params)
- described_class.track_snippet_editor_edit_action(**params)
- end
-
- def count_unique(params)
- described_class.count_snippet_editor_edit_actions(**params)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
index da8098bfee1..5077c3532ef 100644
--- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
@@ -62,15 +62,15 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
describe 'known_events' do
let(:weekly_event) { 'g_analytics_contribution' }
- let(:daily_event) { 'g_analytics_search' }
+ let(:daily_event) { 'g_analytics_issues' }
let(:analytics_slot_event) { 'g_analytics_contribution' }
let(:compliance_slot_event) { 'g_compliance_dashboard' }
- let(:category_analytics_event) { 'g_analytics_search' }
+ let(:category_analytics_event) { 'g_analytics_issues' }
let(:category_productivity_event) { 'g_analytics_productivity' }
let(:no_slot) { 'no_slot' }
let(:different_aggregation) { 'different_aggregation' }
let(:custom_daily_event) { 'g_analytics_custom' }
-
+ let(:event_overridden_for_user) { 'user_created_custom_dashboard' }
let(:global_category) { 'global' }
let(:compliance_category) { 'compliance' }
let(:productivity_category) { 'productivity' }
@@ -84,7 +84,8 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
{ name: category_productivity_event },
{ name: compliance_slot_event },
{ name: no_slot },
- { name: different_aggregation }
+ { name: different_aggregation },
+ { name: event_overridden_for_user }
].map(&:with_indifferent_access)
end
@@ -216,6 +217,97 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
end
end
end
+
+ describe "property_name" do
+ before do
+ stub_feature_flags(redis_hll_property_name_tracking: property_name_flag_enabled)
+ end
+
+ context "with enabled feature flag" do
+ let(:property_name_flag_enabled) { true }
+
+ context "with a property_name for an overridden event" do
+ context "with a property_name sent as a symbol" do
+ it "tracks the events using the Redis key override" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(event_overridden_for_user, values: entity1, property_name: :user)
+ end
+ end
+
+ context "with a property_name sent in string format" do
+ it "tracks the events using the Redis key override" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(event_overridden_for_user, values: entity1, property_name: 'user.id')
+ end
+ end
+ end
+
+ context "with a property_name for an overridden event that doesn't include this property_name" do
+ it "tracks the events using a Redis key with the property_name" do
+ expected_key = "{hll_counters}_#{no_slot}-user-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(no_slot, values: entity1, property_name: 'user')
+ end
+ end
+
+ context "with a property_name for a new event" do
+ it "tracks the events using a Redis key with the property_name" do
+ expected_key = "{hll_counters}_#{no_slot}-project-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(no_slot, values: entity1, property_name: 'project')
+ end
+ end
+
+ context "with no property_name for an overridden event" do
+ it "tracks the events using a Redis key with no property_name" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(event_overridden_for_user, values: entity1)
+ end
+ end
+
+ context "with no property_name for a new event" do
+ it "tracks the events using a Redis key with no property_name" do
+ expected_key = "{hll_counters}_#{no_slot}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(no_slot, values: entity1)
+ end
+ end
+ end
+
+ context "with disabled feature flag" do
+ let(:property_name_flag_enabled) { false }
+
+ it "uses old Redis key for overridden events" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(event_overridden_for_user, values: entity1, property_name: 'user')
+ end
+
+ it "uses old Redis key for new events" do
+ expected_key = "{hll_counters}_#{no_slot}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(no_slot, values: entity1, property_name: 'project')
+ end
+
+ it "uses old Redis key for new events when no property name sent" do
+ expected_key = "{hll_counters}_#{no_slot}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(no_slot, values: entity1)
+ end
+ end
+ end
end
describe '.unique_events' do
@@ -227,7 +319,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
# Events last week
described_class.track_event(weekly_event, values: entity1, time: 2.days.ago)
described_class.track_event(weekly_event, values: entity1, time: 2.days.ago)
- described_class.track_event(no_slot, values: entity1, time: 2.days.ago)
+ described_class.track_event(no_slot, values: entity1, property_name: 'user.id', time: 2.days.ago)
# Events 2 weeks ago
described_class.track_event(weekly_event, values: entity1, time: 2.weeks.ago)
@@ -274,7 +366,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
end
context 'when no slot is set' do
- it { expect(described_class.unique_events(event_names: [no_slot], start_date: 7.days.ago, end_date: Date.current)).to eq(1) }
+ it { expect(described_class.unique_events(event_names: [no_slot], property_name: 'user.id', start_date: 7.days.ago, end_date: Date.current)).to eq(1) }
end
context 'when data crosses into new year' do
@@ -283,6 +375,97 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
.not_to raise_error
end
end
+
+ describe "property_names" do
+ before do
+ stub_feature_flags(redis_hll_property_name_tracking: property_name_flag_enabled)
+ end
+
+ context "with enabled feature flag" do
+ let(:property_name_flag_enabled) { true }
+
+ context "with a property_name for an overridden event" do
+ context "with a property_name sent as a symbol" do
+ it "tracks the events using the Redis key override" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [event_overridden_for_user], property_name: :user, start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+
+ context "with a property_name sent in string format" do
+ it "tracks the events using the Redis key override" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [event_overridden_for_user], property_name: 'user.id', start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+ end
+
+ context "with a property_name for an overridden event that doesn't include this property_name" do
+ it "tracks the events using a Redis key with the property_name" do
+ expected_key = "{hll_counters}_#{no_slot}-user-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [no_slot], property_name: 'user', start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+
+ context "with a property_name for a new event" do
+ it "tracks the events using a Redis key with the property_name" do
+ expected_key = "{hll_counters}_#{no_slot}-project-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [no_slot], property_name: 'project', start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+
+ context "with no property_name for a overridden event" do
+ it "tracks the events using a Redis key with no property_name" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [event_overridden_for_user], start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+
+ context "with no property_name for a new event" do
+ it "tracks the events using a Redis key with no property_name" do
+ expected_key = "{hll_counters}_#{no_slot}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [no_slot], start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+ end
+
+ context "with disabled feature flag" do
+ let(:property_name_flag_enabled) { false }
+
+ it "uses old Redis key for overridden events" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [event_overridden_for_user], property_name: 'user', start_date: 7.days.ago, end_date: Date.current)
+ end
+
+ it "uses old Redis key for new events" do
+ expected_key = "{hll_counters}_#{no_slot}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [no_slot], property_name: 'project', start_date: 7.days.ago, end_date: Date.current)
+ end
+
+ it "uses old Redis key for new events when no property name sent" do
+ expected_key = "{hll_counters}_#{no_slot}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [no_slot], start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+ end
end
describe 'key overrides file' do
@@ -341,43 +524,43 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
let(:time_range) { { start_date: 7.days.ago, end_date: DateTime.current } }
let(:known_events) do
[
- { name: 'event1_slot' },
- { name: 'event2_slot' },
- { name: 'event3_slot' },
- { name: 'event5_slot' },
- { name: 'event4' }
+ { name: 'g_compliance_dashboard' },
+ { name: 'g_project_management_epic_created' },
+ { name: 'g_project_management_epic_closed' },
+ { name: 'g_project_management_epic_reopened' },
+ { name: 'g_project_management_epic_issue_added' }
].map(&:with_indifferent_access)
end
before do
allow(described_class).to receive(:known_events).and_return(known_events)
- described_class.track_event('event1_slot', values: entity1, time: 2.days.ago)
- described_class.track_event('event1_slot', values: entity2, time: 2.days.ago)
- described_class.track_event('event1_slot', values: entity3, time: 2.days.ago)
- described_class.track_event('event2_slot', values: entity1, time: 2.days.ago)
- described_class.track_event('event2_slot', values: entity2, time: 3.days.ago)
- described_class.track_event('event2_slot', values: entity3, time: 3.days.ago)
- described_class.track_event('event3_slot', values: entity1, time: 3.days.ago)
- described_class.track_event('event3_slot', values: entity2, time: 3.days.ago)
- described_class.track_event('event5_slot', values: entity2, time: 3.days.ago)
+ described_class.track_event('g_compliance_dashboard', values: entity1, time: 2.days.ago)
+ described_class.track_event('g_compliance_dashboard', values: entity2, time: 2.days.ago)
+ described_class.track_event('g_compliance_dashboard', values: entity3, time: 2.days.ago)
+ described_class.track_event('g_project_management_epic_created', values: entity1, time: 2.days.ago)
+ described_class.track_event('g_project_management_epic_created', values: entity2, time: 3.days.ago)
+ described_class.track_event('g_project_management_epic_created', values: entity3, time: 3.days.ago)
+ described_class.track_event('g_project_management_epic_closed', values: entity1, time: 3.days.ago)
+ described_class.track_event('g_project_management_epic_closed', values: entity2, time: 3.days.ago)
+ described_class.track_event('g_project_management_epic_reopened', values: entity2, time: 3.days.ago)
# events out of time scope
- described_class.track_event('event2_slot', values: entity4, time: 8.days.ago)
+ described_class.track_event('g_project_management_epic_created', values: entity4, time: 8.days.ago)
# events in different slots
- described_class.track_event('event4', values: entity1, time: 2.days.ago)
- described_class.track_event('event4', values: entity2, time: 2.days.ago)
+ described_class.track_event('g_project_management_epic_issue_added', values: entity1, time: 2.days.ago)
+ described_class.track_event('g_project_management_epic_issue_added', values: entity2, time: 2.days.ago)
end
it 'calculates union of given events', :aggregate_failures do
- expect(described_class.calculate_events_union(**time_range.merge(event_names: %w[event4]))).to eq 2
- expect(described_class.calculate_events_union(**time_range.merge(event_names: %w[event1_slot event2_slot event3_slot]))).to eq 3
+ expect(described_class.calculate_events_union(**time_range.merge(event_names: %w[g_project_management_epic_issue_added]))).to eq 2
+ expect(described_class.calculate_events_union(**time_range.merge(event_names: %w[g_compliance_dashboard g_project_management_epic_created g_project_management_epic_closed]))).to eq 3
end
it 'returns 0 if there are no keys for given events' do
expect(Gitlab::Redis::HLL).not_to receive(:count)
- expect(described_class.calculate_events_union(event_names: %w[event1_slot event2_slot event3_slot], start_date: Date.current, end_date: 4.weeks.ago)).to eq(-1)
+ expect(described_class.calculate_events_union(event_names: %w[g_compliance_dashboard g_project_management_epic_created g_project_management_epic_closed], start_date: Date.current, end_date: 4.weeks.ago)).to eq(-1)
end
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 90c882c42b9..57482a604e2 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -1122,6 +1122,50 @@ RSpec.describe Group, feature_category: :groups_and_projects do
end
end
+ describe '.excluding_restricted_visibility_levels_for_user' do
+ let_it_be(:admin_user) { create(:admin) }
+
+ context 'when restricted_visibility_level is not configured' do
+ context 'when user is an admin', :enable_admin_mode do
+ it 'returns all groups' do
+ expect(described_class.excluding_restricted_visibility_levels_for_user(admin_user)).to eq(
+ [private_group, internal_group, group]
+ )
+ end
+ end
+
+ context 'when user is not an admin' do
+ it 'returns all groups' do
+ expect(described_class.excluding_restricted_visibility_levels_for_user(user1)).to eq(
+ [private_group, internal_group, group]
+ )
+ end
+ end
+ end
+
+ context 'when restricted_visibility_level is set to private' do
+ before do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE])
+ end
+
+ context 'and user is an admin', :enable_admin_mode do
+ it 'returns all groups' do
+ expect(described_class.excluding_restricted_visibility_levels_for_user(admin_user)).to eq(
+ [private_group, internal_group, group]
+ )
+ end
+ end
+
+ context 'and user is not an admin' do
+ it 'excludes private groups' do
+ expect(described_class.excluding_restricted_visibility_levels_for_user(user1)).to eq(
+ [internal_group, group]
+ )
+ end
+ end
+ end
+ end
+
describe '.project_creation_allowed' do
let_it_be(:group_1) { create(:group, project_creation_level: Gitlab::Access::NO_ONE_PROJECT_ACCESS) }
let_it_be(:group_2) { create(:group, project_creation_level: Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) }
@@ -1129,7 +1173,9 @@ RSpec.describe Group, feature_category: :groups_and_projects do
let_it_be(:group_4) { create(:group, project_creation_level: nil) }
it 'only includes groups where project creation is allowed' do
- result = described_class.project_creation_allowed
+ expect(described_class).to receive(:excluding_restricted_visibility_levels_for_user).and_call_original
+
+ result = described_class.project_creation_allowed(user1)
expect(result).to include(group_2, group_3, group_4)
expect(result).not_to include(group_1)
@@ -1141,7 +1187,9 @@ RSpec.describe Group, feature_category: :groups_and_projects do
end
it 'only includes groups where project creation is allowed' do
- result = described_class.project_creation_allowed
+ expect(described_class).to receive(:excluding_restricted_visibility_levels_for_user).and_call_original
+
+ result = described_class.project_creation_allowed(user1)
expect(result).to include(group_2, group_3)
diff --git a/spec/models/projects/branch_rule_spec.rb b/spec/models/projects/branch_rule_spec.rb
index 6910fbbb6db..24f94df5c24 100644
--- a/spec/models/projects/branch_rule_spec.rb
+++ b/spec/models/projects/branch_rule_spec.rb
@@ -8,7 +8,31 @@ RSpec.describe Projects::BranchRule, feature_category: :source_code_management d
subject { described_class.new(protected_branch.project, protected_branch) }
+ describe '::find(id)' do
+ context 'when id matches a Project' do
+ it 'finds the project and initializes a branch rule' do
+ instance = described_class.find(protected_branch.id)
+ expect(instance).to be_instance_of(described_class)
+ expect(instance.protected_branch.id).to eq(protected_branch.id)
+ expect(instance.project.id).to eq(project.id)
+ end
+ end
+
+ context 'when id does not match a Project' do
+ it 'raises an ActiveRecord::RecordNotFound error describing the branch rule' do
+ expect { described_class.find(0) }.to raise_error(
+ ActiveRecord::RecordNotFound, "Couldn't find Projects::BranchRule with 'id'=0"
+ )
+ end
+ end
+ end
+
+ it 'generates a valid global id' do
+ expect(subject.to_global_id.to_s).to eq("gid://gitlab/Projects::BranchRule/#{protected_branch.id}")
+ end
+
it 'delegates methods to protected branch' do
+ expect(subject).to delegate_method(:id).to(:protected_branch)
expect(subject).to delegate_method(:name).to(:protected_branch)
expect(subject).to delegate_method(:group).to(:protected_branch)
expect(subject).to delegate_method(:default_branch?).to(:protected_branch)
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 06ef68f190f..8bea823fd64 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -581,7 +581,7 @@ RSpec.describe API::Commits, feature_category: :source_code_management do
context 'when using access token authentication' do
it 'does not increment the usage counters' do
expect(::Gitlab::UsageDataCounters::WebIdeCounter).not_to receive(:increment_commits_count)
- expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_web_ide_edit_action)
+ expect(::Gitlab::InternalEvents).not_to receive(:track_event)
post api(url, user), params: valid_c_params
end
@@ -596,21 +596,16 @@ RSpec.describe API::Commits, feature_category: :source_code_management do
it 'increments usage counters' do
expect(::Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_commits_count)
- expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_web_ide_edit_action)
subject
end
it_behaves_like 'internal event tracking' do
- let(:event) { ::Gitlab::UsageDataCounters::EditorUniqueCounter::EDIT_BY_WEB_IDE }
+ let(:event) { 'g_edit_by_web_ide' }
let(:namespace) { project.namespace.reload }
end
context 'counts.web_ide_commits Snowplow event tracking' do
- before do
- allow(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_web_ide_edit_action)
- end
-
it_behaves_like 'Snowplow event tracking' do
let(:action) { :commit }
let(:category) { described_class.to_s }
diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
index 7094cb807b2..1b01f43b829 100644
--- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
@@ -106,7 +106,9 @@ RSpec.describe 'Creating a Snippet', feature_category: :source_code_management d
end
context 'with PersonalSnippet' do
- it_behaves_like 'creates snippet'
+ it_behaves_like 'creates snippet' do
+ let(:project) { nil }
+ end
end
context 'with ProjectSnippet' do
diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
index 0bc475c7105..c84aad85598 100644
--- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
@@ -43,7 +43,8 @@ RSpec.describe 'Updating a Snippet', feature_category: :source_code_management d
shared_examples 'graphql update actions' do
context 'when the user does not have permission' do
- let(:current_user) { create(:user) }
+ let(:user) { create(:user) }
+ let(:current_user) { user }
it_behaves_like 'a mutation that returns top-level errors',
errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
@@ -131,14 +132,18 @@ RSpec.describe 'Updating a Snippet', feature_category: :source_code_management d
it_behaves_like 'graphql update actions'
it_behaves_like 'when the snippet is not found'
- it_behaves_like 'snippet edit usage data counters'
+ it_behaves_like 'snippet edit usage data counters' do
+ let(:user) { current_user }
+ end
+
it_behaves_like 'has spam protection' do
let(:mutation_class) { ::Mutations::Snippets::Update }
end
end
describe 'ProjectSnippet' do
- let_it_be(:project) { create(:project, :private) }
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:project) { create(:project, :private, namespace: namespace) }
let(:snippet) do
create(
@@ -181,7 +186,9 @@ RSpec.describe 'Updating a Snippet', feature_category: :source_code_management d
end
end
- it_behaves_like 'snippet edit usage data counters'
+ it_behaves_like 'snippet edit usage data counters' do
+ let(:user) { current_user }
+ end
it_behaves_like 'has spam protection' do
let(:mutation_class) { ::Mutations::Snippets::Update }
@@ -193,9 +200,8 @@ RSpec.describe 'Updating a Snippet', feature_category: :source_code_management d
end
it_behaves_like 'internal event tracking' do
- let(:event) { ::Gitlab::UsageDataCounters::EditorUniqueCounter::EDIT_BY_SNIPPET_EDITOR }
+ let(:event) { 'g_edit_by_snippet_ide' }
let(:user) { current_user }
- let(:namespace) { project.namespace }
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index d08f6ef9d0d..2add01807cb 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -330,6 +330,9 @@ RSpec.configure do |config|
# Postgres is the primary data source, and ClickHouse only when enabled in certain cases.
stub_feature_flags(clickhouse_data_collection: false)
+
+ # This is going to be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/432866
+ stub_feature_flags(redis_hll_property_name_tracking: false)
else
unstub_all_feature_flags
end
diff --git a/spec/support/helpers/database/duplicate_indexes.yml b/spec/support/helpers/database/duplicate_indexes.yml
index 29e1c1da245..941ea4305c5 100644
--- a/spec/support/helpers/database/duplicate_indexes.yml
+++ b/spec/support/helpers/database/duplicate_indexes.yml
@@ -125,9 +125,6 @@ project_compliance_standards_adherence:
project_repositories:
index_project_repositories_on_shard_id_and_project_id:
- index_project_repositories_on_shard_id
-protected_environments:
- index_protected_environments_on_project_id_and_name:
- - index_protected_environments_on_project_id
protected_tags:
index_protected_tags_on_project_id_and_name:
- index_protected_tags_on_project_id
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 91d423fcd67..a11f2cdb41b 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -6727,7 +6727,6 @@
- './spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb'
- './spec/lib/gitlab/usage_data_counters/cycle_analytics_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/designs_counter_spec.rb'
-- './spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/gitlab_cli_activity_unique_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter_spec.rb'
diff --git a/spec/support/shared_examples/controllers/internal_event_tracking_examples.rb b/spec/support/shared_examples/controllers/internal_event_tracking_examples.rb
index 0c19865999f..2332d6cd2d0 100644
--- a/spec/support/shared_examples/controllers/internal_event_tracking_examples.rb
+++ b/spec/support/shared_examples/controllers/internal_event_tracking_examples.rb
@@ -23,7 +23,7 @@ RSpec.shared_examples 'internal event tracking' do
project = try(:project)
user = try(:user)
- namespace = try(:namespace)
+ namespace = try(:namespace) || project&.namespace
expect(Gitlab::Tracking::StandardContext)
.to have_received(:new)
diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
index 53329c5caec..8ed3464b009 100644
--- a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
@@ -14,7 +14,7 @@ RSpec.shared_examples 'snippet edit usage data counters' do
context 'when user is sessionless' do
it 'does not track usage data actions' do
- expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_snippet_editor_edit_action)
+ expect(::Gitlab::InternalEvents).not_to receive(:track_event)
post_graphql_mutation(mutation, current_user: current_user)
end
@@ -25,17 +25,19 @@ RSpec.shared_examples 'snippet edit usage data counters' do
stub_session('warden.user.user.key' => [[current_user.id], current_user.authenticatable_salt])
end
- it 'tracks usage data actions', :clean_gitlab_redis_sessions do
- expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_snippet_editor_edit_action)
-
+ subject do
post_graphql_mutation(mutation)
end
+ it_behaves_like 'internal event tracking' do
+ let(:event) { 'g_edit_by_snippet_ide' }
+ end
+
context 'when mutation result raises an error' do
it 'does not track usage data actions' do
mutation_vars[:title] = nil
- expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_snippet_editor_edit_action)
+ expect(::Gitlab::InternalEvents).not_to receive(:track_event)
post_graphql_mutation(mutation)
end