Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-08-03 12:11:38 +00:00
parent dda49284fc
commit 91035102b4
68 changed files with 608 additions and 264 deletions

View File

@ -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'

View File

@ -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

View File

@ -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?

View File

@ -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

View 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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
module TimeTracking
class TimelogCategoryPolicy < BasePolicy
delegate { @subject.namespace }
end
end

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -30,6 +30,8 @@ module Webauthn
false
end
private
##
# Validates that webauthn_credential is syntactically valid
#

View 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

View File

@ -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

View File

@ -0,0 +1 @@
c868a83176c8e0024ef16e0f95d8a16a0f1b7be0c1a5d58902397cc0462a7e34

View File

@ -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);

View File

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

View File

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

View File

@ -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 -->

View File

@ -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 -->

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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!

View File

@ -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

View File

@ -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|

View File

@ -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

View File

@ -61,7 +61,7 @@ module Gitlab
end
def evaluate(&block)
instance_eval(&block) if block_given?
instance_eval(&block) if block
self
end

View File

@ -35,7 +35,7 @@ module Gitlab
end
def delete(key, &block)
yield(key) if block_given?
yield(key) if block
end
end
end

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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)

View File

@ -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

View File

@ -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 }

View File

@ -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)

View File

@ -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 ""

View File

@ -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",

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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|

View File

@ -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)

View File

@ -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"