Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-06-28 09:09:38 +00:00
parent 8b7a679616
commit d81f7fc0b3
43 changed files with 278 additions and 697 deletions

View File

@ -13,8 +13,6 @@ inherit_from:
<% end %> <% end %>
- '.rubocop_todo.yml' - '.rubocop_todo.yml'
<% end %> <% end %>
<%# See https://gitlab.com/gitlab-org/gitlab/-/issues/321982#rubymine-note for context on why namespaced_class has special handling here %>
- ./.rubocop_todo/gitlab/namespaced_class.yml
- ./rubocop/rubocop-migrations.yml - ./rubocop/rubocop-migrations.yml
- ./rubocop/rubocop-usage-data.yml - ./rubocop/rubocop-usage-data.yml
- ./rubocop/rubocop-code_reuse.yml - ./rubocop/rubocop-code_reuse.yml

View File

@ -10,8 +10,8 @@ export const OPENED = 'opened';
export const REOPENED = 'reopened'; export const REOPENED = 'reopened';
export const CLOSED = 'closed'; export const CLOSED = 'closed';
export const MERGED = 'merged'; export const MERGED = 'merged';
export const ISSUE_NOTEABLE_TYPE = 'issue'; export const ISSUE_NOTEABLE_TYPE = 'Issue';
export const EPIC_NOTEABLE_TYPE = 'epic'; export const EPIC_NOTEABLE_TYPE = 'Epic';
export const MERGE_REQUEST_NOTEABLE_TYPE = 'MergeRequest'; export const MERGE_REQUEST_NOTEABLE_TYPE = 'MergeRequest';
export const UNRESOLVE_NOTE_METHOD_NAME = 'delete'; export const UNRESOLVE_NOTE_METHOD_NAME = 'delete';
export const RESOLVE_NOTE_METHOD_NAME = 'post'; export const RESOLVE_NOTE_METHOD_NAME = 'post';

View File

