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