diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index 459afc7c31c..f4b001ed1ba 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -393,8 +393,7 @@ db:migrate-from-previous-major-version:
- sed -i -e "s/gem 'google-protobuf', '~> 3.8.0'/gem 'google-protobuf', '~> 3.12'/" Gemfile
- sed -i -e "s/gem 'nokogiri', '~> 1.10.5'/gem 'nokogiri', '~> 1.11.0'/" Gemfile
- sed -i -e "s/gem 'mimemagic', '~> 0.3.2'/gem 'ruby-magic', '~> 0.4.0'/" Gemfile
- - run_timed_command "gem install bundler:1.17.3"
- - run_timed_command "bundle update google-protobuf nokogiri grpc mimemagic bootsnap"
+ - run_timed_command "bundle update --bundler google-protobuf nokogiri grpc mimemagic bootsnap"
- SETUP_DB=false USE_BUNDLE_INSTALL=true bash scripts/prepare_build.sh
- run_timed_command "bundle exec rake db:drop db:create db:structure:load db:migrate db:seed_fu"
- git checkout -f $CI_COMMIT_SHA
diff --git a/app/assets/javascripts/content_editor/components/content_editor.vue b/app/assets/javascripts/content_editor/components/content_editor.vue
index 3a3e5bdfa39..70e6cbe9aca 100644
--- a/app/assets/javascripts/content_editor/components/content_editor.vue
+++ b/app/assets/javascripts/content_editor/components/content_editor.vue
@@ -2,6 +2,7 @@
import { GlAlert } from '@gitlab/ui';
import { EditorContent as TiptapEditorContent } from '@tiptap/vue-2';
import { ContentEditor } from '../services/content_editor';
+import FormattingBubbleMenu from './formatting_bubble_menu.vue';
import TopToolbar from './top_toolbar.vue';
export default {
@@ -9,6 +10,7 @@ export default {
GlAlert,
TiptapEditorContent,
TopToolbar,
+ FormattingBubbleMenu,
},
provide() {
return {
@@ -44,6 +46,7 @@ export default {
:class="{ 'is-focused': contentEditor.tiptapEditor.isFocused }"
>
+
diff --git a/app/assets/javascripts/content_editor/components/formatting_bubble_menu.vue b/app/assets/javascripts/content_editor/components/formatting_bubble_menu.vue
new file mode 100644
index 00000000000..6c00480b87e
--- /dev/null
+++ b/app/assets/javascripts/content_editor/components/formatting_bubble_menu.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/assets/javascripts/content_editor/components/toolbar_button.vue b/app/assets/javascripts/content_editor/components/toolbar_button.vue
index 3445fe3c4ca..cdb877152d4 100644
--- a/app/assets/javascripts/content_editor/components/toolbar_button.vue
+++ b/app/assets/javascripts/content_editor/components/toolbar_button.vue
@@ -29,6 +29,21 @@ export default {
required: false,
default: '',
},
+ variant: {
+ type: String,
+ required: false,
+ default: 'default',
+ },
+ category: {
+ type: String,
+ required: false,
+ default: 'tertiary',
+ },
+ size: {
+ type: String,
+ required: false,
+ default: 'small',
+ },
},
data() {
return {
@@ -55,9 +70,9 @@ export default {
-import Tracking from '~/tracking';
-import { CONTENT_EDITOR_TRACKING_LABEL, TOOLBAR_CONTROL_TRACKING_ACTION } from '../constants';
+import trackUIControl from '../services/track_ui_control';
import Divider from './divider.vue';
import ToolbarButton from './toolbar_button.vue';
import ToolbarImageButton from './toolbar_image_button.vue';
@@ -8,10 +7,6 @@ import ToolbarLinkButton from './toolbar_link_button.vue';
import ToolbarTableButton from './toolbar_table_button.vue';
import ToolbarTextStyleDropdown from './toolbar_text_style_dropdown.vue';
-const trackingMixin = Tracking.mixin({
- label: CONTENT_EDITOR_TRACKING_LABEL,
-});
-
export default {
components: {
ToolbarButton,
@@ -21,13 +16,9 @@ export default {
ToolbarImageButton,
Divider,
},
- mixins: [trackingMixin],
methods: {
- trackToolbarControlExecution({ contentType: property, value }) {
- this.track(TOOLBAR_CONTROL_TRACKING_ACTION, {
- property,
- value,
- });
+ trackToolbarControlExecution({ contentType, value }) {
+ trackUIControl({ property: contentType, value });
},
},
};
@@ -45,6 +36,7 @@ export default {
data-testid="bold"
content-type="bold"
icon-name="bold"
+ class="gl-mx-2"
editor-command="toggleBold"
:label="__('Bold text')"
@execute="trackToolbarControlExecution"
@@ -53,6 +45,7 @@ export default {
data-testid="italic"
content-type="italic"
icon-name="italic"
+ class="gl-mx-2"
editor-command="toggleItalic"
:label="__('Italic text')"
@execute="trackToolbarControlExecution"
@@ -61,6 +54,7 @@ export default {
data-testid="strike"
content-type="strike"
icon-name="strikethrough"
+ class="gl-mx-2"
editor-command="toggleStrike"
:label="__('Strikethrough')"
@execute="trackToolbarControlExecution"
@@ -69,6 +63,7 @@ export default {
data-testid="code"
content-type="code"
icon-name="code"
+ class="gl-mx-2"
editor-command="toggleCode"
:label="__('Code')"
@execute="trackToolbarControlExecution"
@@ -84,6 +79,7 @@ export default {
data-testid="blockquote"
content-type="blockquote"
icon-name="quote"
+ class="gl-mx-2"
editor-command="toggleBlockquote"
:label="__('Insert a quote')"
@execute="trackToolbarControlExecution"
@@ -92,6 +88,7 @@ export default {
data-testid="code-block"
content-type="codeBlock"
icon-name="doc-code"
+ class="gl-mx-2"
editor-command="toggleCodeBlock"
:label="__('Insert a code block')"
@execute="trackToolbarControlExecution"
@@ -100,6 +97,7 @@ export default {
data-testid="bullet-list"
content-type="bulletList"
icon-name="list-bulleted"
+ class="gl-mx-2"
editor-command="toggleBulletList"
:label="__('Add a bullet list')"
@execute="trackToolbarControlExecution"
@@ -108,6 +106,7 @@ export default {
data-testid="ordered-list"
content-type="orderedList"
icon-name="list-numbered"
+ class="gl-mx-2"
editor-command="toggleOrderedList"
:label="__('Add a numbered list')"
@execute="trackToolbarControlExecution"
@@ -116,6 +115,7 @@ export default {
data-testid="horizontal-rule"
content-type="horizontalRule"
icon-name="dash"
+ class="gl-mx-2"
editor-command="setHorizontalRule"
:label="__('Add a horizontal rule')"
@execute="trackToolbarControlExecution"
diff --git a/app/assets/javascripts/content_editor/constants.js b/app/assets/javascripts/content_editor/constants.js
index 7a5f1d3ed1f..f277508f628 100644
--- a/app/assets/javascripts/content_editor/constants.js
+++ b/app/assets/javascripts/content_editor/constants.js
@@ -6,6 +6,7 @@ export const PROVIDE_SERIALIZER_OR_RENDERER_ERROR = s__(
export const CONTENT_EDITOR_TRACKING_LABEL = 'content_editor';
export const TOOLBAR_CONTROL_TRACKING_ACTION = 'execute_toolbar_control';
+export const BUBBLE_MENU_TRACKING_ACTION = 'execute_bubble_menu_control';
export const KEYBOARD_SHORTCUT_TRACKING_ACTION = 'execute_keyboard_shortcut';
export const INPUT_RULE_TRACKING_ACTION = 'execute_input_rule';
@@ -40,3 +41,7 @@ export const TEXT_STYLE_DROPDOWN_ITEMS = [
label: __('Normal text'),
},
];
+
+export const LOADING_CONTENT_EVENT = 'loadingContent';
+export const LOADING_SUCCESS_EVENT = 'loadingSuccess';
+export const LOADING_ERROR_EVENT = 'loadingError';
diff --git a/app/assets/javascripts/content_editor/services/content_editor.js b/app/assets/javascripts/content_editor/services/content_editor.js
index 29553f4c2ca..209160ad80c 100644
--- a/app/assets/javascripts/content_editor/services/content_editor.js
+++ b/app/assets/javascripts/content_editor/services/content_editor.js
@@ -1,8 +1,11 @@
+import eventHubFactory from '~/helpers/event_hub_factory';
+import { LOADING_CONTENT_EVENT, LOADING_SUCCESS_EVENT, LOADING_ERROR_EVENT } from '../constants';
/* eslint-disable no-underscore-dangle */
export class ContentEditor {
constructor({ tiptapEditor, serializer }) {
this._tiptapEditor = tiptapEditor;
this._serializer = serializer;
+ this._eventHub = eventHubFactory();
}
get tiptapEditor() {
@@ -16,12 +19,41 @@ export class ContentEditor {
return doc.childCount === 0 || (doc.childCount === 1 && doc.child(0).childCount === 0);
}
+ once(type, handler) {
+ this._eventHub.$once(type, handler);
+ }
+
+ on(type, handler) {
+ this._eventHub.$on(type, handler);
+ }
+
+ emit(type, params = {}) {
+ this._eventHub.$emit(type, params);
+ }
+
+ off(type, handler) {
+ this._eventHub.$off(type, handler);
+ }
+
+ disposeAllEvents() {
+ this._eventHub.dispose();
+ }
+
async setSerializedContent(serializedContent) {
const { _tiptapEditor: editor, _serializer: serializer } = this;
- editor.commands.setContent(
- await serializer.deserialize({ schema: editor.schema, content: serializedContent }),
- );
+ try {
+ this._eventHub.$emit(LOADING_CONTENT_EVENT);
+ const document = await serializer.deserialize({
+ schema: editor.schema,
+ content: serializedContent,
+ });
+ editor.commands.setContent(document);
+ this._eventHub.$emit(LOADING_SUCCESS_EVENT);
+ } catch (e) {
+ this._eventHub.$emit(LOADING_ERROR_EVENT, e);
+ throw e;
+ }
}
getSerializedContent() {
diff --git a/app/assets/javascripts/content_editor/services/track_ui_control.js b/app/assets/javascripts/content_editor/services/track_ui_control.js
new file mode 100644
index 00000000000..61f130ea861
--- /dev/null
+++ b/app/assets/javascripts/content_editor/services/track_ui_control.js
@@ -0,0 +1,9 @@
+import Tracking from '~/tracking';
+import { CONTENT_EDITOR_TRACKING_LABEL, TOOLBAR_CONTROL_TRACKING_ACTION } from '../constants';
+
+export default ({ action = TOOLBAR_CONTROL_TRACKING_ACTION, property, value } = {}) =>
+ Tracking.event(undefined, action, {
+ label: CONTENT_EDITOR_TRACKING_LABEL,
+ property,
+ value,
+ });
diff --git a/app/controllers/groups/dependency_proxy/application_controller.rb b/app/controllers/groups/dependency_proxy/application_controller.rb
index c6484ffb5f1..fd9db41f748 100644
--- a/app/controllers/groups/dependency_proxy/application_controller.rb
+++ b/app/controllers/groups/dependency_proxy/application_controller.rb
@@ -18,23 +18,14 @@ module Groups
def authenticate_user_from_jwt_token!
return unless dependency_proxy_for_private_groups?
- if Feature.enabled?(:dependency_proxy_deploy_tokens)
- authenticate_with_http_token do |token, _|
- @authentication_result = EMPTY_AUTH_RESULT
+ authenticate_with_http_token do |token, _|
+ @authentication_result = EMPTY_AUTH_RESULT
- found_user = user_from_token(token)
- sign_in(found_user) if found_user.is_a?(User)
- end
-
- request_bearer_token! unless authenticated_user
- else
- authenticate_with_http_token do |token, _|
- user = user_from_token(token)
- sign_in(user) if user
- end
-
- request_bearer_token! unless current_user
+ found_user = user_from_token(token)
+ sign_in(found_user) if found_user.is_a?(User)
end
+
+ request_bearer_token! unless authenticated_user
end
private
@@ -51,7 +42,6 @@ module Groups
def user_from_token(token)
token_payload = ::DependencyProxy::AuthTokenService.decoded_token_payload(token)
- return User.find(token_payload['user_id']) unless Feature.enabled?(:dependency_proxy_deploy_tokens)
if token_payload['user_id']
token_user = User.find(token_payload['user_id'])
diff --git a/app/models/packages/npm.rb b/app/models/packages/npm.rb
new file mode 100644
index 00000000000..e49199d911c
--- /dev/null
+++ b/app/models/packages/npm.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+module Packages
+ module Npm
+ # from "@scope/package-name" return "scope" or nil
+ def self.scope_of(package_name)
+ return unless package_name
+ return unless package_name.starts_with?('@')
+ return unless package_name.include?('/')
+
+ package_name.match(Gitlab::Regex.npm_package_name_regex)&.captures&.first
+ end
+ end
+end
diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb
index d2e4f46898c..010e1835520 100644
--- a/app/models/packages/package.rb
+++ b/app/models/packages/package.rb
@@ -62,7 +62,7 @@ class Packages::Package < ApplicationRecord
validate :valid_conan_package_recipe, if: :conan?
validate :valid_composer_global_name, if: :composer?
- validate :package_already_taken, if: :npm?
+ validate :npm_package_already_taken, if: :npm?
validates :name, format: { with: Gitlab::Regex.conan_recipe_component_regex }, if: :conan?
validates :name, format: { with: Gitlab::Regex.generic_package_name_regex }, if: :generic?
validates :name, format: { with: Gitlab::Regex.helm_package_regex }, if: :helm?
@@ -320,14 +320,22 @@ class Packages::Package < ApplicationRecord
end
end
- def package_already_taken
+ def npm_package_already_taken
return unless project
+ return unless follows_npm_naming_convention?
- if project.package_already_taken?(name)
+ if project.package_already_taken?(name, package_type: :npm)
errors.add(:base, _('Package already exists'))
end
end
+ # https://docs.gitlab.com/ee/user/packages/npm_registry/#package-naming-convention
+ def follows_npm_naming_convention?
+ return false unless project&.root_namespace&.path
+
+ project.root_namespace.path == ::Packages::Npm.scope_of(name)
+ end
+
def unique_debian_package_name
return unless debian_publication&.distribution
diff --git a/app/models/project.rb b/app/models/project.rb
index 0d32138b08c..e0b8698c30b 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2566,12 +2566,15 @@ class Project < ApplicationRecord
[project&.id, root_group&.id]
end
- def package_already_taken?(package_name)
+ def package_already_taken?(package_name, package_type:)
namespace.root_ancestor.all_projects
.joins(:packages)
.where.not(id: id)
- .merge(Packages::Package.default_scoped.with_name(package_name))
- .exists?
+ .merge(
+ Packages::Package.default_scoped
+ .with_name(package_name)
+ .with_package_type(package_type)
+ ).exists?
end
def default_branch_or_main
diff --git a/config/feature_flags/development/dependency_proxy_deploy_tokens.yml b/config/feature_flags/development/dependency_proxy_deploy_tokens.yml
deleted file mode 100644
index f3cb1fc2c18..00000000000
--- a/config/feature_flags/development/dependency_proxy_deploy_tokens.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: dependency_proxy_deploy_tokens
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64363
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334565
-milestone: '14.2'
-type: development
-group: group::package
-default_enabled: false
diff --git a/config/feature_flags/development/multiple_database_metrics.yml b/config/feature_flags/development/multiple_database_metrics.yml
deleted file mode 100644
index 7a700982022..00000000000
--- a/config/feature_flags/development/multiple_database_metrics.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: multiple_database_metrics
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63490
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/333227
-milestone: '14.1'
-type: development
-group: group::sharding
-default_enabled: false
diff --git a/config/metrics/counts_28d/20210216182136_i_testing_test_case_parsed_monthly.yml b/config/metrics/counts_28d/20210216182136_i_testing_test_case_parsed_monthly.yml
index 23a7074e72a..97479d10ff2 100644
--- a/config/metrics/counts_28d/20210216182136_i_testing_test_case_parsed_monthly.yml
+++ b/config/metrics/counts_28d/20210216182136_i_testing_test_case_parsed_monthly.yml
@@ -11,6 +11,10 @@ value_type: number
status: data_available
time_frame: 28d
data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_testing_test_case_parsed
distribution:
- ce
- ee
diff --git a/config/metrics/counts_28d/20210216184140_testing_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184140_testing_total_unique_counts_monthly.yml
index 3e6e920e3ec..b4b800d1e10 100644
--- a/config/metrics/counts_28d/20210216184140_testing_total_unique_counts_monthly.yml
+++ b/config/metrics/counts_28d/20210216184140_testing_total_unique_counts_monthly.yml
@@ -10,6 +10,20 @@ value_type: number
status: removed
time_frame: 28d
data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_testing_test_case_parsed
+ - i_testing_metrics_report_widget_total
+ - i_testing_group_code_coverage_visit_total
+ - i_testing_full_code_quality_report_total
+ - i_testing_web_performance_widget_total
+ - i_testing_group_code_coverage_project_click_total
+ - i_testing_load_performance_widget_total
+ - i_testing_metrics_report_artifact_uploaders
+ - i_testing_summary_widget_total
+ - users_expanding_testing_code_quality_report
+ - users_expanding_testing_accessibility_report
distribution:
- ce
tier:
diff --git a/config/metrics/counts_28d/20210409100451_users_expanding_testing_code_quality_report_monthly.yml b/config/metrics/counts_28d/20210409100451_users_expanding_testing_code_quality_report_monthly.yml
index d993b1a1ce7..a86d69b33fb 100644
--- a/config/metrics/counts_28d/20210409100451_users_expanding_testing_code_quality_report_monthly.yml
+++ b/config/metrics/counts_28d/20210409100451_users_expanding_testing_code_quality_report_monthly.yml
@@ -12,6 +12,10 @@ milestone: '13.11'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57133
time_frame: 28d
data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - users_expanding_testing_code_quality_report
distribution:
- ce
- ee
diff --git a/config/metrics/counts_28d/20210409100628_users_expanding_testing_accessibility_report_monthly.yml b/config/metrics/counts_28d/20210409100628_users_expanding_testing_accessibility_report_monthly.yml
index 01d6512d124..5c8f72e96b9 100644
--- a/config/metrics/counts_28d/20210409100628_users_expanding_testing_accessibility_report_monthly.yml
+++ b/config/metrics/counts_28d/20210409100628_users_expanding_testing_accessibility_report_monthly.yml
@@ -12,6 +12,10 @@ milestone: '13.11'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57133
time_frame: 28d
data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - users_expanding_testing_accessibility_report
distribution:
- ce
- ee
diff --git a/config/metrics/counts_28d/20210413205507_i_testing_summary_widget_total_monthly.yml b/config/metrics/counts_28d/20210413205507_i_testing_summary_widget_total_monthly.yml
index 8e2da49198b..aeaf9615ec4 100644
--- a/config/metrics/counts_28d/20210413205507_i_testing_summary_widget_total_monthly.yml
+++ b/config/metrics/counts_28d/20210413205507_i_testing_summary_widget_total_monthly.yml
@@ -12,6 +12,10 @@ milestone: "13.11"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59316
time_frame: 28d
data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_testing_summary_widget_total
distribution:
- ee
- ce
diff --git a/config/metrics/counts_7d/20210216182134_i_testing_test_case_parsed_weekly.yml b/config/metrics/counts_7d/20210216182134_i_testing_test_case_parsed_weekly.yml
index c7ce2c729e1..6764ce3be94 100644
--- a/config/metrics/counts_7d/20210216182134_i_testing_test_case_parsed_weekly.yml
+++ b/config/metrics/counts_7d/20210216182134_i_testing_test_case_parsed_weekly.yml
@@ -11,6 +11,10 @@ value_type: number
status: data_available
time_frame: 7d
data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_testing_test_case_parsed
distribution:
- ee
- ce
diff --git a/config/metrics/counts_7d/20210409100451_users_expanding_testing_code_quality_report_weekly.yml b/config/metrics/counts_7d/20210409100451_users_expanding_testing_code_quality_report_weekly.yml
index 11aad6e8af1..13cc4ae2498 100644
--- a/config/metrics/counts_7d/20210409100451_users_expanding_testing_code_quality_report_weekly.yml
+++ b/config/metrics/counts_7d/20210409100451_users_expanding_testing_code_quality_report_weekly.yml
@@ -12,6 +12,10 @@ milestone: '13.11'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57133
time_frame: 7d
data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - users_expanding_testing_code_quality_report
distribution:
- ce
- ee
diff --git a/config/metrics/counts_7d/20210409100628_users_expanding_testing_accessibility_report_weekly.yml b/config/metrics/counts_7d/20210409100628_users_expanding_testing_accessibility_report_weekly.yml
index b6929fd771f..e59e3e0bd7c 100644
--- a/config/metrics/counts_7d/20210409100628_users_expanding_testing_accessibility_report_weekly.yml
+++ b/config/metrics/counts_7d/20210409100628_users_expanding_testing_accessibility_report_weekly.yml
@@ -12,6 +12,10 @@ milestone: '13.11'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57133
time_frame: 7d
data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - users_expanding_testing_accessibility_report
distribution:
- ce
- ee
diff --git a/config/metrics/counts_7d/20210413205507_i_testing_summary_widget_total_weekly.yml b/config/metrics/counts_7d/20210413205507_i_testing_summary_widget_total_weekly.yml
index 44597d4910b..847f615e166 100644
--- a/config/metrics/counts_7d/20210413205507_i_testing_summary_widget_total_weekly.yml
+++ b/config/metrics/counts_7d/20210413205507_i_testing_summary_widget_total_weekly.yml
@@ -12,6 +12,10 @@ milestone: "13.11"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59316
time_frame: 7d
data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_testing_summary_widget_total
distribution:
- ee
- ce
diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md
index 7440c5a95d9..6d9418133d8 100644
--- a/doc/administration/monitoring/performance/performance_bar.md
+++ b/doc/administration/monitoring/performance/performance_bar.md
@@ -31,9 +31,9 @@ From left to right, the performance bar displays:
was sent to the read/write primary server. "Replica" means it was sent
to a read-only replica.
- **Config name**: shows up only when the
- `multiple_database_metrics` feature flag is enabled. This is used to
- distinguish between different databases configured for different GitLab
- features. The name shown is the same name used to configure database
+ `GITLAB_MULTIPLE_DATABASE_METRICS` environment variable is set. This is
+ used to distinguish between different databases configured for different
+ GitLab features. The name shown is the same name used to configure database
connections in GitLab.
- **Gitaly calls**: the time taken (in milliseconds) and the total number of
[Gitaly](../../gitaly/index.md) calls. Click to display a modal window with more
diff --git a/lib/api/helpers/packages/npm.rb b/lib/api/helpers/packages/npm.rb
index 2d556f889bf..ce5db52fdbc 100644
--- a/lib/api/helpers/packages/npm.rb
+++ b/lib/api/helpers/packages/npm.rb
@@ -49,28 +49,20 @@ module API
when :project
params[:id]
when :instance
- namespace_path = namespace_path_from_package_name
+ package_name = params[:package_name]
+ namespace_path = ::Packages::Npm.scope_of(package_name)
next unless namespace_path
namespace = Namespace.top_most
.by_path(namespace_path)
next unless namespace
- finder = ::Packages::Npm::PackageFinder.new(params[:package_name], namespace: namespace)
+ finder = ::Packages::Npm::PackageFinder.new(package_name, namespace: namespace)
finder.last&.project_id
end
end
end
-
- # from "@scope/package-name" return "scope" or nil
- def namespace_path_from_package_name
- package_name = params[:package_name]
- return unless package_name.starts_with?('@')
- return unless package_name.include?('/')
-
- package_name.match(Gitlab::Regex.npm_package_name_regex)&.captures&.first
- end
end
end
end
diff --git a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
index 48e877684f6..43ecc4b96d5 100644
--- a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
@@ -27,7 +27,7 @@ code_quality:
}
- docker pull --quiet "$CODE_QUALITY_IMAGE"
- |
- docker run \
+ docker run --rm \
$(propagate_env_vars \
SOURCE_CODE \
TIMEOUT_SECONDS \
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 70e2af69c56..1b25d3c2090 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -163,6 +163,10 @@ module Gitlab
end
end
+ def self.db_config_names
+ ::ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).map(&:name)
+ end
+
def self.db_config_name(ar_connection)
if ar_connection.respond_to?(:pool) &&
ar_connection.pool.respond_to?(:db_config) &&
diff --git a/lib/gitlab/experimentation.rb b/lib/gitlab/experimentation.rb
index 1318d3831a4..99b488a4aa7 100644
--- a/lib/gitlab/experimentation.rb
+++ b/lib/gitlab/experimentation.rb
@@ -42,8 +42,13 @@ module Gitlab
tracking_category: 'Growth::Conversion::Experiment::ContactSalesInApp',
use_backwards_compatible_subject_index: true
},
- remove_known_trial_form_fields: {
- tracking_category: 'Growth::Conversion::Experiment::RemoveKnownTrialFormFields'
+ remove_known_trial_form_fields_welcoming: {
+ tracking_category: 'Growth::Conversion::Experiment::RemoveKnownTrialFormFieldsWelcoming',
+ rollout_strategy: :user
+ },
+ remove_known_trial_form_fields_noneditable: {
+ tracking_category: 'Growth::Conversion::Experiment::RemoveKnownTrialFormFieldsNoneditable',
+ rollout_strategy: :user
},
invite_members_new_dropdown: {
tracking_category: 'Growth::Expansion::Experiment::InviteMembersNewDropdown'
diff --git a/lib/gitlab/git/commit_stats.rb b/lib/gitlab/git/commit_stats.rb
index 8815088d23c..6a7a7032665 100644
--- a/lib/gitlab/git/commit_stats.rb
+++ b/lib/gitlab/git/commit_stats.rb
@@ -14,20 +14,22 @@ module Gitlab
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/323
def initialize(repo, commit)
@id = commit.id
- @additions = 0
- @deletions = 0
- @total = 0
- wrapped_gitaly_errors do
- gitaly_stats(repo, commit)
- end
+ additions, deletions = fetch_stats(repo, commit)
+
+ @additions = additions.to_i
+ @deletions = deletions.to_i
+ @total = @additions + @deletions
end
- def gitaly_stats(repo, commit)
- stats = repo.gitaly_commit_client.commit_stats(@id)
- @additions = stats.additions
- @deletions = stats.deletions
- @total = @additions + @deletions
+ def fetch_stats(repo, commit)
+ Rails.cache.fetch("commit_stats:#{repo.gl_project_path}:#{@id}") do
+ stats = wrapped_gitaly_errors do
+ repo.gitaly_commit_client.commit_stats(@id)
+ end
+
+ [stats.additions, stats.deletions]
+ end
end
end
end
diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb
index 03c8e8b4cfe..5856031aa4f 100644
--- a/lib/gitlab/metrics/subscribers/active_record.rb
+++ b/lib/gitlab/metrics/subscribers/active_record.rb
@@ -8,17 +8,15 @@ module Gitlab
attach_to :active_record
IGNORABLE_SQL = %w{BEGIN COMMIT}.freeze
- DB_COUNTERS = %i{db_count db_write_count db_cached_count}.freeze
+ DB_COUNTERS = %i{count write_count cached_count}.freeze
SQL_COMMANDS_WITH_COMMENTS_REGEX = %r{\A(/\*.*\*/\s)?((?!(.*[^\w'"](DELETE|UPDATE|INSERT INTO)[^\w'"])))(WITH.*)?(SELECT)((?!(FOR UPDATE|FOR SHARE)).)*$}i.freeze
SQL_DURATION_BUCKET = [0.05, 0.1, 0.25].freeze
TRANSACTION_DURATION_BUCKET = [0.1, 0.25, 1].freeze
- DB_LOAD_BALANCING_COUNTERS = %i{
- db_replica_count db_replica_cached_count db_replica_wal_count db_replica_wal_cached_count
- db_primary_count db_primary_cached_count db_primary_wal_count db_primary_wal_cached_count
- }.freeze
- DB_LOAD_BALANCING_DURATIONS = %i{db_primary_duration_s db_replica_duration_s}.freeze
+ DB_LOAD_BALANCING_ROLES = %i{replica primary}.freeze
+ DB_LOAD_BALANCING_COUNTERS = %i{count cached_count wal_count wal_cached_count}.freeze
+ DB_LOAD_BALANCING_DURATIONS = %i{duration_s}.freeze
SQL_WAL_LOCATION_REGEX = /(pg_current_wal_insert_lsn\(\)::text|pg_last_wal_replay_lsn\(\)::text)/.freeze
@@ -40,9 +38,10 @@ module Gitlab
payload = event.payload
return if ignored_query?(payload)
- increment(:db_count)
- increment(:db_cached_count) if cached_query?(payload)
- increment(:db_write_count) unless select_sql_command?(payload)
+ db_config_name = db_config_name(event.payload)
+ increment(:count, db_config_name: db_config_name)
+ increment(:cached_count, db_config_name: db_config_name) if cached_query?(payload)
+ increment(:write_count, db_config_name: db_config_name) unless select_sql_command?(payload)
observe(:gitlab_sql_duration_seconds, event) do
buckets SQL_DURATION_BUCKET
@@ -61,24 +60,17 @@ module Gitlab
return {} unless Gitlab::SafeRequestStore.active?
{}.tap do |payload|
- DB_COUNTERS.each do |counter|
- payload[counter] = Gitlab::SafeRequestStore[counter].to_i
+ db_counter_keys.each do |key|
+ payload[key] = Gitlab::SafeRequestStore[key].to_i
end
if ::Gitlab::SafeRequestStore.active? && ::Gitlab::Database::LoadBalancing.enable?
- DB_LOAD_BALANCING_COUNTERS.each do |counter|
+ load_balancing_metric_counter_keys.each do |counter|
payload[counter] = ::Gitlab::SafeRequestStore[counter].to_i
end
- DB_LOAD_BALANCING_DURATIONS.each do |duration|
- payload[duration] = ::Gitlab::SafeRequestStore[duration].to_f.round(3)
- end
- if Feature.enabled?(:multiple_database_metrics, default_enabled: :yaml)
- ::Gitlab::SafeRequestStore[:duration_by_database]&.each do |name, duration_by_role|
- duration_by_role.each do |db_role, duration|
- payload[:"db_#{db_role}_#{name}_duration_s"] = duration.to_f.round(3)
- end
- end
+ load_balancing_metric_duration_keys.each do |duration|
+ payload[duration] = ::Gitlab::SafeRequestStore[duration].to_f.round(3)
end
end
end
@@ -92,12 +84,15 @@ module Gitlab
def increment_db_role_counters(db_role, payload)
cached = cached_query?(payload)
- increment("db_#{db_role}_count".to_sym)
- increment("db_#{db_role}_cached_count".to_sym) if cached
+
+ db_config_name = db_config_name(payload)
+
+ increment(:count, db_role: db_role, db_config_name: db_config_name)
+ increment(:cached_count, db_role: db_role, db_config_name: db_config_name) if cached
if wal_command?(payload)
- increment("db_#{db_role}_wal_count".to_sym)
- increment("db_#{db_role}_wal_cached_count".to_sym) if cached
+ increment(:wal_count, db_role: db_role, db_config_name: db_config_name)
+ increment(:wal_cached_count, db_role: db_role, db_config_name: db_config_name) if cached
end
end
@@ -109,15 +104,13 @@ module Gitlab
return unless ::Gitlab::SafeRequestStore.active?
duration = event.duration / 1000.0
- duration_key = "db_#{db_role}_duration_s".to_sym
+ duration_key = compose_metric_key(:duration_s, db_role)
::Gitlab::SafeRequestStore[duration_key] = (::Gitlab::SafeRequestStore[duration_key].presence || 0) + duration
# Per database metrics
- name = ::Gitlab::Database.db_config_name(event.payload[:connection])
- ::Gitlab::SafeRequestStore[:duration_by_database] ||= {}
- ::Gitlab::SafeRequestStore[:duration_by_database][name] ||= {}
- ::Gitlab::SafeRequestStore[:duration_by_database][name][db_role] ||= 0
- ::Gitlab::SafeRequestStore[:duration_by_database][name][db_role] += duration
+ db_config_name = db_config_name(event.payload)
+ duration_key = compose_metric_key(:duration_s, db_role, db_config_name)
+ ::Gitlab::SafeRequestStore[duration_key] = (::Gitlab::SafeRequestStore[duration_key].presence || 0) + duration
end
def ignored_query?(payload)
@@ -132,10 +125,25 @@ module Gitlab
payload[:sql].match(SQL_COMMANDS_WITH_COMMENTS_REGEX)
end
- def increment(counter)
- current_transaction&.increment("gitlab_transaction_#{counter}_total".to_sym, 1)
+ def increment(counter, db_config_name:, db_role: nil)
+ log_key = compose_metric_key(counter, db_role)
- Gitlab::SafeRequestStore[counter] = Gitlab::SafeRequestStore[counter].to_i + 1
+ prometheus_key = if db_role
+ :"gitlab_transaction_db_#{db_role}_#{counter}_total"
+ else
+ :"gitlab_transaction_db_#{counter}_total"
+ end
+
+ current_transaction&.increment(prometheus_key, 1)
+ Gitlab::SafeRequestStore[log_key] = Gitlab::SafeRequestStore[log_key].to_i + 1
+
+ # To avoid confusing log keys we only log the db_config_name metrics
+ # when we are also logging the db_role. Otherwise it will be hard to
+ # tell if the log key is referring to a db_role OR a db_config_name.
+ if db_role.present? && db_config_name.present?
+ log_key = compose_metric_key(counter, db_role, db_config_name)
+ Gitlab::SafeRequestStore[log_key] = Gitlab::SafeRequestStore[log_key].to_i + 1
+ end
end
def observe(histogram, event, &block)
@@ -145,6 +153,45 @@ module Gitlab
def current_transaction
::Gitlab::Metrics::WebTransaction.current || ::Gitlab::Metrics::BackgroundTransaction.current
end
+
+ def db_config_name(payload)
+ ::Gitlab::Database.db_config_name(payload[:connection])
+ end
+
+ def self.db_counter_keys
+ DB_COUNTERS.map { |c| compose_metric_key(c) }
+ end
+
+ def self.load_balancing_metric_counter_keys
+ load_balancing_metric_keys(DB_LOAD_BALANCING_COUNTERS)
+ end
+
+ def self.load_balancing_metric_duration_keys
+ load_balancing_metric_keys(DB_LOAD_BALANCING_DURATIONS)
+ end
+
+ def self.load_balancing_metric_keys(metrics)
+ [].tap do |counters|
+ DB_LOAD_BALANCING_ROLES.each do |role|
+ metrics.each do |metric|
+ counters << compose_metric_key(metric, role)
+ next unless ENV['GITLAB_MULTIPLE_DATABASE_METRICS']
+
+ ::Gitlab::Database.db_config_names.each do |config_name|
+ counters << compose_metric_key(metric, role, config_name)
+ end
+ end
+ end
+ end
+ end
+
+ def compose_metric_key(metric, db_role = nil, db_config_name = nil)
+ self.class.compose_metric_key(metric, db_role, db_config_name)
+ end
+
+ def self.compose_metric_key(metric, db_role = nil, db_config_name = nil)
+ [:db, db_role, db_config_name, metric].compact.join("_").to_sym
+ end
end
end
end
diff --git a/lib/peek/views/active_record.rb b/lib/peek/views/active_record.rb
index 6d2c9a86c62..a3fe206c86f 100644
--- a/lib/peek/views/active_record.rb
+++ b/lib/peek/views/active_record.rb
@@ -81,7 +81,7 @@ module Peek
end
def format_call_details(call)
- if Feature.enabled?(:multiple_database_metrics, default_enabled: :yaml)
+ if ENV['GITLAB_MULTIPLE_DATABASE_METRICS']
super
else
super.except(:db_config_name)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 339e6d192ca..84373a7d8df 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -16337,6 +16337,9 @@ msgstr ""
msgid "Hi %{username}!"
msgstr ""
+msgid "Hi%{salutation}, your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information about %{company} to activate your trial."
+msgstr ""
+
msgid "Hide"
msgstr ""
@@ -38248,7 +38251,7 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
-msgid "Your GitLab Ultimate trial will last 30 days after which point you can keep your free GitLab account forever. We just need some additional information to activate your trial."
+msgid "Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
msgid "Your GitLab account has been locked due to an excessive amount of unsuccessful sign in attempts. Your account will automatically unlock in %{duration} or you may click the link below to unlock now."
@@ -40195,5 +40198,8 @@ msgstr ""
msgid "yaml invalid"
msgstr ""
+msgid "your company"
+msgstr ""
+
msgid "your settings"
msgstr ""
diff --git a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
index 0f0fb781512..00c3472bf55 100644
--- a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
+++ b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
@@ -65,82 +65,39 @@ RSpec.describe Groups::DependencyProxyForContainersController do
it { is_expected.to have_gitlab_http_status(:not_found) }
end
- context 'deploy tokens with dependency_proxy_deploy_tokens disabled' do
- before do
- stub_feature_flags(dependency_proxy_deploy_tokens: false)
- end
+ context 'with deploy token from a different group,' do
+ let_it_be(:user) { create(:deploy_token, :group, :dependency_proxy_scopes) }
- context 'with deploy token from a different group,' do
- let_it_be(:user) { create(:deploy_token, :group, :dependency_proxy_scopes) }
-
- it { is_expected.to have_gitlab_http_status(:not_found) }
- end
-
- context 'with revoked deploy token' do
- let_it_be(:user) { create(:deploy_token, :revoked, :group, :dependency_proxy_scopes) }
- let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
-
- it { is_expected.to have_gitlab_http_status(:not_found) }
- end
-
- context 'with expired deploy token' do
- let_it_be(:user) { create(:deploy_token, :expired, :group, :dependency_proxy_scopes) }
- let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
-
- it { is_expected.to have_gitlab_http_status(:not_found) }
- end
-
- context 'with deploy token with insufficient scopes' do
- let_it_be(:user) { create(:deploy_token, :group) }
- let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
-
- it { is_expected.to have_gitlab_http_status(:not_found) }
- end
-
- context 'when a group is not found' do
- before do
- expect(Group).to receive(:find_by_full_path).and_return(nil)
- end
-
- it { is_expected.to have_gitlab_http_status(:not_found) }
- end
+ it { is_expected.to have_gitlab_http_status(:not_found) }
end
- context 'deploy tokens with dependency_proxy_deploy_tokens enabled' do
- context 'with deploy token from a different group,' do
- let_it_be(:user) { create(:deploy_token, :group, :dependency_proxy_scopes) }
+ context 'with revoked deploy token' do
+ let_it_be(:user) { create(:deploy_token, :revoked, :group, :dependency_proxy_scopes) }
+ let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
- it { is_expected.to have_gitlab_http_status(:not_found) }
+ it { is_expected.to have_gitlab_http_status(:unauthorized) }
+ end
+
+ context 'with expired deploy token' do
+ let_it_be(:user) { create(:deploy_token, :expired, :group, :dependency_proxy_scopes) }
+ let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
+
+ it { is_expected.to have_gitlab_http_status(:unauthorized) }
+ end
+
+ context 'with deploy token with insufficient scopes' do
+ let_it_be(:user) { create(:deploy_token, :group) }
+ let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
+
+ it { is_expected.to have_gitlab_http_status(:not_found) }
+ end
+
+ context 'when a group is not found' do
+ before do
+ expect(Group).to receive(:find_by_full_path).and_return(nil)
end
- context 'with revoked deploy token' do
- let_it_be(:user) { create(:deploy_token, :revoked, :group, :dependency_proxy_scopes) }
- let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
-
- it { is_expected.to have_gitlab_http_status(:unauthorized) }
- end
-
- context 'with expired deploy token' do
- let_it_be(:user) { create(:deploy_token, :expired, :group, :dependency_proxy_scopes) }
- let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
-
- it { is_expected.to have_gitlab_http_status(:unauthorized) }
- end
-
- context 'with deploy token with insufficient scopes' do
- let_it_be(:user) { create(:deploy_token, :group) }
- let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
-
- it { is_expected.to have_gitlab_http_status(:not_found) }
- end
-
- context 'when a group is not found' do
- before do
- expect(Group).to receive(:find_by_full_path).and_return(nil)
- end
-
- it { is_expected.to have_gitlab_http_status(:not_found) }
- end
+ it { is_expected.to have_gitlab_http_status(:not_found) }
end
context 'when user is not found' do
@@ -274,25 +231,6 @@ RSpec.describe Groups::DependencyProxyForContainersController do
it_behaves_like 'returning response status', :success
it_behaves_like 'a package tracking event', described_class.name, 'pull_manifest_from_cache'
end
-
- context 'with dependency_proxy_deploy_tokens feature flag disabled' do
- before do
- stub_feature_flags(dependency_proxy_deploy_tokens: false)
- end
-
- it_behaves_like 'a successful manifest pull'
- end
- end
-
- context 'a valid deploy token with dependency_proxy_deploy_tokens feature flag disabled' do
- let_it_be(:user) { create(:deploy_token, :dependency_proxy_scopes, :group) }
- let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
-
- before do
- stub_feature_flags(dependency_proxy_deploy_tokens: false)
- end
-
- it { is_expected.to have_gitlab_http_status(:not_found) }
end
context 'a valid deploy token' do
@@ -395,25 +333,6 @@ RSpec.describe Groups::DependencyProxyForContainersController do
it_behaves_like 'returning response status', :success
it_behaves_like 'a package tracking event', described_class.name, 'pull_blob_from_cache'
end
-
- context 'with dependency_proxy_deploy_tokens feature flag disabled' do
- before do
- stub_feature_flags(dependency_proxy_deploy_tokens: false)
- end
-
- it_behaves_like 'a successful blob pull'
- end
- end
-
- context 'a valid deploy token with dependency_proxy_deploy_tokens feature flag disabled' do
- let_it_be(:user) { create(:deploy_token, :group, :dependency_proxy_scopes) }
- let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) }
-
- before do
- stub_feature_flags(dependency_proxy_deploy_tokens: false)
- end
-
- it { is_expected.to have_gitlab_http_status(:not_found) }
end
context 'a valid deploy token' do
diff --git a/spec/frontend/content_editor/components/__snapshots__/toolbar_button_spec.js.snap b/spec/frontend/content_editor/components/__snapshots__/toolbar_button_spec.js.snap
index 35c02911e27..e508cddd6f9 100644
--- a/spec/frontend/content_editor/components/__snapshots__/toolbar_button_spec.js.snap
+++ b/spec/frontend/content_editor/components/__snapshots__/toolbar_button_spec.js.snap
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`content_editor/components/toolbar_button displays tertiary, small button with a provided label and icon 1`] = `
-"
+"
diff --git a/spec/frontend/content_editor/components/formatting_bubble_menu_spec.js b/spec/frontend/content_editor/components/formatting_bubble_menu_spec.js
new file mode 100644
index 00000000000..e44a7fa4ddb
--- /dev/null
+++ b/spec/frontend/content_editor/components/formatting_bubble_menu_spec.js
@@ -0,0 +1,80 @@
+import { BubbleMenu } from '@tiptap/vue-2';
+import { mockTracking } from 'helpers/tracking_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import FormattingBubbleMenu from '~/content_editor/components/formatting_bubble_menu.vue';
+
+import {
+ BUBBLE_MENU_TRACKING_ACTION,
+ CONTENT_EDITOR_TRACKING_LABEL,
+} from '~/content_editor/constants';
+import { createTestEditor } from '../test_utils';
+
+describe('content_editor/components/top_toolbar', () => {
+ let wrapper;
+ let trackingSpy;
+ let tiptapEditor;
+
+ const buildEditor = () => {
+ tiptapEditor = createTestEditor();
+
+ jest.spyOn(tiptapEditor, 'isActive');
+ };
+
+ const buildWrapper = () => {
+ wrapper = shallowMountExtended(FormattingBubbleMenu, {
+ provide: {
+ tiptapEditor,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ trackingSpy = mockTracking(undefined, null, jest.spyOn);
+ buildEditor();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders bubble menu component', () => {
+ buildWrapper();
+ const bubbleMenu = wrapper.findComponent(BubbleMenu);
+
+ expect(bubbleMenu.props().editor).toBe(tiptapEditor);
+ expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base']);
+ });
+
+ describe.each`
+ testId | controlProps
+ ${'bold'} | ${{ contentType: 'bold', iconName: 'bold', label: 'Bold text', editorCommand: 'toggleBold', size: 'medium', category: 'primary' }}
+ ${'italic'} | ${{ contentType: 'italic', iconName: 'italic', label: 'Italic text', editorCommand: 'toggleItalic', size: 'medium', category: 'primary' }}
+ ${'strike'} | ${{ contentType: 'strike', iconName: 'strikethrough', label: 'Strikethrough', editorCommand: 'toggleStrike', size: 'medium', category: 'primary' }}
+ ${'code'} | ${{ contentType: 'code', iconName: 'code', label: 'Code', editorCommand: 'toggleCode', size: 'medium', category: 'primary' }}
+ `('given a $testId toolbar control', ({ testId, controlProps }) => {
+ beforeEach(() => {
+ buildWrapper();
+ });
+
+ it('renders the toolbar control with the provided properties', () => {
+ expect(wrapper.findByTestId(testId).exists()).toBe(true);
+
+ Object.keys(controlProps).forEach((propName) => {
+ expect(wrapper.findByTestId(testId).props(propName)).toBe(controlProps[propName]);
+ });
+ });
+
+ it('tracks the execution of toolbar controls', () => {
+ const eventData = { contentType: 'italic', value: 1 };
+ const { contentType, value } = eventData;
+
+ wrapper.findByTestId(testId).vm.$emit('execute', eventData);
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, BUBBLE_MENU_TRACKING_ACTION, {
+ label: CONTENT_EDITOR_TRACKING_LABEL,
+ property: contentType,
+ value,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/content_editor/components/toolbar_button_spec.js b/spec/frontend/content_editor/components/toolbar_button_spec.js
index da32f308481..60263c46bdd 100644
--- a/spec/frontend/content_editor/components/toolbar_button_spec.js
+++ b/spec/frontend/content_editor/components/toolbar_button_spec.js
@@ -50,6 +50,24 @@ describe('content_editor/components/toolbar_button', () => {
expect(findButton().html()).toMatchSnapshot();
});
+ it('allows customizing the variant, category, size of the button', () => {
+ const variant = 'danger';
+ const category = 'secondary';
+ const size = 'medium';
+
+ buildWrapper({
+ variant,
+ category,
+ size,
+ });
+
+ expect(findButton().props()).toMatchObject({
+ variant,
+ category,
+ size,
+ });
+ });
+
it.each`
editorState | outcomeDescription | outcome
${{ isActive: true, isFocused: true }} | ${'button is active'} | ${true}
diff --git a/spec/frontend/content_editor/components/top_toolbar_spec.js b/spec/frontend/content_editor/components/top_toolbar_spec.js
index 18faf1930e7..a5df3d73289 100644
--- a/spec/frontend/content_editor/components/top_toolbar_spec.js
+++ b/spec/frontend/content_editor/components/top_toolbar_spec.js
@@ -1,6 +1,5 @@
-import { shallowMount } from '@vue/test-utils';
import { mockTracking } from 'helpers/tracking_helper';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import TopToolbar from '~/content_editor/components/top_toolbar.vue';
import {
TOOLBAR_CONTROL_TRACKING_ACTION,
@@ -12,7 +11,7 @@ describe('content_editor/components/top_toolbar', () => {
let trackingSpy;
const buildWrapper = () => {
- wrapper = extendedWrapper(shallowMount(TopToolbar));
+ wrapper = shallowMountExtended(TopToolbar);
};
beforeEach(() => {
@@ -43,17 +42,17 @@ describe('content_editor/components/top_toolbar', () => {
});
it('renders the toolbar control with the provided properties', () => {
- expect(wrapper.findByTestId(testId).props()).toEqual({
- ...controlProps,
+ expect(wrapper.findByTestId(testId).exists()).toBe(true);
+
+ Object.keys(controlProps).forEach((propName) => {
+ expect(wrapper.findByTestId(testId).props(propName)).toBe(controlProps[propName]);
});
});
- it.each`
- eventData
- ${{ contentType: 'bold' }}
- ${{ contentType: 'blockquote', value: 1 }}
- `('tracks the execution of toolbar controls', ({ eventData }) => {
+ it('tracks the execution of toolbar controls', () => {
+ const eventData = { contentType: 'blockquote', value: 1 };
const { contentType, value } = eventData;
+
wrapper.findByTestId(testId).vm.$emit('execute', eventData);
expect(trackingSpy).toHaveBeenCalledWith(undefined, TOOLBAR_CONTROL_TRACKING_ACTION, {
diff --git a/spec/frontend/content_editor/services/content_editor_spec.js b/spec/frontend/content_editor/services/content_editor_spec.js
new file mode 100644
index 00000000000..8580d3249b9
--- /dev/null
+++ b/spec/frontend/content_editor/services/content_editor_spec.js
@@ -0,0 +1,56 @@
+import {
+ LOADING_CONTENT_EVENT,
+ LOADING_SUCCESS_EVENT,
+ LOADING_ERROR_EVENT,
+} from '~/content_editor/constants';
+import { ContentEditor } from '~/content_editor/services/content_editor';
+
+import { createTestEditor } from '../test_utils';
+
+describe('content_editor/services/content_editor', () => {
+ let contentEditor;
+ let serializer;
+
+ beforeEach(() => {
+ const tiptapEditor = createTestEditor();
+ serializer = { deserialize: jest.fn() };
+ contentEditor = new ContentEditor({ tiptapEditor, serializer });
+ });
+
+ describe('when setSerializedContent succeeds', () => {
+ beforeEach(() => {
+ serializer.deserialize.mockResolvedValueOnce('');
+ });
+
+ it('emits loadingContent and loadingSuccess event', () => {
+ let loadingContentEmitted = false;
+
+ contentEditor.on(LOADING_CONTENT_EVENT, () => {
+ loadingContentEmitted = true;
+ });
+ contentEditor.on(LOADING_SUCCESS_EVENT, () => {
+ expect(loadingContentEmitted).toBe(true);
+ });
+
+ contentEditor.setSerializedContent('**bold text**');
+ });
+ });
+
+ describe('when setSerializedContent fails', () => {
+ const error = 'error';
+
+ beforeEach(() => {
+ serializer.deserialize.mockRejectedValueOnce(error);
+ });
+
+ it('emits loadingError event', async () => {
+ contentEditor.on(LOADING_ERROR_EVENT, (e) => {
+ expect(e).toBe('error');
+ });
+
+ await expect(() => contentEditor.setSerializedContent('**bold text**')).rejects.toEqual(
+ error,
+ );
+ });
+ });
+});
diff --git a/spec/lib/gitlab/git/commit_stats_spec.rb b/spec/lib/gitlab/git/commit_stats_spec.rb
new file mode 100644
index 00000000000..29d3909efec
--- /dev/null
+++ b/spec/lib/gitlab/git/commit_stats_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Gitlab::Git::CommitStats, :seed_helper do
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
+ let(:commit) { Gitlab::Git::Commit.find(repository, SeedRepo::Commit::ID) }
+
+ def verify_stats!
+ stats = described_class.new(repository, commit)
+
+ expect(stats).to have_attributes(
+ additions: eq(11),
+ deletions: eq(6),
+ total: eq(17)
+ )
+ end
+
+ it 'returns commit stats and caches them', :use_clean_rails_redis_caching do
+ expect(repository.gitaly_commit_client).to receive(:commit_stats).with(commit.id).and_call_original
+
+ verify_stats!
+
+ expect(Rails.cache.fetch("commit_stats:group/project:#{commit.id}")).to eq([11, 6])
+
+ expect(repository.gitaly_commit_client).not_to receive(:commit_stats)
+
+ verify_stats!
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
index 4f2d84028cf..a98038cd3f8 100644
--- a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
+++ b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
@@ -294,47 +294,38 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
context 'when load balancing is enabled', :db_load_balancing do
let(:db_config_name) { ::Gitlab::Database.db_config_name(ApplicationRecord.connection) }
+ let(:expected_db_payload_defaults) do
+ metrics =
+ ::Gitlab::Metrics::Subscribers::ActiveRecord.load_balancing_metric_counter_keys +
+ ::Gitlab::Metrics::Subscribers::ActiveRecord.load_balancing_metric_duration_keys +
+ ::Gitlab::Metrics::Subscribers::ActiveRecord.db_counter_keys +
+ [:db_duration_s]
+
+ metrics.each_with_object({}) do |key, result|
+ result[key.to_s] = 0
+ end
+ end
+
let(:expected_end_payload_with_db) do
- expected_end_payload.merge(
+ expected_end_payload.merge(expected_db_payload_defaults).merge(
'db_duration_s' => a_value >= 0.1,
'db_count' => a_value >= 1,
- 'db_cached_count' => 0,
- 'db_write_count' => 0,
- 'db_replica_count' => 0,
- 'db_replica_cached_count' => 0,
- 'db_replica_wal_count' => 0,
+ "db_replica_#{db_config_name}_count" => 0,
'db_replica_duration_s' => a_value >= 0,
'db_primary_count' => a_value >= 1,
- 'db_primary_cached_count' => 0,
- 'db_primary_wal_count' => 0,
+ "db_primary_#{db_config_name}_count" => a_value >= 1,
'db_primary_duration_s' => a_value > 0,
- "db_primary_#{db_config_name}_duration_s" => a_value > 0,
- 'db_primary_wal_cached_count' => 0,
- 'db_replica_wal_cached_count' => 0
+ "db_primary_#{db_config_name}_duration_s" => a_value > 0
)
end
let(:end_payload) do
- start_payload.merge(
+ start_payload.merge(expected_db_payload_defaults).merge(
'message' => 'TestWorker JID-da883554ee4fe414012f5f42: done: 0.0 sec',
'job_status' => 'done',
'duration_s' => 0.0,
'completed_at' => timestamp.to_f,
- 'cpu_s' => 1.111112,
- 'db_duration_s' => 0.0,
- 'db_cached_count' => 0,
- 'db_count' => 0,
- 'db_write_count' => 0,
- 'db_replica_count' => 0,
- 'db_replica_cached_count' => 0,
- 'db_replica_wal_count' => 0,
- 'db_replica_duration_s' => 0,
- 'db_primary_count' => 0,
- 'db_primary_cached_count' => 0,
- 'db_primary_wal_count' => 0,
- 'db_primary_wal_cached_count' => 0,
- 'db_replica_wal_cached_count' => 0,
- 'db_primary_duration_s' => 0
+ 'cpu_s' => 1.111112
)
end
diff --git a/spec/lib/gitlab/usage_data_metrics_spec.rb b/spec/lib/gitlab/usage_data_metrics_spec.rb
index 59ceaeaf6de..476a6e99cfd 100644
--- a/spec/lib/gitlab/usage_data_metrics_spec.rb
+++ b/spec/lib/gitlab/usage_data_metrics_spec.rb
@@ -66,6 +66,16 @@ RSpec.describe Gitlab::UsageDataMetrics do
)
end
+ it 'includes testing monthly and weekly keys' do
+ expect(subject[:redis_hll_counters][:testing]).to include(
+ :i_testing_test_case_parsed_monthly, :i_testing_test_case_parsed_weekly,
+ :users_expanding_testing_code_quality_report_monthly, :users_expanding_testing_code_quality_report_weekly,
+ :users_expanding_testing_accessibility_report_monthly, :users_expanding_testing_accessibility_report_weekly,
+ :i_testing_summary_widget_total_monthly, :i_testing_summary_widget_total_weekly,
+ :testing_total_unique_counts_monthly
+ )
+ end
+
it 'includes source_code monthly and weekly keys' do
expect(subject[:redis_hll_counters][:source_code].keys).to contain_exactly(*[
:wiki_action_monthly, :wiki_action_weekly,
diff --git a/spec/lib/peek/views/active_record_spec.rb b/spec/lib/peek/views/active_record_spec.rb
index b9d977c38e4..6d50922904e 100644
--- a/spec/lib/peek/views/active_record_spec.rb
+++ b/spec/lib/peek/views/active_record_spec.rb
@@ -109,9 +109,9 @@ RSpec.describe Peek::Views::ActiveRecord, :request_store do
)
end
- context 'when the multiple_database_metrics feature flag is disabled' do
+ context 'when the GITLAB_MULTIPLE_DATABASE_METRICS env var is disabled' do
before do
- stub_feature_flags(multiple_database_metrics: false)
+ stub_env('GITLAB_MULTIPLE_DATABASE_METRICS', nil)
end
it 'does not include db_config_name field' do
@@ -191,9 +191,9 @@ RSpec.describe Peek::Views::ActiveRecord, :request_store do
)
end
- context 'when the multiple_database_metrics feature flag is disabled' do
+ context 'when the GITLAB_MULTIPLE_DATABASE_METRICS env var is disabled' do
before do
- stub_feature_flags(multiple_database_metrics: false)
+ stub_env('GITLAB_MULTIPLE_DATABASE_METRICS', nil)
end
it 'does not include db_config_name field' do
diff --git a/spec/models/packages/npm_spec.rb b/spec/models/packages/npm_spec.rb
new file mode 100644
index 00000000000..fa4adadfe06
--- /dev/null
+++ b/spec/models/packages/npm_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Npm do
+ using RSpec::Parameterized::TableSyntax
+
+ describe '.scope_of' do
+ subject { described_class.scope_of(package_name) }
+
+ where(:package_name, :expected_result) do
+ nil | nil
+ 'test' | nil
+ '@test' | nil
+ 'test/package' | nil
+ '@/package' | nil
+ '@test/package' | 'test'
+ '@test/' | nil
+ end
+
+ with_them do
+ it { is_expected.to eq(expected_result) }
+ end
+ end
+end
diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb
index 449e30f9fb7..285c39e7aaf 100644
--- a/spec/models/packages/package_spec.rb
+++ b/spec/models/packages/package_spec.rb
@@ -418,7 +418,7 @@ RSpec.describe Packages::Package, type: :model do
end
end
- describe '#package_already_taken' do
+ describe '#npm_package_already_taken' do
context 'maven package' do
let!(:package) { create(:maven_package) }
@@ -428,6 +428,43 @@ RSpec.describe Packages::Package, type: :model do
expect(new_package).to be_valid
end
end
+
+ context 'npm package' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, namespace: group) }
+ let_it_be(:second_project) { create(:project, namespace: group)}
+
+ let(:package) { build(:npm_package, project: project, name: name) }
+ let(:second_package) { build(:npm_package, project: second_project, name: name, version: '5.0.0') }
+
+ context 'following the naming convention' do
+ let(:name) { "@#{group.path}/test" }
+
+ it 'will allow the first package' do
+ expect(package).to be_valid
+ end
+
+ it 'will not allow npm package with duplicate name' do
+ package.save!
+
+ expect(second_package).not_to be_valid
+ end
+ end
+
+ context 'not following the naming convention' do
+ let(:name) { '@foobar/test' }
+
+ it 'will allow the first package' do
+ expect(package).to be_valid
+ end
+
+ it 'will allow npm package with duplicate name' do
+ package.save!
+
+ expect(second_package).to be_valid
+ end
+ end
+ end
end
context "recipe uniqueness for conan packages" do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 72c45de402e..048ac7ab6d5 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -6839,29 +6839,36 @@ RSpec.describe Project, factory_default: :keep do
end
describe '#package_already_taken?' do
- let(:namespace) { create(:namespace) }
- let(:project) { create(:project, :public, namespace: namespace) }
- let!(:package) { create(:npm_package, project: project, name: "@#{namespace.path}/foo") }
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:project) { create(:project, :public, namespace: namespace) }
+ let_it_be(:package) { create(:npm_package, project: project, name: "@#{namespace.path}/foo") }
context 'no package exists with the same name' do
it 'returns false' do
- result = project.package_already_taken?("@#{namespace.path}/bar")
+ result = project.package_already_taken?("@#{namespace.path}/bar", package_type: :npm)
expect(result).to be false
end
it 'returns false if it is the project that the package belongs to' do
- result = project.package_already_taken?("@#{namespace.path}/foo")
+ result = project.package_already_taken?("@#{namespace.path}/foo", package_type: :npm)
expect(result).to be false
end
end
context 'a package already exists with the same name' do
- let(:alt_project) { create(:project, :public, namespace: namespace) }
+ let_it_be(:alt_project) { create(:project, :public, namespace: namespace) }
it 'returns true' do
- result = alt_project.package_already_taken?("@#{namespace.path}/foo")
+ result = alt_project.package_already_taken?(package.name, package_type: :npm)
expect(result).to be true
end
+
+ context 'for a different package type' do
+ it 'returns false' do
+ result = alt_project.package_already_taken?(package.name, package_type: :nuget)
+ expect(result).to be false
+ end
+ end
end
end
diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb
index 8230061546f..fa7bb911af8 100644
--- a/spec/requests/api/npm_project_packages_spec.rb
+++ b/spec/requests/api/npm_project_packages_spec.rb
@@ -228,6 +228,31 @@ RSpec.describe API::NpmProjectPackages do
it_behaves_like 'handling upload with different authentications'
end
+
+ context 'with an existing package' do
+ let_it_be(:second_project) { create(:project, namespace: namespace) }
+
+ context 'following the naming convention' do
+ let_it_be(:second_package) { create(:npm_package, project: second_project, name: "@#{group.path}/test") }
+
+ let(:package_name) { "@#{group.path}/test" }
+
+ it_behaves_like 'handling invalid record with 400 error'
+ end
+
+ context 'not following the naming convention' do
+ let_it_be(:second_package) { create(:npm_package, project: second_project, name: "@any_scope/test") }
+
+ let(:package_name) { "@any_scope/test" }
+
+ it "uploads the package" do
+ expect { upload_package_with_token(package_name, params) }
+ .to change { project.packages.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
end
context 'package creation fails' do
diff --git a/spec/support/enable_multiple_database_metrics_by_default.rb b/spec/support/enable_multiple_database_metrics_by_default.rb
new file mode 100644
index 00000000000..6eeb4acd3d6
--- /dev/null
+++ b/spec/support/enable_multiple_database_metrics_by_default.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+RSpec.configure do |config|
+ config.before do
+ # Enable this by default in all tests so it behaves like a FF
+ stub_env('GITLAB_MULTIPLE_DATABASE_METRICS', '1')
+ end
+end
diff --git a/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb b/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
index 8b21ae1101e..2df953638ee 100644
--- a/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
+++ b/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb
@@ -1,70 +1,80 @@
# frozen_string_literal: true
RSpec.shared_examples 'store ActiveRecord info in RequestStore' do |db_role|
+ let(:db_config_name) { ::Gitlab::Database.db_config_name(ApplicationRecord.connection) }
+
+ let(:expected_payload_defaults) do
+ metrics =
+ ::Gitlab::Metrics::Subscribers::ActiveRecord.load_balancing_metric_counter_keys +
+ ::Gitlab::Metrics::Subscribers::ActiveRecord.load_balancing_metric_duration_keys +
+ ::Gitlab::Metrics::Subscribers::ActiveRecord.db_counter_keys
+
+ metrics.each_with_object({}) do |key, result|
+ result[key] = 0
+ end
+ end
+
it 'prevents db counters from leaking to the next transaction' do
2.times do
Gitlab::WithRequestStore.with_request_store do
subscriber.sql(event)
- connection = event.payload[:connection]
- if db_role == :primary
- expected = {
- db_count: record_query ? 1 : 0,
- db_write_count: record_write_query ? 1 : 0,
- db_cached_count: record_cached_query ? 1 : 0,
- db_primary_cached_count: record_cached_query ? 1 : 0,
- db_primary_count: record_query ? 1 : 0,
- db_primary_duration_s: record_query ? 0.002 : 0,
- db_replica_cached_count: 0,
- db_replica_count: 0,
- db_replica_duration_s: 0.0,
- db_primary_wal_count: record_wal_query ? 1 : 0,
- db_primary_wal_cached_count: record_wal_query && record_cached_query ? 1 : 0,
- db_replica_wal_cached_count: 0,
- db_replica_wal_count: 0
- }
- expected[:"db_primary_#{::Gitlab::Database.db_config_name(connection)}_duration_s"] = 0.002 if record_query
- elsif db_role == :replica
- expected = {
- db_count: record_query ? 1 : 0,
- db_write_count: record_write_query ? 1 : 0,
- db_cached_count: record_cached_query ? 1 : 0,
- db_primary_cached_count: 0,
- db_primary_count: 0,
- db_primary_duration_s: 0.0,
- db_replica_cached_count: record_cached_query ? 1 : 0,
- db_replica_count: record_query ? 1 : 0,
- db_replica_duration_s: record_query ? 0.002 : 0,
- db_replica_wal_count: record_wal_query ? 1 : 0,
- db_replica_wal_cached_count: record_wal_query && record_cached_query ? 1 : 0,
- db_primary_wal_cached_count: 0,
- db_primary_wal_count: 0
- }
- expected[:"db_replica_#{::Gitlab::Database.db_config_name(connection)}_duration_s"] = 0.002 if record_query
- else
- expected = {
- db_count: record_query ? 1 : 0,
- db_write_count: record_write_query ? 1 : 0,
- db_cached_count: record_cached_query ? 1 : 0
- }
- end
+ expected = if db_role == :primary
+ expected_payload_defaults.merge({
+ db_count: record_query ? 1 : 0,
+ db_write_count: record_write_query ? 1 : 0,
+ db_cached_count: record_cached_query ? 1 : 0,
+ db_primary_cached_count: record_cached_query ? 1 : 0,
+ "db_primary_#{db_config_name}_cached_count": record_cached_query ? 1 : 0,
+ db_primary_count: record_query ? 1 : 0,
+ "db_primary_#{db_config_name}_count": record_query ? 1 : 0,
+ db_primary_duration_s: record_query ? 0.002 : 0,
+ "db_primary_#{db_config_name}_duration_s": record_query ? 0.002 : 0,
+ db_primary_wal_count: record_wal_query ? 1 : 0,
+ "db_primary_#{db_config_name}_wal_count": record_wal_query ? 1 : 0,
+ db_primary_wal_cached_count: record_wal_query && record_cached_query ? 1 : 0,
+ "db_primary_#{db_config_name}_wal_cached_count": record_wal_query && record_cached_query ? 1 : 0
+ })
+ elsif db_role == :replica
+ expected_payload_defaults.merge({
+ db_count: record_query ? 1 : 0,
+ db_write_count: record_write_query ? 1 : 0,
+ db_cached_count: record_cached_query ? 1 : 0,
+ db_replica_cached_count: record_cached_query ? 1 : 0,
+ "db_replica_#{db_config_name}_cached_count": record_cached_query ? 1 : 0,
+ db_replica_count: record_query ? 1 : 0,
+ "db_replica_#{db_config_name}_count": record_query ? 1 : 0,
+ db_replica_duration_s: record_query ? 0.002 : 0,
+ "db_replica_#{db_config_name}_duration_s": record_query ? 0.002 : 0,
+ db_replica_wal_count: record_wal_query ? 1 : 0,
+ "db_replica_#{db_config_name}_wal_count": record_wal_query ? 1 : 0,
+ db_replica_wal_cached_count: record_wal_query && record_cached_query ? 1 : 0,
+ "db_replica_#{db_config_name}_wal_cached_count": record_wal_query && record_cached_query ? 1 : 0
+ })
+ else
+ {
+ db_count: record_query ? 1 : 0,
+ db_write_count: record_write_query ? 1 : 0,
+ db_cached_count: record_cached_query ? 1 : 0
+ }
+ end
expect(described_class.db_counter_payload).to eq(expected)
end
end
end
- context 'when multiple_database_metrics is disabled' do
+ context 'when the GITLAB_MULTIPLE_DATABASE_METRICS env var is disabled' do
before do
- stub_feature_flags(multiple_database_metrics: false)
+ stub_env('GITLAB_MULTIPLE_DATABASE_METRICS', nil)
end
it 'does not include per database metrics' do
Gitlab::WithRequestStore.with_request_store do
subscriber.sql(event)
- connection = event.payload[:connection]
- expect(described_class.db_counter_payload).not_to include(:"db_replica_#{::Gitlab::Database.db_config_name(connection)}_duration_s")
+ expect(described_class.db_counter_payload).not_to include(:"db_replica_#{db_config_name}_duration_s")
+ expect(described_class.db_counter_payload).not_to include(:"db_replica_#{db_config_name}_count")
end
end
end