From 91264a675246bc77a83322a2eff560eeaba7ce9a Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 5 Oct 2022 21:09:08 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .gitlab/ci/caching.gitlab-ci.yml | 2 +- .gitlab/ci/frontend.gitlab-ci.yml | 8 +- .gitlab/ci/review.gitlab-ci.yml | 2 +- .gitlab/ci/static-analysis.gitlab-ci.yml | 4 +- app/policies/project_policy.rb | 1 + ...ci_project_pipeline_config_refactoring.yml | 8 - config/metrics/aggregates/common.yml | 9 -- ..._management_alerts_total_unique_counts.yml | 10 ++ ..._management_alerts_total_unique_counts.yml | 10 ++ ...094814_schedule_destroy_invalid_members.rb | 27 ++++ db/schema_migrations/20221004094814 | 1 + doc/development/service_ping/implement.md | 16 ++ .../service_ping/metrics_instrumentation.md | 80 ++++++++++ doc/user/usage_quotas.md | 7 +- lib/api/applications.rb | 3 + .../destroy_invalid_members.rb | 23 +++ .../ci/pipeline/chain/config/content.rb | 26 ---- .../chain/config/content/auto_devops.rb | 34 ----- .../pipeline/chain/config/content/bridge.rb | 25 ---- .../chain/config/content/external_project.rb | 51 ------- .../chain/config/content/parameter.rb | 29 ---- .../pipeline/chain/config/content/remote.rb | 27 ---- .../chain/config/content/repository.rb | 38 ----- .../pipeline/chain/config/content/source.rb | 49 ------ .../usage/metrics/aggregates/aggregate.rb | 26 ++-- .../instrumentations/aggregated_metric.rb | 71 +++++++++ rubocop/rubocop-usage-data.yml | 1 + scripts/utils.sh | 16 ++ .../commit_sidebar/list_item_spec.js | 28 ++-- .../destroy_invalid_members_spec.rb | 141 ++++++++++++++++++ .../ci/pipeline/chain/config/content_spec.rb | 14 +- .../metrics/aggregates/aggregate_spec.rb | 127 ++++++++++++++++ .../aggregated_metric_spec.rb | 72 +++++++++ .../instrumentations/redis_metric_spec.rb | 2 +- .../projects/menus/analytics_menu_spec.rb | 30 +++- ...4_schedule_destroy_invalid_members_spec.rb | 31 ++++ spec/policies/project_policy_spec.rb | 14 ++ 37 files changed, 716 insertions(+), 347 deletions(-) delete mode 100644 config/feature_flags/development/ci_project_pipeline_config_refactoring.yml create mode 100644 db/post_migrate/20221004094814_schedule_destroy_invalid_members.rb create mode 100644 db/schema_migrations/20221004094814 create mode 100644 lib/gitlab/background_migration/destroy_invalid_members.rb delete mode 100644 lib/gitlab/ci/pipeline/chain/config/content/auto_devops.rb delete mode 100644 lib/gitlab/ci/pipeline/chain/config/content/bridge.rb delete mode 100644 lib/gitlab/ci/pipeline/chain/config/content/external_project.rb delete mode 100644 lib/gitlab/ci/pipeline/chain/config/content/parameter.rb delete mode 100644 lib/gitlab/ci/pipeline/chain/config/content/remote.rb delete mode 100644 lib/gitlab/ci/pipeline/chain/config/content/repository.rb delete mode 100644 lib/gitlab/ci/pipeline/chain/config/content/source.rb create mode 100644 lib/gitlab/usage/metrics/instrumentations/aggregated_metric.rb create mode 100644 spec/lib/gitlab/background_migration/destroy_invalid_members_spec.rb create mode 100644 spec/lib/gitlab/usage/metrics/instrumentations/aggregated_metric_spec.rb create mode 100644 spec/migrations/20221004094814_schedule_destroy_invalid_members_spec.rb diff --git a/.gitlab/ci/caching.gitlab-ci.yml b/.gitlab/ci/caching.gitlab-ci.yml index 55f518c102d..4a7a843b514 100644 --- a/.gitlab/ci/caching.gitlab-ci.yml +++ b/.gitlab/ci/caching.gitlab-ci.yml @@ -33,7 +33,7 @@ cache-workhorse: - export GITLAB_ASSETS_HASH=$(bundle exec rake gitlab:assets:hash_sum) - source scripts/gitlab_component_helpers.sh - 'gitlab_assets_archive_doesnt_exist || { echoinfo "INFO: Exiting early as package exists."; exit 0; }' - - run_timed_command "bin/rake gitlab:assets:compile" + - assets_compile_script - echo -n "${GITLAB_ASSETS_HASH}" > "cached-assets-hash.txt" - run_timed_command "create_gitlab_assets_package" - run_timed_command "upload_gitlab_assets_package" diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index fa0fba54014..d7d27b91b13 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -1,7 +1,7 @@ .yarn-install: script: - source scripts/utils.sh - - run_timed_command "retry yarn install --frozen-lockfile" + - yarn_install_script .storybook-yarn-install: script: @@ -23,7 +23,7 @@ WEBPACK_COMPILE_LOG_PATH: "tmp/webpack-output.log" stage: prepare script: - - !reference [.yarn-install, script] + - yarn_install_script - export GITLAB_ASSETS_HASH=$(bin/rake gitlab:assets:hash_sum) - 'echo "CACHE_ASSETS_AS_PACKAGE: ${CACHE_ASSETS_AS_PACKAGE}"' # The new strategy to cache assets as generic packages is experimental and can be disabled by removing the `CACHE_ASSETS_AS_PACKAGE` variable @@ -32,7 +32,7 @@ source scripts/gitlab_component_helpers.sh gitlab_assets_archive_doesnt_exist || run_timed_command "download_and_extract_gitlab_assets" fi - - run_timed_command "bin/rake gitlab:assets:compile" + - assets_compile_script compile-production-assets: extends: @@ -294,7 +294,7 @@ coverage-frontend: - .yarn-cache - .frontend:rules:qa-frontend-node stage: test - dependencies: [] + needs: [] script: - !reference [.yarn-install, script] - run_timed_command "retry yarn run webpack-prod" diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml index 9bfa7ccc36b..8f912dc8231 100644 --- a/.gitlab/ci/review.gitlab-ci.yml +++ b/.gitlab/ci/review.gitlab-ci.yml @@ -79,7 +79,7 @@ danger-review: before_script: - source scripts/utils.sh - bundle_install_script "--with danger" - - run_timed_command "retry yarn install --frozen-lockfile" + - yarn_install_script script: # ${DANGER_DANGERFILE} is used by Jihulab for customizing danger support: https://jihulab.com/gitlab-cn/gitlab/-/blob/main-jh/jh/.gitlab-ci.yml - > diff --git a/.gitlab/ci/static-analysis.gitlab-ci.yml b/.gitlab/ci/static-analysis.gitlab-ci.yml index e845e7eb88b..59ea665ae07 100644 --- a/.gitlab/ci/static-analysis.gitlab-ci.yml +++ b/.gitlab/ci/static-analysis.gitlab-ci.yml @@ -31,7 +31,7 @@ static-analysis: - .static-analysis:rules:static-analysis parallel: 2 script: - - run_timed_command "retry yarn install --frozen-lockfile" + - yarn_install_script - scripts/static-analysis static-analysis as-if-foss: @@ -84,7 +84,7 @@ eslint: variables: USE_BUNDLE_INSTALL: "false" script: - - run_timed_command "retry yarn install --frozen-lockfile" + - yarn_install_script - run_timed_command "yarn run lint:eslint:all" eslint as-if-foss: diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 83a9e3583a8..4f15b5f6ded 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -574,6 +574,7 @@ class ProjectPolicy < BasePolicy rule { issues_disabled & merge_requests_disabled }.policy do prevent(*create_read_update_admin_destroy(:label)) prevent(*create_read_update_admin_destroy(:milestone)) + prevent(:read_cycle_analytics) end rule { snippets_disabled }.policy do diff --git a/config/feature_flags/development/ci_project_pipeline_config_refactoring.yml b/config/feature_flags/development/ci_project_pipeline_config_refactoring.yml deleted file mode 100644 index 0338b81caf7..00000000000 --- a/config/feature_flags/development/ci_project_pipeline_config_refactoring.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: ci_project_pipeline_config_refactoring -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97240 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372867 -milestone: '15.4' -type: development -group: group::pipeline authoring -default_enabled: false diff --git a/config/metrics/aggregates/common.yml b/config/metrics/aggregates/common.yml index 35311858c64..ca6c664a6e9 100644 --- a/config/metrics/aggregates/common.yml +++ b/config/metrics/aggregates/common.yml @@ -17,15 +17,6 @@ # Corresponding feature flag should have `default_enabled` attribute set to `false`. # This attribute is OPTIONAL and can be omitted, when `feature_flag` is missing no feature flag will be checked. --- -- name: incident_management_alerts_total_unique_counts - operator: OR - source: redis - time_frame: [7d, 28d] - events: - - 'incident_management_alert_status_changed' - - 'incident_management_alert_assigned' - - 'incident_management_alert_todo' - - 'incident_management_alert_create_incident' - name: incident_management_incidents_total_unique_counts operator: OR source: redis diff --git a/config/metrics/counts_28d/20210216180509_incident_management_alerts_total_unique_counts.yml b/config/metrics/counts_28d/20210216180509_incident_management_alerts_total_unique_counts.yml index 1632aed611a..3b61fb65911 100644 --- a/config/metrics/counts_28d/20210216180509_incident_management_alerts_total_unique_counts.yml +++ b/config/metrics/counts_28d/20210216180509_incident_management_alerts_total_unique_counts.yml @@ -9,7 +9,17 @@ product_category: incident_management value_type: number status: active time_frame: 28d +instrumentation_class: AggregatedMetric data_source: redis_hll +options: + aggregate: + operator: OR + attribute: user_id + events: + - 'incident_management_alert_status_changed' + - 'incident_management_alert_assigned' + - 'incident_management_alert_todo' + - 'incident_management_alert_create_incident' distribution: - ce - ee diff --git a/config/metrics/counts_7d/20210216180513_incident_management_alerts_total_unique_counts.yml b/config/metrics/counts_7d/20210216180513_incident_management_alerts_total_unique_counts.yml index eeb751682c7..e4089d589cc 100644 --- a/config/metrics/counts_7d/20210216180513_incident_management_alerts_total_unique_counts.yml +++ b/config/metrics/counts_7d/20210216180513_incident_management_alerts_total_unique_counts.yml @@ -9,7 +9,17 @@ product_category: incident_management value_type: number status: active time_frame: 7d +instrumentation_class: AggregatedMetric data_source: redis_hll +options: + aggregate: + operator: OR + attribute: user_id + events: + - 'incident_management_alert_status_changed' + - 'incident_management_alert_assigned' + - 'incident_management_alert_todo' + - 'incident_management_alert_create_incident' distribution: - ce - ee diff --git a/db/post_migrate/20221004094814_schedule_destroy_invalid_members.rb b/db/post_migrate/20221004094814_schedule_destroy_invalid_members.rb new file mode 100644 index 00000000000..fa6a1fd5f72 --- /dev/null +++ b/db/post_migrate/20221004094814_schedule_destroy_invalid_members.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true +class ScheduleDestroyInvalidMembers < Gitlab::Database::Migration[2.0] + MIGRATION = 'DestroyInvalidMembers' + DELAY_INTERVAL = 2.minutes + BATCH_SIZE = 1_000 + MAX_BATCH_SIZE = 10_000 + SUB_BATCH_SIZE = 200 + + restrict_gitlab_migration gitlab_schema: :gitlab_main + + def up + queue_batched_background_migration( + MIGRATION, + :members, + :id, + job_interval: DELAY_INTERVAL, + batch_size: BATCH_SIZE, + max_batch_size: MAX_BATCH_SIZE, + sub_batch_size: SUB_BATCH_SIZE, + gitlab_schema: :gitlab_main + ) + end + + def down + delete_batched_background_migration(MIGRATION, :members, :id, []) + end +end diff --git a/db/schema_migrations/20221004094814 b/db/schema_migrations/20221004094814 new file mode 100644 index 00000000000..f3ac2ccb0d3 --- /dev/null +++ b/db/schema_migrations/20221004094814 @@ -0,0 +1 @@ +b274eaa3ef886e5de6fe5dacb11ab72a724a07a5022da6c01b94309fdaebfd0b \ No newline at end of file diff --git a/doc/development/service_ping/implement.md b/doc/development/service_ping/implement.md index 6825255407f..30503bbbdc2 100644 --- a/doc/development/service_ping/implement.md +++ b/doc/development/service_ping/implement.md @@ -839,6 +839,22 @@ However, it has the following limitations: WARNING: This feature is intended solely for internal GitLab use. +The aggregated metrics feature provides insight into the data attributes in a collection of Service Ping metrics. +This aggregation allows you to count data attributes in events without counting each occurrence of the same data attribute in multiple events. +For example, you can aggregate the number of users who perform several actions, such as creating a new issue and opening a new merge request. +You can then count each user that performed any combination of these actions. + +### Defining aggregated metric via metric YAML definition + +To add data for aggregated metrics to the Service Ping payload, +create metric YAML definition file following [Aggregated metric instrumentation guide](metrics_instrumentation.md#aggregated-metrics). + +### (DEPRECATED) Defining aggregated metric via aggregated metric YAML config file + +WARNING: +This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98206) in GitLab 15.5 +and is planned for removal in 15.5. Use [metrics definition YAMLs](https://gitlab.com/gitlab-org/gitlab/-/issues/370963) instead. + To add data for aggregated metrics to the Service Ping payload, add a corresponding definition to: - [`config/metrics/aggregates/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/aggregates/) for metrics available in the Community Edition. diff --git a/doc/development/service_ping/metrics_instrumentation.md b/doc/development/service_ping/metrics_instrumentation.md index 814332fa621..7a3f291460c 100644 --- a/doc/development/service_ping/metrics_instrumentation.md +++ b/doc/development/service_ping/metrics_instrumentation.md @@ -254,6 +254,86 @@ options: - i_quickactions_approve ``` +## Aggregated metrics + +The aggregated metrics feature provides insight into the number of data attributes, for example `pseudonymized_user_ids`, that occurred in a collection of events. For example, you can aggregate the number of users who perform multiple actions such as creating a new issue and opening +a new merge request. + +You can use a YAML file to define your aggregated metrics. The following arguments are required: + +- `options.events`: List of event names to aggregate into metric data. All events in this list must + use the same data source. Additional data source requirements are described in + [Database sourced aggregated metrics](implement.md#database-sourced-aggregated-metrics) and + [Redis sourced aggregated metrics](implement.md#redis-sourced-aggregated-metrics). +- `options.aggregate.operator`: Operator that defines how the aggregated metric data is counted. Available operators are: + - `OR`: Removes duplicates and counts all entries that triggered any of the listed events. + - `AND`: Removes duplicates and counts all elements that were observed triggering all of the following events. +- `options.aggregate.attribute`: Information pointing to the attribute that is being aggregated across events. +- `time_frame`: One or more valid time frames. Use these to limit the data included in aggregated metrics to events within a specific date-range. Valid time frames are: + - `7d`: The last 7 days of data. + - `28d`: The last 28 days of data. + - `all`: All historical data, only available for `database` sourced aggregated metrics. +- `data_source`: Data source used to collect all events data included in the aggregated metrics. Valid data sources are: + - [`database`](implement.md#database-sourced-aggregated-metrics) + - [`redis_hll`](implement.md#redis-sourced-aggregated-metrics) + +Refer to merge request [98206](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98206) for an example of a merge request that adds an `AggregatedMetric` metric. + +Count unique `user_ids` that occurred in at least one of the events: `incident_management_alert_status_changed`, +`incident_management_alert_assigned`, `incident_management_alert_todo`, `incident_management_alert_create_incident`. + +```yaml +time_frame: 28d +instrumentation_class: AggregatedMetric +data_source: redis_hll +options: + aggregate: + operator: OR + attribute: user_id + events: + - `incident_management_alert_status_changed` + - `incident_management_alert_assigned` + - `incident_management_alert_todo` + - `incident_management_alert_create_incident` +``` + +### Availability-restrained Aggregated metrics + +If the Aggregated metric should only be available in the report under specific conditions, then you must specify these conditions in a new class that is a child of the `AggregatedMetric` class. + +```ruby +# frozen_string_literal: true + +module Gitlab + module Usage + module Metrics + module Instrumentations + class MergeUsageCountAggregatedMetric < AggregatedMetric + available? { Feature.enabled?(:merge_usage_data_missing_key_paths) } + end + end + end + end +end +``` + +You must also use the class's name in the YAML setup. + +```yaml +time_frame: 28d +instrumentation_class: MergeUsageCountAggregatedMetric +data_source: redis_hll +options: + aggregate: + operator: OR + attribute: user_id + events: + - `incident_management_alert_status_changed` + - `incident_management_alert_assigned` + - `incident_management_alert_todo` + - `incident_management_alert_create_incident` +``` + ## Numbers metrics - `operation`: Operations for the given `data` block. Currently we only support `add` operation. diff --git a/doc/user/usage_quotas.md b/doc/user/usage_quotas.md index e123f537424..1b265e86dd4 100644 --- a/doc/user/usage_quotas.md +++ b/doc/user/usage_quotas.md @@ -95,7 +95,6 @@ Depending on your role, you can also use the following methods to manage or redu - [Reduce dependency proxy storage](packages/dependency_proxy/reduce_dependency_proxy_storage.md). - [Reduce repository size](project/repository/reducing_the_repo_size_using_git.md). - [Reduce container registry storage](packages/container_registry/reduce_container_registry_storage.md). -- [Reduce container registry data transfers](packages/container_registry/reduce_container_registry_data_transfer.md). - [Reduce wiki repository size](../administration/wikis/index.md#reduce-wiki-repository-size). ## Excess storage usage @@ -141,3 +140,9 @@ available decreases. All projects remain unlocked because 40 GB purchased storag | Green | 11 GB | 1 GB | 10 GB | Not locked | | Yellow | 5 GB | 0 GB | 10 GB | Not locked | | **Totals** | **45 GB** | **10 GB** | - | - | + +## Manage your transfer usage + +Depending on your role, you can use the following methods to manage or reduce your transfer: + +- [Reduce Container Registry data transfers](packages/container_registry/reduce_container_registry_data_transfer.md). diff --git a/lib/api/applications.rb b/lib/api/applications.rb index 70488621f33..4048215160f 100644 --- a/lib/api/applications.rb +++ b/lib/api/applications.rb @@ -41,6 +41,9 @@ module API end desc 'Delete an application' + params do + requires :id, type: Integer, desc: 'The ID of the application (not the application_id)' + end delete ':id' do application = ApplicationsFinder.new(params).execute break not_found!('Application') unless application diff --git a/lib/gitlab/background_migration/destroy_invalid_members.rb b/lib/gitlab/background_migration/destroy_invalid_members.rb new file mode 100644 index 00000000000..7d78795bea9 --- /dev/null +++ b/lib/gitlab/background_migration/destroy_invalid_members.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + class DestroyInvalidMembers < Gitlab::BackgroundMigration::BatchedMigrationJob # rubocop:disable Style/Documentation + scope_to ->(relation) { relation.where(member_namespace_id: nil) } + + def perform + each_sub_batch(operation_name: :delete_all) do |sub_batch| + deleted_members_data = sub_batch.map do |m| + { id: m.id, source_id: m.source_id, source_type: m.source_type } + end + + deleted_count = sub_batch.delete_all + + Gitlab::AppLogger.info({ message: 'Removing invalid member records', + deleted_count: deleted_count, + deleted_member_data: deleted_members_data }) + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/chain/config/content.rb b/lib/gitlab/ci/pipeline/chain/config/content.rb index a14dec48619..d41213ef6dd 100644 --- a/lib/gitlab/ci/pipeline/chain/config/content.rb +++ b/lib/gitlab/ci/pipeline/chain/config/content.rb @@ -9,15 +9,6 @@ module Gitlab include Chain::Helpers include ::Gitlab::Utils::StrongMemoize - SOURCES = [ - Gitlab::Ci::Pipeline::Chain::Config::Content::Parameter, - Gitlab::Ci::Pipeline::Chain::Config::Content::Bridge, - Gitlab::Ci::Pipeline::Chain::Config::Content::Repository, - Gitlab::Ci::Pipeline::Chain::Config::Content::ExternalProject, - Gitlab::Ci::Pipeline::Chain::Config::Content::Remote, - Gitlab::Ci::Pipeline::Chain::Config::Content::AutoDevops - ].freeze - def perform! if pipeline_config&.exists? @pipeline.build_pipeline_config(content: pipeline_config.content) @@ -36,8 +27,6 @@ module Gitlab def pipeline_config strong_memoize(:pipeline_config) do - next legacy_find_config if ::Feature.disabled?(:ci_project_pipeline_config_refactoring, project) - ::Gitlab::Ci::ProjectConfig.new( project: project, sha: @pipeline.sha, custom_content: @command.content, @@ -45,24 +34,9 @@ module Gitlab ) end end - - def legacy_find_config - sources.each do |source| - config = source.new(@pipeline, @command) - return config if config.exists? - end - - nil - end - - def sources - SOURCES - end end end end end end end - -Gitlab::Ci::Pipeline::Chain::Config::Content.prepend_mod_with('Gitlab::Ci::Pipeline::Chain::Config::Content') diff --git a/lib/gitlab/ci/pipeline/chain/config/content/auto_devops.rb b/lib/gitlab/ci/pipeline/chain/config/content/auto_devops.rb deleted file mode 100644 index 4947e2eb879..00000000000 --- a/lib/gitlab/ci/pipeline/chain/config/content/auto_devops.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Ci - module Pipeline - module Chain - module Config - class Content - class AutoDevops < Source - def content - strong_memoize(:content) do - next unless project&.auto_devops_enabled? - - template = Gitlab::Template::GitlabCiYmlTemplate.find(template_name) - YAML.dump('include' => [{ 'template' => template.full_name }]) - end - end - - def source - :auto_devops_source - end - - private - - def template_name - 'Auto-DevOps' - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/pipeline/chain/config/content/bridge.rb b/lib/gitlab/ci/pipeline/chain/config/content/bridge.rb deleted file mode 100644 index 39ffa2d4e25..00000000000 --- a/lib/gitlab/ci/pipeline/chain/config/content/bridge.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Ci - module Pipeline - module Chain - module Config - class Content - class Bridge < Source - def content - return unless @command.bridge - - @command.bridge.yaml_for_downstream - end - - def source - :bridge_source - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/pipeline/chain/config/content/external_project.rb b/lib/gitlab/ci/pipeline/chain/config/content/external_project.rb deleted file mode 100644 index 092e7d43371..00000000000 --- a/lib/gitlab/ci/pipeline/chain/config/content/external_project.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Ci - module Pipeline - module Chain - module Config - class Content - class ExternalProject < Source - def content - strong_memoize(:content) do - next unless external_project_path? - - path_file, path_project, ref = extract_location_tokens - - config_location = { 'project' => path_project, 'file' => path_file } - config_location['ref'] = ref if ref.present? - - YAML.dump('include' => [config_location]) - end - end - - def source - :external_project_source - end - - private - - # Example: path/to/.gitlab-ci.yml@another-group/another-project - def external_project_path? - ci_config_path =~ /\A.+(yml|yaml)@.+\z/ - end - - # Example: path/to/.gitlab-ci.yml@another-group/another-project:refname - def extract_location_tokens - path_file, path_project = ci_config_path.split('@', 2) - - if path_project.include? ":" - project, ref = path_project.split(':', 2) - [path_file, project, ref] - else - [path_file, path_project] - end - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/pipeline/chain/config/content/parameter.rb b/lib/gitlab/ci/pipeline/chain/config/content/parameter.rb deleted file mode 100644 index 9954aedc4b7..00000000000 --- a/lib/gitlab/ci/pipeline/chain/config/content/parameter.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Ci - module Pipeline - module Chain - module Config - class Content - class Parameter < Source - UnsupportedSourceError = Class.new(StandardError) - - def content - strong_memoize(:content) do - next unless command.content.present? - - command.content - end - end - - def source - :parameter_source - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/pipeline/chain/config/content/remote.rb b/lib/gitlab/ci/pipeline/chain/config/content/remote.rb deleted file mode 100644 index 4990a5a6eb5..00000000000 --- a/lib/gitlab/ci/pipeline/chain/config/content/remote.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Ci - module Pipeline - module Chain - module Config - class Content - class Remote < Source - def content - strong_memoize(:content) do - next unless ci_config_path =~ URI::DEFAULT_PARSER.make_regexp(%w[http https]) - - YAML.dump('include' => [{ 'remote' => ci_config_path }]) - end - end - - def source - :remote_source - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/pipeline/chain/config/content/repository.rb b/lib/gitlab/ci/pipeline/chain/config/content/repository.rb deleted file mode 100644 index 0752b099d3d..00000000000 --- a/lib/gitlab/ci/pipeline/chain/config/content/repository.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Ci - module Pipeline - module Chain - module Config - class Content - class Repository < Source - def content - strong_memoize(:content) do - next unless file_in_repository? - - YAML.dump('include' => [{ 'local' => ci_config_path }]) - end - end - - def source - :repository_source - end - - private - - def file_in_repository? - return unless project - return unless @pipeline.sha - - project.repository.gitlab_ci_yml_for(@pipeline.sha, ci_config_path).present? - rescue GRPC::NotFound, GRPC::Internal - nil - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/pipeline/chain/config/content/source.rb b/lib/gitlab/ci/pipeline/chain/config/content/source.rb deleted file mode 100644 index 69dca1568b6..00000000000 --- a/lib/gitlab/ci/pipeline/chain/config/content/source.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Ci - module Pipeline - module Chain - module Config - class Content - # When removing ci_project_pipeline_config_refactoring, this and its subclasses will be removed. - class Source - include Gitlab::Utils::StrongMemoize - - DEFAULT_YAML_FILE = '.gitlab-ci.yml' - - attr_reader :command - - def initialize(pipeline, command) - @pipeline = pipeline - @command = command - end - - def exists? - strong_memoize(:exists) do - content.present? - end - end - - def content - raise NotImplementedError - end - - def source - raise NotImplementedError - end - - def project - @project ||= @pipeline.project - end - - def ci_config_path - @ci_config_path ||= project.ci_config_path.presence || DEFAULT_YAML_FILE - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/usage/metrics/aggregates/aggregate.rb b/lib/gitlab/usage/metrics/aggregates/aggregate.rb index d8d089c8575..cd72f16d46d 100644 --- a/lib/gitlab/usage/metrics/aggregates/aggregate.rb +++ b/lib/gitlab/usage/metrics/aggregates/aggregate.rb @@ -24,19 +24,6 @@ module Gitlab aggregated_metrics_data(Gitlab::Usage::TimeFrame::SEVEN_DAYS_TIME_FRAME_NAME) end - private - - attr_accessor :aggregated_metrics, :recorded_at - - def aggregated_metrics_data(time_frame) - aggregated_metrics.each_with_object({}) do |aggregation, data| - next if aggregation[:feature_flag] && Feature.disabled?(aggregation[:feature_flag], type: :development) - next unless aggregation[:time_frame].include?(time_frame) - - data[aggregation[:name]] = calculate_count_for_aggregation(aggregation: aggregation, time_frame: time_frame) - end - end - def calculate_count_for_aggregation(aggregation:, time_frame:) with_validate_configuration(aggregation, time_frame) do source = SOURCES[aggregation[:source]] @@ -51,6 +38,19 @@ module Gitlab failure(error) end + private + + attr_accessor :aggregated_metrics, :recorded_at + + def aggregated_metrics_data(time_frame) + aggregated_metrics.each_with_object({}) do |aggregation, data| + next if aggregation[:feature_flag] && Feature.disabled?(aggregation[:feature_flag], type: :development) + next unless aggregation[:time_frame].include?(time_frame) + + data[aggregation[:name]] = calculate_count_for_aggregation(aggregation: aggregation, time_frame: time_frame) + end + end + def with_validate_configuration(aggregation, time_frame) source = aggregation[:source] diff --git a/lib/gitlab/usage/metrics/instrumentations/aggregated_metric.rb b/lib/gitlab/usage/metrics/instrumentations/aggregated_metric.rb new file mode 100644 index 00000000000..63ead5a8cb0 --- /dev/null +++ b/lib/gitlab/usage/metrics/instrumentations/aggregated_metric.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +module Gitlab + module Usage + module Metrics + module Instrumentations + # Usage example + # + # In metric YAML definition: + # + # instrumentation_class: AggregatedMetric + # data_source: redis_hll + # options: + # aggregate: + # operator: OR + # attribute: user_id + # events: + # - 'incident_management_alert_status_changed' + # - 'incident_management_alert_assigned' + # - 'incident_management_alert_todo' + # - 'incident_management_alert_create_incident' + + class AggregatedMetric < BaseMetric + FALLBACK = -1 + + def initialize(metric_definition) + super + @source = parse_data_source_to_legacy_value(metric_definition) + @aggregate = options.fetch(:aggregate, {}) + end + + def value + alt_usage_data(fallback: FALLBACK) do + Aggregates::Aggregate + .new(Time.current) + .calculate_count_for_aggregation( + aggregation: aggregate_config, + time_frame: time_frame + ) + end + end + + def suggested_name + Gitlab::Usage::Metrics::NameSuggestion.for(:alt) + end + + private + + attr_accessor :source, :aggregate + + # TODO: This method is a temporary measure that + # handles backwards compatibility until + # point 5 from is resolved https://gitlab.com/gitlab-org/gitlab/-/issues/370963#implementation + def parse_data_source_to_legacy_value(metric_definition) + return 'redis' if metric_definition[:data_source] == 'redis_hll' + + metric_definition[:data_source] + end + + def aggregate_config + { + source: source, + events: options[:events], + operator: aggregate[:operator] + } + end + end + end + end + end +end diff --git a/rubocop/rubocop-usage-data.yml b/rubocop/rubocop-usage-data.yml index 8d8966144f1..129dbc75acf 100644 --- a/rubocop/rubocop-usage-data.yml +++ b/rubocop/rubocop-usage-data.yml @@ -124,3 +124,4 @@ UsageData/InstrumentationSuperclass: - :RedisHLLMetric - :RedisMetric - :NumbersMetric + - :AggregatedMetric diff --git a/scripts/utils.sh b/scripts/utils.sh index 10b7f856ee6..4554b6988fa 100644 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -62,6 +62,22 @@ function bundle_install_script() { echo -e "section_end:`date +%s`:bundle-install\r\e[0K" } +function yarn_install_script() { + echo -e "section_start:`date +%s`:yarn-install[collapsed=true]\r\e[0KInstalling Yarn packages" + + retry yarn install --frozen-lockfile + + echo -e "section_end:`date +%s`:yarn-install\r\e[0K" +} + +function assets_compile_script() { + echo -e "section_start:`date +%s`:assets-compile[collapsed=true]\r\e[0KCompiling frontend assets" + + bin/rake gitlab:assets:compile + + echo -e "section_end:`date +%s`:assets-compile\r\e[0K" +} + function setup_db_user_only() { source scripts/create_postgres_user.sh } diff --git a/spec/frontend/ide/components/commit_sidebar/list_item_spec.js b/spec/frontend/ide/components/commit_sidebar/list_item_spec.js index f71f6a5e5c7..c9571d39acb 100644 --- a/spec/frontend/ide/components/commit_sidebar/list_item_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/list_item_spec.js @@ -10,7 +10,7 @@ import { file } from '../../helpers'; describe('Multi-file editor commit sidebar list item', () => { let wrapper; - let f; + let testFile; let findPathEl; let store; let router; @@ -21,15 +21,15 @@ describe('Multi-file editor commit sidebar list item', () => { router = createRouter(store); - f = file('test-file'); + testFile = file('test-file'); - store.state.entries[f.path] = f; + store.state.entries[testFile.path] = testFile; wrapper = mount(ListItem, { store, propsData: { - file: f, - activeFileKey: `staged-${f.key}`, + file: testFile, + activeFileKey: `staged-${testFile.key}`, }, }); @@ -43,21 +43,21 @@ describe('Multi-file editor commit sidebar list item', () => { const findPathText = () => trimText(findPathEl.text()); it('renders file path', () => { - expect(findPathText()).toContain(f.path); + expect(findPathText()).toContain(testFile.path); }); it('correctly renders renamed entries', async () => { - Vue.set(f, 'prevName', 'Old name'); + Vue.set(testFile, 'prevName', 'Old name'); await nextTick(); - expect(findPathText()).toEqual(`Old name → ${f.name}`); + expect(findPathText()).toEqual(`Old name → ${testFile.name}`); }); it('correctly renders entry, the name of which did not change after rename (as within a folder)', async () => { - Vue.set(f, 'prevName', f.name); + Vue.set(testFile, 'prevName', testFile.name); await nextTick(); - expect(findPathText()).toEqual(f.name); + expect(findPathText()).toEqual(testFile.name); }); it('opens a closed file in the editor when clicking the file path', async () => { @@ -86,14 +86,14 @@ describe('Multi-file editor commit sidebar list item', () => { }); it('is addition when is a tempFile', async () => { - f.tempFile = true; + testFile.tempFile = true; await nextTick(); expect(getIconName()).toBe('file-addition'); }); it('is deletion when is deleted', async () => { - f.deleted = true; + testFile.deleted = true; await nextTick(); expect(getIconName()).toBe('file-deletion'); @@ -108,14 +108,14 @@ describe('Multi-file editor commit sidebar list item', () => { }); it('is addition when is a tempFile', async () => { - f.tempFile = true; + testFile.tempFile = true; await nextTick(); expect(getIconClass()).toContain('ide-file-addition'); }); it('returns deletion when is deleted', async () => { - f.deleted = true; + testFile.deleted = true; await nextTick(); expect(getIconClass()).toContain('ide-file-deletion'); diff --git a/spec/lib/gitlab/background_migration/destroy_invalid_members_spec.rb b/spec/lib/gitlab/background_migration/destroy_invalid_members_spec.rb new file mode 100644 index 00000000000..9b0cb96b30b --- /dev/null +++ b/spec/lib/gitlab/background_migration/destroy_invalid_members_spec.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +require 'spec_helper' + +# rubocop: disable RSpec/MultipleMemoizedHelpers +RSpec.describe Gitlab::BackgroundMigration::DestroyInvalidMembers, :migration, schema: 20221004094814 do + let!(:migration_attrs) do + { + start_id: 1, + end_id: 1000, + batch_table: :members, + batch_column: :id, + sub_batch_size: 100, + pause_ms: 0, + connection: ApplicationRecord.connection + } + end + + let(:users_table) { table(:users) } + let(:namespaces_table) { table(:namespaces) } + let(:members_table) { table(:members) } + let(:projects_table) { table(:projects) } + let(:members_table_name) { 'members' } + let(:connection) { ApplicationRecord.connection } + let(:user1) { users_table.create!(name: 'user1', email: 'user1@example.com', projects_limit: 5) } + let(:user2) { users_table.create!(name: 'user2', email: 'user2@example.com', projects_limit: 5) } + let(:user3) { users_table.create!(name: 'user3', email: 'user3@example.com', projects_limit: 5) } + let(:user4) { users_table.create!(name: 'user4', email: 'user4@example.com', projects_limit: 5) } + let(:user5) { users_table.create!(name: 'user5', email: 'user5@example.com', projects_limit: 5) } + let(:user6) { users_table.create!(name: 'user6', email: 'user6@example.com', projects_limit: 5) } + let(:user7) { users_table.create!(name: 'user7', email: 'user7@example.com', projects_limit: 5) } + let(:user8) { users_table.create!(name: 'user8', email: 'user8@example.com', projects_limit: 5) } + let!(:group1) { namespaces_table.create!(name: 'marvellous group 1', path: 'group-path-1', type: 'Group') } + let!(:group2) { namespaces_table.create!(name: 'outstanding group 2', path: 'group-path-2', type: 'Group') } + let!(:project_namespace1) do + namespaces_table.create!(name: 'fabulous project', path: 'project-path-1', + type: 'ProjectNamespace', parent_id: group1.id) + end + + let!(:project1) do + projects_table.create!(name: 'fabulous project', path: 'project-path-1', + project_namespace_id: project_namespace1.id, namespace_id: group1.id) + end + + let!(:project_namespace2) do + namespaces_table.create!(name: 'splendiferous project', path: 'project-path-2', + type: 'ProjectNamespace', parent_id: group1.id) + end + + let!(:project2) do + projects_table.create!(name: 'splendiferous project', path: 'project-path-2', + project_namespace_id: project_namespace2.id, namespace_id: group1.id) + end + + # create valid project member records + let!(:project_member1) { create_valid_project_member(id: 1, user_id: user1.id, project: project1) } + let!(:project_member2) { create_valid_project_member(id: 2, user_id: user2.id, project: project2) } + # create valid group member records + let!(:group_member5) { create_valid_group_member(id: 5, user_id: user5.id, group_id: group1.id) } + let!(:group_member6) { create_valid_group_member(id: 6, user_id: user6.id, group_id: group2.id) } + + let!(:migration) { described_class.new(**migration_attrs) } + + subject(:perform_migration) { migration.perform } + + # create invalid project and group member records + def create_members + [ + create_invalid_project_member(id: 3, user_id: user3.id), + create_invalid_project_member(id: 4, user_id: user4.id), + create_invalid_group_member(id: 7, user_id: user7.id), + create_invalid_group_member(id: 8, user_id: user8.id) + ] + end + + it 'removes invalid memberships but keeps valid ones', :aggregate_failures do + without_check_constraint(members_table_name, 'check_508774aac0', connection: connection) do + create_members + + expect(members_table.count).to eq 8 + + queries = ActiveRecord::QueryRecorder.new do + perform_migration + end + + expect(queries.count).to eq(4) + expect(members_table.all).to match_array([project_member1, project_member2, group_member5, group_member6]) + end + end + + it 'tracks timings of queries' do + without_check_constraint(members_table_name, 'check_508774aac0', connection: connection) do + create_members + + expect(migration.batch_metrics.timings).to be_empty + + expect { perform_migration }.to change { migration.batch_metrics.timings } + end + end + + it 'logs IDs of deleted records' do + without_check_constraint(members_table_name, 'check_508774aac0', connection: connection) do + members = create_members + + member_data = members.map do |m| + { id: m.id, source_id: m.source_id, source_type: m.source_type } + end + + expect(Gitlab::AppLogger).to receive(:info).with({ message: 'Removing invalid member records', + deleted_count: 4, + deleted_member_data: member_data }) + + perform_migration + end + end + + def create_invalid_project_member(id:, user_id:) + members_table.create!(id: id, user_id: user_id, source_id: non_existing_record_id, + access_level: Gitlab::Access::MAINTAINER, type: "ProjectMember", + source_type: "Project", notification_level: 3, member_namespace_id: nil) + end + + def create_valid_project_member(id:, user_id:, project:) + members_table.create!(id: id, user_id: user_id, source_id: project.id, + access_level: Gitlab::Access::MAINTAINER, type: "ProjectMember", source_type: "Project", + member_namespace_id: project.project_namespace_id, notification_level: 3) + end + + def create_invalid_group_member(id:, user_id:) + members_table.create!(id: id, user_id: user_id, source_id: non_existing_record_id, + access_level: Gitlab::Access::MAINTAINER, type: "GroupMember", + source_type: "Namespace", notification_level: 3, member_namespace_id: nil) + end + + def create_valid_group_member(id:, user_id:, group_id:) + members_table.create!(id: id, user_id: user_id, source_id: group_id, + access_level: Gitlab::Access::MAINTAINER, type: "GroupMember", + source_type: "Namespace", member_namespace_id: group_id, notification_level: 3) + end +end +# rubocop: enable RSpec/MultipleMemoizedHelpers diff --git a/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb index f451bd6bfef..e0d656f456e 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb @@ -11,9 +11,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Config::Content do subject { described_class.new(pipeline, command) } - # TODO: change this to `describe` and remove rubocop-disable - # when removing the FF ci_project_pipeline_config_refactoring - shared_context '#perform!' do # rubocop:disable RSpec/ContextWording + describe '#perform!' do context 'when bridge job is passed in as parameter' do let(:ci_config_path) { nil } let(:bridge) { create(:ci_bridge) } @@ -203,14 +201,4 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Config::Content do end end end - - it_behaves_like '#perform!' - - context 'when the FF ci_project_pipeline_config_refactoring is disabled' do - before do - stub_feature_flags(ci_project_pipeline_config_refactoring: false) - end - - it_behaves_like '#perform!' - end end diff --git a/spec/lib/gitlab/usage/metrics/aggregates/aggregate_spec.rb b/spec/lib/gitlab/usage/metrics/aggregates/aggregate_spec.rb index 8e02f4f562c..be50685a4f1 100644 --- a/spec/lib/gitlab/usage/metrics/aggregates/aggregate_spec.rb +++ b/spec/lib/gitlab/usage/metrics/aggregates/aggregate_spec.rb @@ -260,5 +260,132 @@ RSpec.describe Gitlab::Usage::Metrics::Aggregates::Aggregate, :clean_gitlab_redi it_behaves_like 'database_sourced_aggregated_metrics' it_behaves_like 'redis_sourced_aggregated_metrics' end + + describe '.calculate_count_for_aggregation' do + using RSpec::Parameterized::TableSyntax + + context 'with valid configuration' do + where(:number_of_days, :operator, :datasource, :expected_method) do + 28 | 'AND' | 'redis' | :calculate_metrics_intersections + 7 | 'AND' | 'redis' | :calculate_metrics_intersections + 28 | 'AND' | 'database' | :calculate_metrics_intersections + 7 | 'AND' | 'database' | :calculate_metrics_intersections + 28 | 'OR' | 'redis' | :calculate_metrics_union + 7 | 'OR' | 'redis' | :calculate_metrics_union + 28 | 'OR' | 'database' | :calculate_metrics_union + 7 | 'OR' | 'database' | :calculate_metrics_union + end + + with_them do + let(:time_frame) { "#{number_of_days}d" } + let(:start_date) { number_of_days.days.ago.to_date } + let(:params) { { start_date: start_date, end_date: end_date, recorded_at: recorded_at } } + let(:aggregate) do + { + source: datasource, + operator: operator, + events: %w[event1 event2] + } + end + + subject(:calculate_count_for_aggregation) do + described_class + .new(recorded_at) + .calculate_count_for_aggregation(aggregation: aggregate, time_frame: time_frame) + end + + it 'returns the number of unique events for aggregation', :aggregate_failures do + expect(namespace::SOURCES[datasource]) + .to receive(expected_method) + .with(params.merge(metric_names: %w[event1 event2])) + .and_return(5) + expect(calculate_count_for_aggregation).to eq(5) + end + end + end + + context 'with invalid configuration' do + where(:time_frame, :operator, :datasource, :expected_error) do + '28d' | 'SUM' | 'redis' | namespace::UnknownAggregationOperator + '7d' | 'AND' | 'mongodb' | namespace::UnknownAggregationSource + 'all' | 'AND' | 'redis' | namespace::DisallowedAggregationTimeFrame + end + + with_them do + let(:aggregate) do + { + source: datasource, + operator: operator, + events: %w[event1 event2] + } + end + + subject(:calculate_count_for_aggregation) do + described_class + .new(recorded_at) + .calculate_count_for_aggregation(aggregation: aggregate, time_frame: time_frame) + end + + context 'with non prod environment' do + it 'raises error' do + expect { calculate_count_for_aggregation }.to raise_error expected_error + end + end + + context 'with prod environment' do + before do + stub_rails_env('production') + end + + it 'returns fallback value' do + expect(calculate_count_for_aggregation).to be(-1) + end + end + end + end + + context 'when union data is not available' do + subject(:calculate_count_for_aggregation) do + described_class + .new(recorded_at) + .calculate_count_for_aggregation(aggregation: aggregate, time_frame: time_frame) + end + + where(:time_frame, :operator, :datasource) do + '28d' | 'OR' | 'redis' + '7d' | 'OR' | 'database' + end + + with_them do + before do + allow(namespace::SOURCES[datasource]).to receive(:calculate_metrics_union).and_raise(sources::UnionNotAvailable) + end + + let(:aggregate) do + { + source: datasource, + operator: operator, + events: %w[event1 event2] + } + end + + context 'with non prod environment' do + it 'raises error' do + expect { calculate_count_for_aggregation }.to raise_error sources::UnionNotAvailable + end + end + + context 'with prod environment' do + before do + stub_rails_env('production') + end + + it 'returns fallback value' do + expect(calculate_count_for_aggregation).to be(-1) + end + end + end + end + end end end diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/aggregated_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/aggregated_metric_spec.rb new file mode 100644 index 00000000000..71e947df3ef --- /dev/null +++ b/spec/lib/gitlab/usage/metrics/instrumentations/aggregated_metric_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Usage::Metrics::Instrumentations::AggregatedMetric, :clean_gitlab_redis_shared_state do + using RSpec::Parameterized::TableSyntax + before do + # weekly AND 1 weekly OR 2 + Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:i_quickactions_approve, values: 1, time: 1.week.ago) + Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:i_quickactions_unapprove, values: 1, time: 1.week.ago) + Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:i_quickactions_unapprove, values: 2, time: 1.week.ago) + + # monthly AND 2 weekly OR 3 + Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:i_quickactions_approve, values: 2, time: 2.weeks.ago) + Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:i_quickactions_unapprove, values: 3, time: 2.weeks.ago) + + # out of date range + Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:i_quickactions_approve, values: 3, time: 2.months.ago) + + # database events + Gitlab::Usage::Metrics::Aggregates::Sources::PostgresHll + .save_aggregated_metrics( + metric_name: :i_quickactions_approve, + time_period: { created_at: (1.week.ago..Date.current) }, + recorded_at_timestamp: Time.current, + data: ::Gitlab::Database::PostgresHll::Buckets.new(141 => 1, 56 => 1) + ) + Gitlab::Usage::Metrics::Aggregates::Sources::PostgresHll + .save_aggregated_metrics( + metric_name: :i_quickactions_unapprove, + time_period: { created_at: (1.week.ago..Date.current) }, + recorded_at_timestamp: Time.current, + data: ::Gitlab::Database::PostgresHll::Buckets.new(10 => 1, 56 => 1) + ) + end + + where(:data_source, :time_frame, :operator, :expected_value) do + 'redis_hll' | '28d' | 'AND' | 2 + 'redis_hll' | '28d' | 'OR' | 3 + 'redis_hll' | '7d' | 'AND' | 1 + 'redis_hll' | '7d' | 'OR' | 2 + 'database' | '7d' | 'OR' | 3 + 'database' | '7d' | 'AND' | 1 + end + + with_them do + let(:metric_definition) do + { + data_source: data_source, + time_frame: time_frame, + options: { + aggregate: { + operator: operator + }, + events: %w[ + i_quickactions_approve + i_quickactions_unapprove + ] + } + } + end + + around do |example| + freeze_time { example.run } + end + + it 'has correct value' do + # database source is providing estimated value that have a lot of decimal places, round it for convenience + expect(described_class.new(metric_definition).value.round).to eq(expected_value) + end + end +end diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb index 9ff3ad1dfc0..c4d6edd43e1 100644 --- a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb +++ b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb @@ -77,7 +77,7 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisMetric, :clean_git end it_behaves_like 'a correct instrumented metric value', { - options: { event: 'all_searches_count', prefix: nil, include_usage_prefix: false } + options: { event: 'all_searches_count', prefix: nil, include_usage_prefix: false }, time_frame: 'all' } end end diff --git a/spec/lib/sidebars/projects/menus/analytics_menu_spec.rb b/spec/lib/sidebars/projects/menus/analytics_menu_spec.rb index 25a65015847..878da747abe 100644 --- a/spec/lib/sidebars/projects/menus/analytics_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/analytics_menu_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Sidebars::Projects::Menus::AnalyticsMenu do - let_it_be(:project) { create(:project, :repository) } + let_it_be_with_refind(:project) { create(:project, :repository) } let_it_be(:guest) do create(:user).tap { |u| project.add_guest(u) } end @@ -125,6 +125,34 @@ RSpec.describe Sidebars::Projects::Menus::AnalyticsMenu do specify { is_expected.to be_nil } end + + describe 'when issues are disabled' do + before do + project.issues_enabled = false + project.save! + end + + specify { is_expected.not_to be_nil } + end + + describe 'when merge requests are disabled' do + before do + project.merge_requests_enabled = false + project.save! + end + + specify { is_expected.not_to be_nil } + end + + describe 'when the issues and merge requests are disabled' do + before do + project.issues_enabled = false + project.merge_requests_enabled = false + project.save! + end + + specify { is_expected.to be_nil } + end end end end diff --git a/spec/migrations/20221004094814_schedule_destroy_invalid_members_spec.rb b/spec/migrations/20221004094814_schedule_destroy_invalid_members_spec.rb new file mode 100644 index 00000000000..73fdfa78eb4 --- /dev/null +++ b/spec/migrations/20221004094814_schedule_destroy_invalid_members_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe ScheduleDestroyInvalidMembers, :migration do + let_it_be(:migration) { described_class::MIGRATION } + + describe '#up' do + it 'schedules background jobs for each batch of members' do + migrate! + + expect(migration).to have_scheduled_batched_migration( + table_name: :members, + column_name: :id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + max_batch_size: described_class::MAX_BATCH_SIZE + ) + end + end + + describe '#down' do + it 'deletes all batched migration records' do + migrate! + schema_migrate_down! + + expect(migration).not_to have_scheduled_batched_migration + end + end +end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index cf06de8b850..847379af91a 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -103,6 +103,20 @@ RSpec.describe ProjectPolicy do end end + context 'when both issues and merge requests are disabled' do + let(:current_user) { owner } + + before do + project.issues_enabled = false + project.merge_requests_enabled = false + project.save! + end + + it 'does not include the issues permissions' do + expect_disallowed :read_cycle_analytics + end + end + context 'creating_merge_request_in' do context 'when the current_user can download_code' do before do