From a9a2f9257eae40935e03ca4185d5263bcb7ba45f Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 22 Jan 2024 18:10:33 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .gitlab/issue_templates/AI Project Proposal.md | 2 +- .gitlab/issue_templates/Design Sprint.md | 38 ++-- .gitlab/issue_templates/Documentation.md | 2 +- .gitlab/issue_templates/Feature Proposal - lean.md | 38 ++-- .../issue_templates/Feature proposal - detailed.md | 40 ++-- .../issue_templates/Fulfillment Group UX Issue.md | 38 ++-- .gitlab/issue_templates/Navigation Proposals.md | 2 +- .../Pipeline Security issue implementation.md | 2 +- .gitlab/issue_templates/Problem Validation.md | 2 +- .rubocop_todo/layout/line_length.yml | 1 - .rubocop_todo/rspec/feature_category.yml | 1 - .rubocop_todo/style/keyword_parameters_order.yml | 1 - app/assets/javascripts/diffs/components/app.vue | 19 +- .../diffs/components/diff_inline_findings.vue | 32 --- .../diffs/components/diff_inline_findings_item.vue | 39 ---- .../javascripts/diffs/components/diff_line.vue | 32 --- .../javascripts/diffs/components/diff_row.vue | 14 +- .../javascripts/diffs/components/diff_view.vue | 34 --- .../diffs/components/inline_findings.vue | 41 ---- app/assets/javascripts/diffs/index.js | 1 - .../javascripts/diffs/store/modules/index.js | 2 +- .../components/filter_sort/sort_dropdown.vue | 41 ++-- .../vue_shared/components/registry/title_area.vue | 1 + .../projects/merge_requests_controller.rb | 1 - app/controllers/projects_controller.rb | 8 +- .../groups/accepting_project_creations_finder.rb | 4 +- .../groups/accepting_project_transfers_finder.rb | 2 +- app/graphql/mutations/snippets/create.rb | 6 +- app/graphql/mutations/snippets/update.rb | 6 +- app/models/group.rb | 8 +- app/models/projects/branch_rule.rb | 11 +- app/views/notify/new_review_email.html.haml | 49 ++--- .../event_paths_consistency_cron_worker.rb | 4 +- .../gitlab_com_derisk/enhanced_review_email.yml | 9 - .../wip/redis_hll_property_name_tracking.yml | 8 + config/initializers/7_redis.rb | 1 + db/docs/catalog_resource_components.yml | 11 +- db/docs/catalog_resource_versions.yml | 10 +- db/docs/catalog_resources.yml | 10 +- ...e_index_protected_environments_on_project_id.rb | 17 ++ db/schema_migrations/20240117070119 | 1 + db/structure.sql | 2 - .../img/current_page.png | Bin 132200 -> 34877 bytes .../blueprints/remote_development/index.md | 2 +- doc/ci/runners/img/runner_fleet_dashboard.png | Bin 348380 -> 118249 bytes doc/ci/yaml/index.md | 11 +- doc/development/contributing/first_contribution.md | 20 +- .../contributing/img/ui_color_theme_after.png | Bin 0 -> 13246 bytes .../contributing/img/ui_color_theme_before.png | Bin 0 -> 11897 bytes doc/development/contributing/img/ui_text_after.png | Bin 4446 -> 0 bytes .../contributing/img/ui_text_before.png | Bin 5243 -> 0 bytes .../merge_request_concepts/diffs/frontend.md | 2 +- doc/user/ai_features.md | 2 +- .../agent/gitops/example_repository_structure.md | 2 +- .../clusters/agent/gitops/flux_oci_tutorial.md | 2 +- doc/user/clusters/agent/gitops/flux_tutorial.md | 2 +- doc/user/clusters/agent/index.md | 2 +- doc/user/project/merge_requests/changes.md | 2 +- lib/api/commits.rb | 3 +- lib/gitlab/instrumentation/redis_helper.rb | 26 ++- lib/gitlab/patch/redis_client.rb | 18 ++ .../usage_data_counters/editor_unique_counter.rb | 55 ----- .../usage_data_counters/hll_redis_counter.rb | 56 +++-- .../hll_redis_key_overrides.yml | 128 ++++++++++- locale/gitlab.pot | 20 +- spec/features/groups/members/sort_members_spec.rb | 2 +- spec/features/projects/members/sorting_spec.rb | 2 +- .../accepting_project_transfers_finder_spec.rb | 2 +- spec/frontend/diffs/components/app_spec.js | 4 - .../components/diff_inline_findings_item_spec.js | 38 ---- .../diffs/components/diff_inline_findings_spec.js | 33 --- spec/frontend/diffs/components/diff_line_spec.js | 65 ------ spec/frontend/diffs/components/diff_view_spec.js | 45 +--- .../diffs/components/inline_findings_spec.js | 33 --- spec/frontend/diffs/utils/diff_line_spec.js | 30 --- .../components/filter_sort/sort_dropdown_spec.js | 65 +++--- .../redis_client_middleware_spec.rb | 6 +- .../gitlab/instrumentation/redis_helper_spec.rb | 52 ++++- .../instrumentation/redis_interceptor_spec.rb | 2 + spec/lib/gitlab/patch/redis_client_spec.rb | 38 ++++ spec/lib/gitlab/process_supervisor_spec.rb | 14 +- .../editor_unique_counter_spec.rb | 80 ------- .../usage_data_counters/hll_redis_counter_spec.rb | 235 ++++++++++++++++++--- spec/models/group_spec.rb | 52 ++++- spec/models/projects/branch_rule_spec.rb | 24 +++ spec/requests/api/commits_spec.rb | 9 +- .../api/graphql/mutations/snippets/create_spec.rb | 4 +- .../api/graphql/mutations/snippets/update_spec.rb | 18 +- spec/spec_helper.rb | 3 + .../support/helpers/database/duplicate_indexes.yml | 3 - spec/support/rspec_order_todo.yml | 1 - .../internal_event_tracking_examples.rb | 2 +- .../graphql/mutations/snippets_shared_examples.rb | 12 +- 93 files changed, 898 insertions(+), 920 deletions(-) delete mode 100644 app/assets/javascripts/diffs/components/diff_inline_findings.vue delete mode 100644 app/assets/javascripts/diffs/components/diff_inline_findings_item.vue delete mode 100644 app/assets/javascripts/diffs/components/diff_line.vue delete mode 100644 app/assets/javascripts/diffs/components/inline_findings.vue delete mode 100644 config/feature_flags/gitlab_com_derisk/enhanced_review_email.yml create mode 100644 config/feature_flags/wip/redis_hll_property_name_tracking.yml create mode 100644 db/post_migrate/20240117070119_remove_index_protected_environments_on_project_id.rb create mode 100644 db/schema_migrations/20240117070119 create mode 100644 doc/development/contributing/img/ui_color_theme_after.png create mode 100644 doc/development/contributing/img/ui_color_theme_before.png delete mode 100644 doc/development/contributing/img/ui_text_after.png delete mode 100644 doc/development/contributing/img/ui_text_before.png create mode 100644 lib/gitlab/patch/redis_client.rb delete mode 100644 lib/gitlab/usage_data_counters/editor_unique_counter.rb delete mode 100644 spec/frontend/diffs/components/diff_inline_findings_item_spec.js delete mode 100644 spec/frontend/diffs/components/diff_inline_findings_spec.js delete mode 100644 spec/frontend/diffs/components/diff_line_spec.js delete mode 100644 spec/frontend/diffs/components/inline_findings_spec.js delete mode 100644 spec/frontend/diffs/utils/diff_line_spec.js create mode 100644 spec/lib/gitlab/patch/redis_client_spec.rb delete mode 100644 spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb 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 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: - 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 ### 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 @@ @@ -110,7 +110,7 @@ Explore (../../doc/development/internal_analytics/internal_event_instrumentation ### What is the type of buyer? - ### 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 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 ``, I want to `` so that I can ``._ -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 - + -
-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, - }, - }, -}; - - - 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 @@ @@ -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" - > - - {{ option.label }} - - + /> 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 {

