Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile10
-rw-r--r--app/assets/javascripts/captcha/captcha_modal_axios_interceptor.js56
-rw-r--r--app/assets/javascripts/diffs/store/actions.js5
-rw-r--r--app/assets/javascripts/issue_show/services/index.js2
-rw-r--r--app/assets/javascripts/lib/utils/axios_utils.js3
-rw-r--r--app/controllers/concerns/spammable_actions.rb18
-rw-r--r--app/controllers/projects/commit_controller.rb3
-rw-r--r--app/controllers/projects/issues_controller.rb1
-rw-r--r--app/controllers/projects/merge_requests_controller.rb2
-rw-r--r--app/controllers/projects/pipelines_controller.rb1
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--config/environments/production.rb3
-rw-r--r--config/feature_flags/development/gitlab_experiment_middleware.yml8
-rw-r--r--config/initializers/7_prometheus_metrics.rb2
-rw-r--r--config/initializers/gitlab_experiment.rb14
-rw-r--r--db/migrate/20210601132134_remove_partial_index_for_hashed_storage_migration.rb15
-rw-r--r--db/schema_migrations/202106011321341
-rw-r--r--db/structure.sql2
-rw-r--r--doc/.vale/gitlab/HeaderGerunds.yml14
-rw-r--r--doc/administration/monitoring/ip_whitelist.md15
-rw-r--r--doc/topics/autodevops/stages.md9
-rw-r--r--doc/user/analytics/ci_cd_analytics.md4
-rw-r--r--doc/user/application_security/threat_monitoring/index.md23
-rw-r--r--doc/user/clusters/agent/index.md27
-rw-r--r--doc/user/clusters/agent/repository.md4
-rw-r--r--doc/user/clusters/applications.md2
-rw-r--r--doc/user/discussions/img/thread_view.pngbin162019 -> 0 bytes
-rw-r--r--doc/user/discussions/img/unresolved_threads_v14_1.pngbin0 -> 1540 bytes
-rw-r--r--doc/user/discussions/index.md29
-rw-r--r--doc/user/index.md2
-rw-r--r--doc/user/packages/npm_registry/index.md14
-rw-r--r--doc/user/project/clusters/protect/container_network_security/quick_start_guide.md117
-rw-r--r--doc/user/project/merge_requests/index.md2
-rw-r--r--doc/user/project/merge_requests/reviews/index.md2
-rw-r--r--lib/gitlab/metrics/exporter/base_exporter.rb3
-rw-r--r--lib/gitlab/metrics/exporter/sidekiq_exporter.rb3
-rw-r--r--lib/gitlab/metrics/exporter/web_exporter.rb3
-rw-r--r--lib/gitlab/metrics/prometheus.rb2
-rw-r--r--lib/gitlab/pagination/offset_pagination.rb15
-rw-r--r--locale/gitlab.pot39
-rw-r--r--spec/controllers/projects/commit_controller_spec.rb22
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb12
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb14
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb8
-rw-r--r--spec/features/groups/import_export/connect_instance_spec.rb2
-rw-r--r--spec/frontend/captcha/captcha_modal_axios_interceptor_spec.js55
-rw-r--r--spec/lib/gitlab/pagination/offset_pagination_spec.rb74
-rw-r--r--spec/models/ci/pipeline_spec.rb4
-rw-r--r--spec/models/integrations/base_chat_notification_spec.rb88
50 files changed, 515 insertions, 243 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 7b249908114..b3dbd184b31 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-21e7c85471a8d7401fad69be300eaff1c0384577
+82a7a8e90f5bf3f0cae18d158a28eb8a7a1693c6
diff --git a/Gemfile b/Gemfile
index 7b1396e6b53..85650d09eed 100644
--- a/Gemfile
+++ b/Gemfile
@@ -335,13 +335,9 @@ gem 'peek', '~> 1.1'
gem 'snowplow-tracker', '~> 0.6.1'
# Metrics
-group :metrics do
- gem 'method_source', '~> 1.0', require: false
- gem 'webrick', '~> 1.6.1', require: false
-
- # Prometheus
- gem 'prometheus-client-mmap', '~> 0.12.0'
-end
+gem 'method_source', '~> 1.0', require: false
+gem 'webrick', '~> 1.6.1', require: false
+gem 'prometheus-client-mmap', '~> 0.12.0', require: 'prometheus/client'
group :development do
gem 'lefthook', '~> 0.7.0', require: false
diff --git a/app/assets/javascripts/captcha/captcha_modal_axios_interceptor.js b/app/assets/javascripts/captcha/captcha_modal_axios_interceptor.js
index c9eac44eb28..fdab188f6be 100644
--- a/app/assets/javascripts/captcha/captcha_modal_axios_interceptor.js
+++ b/app/assets/javascripts/captcha/captcha_modal_axios_interceptor.js
@@ -1,4 +1,33 @@
-const supportedMethods = ['patch', 'post', 'put'];
+const SUPPORTED_METHODS = ['patch', 'post', 'put'];
+
+function needsCaptchaResponse(err) {
+ return (
+ SUPPORTED_METHODS.includes(err?.config?.method) && err?.response?.data?.needs_captcha_response
+ );
+}
+
+const showCaptchaModalAndResubmit = async (axios, data, errConfig) => {
+ // NOTE: We asynchronously import and unbox the module. Since this is included globally, we don't
+ // do a regular import because that would increase the size of the webpack bundle.
+ const { waitForCaptchaToBeSolved } = await import('~/captcha/wait_for_captcha_to_be_solved');
+
+ // show the CAPTCHA modal and wait for it to be solved or closed
+ const captchaResponse = await waitForCaptchaToBeSolved(data.captcha_site_key);
+
+ // resubmit the original request with the captcha_response and spam_log_id in the headers
+ const originalData = JSON.parse(errConfig.data);
+ const originalHeaders = errConfig.headers;
+ return axios({
+ method: errConfig.method,
+ url: errConfig.url,
+ headers: {
+ ...originalHeaders,
+ 'X-GitLab-Captcha-Response': captchaResponse,
+ 'X-GitLab-Spam-Log-Id': data.spam_log_id,
+ },
+ data: originalData,
+ });
+};
export function registerCaptchaModalInterceptor(axios) {
return axios.interceptors.response.use(
@@ -6,29 +35,8 @@ export function registerCaptchaModalInterceptor(axios) {
return response;
},
(err) => {
- if (
- supportedMethods.includes(err?.config?.method) &&
- err?.response?.data?.needs_captcha_response
- ) {
- const { data } = err.response;
- const captchaSiteKey = data.captcha_site_key;
- const spamLogId = data.spam_log_id;
- // eslint-disable-next-line promise/no-promise-in-callback
- return import('~/captcha/wait_for_captcha_to_be_solved')
- .then(({ waitForCaptchaToBeSolved }) => waitForCaptchaToBeSolved(captchaSiteKey))
- .then((captchaResponse) => {
- const errConfig = err.config;
- const originalData = JSON.parse(errConfig.data);
- return axios({
- method: errConfig.method,
- url: errConfig.url,
- data: {
- ...originalData,
- captcha_response: captchaResponse,
- spam_log_id: spamLogId,
- },
- });
- });
+ if (needsCaptchaResponse(err)) {
+ return showCaptchaModalAndResubmit(axios, err.response.data, err.config);
}
return Promise.reject(err);
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index b8180ec47e5..330ff8f8f83 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -118,7 +118,10 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
commit(types.SET_BATCH_LOADING, false);
if (window.gon?.features?.diffsVirtualScrolling && !scrolledVirtualScroller) {
- const index = state.diffFiles.findIndex((f) => f.file_hash === hash);
+ const index = state.diffFiles.findIndex(
+ (f) =>
+ f.file_hash === hash || f[INLINE_DIFF_LINES_KEY].find((l) => l.line_code === hash),
+ );
if (index >= 0) {
eventHub.$emit('scrollToIndex', index);
diff --git a/app/assets/javascripts/issue_show/services/index.js b/app/assets/javascripts/issue_show/services/index.js
index 08b04ebfdaf..b1deeaae0fc 100644
--- a/app/assets/javascripts/issue_show/services/index.js
+++ b/app/assets/javascripts/issue_show/services/index.js
@@ -1,11 +1,9 @@
-import { registerCaptchaModalInterceptor } from '~/captcha/captcha_modal_axios_interceptor';
import axios from '../../lib/utils/axios_utils';
export default class Service {
constructor(endpoint) {
this.endpoint = `${endpoint}.json`;
this.realtimeEndpoint = `${endpoint}/realtime_changes`;
- registerCaptchaModalInterceptor(axios);
}
getData() {
diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js
index 204c84b879e..0a26f78e253 100644
--- a/app/assets/javascripts/lib/utils/axios_utils.js
+++ b/app/assets/javascripts/lib/utils/axios_utils.js
@@ -1,4 +1,5 @@
import axios from 'axios';
+import { registerCaptchaModalInterceptor } from '~/captcha/captcha_modal_axios_interceptor';
import setupAxiosStartupCalls from './axios_startup_calls';
import csrf from './csrf';
import suppressAjaxErrorsDuringNavigation from './suppress_ajax_errors_during_navigation';
@@ -41,6 +42,8 @@ axios.interceptors.response.use(
(err) => suppressAjaxErrorsDuringNavigation(err, isUserNavigating),
);
+registerCaptchaModalInterceptor(axios);
+
export default axios;
/**
diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb
index 5f47d9fadef..eb1223f22a9 100644
--- a/app/controllers/concerns/spammable_actions.rb
+++ b/app/controllers/concerns/spammable_actions.rb
@@ -47,17 +47,13 @@ module SpammableActions
end
end
- # TODO: This method is currently only needed for issue create and update. It can be removed when:
- #
- # 1. Issue create is is converted to a client/JS based approach instead of the legacy HAML
- # `_recaptcha_form.html.haml` which is rendered via the `projects/issues/verify` template.
- # In this case, which is based on the legacy reCAPTCHA implementation using the HTML/HAML form,
- # the 'g-recaptcha-response' field name comes from `Recaptcha::ClientHelper#recaptcha_tags` in the
- # recaptcha gem, which is called from the HAML `_recaptcha_form.html.haml` form.
- # 2. Issue update is converted to use the headers-based approach, which will require adding
- # support to captcha_modal_axios_interceptor.js like we have already added to
- # apollo_captcha_link.js.
- # In this case, the `captcha_response` field name comes from our captcha_modal_axios_interceptor.js.
+ # TODO: This method is currently only needed for issue create, to convert spam/CAPTCHA values from
+ # params, and instead be passed as headers, as the spam services now all expect. It can be removed
+ # when issue create is is converted to a client/JS based approach instead of the legacy HAML
+ # `_recaptcha_form.html.haml` which is rendered via the `projects/issues/verify` template.
+ # In that case, which is based on the legacy reCAPTCHA implementation using the HTML/HAML form,
+ # the 'g-recaptcha-response' field name comes from `Recaptcha::ClientHelper#recaptcha_tags` in the
+ # recaptcha gem, which is called from the HAML `_recaptcha_form.html.haml` form.
def extract_legacy_spam_params_to_headers
request.headers['X-GitLab-Captcha-Response'] = params['g-recaptcha-response'] || params[:captcha_response]
request.headers['X-GitLab-Spam-Log-Id'] = params[:spam_log_id]
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 3d2398f7ee3..6748be06ded 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -52,7 +52,8 @@ class Projects::CommitController < Projects::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def pipelines
@pipelines = @commit.pipelines.order(id: :desc)
- @pipelines = @pipelines.where(ref: params[:ref]).page(params[:page]).per(30) if params[:ref]
+ @pipelines = @pipelines.where(ref: params[:ref]) if params[:ref]
+ @pipelines = @pipelines.page(params[:page])
respond_to do |format|
format.html
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 0bb0228cfea..5d38e431c8a 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -336,7 +336,6 @@ class Projects::IssuesController < Projects::ApplicationController
end
def update_service
- extract_legacy_spam_params_to_headers
spam_params = ::Spam::SpamParams.new_from_request(request: request)
::Issues::UpdateService.new(project: project, current_user: current_user, params: issue_params, spam_params: spam_params)
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 8ed5c98084c..3cf0fee2536 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -167,7 +167,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
def pipelines
set_pipeline_variables
- @pipelines = @pipelines.page(params[:page]).per(30)
+ @pipelines = @pipelines.page(params[:page])
Gitlab::PollingInterval.set_header(response, interval: 10_000)
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 98feadd8b23..576492110d8 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -42,7 +42,6 @@ class Projects::PipelinesController < Projects::ApplicationController
.new(project, current_user, index_params)
.execute
.page(params[:page])
- .per(20)
@pipelines_count = limited_pipelines_count(project)
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 1eb07e24cff..3a6bebb610b 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -29,6 +29,8 @@ module Ci
BridgeStatusError = Class.new(StandardError)
+ paginates_per 15
+
sha_attribute :source_sha
sha_attribute :target_sha
diff --git a/config/environments/production.rb b/config/environments/production.rb
index f1d6b6c8bbc..e1a7db8d860 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -54,9 +54,6 @@ Rails.application.configure do
# Enable serving of images, stylesheets, and JavaScripts from an asset server
config.action_controller.asset_host = ENV['GITLAB_CDN_HOST'] if ENV['GITLAB_CDN_HOST'].present?
- # We use a env var to keep at old default until we enable this for GitLab.com
- config.active_record.legacy_connection_handling = !Gitlab::Utils.to_boolean(ENV.fetch('ENABLE_RAILS_61_CONNECTION_HANDLING', false))
-
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
diff --git a/config/feature_flags/development/gitlab_experiment_middleware.yml b/config/feature_flags/development/gitlab_experiment_middleware.yml
deleted file mode 100644
index 76f8a36812d..00000000000
--- a/config/feature_flags/development/gitlab_experiment_middleware.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: gitlab_experiment_middleware
-introduced_by_url:
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323643
-milestone: '14.1'
-type: development
-group: group::adoption
-default_enabled: false
diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb
index ebd251abe22..f3fe61d2f92 100644
--- a/config/initializers/7_prometheus_metrics.rb
+++ b/config/initializers/7_prometheus_metrics.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require 'prometheus/client'
-
# Keep separate directories for separate processes
def prometheus_default_multiproc_dir
return unless Rails.env.development? || Rails.env.test?
diff --git a/config/initializers/gitlab_experiment.rb b/config/initializers/gitlab_experiment.rb
index 39131e11260..134285af56d 100644
--- a/config/initializers/gitlab_experiment.rb
+++ b/config/initializers/gitlab_experiment.rb
@@ -7,17 +7,3 @@ Gitlab::Experiment.configure do |config|
pool: ->(&block) { Gitlab::Redis::SharedState.with { |redis| block.call(redis) } }
)
end
-
-# TODO: This shim should be removed after the feature flag is rolled out, as
-# it only exists to facilitate the feature flag control of the behavior.
-module Gitlab::Experiment::MiddlewareWithFeatureFlags
- attr_reader :app
-
- def call(env)
- return app.call(env) unless Feature.enabled?(:gitlab_experiment_middleware)
-
- super
- end
-end
-
-Gitlab::Experiment::Middleware.prepend(Gitlab::Experiment::MiddlewareWithFeatureFlags)
diff --git a/db/migrate/20210601132134_remove_partial_index_for_hashed_storage_migration.rb b/db/migrate/20210601132134_remove_partial_index_for_hashed_storage_migration.rb
new file mode 100644
index 00000000000..d9793832d8c
--- /dev/null
+++ b/db/migrate/20210601132134_remove_partial_index_for_hashed_storage_migration.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class RemovePartialIndexForHashedStorageMigration < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index :projects, :id, name: 'index_on_id_partial_with_legacy_storage'
+ end
+
+ def down
+ add_concurrent_index :projects, :id, where: 'storage_version < 2 or storage_version IS NULL', name: 'index_on_id_partial_with_legacy_storage'
+ end
+end
diff --git a/db/schema_migrations/20210601132134 b/db/schema_migrations/20210601132134
new file mode 100644
index 00000000000..3fa9505de2f
--- /dev/null
+++ b/db/schema_migrations/20210601132134
@@ -0,0 +1 @@
+966299fecd160b594f0837f19cc01b38fc365fa749982f9245c296d912e3eb2f \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 8d3b0e77fa4..03e66c920b0 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -24076,8 +24076,6 @@ CREATE INDEX index_oauth_openid_requests_on_access_grant_id ON oauth_openid_requ
CREATE UNIQUE INDEX index_on_deploy_keys_id_and_type_and_public ON keys USING btree (id, type) WHERE (public = true);
-CREATE INDEX index_on_id_partial_with_legacy_storage ON projects USING btree (id) WHERE ((storage_version < 2) OR (storage_version IS NULL));
-
CREATE INDEX index_on_identities_lower_extern_uid_and_provider ON identities USING btree (lower((extern_uid)::text), provider);
CREATE UNIQUE INDEX index_on_instance_statistics_recorded_at_and_identifier ON analytics_usage_trends_measurements USING btree (identifier, recorded_at);
diff --git a/doc/.vale/gitlab/HeaderGerunds.yml b/doc/.vale/gitlab/HeaderGerunds.yml
new file mode 100644
index 00000000000..c73ed78da34
--- /dev/null
+++ b/doc/.vale/gitlab/HeaderGerunds.yml
@@ -0,0 +1,14 @@
+---
+# Error: gitlab.HeaderGerunds
+#
+# Checks for headers that start with gerunds (ing words).
+# Related to: https://docs.gitlab.com/ee/development/documentation/structure.html
+#
+# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
+extends: substitution
+message: 'Can this header start with an imperative verb, instead of a gerund (ing word)?'
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/#heading-titles
+level: suggestion
+scope: heading
+swap:
+ - '^\w*ing.*': 'Troubleshooting'
diff --git a/doc/administration/monitoring/ip_whitelist.md b/doc/administration/monitoring/ip_whitelist.md
index 522267ce362..20c97a0df8f 100644
--- a/doc/administration/monitoring/ip_whitelist.md
+++ b/doc/administration/monitoring/ip_whitelist.md
@@ -29,6 +29,21 @@ hosts or use IP ranges:
---
+**For installations using cloud native Helm charts**
+
+You can set the required IPs under the `gitlab.webservice.monitoring.ipWhitelist` key. For example:
+
+```yaml
+gitlab:
+ webservice:
+ monitoring:
+ # Monitoring IP whitelist
+ ipWhitelist:
+ - 0.0.0.0/0 # Default
+```
+
+---
+
**For installations from source**
1. Edit `config/gitlab.yml`:
diff --git a/doc/topics/autodevops/stages.md b/doc/topics/autodevops/stages.md
index 58ad11e88af..f689183d556 100644
--- a/doc/topics/autodevops/stages.md
+++ b/doc/topics/autodevops/stages.md
@@ -541,7 +541,7 @@ You must use a Kubernetes network plugin that implements support for
`NetworkPolicy`. The default network plugin for Kubernetes (`kubenet`)
[does not implement](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/#kubenet)
support for it. The [Cilium](https://cilium.io/) network plugin can be
-installed as a [cluster application](../../user/clusters/applications.md#install-cilium-using-gitlab-cicd)
+installed as a [cluster application](../../user/project/clusters/protect/container_network_security/quick_start_guide.md#use-the-cluster-management-template-to-install-cilium)
to enable support for network policies.
You can enable deployment of a network policy by setting the following
@@ -577,7 +577,7 @@ networkPolicy:
```
For more information on installing Network Policies, see
-[Install Cilium using GitLab CI/CD](../../user/clusters/applications.md#install-cilium-using-gitlab-cicd).
+[Use the Cluster Management Template to Install Cilium](../../user/project/clusters/protect/container_network_security/quick_start_guide.md#use-the-cluster-management-template-to-install-cilium).
### Cilium Network Policy
@@ -596,7 +596,7 @@ As the default network plugin for Kubernetes (`kubenet`)
support for it, you must have [Cilium](https://docs.cilium.io/en/v1.8/intro/) as your Kubernetes network plugin.
The [Cilium](https://cilium.io/) network plugin can be
-installed as a [cluster application](../../user/clusters/applications.md#install-cilium-using-gitlab-cicd)
+installed with a [cluster management project template](../../user/project/clusters/protect/container_network_security/quick_start_guide.md#use-the-cluster-management-template-to-install-cilium)
to enable support for network policies.
#### Configuration
@@ -643,11 +643,10 @@ ciliumNetworkPolicy:
enabled: true
alerts:
enabled: true
-
```
For more information on installing Network Policies, see
-[Install Cilium using GitLab CI/CD](../../user/clusters/applications.md#install-cilium-using-gitlab-cicd).
+[Use the Cluster Management Template to Install Cilium](../../user/project/clusters/protect/container_network_security/quick_start_guide.md#use-the-cluster-management-template-to-install-cilium).
### Running commands in the container
diff --git a/doc/user/analytics/ci_cd_analytics.md b/doc/user/analytics/ci_cd_analytics.md
index 1fce741cbef..3c80e1f5f2a 100644
--- a/doc/user/analytics/ci_cd_analytics.md
+++ b/doc/user/analytics/ci_cd_analytics.md
@@ -50,9 +50,9 @@ The following table shows the supported metrics, at which level they are support
| Metric | Level | API version | Chart (UI) version | Comments |
|---------------------------|---------------------|--------------------------------------|---------------------------------------|-----------|
| `deployment_frequency` | Project-level | [13.7+](../../api/dora/metrics.md) | [13.8+](#deployment-frequency-charts) | The [old API endpoint](../../api/dora4_project_analytics.md) was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/323713) in 13.10. |
-| `deployment_frequency` | Group-level | [13.10+](../../api/dora/metrics.md) | To be supported | |
+| `deployment_frequency` | Group-level | [13.10+](../../api/dora/metrics.md) | [13.12+](#deployment-frequency-charts) | |
| `lead_time_for_changes` | Project-level | [13.10+](../../api/dora/metrics.md) | [13.11+](#lead-time-charts) | Unit in seconds. Aggregation method is median. |
-| `lead_time_for_changes` | Group-level | [13.10+](../../api/dora/metrics.md) | To be supported | Unit in seconds. Aggregation method is median. |
+| `lead_time_for_changes` | Group-level | [13.10+](../../api/dora/metrics.md) | [14.0+](#lead-time-charts) | Unit in seconds. Aggregation method is median. |
| `change_failure_rate` | Project/Group-level | To be supported | To be supported | |
| `time_to_restore_service` | Project/Group-level | To be supported | To be supported | |
diff --git a/doc/user/application_security/threat_monitoring/index.md b/doc/user/application_security/threat_monitoring/index.md
index e1200c60419..8cecb9c5929 100644
--- a/doc/user/application_security/threat_monitoring/index.md
+++ b/doc/user/application_security/threat_monitoring/index.md
@@ -27,20 +27,19 @@ your application's Kubernetes namespace. This section has the following
prerequisites:
- Your project contains at least one [environment](../../../ci/environments/index.md)
-- You've [installed Cilium](../../clusters/applications.md#install-cilium-using-gitlab-cicd)
+- You've [installed Cilium](../../project/clusters/protect/container_network_security/quick_start_guide.md#use-the-cluster-management-template-to-install-cilium)
- You've configured the [Prometheus service](../../project/integrations/prometheus.md#enabling-prometheus-integration)
If you're using custom Helm values for Cilium, you must enable Hubble
with flow metrics for each namespace by adding the following lines to
-your [Cilium values](../../clusters/applications.md#install-cilium-using-gitlab-cicd):
+your [Cilium values](../../project/clusters/protect/container_network_security/quick_start_guide.md#use-the-cluster-management-template-to-install-cilium):
```yaml
-global:
- hubble:
- enabled: true
- metrics:
- enabled:
- - 'flow:sourceContext=namespace;destinationContext=namespace'
+hubble:
+ enabled: true
+ metrics:
+ enabled:
+ - 'flow:sourceContext=namespace;destinationContext=namespace'
```
The **Container Network Policy** section displays the following information
@@ -54,7 +53,11 @@ about your packet flow:
If a significant percentage of packets is dropped, you should
investigate it for potential threats by
-[examining the Cilium logs](../../clusters/applications.md#install-cilium-using-gitlab-cicd).
+examining the Cilium logs:
+
+```shell
+kubectl -n gitlab-managed-apps logs -l k8s-app=cilium -c cilium-monitor
+```
## Container Network Policy management
@@ -67,7 +70,7 @@ status, and create and edit deployed policies. This section has the
following prerequisites:
- Your project contains at least one [environment](../../../ci/environments/index.md)
-- You've [installed Cilium](../../clusters/applications.md#install-cilium-using-gitlab-cicd)
+- You've [installed Cilium](../../project/clusters/protect/container_network_security/quick_start_guide.md#use-the-cluster-management-template-to-install-cilium)
Network policies are fetched directly from the selected environment's
deployment platform. Changes performed outside of this tab are
diff --git a/doc/user/clusters/agent/index.md b/doc/user/clusters/agent/index.md
index 4c95ebcd5ba..03fc1f6cb98 100644
--- a/doc/user/clusters/agent/index.md
+++ b/doc/user/clusters/agent/index.md
@@ -448,42 +448,21 @@ There are several components that work in concert for the Agent to generate the
- A working Kubernetes cluster.
- Cilium integration through either of these options:
- - Installation through [GitLab Managed Apps](../applications.md#install-cilium-using-gitlab-cicd).
+ - Installation through [cluster management template](../../project/clusters/protect/container_network_security/quick_start_guide.md#use-the-cluster-management-template-to-install-cilium).
- Enablement of [hubble-relay](https://docs.cilium.io/en/v1.8/concepts/overview/#hubble) on an
existing installation.
- One or more network policies through any of these options:
- Use the [Container Network Policy editor](../../application_security/threat_monitoring/index.md#container-network-policy-editor) to create and manage policies.
- Use an [AutoDevOps](../../application_security/threat_monitoring/index.md#container-network-policy-management) configuration.
- Add the required labels and annotations to existing network policies.
-- Use a configuration repository to inform the Agent through a `config.yaml` file, which
- repositories can synchronize with. This repository might be the same, or a separate GitLab
- project.
+- A configuration repository with [Cilium configured in `config.yaml`](repository.md#surface-network-security-alerts-from-cluster-to-gitlab)
The setup process follows the same steps as [GitOps](#get-started-with-gitops-and-the-gitlab-agent),
with the following differences:
-- When you define a configuration repository, you must do so with [Cilium settings](#define-a-configuration-repository-with-cilium-settings).
+- When you define a configuration repository, you must do so with [Cilium settings](repository.md#surface-network-security-alerts-from-cluster-to-gitlab).
- You do not need to specify the `gitops` configuration section.
-### Define a configuration repository with Cilium settings
-
-You need a GitLab repository to contain your Agent configuration. The minimal repository layout
-looks like this:
-
-```plaintext
-.gitlab/agents/<agent-name>/config.yaml
-```
-
-Your `config.yaml` file must specify the `host` and `port` of your Hubble Relay service. If your
-Cilium integration was performed through [GitLab Managed Apps](../applications.md#install-cilium-using-gitlab-cicd),
-you can use `hubble-relay.gitlab-managed-apps.svc.cluster.local:80`:
-
-```yaml
-cilium:
- hubble_relay_address: "<hubble-relay-host>:<hubble-relay-port>"
- ...
-```
-
## Management interfaces
Users with at least the [Developer](../../permissions.md) can access the user interface
diff --git a/doc/user/clusters/agent/repository.md b/doc/user/clusters/agent/repository.md
index cd40cc6810e..c9ad2ae5ef6 100644
--- a/doc/user/clusters/agent/repository.md
+++ b/doc/user/clusters/agent/repository.md
@@ -157,7 +157,9 @@ cilium:
hubble_relay_address: "<hubble-relay-host>:<hubble-relay-port>"
```
-If your Cilium integration was performed through GitLab Managed Apps, you can use `hubble-relay.gitlab-managed-apps.svc.cluster.local:80` as the address:
+If your Cilium integration was performed through [GitLab Managed Apps](../applications.md#install-cilium-using-gitlab-cicd) or the
+[cluster management template](../../project/clusters/protect/container_network_security/quick_start_guide.md#use-the-cluster-management-template-to-install-cilium),
+you can use `hubble-relay.gitlab-managed-apps.svc.cluster.local:80` as the address:
```yaml
cilium:
diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md
index 501c968debf..e23a08f076d 100644
--- a/doc/user/clusters/applications.md
+++ b/doc/user/clusters/applications.md
@@ -460,7 +460,7 @@ You can check Cilium's installation status on the cluster management page:
WARNING:
Installation and removal of the Cilium requires a **manual**
-[restart](https://docs.cilium.io/en/stable/gettingstarted/k8s-install-gke/#restart-unmanaged-pods)
+[restart](https://docs.cilium.io/en/stable/gettingstarted/k8s-install-helm/#restart-unmanaged-pods)
of all affected pods in all namespaces to ensure that they are
[managed](https://docs.cilium.io/en/v1.8/operations/troubleshooting/#ensure-managed-pod)
by the correct networking plugin. Whenever Hubble is enabled, its related pod might require a
diff --git a/doc/user/discussions/img/thread_view.png b/doc/user/discussions/img/thread_view.png
deleted file mode 100644
index e2db44aa604..00000000000
--- a/doc/user/discussions/img/thread_view.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/discussions/img/unresolved_threads_v14_1.png b/doc/user/discussions/img/unresolved_threads_v14_1.png
new file mode 100644
index 00000000000..806dfdc995c
--- /dev/null
+++ b/doc/user/discussions/img/unresolved_threads_v14_1.png
Binary files differ
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 789cd860451..74ee95e7ded 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -65,7 +65,7 @@ You can create a thread without replying to a standard comment.
Prerequisites:
- You must have at least the [Guest role](../permissions.md#project-members-permissions).
-- You must be in an issue, commit, snippet, or merge request.
+- You must be in an issue, merge request, commit, or snippet.
To create a thread:
@@ -95,25 +95,30 @@ You can edit your own comment at any time.
Anyone with the [Maintainer role](../permissions.md) or
higher can also edit a comment made by someone else.
-## Resolvable comments and threads
+## Resolve a thread
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5022) in GitLab 8.11.
> - Resolvable threads can be added only to merge request diffs.
> - Resolving comments individually was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/28750) in GitLab 13.6.
-Thread resolution helps keep track of progress during planning or code review.
+You can resolve a thread when you want to finish a conversation.
-Every thread in merge requests, commits, commit diffs, and
-snippets is initially displayed as unresolved. They can then be individually resolved by anyone
-with at least the Developer role to the project or by the author of the change being reviewed.
-If the thread has been resolved and a non-member un-resolves their own response,
-this also unresolves the discussion thread.
-If the non-member then resolves this same response, this resolves the discussion thread.
+Prerequisites:
+
+- You must have at least the [Developer role](../permissions.md#project-members-permissions)
+ or be the author of the change being reviewed.
+- You must be in an issue, merge request, commit, or snippet.
+
+To resolve a thread:
+
+1. Go to the thread.
+1. Below the last reply, in the **Reply** field, either:
+ - Select **Resolve thread**.
+ - Enter text, select the **Resolve thread** checkbox, and select **Add comment now**.
-The need to resolve threads prevents you from forgetting to address feedback and lets you
-hide threads that are no longer relevant.
+At the top of the page, the number of unresolved threads is updated.
-!["A thread between two people on a piece of code"](img/thread_view.png)
+![Count of unresolved threads](img/unresolved_threads_v14_1.png)
### Commit threads in the context of a merge request
diff --git a/doc/user/index.md b/doc/user/index.md
index f93d88e106b..ccc4ea05f15 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -136,7 +136,7 @@ merge requests, code snippets, and commits.
When performing inline reviews to implementations
to your codebase through merge requests you can
-gather feedback through [resolvable threads](discussions/index.md#resolvable-comments-and-threads).
+gather feedback through [resolvable threads](discussions/index.md#resolve-a-thread).
### GitLab Flavored Markdown (GFM)
diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md
index ed22804a4ad..5d0a8200e43 100644
--- a/doc/user/packages/npm_registry/index.md
+++ b/doc/user/packages/npm_registry/index.md
@@ -532,12 +532,24 @@ If you get this error, ensure that:
### `npm publish` returns `npm ERR! 400 Bad Request`
-If you get this error, your package name may not meet the
+If you get this error, one of the following problems could be causing it.
+
+#### Package name does not meet the naming convention
+
+Your package name may not meet the
[`@scope/package-name` package naming convention](#package-naming-convention).
Ensure the name meets the convention exactly, including the case.
Then try to publish again.
+#### Package already exists
+
+Your package has already been published to another project in the same
+root namespace and therefore cannot be published again using the same name.
+
+This is also true even if the prior published package shares the same name,
+but not the version.
+
### `npm publish` returns `npm ERR! 500 Internal Server Error - PUT`
This is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/238950) in GitLab
diff --git a/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md b/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md
index 24ee8601638..62010bb7802 100644
--- a/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md
+++ b/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md
@@ -24,16 +24,110 @@ The following steps are recommended to install and use Container Network Securit
into the **Base domain** field on the **Details** tab. Save the changes to the Kubernetes
cluster.
-1. [Install and configure Cilium](../../../../clusters/applications.md#install-cilium-using-gitlab-cicd).
+1. [Install and configure Cilium](#use-the-cluster-management-template-to-install-cilium).
1. Be sure to restart all pods that were running before Cilium was installed by running this command
in your cluster:
`kubectl get pods --all-namespaces -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,HOSTNETWORK:.spec.hostNetwork --no-headers=true | grep '<none>' | awk '{print "-n "$1" "$2}' | xargs -L 1 -r kubectl delete pod`
+ You can skip this step if `nodeinit.restartPods` is set to `true` on your Helm chart.
+
It's possible to install and manage Cilium in other ways. For example, you could use the GitLab Helm
chart to install Cilium manually in a Kubernetes cluster, and then connect it back to GitLab.
However, such methods aren't documented or officially supported by GitLab.
+### Use the Cluster Management template to install Cilium
+
+[Cilium](https://cilium.io/) is a networking plug-in for Kubernetes that you can use to implement
+support for [`NetworkPolicy`](https://kubernetes.io/docs/concepts/services-networking/network-policies/)
+resources. For more information, see [Network Policies](../../../../../topics/autodevops/stages.md#network-policy).
+
+You can use the [Cluster Management Project Template](../../../../clusters/management_project_template.md)
+to install Cilium in your Kubernetes cluster.
+
+1. In your cluster management project, go to `helmfile.yaml` and uncomment `- path: applications/cilium/helmfile.yaml`.
+1. In `applications/cilium/helmfile.yaml`, set `clusterType` to either `gke` or `eks` based on which Kubernetes provider your are using.
+
+ ```yaml
+ environments:
+ default:
+ values:
+ # Set to "gke" or "eks" based on your cluster type
+ - clusterType: ""
+ ```
+
+1. Merge or push these changes to the default branch of your cluster management project,
+and [GitLab CI/CD](../../../../../ci/README.md) will automatically install Cilium.
+
+WARNING:
+Installation and removal of the Cilium requires a **manual**
+[restart](https://docs.cilium.io/en/stable/gettingstarted/k8s-install-helm/#restart-unmanaged-pods)
+of all affected pods in all namespaces to ensure that they are
+[managed](https://docs.cilium.io/en/stable/operations/troubleshooting/#ensure-managed-pod)
+by the correct networking plug-in. When Hubble is enabled, its related pod might require a
+restart depending on whether it started prior to Cilium. For more information, see
+[Failed Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#failed-deployment)
+in the Kubernetes docs.
+
+NOTE:
+Major upgrades might require additional setup steps. For more information, see
+the official [upgrade guide](https://docs.cilium.io/en/stable/operations/upgrade/).
+
+Support for installing the Cilium application is provided by the
+GitLab Container Security group. If you run into unknown issues,
+[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at
+least 2 people from the
+[Container Security group](https://about.gitlab.com/handbook/product/categories/#container-security-group).
+
+### Configure the Cilium Helm chart
+
+You can customize Cilium's Helm variables by editing the `applications/cilium/values.yaml`
+file in your cluster management project. Refer to the [Cilium Helm reference](https://docs.cilium.io/en/stable/helm-reference/)
+for the available configuration options.
+
+By default, Cilium's
+[audit mode](https://docs.cilium.io/en/stable/gettingstarted/policy-creation/#enable-policy-audit-mode)
+is enabled. In audit mode, Cilium doesn't drop disallowed packets. You
+can use `policy-verdict` log to observe policy-related decisions. You
+can disable audit mode by setting `policyAuditMode: false` in
+`applications/cilium/values.yaml`.
+
+The Cilium monitor log for traffic is logged out by the
+`cilium-monitor` sidecar container. You can check these logs with the following command:
+
+```shell
+kubectl -n gitlab-managed-apps logs -l k8s-app=cilium -c cilium-monitor
+```
+
+You can disable the monitor log in `application/cilium/values.yaml`:
+
+```yaml
+monitor:
+ enabled: false
+```
+
+The [Hubble](https://github.com/cilium/hubble) monitoring daemon is enabled by default
+and it's set to collect per namespace flow metrics. This metrics are accessible on the
+[Threat Monitoring](../../../../application_security/threat_monitoring/index.md)
+dashboard. You can disable Hubble by adding the following to
+`applications/cilium/values.yaml`:
+
+```yaml
+hubble:
+ enabled: false
+```
+
+You can also adjust Helm values for Hubble by using
+`applications/cilium/values.yaml`:
+
+```yaml
+hubble:
+ enabled: true
+ metrics:
+ enabled:
+ - 'flow:sourceContext=namespace;destinationContext=namespace'
+```
+
## Managing Network Policies
Managing NetworkPolicies through GitLab is advantageous over managing the policies in Kubernetes
@@ -62,16 +156,14 @@ editor.
To view statistics for Container Network Security, you must follow the installation steps above and
configure GitLab integration with Prometheus. Also, if you use custom Helm values for Cilium, you
must enable Hubble with flow metrics for each namespace by adding the following lines to
-your [Cilium values](../../../../clusters/applications.md#install-cilium-using-gitlab-cicd):
-your [Cilium values](../../../../clusters/applications.md#install-cilium-using-gitlab-cicd):
+your [Cilium values](#use-the-cluster-management-template-to-install-cilium):
```yaml
-global:
- hubble:
- enabled: true
- metrics:
- enabled:
- - 'flow:sourceContext=namespace;destinationContext=namespace'
+hubble:
+ enabled: true
+ metrics:
+ enabled:
+ - 'flow:sourceContext=namespace;destinationContext=namespace'
```
Additional information about the statistics page is available in the
@@ -97,15 +189,14 @@ kubectl -n gitlab-managed-apps logs -l k8s-app=cilium -c cilium-monitor
By default, Cilium is installed in Audit mode only, meaning that NetworkPolicies log policy
violations but don't block any traffic. To set Cilium to Blocking mode, you must add the following
-lines to the `.gitlab/managed-apps/cilium/values.yaml` file in your cluster management project:
+lines to the `applications/cilium/values.yaml` file in your cluster management project:
```yaml
config:
policyAuditMode: false
-agent:
- monitor:
- eventTypes: ["drop"]
+monitor:
+ eventTypes: ["drop"]
```
### Traffic is not being allowed as expected
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index f853bba7ad4..fa90cf524ec 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -27,7 +27,7 @@ important parts of the merge request:
![Merge request tab positions](img/merge_request_tab_position_v13_11.png)
- **Overview**: Contains the description, notifications from pipelines, and a
- discussion area for [comment threads](../../discussions/index.md#resolvable-comments-and-threads)
+ discussion area for [comment threads](../../discussions/index.md#resolve-a-thread))
and [code suggestions](reviews/suggestions.md). The right sidebar provides fields
to add assignees, reviewers, labels, and a milestone to your work, and the
[merge request widgets area](widgets.md) reports results from pipelines and tests.
diff --git a/doc/user/project/merge_requests/reviews/index.md b/doc/user/project/merge_requests/reviews/index.md
index 317202e9303..16267e13fd5 100644
--- a/doc/user/project/merge_requests/reviews/index.md
+++ b/doc/user/project/merge_requests/reviews/index.md
@@ -100,7 +100,7 @@ When you submit your review, GitLab:
### Resolving/Unresolving threads
-Review comments can also resolve or unresolve [resolvable threads](../../../discussions/index.md#resolvable-comments-and-threads).
+Review comments can also resolve or unresolve [resolvable threads](../../../discussions/index.md#resolve-a-thread)).
When replying to a comment, a checkbox is displayed to resolve or unresolve
the thread after publication.
diff --git a/lib/gitlab/metrics/exporter/base_exporter.rb b/lib/gitlab/metrics/exporter/base_exporter.rb
index 7111835c85a..ff8b8bf2237 100644
--- a/lib/gitlab/metrics/exporter/base_exporter.rb
+++ b/lib/gitlab/metrics/exporter/base_exporter.rb
@@ -1,5 +1,8 @@
# frozen_string_literal: true
+require 'webrick'
+require 'prometheus/client/rack/exporter'
+
module Gitlab
module Metrics
module Exporter
diff --git a/lib/gitlab/metrics/exporter/sidekiq_exporter.rb b/lib/gitlab/metrics/exporter/sidekiq_exporter.rb
index 36262b09b2d..4d38d9e67bf 100644
--- a/lib/gitlab/metrics/exporter/sidekiq_exporter.rb
+++ b/lib/gitlab/metrics/exporter/sidekiq_exporter.rb
@@ -1,8 +1,5 @@
# frozen_string_literal: true
-require 'webrick'
-require 'prometheus/client/rack/exporter'
-
module Gitlab
module Metrics
module Exporter
diff --git a/lib/gitlab/metrics/exporter/web_exporter.rb b/lib/gitlab/metrics/exporter/web_exporter.rb
index 756e6b0641a..f378577f08e 100644
--- a/lib/gitlab/metrics/exporter/web_exporter.rb
+++ b/lib/gitlab/metrics/exporter/web_exporter.rb
@@ -1,8 +1,5 @@
# frozen_string_literal: true
-require 'webrick'
-require 'prometheus/client/rack/exporter'
-
module Gitlab
module Metrics
module Exporter
diff --git a/lib/gitlab/metrics/prometheus.rb b/lib/gitlab/metrics/prometheus.rb
index 757762499a9..848b73792cb 100644
--- a/lib/gitlab/metrics/prometheus.rb
+++ b/lib/gitlab/metrics/prometheus.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require 'prometheus/client'
-
module Gitlab
module Metrics
module Prometheus
diff --git a/lib/gitlab/pagination/offset_pagination.rb b/lib/gitlab/pagination/offset_pagination.rb
index 2805b12d95d..4f8a6ffb2cc 100644
--- a/lib/gitlab/pagination/offset_pagination.rb
+++ b/lib/gitlab/pagination/offset_pagination.rb
@@ -19,11 +19,10 @@ module Gitlab
private
def paginate_with_limit_optimization(relation)
- # do not paginate relation if it is already paginated
- pagination_data = if relation.respond_to?(:current_page) && relation.current_page == params[:page] && relation.limit_value == params[:per_page]
- relation
- else
+ pagination_data = if needs_pagination?(relation)
relation.page(params[:page]).per(params[:per_page])
+ else
+ relation
end
return pagination_data unless pagination_data.is_a?(ActiveRecord::Relation)
@@ -39,6 +38,14 @@ module Gitlab
end
end
+ def needs_pagination?(relation)
+ return true unless relation.respond_to?(:current_page)
+ return true if params[:page].present? && relation.current_page != params[:page].to_i
+ return true if params[:per_page].present? && relation.limit_value != params[:per_page].to_i
+
+ false
+ end
+
def add_default_order(relation)
if relation.is_a?(ActiveRecord::Relation) && relation.order_values.empty?
relation = relation.order(:id) # rubocop: disable CodeReuse/ActiveRecord
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c41ed976001..1b9662da035 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -12979,9 +12979,6 @@ msgstr ""
msgid "Events"
msgstr ""
-msgid "Events in %{project_path}"
-msgstr ""
-
msgid "Every %{action} attempt has failed: %{job_error_message}. Please try again."
msgstr ""
@@ -15253,9 +15250,6 @@ msgstr ""
msgid "Group %{group_name} was successfully created."
msgstr ""
-msgid "Group Audit Events"
-msgstr ""
-
msgid "Group Git LFS status:"
msgstr ""
@@ -15286,6 +15280,9 @@ msgstr ""
msgid "Group applications"
msgstr ""
+msgid "Group audit events"
+msgstr ""
+
msgid "Group avatar"
msgstr ""
@@ -15394,9 +15391,6 @@ msgstr ""
msgid "Group was successfully updated."
msgstr ""
-msgid "Group-level events in %{group_name} (no project-level events)"
-msgstr ""
-
msgid "Group: %{group_name}"
msgstr ""
@@ -17377,6 +17371,9 @@ msgstr ""
msgid "Instance administrators group already exists"
msgstr ""
+msgid "Instance audit events"
+msgstr ""
+
msgid "Instance overview"
msgstr ""
@@ -25208,9 +25205,6 @@ msgstr ""
msgid "Project Access Tokens"
msgstr ""
-msgid "Project Audit Events"
-msgstr ""
-
msgid "Project Badges"
msgstr ""
@@ -25238,6 +25232,9 @@ msgstr ""
msgid "Project and wiki repositories"
msgstr ""
+msgid "Project audit events"
+msgstr ""
+
msgid "Project avatar"
msgstr ""
@@ -34189,6 +34186,15 @@ msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr ""
+msgid "Track important events in your GitLab instance."
+msgstr ""
+
+msgid "Track important events in your group."
+msgstr ""
+
+msgid "Track important events in your project."
+msgstr ""
+
msgid "Track time with quick actions"
msgstr ""
@@ -36550,6 +36556,15 @@ msgstr ""
msgid "Welcome, %{name}!"
msgstr ""
+msgid "What are group audit events?"
+msgstr ""
+
+msgid "What are instance audit events?"
+msgstr ""
+
+msgid "What are project audit events?"
+msgstr ""
+
msgid "What are you searching for?"
msgstr ""
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index c650d145bef..16bb33e95c8 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -483,7 +483,7 @@ RSpec.describe Projects::CommitController do
end
context 'when rendering a JSON format' do
- it 'responds with serialized pipelines' do
+ it 'responds with serialized pipelines', :aggregate_failures do
get_pipelines(id: commit.id, format: :json)
expect(response).to be_ok
@@ -491,6 +491,26 @@ RSpec.describe Projects::CommitController do
expect(json_response['count']['all']).to eq 1
expect(response).to include_pagination_headers
end
+
+ context 'with pagination' do
+ let!(:extra_pipeline) { create(:ci_pipeline, project: project, ref: project.default_branch, sha: commit.sha, status: :running) }
+
+ it 'paginates the result when ref is blank' do
+ allow(Ci::Pipeline).to receive(:default_per_page).and_return(1)
+
+ get_pipelines(id: commit.id, format: :json)
+
+ expect(json_response['pipelines'].count).to eq(1)
+ end
+
+ it 'paginates the result when ref is present' do
+ allow(Ci::Pipeline).to receive(:default_per_page).and_return(1)
+
+ get_pipelines(id: commit.id, ref: project.default_branch, format: :json)
+
+ expect(json_response['pipelines'].count).to eq(1)
+ end
+ end
end
end
end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 3385505bb62..922ecb6052a 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -1016,10 +1016,13 @@ RSpec.describe Projects::IssuesController do
let(:spammy_title) { 'Whatever' }
let!(:spam_logs) { create_list(:spam_log, 2, user: user, title: spammy_title) }
+ before do
+ request.headers['X-GitLab-Captcha-Response'] = 'a-valid-captcha-response'
+ request.headers['X-GitLab-Spam-Log-Id'] = spam_logs.last.id
+ end
+
def update_verified_issue
- update_issue(
- issue_params: { title: spammy_title },
- additional_params: { spam_log_id: spam_logs.last.id, 'g-recaptcha-response': true })
+ update_issue(issue_params: { title: spammy_title })
end
it 'returns 200 status' do
@@ -1036,8 +1039,9 @@ RSpec.describe Projects::IssuesController do
it 'does not mark spam log as recaptcha_verified when it does not belong to current_user' do
spam_log = create(:spam_log)
+ request.headers['X-GitLab-Spam-Log-Id'] = spam_log.id
- expect { update_issue(issue_params: { spam_log_id: spam_log.id, 'g-recaptcha-response': true }) }
+ expect { update_issue }
.not_to change { SpamLog.last.recaptcha_verified }
end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index d4c52e1c7ca..7b5a58fe2e5 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -860,6 +860,20 @@ RSpec.describe Projects::MergeRequestsController do
end
end
end
+
+ context 'with pagination' do
+ before do
+ create(:ci_pipeline, project: merge_request.source_project, ref: merge_request.source_branch, sha: merge_request.diff_head_sha)
+ end
+
+ it 'paginates the result' do
+ allow(Ci::Pipeline).to receive(:default_per_page).and_return(1)
+
+ get :pipelines, params: { namespace_id: project.namespace.to_param, project_id: project, id: merge_request.iid }, format: :json
+
+ expect(json_response['pipelines'].count).to eq(1)
+ end
+ end
end
describe 'GET context commits' do
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index c3e29f45ac9..2379ff9fd98 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -66,6 +66,14 @@ RSpec.describe Projects::PipelinesController do
expect(json_response['pipelines'][0]).not_to include('coverage')
end
+ it 'paginates the result' do
+ allow(Ci::Pipeline).to receive(:default_per_page).and_return(2)
+
+ get_pipelines_index_json
+
+ check_pipeline_response(returned: 2, all: 6)
+ end
+
context 'when performing gitaly calls', :request_store do
it 'limits the Gitaly requests' do
# Isolate from test preparation (Repository#exists? is also cached in RequestStore)
diff --git a/spec/features/groups/import_export/connect_instance_spec.rb b/spec/features/groups/import_export/connect_instance_spec.rb
index 4ed2137f7ae..cf893e444c4 100644
--- a/spec/features/groups/import_export/connect_instance_spec.rb
+++ b/spec/features/groups/import_export/connect_instance_spec.rb
@@ -59,6 +59,8 @@ RSpec.describe 'Import/Export - Connect to another instance', :js do
expect(page).to have_content 'Showing 1-1 of %{total} groups from %{url}' % { url: source_url, total: total }
expect(page).to have_content stub_path
+ visit '/'
+
wait_for_all_requests
end
end
diff --git a/spec/frontend/captcha/captcha_modal_axios_interceptor_spec.js b/spec/frontend/captcha/captcha_modal_axios_interceptor_spec.js
index df81b78d010..553ca52f9ce 100644
--- a/spec/frontend/captcha/captcha_modal_axios_interceptor_spec.js
+++ b/spec/frontend/captcha/captcha_modal_axios_interceptor_spec.js
@@ -1,6 +1,7 @@
import MockAdapter from 'axios-mock-adapter';
import { registerCaptchaModalInterceptor } from '~/captcha/captcha_modal_axios_interceptor';
+import UnsolvedCaptchaError from '~/captcha/unsolved_captcha_error';
import { waitForCaptchaToBeSolved } from '~/captcha/wait_for_captcha_to_be_solved';
import axios from '~/lib/utils/axios_utils';
import httpStatusCodes from '~/lib/utils/http_status';
@@ -25,22 +26,24 @@ describe('registerCaptchaModalInterceptor', () => {
let mock;
beforeEach(() => {
+ waitForCaptchaToBeSolved.mockRejectedValue(new UnsolvedCaptchaError());
+
mock = new MockAdapter(axios);
- mock.onAny('/no-captcha').reply(200, AXIOS_RESPONSE);
- mock.onAny('/error').reply(404, AXIOS_RESPONSE);
- mock.onAny('/captcha').reply((config) => {
+ mock.onAny('/endpoint-without-captcha').reply(200, AXIOS_RESPONSE);
+ mock.onAny('/endpoint-with-unrelated-error').reply(404, AXIOS_RESPONSE);
+ mock.onAny('/endpoint-with-captcha').reply((config) => {
if (!supportedMethods.includes(config.method)) {
return [httpStatusCodes.METHOD_NOT_ALLOWED, { method: config.method }];
}
- try {
- const { captcha_response, spam_log_id, ...rest } = JSON.parse(config.data);
- // eslint-disable-next-line babel/camelcase
- if (captcha_response === CAPTCHA_RESPONSE && spam_log_id === SPAM_LOG_ID) {
- return [httpStatusCodes.OK, { ...rest, method: config.method, CAPTCHA_SUCCESS }];
- }
- } catch (e) {
- return [httpStatusCodes.BAD_REQUEST, { method: config.method }];
+ const data = JSON.parse(config.data);
+ const {
+ 'X-GitLab-Captcha-Response': captchaResponse,
+ 'X-GitLab-Spam-Log-Id': spamLogId,
+ } = config.headers;
+
+ if (captchaResponse === CAPTCHA_RESPONSE && spamLogId === SPAM_LOG_ID) {
+ return [httpStatusCodes.OK, { ...data, method: config.method, CAPTCHA_SUCCESS }];
}
return [httpStatusCodes.CONFLICT, NEEDS_CAPTCHA_RESPONSE];
@@ -56,7 +59,7 @@ describe('registerCaptchaModalInterceptor', () => {
describe.each([...supportedMethods, ...unsupportedMethods])('For HTTP method %s', (method) => {
it('successful requests are passed through', async () => {
- const { data, status } = await axios[method]('/no-captcha');
+ const { data, status } = await axios[method]('/endpoint-without-captcha');
expect(status).toEqual(httpStatusCodes.OK);
expect(data).toEqual(AXIOS_RESPONSE);
@@ -64,7 +67,7 @@ describe('registerCaptchaModalInterceptor', () => {
});
it('error requests without needs_captcha_response_errors are passed through', async () => {
- await expect(() => axios[method]('/error')).rejects.toThrow(
+ await expect(() => axios[method]('/endpoint-with-unrelated-error')).rejects.toThrow(
expect.objectContaining({
response: expect.objectContaining({
status: httpStatusCodes.NOT_FOUND,
@@ -79,21 +82,35 @@ describe('registerCaptchaModalInterceptor', () => {
describe.each(supportedMethods)('For HTTP method %s', (method) => {
describe('error requests with needs_captcha_response_errors', () => {
const submittedData = { ID: 12345 };
+ const submittedHeaders = { 'Submitted-Header': 67890 };
it('re-submits request if captcha was solved correctly', async () => {
- waitForCaptchaToBeSolved.mockResolvedValue(CAPTCHA_RESPONSE);
- const { data: returnedData } = await axios[method]('/captcha', submittedData);
+ waitForCaptchaToBeSolved.mockResolvedValueOnce(CAPTCHA_RESPONSE);
+ const axiosResponse = await axios[method]('/endpoint-with-captcha', submittedData, {
+ headers: submittedHeaders,
+ });
+ const {
+ data: returnedData,
+ config: { headers: returnedHeaders },
+ } = axiosResponse;
expect(waitForCaptchaToBeSolved).toHaveBeenCalledWith(CAPTCHA_SITE_KEY);
expect(returnedData).toEqual({ ...submittedData, CAPTCHA_SUCCESS, method });
+ expect(returnedHeaders).toEqual(
+ expect.objectContaining({
+ ...submittedHeaders,
+ 'X-GitLab-Captcha-Response': CAPTCHA_RESPONSE,
+ 'X-GitLab-Spam-Log-Id': SPAM_LOG_ID,
+ }),
+ );
expect(mock.history[method]).toHaveLength(2);
});
it('does not re-submit request if captcha was not solved', async () => {
- const error = new Error('Captcha not solved');
- waitForCaptchaToBeSolved.mockRejectedValue(error);
- await expect(() => axios[method]('/captcha', submittedData)).rejects.toThrow(error);
+ await expect(() => axios[method]('/endpoint-with-captcha', submittedData)).rejects.toThrow(
+ new UnsolvedCaptchaError(),
+ );
expect(waitForCaptchaToBeSolved).toHaveBeenCalledWith(CAPTCHA_SITE_KEY);
expect(mock.history[method]).toHaveLength(1);
@@ -103,7 +120,7 @@ describe('registerCaptchaModalInterceptor', () => {
describe.each(unsupportedMethods)('For HTTP method %s', (method) => {
it('ignores captcha response', async () => {
- await expect(() => axios[method]('/captcha')).rejects.toThrow(
+ await expect(() => axios[method]('/endpoint-with-captcha')).rejects.toThrow(
expect.objectContaining({
response: expect.objectContaining({
status: httpStatusCodes.METHOD_NOT_ALLOWED,
diff --git a/spec/lib/gitlab/pagination/offset_pagination_spec.rb b/spec/lib/gitlab/pagination/offset_pagination_spec.rb
index c9a23170137..f8d50fbc517 100644
--- a/spec/lib/gitlab/pagination/offset_pagination_spec.rb
+++ b/spec/lib/gitlab/pagination/offset_pagination_spec.rb
@@ -130,6 +130,80 @@ RSpec.describe Gitlab::Pagination::OffsetPagination do
end
end
+ context 'when resource already paginated' do
+ let(:resource) { Project.all.page(1).per(1) }
+
+ context 'when per_page param is specified' do
+ let(:query) { base_query.merge(page: 1, per_page: 2) }
+
+ it 'returns appropriate amount of resources based on per_page param' do
+ expect(subject.paginate(resource).count).to eq 2
+ end
+ end
+
+ context 'when page and per page params are strings' do
+ let(:query) { base_query.merge(page: '1', per_page: '1') }
+
+ it 'returns appropriate amount of resources' do
+ expect(subject.paginate(resource).count).to eq 1
+ end
+ end
+
+ context 'when per_page param is blank' do
+ let(:query) { base_query.merge(page: 1) }
+
+ it 'returns appropriate amount of resources' do
+ expect(subject.paginate(resource).count).to eq 1
+ end
+ end
+
+ context 'when page param is blank' do
+ let(:query) { base_query }
+
+ it 'returns appropriate amount of resources based on resource per(N)' do
+ expect(subject.paginate(resource).count).to eq 1
+ end
+ end
+ end
+
+ context 'when resource does not respond to limit_value' do
+ let(:custom_collection) do
+ Class.new do
+ include Enumerable
+
+ def initialize(items)
+ @collection = items
+ end
+
+ def each
+ @collection.each { |item| yield item }
+ end
+
+ def page(number)
+ Kaminari.paginate_array(@collection).page(number)
+ end
+ end
+ end
+
+ let(:resource) { custom_collection.new(Project.all).page(query[:page]) }
+
+ context 'when page param is blank' do
+ let(:query) { base_query }
+
+ it 'returns appropriate amount of resources' do
+ expect(subject.paginate(resource).count).to eq 3
+ end
+ end
+
+ context 'when per_page param is blank' do
+ let(:query) { base_query.merge(page: 1) }
+
+ it 'returns appropriate amount of resources with default per page value' do
+ expect(subject.paginate(resource).count).to eq 3
+ end
+ end
+ end
+
context 'when resource is a paginatable array' do
let(:resource) { Kaminari.paginate_array(Project.all.to_a) }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index ee040644394..cb9ce851137 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -11,6 +11,10 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
let_it_be(:namespace) { create_default(:namespace).freeze }
let_it_be(:project) { create_default(:project, :repository).freeze }
+ it 'paginates 15 pipeleines per page' do
+ expect(described_class.default_per_page).to eq(15)
+ end
+
it_behaves_like 'having unique enum values'
it { is_expected.to belong_to(:project) }
diff --git a/spec/models/integrations/base_chat_notification_spec.rb b/spec/models/integrations/base_chat_notification_spec.rb
index 656eaa3bbdd..b5c1b61059d 100644
--- a/spec/models/integrations/base_chat_notification_spec.rb
+++ b/spec/models/integrations/base_chat_notification_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe Integrations::BaseChatNotification do
end
describe '#execute' do
- subject(:chat_service) { described_class.new }
+ subject(:chat_integration) { described_class.new }
let_it_be(:project) { create(:project, :repository) }
@@ -43,7 +43,7 @@ RSpec.describe Integrations::BaseChatNotification do
let(:data) { Gitlab::DataBuilder::Push.build_sample(subject.project, user) }
before do
- allow(chat_service).to receive_messages(
+ allow(chat_integration).to receive_messages(
project: project,
project_id: project.id,
service_hook: true,
@@ -57,8 +57,8 @@ RSpec.describe Integrations::BaseChatNotification do
context 'with a repository' do
it 'returns true' do
- expect(chat_service).to receive(:notify).and_return(true)
- expect(chat_service.execute(data)).to be true
+ expect(chat_integration).to receive(:notify).and_return(true)
+ expect(chat_integration.execute(data)).to be true
end
end
@@ -66,8 +66,8 @@ RSpec.describe Integrations::BaseChatNotification do
it 'returns true' do
subject.project = create(:project, :empty_repo)
- expect(chat_service).to receive(:notify).and_return(true)
- expect(chat_service.execute(data)).to be true
+ expect(chat_integration).to receive(:notify).and_return(true)
+ expect(chat_integration.execute(data)).to be true
end
end
@@ -75,8 +75,8 @@ RSpec.describe Integrations::BaseChatNotification do
it 'does not remove spaces' do
allow(project).to receive(:full_name).and_return('Project Name')
- expect(chat_service).to receive(:get_message).with(any_args, hash_including(project_name: 'Project Name'))
- chat_service.execute(data)
+ expect(chat_integration).to receive(:get_message).with(any_args, hash_including(project_name: 'Project Name'))
+ chat_integration.execute(data)
end
end
@@ -89,76 +89,76 @@ RSpec.describe Integrations::BaseChatNotification do
let(:data) { Gitlab::DataBuilder::Note.build(note, user) }
- shared_examples 'notifies the chat service' do
+ shared_examples 'notifies the chat integration' do
specify do
- expect(chat_service).to receive(:notify).with(any_args)
+ expect(chat_integration).to receive(:notify).with(any_args)
- chat_service.execute(data)
+ chat_integration.execute(data)
end
end
- shared_examples 'does not notify the chat service' do
+ shared_examples 'does not notify the chat integration' do
specify do
- expect(chat_service).not_to receive(:notify).with(any_args)
+ expect(chat_integration).not_to receive(:notify).with(any_args)
- chat_service.execute(data)
+ chat_integration.execute(data)
end
end
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
context 'with label filter' do
- subject(:chat_service) { described_class.new(labels_to_be_notified: '~Bug') }
+ subject(:chat_integration) { described_class.new(labels_to_be_notified: '~Bug') }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
context 'MergeRequest events' do
let(:data) { create(:merge_request, labels: [label]).to_hook_data(user) }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
context 'Issue events' do
let(:data) { issue.to_hook_data(user) }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
end
context 'when labels_to_be_notified_behavior is not defined' do
- subject(:chat_service) { described_class.new(labels_to_be_notified: label_filter) }
+ subject(:chat_integration) { described_class.new(labels_to_be_notified: label_filter) }
context 'no matching labels' do
let(:label_filter) { '~some random label' }
- it_behaves_like 'does not notify the chat service'
+ it_behaves_like 'does not notify the chat integration'
end
context 'only one label matches' do
let(:label_filter) { '~some random label, ~Bug' }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
end
context 'when labels_to_be_notified_behavior is blank' do
- subject(:chat_service) { described_class.new(labels_to_be_notified: label_filter, labels_to_be_notified_behavior: '') }
+ subject(:chat_integration) { described_class.new(labels_to_be_notified: label_filter, labels_to_be_notified_behavior: '') }
context 'no matching labels' do
let(:label_filter) { '~some random label' }
- it_behaves_like 'does not notify the chat service'
+ it_behaves_like 'does not notify the chat integration'
end
context 'only one label matches' do
let(:label_filter) { '~some random label, ~Bug' }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
end
context 'when labels_to_be_notified_behavior is match_any' do
- subject(:chat_service) do
+ subject(:chat_integration) do
described_class.new(
labels_to_be_notified: label_filter,
labels_to_be_notified_behavior: 'match_any'
@@ -168,24 +168,24 @@ RSpec.describe Integrations::BaseChatNotification do
context 'no label filter' do
let(:label_filter) { nil }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
context 'no matching labels' do
let(:label_filter) { '~some random label' }
- it_behaves_like 'does not notify the chat service'
+ it_behaves_like 'does not notify the chat integration'
end
context 'only one label matches' do
let(:label_filter) { '~some random label, ~Bug' }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
end
context 'when labels_to_be_notified_behavior is match_all' do
- subject(:chat_service) do
+ subject(:chat_integration) do
described_class.new(
labels_to_be_notified: label_filter,
labels_to_be_notified_behavior: 'match_all'
@@ -195,31 +195,31 @@ RSpec.describe Integrations::BaseChatNotification do
context 'no label filter' do
let(:label_filter) { nil }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
context 'no matching labels' do
let(:label_filter) { '~some random label' }
- it_behaves_like 'does not notify the chat service'
+ it_behaves_like 'does not notify the chat integration'
end
context 'only one label matches' do
let(:label_filter) { '~some random label, ~Bug' }
- it_behaves_like 'does not notify the chat service'
+ it_behaves_like 'does not notify the chat integration'
end
context 'labels matches exactly' do
let(:label_filter) { '~Bug, ~Backend, ~Community contribution' }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
context 'labels matches but object has more' do
let(:label_filter) { '~Bug, ~Backend' }
- it_behaves_like 'notifies the chat service'
+ it_behaves_like 'notifies the chat integration'
end
context 'labels are distributed on multiple objects' do
@@ -241,22 +241,22 @@ RSpec.describe Integrations::BaseChatNotification do
})
end
- it_behaves_like 'does not notify the chat service'
+ it_behaves_like 'does not notify the chat integration'
end
end
end
context 'with "channel" property' do
before do
- allow(chat_service).to receive(:channel).and_return(channel)
+ allow(chat_integration).to receive(:channel).and_return(channel)
end
context 'empty string' do
let(:channel) { '' }
it 'does not include the channel' do
- expect(chat_service).to receive(:notify).with(any_args, hash_excluding(:channel)).and_return(true)
- expect(chat_service.execute(data)).to be(true)
+ expect(chat_integration).to receive(:notify).with(any_args, hash_excluding(:channel)).and_return(true)
+ expect(chat_integration.execute(data)).to be(true)
end
end
@@ -264,20 +264,20 @@ RSpec.describe Integrations::BaseChatNotification do
let(:channel) { ' ' }
it 'does not include the channel' do
- expect(chat_service).to receive(:notify).with(any_args, hash_excluding(:channel)).and_return(true)
- expect(chat_service.execute(data)).to be(true)
+ expect(chat_integration).to receive(:notify).with(any_args, hash_excluding(:channel)).and_return(true)
+ expect(chat_integration.execute(data)).to be(true)
end
end
end
shared_examples 'with channel specified' do |channel, expected_channels|
before do
- allow(chat_service).to receive(:push_channel).and_return(channel)
+ allow(chat_integration).to receive(:push_channel).and_return(channel)
end
it 'notifies all channels' do
- expect(chat_service).to receive(:notify).with(any_args, hash_including(channel: expected_channels)).and_return(true)
- expect(chat_service.execute(data)).to be(true)
+ expect(chat_integration).to receive(:notify).with(any_args, hash_including(channel: expected_channels)).and_return(true)
+ expect(chat_integration.execute(data)).to be(true)
end
end