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--.gitlab/ci/rules.gitlab-ci.yml2
-rw-r--r--app/assets/javascripts/environments/mixins/environments_pagination_api_mixin.js2
-rw-r--r--app/controllers/application_controller.rb12
-rw-r--r--app/controllers/graphql_controller.rb14
-rw-r--r--app/models/iterations/cadence.rb8
-rw-r--r--app/services/merge_requests/base_service.rb2
-rw-r--r--app/services/merge_requests/update_service.rb11
-rw-r--r--changelogs/unreleased/292824-track-mr-lock-changes.yml5
-rw-r--r--changelogs/unreleased/expose_project_container_registry_url.yml5
-rw-r--r--changelogs/unreleased/qmnguyen0711-846-sentry-merge-sentry-s-contexts-and-users-into-applicatio.yml5
-rw-r--r--changelogs/unreleased/show-nested-env.yml5
-rw-r--r--config/feature_flags/development/usage_data_i_code_review_user_mr_discussion_locked.yml8
-rw-r--r--config/feature_flags/development/usage_data_i_code_review_user_mr_discussion_unlocked.yml8
-rw-r--r--config/metrics/counts_28d/20210301103859_i_code_review_user_mr_discussion_locked_monthly.yml20
-rw-r--r--config/metrics/counts_28d/20210301103925_i_code_review_user_mr_discussion_unlocked_monthly.yml20
-rw-r--r--config/metrics/counts_7d/20210302105258_i_code_review_user_mr_discussion_unlocked_weekly.yml20
-rw-r--r--config/metrics/counts_7d/20210302105318_i_code_review_user_mr_discussion_locked_weekly.yml20
-rw-r--r--doc/administration/pages/index.md13
-rw-r--r--doc/administration/pseudonymizer.md4
-rw-r--r--doc/administration/terraform_state.md2
-rw-r--r--doc/administration/uploads.md2
-rw-r--r--doc/api/projects.md13
-rw-r--r--doc/development/usage_ping/dictionary.md80
-rw-r--r--lib/api/entities/project.rb2
-rw-r--r--lib/api/helpers.rb2
-rw-r--r--lib/gitlab/application_context.rb6
-rw-r--r--lib/gitlab/error_tracking.rb60
-rw-r--r--lib/gitlab/error_tracking/context_payload_generator.rb66
-rw-r--r--lib/gitlab/error_tracking/log_formatter.rb55
-rw-r--r--lib/gitlab/error_tracking/processor/context_payload_processor.rb18
-rw-r--r--lib/gitlab/exception_log_formatter.rb10
-rw-r--r--lib/gitlab/usage_data_counters/aggregated_metrics/code_review.yml8
-rw-r--r--lib/gitlab/usage_data_counters/known_events/code_review_events.yml10
-rw-r--r--lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb10
-rw-r--r--locale/gitlab.pot3
-rw-r--r--package.json2
-rwxr-xr-xscripts/review_apps/review-apps.sh2
-rw-r--r--spec/frontend/environments/environments_app_spec.js12
-rw-r--r--spec/frontend/environments/folder/environments_folder_view_spec.js14
-rw-r--r--spec/lib/gitlab/error_tracking/context_payload_generator_spec.rb176
-rw-r--r--spec/lib/gitlab/error_tracking/log_formatter_spec.rb71
-rw-r--r--spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb45
-rw-r--r--spec/lib/gitlab/error_tracking_spec.rb219
-rw-r--r--spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb16
-rw-r--r--spec/models/upload_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/merge_requests_spec.rb137
-rw-r--r--spec/requests/api/helpers_spec.rb20
-rw-r--r--spec/requests/api/project_attributes.yml1
-rw-r--r--spec/requests/api/projects_spec.rb11
-rw-r--r--spec/services/merge_requests/update_service_spec.rb54
-rw-r--r--yarn.lock8
51 files changed, 1001 insertions, 320 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index ef66c8aeb2e..16f8783c9a0 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -446,6 +446,8 @@
.frontend:rules:bundle-size-review:
rules:
+ - <<: *if-not-canonical-namespace
+ when: never
- if: '$DANGER_GITLAB_API_TOKEN && $CI_MERGE_REQUEST_IID && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main")'
changes: *frontend-patterns
allow_failure: true
diff --git a/app/assets/javascripts/environments/mixins/environments_pagination_api_mixin.js b/app/assets/javascripts/environments/mixins/environments_pagination_api_mixin.js
index b62fe196a6f..a76c8e445ed 100644
--- a/app/assets/javascripts/environments/mixins/environments_pagination_api_mixin.js
+++ b/app/assets/javascripts/environments/mixins/environments_pagination_api_mixin.js
@@ -16,6 +16,7 @@ export default {
let params = {
scope,
page: '1',
+ nested: true,
};
params = this.onChangeWithFilter(params);
@@ -27,6 +28,7 @@ export default {
/* URLS parameters are strings, we need to parse to match types */
let params = {
page: Number(page).toString(),
+ nested: true,
};
if (this.scope) {
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 5f14d95ffed..379da90827a 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -29,7 +29,6 @@ class ApplicationController < ActionController::Base
before_action :validate_user_service_ticket!
before_action :check_password_expiration, if: :html_request?
before_action :ldap_security_check
- around_action :sentry_context
before_action :default_headers
before_action :default_cache_headers
before_action :add_gon_variables, if: :html_request?
@@ -171,7 +170,12 @@ class ApplicationController < ActionController::Base
end
def log_exception(exception)
- Gitlab::ErrorTracking.track_exception(exception)
+ # At this point, the controller already exits set_current_context around
+ # block. To maintain the context while handling error exception, we need to
+ # set the context again
+ set_current_context do
+ Gitlab::ErrorTracking.track_exception(exception)
+ end
backtrace_cleaner = request.env["action_dispatch.backtrace_cleaner"]
application_trace = ActionDispatch::ExceptionWrapper.new(backtrace_cleaner, exception).application_trace
@@ -528,10 +532,6 @@ class ApplicationController < ActionController::Base
.execute
end
- def sentry_context(&block)
- Gitlab::ErrorTracking.with_context(current_user, &block)
- end
-
def allow_gitaly_ref_name_caching
::Gitlab::GitalyClient.allow_ref_name_caching do
yield
diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb
index 152f07b4c16..53064041ab8 100644
--- a/app/controllers/graphql_controller.rb
+++ b/app/controllers/graphql_controller.rb
@@ -4,6 +4,8 @@ class GraphqlController < ApplicationController
# Unauthenticated users have access to the API for public data
skip_before_action :authenticate_user!
+ WHITELIST_HEADER = 'HTTP_X_GITLAB_QUERY_WHITELIST_ISSUE'
+
# If a user is using their session to access GraphQL, we need to have session
# storage, since the admin-mode check is session wide.
# We can't enable this for anonymous users because that would cause users using
@@ -21,6 +23,7 @@ class GraphqlController < ApplicationController
before_action(only: [:execute]) { authenticate_sessionless_user!(:api) }
before_action :set_user_last_activity
before_action :track_vs_code_usage
+ before_action :whitelist_query!
# Since we deactivate authentication from the main ApplicationController and
# defer it to :authorize_access_api!, we need to override the bypass session
@@ -59,6 +62,14 @@ class GraphqlController < ApplicationController
private
+ # Tests may mark some queries as exempt from query limits
+ def whitelist_query!
+ whitelist_issue = request.headers[WHITELIST_HEADER]
+ return unless whitelist_issue
+
+ Gitlab::QueryLimiting.whitelist(whitelist_issue)
+ end
+
def set_user_last_activity
return unless current_user
@@ -66,7 +77,8 @@ class GraphqlController < ApplicationController
end
def track_vs_code_usage
- Gitlab::UsageDataCounters::VSCodeExtensionActivityUniqueCounter.track_api_request_when_trackable(user_agent: request.user_agent, user: current_user)
+ Gitlab::UsageDataCounters::VSCodeExtensionActivityUniqueCounter
+ .track_api_request_when_trackable(user_agent: request.user_agent, user: current_user)
end
def execute_multiplex
diff --git a/app/models/iterations/cadence.rb b/app/models/iterations/cadence.rb
deleted file mode 100644
index 7945fb1785a..00000000000
--- a/app/models/iterations/cadence.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-# Placeholder class for model that is implemented in EE
-class Iterations::Cadence < ApplicationRecord
- self.table_name = 'iterations_cadences'
-end
-
-Iterations::Cadence.prepend_if_ee('::EE::Iterations::Cadence')
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 6bd31e26748..317cd11a69d 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -181,7 +181,7 @@ module MergeRequests
}
if exception
- Gitlab::ErrorTracking.with_context(current_user) do
+ Gitlab::ApplicationContext.with_context(user: current_user) do
Gitlab::ErrorTracking.track_exception(exception, data)
end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 38df3ee0e43..217679e733c 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -47,6 +47,7 @@ module MergeRequests
handle_draft_status_change(merge_request, changed_fields)
track_title_and_desc_edits(changed_fields)
+ track_discussion_lock_toggle(merge_request, changed_fields)
notify_if_labels_added(merge_request, old_labels)
notify_if_mentions_added(merge_request, old_mentioned_users)
@@ -95,6 +96,16 @@ module MergeRequests
end
end
+ def track_discussion_lock_toggle(merge_request, changed_fields)
+ return unless changed_fields.include?('discussion_locked')
+
+ if merge_request.discussion_locked
+ merge_request_activity_counter.track_discussion_locked_action(user: current_user)
+ else
+ merge_request_activity_counter.track_discussion_unlocked_action(user: current_user)
+ end
+ end
+
def notify_if_labels_added(merge_request, old_labels)
added_labels = merge_request.labels - old_labels
diff --git a/changelogs/unreleased/292824-track-mr-lock-changes.yml b/changelogs/unreleased/292824-track-mr-lock-changes.yml
new file mode 100644
index 00000000000..89102528aed
--- /dev/null
+++ b/changelogs/unreleased/292824-track-mr-lock-changes.yml
@@ -0,0 +1,5 @@
+---
+title: Track usage pings when MR gets locked/unlocked
+merge_request: 55069
+author:
+type: other
diff --git a/changelogs/unreleased/expose_project_container_registry_url.yml b/changelogs/unreleased/expose_project_container_registry_url.yml
new file mode 100644
index 00000000000..923d14ed5a6
--- /dev/null
+++ b/changelogs/unreleased/expose_project_container_registry_url.yml
@@ -0,0 +1,5 @@
+---
+title: Expose container_registry_image_prefix to project API
+merge_request: 54090
+author: Mathieu Parent
+type: added
diff --git a/changelogs/unreleased/qmnguyen0711-846-sentry-merge-sentry-s-contexts-and-users-into-applicatio.yml b/changelogs/unreleased/qmnguyen0711-846-sentry-merge-sentry-s-contexts-and-users-into-applicatio.yml
new file mode 100644
index 00000000000..bd4a97d264f
--- /dev/null
+++ b/changelogs/unreleased/qmnguyen0711-846-sentry-merge-sentry-s-contexts-and-users-into-applicatio.yml
@@ -0,0 +1,5 @@
+---
+title: Merge Sentry's contexts into Gitlab::ApplicationContext
+merge_request: 53691
+author:
+type: changed
diff --git a/changelogs/unreleased/show-nested-env.yml b/changelogs/unreleased/show-nested-env.yml
new file mode 100644
index 00000000000..7305cf17975
--- /dev/null
+++ b/changelogs/unreleased/show-nested-env.yml
@@ -0,0 +1,5 @@
+---
+title: Show nested environments when change tab, page
+merge_request: 55167
+author:
+type: changed
diff --git a/config/feature_flags/development/usage_data_i_code_review_user_mr_discussion_locked.yml b/config/feature_flags/development/usage_data_i_code_review_user_mr_discussion_locked.yml
new file mode 100644
index 00000000000..e6e81f48028
--- /dev/null
+++ b/config/feature_flags/development/usage_data_i_code_review_user_mr_discussion_locked.yml
@@ -0,0 +1,8 @@
+---
+name: usage_data_i_code_review_user_mr_discussion_locked
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55069
+rollout_issue_url:
+milestone: '13.10'
+type: development
+group: group::code review
+default_enabled: true
diff --git a/config/feature_flags/development/usage_data_i_code_review_user_mr_discussion_unlocked.yml b/config/feature_flags/development/usage_data_i_code_review_user_mr_discussion_unlocked.yml
new file mode 100644
index 00000000000..03ec6cde34b
--- /dev/null
+++ b/config/feature_flags/development/usage_data_i_code_review_user_mr_discussion_unlocked.yml
@@ -0,0 +1,8 @@
+---
+name: usage_data_i_code_review_user_mr_discussion_unlocked
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55069
+rollout_issue_url:
+milestone: '13.10'
+type: development
+group: group::code review
+default_enabled: true
diff --git a/config/metrics/counts_28d/20210301103859_i_code_review_user_mr_discussion_locked_monthly.yml b/config/metrics/counts_28d/20210301103859_i_code_review_user_mr_discussion_locked_monthly.yml
new file mode 100644
index 00000000000..9ad7ff81231
--- /dev/null
+++ b/config/metrics/counts_28d/20210301103859_i_code_review_user_mr_discussion_locked_monthly.yml
@@ -0,0 +1,20 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_user_mr_discussion_locked_monthly
+description: Count of unique users per month who locked a MR
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: "13.10"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55069
+time_frame: 28d
+data_source: redis_hll
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20210301103925_i_code_review_user_mr_discussion_unlocked_monthly.yml b/config/metrics/counts_28d/20210301103925_i_code_review_user_mr_discussion_unlocked_monthly.yml
new file mode 100644
index 00000000000..707a2fc76d1
--- /dev/null
+++ b/config/metrics/counts_28d/20210301103925_i_code_review_user_mr_discussion_unlocked_monthly.yml
@@ -0,0 +1,20 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_user_mr_discussion_unlocked_monthly
+description: Count of unique users per month who unlocked a MR
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: "13.10"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55069
+time_frame: 28d
+data_source: redis_hll
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20210302105258_i_code_review_user_mr_discussion_unlocked_weekly.yml b/config/metrics/counts_7d/20210302105258_i_code_review_user_mr_discussion_unlocked_weekly.yml
new file mode 100644
index 00000000000..80471ed836a
--- /dev/null
+++ b/config/metrics/counts_7d/20210302105258_i_code_review_user_mr_discussion_unlocked_weekly.yml
@@ -0,0 +1,20 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_user_mr_discussion_unlocked_weekly
+description: Count of unique users per week who unlocked a MR
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: "13.10"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55069
+time_frame: 7d
+data_source: redis_hll
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20210302105318_i_code_review_user_mr_discussion_locked_weekly.yml b/config/metrics/counts_7d/20210302105318_i_code_review_user_mr_discussion_locked_weekly.yml
new file mode 100644
index 00000000000..2295fb75a48
--- /dev/null
+++ b/config/metrics/counts_7d/20210302105318_i_code_review_user_mr_discussion_locked_weekly.yml
@@ -0,0 +1,20 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_user_mr_discussion_locked_weekly
+description: Count of unique users per week who locked a MR
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: "13.10"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55069
+time_frame: 7d
+data_source: redis_hll
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index b552c2ba957..40e9d5f6780 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -862,7 +862,7 @@ In installations from source:
remote_directory: "pages" # The bucket name
connection:
provider: AWS # Only AWS supported at the moment
- aws_access_key_id: AWS_ACESS_KEY_ID
+ aws_access_key_id: AWS_ACCESS_KEY_ID
aws_secret_access_key: AWS_SECRET_ACCESS_KEY
region: eu-central-1
```
@@ -969,14 +969,15 @@ to define the explicit address that the GitLab Pages daemon should listen on:
gitlab_pages['listen_proxy'] = '127.0.0.1:8090'
```
-### 404 error after transferring project to a different group or user
+### 404 error after transferring the project to a different group or user, or changing project path
If you encounter a `404 Not Found` error a Pages site after transferring a project to
-another group or user, you must trigger a domain configuration update for Pages. To do
-so, write something in the `.update` file. The Pages daemon monitors for changes to this
-file, and reloads the configuration when changes occur.
+another group or user, or changing project path, you must trigger a domain configuration
+update for Pages. To do so, write something in the `.update` file. The Pages daemon
+monitors for changes to this file, and reloads the configuration when changes occur.
-Use this example to fix a `404 Not Found` error after transferring a project with Pages:
+Use this example to fix a `404 Not Found` error after transferring a project or changing
+a project path with Pages:
```shell
date > /var/opt/gitlab/gitlab-rails/shared/pages/.update
diff --git a/doc/administration/pseudonymizer.md b/doc/administration/pseudonymizer.md
index 5f1272b1f4a..4aa73212e43 100644
--- a/doc/administration/pseudonymizer.md
+++ b/doc/administration/pseudonymizer.md
@@ -24,7 +24,7 @@ be textually exported. This ensures that:
## Configuration
-To configure the pseudonymizer, you need to:
+To configure the Pseudonymizer, you need to:
- Provide a manifest file that describes which fields should be included or
pseudonymized ([example `manifest.yml` file](https://gitlab.com/gitlab-org/gitlab/tree/master/config/pseudonymizer.yml)).
@@ -87,7 +87,7 @@ To configure the pseudonymizer, you need to:
## Usage
-You can optionally run the pseudonymizer using the following environment variables:
+You can optionally run the Pseudonymizer using the following environment variables:
- `PSEUDONYMIZER_OUTPUT_DIR` - where to store the output CSV files (defaults to `/tmp`)
- `PSEUDONYMIZER_BATCH` - the batch size when querying the DB (defaults to `100000`)
diff --git a/doc/administration/terraform_state.md b/doc/administration/terraform_state.md
index 6e5d6b274b6..58a18d49417 100644
--- a/doc/administration/terraform_state.md
+++ b/doc/administration/terraform_state.md
@@ -121,7 +121,7 @@ See [the available connection settings for different providers](object_storage.m
remote_directory: "terraform" # The bucket name
connection:
provider: AWS # Only AWS supported at the moment
- aws_access_key_id: AWS_ACESS_KEY_ID
+ aws_access_key_id: AWS_ACCESS_KEY_ID
aws_secret_access_key: AWS_SECRET_ACCESS_KEY
region: eu-central-1
```
diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md
index d3a9777775f..949687cfa0a 100644
--- a/doc/administration/uploads.md
+++ b/doc/administration/uploads.md
@@ -146,7 +146,7 @@ _The uploads are stored by default in
remote_directory: "uploads" # The bucket name
connection:
provider: AWS # Only AWS supported at the moment
- aws_access_key_id: AWS_ACESS_KEY_ID
+ aws_access_key_id: AWS_ACCESS_KEY_ID
aws_secret_access_key: AWS_SECRET_ACCESS_KEY
region: eu-central-1
```
diff --git a/doc/api/projects.md b/doc/api/projects.md
index b8687314697..da883d376b0 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -179,6 +179,7 @@ When the user is authenticated and `simple` is not set this returns something li
"packages_size": 0,
"snippets_size": 0
},
+ "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-client",
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
@@ -284,6 +285,7 @@ When the user is authenticated and `simple` is not set this returns something li
"packages_size": 0,
"snippets_size": 0
},
+ "container_registry_image_prefix": "registry.example.com/brightbox/puppet",
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
@@ -439,6 +441,7 @@ GET /users/:user_id/projects
"packages_size": 0,
"snippets_size": 0
},
+ "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-client",
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
@@ -544,6 +547,7 @@ GET /users/:user_id/projects
"packages_size": 0,
"snippets_size": 0
},
+ "container_registry_image_prefix": "registry.example.com/brightbox/puppet",
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
@@ -658,6 +662,7 @@ Example response:
"lfs_objects_size": 0,
"job_artifacts_size": 0
},
+ "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-client",
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
@@ -758,6 +763,7 @@ Example response:
"lfs_objects_size": 0,
"job_artifacts_size": 0
},
+ "container_registry_image_prefix": "registry.example.com/brightbox/puppet",
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
@@ -921,6 +927,7 @@ GET /projects/:id
"packages_size": 0,
"snippets_size": 0
},
+ "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-client",
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
@@ -1373,6 +1380,7 @@ Example responses:
"merge_method": "merge",
"autoclose_referenced_issues": true,
"suggestion_commit_message": null,
+ "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
@@ -1467,6 +1475,7 @@ Example response:
"merge_method": "merge",
"autoclose_referenced_issues": true,
"suggestion_commit_message": null,
+ "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
@@ -1559,6 +1568,7 @@ Example response:
"merge_method": "merge",
"autoclose_referenced_issues": true,
"suggestion_commit_message": null,
+ "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
@@ -1745,6 +1755,7 @@ Example response:
"merge_method": "merge",
"autoclose_referenced_issues": true,
"suggestion_commit_message": null,
+ "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
@@ -1858,6 +1869,7 @@ Example response:
"merge_method": "merge",
"autoclose_referenced_issues": true,
"suggestion_commit_message": null,
+ "container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
@@ -2354,6 +2366,7 @@ Example response:
"avatar_url": null,
"web_url": "https://gitlab.example.com/groups/cute-cats"
},
+ "container_registry_image_prefix": "registry.example.com/cute-cats/hello-world",
"_links": {
"self": "https://gitlab.example.com/api/v4/projects/7",
"issues": "https://gitlab.example.com/api/v4/projects/7/issues",
diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md
index 512f31903c8..93ce26fceb5 100644
--- a/doc/development/usage_ping/dictionary.md
+++ b/doc/development/usage_ping/dictionary.md
@@ -13384,6 +13384,86 @@ Count of unique users per week|month who merged a MR
| `tier` | |
| `skip_validation` | true |
+## `redis_hll_counters.code_review.i_code_review_user_mr_discussion_locked_monthly`
+
+Count of unique users per month who locked a MR
+
+| field | value |
+| --- | --- |
+| `key_path` | **`redis_hll_counters.code_review.i_code_review_user_mr_discussion_locked_monthly`** |
+| `product_section` | dev |
+| `product_stage` | create |
+| `product_group` | `group::code review` |
+| `product_category` | `code_review` |
+| `value_type` | number |
+| `status` | implemented |
+| `milestone` | 13.10 |
+| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55069) |
+| `time_frame` | 28d |
+| `data_source` | Redis_hll |
+| `distribution` | ce, ee |
+| `tier` | free, premium, ultimate |
+
+## `redis_hll_counters.code_review.i_code_review_user_mr_discussion_locked_weekly`
+
+Count of unique users per week who locked a MR
+
+| field | value |
+| --- | --- |
+| `key_path` | **`redis_hll_counters.code_review.i_code_review_user_mr_discussion_locked_weekly`** |
+| `product_section` | dev |
+| `product_stage` | create |
+| `product_group` | `group::code review` |
+| `product_category` | `code_review` |
+| `value_type` | number |
+| `status` | implemented |
+| `milestone` | 13.10 |
+| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55069) |
+| `time_frame` | 7d |
+| `data_source` | Redis_hll |
+| `distribution` | ce, ee |
+| `tier` | free, premium, ultimate |
+
+## `redis_hll_counters.code_review.i_code_review_user_mr_discussion_unlocked_monthly`
+
+Count of unique users per month who unlocked a MR
+
+| field | value |
+| --- | --- |
+| `key_path` | **`redis_hll_counters.code_review.i_code_review_user_mr_discussion_unlocked_monthly`** |
+| `product_section` | dev |
+| `product_stage` | create |
+| `product_group` | `group::code review` |
+| `product_category` | `code_review` |
+| `value_type` | number |
+| `status` | implemented |
+| `milestone` | 13.10 |
+| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55069) |
+| `time_frame` | 28d |
+| `data_source` | Redis_hll |
+| `distribution` | ce, ee |
+| `tier` | free, premium, ultimate |
+
+## `redis_hll_counters.code_review.i_code_review_user_mr_discussion_unlocked_weekly`
+
+Count of unique users per week who unlocked a MR
+
+| field | value |
+| --- | --- |
+| `key_path` | **`redis_hll_counters.code_review.i_code_review_user_mr_discussion_unlocked_weekly`** |
+| `product_section` | dev |
+| `product_stage` | create |
+| `product_group` | `group::code review` |
+| `product_category` | `code_review` |
+| `value_type` | number |
+| `status` | implemented |
+| `milestone` | 13.10 |
+| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55069) |
+| `time_frame` | 7d |
+| `data_source` | Redis_hll |
+| `distribution` | ce, ee |
+| `tier` | free, premium, ultimate |
+
## `redis_hll_counters.code_review.i_code_review_user_publish_review_monthly`
Missing description
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 6ad6123a20e..e332e5e40fa 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -5,6 +5,8 @@ module API
class Project < BasicProjectDetails
include ::API::Helpers::RelatedResourcesHelpers
+ expose :container_registry_url, as: :container_registry_image_prefix, if: -> (_, _) { Gitlab.config.registry.enabled }
+
expose :_links do
expose :self do |project|
expose_url(api_v4_projects_path(id: project.id))
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 0abb21c9831..9db4a03c5b9 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -467,7 +467,7 @@ module API
def handle_api_exception(exception)
if report_exception?(exception)
define_params_for_grape_middleware
- Gitlab::ErrorTracking.with_context(current_user) do
+ Gitlab::ApplicationContext.with_context(user: current_user) do
Gitlab::ErrorTracking.track_exception(exception)
end
end
diff --git a/lib/gitlab/application_context.rb b/lib/gitlab/application_context.rb
index cefe983848c..5d07610feb5 100644
--- a/lib/gitlab/application_context.rb
+++ b/lib/gitlab/application_context.rb
@@ -27,8 +27,12 @@ module Gitlab
Labkit::Context.push(application_context.to_lazy_hash)
end
+ def self.current
+ Labkit::Context.current.to_h
+ end
+
def self.current_context_include?(attribute_name)
- Labkit::Context.current.to_h.include?(Labkit::Context.log_key(attribute_name))
+ current.include?(Labkit::Context.log_key(attribute_name))
end
def initialize(**args)
diff --git a/lib/gitlab/error_tracking.rb b/lib/gitlab/error_tracking.rb
index 1a8e5aaf07a..dfed8db8df0 100644
--- a/lib/gitlab/error_tracking.rb
+++ b/lib/gitlab/error_tracking.rb
@@ -27,33 +27,16 @@ module Gitlab
config.sanitize_fields = Rails.application.config.filter_parameters.map(&:to_s)
config.processors << ::Gitlab::ErrorTracking::Processor::SidekiqProcessor
config.processors << ::Gitlab::ErrorTracking::Processor::GrpcErrorProcessor
+ config.processors << ::Gitlab::ErrorTracking::Processor::ContextPayloadProcessor
# Sanitize authentication headers
config.sanitize_http_headers = %w[Authorization Private-Token]
- config.tags = extra_tags_from_env.merge(program: Gitlab.process_name)
config.before_send = method(:before_send)
yield config if block_given?
end
end
- def with_context(current_user = nil)
- last_user_context = Raven.context.user
-
- user_context = {
- id: current_user&.id,
- email: current_user&.email,
- username: current_user&.username
- }.compact
-
- Raven.tags_context(default_tags)
- Raven.user_context(user_context)
-
- yield
- ensure
- Raven.user_context(last_user_context)
- end
-
# This should be used when you want to passthrough exception handling:
# rescue and raise to be catched in upper layers of the application.
#
@@ -118,37 +101,20 @@ module Gitlab
end
def process_exception(exception, sentry: false, logging: true, extra:)
- exception.try(:sentry_extra_data)&.tap do |data|
- extra = extra.merge(data) if data.is_a?(Hash)
- end
-
- extra = sanitize_request_parameters(extra)
+ context_payload = Gitlab::ErrorTracking::ContextPayloadGenerator.generate(exception, extra)
if sentry && Raven.configuration.server
- Raven.capture_exception(exception, tags: default_tags, extra: extra)
+ Raven.capture_exception(exception, **context_payload)
end
if logging
- # TODO: this logic could migrate into `Gitlab::ExceptionLogFormatter`
- # and we could also flatten deep nested hashes if required for search
- # (e.g. if `extra` includes hash of hashes).
- # In the current implementation, we don't flatten multi-level folded hashes.
- log_hash = {}
- Raven.context.tags.each { |name, value| log_hash["tags.#{name}"] = value }
- Raven.context.user.each { |name, value| log_hash["user.#{name}"] = value }
- Raven.context.extra.merge(extra).each { |name, value| log_hash["extra.#{name}"] = value }
-
- Gitlab::ExceptionLogFormatter.format!(exception, log_hash)
+ formatter = Gitlab::ErrorTracking::LogFormatter.new
+ log_hash = formatter.generate_log(exception, context_payload)
Gitlab::ErrorTracking::Logger.error(log_hash)
end
end
- def sanitize_request_parameters(parameters)
- filter = ActiveSupport::ParameterFilter.new(::Rails.application.config.filter_parameters)
- filter.filter(parameters)
- end
-
def sentry_dsn
return unless Rails.env.production? || Rails.env.development?
return unless Gitlab.config.sentry.enabled
@@ -160,22 +126,6 @@ module Gitlab
Rails.env.development? || Rails.env.test?
end
- def default_tags
- {
- Labkit::Correlation::CorrelationId::LOG_KEY.to_sym => Labkit::Correlation::CorrelationId.current_id,
- locale: I18n.locale
- }
- end
-
- # Static tags that are set on application start
- def extra_tags_from_env
- Gitlab::Json.parse(ENV.fetch('GITLAB_SENTRY_EXTRA_TAGS', '{}')).to_hash
- rescue => e
- Gitlab::AppLogger.debug("GITLAB_SENTRY_EXTRA_TAGS could not be parsed as JSON: #{e.class.name}: #{e.message}")
-
- {}
- end
-
# Group common, mostly non-actionable exceptions by type and message,
# rather than cause
def custom_fingerprinting(event, ex)
diff --git a/lib/gitlab/error_tracking/context_payload_generator.rb b/lib/gitlab/error_tracking/context_payload_generator.rb
new file mode 100644
index 00000000000..c99283b3d20
--- /dev/null
+++ b/lib/gitlab/error_tracking/context_payload_generator.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module ErrorTracking
+ class ContextPayloadGenerator
+ def self.generate(exception, extra = {})
+ new.generate(exception, extra)
+ end
+
+ def generate(exception, extra = {})
+ {
+ extra: extra_payload(exception, extra),
+ tags: tags_payload,
+ user: user_payload
+ }
+ end
+
+ private
+
+ def extra_payload(exception, extra)
+ inline_extra = exception.try(:sentry_extra_data)
+ if inline_extra.present? && inline_extra.is_a?(Hash)
+ extra = extra.merge(inline_extra)
+ end
+
+ sanitize_request_parameters(extra)
+ end
+
+ def sanitize_request_parameters(parameters)
+ filter = ActiveSupport::ParameterFilter.new(::Rails.application.config.filter_parameters)
+ filter.filter(parameters)
+ end
+
+ def tags_payload
+ extra_tags_from_env.merge!(
+ program: Gitlab.process_name,
+ locale: I18n.locale,
+ feature_category: current_context['meta.feature_category'],
+ Labkit::Correlation::CorrelationId::LOG_KEY.to_sym => Labkit::Correlation::CorrelationId.current_id
+ )
+ end
+
+ def user_payload
+ {
+ username: current_context['meta.user']
+ }
+ end
+
+ # Static tags that are set on application start
+ def extra_tags_from_env
+ Gitlab::Json.parse(ENV.fetch('GITLAB_SENTRY_EXTRA_TAGS', '{}')).to_hash
+ rescue => e
+ Gitlab::AppLogger.debug("GITLAB_SENTRY_EXTRA_TAGS could not be parsed as JSON: #{e.class.name}: #{e.message}")
+
+ {}
+ end
+
+ def current_context
+ # In case Gitlab::ErrorTracking is used when the app starts
+ return {} unless defined?(::Gitlab::ApplicationContext)
+
+ ::Gitlab::ApplicationContext.current.to_h
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/error_tracking/log_formatter.rb b/lib/gitlab/error_tracking/log_formatter.rb
new file mode 100644
index 00000000000..d004c4e20bb
--- /dev/null
+++ b/lib/gitlab/error_tracking/log_formatter.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module ErrorTracking
+ class LogFormatter
+ # Note: all the accesses to Raven's contexts here are to keep the
+ # backward-compatibility to Sentry's built-in integrations. In future,
+ # they can be removed.
+ def generate_log(exception, context_payload)
+ payload = {}
+
+ Gitlab::ExceptionLogFormatter.format!(exception, payload)
+ append_user_to_log!(payload, context_payload)
+ append_tags_to_log!(payload, context_payload)
+ append_extra_to_log!(payload, context_payload)
+
+ payload
+ end
+
+ private
+
+ def append_user_to_log!(payload, context_payload)
+ user_context = Raven.context.user.merge(context_payload[:user])
+ user_context.each do |key, value|
+ payload["user.#{key}"] = value
+ end
+ end
+
+ def append_tags_to_log!(payload, context_payload)
+ tags_context = Raven.context.tags.merge(context_payload[:tags])
+ tags_context.each do |key, value|
+ payload["tags.#{key}"] = value
+ end
+ end
+
+ def append_extra_to_log!(payload, context_payload)
+ extra = Raven.context.extra.merge(context_payload[:extra])
+ extra = extra.except(:server)
+
+ # The extra value for sidekiq is a hash whose keys are strings.
+ if extra[:sidekiq].is_a?(Hash) && extra[:sidekiq].key?('args')
+ sidekiq_extra = extra.delete(:sidekiq)
+ sidekiq_extra['args'] = Gitlab::ErrorTracking::Processor::SidekiqProcessor.loggable_arguments(
+ sidekiq_extra['args'], sidekiq_extra['class']
+ )
+ payload["extra.sidekiq"] = sidekiq_extra
+ end
+
+ extra.each do |key, value|
+ payload["extra.#{key}"] = value
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/error_tracking/processor/context_payload_processor.rb b/lib/gitlab/error_tracking/processor/context_payload_processor.rb
new file mode 100644
index 00000000000..5185205e94e
--- /dev/null
+++ b/lib/gitlab/error_tracking/processor/context_payload_processor.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module ErrorTracking
+ module Processor
+ class ContextPayloadProcessor < ::Raven::Processor
+ # This processor is added to inject application context into Sentry
+ # events generated by Sentry built-in integrations. When the
+ # integrations are re-implemented and use Gitlab::ErrorTracking, this
+ # processor should be removed.
+ def process(payload)
+ context_payload = Gitlab::ErrorTracking::ContextPayloadGenerator.generate(nil, {})
+ payload.deep_merge!(context_payload)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/exception_log_formatter.rb b/lib/gitlab/exception_log_formatter.rb
index 6aff8f909f3..9898651c9e3 100644
--- a/lib/gitlab/exception_log_formatter.rb
+++ b/lib/gitlab/exception_log_formatter.rb
@@ -12,16 +12,6 @@ module Gitlab
'exception.message' => exception.message
)
- payload.delete('extra.server')
-
- payload['extra.sidekiq'].tap do |value|
- if value.is_a?(Hash) && value.key?('args')
- value = value.dup
- payload['extra.sidekiq']['args'] = Gitlab::ErrorTracking::Processor::SidekiqProcessor
- .loggable_arguments(value['args'], value['class'])
- end
- end
-
if exception.backtrace
payload['exception.backtrace'] = Rails.backtrace_cleaner.clean(exception.backtrace)
end
diff --git a/lib/gitlab/usage_data_counters/aggregated_metrics/code_review.yml b/lib/gitlab/usage_data_counters/aggregated_metrics/code_review.yml
index 4f86d500164..c856d0a5eee 100644
--- a/lib/gitlab/usage_data_counters/aggregated_metrics/code_review.yml
+++ b/lib/gitlab/usage_data_counters/aggregated_metrics/code_review.yml
@@ -42,7 +42,9 @@
'i_code_review_user_approval_rule_edited',
'i_code_review_user_vs_code_api_request',
'i_code_review_user_toggled_task_item_status',
- 'i_code_review_user_create_mr_from_issue'
+ 'i_code_review_user_create_mr_from_issue',
+ 'i_code_review_user_mr_discussion_locked',
+ 'i_code_review_user_mr_discussion_unlocked'
]
- name: code_review_category_monthly_active_users
operator: OR
@@ -78,7 +80,9 @@
'i_code_review_user_approval_rule_deleted',
'i_code_review_user_approval_rule_edited',
'i_code_review_user_toggled_task_item_status',
- 'i_code_review_user_create_mr_from_issue'
+ 'i_code_review_user_create_mr_from_issue',
+ 'i_code_review_user_mr_discussion_locked',
+ 'i_code_review_user_mr_discussion_unlocked'
]
- name: code_review_extension_category_monthly_active_users
operator: OR
diff --git a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
index d657c5487d7..21613740142 100644
--- a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
+++ b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
@@ -164,3 +164,13 @@
category: code_review
aggregation: weekly
feature_flag: usage_data_i_code_review_user_create_mr_from_issue
+- name: i_code_review_user_mr_discussion_locked
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: usage_data_i_code_review_user_mr_discussion_locked
+- name: i_code_review_user_mr_discussion_unlocked
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: usage_data_i_code_review_user_mr_discussion_unlocked
diff --git a/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb b/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb
index b9856e1f74a..b94caa32bf7 100644
--- a/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb
@@ -35,6 +35,8 @@ module Gitlab
MR_EDIT_MR_TITLE_ACTION = 'i_code_review_edit_mr_title'
MR_EDIT_MR_DESC_ACTION = 'i_code_review_edit_mr_desc'
MR_CREATE_FROM_ISSUE_ACTION = 'i_code_review_user_create_mr_from_issue'
+ MR_DISCUSSION_LOCKED_ACTION = 'i_code_review_user_mr_discussion_locked'
+ MR_DISCUSSION_UNLOCKED_ACTION = 'i_code_review_user_mr_discussion_unlocked'
class << self
def track_mr_diffs_action(merge_request:)
@@ -153,6 +155,14 @@ module Gitlab
track_unique_action_by_user(MR_CREATE_FROM_ISSUE_ACTION, user)
end
+ def track_discussion_locked_action(user:)
+ track_unique_action_by_user(MR_DISCUSSION_LOCKED_ACTION, user)
+ end
+
+ def track_discussion_unlocked_action(user:)
+ track_unique_action_by_user(MR_DISCUSSION_UNLOCKED_ACTION, user)
+ end
+
private
def track_unique_action_by_merge_request(action, merge_request)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 4c4f25cf7a4..d8e05fd9baf 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -21028,6 +21028,9 @@ msgstr ""
msgid "OnCallSchedules|Restrict to time intervals"
msgstr ""
+msgid "OnCallSchedules|Rotation end date/time must come after start date/time"
+msgstr ""
+
msgid "OnCallSchedules|Rotation length"
msgstr ""
diff --git a/package.json b/package.json
index b03966214d3..4672866478f 100644
--- a/package.json
+++ b/package.json
@@ -58,7 +58,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.184.0",
"@gitlab/tributejs": "1.0.0",
- "@gitlab/ui": "28.0.0",
+ "@gitlab/ui": "28.1.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-4",
"@rails/ujs": "^6.0.3-4",
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index 59e7d183ae7..88be78c7615 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -314,7 +314,7 @@ function deploy() {
if [[ "$(base_config_changed)" == "true" ]]; then base_config_file_ref="${CI_COMMIT_SHA}"; fi
local base_config_file="https://gitlab.com/gitlab-org/gitlab/raw/${base_config_file_ref}/scripts/review_apps/base-config.yaml"
- echoinfo "Deploying ${release}..." true
+ echoinfo "Deploying ${release} to ${CI_ENVIRONMENT_URL} ..." true
IMAGE_REPOSITORY="registry.gitlab.com/gitlab-org/build/cng-mirror"
gitlab_migrations_image_repository="${IMAGE_REPOSITORY}/gitlab-rails-ee"
diff --git a/spec/frontend/environments/environments_app_spec.js b/spec/frontend/environments/environments_app_spec.js
index 50d84b19ce8..542cf58b079 100644
--- a/spec/frontend/environments/environments_app_spec.js
+++ b/spec/frontend/environments/environments_app_spec.js
@@ -97,13 +97,21 @@ describe('Environment', () => {
jest.spyOn(wrapper.vm, 'updateContent').mockImplementation(() => {});
wrapper.find('.gl-pagination li:nth-child(3) .page-link').trigger('click');
- expect(wrapper.vm.updateContent).toHaveBeenCalledWith({ scope: 'available', page: '2' });
+ expect(wrapper.vm.updateContent).toHaveBeenCalledWith({
+ scope: 'available',
+ page: '2',
+ nested: true,
+ });
});
it('should make an API request when using tabs', () => {
jest.spyOn(wrapper.vm, 'updateContent').mockImplementation(() => {});
findEnvironmentsTabStopped().trigger('click');
- expect(wrapper.vm.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' });
+ expect(wrapper.vm.updateContent).toHaveBeenCalledWith({
+ scope: 'stopped',
+ page: '1',
+ nested: true,
+ });
});
it('should not make the same API request when clicking on the current scope tab', () => {
diff --git a/spec/frontend/environments/folder/environments_folder_view_spec.js b/spec/frontend/environments/folder/environments_folder_view_spec.js
index 3943e89c6cf..d02ed8688c6 100644
--- a/spec/frontend/environments/folder/environments_folder_view_spec.js
+++ b/spec/frontend/environments/folder/environments_folder_view_spec.js
@@ -103,13 +103,18 @@ describe('Environments Folder View', () => {
expect(wrapper.vm.updateContent).toHaveBeenCalledWith({
scope: wrapper.vm.scope,
page: '10',
+ nested: true,
});
});
it('should make an API request when using tabs', () => {
jest.spyOn(wrapper.vm, 'updateContent').mockImplementation(() => {});
findEnvironmentsTabStopped().trigger('click');
- expect(wrapper.vm.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' });
+ expect(wrapper.vm.updateContent).toHaveBeenCalledWith({
+ scope: 'stopped',
+ page: '1',
+ nested: true,
+ });
});
});
});
@@ -161,7 +166,11 @@ describe('Environments Folder View', () => {
it('should set page to 1', () => {
jest.spyOn(wrapper.vm, 'updateContent').mockImplementation(() => {});
wrapper.vm.onChangeTab('stopped');
- expect(wrapper.vm.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' });
+ expect(wrapper.vm.updateContent).toHaveBeenCalledWith({
+ scope: 'stopped',
+ page: '1',
+ nested: true,
+ });
});
});
@@ -172,6 +181,7 @@ describe('Environments Folder View', () => {
expect(wrapper.vm.updateContent).toHaveBeenCalledWith({
scope: wrapper.vm.scope,
page: '4',
+ nested: true,
});
});
});
diff --git a/spec/lib/gitlab/error_tracking/context_payload_generator_spec.rb b/spec/lib/gitlab/error_tracking/context_payload_generator_spec.rb
new file mode 100644
index 00000000000..0e72dd7ec5e
--- /dev/null
+++ b/spec/lib/gitlab/error_tracking/context_payload_generator_spec.rb
@@ -0,0 +1,176 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+
+RSpec.describe Gitlab::ErrorTracking::ContextPayloadGenerator do
+ subject(:generator) { described_class.new }
+
+ let(:extra) do
+ {
+ some_other_info: 'info',
+ issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/-/issues/1'
+ }
+ end
+
+ let(:exception) { StandardError.new("Dummy exception") }
+
+ before do
+ allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('cid')
+ allow(I18n).to receive(:locale).and_return('en')
+ end
+
+ context 'user metadata' do
+ let(:user) { create(:user) }
+
+ it 'appends user metadata to the payload' do
+ payload = {}
+
+ Gitlab::ApplicationContext.with_context(user: user) do
+ payload = generator.generate(exception, extra)
+ end
+
+ expect(payload[:user]).to eql(
+ username: user.username
+ )
+ end
+ end
+
+ context 'tags metadata' do
+ context 'when the GITLAB_SENTRY_EXTRA_TAGS env is not set' do
+ before do
+ stub_env('GITLAB_SENTRY_EXTRA_TAGS', nil)
+ end
+
+ it 'does not log into AppLogger' do
+ expect(Gitlab::AppLogger).not_to receive(:debug)
+
+ generator.generate(exception, extra)
+ end
+
+ it 'does not send any extra tags' do
+ payload = {}
+
+ Gitlab::ApplicationContext.with_context(feature_category: 'feature_a') do
+ payload = generator.generate(exception, extra)
+ end
+
+ expect(payload[:tags]).to eql(
+ correlation_id: 'cid',
+ locale: 'en',
+ program: 'test',
+ feature_category: 'feature_a'
+ )
+ end
+ end
+
+ context 'when the GITLAB_SENTRY_EXTRA_TAGS env is a JSON hash' do
+ it 'includes those tags in all events' do
+ stub_env('GITLAB_SENTRY_EXTRA_TAGS', { foo: 'bar', baz: 'quux' }.to_json)
+ payload = {}
+
+ Gitlab::ApplicationContext.with_context(feature_category: 'feature_a') do
+ payload = generator.generate(exception, extra)
+ end
+
+ expect(payload[:tags]).to eql(
+ correlation_id: 'cid',
+ locale: 'en',
+ program: 'test',
+ feature_category: 'feature_a',
+ 'foo' => 'bar',
+ 'baz' => 'quux'
+ )
+ end
+
+ it 'does not log into AppLogger' do
+ expect(Gitlab::AppLogger).not_to receive(:debug)
+
+ generator.generate(exception, extra)
+ end
+ end
+
+ context 'when the GITLAB_SENTRY_EXTRA_TAGS env is not a JSON hash' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:env_var, :error) do
+ { foo: 'bar', baz: 'quux' }.inspect | 'JSON::ParserError'
+ [].to_json | 'NoMethodError'
+ [%w[foo bar]].to_json | 'NoMethodError'
+ %w[foo bar].to_json | 'NoMethodError'
+ '"string"' | 'NoMethodError'
+ end
+
+ with_them do
+ before do
+ stub_env('GITLAB_SENTRY_EXTRA_TAGS', env_var)
+ end
+
+ it 'logs into AppLogger' do
+ expect(Gitlab::AppLogger).to receive(:debug).with(a_string_matching(error))
+
+ generator.generate({})
+ end
+
+ it 'does not include any extra tags' do
+ payload = {}
+
+ Gitlab::ApplicationContext.with_context(feature_category: 'feature_a') do
+ payload = generator.generate(exception, extra)
+ end
+
+ expect(payload[:tags]).to eql(
+ correlation_id: 'cid',
+ locale: 'en',
+ program: 'test',
+ feature_category: 'feature_a'
+ )
+ end
+ end
+ end
+ end
+
+ context 'extra metadata' do
+ it 'appends extra metadata to the payload' do
+ payload = generator.generate(exception, extra)
+
+ expect(payload[:extra]).to eql(
+ some_other_info: 'info',
+ issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/-/issues/1'
+ )
+ end
+
+ it 'appends exception embedded extra metadata to the payload' do
+ allow(exception).to receive(:sentry_extra_data).and_return(
+ some_other_info: 'another_info',
+ mr_url: 'https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1'
+ )
+
+ payload = generator.generate(exception, extra)
+
+ expect(payload[:extra]).to eql(
+ some_other_info: 'another_info',
+ issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/-/issues/1',
+ mr_url: 'https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1'
+ )
+ end
+
+ it 'filters sensitive extra info' do
+ extra[:my_token] = '456'
+ allow(exception).to receive(:sentry_extra_data).and_return(
+ mr_url: 'https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1',
+ another_token: '1234'
+ )
+
+ payload = generator.generate(exception, extra)
+
+ expect(payload[:extra]).to eql(
+ some_other_info: 'info',
+ issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/-/issues/1',
+ mr_url: 'https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1',
+ my_token: '[FILTERED]',
+ another_token: '[FILTERED]'
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/error_tracking/log_formatter_spec.rb b/spec/lib/gitlab/error_tracking/log_formatter_spec.rb
new file mode 100644
index 00000000000..188ccd000a1
--- /dev/null
+++ b/spec/lib/gitlab/error_tracking/log_formatter_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe Gitlab::ErrorTracking::LogFormatter do
+ let(:exception) { StandardError.new('boom') }
+ let(:context_payload) do
+ {
+ server: 'local-hostname-of-the-server',
+ user: {
+ ip_address: '127.0.0.1',
+ username: 'root'
+ },
+ tags: {
+ locale: 'en',
+ feature_category: 'category_a'
+ },
+ extra: {
+ some_other_info: 'other_info',
+ sidekiq: {
+ 'class' => 'HelloWorker',
+ 'args' => ['senstive string', 1, 2],
+ 'another_field' => 'field'
+ }
+ }
+ }
+ end
+
+ before do
+ Raven.context.user[:user_flag] = 'flag'
+ Raven.context.tags[:shard] = 'catchall'
+ Raven.context.extra[:some_info] = 'info'
+
+ allow(exception).to receive(:backtrace).and_return(
+ [
+ 'lib/gitlab/file_a.rb:1',
+ 'lib/gitlab/file_b.rb:2'
+ ]
+ )
+ end
+
+ after do
+ ::Raven::Context.clear!
+ end
+
+ it 'appends error-related log fields and filters sensitive Sidekiq arguments' do
+ payload = described_class.new.generate_log(exception, context_payload)
+
+ expect(payload).to eql(
+ 'exception.class' => 'StandardError',
+ 'exception.message' => 'boom',
+ 'exception.backtrace' => [
+ 'lib/gitlab/file_a.rb:1',
+ 'lib/gitlab/file_b.rb:2'
+ ],
+ 'user.ip_address' => '127.0.0.1',
+ 'user.username' => 'root',
+ 'user.user_flag' => 'flag',
+ 'tags.locale' => 'en',
+ 'tags.feature_category' => 'category_a',
+ 'tags.shard' => 'catchall',
+ 'extra.some_other_info' => 'other_info',
+ 'extra.some_info' => 'info',
+ "extra.sidekiq" => {
+ "another_field" => "field",
+ "args" => ["[FILTERED]", "1", "2"],
+ "class" => "HelloWorker"
+ }
+ )
+ end
+end
diff --git a/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb b/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb
new file mode 100644
index 00000000000..0db40eca989
--- /dev/null
+++ b/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe Gitlab::ErrorTracking::Processor::ContextPayloadProcessor do
+ subject(:processor) { described_class.new }
+
+ before do
+ allow_next_instance_of(Gitlab::ErrorTracking::ContextPayloadGenerator) do |generator|
+ allow(generator).to receive(:generate).and_return(
+ user: { username: 'root' },
+ tags: { locale: 'en', program: 'test', feature_category: 'feature_a', correlation_id: 'cid' },
+ extra: { some_info: 'info' }
+ )
+ end
+ end
+
+ it 'merges the context payload into event payload' do
+ payload = {
+ user: { ip_address: '127.0.0.1' },
+ tags: { priority: 'high' },
+ extra: { sidekiq: { class: 'SomeWorker', args: ['[FILTERED]', 1, 2] } }
+ }
+
+ processor.process(payload)
+
+ expect(payload).to eql(
+ user: {
+ ip_address: '127.0.0.1',
+ username: 'root'
+ },
+ tags: {
+ priority: 'high',
+ locale: 'en',
+ program: 'test',
+ feature_category: 'feature_a',
+ correlation_id: 'cid'
+ },
+ extra: {
+ some_info: 'info',
+ sidekiq: { class: 'SomeWorker', args: ['[FILTERED]', 1, 2] }
+ }
+ )
+ end
+end
diff --git a/spec/lib/gitlab/error_tracking_spec.rb b/spec/lib/gitlab/error_tracking_spec.rb
index 764478ad1d7..a905b9f8d40 100644
--- a/spec/lib/gitlab/error_tracking_spec.rb
+++ b/spec/lib/gitlab/error_tracking_spec.rb
@@ -8,116 +8,55 @@ RSpec.describe Gitlab::ErrorTracking do
let(:exception) { RuntimeError.new('boom') }
let(:issue_url) { 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1' }
- let(:expected_payload_includes) do
- [
- { 'exception.class' => 'RuntimeError' },
- { 'exception.message' => 'boom' },
- { 'tags.correlation_id' => 'cid' },
- { 'extra.some_other_info' => 'info' },
- { 'extra.issue_url' => 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1' }
- ]
+ let(:user) { create(:user) }
+
+ let(:sentry_payload) do
+ {
+ tags: {
+ program: 'test',
+ locale: 'en',
+ feature_category: 'feature_a',
+ correlation_id: 'cid'
+ },
+ user: {
+ username: user.username
+ },
+ extra: {
+ some_other_info: 'info',
+ issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1'
+ }
+ }
end
- let(:sentry_event) { Gitlab::Json.parse(Raven.client.transport.events.last[1]) }
+ let(:logger_payload) do
+ {
+ 'exception.class' => 'RuntimeError',
+ 'exception.message' => 'boom',
+ 'tags.program' => 'test',
+ 'tags.locale' => 'en',
+ 'tags.feature_category' => 'feature_a',
+ 'tags.correlation_id' => 'cid',
+ 'user.username' => user.username,
+ 'extra.some_other_info' => 'info',
+ 'extra.issue_url' => 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1'
+ }
+ end
before do
stub_sentry_settings
allow(described_class).to receive(:sentry_dsn).and_return(Gitlab.config.sentry.dsn)
allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('cid')
+ allow(I18n).to receive(:locale).and_return('en')
described_class.configure do |config|
config.encoding = 'json'
end
end
- describe '.configure' do
- context 'default tags from GITLAB_SENTRY_EXTRA_TAGS' do
- context 'when the value is a JSON hash' do
- it 'includes those tags in all events' do
- stub_env('GITLAB_SENTRY_EXTRA_TAGS', { foo: 'bar', baz: 'quux' }.to_json)
-
- described_class.configure do |config|
- config.encoding = 'json'
- end
-
- described_class.track_exception(StandardError.new)
-
- expect(sentry_event['tags'].except('correlation_id', 'locale', 'program'))
- .to eq('foo' => 'bar', 'baz' => 'quux')
- end
- end
-
- context 'when the value is not set' do
- before do
- stub_env('GITLAB_SENTRY_EXTRA_TAGS', nil)
- end
-
- it 'does not log an error' do
- expect(Gitlab::AppLogger).not_to receive(:debug)
-
- described_class.configure do |config|
- config.encoding = 'json'
- end
- end
-
- it 'does not send any extra tags' do
- described_class.configure do |config|
- config.encoding = 'json'
- end
-
- described_class.track_exception(StandardError.new)
-
- expect(sentry_event['tags'].keys).to contain_exactly('correlation_id', 'locale', 'program')
- end
- end
-
- context 'when the value is not a JSON hash' do
- using RSpec::Parameterized::TableSyntax
-
- where(:env_var, :error) do
- { foo: 'bar', baz: 'quux' }.inspect | 'JSON::ParserError'
- [].to_json | 'NoMethodError'
- [%w[foo bar]].to_json | 'NoMethodError'
- %w[foo bar].to_json | 'NoMethodError'
- '"string"' | 'NoMethodError'
- end
-
- with_them do
- before do
- stub_env('GITLAB_SENTRY_EXTRA_TAGS', env_var)
- end
-
- it 'does not include any extra tags' do
- described_class.configure do |config|
- config.encoding = 'json'
- end
-
- described_class.track_exception(StandardError.new)
-
- expect(sentry_event['tags'].except('correlation_id', 'locale', 'program'))
- .to be_empty
- end
-
- it 'logs the error class' do
- expect(Gitlab::AppLogger).to receive(:debug).with(a_string_matching(error))
-
- described_class.configure do |config|
- config.encoding = 'json'
- end
- end
- end
- end
- end
- end
-
- describe '.with_context' do
- it 'adds the expected tags' do
- described_class.with_context {}
-
- expect(Raven.tags_context[:locale].to_s).to eq(I18n.locale.to_s)
- expect(Raven.tags_context[Labkit::Correlation::CorrelationId::LOG_KEY.to_sym].to_s)
- .to eq('cid')
+ around do |example|
+ Gitlab::ApplicationContext.with_context(user: user, feature_category: 'feature_a') do
+ example.run
end
end
@@ -128,10 +67,15 @@ RSpec.describe Gitlab::ErrorTracking do
end
it 'raises the exception' do
- expect(Raven).to receive(:capture_exception)
-
- expect { described_class.track_and_raise_for_dev_exception(exception) }
- .to raise_error(RuntimeError)
+ expect(Raven).to receive(:capture_exception).with(exception, sentry_payload)
+
+ expect do
+ described_class.track_and_raise_for_dev_exception(
+ exception,
+ issue_url: issue_url,
+ some_other_info: 'info'
+ )
+ end.to raise_error(RuntimeError, /boom/)
end
end
@@ -141,19 +85,7 @@ RSpec.describe Gitlab::ErrorTracking do
end
it 'logs the exception with all attributes passed' do
- expected_extras = {
- some_other_info: 'info',
- issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1'
- }
-
- expected_tags = {
- correlation_id: 'cid'
- }
-
- expect(Raven).to receive(:capture_exception)
- .with(exception,
- tags: a_hash_including(expected_tags),
- extra: a_hash_including(expected_extras))
+ expect(Raven).to receive(:capture_exception).with(exception, sentry_payload)
described_class.track_and_raise_for_dev_exception(
exception,
@@ -163,8 +95,7 @@ RSpec.describe Gitlab::ErrorTracking do
end
it 'calls Gitlab::ErrorTracking::Logger.error with formatted payload' do
- expect(Gitlab::ErrorTracking::Logger).to receive(:error)
- .with(a_hash_including(*expected_payload_includes))
+ expect(Gitlab::ErrorTracking::Logger).to receive(:error).with(logger_payload)
described_class.track_and_raise_for_dev_exception(
exception,
@@ -177,15 +108,19 @@ RSpec.describe Gitlab::ErrorTracking do
describe '.track_and_raise_exception' do
it 'always raises the exception' do
- expect(Raven).to receive(:capture_exception)
+ expect(Raven).to receive(:capture_exception).with(exception, sentry_payload)
- expect { described_class.track_and_raise_exception(exception) }
- .to raise_error(RuntimeError)
+ expect do
+ described_class.track_and_raise_for_dev_exception(
+ exception,
+ issue_url: issue_url,
+ some_other_info: 'info'
+ )
+ end.to raise_error(RuntimeError, /boom/)
end
it 'calls Gitlab::ErrorTracking::Logger.error with formatted payload' do
- expect(Gitlab::ErrorTracking::Logger).to receive(:error)
- .with(a_hash_including(*expected_payload_includes))
+ expect(Gitlab::ErrorTracking::Logger).to receive(:error).with(logger_payload)
expect do
described_class.track_and_raise_exception(
@@ -210,17 +145,16 @@ RSpec.describe Gitlab::ErrorTracking do
it 'calls Raven.capture_exception' do
track_exception
- expect(Raven).to have_received(:capture_exception)
- .with(exception,
- tags: a_hash_including(correlation_id: 'cid'),
- extra: a_hash_including(some_other_info: 'info', issue_url: issue_url))
+ expect(Raven).to have_received(:capture_exception).with(
+ exception,
+ sentry_payload
+ )
end
it 'calls Gitlab::ErrorTracking::Logger.error with formatted payload' do
track_exception
- expect(Gitlab::ErrorTracking::Logger).to have_received(:error)
- .with(a_hash_including(*expected_payload_includes))
+ expect(Gitlab::ErrorTracking::Logger).to have_received(:error).with(logger_payload)
end
context 'with filterable parameters' do
@@ -229,8 +163,9 @@ RSpec.describe Gitlab::ErrorTracking do
it 'filters parameters' do
track_exception
- expect(Gitlab::ErrorTracking::Logger).to have_received(:error)
- .with(hash_including({ 'extra.test' => 1, 'extra.my_token' => '[FILTERED]' }))
+ expect(Gitlab::ErrorTracking::Logger).to have_received(:error).with(
+ hash_including({ 'extra.test' => 1, 'extra.my_token' => '[FILTERED]' })
+ )
end
end
@@ -241,8 +176,9 @@ RSpec.describe Gitlab::ErrorTracking do
it 'includes the extra data from the exception in the tracking information' do
track_exception
- expect(Raven).to have_received(:capture_exception)
- .with(exception, a_hash_including(extra: a_hash_including(extra_info)))
+ expect(Raven).to have_received(:capture_exception).with(
+ exception, a_hash_including(extra: a_hash_including(extra_info))
+ )
end
end
@@ -253,8 +189,9 @@ RSpec.describe Gitlab::ErrorTracking do
it 'just includes the other extra info' do
track_exception
- expect(Raven).to have_received(:capture_exception)
- .with(exception, a_hash_including(extra: a_hash_including(extra)))
+ expect(Raven).to have_received(:capture_exception).with(
+ exception, a_hash_including(extra: a_hash_including(extra))
+ )
end
end
@@ -266,7 +203,13 @@ RSpec.describe Gitlab::ErrorTracking do
track_exception
expect(Gitlab::ErrorTracking::Logger).to have_received(:error).with(
- hash_including({ 'extra.sidekiq' => { 'class' => 'PostReceive', 'args' => ['1', '{"id"=>2, "name"=>"hello"}', 'some-value', 'another-value'] } }))
+ hash_including(
+ 'extra.sidekiq' => {
+ 'class' => 'PostReceive',
+ 'args' => ['1', '{"id"=>2, "name"=>"hello"}', 'some-value', 'another-value']
+ }
+ )
+ )
end
end
@@ -276,9 +219,17 @@ RSpec.describe Gitlab::ErrorTracking do
it 'filters sensitive arguments before sending' do
track_exception
+ sentry_event = Gitlab::Json.parse(Raven.client.transport.events.last[1])
+
expect(sentry_event.dig('extra', 'sidekiq', 'args')).to eq(['[FILTERED]', 1, 2])
expect(Gitlab::ErrorTracking::Logger).to have_received(:error).with(
- hash_including('extra.sidekiq' => { 'class' => 'UnknownWorker', 'args' => ['[FILTERED]', '1', '2'] }))
+ hash_including(
+ 'extra.sidekiq' => {
+ 'class' => 'UnknownWorker',
+ 'args' => ['[FILTERED]', '1', '2']
+ }
+ )
+ )
end
end
end
diff --git a/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
index a604de4a61f..6bc42430889 100644
--- a/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
@@ -284,4 +284,20 @@ RSpec.describe Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter, :cl
let(:action) { described_class::MR_CREATE_FROM_ISSUE_ACTION }
end
end
+
+ describe '.track_discussion_locked_action' do
+ subject { described_class.track_discussion_locked_action(user: user) }
+
+ it_behaves_like 'a tracked merge request unique event' do
+ let(:action) { described_class::MR_DISCUSSION_LOCKED_ACTION }
+ end
+ end
+
+ describe '.track_discussion_unlocked_action' do
+ subject { described_class.track_discussion_unlocked_action(user: user) }
+
+ it_behaves_like 'a tracked merge request unique event' do
+ let(:action) { described_class::MR_DISCUSSION_UNLOCKED_ACTION }
+ end
+ end
end
diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb
index 18388b4cd83..6bac5e31435 100644
--- a/spec/models/upload_spec.rb
+++ b/spec/models/upload_spec.rb
@@ -221,7 +221,7 @@ RSpec.describe Upload do
it 'does not send a message to Sentry' do
upload = described_class.new(path: "#{__FILE__}-nope", store: ObjectStorage::Store::LOCAL)
- expect(Raven).not_to receive(:capture_message)
+ expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
upload.exist?
end
diff --git a/spec/requests/api/graphql/project/merge_requests_spec.rb b/spec/requests/api/graphql/project/merge_requests_spec.rb
index d684be91dc9..12060eb51e9 100644
--- a/spec/requests/api/graphql/project/merge_requests_spec.rb
+++ b/spec/requests/api/graphql/project/merge_requests_spec.rb
@@ -7,13 +7,27 @@ RSpec.describe 'getting merge request listings nested in a project' do
let_it_be(:project) { create(:project, :repository, :public) }
let_it_be(:current_user) { create(:user) }
-
let_it_be(:label) { create(:label, project: project) }
- let_it_be(:merge_request_a) { create(:labeled_merge_request, :unique_branches, source_project: project, labels: [label]) }
- let_it_be(:merge_request_b) { create(:merge_request, :closed, :unique_branches, source_project: project) }
- let_it_be(:merge_request_c) { create(:labeled_merge_request, :closed, :unique_branches, source_project: project, labels: [label]) }
- let_it_be(:merge_request_d) { create(:merge_request, :locked, :unique_branches, source_project: project) }
- let_it_be(:merge_request_e) { create(:merge_request, :unique_branches, source_project: project) }
+
+ let_it_be(:merge_request_a) do
+ create(:labeled_merge_request, :unique_branches, source_project: project, labels: [label])
+ end
+
+ let_it_be(:merge_request_b) do
+ create(:merge_request, :closed, :unique_branches, source_project: project)
+ end
+
+ let_it_be(:merge_request_c) do
+ create(:labeled_merge_request, :closed, :unique_branches, source_project: project, labels: [label])
+ end
+
+ let_it_be(:merge_request_d) do
+ create(:merge_request, :locked, :unique_branches, source_project: project)
+ end
+
+ let_it_be(:merge_request_e) do
+ create(:merge_request, :unique_branches, source_project: project)
+ end
let(:results) { graphql_data.dig('project', 'mergeRequests', 'nodes') }
@@ -27,32 +41,38 @@ RSpec.describe 'getting merge request listings nested in a project' do
)
end
- let(:query) do
- query_merge_requests(all_graphql_fields_for('MergeRequest', max_depth: 1))
- end
-
it_behaves_like 'a working graphql query' do
+ let(:query) do
+ query_merge_requests(all_graphql_fields_for('MergeRequest', max_depth: 2))
+ end
+
before do
- post_graphql(query, current_user: current_user)
+ # We cannot call the whitelist here, since the transaction does not
+ # begin until we enter the controller.
+ headers = {
+ 'X-GITLAB-QUERY-WHITELIST-ISSUE' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/322979'
+ }
+
+ post_graphql(query, current_user: current_user, headers: headers)
end
end
# The following tests are needed to guarantee that we have correctly annotated
# all the gitaly calls. Selecting combinations of fields may mask this due to
# memoization.
- context 'requesting a single field' do
+ context 'when requesting a single field' do
let_it_be(:fresh_mr) { create(:merge_request, :unique_branches, source_project: project) }
+
let(:search_params) { { iids: [fresh_mr.iid.to_s] } }
+ let(:graphql_data) do
+ GitlabSchema.execute(query, context: { current_user: current_user }).to_h['data']
+ end
before do
project.repository.expire_branches_cache
end
- let(:graphql_data) do
- GitlabSchema.execute(query, context: { current_user: current_user }).to_h['data']
- end
-
- context 'selecting any single scalar field' do
+ context 'when selecting any single scalar field' do
where(:field) do
scalar_fields_of('MergeRequest').map { |name| [name] }
end
@@ -68,7 +88,7 @@ RSpec.describe 'getting merge request listings nested in a project' do
end
end
- context 'selecting any single nested field' do
+ context 'when selecting any single nested field' do
where(:field, :subfield, :is_connection) do
nested_fields_of('MergeRequest').flat_map do |name, field|
type = field_type(field)
@@ -95,7 +115,11 @@ RSpec.describe 'getting merge request listings nested in a project' do
end
end
- shared_examples 'searching with parameters' do
+ shared_examples 'when searching with parameters' do
+ let(:query) do
+ query_merge_requests('iid title')
+ end
+
let(:expected) do
mrs.map { |mr| a_hash_including('iid' => mr.iid.to_s, 'title' => mr.title) }
end
@@ -107,60 +131,60 @@ RSpec.describe 'getting merge request listings nested in a project' do
end
end
- context 'there are no search params' do
+ context 'when there are no search params' do
let(:search_params) { nil }
let(:mrs) { [merge_request_a, merge_request_b, merge_request_c, merge_request_d, merge_request_e] }
- it_behaves_like 'searching with parameters'
+ it_behaves_like 'when searching with parameters'
end
- context 'the search params do not match anything' do
- let(:search_params) { { iids: %w(foo bar baz) } }
+ context 'when the search params do not match anything' do
+ let(:search_params) { { iids: %w[foo bar baz] } }
let(:mrs) { [] }
- it_behaves_like 'searching with parameters'
+ it_behaves_like 'when searching with parameters'
end
- context 'searching by iids' do
+ context 'when searching by iids' do
let(:search_params) { { iids: mrs.map(&:iid).map(&:to_s) } }
let(:mrs) { [merge_request_a, merge_request_c] }
- it_behaves_like 'searching with parameters'
+ it_behaves_like 'when searching with parameters'
end
- context 'searching by state' do
+ context 'when searching by state' do
let(:search_params) { { state: :closed } }
let(:mrs) { [merge_request_b, merge_request_c] }
- it_behaves_like 'searching with parameters'
+ it_behaves_like 'when searching with parameters'
end
- context 'searching by source_branch' do
+ context 'when searching by source_branch' do
let(:search_params) { { source_branches: mrs.map(&:source_branch) } }
let(:mrs) { [merge_request_b, merge_request_c] }
- it_behaves_like 'searching with parameters'
+ it_behaves_like 'when searching with parameters'
end
- context 'searching by target_branch' do
+ context 'when searching by target_branch' do
let(:search_params) { { target_branches: mrs.map(&:target_branch) } }
let(:mrs) { [merge_request_a, merge_request_d] }
- it_behaves_like 'searching with parameters'
+ it_behaves_like 'when searching with parameters'
end
- context 'searching by label' do
+ context 'when searching by label' do
let(:search_params) { { labels: [label.title] } }
let(:mrs) { [merge_request_a, merge_request_c] }
- it_behaves_like 'searching with parameters'
+ it_behaves_like 'when searching with parameters'
end
- context 'searching by combination' do
+ context 'when searching by combination' do
let(:search_params) { { state: :closed, labels: [label.title] } }
let(:mrs) { [merge_request_c] }
- it_behaves_like 'searching with parameters'
+ it_behaves_like 'when searching with parameters'
end
context 'when requesting `approved_by`' do
@@ -203,10 +227,10 @@ RSpec.describe 'getting merge request listings nested in a project' do
it 'exposes `commit_count`' do
execute_query
- expect(results).to match_array([
+ expect(results).to match_array [
{ "iid" => merge_request_a.iid.to_s, "commitCount" => 0 },
{ "iid" => merge_request_with_commits.iid.to_s, "commitCount" => 29 }
- ])
+ ]
end
end
@@ -216,8 +240,8 @@ RSpec.describe 'getting merge request listings nested in a project' do
before do
# make the MRs "merged"
[merge_request_a, merge_request_b, merge_request_c].each do |mr|
- mr.update_column(:state_id, MergeRequest.available_states[:merged])
- mr.metrics.update_column(:merged_at, Time.now)
+ mr.update!(state_id: MergeRequest.available_states[:merged])
+ mr.metrics.update!(merged_at: Time.now)
end
end
@@ -256,13 +280,12 @@ RSpec.describe 'getting merge request listings nested in a project' do
end
it 'returns the reviewers' do
+ nodes = merge_request_a.reviewers.map { |r| { 'username' => r.username } }
+ reviewers = { 'nodes' => match_array(nodes) }
+
execute_query
- expect(results).to include a_hash_including('reviewers' => {
- 'nodes' => match_array(merge_request_a.reviewers.map do |r|
- a_hash_including('username' => r.username)
- end)
- })
+ expect(results).to include a_hash_including('reviewers' => match(reviewers))
end
include_examples 'N+1 query check'
@@ -309,12 +332,14 @@ RSpec.describe 'getting merge request listings nested in a project' do
allow(Gitlab::Database).to receive(:read_only?).and_return(false)
end
+ def query_context
+ { current_user: current_user }
+ end
+
def run_query(number)
# Ensure that we have a fresh request store and batch-context between runs
- result = run_with_clean_state(query,
- context: { current_user: current_user },
- variables: { first: number }
- )
+ vars = { first: number }
+ result = run_with_clean_state(query, context: query_context, variables: vars)
graphql_dig_at(result.to_h, :data, :project, :merge_requests, :nodes)
end
@@ -348,13 +373,11 @@ RSpec.describe 'getting merge request listings nested in a project' do
let(:data_path) { [:project, :mergeRequests] }
def pagination_query(params)
- graphql_query_for(:project, { full_path: project.full_path },
- <<~QUERY
+ graphql_query_for(:project, { full_path: project.full_path }, <<~QUERY)
mergeRequests(#{params}) {
#{page_info} nodes { id }
}
- QUERY
- )
+ QUERY
end
context 'when sorting by merged_at DESC' do
@@ -396,14 +419,12 @@ RSpec.describe 'getting merge request listings nested in a project' do
let(:query) do
# Note: __typename meta field is always requested by the FE
- graphql_query_for(:project, { full_path: project.full_path },
- <<~QUERY
+ graphql_query_for(:project, { full_path: project.full_path }, <<~QUERY)
mergeRequests(mergedAfter: "2020-01-01", mergedBefore: "2020-01-05", first: 0, sourceBranches: null, labels: null) {
count
__typename
}
QUERY
- )
end
shared_examples 'count examples' do
@@ -430,14 +451,12 @@ RSpec.describe 'getting merge request listings nested in a project' do
context 'when total_time_to_merge and count is queried' do
let(:query) do
- graphql_query_for(:project, { full_path: project.full_path },
- <<~QUERY
+ graphql_query_for(:project, { full_path: project.full_path }, <<~QUERY)
mergeRequests(mergedAfter: "2020-01-01", mergedBefore: "2020-01-05", first: 0) {
totalTimeToMerge
count
}
QUERY
- )
end
it 'does not query the merge requests table for the total_time_to_merge' do
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index 91d10791541..8160a94aef2 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -314,14 +314,13 @@ RSpec.describe API::Helpers do
expect(Gitlab::ErrorTracking).to receive(:sentry_dsn).and_return(Gitlab.config.sentry.dsn)
Gitlab::ErrorTracking.configure
- Raven.client.configuration.encoding = 'json'
end
it 'does not report a MethodNotAllowed exception to Sentry' do
exception = Grape::Exceptions::MethodNotAllowed.new({ 'X-GitLab-Test' => '1' })
allow(exception).to receive(:backtrace).and_return(caller)
- expect(Raven).not_to receive(:capture_exception).with(exception)
+ expect(Gitlab::ErrorTracking).not_to receive(:track_exception).with(exception)
handle_api_exception(exception)
end
@@ -330,8 +329,7 @@ RSpec.describe API::Helpers do
exception = RuntimeError.new('test error')
allow(exception).to receive(:backtrace).and_return(caller)
- expect(Raven).to receive(:capture_exception).with(exception, tags:
- a_hash_including(correlation_id: 'new-correlation-id'), extra: {})
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(exception)
Labkit::Correlation::CorrelationId.use_id('new-correlation-id') do
handle_api_exception(exception)
@@ -357,20 +355,6 @@ RSpec.describe API::Helpers do
expect(json_response['message']).to start_with("\nRuntimeError (Runtime Error!):")
end
end
-
- context 'extra information' do
- # Sentry events are an array of the form [auth_header, data, options]
- let(:event_data) { Raven.client.transport.events.first[1] }
-
- it 'sends the params, excluding confidential values' do
- expect(ProjectsFinder).to receive(:new).and_raise('Runtime Error!')
-
- get api('/projects', user), params: { password: 'dont_send_this', other_param: 'send_this' }
-
- expect(event_data).to include('other_param=send_this')
- expect(event_data).to include('password=********')
- end
- end
end
describe '.authenticate_non_get!' do
diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml
index 181fcafd577..104918810f8 100644
--- a/spec/requests/api/project_attributes.yml
+++ b/spec/requests/api/project_attributes.yml
@@ -56,6 +56,7 @@ itself: # project
- can_create_merge_request_in
- compliance_frameworks
- container_expiration_policy
+ - container_registry_image_prefix
- default_branch
- empty_repo
- forks_count
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 8a68db6ae4b..19cab51ef34 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1540,6 +1540,10 @@ RSpec.describe API::Projects do
end
context 'when authenticated as an admin' do
+ before do
+ stub_container_registry_config(enabled: true, host_port: 'registry.example.org:5000')
+ end
+
let(:project_attributes_file) { 'spec/requests/api/project_attributes.yml' }
let(:project_attributes) { YAML.load_file(project_attributes_file) }
@@ -1569,7 +1573,7 @@ RSpec.describe API::Projects do
keys
end
- it 'returns a project by id' do
+ it 'returns a project by id', :aggregate_failures do
project
project_member
group = create(:group)
@@ -1587,6 +1591,7 @@ RSpec.describe API::Projects do
expect(json_response['ssh_url_to_repo']).to be_present
expect(json_response['http_url_to_repo']).to be_present
expect(json_response['web_url']).to be_present
+ expect(json_response['container_registry_image_prefix']).to eq("registry.example.org:5000/#{project.full_path}")
expect(json_response['owner']).to be_a Hash
expect(json_response['name']).to eq(project.name)
expect(json_response['path']).to be_present
@@ -1644,9 +1649,10 @@ RSpec.describe API::Projects do
before do
project
project_member
+ stub_container_registry_config(enabled: true, host_port: 'registry.example.org:5000')
end
- it 'returns a project by id' do
+ it 'returns a project by id', :aggregate_failures do
group = create(:group)
link = create(:project_group_link, project: project, group: group)
@@ -1662,6 +1668,7 @@ RSpec.describe API::Projects do
expect(json_response['ssh_url_to_repo']).to be_present
expect(json_response['http_url_to_repo']).to be_present
expect(json_response['web_url']).to be_present
+ expect(json_response['container_registry_image_prefix']).to eq("registry.example.org:5000/#{project.full_path}")
expect(json_response['owner']).to be_a Hash
expect(json_response['name']).to eq(project.name)
expect(json_response['path']).to be_present
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index a6bd8416e58..e9ec3bccda3 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -48,6 +48,8 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
end
context 'valid params' do
+ let(:locked) { true }
+
let(:opts) do
{
title: 'New title',
@@ -58,7 +60,7 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
label_ids: [label.id],
target_branch: 'target',
force_remove_source_branch: '1',
- discussion_locked: true
+ discussion_locked: locked
}
end
@@ -117,6 +119,56 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
MergeRequests::UpdateService.new(project, user, opts).execute(draft_merge_request)
end
+
+ context 'when MR is locked' do
+ context 'when locked again' do
+ it 'does not track discussion locking' do
+ expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
+ .not_to receive(:track_discussion_locked_action)
+
+ opts[:discussion_locked] = true
+
+ MergeRequests::UpdateService.new(project, user, opts).execute(merge_request)
+ end
+ end
+
+ context 'when unlocked' do
+ it 'tracks dicussion unlocking' do
+ expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
+ .to receive(:track_discussion_unlocked_action).once.with(user: user)
+
+ opts[:discussion_locked] = false
+
+ MergeRequests::UpdateService.new(project, user, opts).execute(merge_request)
+ end
+ end
+ end
+
+ context 'when MR is unlocked' do
+ let(:locked) { false }
+
+ context 'when unlocked again' do
+ it 'does not track discussion unlocking' do
+ expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
+ .not_to receive(:track_discussion_unlocked_action)
+
+ opts[:discussion_locked] = false
+
+ MergeRequests::UpdateService.new(project, user, opts).execute(merge_request)
+ end
+ end
+
+ context 'when locked' do
+ it 'tracks dicussion locking' do
+ expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
+ .to receive(:track_discussion_locked_action).once.with(user: user)
+
+ opts[:discussion_locked] = true
+
+ MergeRequests::UpdateService.new(project, user, opts).execute(merge_request)
+ end
+ end
+ end
end
context 'updating milestone' do
diff --git a/yarn.lock b/yarn.lock
index 08717ee05c6..768b29ab5ba 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -907,10 +907,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
-"@gitlab/ui@28.0.0":
- version "28.0.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-28.0.0.tgz#712a24ba970e69eed595209194d69574851034c6"
- integrity sha512-6gJKUEnqYAYxyWta/CJcSSPNu07iVrQ4m6Sxoh/Sjo7VpPE4VjLgTax3CdqET4KokUbIKGSWyH3s+IvtzDzFqA==
+"@gitlab/ui@28.1.0":
+ version "28.1.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-28.1.0.tgz#aaec227c03c345632bcb2bc6222ed49a004b2dbd"
+ integrity sha512-V4AH41sRVPp4gOSNt/safVbmsF8RS7H49JuaMWtOkNqfc90ePW+OGyv5ioCYDMYLvk8BaGlmH8f01BuCgOrplA==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"