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:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-01-15 06:10:30 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-01-15 06:10:30 +0300
commitc9bdf919932b7285ef9920bdac955459340da8fe (patch)
treec133d05b5cb003de0c6bb2234afb884f6882ccd9
parent8b75948934667904196aba319aedda25b00f06ff (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/snippets/components/snippet_header.vue7
-rw-r--r--app/graphql/queries/snippet/snippet.query.graphql5
-rw-r--r--app/helpers/application_helper.rb4
-rw-r--r--app/helpers/tab_helper.rb3
-rw-r--r--app/models/project.rb5
-rw-r--r--app/models/project_services/alerts_service.rb84
-rw-r--r--app/models/project_services/alerts_service_data.rb18
-rw-r--r--app/models/service.rb1
-rw-r--r--app/services/alert_management/sync_alert_service_data_service.rb56
-rw-r--r--app/views/projects/services/alerts/_help.html.haml1
-rw-r--r--app/views/projects/services/alerts/_top.html.haml8
-rw-r--r--changelogs/unreleased/262394-show-status-of-snippet-author.yml5
-rw-r--r--changelogs/unreleased/lower-allocations-nav.yml5
-rw-r--r--changelogs/unreleased/sy-remove-alerts-service.yml5
-rw-r--r--db/post_migrate/20210107194543_remove_alerts_service_records.rb19
-rw-r--r--db/schema_migrations/202101071945431
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql1
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json6
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/integration/jira_development_panel.md8
-rw-r--r--doc/user/application_security/dast/index.md4
-rw-r--r--lib/api/helpers/services_helpers.rb2
-rw-r--r--lib/gitlab/tracking.rb4
-rw-r--r--lib/gitlab/tracking/standard_context.rb41
-rw-r--r--lib/gitlab/utils.rb12
-rw-r--r--locale/gitlab.pot8
-rw-r--r--spec/factories/alerts_service_data.rb8
-rw-r--r--spec/factories/services.rb18
-rw-r--r--spec/factories/usage_data.rb4
-rw-r--r--spec/features/projects/services/user_activates_alerts_spec.rb68
-rw-r--r--spec/frontend/snippets/components/snippet_header_spec.js39
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/tracking/standard_context_spec.rb55
-rw-r--r--spec/lib/gitlab/tracking_spec.rb43
-rw-r--r--spec/lib/gitlab/utils_spec.rb17
-rw-r--r--spec/migrations/remove_alerts_service_records_spec.rb30
-rw-r--r--spec/models/project_services/alerts_service_spec.rb110
-rw-r--r--spec/services/alert_management/sync_alert_service_data_service_spec.rb55
38 files changed, 319 insertions, 443 deletions
diff --git a/app/assets/javascripts/snippets/components/snippet_header.vue b/app/assets/javascripts/snippets/components/snippet_header.vue
index e65e55f6595..5ba62908b43 100644
--- a/app/assets/javascripts/snippets/components/snippet_header.vue
+++ b/app/assets/javascripts/snippets/components/snippet_header.vue
@@ -200,6 +200,13 @@ export default {
<gl-avatar :size="24" :src="snippet.author.avatarUrl" />
<span class="bold">{{ snippet.author.name }}</span>
</a>
+ <gl-emoji
+ v-if="snippet.author.status"
+ v-gl-tooltip
+ class="gl-vertical-align-baseline font-size-inherit gl-mr-1"
+ :title="snippet.author.status.message"
+ :data-name="snippet.author.status.emoji"
+ />
</template>
</gl-sprintf>
</div>
diff --git a/app/graphql/queries/snippet/snippet.query.graphql b/app/graphql/queries/snippet/snippet.query.graphql
index 2205dc26642..ebfc135c51c 100644
--- a/app/graphql/queries/snippet/snippet.query.graphql
+++ b/app/graphql/queries/snippet/snippet.query.graphql
@@ -59,6 +59,11 @@ query GetSnippetQuery($ids: [SnippetID!]) {
name
username
webUrl
+ status {
+ __typename
+ emoji
+ message
+ }
}
}
}
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index f2a9a927ded..2a1652cf2ba 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -44,7 +44,7 @@ module ApplicationHelper
# current_controller?('gitlab/application') # => false
def current_controller?(*args)
args.any? do |v|
- v.to_s.downcase == controller.controller_name || v.to_s.downcase == controller.controller_path
+ Gitlab::Utils.safe_downcase!(v.to_s) == controller.controller_name || Gitlab::Utils.safe_downcase!(v.to_s) == controller.controller_path
end
end
@@ -59,7 +59,7 @@ module ApplicationHelper
# current_action?(:create) # => false
# current_action?(:new, :create) # => true
def current_action?(*args)
- args.any? { |v| v.to_s.downcase == action_name }
+ args.any? { |v| Gitlab::Utils.safe_downcase!(v.to_s) == action_name }
end
def admin_section?
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index 6fbe2642056..e81986d4453 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -72,7 +72,8 @@ module TabHelper
# Add our custom class into the html_options, which may or may not exist
# and which may or may not already have a :class key
o = options.delete(:html_options) || {}
- o[:class] = [*o[:class], klass].join(' ').strip
+ o[:class] = [*o[:class], klass].join(' ')
+ o[:class].strip!
if block_given?
content_tag(:li, capture(&block), o)
diff --git a/app/models/project.rb b/app/models/project.rb
index b03dcde9f3f..0905815cec2 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -147,7 +147,6 @@ class Project < ApplicationRecord
has_many :boards
# Project services
- has_one :alerts_service
has_one :campfire_service
has_one :datadog_service
has_one :discord_service
@@ -1357,9 +1356,9 @@ class Project < ApplicationRecord
end
def disabled_services
- return ['datadog'] unless Feature.enabled?(:datadog_ci_integration, self)
+ return %w(datadog alerts) unless Feature.enabled?(:datadog_ci_integration, self)
- []
+ %w(alerts)
end
def find_or_initialize_service(name)
diff --git a/app/models/project_services/alerts_service.rb b/app/models/project_services/alerts_service.rb
index 58d507971ca..4afce0dfe95 100644
--- a/app/models/project_services/alerts_service.rb
+++ b/app/models/project_services/alerts_service.rb
@@ -1,54 +1,10 @@
# frozen_string_literal: true
-require 'securerandom'
-
+# This service is scheduled for removal. All records must
+# be deleted before the class can be removed.
+# https://gitlab.com/groups/gitlab-org/-/epics/5056
class AlertsService < Service
- has_one :data, class_name: 'AlertsServiceData', autosave: true,
- inverse_of: :service, foreign_key: :service_id
-
- attribute :token, :string
- delegate :token, :token=, :token_changed?, :token_was, to: :data
-
- validates :token, presence: true, if: :activated?
-
- before_validation :prevent_token_assignment
- before_validation :ensure_token, if: :activated?
-
- after_save :update_http_integration
-
- def url
- return if instance? || template?
-
- url_helpers.project_alerts_notify_url(project, format: :json)
- end
-
- def json_fields
- super + %w(token)
- end
-
- def editable?
- false
- end
-
- def show_active_box?
- false
- end
-
- def can_test?
- false
- end
-
- def title
- _('Alerts endpoint')
- end
-
- def description
- _('Authorize external services to send alerts to GitLab')
- end
-
- def detailed_description
- description
- end
+ before_save :prevent_save
def self.to_param
'alerts'
@@ -58,33 +14,15 @@ class AlertsService < Service
%w()
end
- def data
- super || build_data
- end
-
private
- def prevent_token_assignment
- self.token = token_was if token.present? && token_changed?
- end
-
- def ensure_token
- self.token = generate_token if token.blank?
- end
-
- def generate_token
- SecureRandom.hex
- end
-
- def url_helpers
- Gitlab::Routing.url_helpers
- end
-
- def update_http_integration
- return unless project_id && type == 'AlertsService'
+ def prevent_save
+ errors.add(:base, _('Alerts endpoint is deprecated and should not be created or modified. Use HTTP Integrations instead.'))
+ log_error('Prevented attempt to save or update deprecated AlertsService')
- AlertManagement::SyncAlertServiceDataService # rubocop: disable CodeReuse/ServiceClass
- .new(self)
- .execute
+ # Stops execution of callbacks and database operation while
+ # preserving expectations of #save (will not raise) & #save! (raises)
+ # https://guides.rubyonrails.org/active_record_callbacks.html#halting-execution
+ throw :abort # rubocop:disable Cop/BanCatchThrow
end
end
diff --git a/app/models/project_services/alerts_service_data.rb b/app/models/project_services/alerts_service_data.rb
deleted file mode 100644
index 827a4ef613a..00000000000
--- a/app/models/project_services/alerts_service_data.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-require 'securerandom'
-
-class AlertsServiceData < ApplicationRecord
- belongs_to :service, class_name: 'AlertsService'
-
- validates :service, presence: true
-
- attr_encrypted :token,
- mode: :per_attribute_iv,
- key: Settings.attr_encrypted_db_key_base_truncated,
- algorithm: 'aes-256-gcm'
-
- def token_changed?
- attribute_changed?(:token)
- end
-end
diff --git a/app/models/service.rb b/app/models/service.rb
index 9f17279d0a3..e5626462dd3 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -19,7 +19,6 @@ class Service < ApplicationRecord
PROJECT_SPECIFIC_SERVICE_NAMES = %w[
jenkins
- alerts
].freeze
# Fake services to help with local development.
diff --git a/app/services/alert_management/sync_alert_service_data_service.rb b/app/services/alert_management/sync_alert_service_data_service.rb
deleted file mode 100644
index 1ba197065c5..00000000000
--- a/app/services/alert_management/sync_alert_service_data_service.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-# frozen_string_literal: true
-
-module AlertManagement
- class SyncAlertServiceDataService
- # @param alert_service [AlertsService]
- def initialize(alert_service)
- @alert_service = alert_service
- end
-
- def execute
- http_integration = find_http_integration
-
- result = if http_integration
- update_integration_data(http_integration)
- else
- create_integration
- end
-
- result ? ServiceResponse.success : ServiceResponse.error(message: 'Update failed')
- end
-
- private
-
- attr_reader :alert_service
-
- def find_http_integration
- AlertManagement::HttpIntegrationsFinder.new(
- alert_service.project,
- endpoint_identifier: ::AlertManagement::HttpIntegration::LEGACY_IDENTIFIER
- )
- .execute
- .first
- end
-
- def create_integration
- new_integration = AlertManagement::HttpIntegration.create(
- project_id: alert_service.project_id,
- name: 'HTTP endpoint',
- endpoint_identifier: AlertManagement::HttpIntegration::LEGACY_IDENTIFIER,
- active: alert_service.active,
- encrypted_token: alert_service.data.encrypted_token,
- encrypted_token_iv: alert_service.data.encrypted_token_iv
- )
-
- new_integration.persisted?
- end
-
- def update_integration_data(http_integration)
- http_integration.update(
- active: alert_service.active,
- encrypted_token: alert_service.data.encrypted_token,
- encrypted_token_iv: alert_service.data.encrypted_token_iv
- )
- end
- end
-end
diff --git a/app/views/projects/services/alerts/_help.html.haml b/app/views/projects/services/alerts/_help.html.haml
deleted file mode 100644
index 7abd198bea5..00000000000
--- a/app/views/projects/services/alerts/_help.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-.js-alerts-service-settings{ data: alerts_settings_data(disabled: true) }
diff --git a/app/views/projects/services/alerts/_top.html.haml b/app/views/projects/services/alerts/_top.html.haml
deleted file mode 100644
index e3bcb6bd3a0..00000000000
--- a/app/views/projects/services/alerts/_top.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-.row
- .col-lg-12
- .gl-alert.gl-alert-info{ role: 'alert' }
- = sprite_icon('information-o', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
- .gl-alert-body
- = _('You can now manage alert endpoint configuration in the Alerts section on the Operations settings page. Fields on this page have been deprecated.')
- .gl-alert-actions
- = link_to _('Visit settings page'), project_settings_operations_path(@project, anchor: 'js-alert-management-settings'), class: 'btn gl-alert-action btn-info new-gl-button'
diff --git a/changelogs/unreleased/262394-show-status-of-snippet-author.yml b/changelogs/unreleased/262394-show-status-of-snippet-author.yml
new file mode 100644
index 00000000000..e7ed85c99ee
--- /dev/null
+++ b/changelogs/unreleased/262394-show-status-of-snippet-author.yml
@@ -0,0 +1,5 @@
+---
+title: Show status of snippet author in header
+merge_request: 51030
+author: Kev @KevSlashNull
+type: fixed
diff --git a/changelogs/unreleased/lower-allocations-nav.yml b/changelogs/unreleased/lower-allocations-nav.yml
new file mode 100644
index 00000000000..f9ea2a20073
--- /dev/null
+++ b/changelogs/unreleased/lower-allocations-nav.yml
@@ -0,0 +1,5 @@
+---
+title: Lower allocations when building nav
+merge_request: 51628
+author:
+type: performance
diff --git a/changelogs/unreleased/sy-remove-alerts-service.yml b/changelogs/unreleased/sy-remove-alerts-service.yml
new file mode 100644
index 00000000000..0ca8f108a38
--- /dev/null
+++ b/changelogs/unreleased/sy-remove-alerts-service.yml
@@ -0,0 +1,5 @@
+---
+title: Remove deprecated generic alert integration in favor of HTTP Integrations
+merge_request: 50913
+author:
+type: removed
diff --git a/db/post_migrate/20210107194543_remove_alerts_service_records.rb b/db/post_migrate/20210107194543_remove_alerts_service_records.rb
new file mode 100644
index 00000000000..51a2f96ac7f
--- /dev/null
+++ b/db/post_migrate/20210107194543_remove_alerts_service_records.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class RemoveAlertsServiceRecords < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ class Service < ActiveRecord::Base
+ self.table_name = 'services'
+ end
+
+ def up
+ Service.delete_by(type: 'AlertsService')
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema_migrations/20210107194543 b/db/schema_migrations/20210107194543
new file mode 100644
index 00000000000..997bdd98b5a
--- /dev/null
+++ b/db/schema_migrations/20210107194543
@@ -0,0 +1 @@
+d72cf1c88a060ccadd9f90cbef5ae7d4ea6a4416a6263d11a870e01b02d1f935 \ No newline at end of file
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 8c27a3b72d9..3c82e19e3c5 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -22981,7 +22981,6 @@ type ServiceEdge {
}
enum ServiceType {
- ALERTS_SERVICE
ASANA_SERVICE
ASSEMBLA_SERVICE
BAMBOO_SERVICE
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 6ae3f9afda7..e872ecf8ad8 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -66649,12 +66649,6 @@
"interfaces": null,
"enumValues": [
{
- "name": "ALERTS_SERVICE",
- "description": null,
- "isDeprecated": false,
- "deprecationReason": null
- },
- {
"name": "ASANA_SERVICE",
"description": null,
"isDeprecated": false,
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 0a732c59832..70b6a29f1ed 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -4921,7 +4921,6 @@ State of a Sentry error.
| Value | Description |
| ----- | ----------- |
-| `ALERTS_SERVICE` | |
| `ASANA_SERVICE` | |
| `ASSEMBLA_SERVICE` | |
| `BAMBOO_SERVICE` | |
diff --git a/doc/integration/jira_development_panel.md b/doc/integration/jira_development_panel.md
index 9eef739c52b..bebb584b125 100644
--- a/doc/integration/jira_development_panel.md
+++ b/doc/integration/jira_development_panel.md
@@ -272,7 +272,13 @@ The GitLab user only needs access when adding a new namespace. For syncing with
![Configure namespace on GitLab Jira App](img/jira_dev_panel_setup_com_3.png)
-After a namespace is added, all future commits, branches, and merge requests of all projects under that namespace are synced to Jira. Past Merge Request data is initially synced. Past branch and commit data cannot be synced at the moment.
+After a namespace is added:
+
+- All future commits, branches, and merge requests of all projects under that namespace
+ are synced to Jira.
+- From GitLab 13.8, past merge request data is synced to Jira.
+
+Support for syncing past branch and commit data [is planned](https://gitlab.com/gitlab-org/gitlab/-/issues/263240).
For more information, see [Usage](#usage).
diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md
index 0f7f490cdcf..395a8702d1b 100644
--- a/doc/user/application_security/dast/index.md
+++ b/doc/user/application_security/dast/index.md
@@ -214,7 +214,7 @@ variables:
DAST_PASSWORD_FIELD: session[password] # the name of password field at the sign-in HTML form
DAST_SUBMIT_FIELD: login # the `id` or `name` of the element that when clicked will submit the login form or the password form of a multi-page login process
DAST_FIRST_SUBMIT_FIELD: next # the `id` or `name` of the element that when clicked will submit the username form of a multi-page login process
- DAST_AUTH_EXCLUDE_URLS: http://example.com/sign-out,http://example.com/sign-out-2 # optional, URLs to skip during the authenticated scan; comma-separated, no spaces in between
+ DAST_EXCLUDE_URLS: http://example.com/sign-out,http://example.com/sign-out-2 # optional, URLs to skip during the authenticated scan; comma-separated, no spaces in between
DAST_AUTH_VALIDATION_URL: http://example.com/loggedin_page # optional, a URL only accessible to logged in users that DAST can use to confirm successful authentication
```
@@ -570,7 +570,7 @@ DAST can be [configured](#customizing-the-dast-settings) using environment varia
| `DAST_PASSWORD_FIELD` | string | The name of password field at the sign-in HTML form. |
| `DAST_SKIP_TARGET_CHECK` | boolean | Set to `true` to prevent DAST from checking that the target is available before scanning. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229067) in GitLab 13.8. |
| `DAST_MASK_HTTP_HEADERS` | string | Comma-separated list of request and response headers to be masked (GitLab 13.1). Must contain **all** headers to be masked. Refer to [list of headers that are masked by default](#hide-sensitive-information). |
-| `DAST_AUTH_EXCLUDE_URLS` | URLs | The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. Not supported for API scans. |
+| `DAST_EXCLUDE_URLS` | URLs | The URLs to skip during the authenticated scan; comma-separated. Regular expression syntax can be used to match multiple URLs. For example, `.*` matches an arbitrary character sequence. Not supported for API scans. In [GitLab 13.7 and earlier](https://gitlab.com/gitlab-org/security-products/dast/-/merge_requests/367), was `DAST_AUTH_EXCLUDE_URLS` (which we plan to support until GitLab 14.0). |
| `DAST_FULL_SCAN_ENABLED` | boolean | Set to `true` to run a [ZAP Full Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Full-Scan) instead of a [ZAP Baseline Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Baseline-Scan). Default: `false` |
| `DAST_FULL_SCAN_DOMAIN_VALIDATION_REQUIRED` | boolean | Set to `true` to require [domain validation](#domain-validation) when running DAST full scans. Not supported for API scans. Default: `false` |
| `DAST_AUTO_UPDATE_ADDONS` | boolean | ZAP add-ons are pinned to specific versions in the DAST Docker image. Set to `true` to download the latest versions when the scan starts. Default: `false` |
diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb
index 9d2fd9978d9..6101a8d307e 100644
--- a/lib/api/helpers/services_helpers.rb
+++ b/lib/api/helpers/services_helpers.rb
@@ -161,7 +161,6 @@ module API
def self.services
{
- 'alerts' => [],
'asana' => [
{
required: true,
@@ -807,7 +806,6 @@ module API
def self.service_classes
[
- ::AlertsService,
::AsanaService,
::AssemblaService,
::BambooService,
diff --git a/lib/gitlab/tracking.rb b/lib/gitlab/tracking.rb
index 618e359211b..ca4afb4c19c 100644
--- a/lib/gitlab/tracking.rb
+++ b/lib/gitlab/tracking.rb
@@ -24,7 +24,9 @@ module Gitlab
Gitlab::CurrentSettings.snowplow_enabled?
end
- def event(category, action, label: nil, property: nil, value: nil, context: nil)
+ def event(category, action, label: nil, property: nil, value: nil, context: [], standard_context: nil)
+ context.push(standard_context.to_context) if standard_context
+
snowplow.event(category, action, label: label, property: property, value: value, context: context)
product_analytics.event(category, action, label: label, property: property, value: value, context: context)
end
diff --git a/lib/gitlab/tracking/standard_context.rb b/lib/gitlab/tracking/standard_context.rb
new file mode 100644
index 00000000000..71dfe27dd5a
--- /dev/null
+++ b/lib/gitlab/tracking/standard_context.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Tracking
+ class StandardContext
+ GITLAB_STANDARD_SCHEMA_URL = 'iglu:com.gitlab/gitlab_standard/jsonschema/1-0-1'.freeze
+
+ def initialize(namespace: nil, project: nil, **data)
+ @namespace = namespace
+ @project = project
+ @data = data
+ end
+
+ def namespace_id
+ namespace&.id
+ end
+
+ def project_id
+ @project&.id
+ end
+
+ def to_context
+ SnowplowTracker::SelfDescribingJson.new(GITLAB_STANDARD_SCHEMA_URL, to_h)
+ end
+
+ private
+
+ def namespace
+ @namespace || @project&.namespace
+ end
+
+ def to_h
+ public_methods(false).each_with_object({}) do |method, hash|
+ next if method == :to_context
+
+ hash[method] = public_send(method) # rubocop:disable GitlabSecurity/PublicSend
+ end.merge(@data)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/utils.rb b/lib/gitlab/utils.rb
index 3df54e74b4f..29f02a5912a 100644
--- a/lib/gitlab/utils.rb
+++ b/lib/gitlab/utils.rb
@@ -174,6 +174,18 @@ module Gitlab
rescue IPAddr::InvalidAddressError
end
+ # A safe alternative to String#downcase!
+ #
+ # This will make copies of frozen strings but downcase unfrozen
+ # strings in place, reducing allocations.
+ def safe_downcase!(str)
+ if str.frozen?
+ str.downcase
+ else
+ str.downcase! || str
+ end
+ end
+
# Converts a string to an Addressable::URI object.
# If the string is not a valid URI, it returns nil.
# Param uri_string should be a String object.
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 4a36a464686..6cc8b4be071 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2760,7 +2760,7 @@ msgstr ""
msgid "Alerts"
msgstr ""
-msgid "Alerts endpoint"
+msgid "Alerts endpoint is deprecated and should not be created or modified. Use HTTP Integrations instead."
msgstr ""
msgid "AlertsIntegrations|Alerts will be created through this integration"
@@ -4151,9 +4151,6 @@ msgstr ""
msgid "Authorize %{user} to use your account?"
msgstr ""
-msgid "Authorize external services to send alerts to GitLab"
-msgstr ""
-
msgid "Authorized %{new_chat_name}"
msgstr ""
@@ -32306,9 +32303,6 @@ msgstr ""
msgid "You can now export your security dashboard to a CSV report."
msgstr ""
-msgid "You can now manage alert endpoint configuration in the Alerts section on the Operations settings page. Fields on this page have been deprecated."
-msgstr ""
-
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
diff --git a/spec/factories/alerts_service_data.rb b/spec/factories/alerts_service_data.rb
deleted file mode 100644
index 2dd1d0a714e..00000000000
--- a/spec/factories/alerts_service_data.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-FactoryBot.define do
- factory :alerts_service_data do
- service { association(:alerts_service) }
- token { SecureRandom.hex }
- end
-end
diff --git a/spec/factories/services.rb b/spec/factories/services.rb
index 44b157014a5..18d3b2d99b7 100644
--- a/spec/factories/services.rb
+++ b/spec/factories/services.rb
@@ -38,24 +38,6 @@ FactoryBot.define do
end
end
- factory :alerts_service do
- active
- project
- type { 'AlertsService' }
-
- trait :active do
- active { true }
- end
-
- trait :inactive do
- active { false }
- end
-
- before(:create) do |service|
- service.data = build(:alerts_service_data, service: service)
- end
- end
-
factory :drone_ci_service do
project
active { true }
diff --git a/spec/factories/usage_data.rb b/spec/factories/usage_data.rb
index f933461a07a..0ed4176109a 100644
--- a/spec/factories/usage_data.rb
+++ b/spec/factories/usage_data.rb
@@ -32,8 +32,8 @@ FactoryBot.define do
create(:service, project: projects[2], type: 'CustomIssueTrackerService', active: true)
create(:project_error_tracking_setting, project: projects[0])
create(:project_error_tracking_setting, project: projects[1], enabled: false)
- create(:alerts_service, project: projects[0])
- create(:alerts_service, :inactive, project: projects[1])
+ create(:service, project: projects[0], type: 'AlertsService', active: true)
+ create(:service, project: projects[1], type: 'AlertsService', active: false)
alert_bot_issues = create_list(:incident, 2, project: projects[0], author: User.alert_bot)
create_list(:incident, 2, project: projects[1], author: User.alert_bot)
issues = create_list(:issue, 4, project: projects[0])
diff --git a/spec/features/projects/services/user_activates_alerts_spec.rb b/spec/features/projects/services/user_activates_alerts_spec.rb
deleted file mode 100644
index e8eddea8ed0..00000000000
--- a/spec/features/projects/services/user_activates_alerts_spec.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'User activates Alerts', :js do
- let_it_be(:project) { create(:project) }
- let_it_be(:user) { create(:user) }
-
- let(:service_name) { 'alerts' }
- let(:service_title) { 'Alerts endpoint' }
-
- before do
- sign_in(user)
- project.add_maintainer(user)
- end
-
- context 'when service is deactivated' do
- it 'user cannot activate service' do
- visit_project_services
-
- expect(page).to have_link(service_title)
- click_link(service_title)
-
- expect(page).to have_callout_message
- expect(page).to have_toggle_active_disabled
- end
- end
-
- context 'when service is activated' do
- let_it_be(:activated_alerts_service) do
- create(:alerts_service, :active, project: project)
- end
-
- before do
- visit_alerts_service
- end
-
- it 'user cannot change settings' do
- expect(page).to have_callout_message
- expect(page).to have_toggle_active_disabled
- expect(page).to have_button_reset_key_disabled
- end
- end
-
- private
-
- def visit_project_services
- visit(project_settings_integrations_path(project))
- end
-
- def visit_alerts_service
- visit(edit_project_service_path(project, service_name))
- end
-
- def have_callout_message
- within('.gl-alert') do
- have_content('You can now manage alert endpoint configuration in the Alerts section on the Operations settings page.')
- end
- end
-
- def have_toggle_active_disabled
- have_selector('#activated .project-feature-toggle.is-disabled')
- end
-
- def have_button_reset_key_disabled
- have_button('Reset key', disabled: true)
- end
-end
diff --git a/spec/frontend/snippets/components/snippet_header_spec.js b/spec/frontend/snippets/components/snippet_header_spec.js
index 5836de1fdbe..93a66db32c6 100644
--- a/spec/frontend/snippets/components/snippet_header_spec.js
+++ b/spec/frontend/snippets/components/snippet_header_spec.js
@@ -17,6 +17,8 @@ describe('Snippet header component', () => {
let err;
const originalRelativeUrlRoot = gon.relative_url_root;
+ const GlEmoji = { template: '<img/>' };
+
function createComponent({
loading = false,
permissions = {},
@@ -47,10 +49,15 @@ describe('Snippet header component', () => {
},
stubs: {
ApolloMutation,
+ GlEmoji,
},
});
}
+ const findAuthorEmoji = () => wrapper.find(GlEmoji);
+ const findAuthoredMessage = () => wrapper.find('[data-testid="authored-message"]').text();
+ const buttonCount = () => wrapper.findAll(GlButton).length;
+
beforeEach(() => {
gon.relative_url_root = '/foo/';
snippet = {
@@ -66,6 +73,7 @@ describe('Snippet header component', () => {
project: null,
author: {
name: 'Thor Odinson',
+ status: null,
},
blobs: [Blob],
createdAt: new Date(differenceInMilliseconds(32 * 24 * 3600 * 1000)).toISOString(),
@@ -100,17 +108,36 @@ describe('Snippet header component', () => {
it('renders a message showing snippet creation date and author', () => {
createComponent();
- const text = wrapper.find('[data-testid="authored-message"]').text();
+ const text = findAuthoredMessage();
expect(text).toContain('Authored 1 month ago by');
expect(text).toContain('Thor Odinson');
});
+ describe('author status', () => {
+ it('is rendered when it is set', () => {
+ snippet.author.status = {
+ message: 'At work',
+ emoji: 'hammer',
+ };
+ createComponent();
+
+ expect(findAuthorEmoji().attributes('title')).toBe(snippet.author.status.message);
+ expect(findAuthorEmoji().attributes('data-name')).toBe(snippet.author.status.emoji);
+ });
+
+ it('is not rendered when the user has no status', () => {
+ createComponent();
+
+ expect(findAuthorEmoji().exists()).toBe(false);
+ });
+ });
+
it('renders a message showing only snippet creation date if author is null', () => {
snippet.author = null;
createComponent();
- const text = wrapper.find('[data-testid="authored-message"]').text();
+ const text = findAuthoredMessage();
expect(text).toBe('Authored 1 month ago');
});
@@ -121,7 +148,7 @@ describe('Snippet header component', () => {
updateSnippet: false,
},
});
- expect(wrapper.findAll(GlButton).length).toEqual(0);
+ expect(buttonCount()).toEqual(0);
createComponent({
permissions: {
@@ -129,7 +156,7 @@ describe('Snippet header component', () => {
updateSnippet: false,
},
});
- expect(wrapper.findAll(GlButton).length).toEqual(1);
+ expect(buttonCount()).toEqual(1);
createComponent({
permissions: {
@@ -137,7 +164,7 @@ describe('Snippet header component', () => {
updateSnippet: true,
},
});
- expect(wrapper.findAll(GlButton).length).toEqual(2);
+ expect(buttonCount()).toEqual(2);
createComponent({
permissions: {
@@ -149,7 +176,7 @@ describe('Snippet header component', () => {
canCreateSnippet: true,
});
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.findAll(GlButton).length).toEqual(3);
+ expect(buttonCount()).toEqual(3);
});
});
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 8798099f9f3..825513bdfc5 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -525,7 +525,6 @@ project:
- designs
- project_aliases
- external_pull_requests
-- alerts_service
- grafana_integration
- remove_source_branch_after_merge
- deleting_user
diff --git a/spec/lib/gitlab/tracking/standard_context_spec.rb b/spec/lib/gitlab/tracking/standard_context_spec.rb
new file mode 100644
index 00000000000..acf7aeb303a
--- /dev/null
+++ b/spec/lib/gitlab/tracking/standard_context_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Tracking::StandardContext do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:namespace) { create(:namespace) }
+
+ let(:snowplow_context) { subject.to_context }
+
+ describe '#to_context' do
+ context 'with no arguments' do
+ it 'creates a Snowplow context with no data' do
+ snowplow_context.to_json[:data].each do |_, v|
+ expect(v).to be_nil
+ end
+ end
+ end
+
+ context 'with extra data' do
+ subject { described_class.new(foo: 'bar') }
+
+ it 'creates a Snowplow context with the given data' do
+ expect(snowplow_context.to_json.dig(:data, :foo)).to eq('bar')
+ end
+ end
+
+ context 'with namespace' do
+ subject { described_class.new(namespace: namespace) }
+
+ it 'creates a Snowplow context using the given data' do
+ expect(snowplow_context.to_json.dig(:data, :namespace_id)).to eq(namespace.id)
+ expect(snowplow_context.to_json.dig(:data, :project_id)).to be_nil
+ end
+ end
+
+ context 'with project' do
+ subject { described_class.new(project: project) }
+
+ it 'creates a Snowplow context using the given data' do
+ expect(snowplow_context.to_json.dig(:data, :namespace_id)).to eq(project.namespace.id)
+ expect(snowplow_context.to_json.dig(:data, :project_id)).to eq(project.id)
+ end
+ end
+
+ context 'with project and namespace' do
+ subject { described_class.new(namespace: namespace, project: project) }
+
+ it 'creates a Snowplow context using the given data' do
+ expect(snowplow_context.to_json.dig(:data, :namespace_id)).to eq(namespace.id)
+ expect(snowplow_context.to_json.dig(:data, :project_id)).to eq(project.id)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/tracking_spec.rb b/spec/lib/gitlab/tracking_spec.rb
index 57882de0974..8f1fd49f4c5 100644
--- a/spec/lib/gitlab/tracking_spec.rb
+++ b/spec/lib/gitlab/tracking_spec.rb
@@ -41,21 +41,42 @@ RSpec.describe Gitlab::Tracking do
allow_any_instance_of(Gitlab::Tracking::Destinations::ProductAnalytics).to receive(:event)
end
- it 'delegates to snowplow destination' do
- expect_any_instance_of(Gitlab::Tracking::Destinations::Snowplow)
- .to receive(:event)
- .with('category', 'action', label: 'label', property: 'property', value: 1.5, context: nil)
+ shared_examples 'delegates to destination' do |klass|
+ context 'with standard context' do
+ it "delegates to #{klass} destination" do
+ expect_any_instance_of(klass).to receive(:event) do |_, category, action, args|
+ expect(category).to eq('category')
+ expect(action).to eq('action')
+ expect(args[:label]).to eq('label')
+ expect(args[:property]).to eq('property')
+ expect(args[:value]).to eq(1.5)
+ expect(args[:context].length).to eq(1)
+ expect(args[:context].first.to_json[:schema]).to eq(Gitlab::Tracking::StandardContext::GITLAB_STANDARD_SCHEMA_URL)
+ expect(args[:context].first.to_json[:data]).to include(foo: 'bar')
+ end
- described_class.event('category', 'action', label: 'label', property: 'property', value: 1.5)
- end
+ described_class.event('category', 'action', label: 'label', property: 'property', value: 1.5,
+ standard_context: Gitlab::Tracking::StandardContext.new(foo: 'bar'))
+ end
+ end
- it 'delegates to ProductAnalytics destination' do
- expect_any_instance_of(Gitlab::Tracking::Destinations::ProductAnalytics)
- .to receive(:event)
- .with('category', 'action', label: 'label', property: 'property', value: 1.5, context: nil)
+ context 'without standard context' do
+ it "delegates to #{klass} destination" do
+ expect_any_instance_of(klass).to receive(:event) do |_, category, action, args|
+ expect(category).to eq('category')
+ expect(action).to eq('action')
+ expect(args[:label]).to eq('label')
+ expect(args[:property]).to eq('property')
+ expect(args[:value]).to eq(1.5)
+ end
- described_class.event('category', 'action', label: 'label', property: 'property', value: 1.5)
+ described_class.event('category', 'action', label: 'label', property: 'property', value: 1.5)
+ end
+ end
end
+
+ include_examples 'delegates to destination', Gitlab::Tracking::Destinations::Snowplow
+ include_examples 'delegates to destination', Gitlab::Tracking::Destinations::ProductAnalytics
end
describe '.self_describing_event' do
diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb
index 36257a0605b..1052d4cbacc 100644
--- a/spec/lib/gitlab/utils_spec.rb
+++ b/spec/lib/gitlab/utils_spec.rb
@@ -392,6 +392,23 @@ RSpec.describe Gitlab::Utils do
end
end
+ describe ".safe_downcase!" do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:str, :result) do
+ "test".freeze | "test"
+ "Test".freeze | "test"
+ "test" | "test"
+ "Test" | "test"
+ end
+
+ with_them do
+ it "downcases the string" do
+ expect(described_class.safe_downcase!(str)).to eq(result)
+ end
+ end
+ end
+
describe '.parse_url' do
it 'returns Addressable::URI object' do
expect(described_class.parse_url('http://gitlab.com')).to be_instance_of(Addressable::URI)
diff --git a/spec/migrations/remove_alerts_service_records_spec.rb b/spec/migrations/remove_alerts_service_records_spec.rb
new file mode 100644
index 00000000000..eaf9f90b445
--- /dev/null
+++ b/spec/migrations/remove_alerts_service_records_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20210107194543_remove_alerts_service_records.rb')
+
+RSpec.describe RemoveAlertsServiceRecords do
+ let(:services) { table(:services) }
+ let(:alerts_service_data) { table(:alerts_service_data) }
+
+ before do
+ 5.times do
+ service = services.create!(type: 'AlertsService')
+ alerts_service_data.create!(service_id: service.id)
+ end
+
+ services.create!(type: 'SomeOtherType')
+ end
+
+ it 'removes services records of type AlertsService and corresponding data', :aggregate_failures do
+ expect(services.count).to eq(6)
+ expect(alerts_service_data.count).to eq(5)
+
+ migrate!
+
+ expect(services.count).to eq(1)
+ expect(services.first.type).to eq('SomeOtherType')
+ expect(services.where(type: 'AlertsService')).to be_empty
+ expect(alerts_service_data.all).to be_empty
+ end
+end
diff --git a/spec/models/project_services/alerts_service_spec.rb b/spec/models/project_services/alerts_service_spec.rb
index db25885c76a..75b91c29914 100644
--- a/spec/models/project_services/alerts_service_spec.rb
+++ b/spec/models/project_services/alerts_service_spec.rb
@@ -2,108 +2,38 @@
require 'spec_helper'
+# AlertsService is stripped down to only required methods
+# to avoid errors loading integration-related pages if
+# records are present.
RSpec.describe AlertsService do
let_it_be(:project) { create(:project) }
- let(:service_params) { { project: project, active: active } }
- let(:active) { true }
- let(:service) { described_class.new(service_params) }
+ subject(:service) { described_class.new(project: project) }
- shared_context 'when active' do
- let(:active) { true }
- end
-
- shared_context 'when inactive' do
- let(:active) { false }
- end
-
- shared_context 'when persisted' do
- before do
- service.save!
- service.reload
- end
- end
-
- describe '#url' do
- include Gitlab::Routing
-
- subject { service.url }
+ it { is_expected.to be_valid }
- it { is_expected.to eq(project_alerts_notify_url(project, format: :json)) }
- end
-
- describe '#json_fields' do
- subject { service.json_fields }
+ describe '#to_param' do
+ subject { service.to_param }
- it { is_expected.to eq(%w(active token)) }
+ it { is_expected.to eq('alerts') }
end
- describe '#as_json' do
- subject { service.as_json(only: service.json_fields) }
+ describe '#supported_events' do
+ subject { service.supported_events }
- it { is_expected.to eq('active' => true, 'token' => nil) }
+ it { is_expected.to be_empty }
end
- describe '#token' do
- shared_context 'reset token' do
- before do
- service.token = ''
- service.valid?
- end
- end
-
- shared_context 'assign token' do |token|
- before do
- service.token = token
- service.valid?
- end
- end
-
- shared_examples 'valid token' do
- it { is_expected.to match(/\A\h{32}\z/) }
- end
-
- shared_examples 'no token' do
- it { is_expected.to be_blank }
- end
-
- subject { service.token }
-
- context 'when active' do
- include_context 'when active'
-
- context 'when resetting' do
- let!(:previous_token) { service.token }
-
- include_context 'reset token'
-
- it_behaves_like 'valid token'
-
- it { is_expected.not_to eq(previous_token) }
- end
-
- context 'when assigning' do
- include_context 'assign token', 'random token'
-
- it_behaves_like 'valid token'
- end
- end
-
- context 'when inactive' do
- include_context 'when inactive'
-
- context 'when resetting' do
- let!(:previous_token) { service.token }
-
- include_context 'reset token'
-
- it_behaves_like 'no token'
- end
- end
+ describe '#save' do
+ it 'prevents records from being created or updated' do
+ expect(Gitlab::ProjectServiceLogger).to receive(:error).with(
+ hash_including(message: 'Prevented attempt to save or update deprecated AlertsService')
+ )
- context 'when persisted' do
- include_context 'when persisted'
+ expect(service.save).to be_falsey
- it_behaves_like 'valid token'
+ expect(service.errors.full_messages).to include(
+ 'Alerts endpoint is deprecated and should not be created or modified. Use HTTP Integrations instead.'
+ )
end
end
end
diff --git a/spec/services/alert_management/sync_alert_service_data_service_spec.rb b/spec/services/alert_management/sync_alert_service_data_service_spec.rb
deleted file mode 100644
index ecec60011db..00000000000
--- a/spec/services/alert_management/sync_alert_service_data_service_spec.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe AlertManagement::SyncAlertServiceDataService do
- let_it_be(:alerts_service) do
- AlertsService.skip_callback(:save, :after, :update_http_integration)
- service = create(:alerts_service, :active)
- AlertsService.set_callback(:save, :after, :update_http_integration)
-
- service
- end
-
- describe '#execute' do
- subject(:execute) { described_class.new(alerts_service).execute }
-
- context 'without http integration' do
- it 'creates the integration' do
- expect { execute }
- .to change { AlertManagement::HttpIntegration.count }.by(1)
- end
-
- it 'returns a success' do
- expect(subject.success?).to eq(true)
- end
- end
-
- context 'existing legacy http integration' do
- let_it_be(:integration) { create(:alert_management_http_integration, :legacy, project: alerts_service.project) }
-
- it 'updates the integration' do
- expect { execute }
- .to change { integration.reload.encrypted_token }.to(alerts_service.data.encrypted_token)
- .and change { integration.encrypted_token_iv }.to(alerts_service.data.encrypted_token_iv)
- end
-
- it 'returns a success' do
- expect(subject.success?).to eq(true)
- end
- end
-
- context 'existing other http integration' do
- let_it_be(:integration) { create(:alert_management_http_integration, project: alerts_service.project) }
-
- it 'creates the integration' do
- expect { execute }
- .to change { AlertManagement::HttpIntegration.count }.by(1)
- end
-
- it 'returns a success' do
- expect(subject.success?).to eq(true)
- end
- end
- end
-end