@ -83,14 +83,17 @@ export const setExpandDiscussions = ({ commit }, { discussionIds, expanded }) =>
commit(types.SET_EXPAND_DISCUSSIONS, { discussionIds, expanded }); commit(types.SET_EXPAND_DISCUSSIONS, { discussionIds, expanded });
}; };
export const fetchDiscussions = ({ commit, dispatch }, { path, filter, persistFilter }) => { export const fetchDiscussions = (
{ commit, dispatch, getters },
{ path, filter, persistFilter },
) => {
const config = const config =
filter !== undefined filter !== undefined
? { params: { notes_filter: filter, persist_filter: persistFilter } } ? { params: { notes_filter: filter, persist_filter: persistFilter } }
: null; : null;
if ( if (
window.gon?.features?.paginatedIssueDiscussions || getters.noteableType === constants.ISSUE_NOTEABLE_TYPE ||
window.gon?.features?.paginatedMrDiscussions window.gon?.features?.paginatedMrDiscussions
) { ) {
return dispatch('fetchDiscussionsBatch', { path, config, perPage: 20 }); return dispatch('fetchDiscussionsBatch', { path, config, perPage: 20 });
@ -114,7 +117,7 @@ export const fetchDiscussionsBatch = ({ commit, dispatch }, { path, config, curs
return axios.get(path, { params }).then(({ data, headers }) => { return axios.get(path, { params }).then(({ data, headers }) => {
commit(types.ADD_OR_UPDATE_DISCUSSIONS, data); commit(types.ADD_OR_UPDATE_DISCUSSIONS, data);
if (headers['x-next-page-cursor']) { if (headers && headers['x-next-page-cursor']) {
const nextConfig = { ...config }; const nextConfig = { ...config };
if (config?.params?.persist_filter) { if (config?.params?.persist_filter) {

View File

@ -184,7 +184,6 @@ module IssuableActions
def paginated_discussions def paginated_discussions
return if params[:per_page].blank? return if params[:per_page].blank?
return if issuable.instance_of?(Issue) && Feature.disabled?(:paginated_issue_discussions, project)
return if issuable.instance_of?(MergeRequest) && Feature.disabled?(:paginated_mr_discussions, project) return if issuable.instance_of?(MergeRequest) && Feature.disabled?(:paginated_mr_discussions, project)
strong_memoize(:paginated_discussions) do strong_memoize(:paginated_discussions) do

View File

@ -47,7 +47,6 @@ class Projects::IssuesController < Projects::ApplicationController
before_action only: :show do before_action only: :show do
push_frontend_feature_flag(:confidential_notes, project&.group) push_frontend_feature_flag(:confidential_notes, project&.group)
push_frontend_feature_flag(:issue_assignees_widget, project) push_frontend_feature_flag(:issue_assignees_widget, project)
push_frontend_feature_flag(:paginated_issue_discussions, project)
push_frontend_feature_flag(:realtime_labels, project) push_frontend_feature_flag(:realtime_labels, project)
push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?)
push_frontend_feature_flag(:work_items_mvc_2) push_frontend_feature_flag(:work_items_mvc_2)

View File

@ -6,13 +6,15 @@
module NotificationBranchSelection module NotificationBranchSelection
extend ActiveSupport::Concern extend ActiveSupport::Concern
def branch_choices class_methods do
[ def branch_choices
[_('All branches'), 'all'].freeze, [
[_('Default branch'), 'default'].freeze, [_('All branches'), 'all'].freeze,
[_('Protected branches'), 'protected'].freeze, [_('Default branch'), 'default'].freeze,
[_('Default branch and protected branches'), 'default_and_protected'].freeze [_('Protected branches'), 'protected'].freeze,
].freeze [_('Default branch and protected branches'), 'default_and_protected'].freeze
].freeze
end
end end
def notify_for_branch?(data) def notify_for_branch?(data)

View File

@ -4,9 +4,22 @@ require 'asana'
module Integrations module Integrations
class Asana < Integration class Asana < Integration
prop_accessor :api_key, :restrict_to_branch
validates :api_key, presence: true, if: :activated? validates :api_key, presence: true, if: :activated?
field :api_key,
type: 'password',
title: 'API key',
help: -> { s_('AsanaService|User Personal Access Token. User must have access to the task. All comments are attributed to this user.') },
non_empty_password_title: -> { s_('ProjectService|Enter new API key') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current API key.') },
# Example Personal Access Token from Asana docs
placeholder: '0/68a9e79b868c6789e79a124c30b0',
required: true
field :restrict_to_branch,
title: -> { s_('Integrations|Restrict to branch (optional)') },
help: -> { s_('AsanaService|Comma-separated list of branches to be automatically inspected. Leave blank to include all branches.') }
def title def title
'Asana' 'Asana'
end end
@ -24,28 +37,6 @@ module Integrations
'asana' 'asana'
end end
def fields
[
{
type: 'password',
name: 'api_key',
title: 'API key',
help: s_('AsanaService|User Personal Access Token. User must have access to the task. All comments are attributed to this user.'),
non_empty_password_title: s_('ProjectService|Enter new API key'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current API key.'),
# Example Personal Access Token from Asana docs
placeholder: '0/68a9e79b868c6789e79a124c30b0',
required: true
},
{
type: 'text',
name: 'restrict_to_branch',
title: 'Restrict to branch (optional)',
help: s_('AsanaService|Comma-separated list of branches to be automatically inspected. Leave blank to include all branches.')
}
]
end
def self.supported_events def self.supported_events
%w(push) %w(push)
end end

View File

@ -2,9 +2,18 @@
module Integrations module Integrations
class Assembla < Integration class Assembla < Integration
prop_accessor :token, :subdomain
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
field :token,
type: 'password',
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
placeholder: '',
required: true
field :subdomain,
placeholder: ''
def title def title
'Assembla' 'Assembla'
end end
@ -17,24 +26,6 @@ module Integrations
'assembla' 'assembla'
end end
def fields
[
{
type: 'password',
name: 'token',
non_empty_password_title: s_('ProjectService|Enter new token'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current token.'),
placeholder: '',
required: true
},
{
type: 'text',
name: 'subdomain',
placeholder: ''
}
]
end
def self.supported_events def self.supported_events
%w(push) %w(push)
end end

View File

@ -78,7 +78,7 @@ module Integrations
type: 'select', type: 'select',
name: 'branches_to_be_notified', name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'), title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices choices: self.class.branch_choices
}.freeze, }.freeze,
{ {
type: 'text', type: 'text',
@ -118,7 +118,7 @@ module Integrations
event_type = data[:event_type] || object_kind event_type = data[:event_type] || object_kind
channel_names = get_channel_field(event_type).presence || channel.presence channel_names = event_channel_value(event_type).presence || channel.presence
channels = channel_names&.split(',')&.map(&:strip) channels = channel_names&.split(',')&.map(&:strip)
opts = {} opts = {}
@ -161,7 +161,7 @@ module Integrations
EVENT_CHANNEL[event] EVENT_CHANNEL[event]
end end
def get_channel_field(event) def event_channel_value(event)
field_name = event_channel_name(event) field_name = event_channel_name(event)
self.public_send(field_name) # rubocop:disable GitlabSecurity/PublicSend self.public_send(field_name) # rubocop:disable GitlabSecurity/PublicSend
end end

View File

@ -6,11 +6,14 @@ module Integrations
VALID_HOST_MATCH = %r{\A.+\.atlassian\.net\Z}.freeze VALID_HOST_MATCH = %r{\A.+\.atlassian\.net\Z}.freeze
VALID_PATH_MATCH = %r{\A/wiki(/|\Z)}.freeze VALID_PATH_MATCH = %r{\A/wiki(/|\Z)}.freeze
prop_accessor :confluence_url
validates :confluence_url, presence: true, if: :activated? validates :confluence_url, presence: true, if: :activated?
validate :validate_confluence_url_is_cloud, if: :activated? validate :validate_confluence_url_is_cloud, if: :activated?
field :confluence_url,
title: -> { s_('Confluence Cloud Workspace URL') },
placeholder: 'https://example.atlassian.net/wiki',
required: true
def self.to_param def self.to_param
'confluence' 'confluence'
end end
@ -38,18 +41,6 @@ module Integrations
end end
end end
def fields
[
{
type: 'text',
name: 'confluence_url',
title: s_('Confluence Cloud Workspace URL'),
placeholder: 'https://example.atlassian.net/wiki',
required: true
}
]
end
def testable? def testable?
false false
end end

View File

@ -39,7 +39,7 @@ module Integrations
type: 'select', type: 'select',
name: 'branches_to_be_notified', name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'), title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices choices: self.class.branch_choices
} }
] ]
end end

View File

@ -6,12 +6,35 @@ module Integrations
RECIPIENTS_LIMIT = 750 RECIPIENTS_LIMIT = 750
boolean_accessor :send_from_committer_email
boolean_accessor :disable_diffs
prop_accessor :recipients, :branches_to_be_notified
validates :recipients, presence: true, if: :validate_recipients? validates :recipients, presence: true, if: :validate_recipients?
validate :number_of_recipients_within_limit, if: :validate_recipients? validate :number_of_recipients_within_limit, if: :validate_recipients?
field :send_from_committer_email,
type: 'checkbox',
title: -> { s_("EmailsOnPushService|Send from committer") },
help: -> do
@help ||= begin
domains = Notify.allowed_email_domains.map { |domain| "user@#{domain}" }.join(", ")
s_("EmailsOnPushService|Send notifications from the committer's email address if the domain matches the domain used by your GitLab instance (such as %{domains}).") % { domains: domains }
end
end
field :disable_diffs,
type: 'checkbox',
title: -> { s_("EmailsOnPushService|Disable code diffs") },
help: -> { s_("EmailsOnPushService|Don't include possibly sensitive code diffs in notification body.") }
field :branches_to_be_notified,
type: 'select',
title: -> { s_('Integrations|Branches for which notifications are to be sent') },
choices: branch_choices
field :recipients,
type: 'textarea',
placeholder: -> { s_('EmailsOnPushService|tanuki@example.com gitlab@example.com') },
help: -> { s_('EmailsOnPushService|Emails separated by whitespace.') }
def self.valid_recipients(recipients) def self.valid_recipients(recipients)
recipients.split.grep(Devise.email_regexp).uniq(&:downcase) recipients.split.grep(Devise.email_regexp).uniq(&:downcase)
end end
@ -67,28 +90,6 @@ module Integrations
Gitlab::Utils.to_boolean(self.disable_diffs) Gitlab::Utils.to_boolean(self.disable_diffs)
end end
def fields
domains = Notify.allowed_email_domains.map { |domain| "user@#{domain}" }.join(", ")
[
{ type: 'checkbox', name: 'send_from_committer_email', title: s_("EmailsOnPushService|Send from committer"),
help: s_("EmailsOnPushService|Send notifications from the committer's email address if the domain matches the domain used by your GitLab instance (such as %{domains}).") % { domains: domains } },
{ type: 'checkbox', name: 'disable_diffs', title: s_("EmailsOnPushService|Disable code diffs"),
help: s_("EmailsOnPushService|Don't include possibly sensitive code diffs in notification body.") },
{
type: 'select',
name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices
},
{
type: 'textarea',
name: 'recipients',
placeholder: s_('EmailsOnPushService|tanuki@example.com gitlab@example.com'),
help: s_('EmailsOnPushService|Emails separated by whitespace.')
}
]
end
private private
def number_of_recipients_within_limit def number_of_recipients_within_limit

View File

@ -2,9 +2,14 @@
module Integrations module Integrations
class ExternalWiki < Integration class ExternalWiki < Integration
prop_accessor :external_wiki_url
validates :external_wiki_url, presence: true, public_url: true, if: :activated? validates :external_wiki_url, presence: true, public_url: true, if: :activated?
field :external_wiki_url,
title: -> { s_('ExternalWikiService|External wiki URL') },
placeholder: -> { s_('ExternalWikiService|https://example.com/xxx/wiki/...') },
help: -> { s_('ExternalWikiService|Enter the URL to the external wiki.') },
required: true
def title def title
s_('ExternalWikiService|External wiki') s_('ExternalWikiService|External wiki')
end end
@ -17,19 +22,6 @@ module Integrations
'external_wiki' 'external_wiki'
end end
def fields
[
{
type: 'text',
name: 'external_wiki_url',
title: s_('ExternalWikiService|External wiki URL'),
placeholder: s_('ExternalWikiService|https://example.com/xxx/wiki/...'),
help: 'Enter the URL to the external wiki.',
required: true
}
]
end
def help def help
docs_link = ActionController::Base.helpers.link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/wiki/index', anchor: 'link-an-external-wiki'), target: '_blank', rel: 'noopener noreferrer' docs_link = ActionController::Base.helpers.link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/wiki/index', anchor: 'link-an-external-wiki'), target: '_blank', rel: 'noopener noreferrer'

View File

@ -2,9 +2,16 @@
module Integrations module Integrations
class Flowdock < Integration class Flowdock < Integration
prop_accessor :token
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
field :token,
type: 'password',
help: -> { s_('FlowdockService|Enter your Flowdock token.') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
placeholder: '1b609b52537...',
required: true
def title def title
'Flowdock' 'Flowdock'
end end
@ -22,20 +29,6 @@ module Integrations
'flowdock' 'flowdock'
end end
def fields
[
{
type: 'password',
name: 'token',
help: s_('FlowdockService|Enter your Flowdock token.'),
non_empty_password_title: s_('ProjectService|Enter new token'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current token.'),
placeholder: '1b609b52537...',
required: true
}
]
end
def self.supported_events def self.supported_events
%w(push) %w(push)
end end

View File

@ -39,7 +39,7 @@ module Integrations
type: 'select', type: 'select',
name: 'branches_to_be_notified', name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'), title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices choices: self.class.branch_choices
} }
] ]
end end

View File

@ -44,7 +44,7 @@ module Integrations
section: SECTION_TYPE_CONFIGURATION, section: SECTION_TYPE_CONFIGURATION,
name: 'branches_to_be_notified', name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'), title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices choices: self.class.branch_choices
} }
] ]
end end

View File

@ -76,7 +76,7 @@ module Integrations
{ type: 'select', { type: 'select',
name: 'branches_to_be_notified', name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'), title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices } choices: self.class.branch_choices }
] ]
end end

View File

@ -4,9 +4,22 @@ module Integrations
class Pivotaltracker < Integration class Pivotaltracker < Integration
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits' API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'
prop_accessor :token, :restrict_to_branch
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
field :token,
type: 'password',
help: -> { s_('PivotalTrackerService|Pivotal Tracker API token. User must have access to the story. All comments are attributed to this user.') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
required: true
field :restrict_to_branch,
title: -> { s_('Integrations|Restrict to branch (optional)') },
help: -> do
s_('PivotalTrackerService|Comma-separated list of branches to ' \
'automatically inspect. Leave blank to include all branches.')
end
def title def title
'Pivotal Tracker' 'Pivotal Tracker'
end end
@ -24,26 +37,6 @@ module Integrations
'pivotaltracker' 'pivotaltracker'
end end
def fields
[
{
type: 'password',
name: 'token',
help: s_('PivotalTrackerService|Pivotal Tracker API token. User must have access to the story. All comments are attributed to this user.'),
non_empty_password_title: s_('ProjectService|Enter new token'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current token.'),
required: true
},
{
type: 'text',
name: 'restrict_to_branch',
title: 'Restrict to branch (optional)',
help: s_('PivotalTrackerService|Comma-separated list of branches to ' \
'automatically inspect. Leave blank to include all branches.')
}
]
end
def self.supported_events def self.supported_events
%w(push) %w(push)
end end

View File

@ -4,11 +4,30 @@ module Integrations
class Prometheus < BaseMonitoring class Prometheus < BaseMonitoring
include PrometheusAdapter include PrometheusAdapter
# Access to prometheus is directly through the API field :manual_configuration,
prop_accessor :api_url type: 'checkbox',
prop_accessor :google_iap_service_account_json title: -> { s_('PrometheusService|Active') },
prop_accessor :google_iap_audience_client_id help: -> { s_('PrometheusService|Select this checkbox to override the auto configuration settings with your own settings.') },
boolean_accessor :manual_configuration required: true
field :api_url,
title: 'API URL',
placeholder: -> { s_('PrometheusService|https://prometheus.example.com/') },
help: -> { s_('PrometheusService|The Prometheus API base URL.') },
required: true
field :google_iap_audience_client_id,
title: 'Google IAP Audience Client ID',
placeholder: -> { s_('PrometheusService|IAP_CLIENT_ID.apps.googleusercontent.com') },
help: -> { s_('PrometheusService|The ID of the IAP-secured resource.') },
required: false
field :google_iap_service_account_json,
type: 'textarea',
title: 'Google IAP Service Account JSON',
placeholder: -> { s_('PrometheusService|{ "type": "service_account", "project_id": ... }') },
help: -> { s_('PrometheusService|The contents of the credentials.json file of your service account.') },
required: false
# We need to allow the self-monitoring project to connect to the internal # We need to allow the self-monitoring project to connect to the internal
# Prometheus instance. # Prometheus instance.
@ -45,43 +64,6 @@ module Integrations
'prometheus' 'prometheus'
end end
def fields
[
{
type: 'checkbox',
name: 'manual_configuration',
title: s_('PrometheusService|Active'),
help: s_('PrometheusService|Select this checkbox to override the auto configuration settings with your own settings.'),
required: true
},
{
type: 'text',
name: 'api_url',
title: 'API URL',
placeholder: s_('PrometheusService|https://prometheus.example.com/'),
help: s_('PrometheusService|The Prometheus API base URL.'),
required: true
},
{
type: 'text',
name: 'google_iap_audience_client_id',
title: 'Google IAP Audience Client ID',
placeholder: s_('PrometheusService|IAP_CLIENT_ID.apps.googleusercontent.com'),
help: s_('PrometheusService|The ID of the IAP-secured resource.'),
autocomplete: 'off',
required: false
},
{
type: 'textarea',
name: 'google_iap_service_account_json',
title: 'Google IAP Service Account JSON',
placeholder: s_('PrometheusService|{ "type": "service_account", "project_id": ... }'),
help: s_('PrometheusService|The contents of the credentials.json file of your service account.'),
required: false
}
]
end
# Check we can connect to the Prometheus API # Check we can connect to the Prometheus API
def test(*args) def test(*args)
return { success: false, result: 'Prometheus configuration error' } unless prometheus_client return { success: false, result: 'Prometheus configuration error' } unless prometheus_client

View File

@ -35,7 +35,7 @@ module Integrations
type: 'select', type: 'select',
name: 'branches_to_be_notified', name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'), title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices choices: self.class.branch_choices
} }
] ]
end end

View File

@ -35,7 +35,7 @@ module Integrations
type: 'select', type: 'select',
name: 'branches_to_be_notified', name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'), title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices choices: self.class.branch_choices
} }
] ]
end end

