diff --git a/.rubocop_todo/performance/block_given_with_explicit_block.yml b/.rubocop_todo/performance/block_given_with_explicit_block.yml
deleted file mode 100644
index b919dbd19e6..00000000000
--- a/.rubocop_todo/performance/block_given_with_explicit_block.yml
+++ /dev/null
@@ -1,43 +0,0 @@
----
-# Cop supports --auto-correct.
-Performance/BlockGivenWithExplicitBlock:
- Exclude:
- - 'app/controllers/concerns/redis_tracking.rb'
- - 'app/helpers/badges_helper.rb'
- - 'app/helpers/instance_configuration_helper.rb'
- - 'app/helpers/labels_helper.rb'
- - 'app/helpers/tab_helper.rb'
- - 'app/services/base_count_service.rb'
- - 'app/services/error_tracking/base_service.rb'
- - 'app/services/users/update_service.rb'
- - 'ee/lib/elastic/latest/query_context.rb'
- - 'ee/lib/gitlab/geo.rb'
- - 'lib/bulk_imports/clients/http.rb'
- - 'lib/gitlab/batch_pop_queueing.rb'
- - 'lib/gitlab/cache/request_cache.rb'
- - 'lib/gitlab/ci/trace/chunked_io.rb'
- - 'lib/gitlab/database/bulk_update.rb'
- - 'lib/gitlab/database/with_lock_retries.rb'
- - 'lib/gitlab/github_import/client.rb'
- - 'lib/gitlab/legacy_github_import/client.rb'
- - 'lib/gitlab/metrics/methods/metric_options.rb'
- - 'lib/gitlab/null_request_store.rb'
- - 'lib/gitlab/quick_actions/dsl.rb'
- - 'lib/gitlab/redis/multi_store.rb'
- - 'lib/gitlab/safe_request_loader.rb'
- - 'lib/gitlab/search/query.rb'
- - 'lib/gitlab/string_placeholder_replacer.rb'
- - 'lib/gitlab/terraform/state_migration_helper.rb'
- - 'lib/gitlab/usage/metrics/instrumentations/base_metric.rb'
- - 'lib/gitlab/usage/metrics/instrumentations/database_metric.rb'
- - 'lib/gitlab/usage/metrics/instrumentations/numbers_metric.rb'
- - 'lib/gitlab/usage_data_queries.rb'
- - 'lib/gitlab/utils/usage_data.rb'
- - 'qa/qa/page/view.rb'
- - 'spec/lib/api/helpers/authentication_spec.rb'
- - 'spec/lib/gitlab/slash_commands/deploy_spec.rb'
- - 'spec/support/helpers/graphql_helpers.rb'
- - 'spec/support/helpers/query_recorder.rb'
- - 'spec/support/helpers/stub_method_calls.rb'
- - 'tooling/lib/tooling/helm3_client.rb'
- - 'tooling/lib/tooling/test_map_packer.rb'
diff --git a/app/controllers/concerns/redis_tracking.rb b/app/controllers/concerns/redis_tracking.rb
index c1135d2f759..445e72b8266 100644
--- a/app/controllers/concerns/redis_tracking.rb
+++ b/app/controllers/concerns/redis_tracking.rb
@@ -29,7 +29,7 @@ module RedisTracking
private
def track_unique_redis_hll_event(event_name, &block)
- custom_id = block_given? ? yield(self) : nil
+ custom_id = block ? yield(self) : nil
unique_id = custom_id || visitor_id
diff --git a/app/graphql/types/namespace_type.rb b/app/graphql/types/namespace_type.rb
index 088a3437f4a..b90226bd733 100644
--- a/app/graphql/types/namespace_type.rb
+++ b/app/graphql/types/namespace_type.rb
@@ -57,6 +57,12 @@ module Types
null: true,
description: "Shared runners availability for the namespace and its descendants."
+ field :timelog_categories,
+ Types::TimeTracking::TimelogCategoryType.connection_type,
+ null: true,
+ description: "Timelog categories for the namespace.",
+ feature_flag: :timelog_categories
+
markdown_field :description_html, null: true
def cross_project_pipeline_available?
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 2a5ba3c7006..5bb8e613844 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -438,6 +438,16 @@ module Types
' Returns `null` if `work_items` feature flag is disabled.' \
' This flag is disabled by default, because the feature is experimental and is subject to change without notice.'
+ field :timelog_categories,
+ Types::TimeTracking::TimelogCategoryType.connection_type,
+ null: true,
+ description: "Timelog categories for the project.",
+ feature_flag: :timelog_categories
+
+ def timelog_categories
+ object.project_namespace.timelog_categories
+ end
+
def label(title:)
BatchLoader::GraphQL.for(title).batch(key: project) do |titles, loader, args|
LabelsFinder
diff --git a/app/graphql/types/time_tracking/timelog_category_type.rb b/app/graphql/types/time_tracking/timelog_category_type.rb
new file mode 100644
index 00000000000..c73a6fbd43b
--- /dev/null
+++ b/app/graphql/types/time_tracking/timelog_category_type.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module Types
+ module TimeTracking
+ class TimelogCategoryType < BaseObject
+ graphql_name 'TimeTrackingTimelogCategory'
+
+ authorize :read_timelog_category
+
+ field :id,
+ GraphQL::Types::ID,
+ null: false,
+ description: 'Internal ID of the timelog category.'
+
+ field :name,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Name of the category.'
+
+ field :description,
+ GraphQL::Types::String,
+ null: true,
+ description: 'Description of the category.'
+
+ field :color,
+ Types::ColorType,
+ null: true,
+ description: 'Color assigned to the category.'
+
+ field :billable,
+ GraphQL::Types::Boolean,
+ null: true,
+ description: 'Whether the category is billable or not.'
+
+ field :billing_rate,
+ GraphQL::Types::Float,
+ null: true,
+ description: 'Billing rate for the category.'
+
+ field :created_at,
+ Types::TimeType,
+ null: false,
+ description: 'When the category was created.'
+
+ field :updated_at,
+ Types::TimeType,
+ null: false,
+ description: 'When the category was last updated.'
+ end
+ end
+end
diff --git a/app/helpers/badges_helper.rb b/app/helpers/badges_helper.rb
index 46f0b043770..d48eae26a90 100644
--- a/app/helpers/badges_helper.rb
+++ b/app/helpers/badges_helper.rb
@@ -53,7 +53,7 @@ module BadgesHelper
#
# See also https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-badge--default.
def gl_badge_tag(*args, &block)
- if block_given?
+ if block
build_gl_badge_tag(capture(&block), *args)
else
build_gl_badge_tag(*args)
diff --git a/app/helpers/instance_configuration_helper.rb b/app/helpers/instance_configuration_helper.rb
index b06e3ff2904..7eb14d31dc7 100644
--- a/app/helpers/instance_configuration_helper.rb
+++ b/app/helpers/instance_configuration_helper.rb
@@ -4,7 +4,7 @@ module InstanceConfigurationHelper
def instance_configuration_cell_html(value, &block)
return '-' unless value.to_s.presence
- block_given? ? yield(value) : value
+ block ? yield(value) : value
end
def instance_configuration_host(host)
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 877785c9eaf..2d0bc1bc63f 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -39,7 +39,7 @@ module LabelsHelper
def link_to_label(label, type: :issue, tooltip: true, small: false, css_class: nil, &block)
link = label.filter_path(type: type)
- if block_given?
+ if block
link_to link, class: css_class, &block
else
render_label(label, link: link, tooltip: tooltip, small: small)
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index dbbe7069ca4..04619ad3bda 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -17,7 +17,7 @@ module TabHelper
class: [*html_options[:class], gl_tabs_classes].join(' ')
)
- content = capture(&block) if block_given?
+ content = capture(&block) if block
content_tag(:ul, content, html_options)
end
@@ -35,7 +35,7 @@ module TabHelper
link_classes = %w[nav-link gl-tab-nav-item]
active_link_classes = %w[active gl-tab-nav-item-active]
- if block_given?
+ if block
# Shift params to skip the omitted "name" param
html_options = options
options = name
@@ -54,7 +54,7 @@ module TabHelper
tab_class = %w[nav-item].push(*extra_tab_classes)
content_tag(:li, class: tab_class) do
- if block_given?
+ if block
link_to(options, html_options, &block)
else
link_to(name, options, html_options)
@@ -150,7 +150,7 @@ module TabHelper
o[:class] = [*o[:class], klass].join(' ')
o[:class].strip!
- if block_given?
+ if block
content_tag(:li, capture(&block), o)
else
content_tag(:li, nil, o)
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index e8ccb78692b..eb310845584 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -82,6 +82,8 @@ class Namespace < ApplicationRecord
has_many :work_items, inverse_of: :namespace
has_many :issues, inverse_of: :namespace
+ has_many :timelog_categories, class_name: 'TimeTracking::TimelogCategory'
+
validates :owner, presence: true, if: ->(n) { n.owner_required? }
validates :name,
presence: true,
diff --git a/app/policies/namespaces/group_project_namespace_shared_policy.rb b/app/policies/namespaces/group_project_namespace_shared_policy.rb
index 1ed9f05306f..bfb1706bc5a 100644
--- a/app/policies/namespaces/group_project_namespace_shared_policy.rb
+++ b/app/policies/namespaces/group_project_namespace_shared_policy.rb
@@ -2,8 +2,20 @@
module Namespaces
class GroupProjectNamespaceSharedPolicy < ::NamespacePolicy
- # Nothing here at the moment, but as we move policies from ProjectPolicy to ProjectNamespacePolicy,
+ # As we move policies from ProjectPolicy to ProjectNamespacePolicy,
# anything common with GroupPolicy but not with UserNamespacePolicy can go in here.
# See https://gitlab.com/groups/gitlab-org/-/epics/6689
+
+ condition(:timelog_categories_enabled, score: 0, scope: :subject) do
+ Feature.enabled?(:timelog_categories, @subject)
+ end
+
+ rule { ~timelog_categories_enabled }.policy do
+ prevent :read_timelog_category
+ end
+
+ rule { can?(:reporter_access) }.policy do
+ enable :read_timelog_category
+ end
end
end
diff --git a/app/policies/namespaces/project_namespace_policy.rb b/app/policies/namespaces/project_namespace_policy.rb
index 33aadc7c411..500c325138e 100644
--- a/app/policies/namespaces/project_namespace_policy.rb
+++ b/app/policies/namespaces/project_namespace_policy.rb
@@ -2,8 +2,8 @@
module Namespaces
class ProjectNamespacePolicy < Namespaces::GroupProjectNamespaceSharedPolicy
- # For now users are not granted any permissions on project namespace
- # as it's completely hidden to them. When we start using project
- # namespaces in queries, we will have to extend this policy.
+ # TODO: once https://gitlab.com/gitlab-org/gitlab/-/issues/364277 is solved, this
+ # should not be necessary anymore, and should be replaced with `delegate(:project)`.
+ delegate(:reload_project)
end
end
diff --git a/app/policies/time_tracking/timelog_category_policy.rb b/app/policies/time_tracking/timelog_category_policy.rb
new file mode 100644
index 00000000000..89161cdacfb
--- /dev/null
+++ b/app/policies/time_tracking/timelog_category_policy.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module TimeTracking
+ class TimelogCategoryPolicy < BasePolicy
+ delegate { @subject.namespace }
+ end
+end
diff --git a/app/services/base_count_service.rb b/app/services/base_count_service.rb
index ff1949ce4dd..eff3eb33c71 100644
--- a/app/services/base_count_service.rb
+++ b/app/services/base_count_service.rb
@@ -45,7 +45,7 @@ class BaseCountService
end
def update_cache_for_key(key, &block)
- Rails.cache.write(key, block_given? ? yield : uncached_count, raw: raw?)
+ Rails.cache.write(key, block ? yield : uncached_count, raw: raw?)
end
end
diff --git a/app/services/error_tracking/base_service.rb b/app/services/error_tracking/base_service.rb
index d2ecd0a6d5a..8458eb1f3b8 100644
--- a/app/services/error_tracking/base_service.rb
+++ b/app/services/error_tracking/base_service.rb
@@ -25,7 +25,7 @@ module ErrorTracking
errors = parse_errors(response)
return errors if errors
- yield if block_given?
+ yield if block
track_usage_event(params[:tracking_event], current_user.id) if params[:tracking_event]
diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb
index c3df9b153a1..cb2711b6fee 100644
--- a/app/services/users/update_service.rb
+++ b/app/services/users/update_service.rb
@@ -17,7 +17,7 @@ module Users
end
def execute(validate: true, check_password: false, &block)
- yield(@user) if block_given?
+ yield(@user) if block
user_exists = @user.persisted?
@user.user_detail # prevent assignment
diff --git a/app/services/webauthn/authenticate_service.rb b/app/services/webauthn/authenticate_service.rb
index a575a853995..52437a77df8 100644
--- a/app/services/webauthn/authenticate_service.rb
+++ b/app/services/webauthn/authenticate_service.rb
@@ -30,6 +30,8 @@ module Webauthn
false
end
+ private
+
##
# Validates that webauthn_credential is syntactically valid
#
diff --git a/config/feature_flags/development/timelog_categories.yml b/config/feature_flags/development/timelog_categories.yml
new file mode 100644
index 00000000000..64bb089bc97
--- /dev/null
+++ b/config/feature_flags/development/timelog_categories.yml
@@ -0,0 +1,8 @@
+---
+name: timelog_categories
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88462
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365829
+milestone: '15.3'
+type: development
+group: group::project management
+default_enabled: false
diff --git a/db/post_migrate/20220728134255_update_index_vulnerabilities_common_finder.rb b/db/post_migrate/20220728134255_update_index_vulnerabilities_common_finder.rb
new file mode 100644
index 00000000000..617d7661b5b
--- /dev/null
+++ b/db/post_migrate/20220728134255_update_index_vulnerabilities_common_finder.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class UpdateIndexVulnerabilitiesCommonFinder < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ NEW_INDEX_NAME = 'index_vulnerabilities_common_finder_query_on_default_branch'
+ OLD_INDEX_NAME = 'index_vulnerabilites_common_finder_query'
+
+ def up
+ add_concurrent_index :vulnerabilities, [:project_id, :state, :report_type, :present_on_default_branch,
+ :severity, :id], name: NEW_INDEX_NAME
+
+ remove_concurrent_index_by_name(:vulnerabilities, OLD_INDEX_NAME)
+ end
+
+ def down
+ add_concurrent_index :vulnerabilities, [:project_id, :state, :report_type, :severity, :id], name: OLD_INDEX_NAME
+
+ remove_concurrent_index_by_name(:vulnerabilities, NEW_INDEX_NAME)
+ end
+end
diff --git a/db/schema_migrations/20220728134255 b/db/schema_migrations/20220728134255
new file mode 100644
index 00000000000..f5ab53d70a0
--- /dev/null
+++ b/db/schema_migrations/20220728134255
@@ -0,0 +1 @@
+c868a83176c8e0024ef16e0f95d8a16a0f1b7be0c1a5d58902397cc0462a7e34
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 0af3f9a8dec..3b36e4546cd 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -30098,7 +30098,7 @@ CREATE INDEX index_vuln_reads_on_namespace_id_state_severity_and_vuln_id ON vuln
CREATE INDEX index_vuln_reads_on_project_id_state_severity_and_vuln_id ON vulnerability_reads USING btree (project_id, state, severity, vulnerability_id DESC);
-CREATE INDEX index_vulnerabilites_common_finder_query ON vulnerabilities USING btree (project_id, state, report_type, severity, id);
+CREATE INDEX index_vulnerabilities_common_finder_query_on_default_branch ON vulnerabilities USING btree (project_id, state, report_type, present_on_default_branch, severity, id);
CREATE INDEX index_vulnerabilities_on_author_id ON vulnerabilities USING btree (author_id);
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index e50f9170a9d..ddafa3e086d 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -8925,6 +8925,29 @@ The edge type for [`TestSuiteSummary`](#testsuitesummary).
| `cursor` | [`String!`](#string) | A cursor for use in pagination. |
| `node` | [`TestSuiteSummary`](#testsuitesummary) | The item at the end of the edge. |
+#### `TimeTrackingTimelogCategoryConnection`
+
+The connection type for [`TimeTrackingTimelogCategory`](#timetrackingtimelogcategory).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `edges` | [`[TimeTrackingTimelogCategoryEdge]`](#timetrackingtimelogcategoryedge) | A list of edges. |
+| `nodes` | [`[TimeTrackingTimelogCategory]`](#timetrackingtimelogcategory) | A list of nodes. |
+| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `TimeTrackingTimelogCategoryEdge`
+
+The edge type for [`TimeTrackingTimelogCategory`](#timetrackingtimelogcategory).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| `node` | [`TimeTrackingTimelogCategory`](#timetrackingtimelogcategory) | The item at the end of the edge. |
+
#### `TimelineEventTypeConnection`
The connection type for [`TimelineEventType`](#timelineeventtype).
@@ -12157,6 +12180,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| `storageSizeLimit` | [`Float`](#float) | Total storage limit of the root namespace in bytes. |
| `subgroupCreationLevel` | [`String`](#string) | Permission level required to create subgroups within the group. |
| `temporaryStorageIncreaseEndsOn` | [`Time`](#time) | Date until the temporary storage increase is active. |
+| `timelogCategories` | [`TimeTrackingTimelogCategoryConnection`](#timetrackingtimelogcategoryconnection) | Timelog categories for the namespace. Available only when feature flag `timelog_categories` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. (see [Connections](#connections)) |
| `totalRepositorySize` | [`Float`](#float) | Total repository size of all projects in the root namespace in bytes. |
| `totalRepositorySizeExcess` | [`Float`](#float) | Total excess repository size of all projects in the root namespace in bytes. |
| `twoFactorGracePeriod` | [`Int`](#int) | Time before two-factor authentication is enforced. |
@@ -14727,6 +14751,7 @@ Contains statistics about a milestone.
| `sharedRunnersSetting` | [`SharedRunnersSetting`](#sharedrunnerssetting) | Shared runners availability for the namespace and its descendants. |
| `storageSizeLimit` | [`Float`](#float) | Total storage limit of the root namespace in bytes. |
| `temporaryStorageIncreaseEndsOn` | [`Time`](#time) | Date until the temporary storage increase is active. |
+| `timelogCategories` | [`TimeTrackingTimelogCategoryConnection`](#timetrackingtimelogcategoryconnection) | Timelog categories for the namespace. Available only when feature flag `timelog_categories` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. (see [Connections](#connections)) |
| `totalRepositorySize` | [`Float`](#float) | Total repository size of all projects in the root namespace in bytes. |
| `totalRepositorySizeExcess` | [`Float`](#float) | Total excess repository size of all projects in the root namespace in bytes. |
| `visibility` | [`String`](#string) | Visibility of the namespace. |
@@ -15493,6 +15518,7 @@ Represents vulnerability finding of a security report on the pipeline.
| `suggestionCommitMessage` | [`String`](#string) | Commit message used to apply merge request suggestions. |
| `tagList` **{warning-solid}** | [`String`](#string) | **Deprecated** in 13.12. Use `topics`. |
| `terraformStates` | [`TerraformStateConnection`](#terraformstateconnection) | Terraform states associated with the project. (see [Connections](#connections)) |
+| `timelogCategories` | [`TimeTrackingTimelogCategoryConnection`](#timetrackingtimelogcategoryconnection) | Timelog categories for the project. Available only when feature flag `timelog_categories` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. (see [Connections](#connections)) |
| `topics` | [`[String!]`](#string) | List of project topics. |
| `userPermissions` | [`ProjectPermissions!`](#projectpermissions) | Permissions for the current user on the resource. |
| `visibility` | [`String`](#string) | Visibility of the project. |
@@ -17733,6 +17759,21 @@ Represents the time report stats for timeboxes.
| `incomplete` | [`TimeboxMetrics`](#timeboxmetrics) | Incomplete issues metrics. |
| `total` | [`TimeboxMetrics`](#timeboxmetrics) | Total issues metrics. |
+### `TimeTrackingTimelogCategory`
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `billable` | [`Boolean`](#boolean) | Whether the category is billable or not. |
+| `billingRate` | [`Float`](#float) | Billing rate for the category. |
+| `color` | [`Color`](#color) | Color assigned to the category. |
+| `createdAt` | [`Time!`](#time) | When the category was created. |
+| `description` | [`String`](#string) | Description of the category. |
+| `id` | [`ID!`](#id) | Internal ID of the timelog category. |
+| `name` | [`String!`](#string) | Name of the category. |
+| `updatedAt` | [`Time!`](#time) | When the category was last updated. |
+
### `TimeboxMetrics`
Represents measured stats metrics for timeboxes.
diff --git a/doc/user/admin_area/monitoring/background_migrations.md b/doc/user/admin_area/monitoring/background_migrations.md
index 02d32099c63..87374849674 100644
--- a/doc/user/admin_area/monitoring/background_migrations.md
+++ b/doc/user/admin_area/monitoring/background_migrations.md
@@ -48,6 +48,50 @@ To disable it:
Feature.disable(:execute_batched_migrations_on_schedule)
```
+### Pause batched background migrations in GitLab 14.x
+
+To pause an ongoing batched background migration, use the `disable` command above.
+This command causes the migration to complete the current batch, and then wait to start the next batch.
+
+Use the following database queries to see the state of the current batched background migration:
+
+1. Obtain the ID of the running migration:
+
+ ```sql
+ SELECT
+ id job_class_name,
+ table_name,
+ column_name,
+ job_arguments
+ FROM batched_background_migrations
+ WHERE status <> 3;
+ ```
+
+1. Run this query, replacing `XX` with the ID you obtained in the previous step,
+ to see the status of the migration:
+
+ ```sql
+ SELECT
+ started_at,
+ finished_at,
+ finished_at - started_at AS duration,
+ min_value,
+ max_value,
+ batch_size,
+ sub_batch_size
+ FROM batched_background_migration_jobs
+ WHERE batched_background_migration_id = XX
+ ORDER BY id DESC
+ limit 10;
+ ```
+
+1. Run the query multiple times within a few minutes to ensure no new row has been added.
+ If no new row has been added, the migration has been paused.
+
+1. After confirming the migration has paused, restart the migration (using the `enable`
+ command above) to proceed with the batch when ready. On larger instances,
+ background migrations can take as long as 48 hours to complete each batch.
+
## Automatic batch size optimization
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60133) in GitLab 13.12.
diff --git a/doc/user/infrastructure/clusters/manage/management_project_applications/prometheus.md b/doc/user/infrastructure/clusters/manage/management_project_applications/prometheus.md
index 383e857bb20..64d325dedc6 100644
--- a/doc/user/infrastructure/clusters/manage/management_project_applications/prometheus.md
+++ b/doc/user/infrastructure/clusters/manage/management_project_applications/prometheus.md
@@ -1,27 +1,11 @@
---
-stage: Monitor
-group: Respond
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+redirect_to: '../../../../../operations/metrics/index.md'
+remove_date: '2022-11-03'
---
-# Install Prometheus with a cluster management project **(FREE)**
+This document was moved to [another location](../../../../../operations/metrics/index.md).
-> [Introduced](https://gitlab.com/gitlab-org/project-templates/cluster-management/-/merge_requests/5) in GitLab 14.0.
-
-[Prometheus](https://prometheus.io/docs/introduction/overview/) is an
-open-source monitoring and alerting system for supervising your
-deployed applications.
-
-Assuming you already have a project created from a
-[management project template](../../../../../user/clusters/management_project_template.md), to install Prometheus you should
-uncomment this line from your `helmfile.yaml`:
-
-```yaml
- - path: applications/prometheus/helmfile.yaml
-```
-
-You can customize the installation of Prometheus by updating the
-`applications/prometheus/values.yaml` file in your cluster
-management project. Refer to the
-[Configuration section](https://github.com/helm/charts/tree/master/stable/prometheus#configuration)
-of the Prometheus chart's README for the available configuration options.
+
+
+
+
diff --git a/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md b/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md
index d2d314b649e..f42e9c83120 100644
--- a/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md
+++ b/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md
@@ -1,70 +1,11 @@
---
-stage: Monitor
-group: Respond
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+redirect_to: '../../../../../operations/error_tracking.md'
+remove_date: '2022-11-03'
---
-# Install Sentry with a cluster management project **(FREE)**
+This document was moved to [another location](../../../../../operations/error_tracking.md).
-> [Introduced](https://gitlab.com/gitlab-org/project-templates/cluster-management/-/merge_requests/5) in GitLab 14.0.
-
-The Sentry Helm chart [recommends](https://github.com/helm/charts/blob/f6e5784f265dd459c5a77430185d0302ed372665/stable/sentry/values.yaml#L284-L285)
-at least 3 GB of available RAM for database migrations.
-
-Assuming you already have a project created from a
-[management project template](../../../../../user/clusters/management_project_template.md), to install Sentry you should
-uncomment this line from your `helmfile.yaml`:
-
-```yaml
- - path: applications/sentry/helmfile.yaml
-```
-
-Sentry is installed by default into the `gitlab-managed-apps` namespace
-of your cluster.
-
-You can customize the installation of Sentry by defining
-`applications/sentry/values.yaml` file in your cluster
-management project. Refer to the
-[chart](https://github.com/helm/charts/tree/master/stable/sentry)
-for the available configuration options.
-
-We recommend you pay close attention to the following configuration options:
-
-- `email`. Needed to invite users to your Sentry instance and to send error emails.
-- `user`. Where you can set the login credentials for the default administrator user.
-- `postgresql`. For a PostgreSQL password that can be used when running future updates.
-
-When upgrading, it's important to provide the existing PostgreSQL password (given
-using the `postgresql.postgresqlPassword` key) to avoid authentication errors.
-Read the [PostgreSQL chart documentation](https://github.com/helm/charts/tree/master/stable/postgresql#upgrade)
-for more information.
-
-Here is an example configuration for Sentry:
-
-```yaml
-# Admin user to create
-user:
- # Indicated to create the admin user or not,
- # Default is true as the initial installation.
- create: true
- email: ""
- password: ""
-
-email:
- from_address: ""
- host: smtp
- port: 25
- use_tls: false
- user: ""
- password: ""
- enable_replies: false
-
-ingress:
- enabled: true
- hostname: ""
-
-# Needs to be here between runs.
-# See https://github.com/helm/charts/tree/master/stable/postgresql#upgrade for more info
-postgresql:
- postgresqlPassword: example-postgresql-password
-```
+
+
+
+
diff --git a/lib/bulk_imports/clients/http.rb b/lib/bulk_imports/clients/http.rb
index 037da5e0816..3e940742de9 100644
--- a/lib/bulk_imports/clients/http.rb
+++ b/lib/bulk_imports/clients/http.rb
@@ -35,7 +35,7 @@ module BulkImports
end
def each_page(method, resource, query = {}, &block)
- return to_enum(__method__, method, resource, query) unless block_given?
+ return to_enum(__method__, method, resource, query) unless block
next_page = @page
diff --git a/lib/gitlab/batch_pop_queueing.rb b/lib/gitlab/batch_pop_queueing.rb
index 62fc8cd048e..103ce644f2b 100644
--- a/lib/gitlab/batch_pop_queueing.rb
+++ b/lib/gitlab/batch_pop_queueing.rb
@@ -73,7 +73,7 @@ module Gitlab
begin
all_args = pop_all
- yield all_args if block_given?
+ yield all_args if block
{ status: :finished, new_items: peek_all }
ensure
diff --git a/lib/gitlab/cache/request_cache.rb b/lib/gitlab/cache/request_cache.rb
index 3ad919fbba8..13b4cace08a 100644
--- a/lib/gitlab/cache/request_cache.rb
+++ b/lib/gitlab/cache/request_cache.rb
@@ -15,7 +15,7 @@ module Gitlab
attr_accessor :request_cache_key_block
def request_cache_key(&block)
- if block_given?
+ if block
self.request_cache_key_block = block
else
request_cache_key_block
diff --git a/lib/gitlab/ci/trace/chunked_io.rb b/lib/gitlab/ci/trace/chunked_io.rb
index 9f24ba99201..32f64948635 100644
--- a/lib/gitlab/ci/trace/chunked_io.rb
+++ b/lib/gitlab/ci/trace/chunked_io.rb
@@ -22,7 +22,7 @@ module Gitlab
@chunks_cache = []
@tell = 0
@size = calculate_size
- yield self if block_given?
+ yield self if block
end
def close
diff --git a/lib/gitlab/database/bulk_update.rb b/lib/gitlab/database/bulk_update.rb
index b1f9da30585..d68be19047e 100644
--- a/lib/gitlab/database/bulk_update.rb
+++ b/lib/gitlab/database/bulk_update.rb
@@ -157,7 +157,7 @@ module Gitlab
def self.execute(columns, mapping, &to_class)
raise ArgumentError if mapping.blank?
- entries_by_class = mapping.group_by { |k, v| block_given? ? to_class.call(k) : k.class }
+ entries_by_class = mapping.group_by { |k, v| to_class ? to_class.call(k) : k.class }
entries_by_class.each do |model, entries|
Setter.new(model, columns, entries).update!
diff --git a/lib/gitlab/database/with_lock_retries.rb b/lib/gitlab/database/with_lock_retries.rb
index f2c5bb9088f..3206c5626c3 100644
--- a/lib/gitlab/database/with_lock_retries.rb
+++ b/lib/gitlab/database/with_lock_retries.rb
@@ -83,7 +83,7 @@ module Gitlab
# @param [Boolean] raise_on_exhaustion whether to raise `AttemptsExhaustedError` when exhausting max attempts
# @param [Proc] block of code that will be executed
def run(raise_on_exhaustion: false, &block)
- raise 'no block given' unless block_given?
+ raise 'no block given' unless block
@block = block
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index d2495b32800..11a41149274 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -107,7 +107,7 @@ module Gitlab
#
# rubocop: disable GitlabSecurity/PublicSend
def each_page(method, *args, &block)
- return to_enum(__method__, method, *args) unless block_given?
+ return to_enum(__method__, method, *args) unless block
page =
if args.last.is_a?(Hash) && args.last[:page]
@@ -134,7 +134,7 @@ module Gitlab
# method - The method to send to Octokit for querying data.
# args - Any arguments to pass to the Octokit method.
def each_object(method, *args, &block)
- return to_enum(__method__, method, *args) unless block_given?
+ return to_enum(__method__, method, *args) unless block
each_page(method, *args) do |page|
page.objects.each do |object|
diff --git a/lib/gitlab/legacy_github_import/client.rb b/lib/gitlab/legacy_github_import/client.rb
index 7a9dae3a3de..7d78c8dee25 100644
--- a/lib/gitlab/legacy_github_import/client.rb
+++ b/lib/gitlab/legacy_github_import/client.rb
@@ -136,7 +136,7 @@ module Gitlab
last_response = api.last_response
- if block_given?
+ if block
yield data
# api.last_response could change while we're yielding (e.g. fetching labels for each PR)
# so we cache our own last response
diff --git a/lib/gitlab/metrics/methods/metric_options.rb b/lib/gitlab/metrics/methods/metric_options.rb
index 1e488df3e99..e93a90415c7 100644
--- a/lib/gitlab/metrics/methods/metric_options.rb
+++ b/lib/gitlab/metrics/methods/metric_options.rb
@@ -61,7 +61,7 @@ module Gitlab
end
def evaluate(&block)
- instance_eval(&block) if block_given?
+ instance_eval(&block) if block
self
end
diff --git a/lib/gitlab/null_request_store.rb b/lib/gitlab/null_request_store.rb
index 8db331dcb9f..4642dcf9e91 100644
--- a/lib/gitlab/null_request_store.rb
+++ b/lib/gitlab/null_request_store.rb
@@ -35,7 +35,7 @@ module Gitlab
end
def delete(key, &block)
- yield(key) if block_given?
+ yield(key) if block
end
end
end
diff --git a/lib/gitlab/quick_actions/dsl.rb b/lib/gitlab/quick_actions/dsl.rb
index a2dfcc6de9a..dfbc00ef847 100644
--- a/lib/gitlab/quick_actions/dsl.rb
+++ b/lib/gitlab/quick_actions/dsl.rb
@@ -30,11 +30,11 @@ module Gitlab
# # Awesome code block
# end
def desc(text = '', &block)
- @description = block_given? ? block : text
+ @description = block || text
end
def warning(text = '', &block)
- @warning = block_given? ? block : text
+ @warning = block || text
end
def icon(string = '')
@@ -51,7 +51,7 @@ module Gitlab
# # Awesome code block
# end
def params(*params, &block)
- @params = block_given? ? block : params
+ @params = block || params
end
# Allows to give an explanation of what the command will do when
@@ -67,7 +67,7 @@ module Gitlab
# # Awesome code block
# end
def explanation(text = '', &block)
- @explanation = block_given? ? block : text
+ @explanation = block || text
end
# Allows to provide a message about quick action execution result, success or failure.
@@ -96,7 +96,7 @@ module Gitlab
# end
#
def execution_message(text = '', &block)
- @execution_message = block_given? ? block : text
+ @execution_message = block || text
end
# Allows to define type(s) that must be met in order for the command
diff --git a/lib/gitlab/redis/multi_store.rb b/lib/gitlab/redis/multi_store.rb
index 94f06e957cf..cdd2ac6100e 100644
--- a/lib/gitlab/redis/multi_store.rb
+++ b/lib/gitlab/redis/multi_store.rb
@@ -274,7 +274,7 @@ module Gitlab
# rubocop:disable GitlabSecurity/PublicSend
def send_command(redis_instance, command_name, *args, **kwargs, &block)
- if block_given?
+ if block
# Make sure that block is wrapped and executed only on the redis instance that is executing the block
redis_instance.send(command_name, *args, **kwargs) do |*params|
with_instance(redis_instance, *params, &block)
diff --git a/lib/gitlab/safe_request_loader.rb b/lib/gitlab/safe_request_loader.rb
index 89eca16c272..4fc88322210 100644
--- a/lib/gitlab/safe_request_loader.rb
+++ b/lib/gitlab/safe_request_loader.rb
@@ -14,7 +14,7 @@ module Gitlab
end
def execute(&block)
- raise ArgumentError, 'Block is mandatory' unless block_given?
+ raise ArgumentError, 'Block is mandatory' unless block
load_resource_data
remove_loaded_resource_ids
diff --git a/lib/gitlab/search/query.rb b/lib/gitlab/search/query.rb
index 97ee7c7817d..4c5fae87420 100644
--- a/lib/gitlab/search/query.rb
+++ b/lib/gitlab/search/query.rb
@@ -13,7 +13,7 @@ module Gitlab
@filters = []
@filter_options = { default_parser: :downcase.to_proc }.merge(filter_opts)
- self.instance_eval(&block) if block_given?
+ self.instance_eval(&block) if block
@query = Gitlab::Search::ParsedQuery.new(*extract_filters)
# set the ParsedQuery as our default delegator thanks to SimpleDelegator
diff --git a/lib/gitlab/string_placeholder_replacer.rb b/lib/gitlab/string_placeholder_replacer.rb
index 62621255a53..f77bd8e2d9f 100644
--- a/lib/gitlab/string_placeholder_replacer.rb
+++ b/lib/gitlab/string_placeholder_replacer.rb
@@ -10,7 +10,7 @@ module Gitlab
# placeholder will be returned.
def self.replace_string_placeholders(string, placeholder_regex = nil, &block)
- return string if string.blank? || placeholder_regex.blank? || !block_given?
+ return string if string.blank? || placeholder_regex.blank? || !block
replace_placeholders(string, placeholder_regex, &block)
end
diff --git a/lib/gitlab/terraform/state_migration_helper.rb b/lib/gitlab/terraform/state_migration_helper.rb
index 04c1cbd0373..e86144f91a5 100644
--- a/lib/gitlab/terraform/state_migration_helper.rb
+++ b/lib/gitlab/terraform/state_migration_helper.rb
@@ -22,7 +22,7 @@ module Gitlab
versions.find_each(batch_size: batch_size) do |version| # rubocop:disable CodeReuse/ActiveRecord
version.file.migrate!(store)
- yield version if block_given?
+ yield version if block
end
end
end
diff --git a/lib/gitlab/usage/metrics/instrumentations/base_metric.rb b/lib/gitlab/usage/metrics/instrumentations/base_metric.rb
index f76ed1753b2..5e20766b1b4 100644
--- a/lib/gitlab/usage/metrics/instrumentations/base_metric.rb
+++ b/lib/gitlab/usage/metrics/instrumentations/base_metric.rb
@@ -13,7 +13,7 @@ module Gitlab
class << self
def available?(&block)
- return @metric_available = block if block_given?
+ return @metric_available = block if block
return @metric_available.call if instance_variable_defined?('@metric_available')
diff --git a/lib/gitlab/usage/metrics/instrumentations/database_metric.rb b/lib/gitlab/usage/metrics/instrumentations/database_metric.rb
index 9ea5ba12b69..6dec0349a38 100644
--- a/lib/gitlab/usage/metrics/instrumentations/database_metric.rb
+++ b/lib/gitlab/usage/metrics/instrumentations/database_metric.rb
@@ -23,25 +23,25 @@ module Gitlab
private_constant :IMPLEMENTED_OPERATIONS
def start(&block)
- return @metric_start&.call unless block_given?
+ return @metric_start&.call unless block
@metric_start = block
end
def finish(&block)
- return @metric_finish&.call unless block_given?
+ return @metric_finish&.call unless block
@metric_finish = block
end
def relation(&block)
- return @metric_relation&.call unless block_given?
+ return @metric_relation&.call unless block
@metric_relation = block
end
def metric_options(&block)
- return @metric_options&.call.to_h unless block_given?
+ return @metric_options&.call.to_h unless block
@metric_options = block
end
@@ -55,7 +55,7 @@ module Gitlab
@metric_operation = symbol
@column = column
- @metric_operation_block = block if block_given?
+ @metric_operation_block = block if block
end
def cache_start_and_finish_as(cache_key)
diff --git a/lib/gitlab/usage/metrics/instrumentations/numbers_metric.rb b/lib/gitlab/usage/metrics/instrumentations/numbers_metric.rb
index 8504ee368fc..3b20e6ad100 100644
--- a/lib/gitlab/usage/metrics/instrumentations/numbers_metric.rb
+++ b/lib/gitlab/usage/metrics/instrumentations/numbers_metric.rb
@@ -26,7 +26,7 @@ module Gitlab
private_constant :IMPLEMENTED_OPERATIONS
def data(&block)
- return @metric_data&.call unless block_given?
+ return @metric_data&.call unless block
@metric_data = block
end
diff --git a/lib/gitlab/usage_data_queries.rb b/lib/gitlab/usage_data_queries.rb
index fef5cd680cb..c2983779603 100644
--- a/lib/gitlab/usage_data_queries.rb
+++ b/lib/gitlab/usage_data_queries.rb
@@ -53,7 +53,7 @@ module Gitlab
end
def alt_usage_data(value = nil, fallback: FALLBACK, &block)
- if block_given?
+ if block
{ alt_usage_data_block: "non-SQL usage data block" }
else
{ alt_usage_data_value: value }
@@ -61,7 +61,7 @@ module Gitlab
end
def redis_usage_data(counter = nil, &block)
- if block_given?
+ if block
{ redis_usage_data_block: "non-SQL usage data block" }
elsif counter.present?
{ redis_usage_data_counter: counter.to_s }
diff --git a/lib/gitlab/utils/usage_data.rb b/lib/gitlab/utils/usage_data.rb
index 4d1b234ae54..19bdeefed7e 100644
--- a/lib/gitlab/utils/usage_data.rb
+++ b/lib/gitlab/utils/usage_data.rb
@@ -196,7 +196,7 @@ module Gitlab
def alt_usage_data(value = nil, fallback: FALLBACK, &block)
with_duration do
- if block_given?
+ if block
yield
else
value
@@ -209,7 +209,7 @@ module Gitlab
def redis_usage_data(counter = nil, &block)
with_duration do
- if block_given?
+ if block
redis_usage_counter(&block)
elsif counter.present?
redis_usage_data_totals(counter)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a3cef05e0a9..6ca4f353dda 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -16895,6 +16895,9 @@ msgstr ""
msgid "Geo|%{component} verified"
msgstr ""
+msgid "Geo|%{label} %{timeAgo}"
+msgstr ""
+
msgid "Geo|%{label} can't be blank"
msgstr ""
@@ -23016,9 +23019,6 @@ msgstr ""
msgid "Last sign-in at:"
msgstr ""
-msgid "Last successful sync"
-msgstr ""
-
msgid "Last successful update"
msgstr ""
@@ -26432,9 +26432,6 @@ msgstr ""
msgid "Normal text"
msgstr ""
-msgid "Not Implemented"
-msgstr ""
-
msgid "Not all browsers support U2F devices. Therefore, we require that you set up a two-factor authentication app first. That way you'll always be able to sign in - even when you're using an unsupported browser."
msgstr ""
diff --git a/package.json b/package.json
index 9bb0cb98a05..578689e4ed1 100644
--- a/package.json
+++ b/package.json
@@ -135,7 +135,7 @@
"lowlight": "^2.6.1",
"marked": "^0.3.12",
"mathjax": "3",
- "mermaid": "^9.1.1",
+ "mermaid": "^9.1.3",
"micromatch": "^4.0.5",
"minimatch": "^3.0.4",
"monaco-editor": "^0.28.0",
diff --git a/qa/qa/page/view.rb b/qa/qa/page/view.rb
index fa17b8fe302..6a6396b8ed4 100644
--- a/qa/qa/page/view.rb
+++ b/qa/qa/page/view.rb
@@ -37,7 +37,7 @@ module QA
def self.evaluate(&block)
Page::View::DSL.new.tap do |evaluator|
- evaluator.instance_exec(&block) if block_given?
+ evaluator.instance_exec(&block) if block
end
end
diff --git a/spec/graphql/types/namespace_type_spec.rb b/spec/graphql/types/namespace_type_spec.rb
index 3b7f7e65e4b..168a6ba4eaa 100644
--- a/spec/graphql/types/namespace_type_spec.rb
+++ b/spec/graphql/types/namespace_type_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe GitlabSchema.types['Namespace'] do
expected_fields = %w[
id name path full_name full_path description description_html visibility
lfs_enabled request_access_enabled projects root_storage_statistics shared_runners_setting
+ timelog_categories
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 6b769230d92..fbb3aca3376 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -36,8 +36,7 @@ RSpec.describe GitlabSchema.types['Project'] do
pipeline_analytics squash_read_only sast_ci_configuration
cluster_agent cluster_agents agent_configurations
ci_template timelogs merge_commit_template squash_commit_template work_item_types
- recent_issue_boards ci_config_path_or_default packages_cleanup_policy ci_variables
- recent_issue_boards ci_config_path_or_default ci_variables
+ recent_issue_boards ci_config_path_or_default packages_cleanup_policy ci_variables timelog_categories
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/time_tracking/timelog_category_type_spec.rb b/spec/graphql/types/time_tracking/timelog_category_type_spec.rb
new file mode 100644
index 00000000000..a14069e8b58
--- /dev/null
+++ b/spec/graphql/types/time_tracking/timelog_category_type_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['TimeTrackingTimelogCategory'] do
+ let(:fields) do
+ %w[
+ id
+ name
+ description
+ color
+ billable
+ billing_rate
+ created_at
+ updated_at
+ ]
+ end
+
+ it { expect(described_class.graphql_name).to eq('TimeTrackingTimelogCategory') }
+ it { expect(described_class).to have_graphql_fields(fields) }
+ it { expect(described_class).to require_graphql_authorizations(:read_timelog_category) }
+end
diff --git a/spec/lib/api/helpers/authentication_spec.rb b/spec/lib/api/helpers/authentication_spec.rb
index eea5c10d4f8..ac5886fdadd 100644
--- a/spec/lib/api/helpers/authentication_spec.rb
+++ b/spec/lib/api/helpers/authentication_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe API::Helpers::Authentication do
class << cls
def helpers(*modules, &block)
modules.each { |m| include m }
- include Module.new.tap { |m| m.class_eval(&block) } if block_given?
+ include Module.new.tap { |m| m.class_eval(&block) } if block
end
end
diff --git a/spec/lib/gitlab/slash_commands/deploy_spec.rb b/spec/lib/gitlab/slash_commands/deploy_spec.rb
index 5167523ff58..5af234ff88e 100644
--- a/spec/lib/gitlab/slash_commands/deploy_spec.rb
+++ b/spec/lib/gitlab/slash_commands/deploy_spec.rb
@@ -165,7 +165,7 @@ RSpec.describe Gitlab::SlashCommands::Deploy do
context 'with ReDoS attempts' do
def duration_for(&block)
start = Time.zone.now
- yield if block_given?
+ yield if block
Time.zone.now - start
end
diff --git a/spec/models/namespaces/project_namespace_spec.rb b/spec/models/namespaces/project_namespace_spec.rb
index c995571c3c9..78403db7fa8 100644
--- a/spec/models/namespaces/project_namespace_spec.rb
+++ b/spec/models/namespaces/project_namespace_spec.rb
@@ -5,6 +5,14 @@ require 'spec_helper'
RSpec.describe Namespaces::ProjectNamespace, type: :model do
describe 'relationships' do
it { is_expected.to have_one(:project).with_foreign_key(:project_namespace_id).inverse_of(:project_namespace) }
+
+ specify do
+ project = create(:project)
+ namespace = project.project_namespace
+ namespace.reload_project
+
+ expect(namespace.project).to eq project
+ end
end
describe 'validations' do
diff --git a/spec/models/u2f_registration_spec.rb b/spec/models/u2f_registration_spec.rb
index 027d26d9657..1fab3882c2a 100644
--- a/spec/models/u2f_registration_spec.rb
+++ b/spec/models/u2f_registration_spec.rb
@@ -6,21 +6,67 @@ RSpec.describe U2fRegistration do
let_it_be(:user) { create(:user) }
let(:u2f_registration_name) { 'u2f_device' }
- let(:u2f_registration) do
- device = U2F::FakeU2F.new(FFaker::BaconIpsum.characters(5))
- create(
- :u2f_registration, name: u2f_registration_name,
- user: user,
- certificate: Base64.strict_encode64(device.cert_raw),
- key_handle: U2F.urlsafe_encode64(device.key_handle_raw),
- public_key: Base64.strict_encode64(device.origin_public_key_raw)
- )
+ let(:app_id) { FFaker::BaconIpsum.characters(5) }
+ let(:device) { U2F::FakeU2F.new(app_id) }
+
+ describe '.authenticate' do
+ context 'when registration is found' do
+ it 'returns true' do
+ create_u2f_registration
+ device_challenge = U2F.urlsafe_encode64(SecureRandom.random_bytes(32))
+ sign_response_json = device.sign_response(device_challenge)
+
+ response = U2fRegistration.authenticate(
+ user,
+ app_id,
+ sign_response_json,
+ device_challenge
+ )
+
+ expect(response).to eq true
+ end
+ end
+
+ context 'when registration not found' do
+ it 'returns nil' do
+ device_challenge = U2F.urlsafe_encode64(SecureRandom.random_bytes(32))
+ sign_response_json = device.sign_response(device_challenge)
+
+ # data is valid but user does not have any u2f_registrations
+ response = U2fRegistration.authenticate(
+ user,
+ app_id,
+ sign_response_json,
+ device_challenge
+ )
+
+ expect(response).to eq nil
+ end
+ end
+
+ context 'when args passed in are invalid' do
+ it 'returns false' do
+ some_app_id = 123
+ invalid_json = 'invalid JSON'
+ challenges = 'whatever'
+
+ response = U2fRegistration.authenticate(
+ user,
+ some_app_id,
+ invalid_json,
+ challenges
+ )
+
+ expect(response).to eq false
+ end
+ end
end
describe 'callbacks' do
describe 'after create' do
shared_examples_for 'creates webauthn registration' do
it 'creates webauthn registration' do
+ u2f_registration = create_u2f_registration
webauthn_registration = WebauthnRegistration.where(u2f_registration_id: u2f_registration.id)
expect(webauthn_registration).to exist
end
@@ -51,13 +97,14 @@ RSpec.describe U2fRegistration do
receive(:track_exception).with(kind_of(StandardError),
u2f_registration_id: 123))
- u2f_registration
+ create_u2f_registration
end
end
describe 'after update' do
context 'when counter is updated' do
it 'updates the webauthn registration counter to be the same value' do
+ u2f_registration = create_u2f_registration
new_counter = u2f_registration.counter + 1
webauthn_registration = WebauthnRegistration.find_by(u2f_registration_id: u2f_registration.id)
@@ -70,6 +117,7 @@ RSpec.describe U2fRegistration do
context 'when sign count of registration is not updated' do
it 'does not update the counter' do
+ u2f_registration = create_u2f_registration
webauthn_registration = WebauthnRegistration.find_by(u2f_registration_id: u2f_registration.id)
expect do
@@ -79,4 +127,15 @@ RSpec.describe U2fRegistration do
end
end
end
+
+ def create_u2f_registration
+ create(
+ :u2f_registration,
+ name: u2f_registration_name,
+ user: user,
+ certificate: Base64.strict_encode64(device.cert_raw),
+ key_handle: U2F.urlsafe_encode64(device.key_handle_raw),
+ public_key: Base64.strict_encode64(device.origin_public_key_raw)
+ )
+ end
end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index c513baea517..57923142648 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -1229,4 +1229,12 @@ RSpec.describe GroupPolicy do
it { is_expected.to be_disallowed(:admin_crm_contact) }
it { is_expected.to be_disallowed(:admin_crm_organization) }
end
+
+ it_behaves_like 'checks timelog categories permissions' do
+ let(:group) { create(:group) }
+ let(:namespace) { group }
+ let(:users_container) { group }
+
+ subject { described_class.new(current_user, group) }
+ end
end
diff --git a/spec/policies/namespaces/project_namespace_policy_spec.rb b/spec/policies/namespaces/project_namespace_policy_spec.rb
index 5ceea9dfb9d..4519f44a6ad 100644
--- a/spec/policies/namespaces/project_namespace_policy_spec.rb
+++ b/spec/policies/namespaces/project_namespace_policy_spec.rb
@@ -3,45 +3,11 @@
require 'spec_helper'
RSpec.describe Namespaces::ProjectNamespacePolicy do
- let_it_be(:parent) { create(:namespace) }
- let_it_be(:project) { create(:project, namespace: parent) }
- let_it_be(:namespace) { project.project_namespace }
-
- let(:permissions) do
- [:owner_access, :create_projects, :admin_namespace, :read_namespace,
- :read_statistics, :transfer_projects, :admin_package,
- :create_jira_connect_subscription]
- end
-
subject { described_class.new(current_user, namespace) }
- context 'with no user' do
- let_it_be(:current_user) { nil }
-
- it { is_expected.to be_disallowed(*permissions) }
- end
-
- context 'regular user' do
- let_it_be(:current_user) { create(:user) }
-
- it { is_expected.to be_disallowed(*permissions) }
- end
-
- context 'parent owner' do
- let_it_be(:current_user) { parent.first_owner }
-
- it { is_expected.to be_disallowed(*permissions) }
- end
-
- context 'admin' do
- let_it_be(:current_user) { create(:admin) }
-
- context 'when admin mode is enabled', :enable_admin_mode do
- it { is_expected.to be_disallowed(*permissions) }
- end
-
- context 'when admin mode is disabled' do
- it { is_expected.to be_disallowed(*permissions) }
- end
+ it_behaves_like 'checks timelog categories permissions' do
+ let(:project) { create(:project) }
+ let(:namespace) { project.project_namespace }
+ let(:users_container) { project }
end
end
diff --git a/spec/requests/api/graphql/group_query_spec.rb b/spec/requests/api/graphql/group_query_spec.rb
index fd0ee5d52b9..41750f44794 100644
--- a/spec/requests/api/graphql/group_query_spec.rb
+++ b/spec/requests/api/graphql/group_query_spec.rb
@@ -122,6 +122,75 @@ RSpec.describe 'getting group information' do
end
end
+ context 'with timelog categories' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:timelog_category) { create(:timelog_category, namespace: group, name: 'TimelogCategoryTest') }
+
+ context 'when user is guest' do
+ it 'includes empty timelog categories array' do
+ post_graphql(group_query(group), current_user: user2)
+
+ expect(graphql_data_at(:group, :timelogCategories, :nodes)).to match([])
+ end
+ end
+
+ context 'when user has reporter role' do
+ before do
+ group.add_reporter(user2)
+ end
+
+ it 'returns the timelog category with all its fields' do
+ post_graphql(group_query(group), current_user: user2)
+
+ expect(graphql_data_at(:group, :timelogCategories, :nodes))
+ .to contain_exactly(a_graphql_entity_for(timelog_category))
+ end
+ end
+
+ context 'for N+1 queries' do
+ let!(:group1) { create(:group) }
+ let!(:group2) { create(:group) }
+
+ before do
+ group1.add_reporter(user2)
+ group2.add_reporter(user2)
+ end
+
+ it 'avoids N+1 database queries' do
+ pending('See: https://gitlab.com/gitlab-org/gitlab/-/issues/369396')
+
+ ctx = { current_user: user2 }
+
+ baseline_query = <<~GQL
+ query {
+ a: group(fullPath: "#{group1.full_path}") { ... g }
+ }
+
+ fragment g on Group {
+ timelogCategories { nodes { name } }
+ }
+ GQL
+
+ query = <<~GQL
+ query {
+ a: group(fullPath: "#{group1.full_path}") { ... g }
+ b: group(fullPath: "#{group2.full_path}") { ... g }
+ }
+
+ fragment g on Group {
+ timelogCategories { nodes { name } }
+ }
+ GQL
+
+ control = ActiveRecord::QueryRecorder.new do
+ run_with_clean_state(baseline_query, context: ctx)
+ end
+
+ expect { run_with_clean_state(query, context: ctx) }.not_to exceed_query_limit(control)
+ end
+ end
+ end
+
context "when authenticated as admin" do
it "returns any existing group" do
post_graphql(group_query(private_group), current_user: admin)
diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb
index 310a8e9fa33..298462268fc 100644
--- a/spec/requests/api/graphql/project_query_spec.rb
+++ b/spec/requests/api/graphql/project_query_spec.rb
@@ -190,4 +190,88 @@ RSpec.describe 'getting project information' do
end
end
end
+
+ context 'with timelog categories' do
+ let_it_be(:timelog_category) do
+ create(:timelog_category, namespace: project.project_namespace, name: 'TimelogCategoryTest')
+ end
+
+ let(:project_fields) do
+ <<~GQL
+ timelogCategories {
+ nodes {
+ #{all_graphql_fields_for('TimeTrackingTimelogCategory')}
+ }
+ }
+ GQL
+ end
+
+ context 'when user is guest and the project is public' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ end
+
+ it 'includes empty timelog categories array' do
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data_at(:project, :timelogCategories, :nodes)).to match([])
+ end
+ end
+
+ context 'when user has reporter role' do
+ before do
+ project.add_reporter(current_user)
+ end
+
+ it 'returns the timelog category with all its fields' do
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data_at(:project, :timelogCategories, :nodes))
+ .to contain_exactly(a_graphql_entity_for(timelog_category))
+ end
+ end
+
+ context 'for N+1 queries' do
+ let!(:project1) { create(:project) }
+ let!(:project2) { create(:project) }
+
+ before do
+ project1.add_reporter(current_user)
+ project2.add_reporter(current_user)
+ end
+
+ it 'avoids N+1 database queries' do
+ pending('See: https://gitlab.com/gitlab-org/gitlab/-/issues/369396')
+
+ ctx = { current_user: current_user }
+
+ baseline_query = <<~GQL
+ query {
+ a: project(fullPath: "#{project1.full_path}") { ... p }
+ }
+
+ fragment p on Project {
+ timelogCategories { nodes { name } }
+ }
+ GQL
+
+ query = <<~GQL
+ query {
+ a: project(fullPath: "#{project1.full_path}") { ... p }
+ b: project(fullPath: "#{project2.full_path}") { ... p }
+ }
+
+ fragment p on Project {
+ timelogCategories { nodes { name } }
+ }
+ GQL
+
+ control = ActiveRecord::QueryRecorder.new do
+ run_with_clean_state(baseline_query, context: ctx)
+ end
+
+ expect { run_with_clean_state(query, context: ctx) }.not_to exceed_query_limit(control)
+ end
+ end
+ end
end
diff --git a/spec/services/webauthn/authenticate_service_spec.rb b/spec/services/webauthn/authenticate_service_spec.rb
index 61f64f24f5e..b40f9465b63 100644
--- a/spec/services/webauthn/authenticate_service_spec.rb
+++ b/spec/services/webauthn/authenticate_service_spec.rb
@@ -30,19 +30,28 @@ RSpec.describe Webauthn::AuthenticateService do
get_result['clientExtensionResults'] = {}
service = Webauthn::AuthenticateService.new(user, get_result.to_json, challenge)
- expect(service.execute).to be_truthy
+ expect(service.execute).to eq true
end
- it 'returns false if the response is valid but no matching stored credential is present' do
- other_client = WebAuthn::FakeClient.new(origin)
- other_client.create(challenge: challenge) # rubocop:disable Rails/SaveBang
+ context 'when response is valid but no matching stored credential is present' do
+ it 'returns false' do
+ other_client = WebAuthn::FakeClient.new(origin)
+ other_client.create(challenge: challenge) # rubocop:disable Rails/SaveBang
- get_result = other_client.get(challenge: challenge)
+ get_result = other_client.get(challenge: challenge)
- get_result['clientExtensionResults'] = {}
- service = Webauthn::AuthenticateService.new(user, get_result.to_json, challenge)
+ get_result['clientExtensionResults'] = {}
+ service = Webauthn::AuthenticateService.new(user, get_result.to_json, challenge)
- expect(service.execute).to be_falsey
+ expect(service.execute).to eq false
+ end
+ end
+
+ context 'when device response includes invalid json' do
+ it 'returns false' do
+ service = Webauthn::AuthenticateService.new(user, 'invalid JSON', '')
+ expect(service.execute).to eq false
+ end
end
end
end
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index d0a1941817a..9ff59d4b6f3 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -307,14 +307,14 @@ module GraphqlHelpers
end
def graphql_mutation(name, input, fields = nil, &block)
- raise ArgumentError, 'Please pass either `fields` parameter or a block to `#graphql_mutation`, but not both.' if fields.present? && block_given?
+ raise ArgumentError, 'Please pass either `fields` parameter or a block to `#graphql_mutation`, but not both.' if fields.present? && block
name = name.graphql_name if name.respond_to?(:graphql_name)
mutation_name = GraphqlHelpers.fieldnamerize(name)
input_variable_name = "$#{input_variable_name_for_mutation(name)}"
mutation_field = GitlabSchema.mutation.fields[mutation_name]
- fields = yield if block_given?
+ fields = yield if block
fields ||= all_graphql_fields_for(mutation_field.type.to_type_signature)
query = <<~MUTATION
diff --git a/spec/support/helpers/query_recorder.rb b/spec/support/helpers/query_recorder.rb
index 01839a74e65..dd124ed9c7f 100644
--- a/spec/support/helpers/query_recorder.rb
+++ b/spec/support/helpers/query_recorder.rb
@@ -14,7 +14,7 @@ module ActiveRecord
@skip_schema_queries = skip_schema_queries
@query_recorder_debug = ENV['QUERY_RECORDER_DEBUG'] || query_recorder_debug
@log_file = log_file
- record(&block) if block_given?
+ record(&block) if block
end
def record(&block)
diff --git a/spec/support/helpers/stub_method_calls.rb b/spec/support/helpers/stub_method_calls.rb
index 45d704958ca..ccbede16563 100644
--- a/spec/support/helpers/stub_method_calls.rb
+++ b/spec/support/helpers/stub_method_calls.rb
@@ -44,7 +44,7 @@ module StubMethodCalls
end
def self.stub_method(object, method, &block)
- raise ArgumentError, "Block is required" unless block_given?
+ raise ArgumentError, "Block is required" unless block
backup_method(object, method) unless backed_up_method?(object, method)
object.define_singleton_method(method, &block)
diff --git a/spec/support/shared_examples/policies/group_project_namespace_policy_shared_examples.rb b/spec/support/shared_examples/policies/group_project_namespace_policy_shared_examples.rb
new file mode 100644
index 00000000000..9a1f0e685be
--- /dev/null
+++ b/spec/support/shared_examples/policies/group_project_namespace_policy_shared_examples.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'checks timelog categories permissions' do
+ context 'with no user' do
+ let_it_be(:current_user) { nil }
+
+ it { is_expected.to be_disallowed(:read_timelog_category) }
+ end
+
+ context 'with a regular user' do
+ let_it_be(:current_user) { create(:user) }
+
+ it { is_expected.to be_disallowed(:read_timelog_category) }
+ end
+
+ context 'with a reporter user' do
+ let_it_be(:current_user) { create(:user) }
+
+ before do
+ users_container.add_reporter(current_user)
+ end
+
+ context 'when timelog_categories is enabled' do
+ it { is_expected.to be_allowed(:read_timelog_category) }
+ end
+
+ context 'when timelog_categories is disabled' do
+ before do
+ stub_feature_flags(timelog_categories: false)
+ end
+
+ it { is_expected.to be_disallowed(:read_timelog_category) }
+ end
+ end
+end
diff --git a/tooling/lib/tooling/helm3_client.rb b/tooling/lib/tooling/helm3_client.rb
index 6e4a35e82f1..82ebe3f51dc 100644
--- a/tooling/lib/tooling/helm3_client.rb
+++ b/tooling/lib/tooling/helm3_client.rb
@@ -84,7 +84,7 @@ module Tooling
# method - The Octokit method to use for getting the data.
# args - Arguments to pass to the `helm list` command.
def each_releases_page(args, &block)
- return to_enum(__method__, args) unless block_given?
+ return to_enum(__method__, args) unless block
page = 0
final_args = args.dup
@@ -100,7 +100,7 @@ module Tooling
#
# args - Any arguments to pass to the `helm list` command.
def each_release(args, &block)
- return to_enum(__method__, args) unless block_given?
+ return to_enum(__method__, args) unless block
each_releases_page(args) do |page|
page.releases.each do |release|
diff --git a/tooling/lib/tooling/test_map_packer.rb b/tooling/lib/tooling/test_map_packer.rb
index d74edb9500f..151ce88111f 100644
--- a/tooling/lib/tooling/test_map_packer.rb
+++ b/tooling/lib/tooling/test_map_packer.rb
@@ -44,7 +44,7 @@ module Tooling
end
def traverse(tree, segments = [], &block)
- return to_enum(__method__, tree, segments) unless block_given?
+ return to_enum(__method__, tree, segments) unless block
if tree == MARKER
return yield segments.join(SEPARATOR)
diff --git a/yarn.lock b/yarn.lock
index 834d04a4bcb..5cf599320d7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4805,10 +4805,10 @@ domhandler@^4.0.0, domhandler@^4.2.0:
dependencies:
domelementtype "^2.2.0"
-dompurify@2.3.6:
- version "2.3.6"
- resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.6.tgz#2e019d7d7617aacac07cbbe3d88ae3ad354cf875"
- integrity sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg==
+dompurify@2.3.8:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.8.tgz#224fe9ae57d7ebd9a1ae1ac18c1c1ca3f532226f"
+ integrity sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==
dompurify@^2.3.10:
version "2.3.10"
@@ -8236,16 +8236,16 @@ merge2@^1.3.0, merge2@^1.4.1:
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
-mermaid@^9.1.1:
- version "9.1.1"
- resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-9.1.1.tgz#5d3d330ca4adf7f3c8ca51095f8bb2f0fb1a93bb"
- integrity sha512-2RVD+WkzZ4VDyO9gQvQAuQ/ux2gLigJtKDTlbwjYqOR/NwsVzTSfGm/kx648/qWJsg6Sv04tE9BWCO8s6a+pFA==
+mermaid@^9.1.3:
+ version "9.1.3"
+ resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-9.1.3.tgz#15d08662c66250124ce31106a4620285061ac59c"
+ integrity sha512-jTIYiqKwsUXVCoxHUVkK8t0QN3zSKIdJlb9thT0J5jCnzXyc+gqTbZE2QmjRfavFTPPn5eRy5zaFp7V+6RhxYg==
dependencies:
"@braintree/sanitize-url" "^6.0.0"
d3 "^7.0.0"
dagre "^0.8.5"
dagre-d3 "^0.6.4"
- dompurify "2.3.6"
+ dompurify "2.3.8"
graphlib "^2.1.8"
khroma "^2.0.0"
moment-mini "^2.24.0"