Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
dda49284fc
commit
91035102b4
68 changed files with 608 additions and 264 deletions
|
@ -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'
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
51
app/graphql/types/time_tracking/timelog_category_type.rb
Normal file
51
app/graphql/types/time_tracking/timelog_category_type.rb
Normal file
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
7
app/policies/time_tracking/timelog_category_policy.rb
Normal file
7
app/policies/time_tracking/timelog_category_policy.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module TimeTracking
|
||||
class TimelogCategoryPolicy < BasePolicy
|
||||
delegate { @subject.namespace }
|
||||
end
|
||||
end
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -30,6 +30,8 @@ module Webauthn
|
|||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
##
|
||||
# Validates that webauthn_credential is syntactically valid
|
||||
#
|
||||
|
|
8
config/feature_flags/development/timelog_categories.yml
Normal file
8
config/feature_flags/development/timelog_categories.yml
Normal file
|
@ -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
|
|
@ -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
|
1
db/schema_migrations/20220728134255
Normal file
1
db/schema_migrations/20220728134255
Normal file
|
@ -0,0 +1 @@
|
|||
c868a83176c8e0024ef16e0f95d8a16a0f1b7be0c1a5d58902397cc0462a7e34
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -8925,6 +8925,29 @@ The edge type for [`TestSuiteSummary`](#testsuitesummary).
|
|||
| <a id="testsuitesummaryedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="testsuitesummaryedgenode"></a>`node` | [`TestSuiteSummary`](#testsuitesummary) | The item at the end of the edge. |
|
||||
|
||||
#### `TimeTrackingTimelogCategoryConnection`
|
||||
|
||||
The connection type for [`TimeTrackingTimelogCategory`](#timetrackingtimelogcategory).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="timetrackingtimelogcategoryconnectionedges"></a>`edges` | [`[TimeTrackingTimelogCategoryEdge]`](#timetrackingtimelogcategoryedge) | A list of edges. |
|
||||
| <a id="timetrackingtimelogcategoryconnectionnodes"></a>`nodes` | [`[TimeTrackingTimelogCategory]`](#timetrackingtimelogcategory) | A list of nodes. |
|
||||
| <a id="timetrackingtimelogcategoryconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
|
||||
|
||||
#### `TimeTrackingTimelogCategoryEdge`
|
||||
|
||||
The edge type for [`TimeTrackingTimelogCategory`](#timetrackingtimelogcategory).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="timetrackingtimelogcategoryedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="timetrackingtimelogcategoryedgenode"></a>`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):
|
|||
| <a id="groupstoragesizelimit"></a>`storageSizeLimit` | [`Float`](#float) | Total storage limit of the root namespace in bytes. |
|
||||
| <a id="groupsubgroupcreationlevel"></a>`subgroupCreationLevel` | [`String`](#string) | Permission level required to create subgroups within the group. |
|
||||
| <a id="grouptemporarystorageincreaseendson"></a>`temporaryStorageIncreaseEndsOn` | [`Time`](#time) | Date until the temporary storage increase is active. |
|
||||
| <a id="grouptimelogcategories"></a>`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)) |
|
||||
| <a id="grouptotalrepositorysize"></a>`totalRepositorySize` | [`Float`](#float) | Total repository size of all projects in the root namespace in bytes. |
|
||||
| <a id="grouptotalrepositorysizeexcess"></a>`totalRepositorySizeExcess` | [`Float`](#float) | Total excess repository size of all projects in the root namespace in bytes. |
|
||||
| <a id="grouptwofactorgraceperiod"></a>`twoFactorGracePeriod` | [`Int`](#int) | Time before two-factor authentication is enforced. |
|
||||
|
@ -14727,6 +14751,7 @@ Contains statistics about a milestone.
|
|||
| <a id="namespacesharedrunnerssetting"></a>`sharedRunnersSetting` | [`SharedRunnersSetting`](#sharedrunnerssetting) | Shared runners availability for the namespace and its descendants. |
|
||||
| <a id="namespacestoragesizelimit"></a>`storageSizeLimit` | [`Float`](#float) | Total storage limit of the root namespace in bytes. |
|
||||
| <a id="namespacetemporarystorageincreaseendson"></a>`temporaryStorageIncreaseEndsOn` | [`Time`](#time) | Date until the temporary storage increase is active. |
|
||||
| <a id="namespacetimelogcategories"></a>`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)) |
|
||||
| <a id="namespacetotalrepositorysize"></a>`totalRepositorySize` | [`Float`](#float) | Total repository size of all projects in the root namespace in bytes. |
|
||||
| <a id="namespacetotalrepositorysizeexcess"></a>`totalRepositorySizeExcess` | [`Float`](#float) | Total excess repository size of all projects in the root namespace in bytes. |
|
||||
| <a id="namespacevisibility"></a>`visibility` | [`String`](#string) | Visibility of the namespace. |
|
||||
|
@ -15493,6 +15518,7 @@ Represents vulnerability finding of a security report on the pipeline.
|
|||
| <a id="projectsuggestioncommitmessage"></a>`suggestionCommitMessage` | [`String`](#string) | Commit message used to apply merge request suggestions. |
|
||||
| <a id="projecttaglist"></a>`tagList` **{warning-solid}** | [`String`](#string) | **Deprecated** in 13.12. Use `topics`. |
|
||||
| <a id="projectterraformstates"></a>`terraformStates` | [`TerraformStateConnection`](#terraformstateconnection) | Terraform states associated with the project. (see [Connections](#connections)) |
|
||||
| <a id="projecttimelogcategories"></a>`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)) |
|
||||
| <a id="projecttopics"></a>`topics` | [`[String!]`](#string) | List of project topics. |
|
||||
| <a id="projectuserpermissions"></a>`userPermissions` | [`ProjectPermissions!`](#projectpermissions) | Permissions for the current user on the resource. |
|
||||
| <a id="projectvisibility"></a>`visibility` | [`String`](#string) | Visibility of the project. |
|
||||
|
@ -17733,6 +17759,21 @@ Represents the time report stats for timeboxes.
|
|||
| <a id="timereportstatsincomplete"></a>`incomplete` | [`TimeboxMetrics`](#timeboxmetrics) | Incomplete issues metrics. |
|
||||
| <a id="timereportstatstotal"></a>`total` | [`TimeboxMetrics`](#timeboxmetrics) | Total issues metrics. |
|
||||
|
||||
### `TimeTrackingTimelogCategory`
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="timetrackingtimelogcategorybillable"></a>`billable` | [`Boolean`](#boolean) | Whether the category is billable or not. |
|
||||
| <a id="timetrackingtimelogcategorybillingrate"></a>`billingRate` | [`Float`](#float) | Billing rate for the category. |
|
||||
| <a id="timetrackingtimelogcategorycolor"></a>`color` | [`Color`](#color) | Color assigned to the category. |
|
||||
| <a id="timetrackingtimelogcategorycreatedat"></a>`createdAt` | [`Time!`](#time) | When the category was created. |
|
||||
| <a id="timetrackingtimelogcategorydescription"></a>`description` | [`String`](#string) | Description of the category. |
|
||||
| <a id="timetrackingtimelogcategoryid"></a>`id` | [`ID!`](#id) | Internal ID of the timelog category. |
|
||||
| <a id="timetrackingtimelogcategoryname"></a>`name` | [`String!`](#string) | Name of the category. |
|
||||
| <a id="timetrackingtimelogcategoryupdatedat"></a>`updatedAt` | [`Time!`](#time) | When the category was last updated. |
|
||||
|
||||
### `TimeboxMetrics`
|
||||
|
||||
Represents measured stats metrics for timeboxes.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
<!-- This redirect file can be deleted after <2022-11-03>. -->
|
||||
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||
<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
|
||||
|
|
|
@ -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: "<your email>"
|
||||
password: "<your password>"
|
||||
|
||||
email:
|
||||
from_address: "<your from email>"
|
||||
host: smtp
|
||||
port: 25
|
||||
use_tls: false
|
||||
user: "<your email username>"
|
||||
password: "<your email password>"
|
||||
enable_replies: false
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
hostname: "<sentry.example.com>"
|
||||
|
||||
# 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
|
||||
```
|
||||
<!-- This redirect file can be deleted after <2022-11-03>. -->
|
||||
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||
<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
2
lib/gitlab/cache/request_cache.rb
vendored
2
lib/gitlab/cache/request_cache.rb
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -61,7 +61,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def evaluate(&block)
|
||||
instance_eval(&block) if block_given?
|
||||
instance_eval(&block) if block
|
||||
|
||||
self
|
||||
end
|
||||
|
|
|
@ -35,7 +35,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def delete(key, &block)
|
||||
yield(key) if block_given?
|
||||
yield(key) if block
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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|
|
||||
|
|
|
@ -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)
|
||||
|
|
18
yarn.lock
18
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"
|
||||
|
|
Loading…
Reference in a new issue