View File

@ -23,7 +23,7 @@ module Integrations
integration.event_channel_name(event) integration.event_channel_name(event)
end end
expose :value do |event| expose :value do |event|
integration.get_channel_field(event) integration.event_channel_value(event)
end end
end end

View File

@ -1,4 +1,4 @@
- add_page_startup_api_call Feature.enabled?(:paginated_issue_discussions, @project) ? discussions_path(@issue, per_page: 20) : discussions_path(@issue) - add_page_startup_api_call discussions_path(@issue, per_page: 20)
- @gfm_form = true - @gfm_form = true

View File

@ -1,8 +0,0 @@
---
name: paginated_issue_discussions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69933
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345351
milestone: '14.5'
type: development
group: group::project management
default_enabled: false

View File

@ -3,7 +3,7 @@ table_name: ci_job_token_project_scope_links
classes: classes:
- Ci::JobToken::ProjectScopeLink - Ci::JobToken::ProjectScopeLink
feature_categories: feature_categories:
- pipeline_authoring - continuous_integration
description: TODO description: The connection between a source project, which defines the job token scope, and a target project, which is the one allowed to be accessed by the job token.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62733 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62733
milestone: '14.0' milestone: '14.0'

View File

@ -475,16 +475,16 @@ end
Developers can add [feature flags](../development/feature_flags/index.md) to GraphQL Developers can add [feature flags](../development/feature_flags/index.md) to GraphQL
fields in the following ways: fields in the following ways:
- Add the `feature_flag` property to a field. This allows the field to be _hidden_ - Add the [`feature_flag` property](#feature_flag-property) to a field. This allows the field to be _hidden_
from the GraphQL schema when the flag is disabled. from the GraphQL schema when the flag is disabled.
- Toggle the return value when resolving the field. - [Toggle the return value](#toggle-the-value-of-a-field) when resolving the field.
You can refer to these guidelines to decide which approach to use: You can refer to these guidelines to decide which approach to use:
- If your field is experimental, and its name or type is subject to - If your field is experimental, and its name or type is subject to
change, use the `feature_flag` property. change, use the [`feature_flag` property](#feature_flag-property).
- If your field is stable and its definition doesn't change, even after the flag is - If your field is stable and its definition doesn't change, even after the flag is
removed, toggle the return value of the field instead. Note that removed, [toggle the return value](#toggle-the-value-of-a-field) of the field instead. Note that
[all fields should be nullable](#nullable-fields) anyway. [all fields should be nullable](#nullable-fields) anyway.
### `feature_flag` property ### `feature_flag` property
@ -524,6 +524,12 @@ return value of the field. This can be done in the resolver, in the
type, or even in a model method, depending on your preference and type, or even in a model method, depending on your preference and
situation. situation.
Consider also [marking the field as Alpha](#marking-schema-items-as-alpha)
while the value of the field can be toggled. You can
[change or remove Alpha fields at any time](#breaking-change-exemptions) without needing to deprecate them.
This also signals to consumers of the public GraphQL API that the field is not
meant to be used yet.
When applying a feature flag to toggle the value of a field, the When applying a feature flag to toggle the value of a field, the
`description` of the field must: `description` of the field must:
@ -537,6 +543,7 @@ Example:
```ruby ```ruby
field :foo, GraphQL::Types::String, field :foo, GraphQL::Types::String,
null: true, null: true,
deprecated: { reason: :alpha, milestone: '10.0' },
description: 'Some test field. Returns `null`' \ description: 'Some test field. Returns `null`' \
'if `my_feature_flag` feature flag is disabled.' 'if `my_feature_flag` feature flag is disabled.'
@ -2007,13 +2014,13 @@ end
.to contain_exactly(a_graphql_entity_for(issue, :iid, :title, created_at: some_time)) .to contain_exactly(a_graphql_entity_for(issue, :iid, :title, created_at: some_time))
``` ```
- Use `GraphqlHelpers#empty_schema` to create an empty schema, rather than creating - Use `GraphqlHelpers#empty_schema` to create an empty schema, rather than creating
one by hand. For example: one by hand. For example:
```ruby ```ruby
# good # good
let(:schema) { empty_schema } let(:schema) { empty_schema }
# bad # bad
let(:query_type) { GraphQL::ObjectType.new } let(:query_type) { GraphQL::ObjectType.new }
let(:schema) { GraphQL::Schema.define(query: query_type, mutation: nil)} let(:schema) { GraphQL::Schema.define(query: query_type, mutation: nil)}
@ -2024,7 +2031,7 @@ end
```ruby ```ruby
# good # good
let(:query) { query_double(schema: GitlabSchema) } let(:query) { query_double(schema: GitlabSchema) }
# bad # bad
let(:query) { double('Query', schema: GitlabSchema) } let(:query) { double('Query', schema: GitlabSchema) }
``` ```
@ -2092,9 +2099,9 @@ end
```ruby ```ruby
type Types::IssueType.connection_type, null: true type Types::IssueType.connection_type, null: true
``` ```
However this might cause a cyclic definition, which can result in errors like: However this might cause a cyclic definition, which can result in errors like:
```ruby ```ruby
NameError: uninitialized constant Resolvers::GroupIssuesResolver NameError: uninitialized constant Resolvers::GroupIssuesResolver
``` ```
@ -2109,7 +2116,7 @@ end
class IssueConnectionType < CountableConnectionType class IssueConnectionType < CountableConnectionType
end end
end end
Types::IssueConnectionType.prepend_mod_with('Types::IssueConnectionType') Types::IssueConnectionType.prepend_mod_with('Types::IssueConnectionType')
``` ```
@ -2120,22 +2127,22 @@ end
```ruby ```ruby
type "Types::IssueConnection", null: true type "Types::IssueConnection", null: true
``` ```
Only use this style if you are having spec failures. This is not intended to be a new Only use this style if you are having spec failures. This is not intended to be a new
pattern that we use. This issue may disappear after we've upgraded to `2.x`. pattern that we use. This issue may disappear after we've upgraded to `2.x`.
- There can be instances where a spec fails because the class is not loaded correctly. - There can be instances where a spec fails because the class is not loaded correctly.
It relates to the It relates to the
[circular dependencies problem](https://github.com/rmosolgo/graphql-ruby/issues/1929) and [circular dependencies problem](https://github.com/rmosolgo/graphql-ruby/issues/1929) and
[Adding field with resolver on a Type causes "Can't determine the return type " error on a different Type](https://github.com/rmosolgo/graphql-ruby/issues/3974). [Adding field with resolver on a Type causes "Can't determine the return type " error on a different Type](https://github.com/rmosolgo/graphql-ruby/issues/3974).
Unfortunately, the errors generated don't really indicate what the problem is. For example, Unfortunately, the errors generated don't really indicate what the problem is. For example,
remove the quotes from the `Rspec.descrbe` in remove the quotes from the `Rspec.descrbe` in
[ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb). [ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb).
Then run `rspec ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb`. Then run `rspec ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb`.
This generates errors with the expectations. For example: This generates errors with the expectations. For example:
```ruby ```ruby
1) Resolvers::ComplianceManagement::MergeRequests::ComplianceViolationResolver#resolve user is authorized filtering the results when given an array of project IDs finds the filtered compliance violations 1) Resolvers::ComplianceManagement::MergeRequests::ComplianceViolationResolver#resolve user is authorized filtering the results when given an array of project IDs finds the filtered compliance violations
Failure/Error: expect(subject).to contain_exactly(compliance_violation) Failure/Error: expect(subject).to contain_exactly(compliance_violation)
@ -2145,10 +2152,10 @@ end
the extra elements were: [#<MergeRequests::ComplianceViolation id: 5, violating_user_id: 27, merge_request_id: 5, reason: "approved_by_merge_request_author", severity_level: "high">] the extra elements were: [#<MergeRequests::ComplianceViolation id: 5, violating_user_id: 27, merge_request_id: 5, reason: "approved_by_merge_request_author", severity_level: "high">]
# ./ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb:55:in `block (6 levels) in <top (required)>' # ./ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb:55:in `block (6 levels) in <top (required)>'
``` ```
However, this is not a case of the wrong result being generated, it's because of the loading order However, this is not a case of the wrong result being generated, it's because of the loading order
of the `ComplianceViolationResolver` class. of the `ComplianceViolationResolver` class.
The only way we've found to fix this is by quoting the class name in the spec. For example, changing The only way we've found to fix this is by quoting the class name in the spec. For example, changing
```ruby ```ruby
@ -2198,7 +2205,7 @@ end
[removed eventually](https://gitlab.com/gitlab-org/gitlab/-/issues/363121), [removed eventually](https://gitlab.com/gitlab-org/gitlab/-/issues/363121),
and writing unit tests for resolvers/mutations is and writing unit tests for resolvers/mutations is
[already deprecated](#writing-unit-tests-deprecated) [already deprecated](#writing-unit-tests-deprecated)
## Notes about Query flow and GraphQL infrastructure ## Notes about Query flow and GraphQL infrastructure
The GitLab GraphQL infrastructure can be found in `lib/gitlab/graphql`. The GitLab GraphQL infrastructure can be found in `lib/gitlab/graphql`.

View File

@ -104,7 +104,10 @@ for the section. For example:
> `widget_message` [introduced](<link-to-issue>) in GitLab 14.3. > `widget_message` [introduced](<link-to-issue>) in GitLab 14.3.
``` ```
## Attribute deprecation ## Deprecations
To document the deprecation of an API endpoint, follow the steps to
[deprecate a page or topic](versions.md#deprecate-a-page-or-topic).
To deprecate an attribute: To deprecate an attribute:
@ -122,8 +125,8 @@ To deprecate an attribute:
| `widget_name` | string | **{dotted-circle}** No | [Deprecated](<link-to-issue>) in GitLab 14.7 and is planned for removal in 15.4. Use `widget_id` instead. The name of the widget. | | `widget_name` | string | **{dotted-circle}** No | [Deprecated](<link-to-issue>) in GitLab 14.7 and is planned for removal in 15.4. Use `widget_id` instead. The name of the widget. |
``` ```
1. Optional. To widely announce the change, or if it's a breaking change, To widely announce a deprecation, or if it's a breaking change,
[update the deprecations and removals documentation](../deprecation_guidelines/#update-the-deprecations-and-removals-documentation). [update the deprecations and removals documentation](../deprecation_guidelines/#update-the-deprecations-and-removals-documentation).
## Method description ## Method description

View File

@ -15520,6 +15520,9 @@ msgstr ""
msgid "ExternalIssueIntegration|This issue is synchronized with %{trackerName}" msgid "ExternalIssueIntegration|This issue is synchronized with %{trackerName}"
msgstr "" msgstr ""
msgid "ExternalWikiService|Enter the URL to the external wiki."
msgstr ""
msgid "ExternalWikiService|External wiki" msgid "ExternalWikiService|External wiki"
msgstr "" msgstr ""
@ -20767,6 +20770,9 @@ msgstr ""
msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration." msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr "" msgstr ""
msgid "Integrations|Restrict to branch (optional)"
msgstr ""
msgid "Integrations|Return to GitLab for Jira" msgid "Integrations|Return to GitLab for Jira"
msgstr "" msgstr ""

View File

@ -151,7 +151,7 @@
"pikaday": "^1.8.0", "pikaday": "^1.8.0",
"popper.js": "^1.16.1", "popper.js": "^1.16.1",
"portal-vue": "^2.1.7", "portal-vue": "^2.1.7",
"postcss": "8.4.5", "postcss": "8.4.14",
"prismjs": "^1.21.0", "prismjs": "^1.21.0",
"prosemirror-markdown": "1.8.0", "prosemirror-markdown": "1.8.0",
"prosemirror-model": "^1.16.1", "prosemirror-model": "^1.16.1",
@ -201,7 +201,7 @@
}, },
"devDependencies": { "devDependencies": {
"@gitlab/eslint-plugin": "12.1.0", "@gitlab/eslint-plugin": "12.1.0",
"@gitlab/stylelint-config": "4.0.0", "@gitlab/stylelint-config": "4.1.0",
"@graphql-eslint/eslint-plugin": "3.10.4", "@graphql-eslint/eslint-plugin": "3.10.4",
"@testing-library/dom": "^7.16.2", "@testing-library/dom": "^7.16.2",
"@types/jest": "^26.0.24", "@types/jest": "^26.0.24",
@ -248,7 +248,7 @@
"purgecss": "^4.0.3", "purgecss": "^4.0.3",
"purgecss-from-html": "^4.0.3", "purgecss-from-html": "^4.0.3",
"sass": "^1.49.9", "sass": "^1.49.9",
"stylelint": "^14.3.0", "stylelint": "^14.9.1",
"timezone-mock": "^1.0.8", "timezone-mock": "^1.0.8",
"vue-jest": "4.0.1", "vue-jest": "4.0.1",
"webpack-dev-server": "4.9.2", "webpack-dev-server": "4.9.2",

View File

@ -34,7 +34,7 @@ module RuboCop
# #
# class Gitlab::MyDomain::MyClass # class Gitlab::MyDomain::MyClass
# end # end
class NamespacedClass < RuboCop::Cop::Cop class NamespacedClass < RuboCop::Cop::Base
MSG = 'Classes must be declared inside a module indicating a product domain namespace. For more info: https://gitlab.com/gitlab-org/gitlab/-/issues/321982' MSG = 'Classes must be declared inside a module indicating a product domain namespace. For more info: https://gitlab.com/gitlab-org/gitlab/-/issues/321982'
# These namespaces are considered top-level semantically. # These namespaces are considered top-level semantically.
@ -51,7 +51,7 @@ module RuboCop
# Remove class name because it's not a domain namespace. # Remove class name because it's not a domain namespace.
add_potential_domain_namespace(node) { _1.pop } add_potential_domain_namespace(node) { _1.pop }
add_offense(node) if domain_namespaces.none? add_offense(node.loc.name) if domain_namespaces.none?
end end
private private

View File

@ -14,7 +14,7 @@ jq -crM '.vulnerabilities |
) | ) |
sort | sort |
if length > 0 then if length > 0 then
{ body: ("The findings below have been detected based on the [AppSec custom Semgrep rules](https://gitlab.com/gitlab-com/gl-security/appsec/sast-custom-rules/) and need attention:\n\n" + join("\n") + "\n\n/cc @gitlab-com/gl-security/appsec") } { body: ("The findings below have been detected based on the AppSec custom SAST rules. For more information about this bot and what to do with this comment head over to the [README](https://gitlab.com/gitlab-com/gl-security/appsec/sast-custom-rules/-/tree/main/appsec-pings). The following lines of code possibly need attention:\n\n" + join("\n") + "\n\n/cc @gitlab-com/gl-security/appsec") }
else else
empty empty
end' gl-sast-report.json >findings.txt end' gl-sast-report.json >findings.txt

View File

@ -217,8 +217,7 @@ describe('note_app', () => {
}); });
afterEach(() => { afterEach(() => {
waitForDiscussionsRequest(); return waitForDiscussionsRequest().then(() => resetHTMLFixture());
resetHTMLFixture();
}); });
it('renders skeleton notes', () => { it('renders skeleton notes', () => {
@ -471,7 +470,7 @@ describe('note_app', () => {
wrapper = shallowMount(NotesApp, { propsData, store: createStore() }); wrapper = shallowMount(NotesApp, { propsData, store: createStore() });
await waitForPromises(); await waitForPromises();
expect(axiosMock.history.get[0].params).toBeUndefined(); expect(axiosMock.history.get[0].params).toEqual({ per_page: 20 });
}); });
}); });
@ -496,14 +495,14 @@ describe('note_app', () => {
wrapper = mountWithNotesFilter(undefined); wrapper = mountWithNotesFilter(undefined);
await waitForPromises(); await waitForPromises();
expect(axiosMock.history.get[0].params).toBeUndefined(); expect(axiosMock.history.get[0].params).toEqual({ per_page: 20 });
}); });
it('does not include extra query params when filter is already set to default', async () => { it('does not include extra query params when filter is already set to default', async () => {
wrapper = mountWithNotesFilter(constants.DISCUSSION_FILTERS_DEFAULT_VALUE); wrapper = mountWithNotesFilter(constants.DISCUSSION_FILTERS_DEFAULT_VALUE);
await waitForPromises(); await waitForPromises();
expect(axiosMock.history.get[0].params).toBeUndefined(); expect(axiosMock.history.get[0].params).toEqual({ per_page: 20 });
}); });
it('includes extra query params when filter is not set to default', async () => { it('includes extra query params when filter is not set to default', async () => {
@ -512,6 +511,7 @@ describe('note_app', () => {
expect(axiosMock.history.get[0].params).toEqual({ expect(axiosMock.history.get[0].params).toEqual({
notes_filter: constants.DISCUSSION_FILTERS_DEFAULT_VALUE, notes_filter: constants.DISCUSSION_FILTERS_DEFAULT_VALUE,
per_page: 20,
persist_filter: false, persist_filter: false,
}); });
}); });

View File

@ -1351,7 +1351,7 @@ describe('Actions Notes Store', () => {
return testAction( return testAction(
actions.fetchDiscussions, actions.fetchDiscussions,
{}, {},
null, { noteableType: notesConstants.MERGE_REQUEST_NOTEABLE_TYPE },
[ [
{ type: mutationTypes.ADD_OR_UPDATE_DISCUSSIONS, payload: { discussion } }, { type: mutationTypes.ADD_OR_UPDATE_DISCUSSIONS, payload: { discussion } },
{ type: mutationTypes.SET_FETCHING_DISCUSSIONS, payload: false }, { type: mutationTypes.SET_FETCHING_DISCUSSIONS, payload: false },
@ -1360,13 +1360,11 @@ describe('Actions Notes Store', () => {
); );
}); });
it('dispatches `fetchDiscussionsBatch` action if `paginatedIssueDiscussions` feature flag is enabled', () => { it('dispatches `fetchDiscussionsBatch` action if noteable is an Issue', () => {
window.gon = { features: { paginatedIssueDiscussions: true } };
return testAction( return testAction(
actions.fetchDiscussions, actions.fetchDiscussions,
{ path: 'test-path', filter: 'test-filter', persistFilter: 'test-persist-filter' }, { path: 'test-path', filter: 'test-filter', persistFilter: 'test-persist-filter' },
null, { noteableType: notesConstants.ISSUE_NOTEABLE_TYPE },
[], [],
[ [
{ {
@ -1389,7 +1387,7 @@ describe('Actions Notes Store', () => {
return testAction( return testAction(
actions.fetchDiscussions, actions.fetchDiscussions,
{ path: 'test-path', filter: 'test-filter', persistFilter: 'test-persist-filter' }, { path: 'test-path', filter: 'test-filter', persistFilter: 'test-persist-filter' },
null, { noteableType: notesConstants.MERGE_REQUEST_NOTEABLE_TYPE },
[], [],
[ [
{ {

View File

@ -1,97 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Elasticsearch::Logs::Lines do
let(:client) { Elasticsearch::Transport::Client }
let(:es_message_1) { { timestamp: "2019-12-13T14:35:34.034Z", pod: "production-6866bc8974-m4sk4", message: "10.8.2.1 - - [25/Oct/2019:08:03:22 UTC] \"GET / HTTP/1.1\" 200 13" } }
let(:es_message_2) { { timestamp: "2019-12-13T14:35:35.034Z", pod: "production-6866bc8974-m4sk4", message: "10.8.2.1 - - [27/Oct/2019:23:49:54 UTC] \"GET / HTTP/1.1\" 200 13" } }
let(:es_message_3) { { timestamp: "2019-12-13T14:35:36.034Z", pod: "production-6866bc8974-m4sk4", message: "10.8.2.1 - - [04/Nov/2019:23:09:24 UTC] \"GET / HTTP/1.1\" 200 13" } }
let(:es_message_4) { { timestamp: "2019-12-13T14:35:37.034Z", pod: "production-6866bc8974-m4sk4", message: "- -\u003e /" } }
let(:es_response) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/logs_response.json')) }
subject { described_class.new(client) }
let(:namespace) { "autodevops-deploy-9-production" }
let(:pod_name) { "production-6866bc8974-m4sk4" }
let(:container_name) { "auto-deploy-app" }
let(:search) { "foo +bar "}
let(:start_time) { "2019-12-13T14:35:34.034Z" }
let(:end_time) { "2019-12-13T14:35:34.034Z" }
let(:cursor) { "9999934,1572449784442" }
let(:body) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query.json')) }
let(:body_with_container) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_container.json')) }
let(:body_with_search) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_search.json')) }
let(:body_with_times) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_times.json')) }
let(:body_with_start_time) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_start_time.json')) }
let(:body_with_end_time) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_end_time.json')) }
let(:body_with_cursor) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_cursor.json')) }
let(:body_with_filebeat_6) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_filebeat_6.json')) }
RSpec::Matchers.define :a_hash_equal_to_json do |expected|
match do |actual|
actual.as_json == expected
end
end
describe '#pod_logs' do
it 'returns the logs as an array' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can further filter the logs by container name' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_container)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, container_name: container_name)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can further filter the logs by search' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_search)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, search: search)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can further filter the logs by start_time and end_time' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_times)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, start_time: start_time, end_time: end_time)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can further filter the logs by only start_time' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_start_time)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, start_time: start_time)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can further filter the logs by only end_time' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_end_time)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, end_time: end_time)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can search after a cursor' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_cursor)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, cursor: cursor)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can search on filebeat 6' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_filebeat_6)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, chart_above_v2: false)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
end
end

View File

@ -1,35 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Elasticsearch::Logs::Pods do
let(:client) { Elasticsearch::Transport::Client }
let(:es_query) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/pods_query.json'), symbolize_names: true) }
let(:es_response) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/pods_response.json')) }
let(:namespace) { "autodevops-deploy-9-production" }
subject { described_class.new(client) }
describe '#pods' do
it 'returns the pods' do
expect(client).to receive(:search).with(body: es_query).and_return(es_response)
result = subject.pods(namespace)
expect(result).to eq([
{
name: "runner-gitlab-runner-7bbfb5dcb5-p6smb",
container_names: %w[runner-gitlab-runner]
},
{
name: "elastic-stack-elasticsearch-master-1",
container_names: %w[elasticsearch chown sysctl]
},
{
name: "ingress-nginx-ingress-controller-76449bcc8d-8qgl6",
container_names: %w[nginx-ingress-controller]
}
])
end
end
end

View File

@ -292,15 +292,15 @@ RSpec.describe Integrations::BaseChatNotification do
end end
end end
describe '#get_channel_field' do describe '#event_channel_value' do
it 'returns the channel field value for the given event' do it 'returns the channel field value for the given event' do
subject.push_channel = '#pushes' subject.push_channel = '#pushes'
expect(subject.get_channel_field(:push)).to eq('#pushes') expect(subject.event_channel_value(:push)).to eq('#pushes')
end end
it 'raises an error for unsupported events' do it 'raises an error for unsupported events' do
expect { subject.get_channel_field(:foo) }.to raise_error(NoMethodError) expect { subject.event_channel_value(:foo) }.to raise_error(NoMethodError)
end end
end end
end end

View File

@ -475,47 +475,4 @@ RSpec.describe Integrations::Prometheus, :use_clean_rails_memory_store_caching,
end end
end end
end end
describe '#fields' do
let(:expected_fields) do
[
{
type: 'checkbox',
name: 'manual_configuration',
title: s_('PrometheusService|Active'),
help: s_('PrometheusService|Select this checkbox to override the auto configuration settings with your own settings.'),
required: true
},
{
type: 'text',
name: 'api_url',
title: 'API URL',
placeholder: s_('PrometheusService|https://prometheus.example.com/'),
help: s_('PrometheusService|The Prometheus API base URL.'),
required: true
},
{
type: 'text',
name: 'google_iap_audience_client_id',
title: 'Google IAP Audience Client ID',
placeholder: s_('PrometheusService|IAP_CLIENT_ID.apps.googleusercontent.com'),
help: s_('PrometheusService|The ID of the IAP-secured resource.'),
autocomplete: 'off',
required: false
},
{
type: 'textarea',
name: 'google_iap_service_account_json',
title: 'Google IAP Service Account JSON',
placeholder: s_('PrometheusService|{ "type": "service_account", "project_id": ... }'),
help: s_('PrometheusService|The contents of the credentials.json file of your service account.'),
required: false
}
]
end
it 'returns fields' do
expect(integration.fields).to eq(expected_fields)
end
end
end end

View File

@ -26,90 +26,7 @@ RSpec.describe API::Environments do
expect(json_response.first['tier']).to eq(environment.tier) expect(json_response.first['tier']).to eq(environment.tier)
expect(json_response.first['external_url']).to eq(environment.external_url) expect(json_response.first['external_url']).to eq(environment.external_url)
expect(json_response.first['project']).to match_schema('public_api/v4/project') expect(json_response.first['project']).to match_schema('public_api/v4/project')
expect(json_response.first['enable_advanced_logs_querying']).to eq(false)
expect(json_response.first).not_to have_key('last_deployment') expect(json_response.first).not_to have_key('last_deployment')
expect(json_response.first).not_to have_key('gitlab_managed_apps_logs_path')
end
context 'when the user can read pod logs' do
context 'with successful deployment on cluster' do
let_it_be(:deployment) { create(:deployment, :on_cluster, :success, environment: environment, project: project) }
it 'returns environment with enable_advanced_logs_querying and logs_api_path' do
get api("/projects/#{project.id}/environments", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.first['gitlab_managed_apps_logs_path']).to eq(
"/#{project.full_path}/-/logs/k8s.json?cluster_id=#{deployment.cluster_id}"
)
end
end
context 'when elastic stack is available' do
before do
allow_next_found_instance_of(Environment) do |env|
allow(env).to receive(:elastic_stack_available?).and_return(true)
end
end
it 'returns environment with enable_advanced_logs_querying and logs_api_path' do
get api("/projects/#{project.id}/environments", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.first['enable_advanced_logs_querying']).to eq(true)
expect(json_response.first['logs_api_path']).to eq(
"/#{project.full_path}/-/logs/elasticsearch.json?environment_name=#{environment.name}"
)
end
end
context 'when elastic stack is not available' do
before do
allow_next_found_instance_of(Environment) do |env|
allow(env).to receive(:elastic_stack_available?).and_return(false)
end
end
it 'returns environment with enable_advanced_logs_querying logs_api_path' do
get api("/projects/#{project.id}/environments", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.first['enable_advanced_logs_querying']).to eq(false)
expect(json_response.first['logs_api_path']).to eq(
"/#{project.full_path}/-/logs/k8s.json?environment_name=#{environment.name}"
)
end
end
end
context 'when the user cannot read pod logs' do
before do
allow_next_found_instance_of(User) do |user|
allow(user).to receive(:can?).and_call_original
allow(user).to receive(:can?).with(:read_pod_logs, project).and_return(false)
end
end
it 'does not contain enable_advanced_logs_querying' do
get api("/projects/#{project.id}/environments", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.first).not_to have_key('enable_advanced_logs_querying')
expect(json_response.first).not_to have_key('logs_api_path')
expect(json_response.first).not_to have_key('gitlab_managed_apps_logs_path')
end
end end
context 'when filtering' do context 'when filtering' do

View File

@ -50,22 +50,6 @@ RSpec.describe Projects::IssuesController do
a_hash_including('id' => discussion_2.id.to_s) a_hash_including('id' => discussion_2.id.to_s)
]) ])
end end
context 'when paginated_issue_discussions is disabled' do
before do
stub_feature_flags(paginated_issue_discussions: false)
end
it 'returns all discussions and ignores per_page param' do
get_discussions(per_page: 2)
discussions = Gitlab::Json.parse(response.body)
notes = discussions.flat_map { |d| d['notes'] }
expect(discussions.count).to eq(4)
expect(notes.count).to eq(5)
end
end
end end
end end

View File

@ -20,7 +20,7 @@ RSpec.describe RuboCop::Cop::Gitlab::NamespacedClass do
it 'flags a class definition without additional namespace' do it 'flags a class definition without additional namespace' do
expect_offense(namespaced(<<~SOURCE)) expect_offense(namespaced(<<~SOURCE))
class MyClass class MyClass
^^^^^^^^^^^^^ #{described_class::MSG} ^^^^^^^ #{described_class::MSG}
end end
SOURCE SOURCE
end end
@ -28,7 +28,7 @@ RSpec.describe RuboCop::Cop::Gitlab::NamespacedClass do
it 'flags a compact class definition without additional namespace' do it 'flags a compact class definition without additional namespace' do
expect_offense(<<~SOURCE, namespace: namespace) expect_offense(<<~SOURCE, namespace: namespace)
class %{namespace}::MyClass class %{namespace}::MyClass
^{namespace}^^^^^^^^^^^^^^^ #{described_class::MSG} ^{namespace}^^^^^^^^^ #{described_class::MSG}
end end
SOURCE SOURCE
end end
@ -36,7 +36,7 @@ RSpec.describe RuboCop::Cop::Gitlab::NamespacedClass do
it 'flags a class definition with inheritance without additional namespace' do it 'flags a class definition with inheritance without additional namespace' do
expect_offense(namespaced(<<~SOURCE)) expect_offense(namespaced(<<~SOURCE))
class MyClass < ApplicationRecord class MyClass < ApplicationRecord
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG} ^^^^^^^ #{described_class::MSG}
def some_method def some_method
true true
end end

View File

@ -56,37 +56,6 @@ RSpec.describe ClusterEntity do
end end
end end
context 'gitlab_managed_apps_logs_path' do
let(:cluster) { create(:cluster, :project) }
let(:user) { create(:user) }
subject { described_class.new(cluster, request: request).as_json }
before do
allow_next_instance_of(Clusters::ClusterPresenter) do |presenter|
allow(presenter).to receive(:show_path).and_return(nil)
end
end
it 'return projects log explorer path' do
log_explorer_path = project_logs_path(cluster.project, cluster_id: cluster.id)
expect_next_instance_of(Clusters::ClusterPresenter, cluster, current_user: user) do |presenter|
expect(presenter).to receive(:gitlab_managed_apps_logs_path).and_return(log_explorer_path)
end
expect(subject[:gitlab_managed_apps_logs_path]).to eq(log_explorer_path)
end
context 'when feature is disabled' do
before do
stub_feature_flags(monitor_logging: false)
end
specify { is_expected.not_to include(:gitlab_managed_apps_logs_path) }
end
end
context 'enable_advanced_logs_querying' do context 'enable_advanced_logs_querying' do
let(:cluster) { create(:cluster, :project) } let(:cluster) { create(:cluster, :project) }
let(:user) { create(:user) } let(:user) { create(:user) }

View File

@ -133,54 +133,6 @@ RSpec.describe EnvironmentEntity do
end end
end end
context 'pod_logs' do
context 'with reporter access' do
before do
project.add_reporter(user)
end
it 'does not expose logs keys' do
expect(subject).not_to include(:logs_path)
expect(subject).not_to include(:logs_api_path)
expect(subject).not_to include(:enable_advanced_logs_querying)
end
end
context 'with developer access' do
before do
project.add_developer(user)
end
it 'exposes logs keys' do
expect(subject).to include(:logs_path)
expect(subject).to include(:logs_api_path)
expect(subject).to include(:enable_advanced_logs_querying)
end
it 'uses k8s api when ES is not available' do
expect(subject[:logs_api_path]).to eq(k8s_project_logs_path(project, environment_name: environment.name, format: :json))
end
it 'uses ES api when ES is available' do
allow(environment).to receive(:elastic_stack_available?).and_return(true)
expect(subject[:logs_api_path]).to eq(elasticsearch_project_logs_path(project, environment_name: environment.name, format: :json))
end
context 'with feature flag disabled' do
before do
stub_feature_flags(monitor_logging: false)
end
it 'does not expose logs keys' do
expect(subject).not_to include(:logs_path)
expect(subject).not_to include(:logs_api_path)
expect(subject).not_to include(:enable_advanced_logs_querying)
end
end
end
end
context 'with deployment service ready' do context 'with deployment service ready' do
before do before do
allow(environment).to receive(:has_terminals?).and_return(true) allow(environment).to receive(:has_terminals?).and_return(true)

169
yarn.lock
View File

@ -976,6 +976,11 @@
exec-sh "^0.3.2" exec-sh "^0.3.2"
minimist "^1.2.0" minimist "^1.2.0"
"@csstools/selector-specificity@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.1.tgz#b6b8d81780b9a9f6459f4bfe9226ac6aefaefe87"
integrity sha512-aG20vknL4/YjQF9BSV7ts4EWm/yrjagAN7OWBNmlbEOUiu0llj4OGrFoOKK3g2vey4/p2omKCoHrWtPxSwV3HA==
"@discoveryjs/json-ext@^0.5.0": "@discoveryjs/json-ext@^0.5.0":
version "0.5.6" version "0.5.6"
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f"
@ -1034,14 +1039,14 @@
resolved "https://registry.yarnpkg.com/@gitlab/favicon-overlay/-/favicon-overlay-2.0.0.tgz#2f32d0b6a4d5b8ac44e2927083d9ab478a78c984" resolved "https://registry.yarnpkg.com/@gitlab/favicon-overlay/-/favicon-overlay-2.0.0.tgz#2f32d0b6a4d5b8ac44e2927083d9ab478a78c984"
integrity sha512-GNcORxXJ98LVGzOT9dDYKfbheqH6lNgPDD72lyXRnQIH7CjgGyos8i17aSBPq1f4s3zF3PyedFiAR4YEZbva2Q== integrity sha512-GNcORxXJ98LVGzOT9dDYKfbheqH6lNgPDD72lyXRnQIH7CjgGyos8i17aSBPq1f4s3zF3PyedFiAR4YEZbva2Q==
"@gitlab/stylelint-config@4.0.0": "@gitlab/stylelint-config@4.1.0":
version "4.0.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/@gitlab/stylelint-config/-/stylelint-config-4.0.0.tgz#d24646592908d92dce35aaa8e7bea66fd53e4160" resolved "https://registry.yarnpkg.com/@gitlab/stylelint-config/-/stylelint-config-4.1.0.tgz#bd431406c8f8725afba353652f08e42c3301a982"
integrity sha512-5avbb8XQLdnL/vdrxf9jvdPl/HPaXnI7Y/fArOHe1uMpgcBoBWUKmon15+lHJ4MgURsGuYSiM2FIgArDSylR4g== integrity sha512-WiTLRKoXutmAKR0z4Z/kuXiTAtblyA2XWX6vb2/NHhLd7oboKrxR8CiznSfw7SNxmYtqW/ripaQOdanR1fYMwA==
dependencies: dependencies:
postcss-scss "4.0.3" postcss-scss "4.0.4"
stylelint-declaration-strict-value "1.8.0" stylelint-declaration-strict-value "1.8.0"
stylelint-scss "4.1.0" stylelint-scss "4.2.0"
"@gitlab/svgs@2.21.0": "@gitlab/svgs@2.21.0":
version "2.21.0" version "2.21.0"
@ -3170,7 +3175,7 @@ braces@^2.3.1:
split-string "^3.0.2" split-string "^3.0.2"
to-regex "^3.0.1" to-regex "^3.0.1"
braces@^3.0.1, braces@~3.0.2: braces@^3.0.2, braces@~3.0.2:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
@ -4114,6 +4119,11 @@ css-color-names@0.0.4:
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=
css-functions-list@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.1.0.tgz#cf5b09f835ad91a00e5959bcfc627cd498e1321b"
integrity sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w==
css-loader@^2.1.1: css-loader@^2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea"
@ -4788,7 +4798,7 @@ debug@^3.1.0, debug@^3.2.6, debug@^3.2.7:
dependencies: dependencies:
ms "^2.1.1" ms "^2.1.1"
debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4" version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@ -6738,10 +6748,10 @@ html-escaper@^2.0.0:
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491"
integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig== integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==
html-tags@^3.1.0: html-tags@^3.2.0:
version "3.1.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961"
integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== integrity sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==
html-void-elements@^2.0.0: html-void-elements@^2.0.0:
version "2.0.1" version "2.0.1"
@ -8117,10 +8127,10 @@ kleur@^4.0.3:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d" resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d"
integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==
known-css-properties@^0.24.0: known-css-properties@^0.25.0:
version "0.24.0" version "0.25.0"
resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.24.0.tgz#19aefd85003ae5698a5560d2b55135bf5432155c" resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.25.0.tgz#6ebc4d4b412f602e5cfbeb4086bd544e34c0a776"
integrity sha512-RTSoaUAfLvpR357vWzAz/50Q/BmHfmE6ETSWfutT0AJiw10e6CmcdYRQJlLRd95B53D0Y2aD1jSxD3V3ySF+PA== integrity sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==
latest-version@^5.0.0: latest-version@^5.0.0:
version "5.1.0" version "5.1.0"
@ -9090,13 +9100,13 @@ micromatch@^3.1.10, micromatch@^3.1.4:
snapdragon "^0.8.1" snapdragon "^0.8.1"
to-regex "^3.0.2" to-regex "^3.0.2"
micromatch@^4.0.2, micromatch@^4.0.4: micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
version "4.0.4" version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
dependencies: dependencies:
braces "^3.0.1" braces "^3.0.2"
picomatch "^2.2.3" picomatch "^2.3.1"
miller-rabin@^4.0.0: miller-rabin@^4.0.0:
version "4.0.1" version "4.0.1"
@ -9366,10 +9376,10 @@ multicast-dns@^7.2.4:
dns-packet "^5.2.2" dns-packet "^5.2.2"
thunky "^1.0.2" thunky "^1.0.2"
nanoid@^3.1.30: nanoid@^3.3.4:
version "3.2.0" version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
nanomatch@^1.2.9: nanomatch@^1.2.9:
version "1.2.13" version "1.2.13"
@ -9549,11 +9559,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
normalize-selector@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03"
integrity sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=
normalize-url@^4.1.0: normalize-url@^4.1.0:
version "4.5.0" version "4.5.0"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
@ -10056,10 +10061,10 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
version "2.3.0" version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pify@^2.0.0: pify@^2.0.0:
version "2.3.0" version "2.3.0"
@ -10176,15 +10181,15 @@ postcss-safe-parser@^6.0.0:
resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1" resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1"
integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ== integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==
postcss-scss@4.0.3: postcss-scss@4.0.4:
version "4.0.3" version "4.0.4"
resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.3.tgz#36c23c19a804274e722e83a54d20b838ab4767ac" resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.4.tgz#aa8f60e19ee18259bc193db9e4b96edfce3f3b1f"
integrity sha512-j4KxzWovfdHsyxwl1BxkUal/O4uirvHgdzMKS1aWJBAV0qh2qj5qAZqpeBfVUYGWv+4iK9Az7SPyZ4fyNju1uA== integrity sha512-aBBbVyzA8b3hUL0MGrpydxxXKXFZc5Eqva0Q3V9qsBOLEMsjb6w49WfpsoWzpEgcqJGW4t7Rio8WXVU9Gd8vWg==
postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9: postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9:
version "6.0.9" version "6.0.10"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ== integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
dependencies: dependencies:
cssesc "^3.0.0" cssesc "^3.0.0"
util-deprecate "^1.0.2" util-deprecate "^1.0.2"
@ -10199,14 +10204,14 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@8.4.5, postcss@^8.2.1, postcss@^8.4.5: postcss@8.4.14, postcss@^8.2.1, postcss@^8.4.14:
version "8.4.5" version "8.4.14"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf"
integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==
dependencies: dependencies:
nanoid "^3.1.30" nanoid "^3.3.4"
picocolors "^1.0.0" picocolors "^1.0.0"
source-map-js "^1.0.1" source-map-js "^1.0.2"
postcss@^7.0.14, postcss@^7.0.5, postcss@^7.0.6: postcss@^7.0.14, postcss@^7.0.5, postcss@^7.0.6:
version "7.0.39" version "7.0.39"
@ -11406,10 +11411,10 @@ sigmund@^1.0.1:
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=
signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7:
version "3.0.6" version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
sirv@^1.0.7: sirv@^1.0.7:
version "1.0.11" version "1.0.11"
@ -11502,7 +11507,7 @@ source-list-map@^2.0.0:
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085"
integrity sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A== integrity sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1: "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
@ -11600,11 +11605,6 @@ spdy@^4.0.2:
select-hose "^2.0.0" select-hose "^2.0.0"
spdy-transport "^3.0.0" spdy-transport "^3.0.0"
specificity@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.1.tgz#aab5e645012db08ba182e151165738d00887b019"
integrity sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==
split-string@^3.0.1, split-string@^3.0.2: split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@ -11846,10 +11846,10 @@ stylelint-declaration-strict-value@1.8.0:
css-values "^0.1.0" css-values "^0.1.0"
shortcss "^0.1.3" shortcss "^0.1.3"
stylelint-scss@4.1.0: stylelint-scss@4.2.0:
version "4.1.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-4.1.0.tgz#39b808696f8152081163d970449257ff80b5c041" resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-4.2.0.tgz#e25fd390ee38a7e89fcfaec2a8f9dce2ec6ddee8"
integrity sha512-BNYTo7MMamhFOlcaAWp2dMpjg6hPyM/FFqfDIYzmYVLMmQJqc8lWRIiTqP4UX5bresj9Vo0dKC6odSh43VP2NA== integrity sha512-HHHMVKJJ5RM9pPIbgJ/XA67h9H0407G68Rm69H4fzFbFkyDMcTV1Byep3qdze5+fJ3c0U7mJrbj6S0Fg072uZA==
dependencies: dependencies:
lodash "^4.17.21" lodash "^4.17.21"
postcss-media-query-parser "^0.2.3" postcss-media-query-parser "^0.2.3"
@ -11857,15 +11857,17 @@ stylelint-scss@4.1.0:
postcss-selector-parser "^6.0.6" postcss-selector-parser "^6.0.6"
postcss-value-parser "^4.1.0" postcss-value-parser "^4.1.0"
stylelint@^14.3.0: stylelint@^14.9.1:
version "14.3.0" version "14.9.1"
resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-14.3.0.tgz#26b62730da7b3dc320021fc469d80048d7b77ebe" resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-14.9.1.tgz#6494ed38f148b1e75b402d678a3b6a8aae86dfda"
integrity sha512-PZXSwtJe4f4qBPWBwAbHL0M0Qjrv8iHN+cLpUNsffaVMS3YzpDDRI73+2lsqLAYfQEzxRwpll6BDKImREbpHWA== integrity sha512-RdAkJdPiLqHawCSnu21nE27MjNXaVd4WcOHA4vK5GtIGjScfhNnaOuWR2wWdfKFAvcWQPOYe311iveiVKSmwsA==
dependencies: dependencies:
"@csstools/selector-specificity" "^2.0.1"
balanced-match "^2.0.0" balanced-match "^2.0.0"
colord "^2.9.2" colord "^2.9.2"
cosmiconfig "^7.0.1" cosmiconfig "^7.0.1"
debug "^4.3.3" css-functions-list "^3.1.0"
debug "^4.3.4"
execall "^2.0.0" execall "^2.0.0"
fast-glob "^3.2.11" fast-glob "^3.2.11"
fastest-levenshtein "^1.0.12" fastest-levenshtein "^1.0.12"
@ -11874,26 +11876,24 @@ stylelint@^14.3.0:
global-modules "^2.0.0" global-modules "^2.0.0"
globby "^11.1.0" globby "^11.1.0"
globjoin "^0.1.4" globjoin "^0.1.4"
html-tags "^3.1.0" html-tags "^3.2.0"
ignore "^5.2.0" ignore "^5.2.0"
import-lazy "^4.0.0" import-lazy "^4.0.0"
imurmurhash "^0.1.4" imurmurhash "^0.1.4"
is-plain-object "^5.0.0" is-plain-object "^5.0.0"
known-css-properties "^0.24.0" known-css-properties "^0.25.0"
mathml-tag-names "^2.1.3" mathml-tag-names "^2.1.3"
meow "^9.0.0" meow "^9.0.0"
micromatch "^4.0.4" micromatch "^4.0.5"
normalize-path "^3.0.0" normalize-path "^3.0.0"
normalize-selector "^0.2.0"
picocolors "^1.0.0" picocolors "^1.0.0"
postcss "^8.4.5" postcss "^8.4.14"
postcss-media-query-parser "^0.2.3" postcss-media-query-parser "^0.2.3"
postcss-resolve-nested-selector "^0.1.1" postcss-resolve-nested-selector "^0.1.1"
postcss-safe-parser "^6.0.0" postcss-safe-parser "^6.0.0"
postcss-selector-parser "^6.0.9" postcss-selector-parser "^6.0.10"
postcss-value-parser "^4.2.0" postcss-value-parser "^4.2.0"
resolve-from "^5.0.0" resolve-from "^5.0.0"
specificity "^0.4.1"
string-width "^4.2.3" string-width "^4.2.3"
strip-ansi "^6.0.1" strip-ansi "^6.0.1"
style-search "^0.1.0" style-search "^0.1.0"
@ -11901,7 +11901,7 @@ stylelint@^14.3.0:
svg-tags "^1.0.0" svg-tags "^1.0.0"
table "^6.8.0" table "^6.8.0"
v8-compile-cache "^2.3.0" v8-compile-cache "^2.3.0"
write-file-atomic "^4.0.0" write-file-atomic "^4.0.1"
stylis@^4.0.10: stylis@^4.0.10:
version "4.0.10" version "4.0.10"
@ -12382,11 +12382,6 @@ typedarray-to-buffer@^3.1.5:
dependencies: dependencies:
is-typedarray "^1.0.0" is-typedarray "^1.0.0"
typedarray-to-buffer@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-4.0.0.tgz#cdd2933c61dd3f5f02eda5d012d441f95bfeb50a"
integrity sha512-6dOYeZfS3O9RtRD1caom0sMxgK59b27+IwoNy8RDPsmslSGOyU+mpTamlaIW7aNKi90ZQZ9DFaZL3YRoiSCULQ==
typedarray@^0.0.6: typedarray@^0.0.6:
version "0.0.6" version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@ -13311,15 +13306,13 @@ write-file-atomic@^3.0.0:
signal-exit "^3.0.2" signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5" typedarray-to-buffer "^3.1.5"
write-file-atomic@^4.0.0: write-file-atomic@^4.0.1:
version "4.0.0" version "4.0.1"
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.0.tgz#0eff5dc687d3e22535ca3fca8558124645a4b053" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f"
integrity sha512-JhcWoKffJNF7ivO9yflBhc7tn3wKnokMUfWpBriM9yCXj4ePQnRPcWglBkkg1AHC8nsW/EfxwwhqsLtOy59djA== integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==
dependencies: dependencies:
imurmurhash "^0.1.4" imurmurhash "^0.1.4"
is-typedarray "^1.0.0" signal-exit "^3.0.7"
signal-exit "^3.0.2"
typedarray-to-buffer "^4.0.0"
"ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.2.3, ws@^7.3.1: "ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.2.3, ws@^7.3.1:
version "7.5.5" version "7.5.5"