+
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/gitlab_com_derisk/enhanced_review_email.yml deleted file mode 100644 index 71bdc424ec2..00000000000 --- a/config/feature_flags/gitlab_com_derisk/enhanced_review_email.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -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 -default_enabled: false diff --git a/config/feature_flags/wip/redis_hll_property_name_tracking.yml b/config/feature_flags/wip/redis_hll_property_name_tracking.yml new file mode 100644 index 00000000000..853eb5ce96c --- /dev/null +++ b/config/feature_flags/wip/redis_hll_property_name_tracking.yml @@ -0,0 +1,8 @@ +--- +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 Binary files a/doc/architecture/blueprints/ci_builds_runner_fleet_metrics/img/current_page.png and b/doc/architecture/blueprints/ci_builds_runner_fleet_metrics/img/current_page.png 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 Binary files a/doc/ci/runners/img/runner_fleet_dashboard.png and b/doc/ci/runners/img/runner_fleet_dashboard.png 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 Binary files /dev/null and b/doc/development/contributing/img/ui_color_theme_after.png 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 Binary files /dev/null and b/doc/development/contributing/img/ui_color_theme_before.png 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 Binary files a/doc/development/contributing/img/ui_text_after.png and /dev/null 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 Binary files a/doc/development/contributing/img/ui_text_before.png and /dev/null 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.

[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.

[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 -- cgit v1.2.3