Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									08931747cc
								
							
						
					
					
						commit
						8e28b42532
					
				|  | @ -218,26 +218,36 @@ export const isMetaKey = e => e.metaKey || e.ctrlKey || e.altKey || e.shiftKey; | |||
| export const isMetaClick = e => e.metaKey || e.ctrlKey || e.which === 2; | ||||
| 
 | ||||
| export const contentTop = () => { | ||||
|   const isDesktop = breakpointInstance.isDesktop(); | ||||
|   const heightCalculators = [ | ||||
|     () => $('#js-peek').outerHeight(), | ||||
|     () => $('.navbar-gitlab').outerHeight(), | ||||
|     ({ desktop }) => { | ||||
|       const container = document.querySelector('.line-resolve-all-container'); | ||||
|       let size = 0; | ||||
| 
 | ||||
|       if (!desktop && container) { | ||||
|         size = container.offsetHeight; | ||||
|       } | ||||
| 
 | ||||
|       return size; | ||||
|     }, | ||||
|     () => $('.merge-request-tabs').outerHeight(), | ||||
|     () => $('.js-diff-files-changed').outerHeight(), | ||||
|     () => { | ||||
|       const isDesktop = breakpointInstance.isDesktop(); | ||||
|     ({ desktop }) => { | ||||
|       const diffsTabIsActive = window.mrTabs?.currentAction === 'diffs'; | ||||
|       let size; | ||||
| 
 | ||||
|       if (isDesktop && diffsTabIsActive) { | ||||
|       if (desktop && diffsTabIsActive) { | ||||
|         size = $('.diff-file .file-title-flex-parent:visible').outerHeight(); | ||||
|       } | ||||
| 
 | ||||
|       return size; | ||||
|     }, | ||||
|     () => { | ||||
|     ({ desktop }) => { | ||||
|       let size; | ||||
| 
 | ||||
|       if (breakpointInstance.isDesktop()) { | ||||
|       if (desktop) { | ||||
|         size = $('.mr-version-controls').outerHeight(); | ||||
|       } | ||||
| 
 | ||||
|  | @ -246,7 +256,7 @@ export const contentTop = () => { | |||
|   ]; | ||||
| 
 | ||||
|   return heightCalculators.reduce((totalHeight, calculator) => { | ||||
|     return totalHeight + (calculator() || 0); | ||||
|     return totalHeight + (calculator({ desktop: isDesktop }) || 0); | ||||
|   }, 0); | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -23,6 +23,9 @@ module ServiceParams | |||
|     :comment_detail, | ||||
|     :confidential_issues_events, | ||||
|     :confluence_url, | ||||
|     :datadog_site, | ||||
|     :datadog_env, | ||||
|     :datadog_service, | ||||
|     :default_irc_uri, | ||||
|     :device, | ||||
|     :disable_diffs, | ||||
|  |  | |||
|  | @ -147,6 +147,7 @@ class Project < ApplicationRecord | |||
|   # Project services | ||||
|   has_one :alerts_service | ||||
|   has_one :campfire_service | ||||
|   has_one :datadog_service | ||||
|   has_one :discord_service | ||||
|   has_one :drone_ci_service | ||||
|   has_one :emails_on_push_service | ||||
|  | @ -1353,6 +1354,8 @@ class Project < ApplicationRecord | |||
|   end | ||||
| 
 | ||||
|   def disabled_services | ||||
|     return ['datadog'] unless Feature.enabled?(:datadog_ci_integration, self) | ||||
| 
 | ||||
|     [] | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,124 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class DatadogService < Service | ||||
|   DEFAULT_SITE = 'datadoghq.com'.freeze | ||||
|   URL_TEMPLATE = 'https://webhooks-http-intake.logs.%{datadog_site}/v1/input/'.freeze | ||||
|   URL_TEMPLATE_API_KEYS = 'https://app.%{datadog_site}/account/settings#api'.freeze | ||||
|   URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_SITE}/account_management/api-app-keys/".freeze | ||||
| 
 | ||||
|   SUPPORTED_EVENTS = %w[ | ||||
|     pipeline job | ||||
|   ].freeze | ||||
| 
 | ||||
|   prop_accessor :datadog_site, :api_url, :api_key, :datadog_service, :datadog_env | ||||
| 
 | ||||
|   with_options presence: true, if: :activated? do | ||||
|     validates :api_key, format: { with: /\A\w+\z/ } | ||||
|     validates :datadog_site, format: { with: /\A[\w\.]+\z/ }, unless: :api_url | ||||
|     validates :api_url, public_url: true, unless: :datadog_site | ||||
|   end | ||||
| 
 | ||||
|   after_save :compose_service_hook, if: :activated? | ||||
| 
 | ||||
|   def self.supported_events | ||||
|     SUPPORTED_EVENTS | ||||
|   end | ||||
| 
 | ||||
|   def self.default_test_event | ||||
|     'pipeline' | ||||
|   end | ||||
| 
 | ||||
|   def configurable_events | ||||
|     [] # do not allow to opt out of required hooks | ||||
|   end | ||||
| 
 | ||||
|   def title | ||||
|     'Datadog' | ||||
|   end | ||||
| 
 | ||||
|   def description | ||||
|     'Trace your GitLab pipelines with Datadog' | ||||
|   end | ||||
| 
 | ||||
|   def help | ||||
|     nil | ||||
|     # Maybe adding something in the future | ||||
|     # We could link to static help pages as well | ||||
|     # [More information](#{Gitlab::Routing.url_helpers.help_page_url('integration/datadog')})" | ||||
|   end | ||||
| 
 | ||||
|   def self.to_param | ||||
|     'datadog' | ||||
|   end | ||||
| 
 | ||||
|   def fields | ||||
|     [ | ||||
|       { | ||||
|         type: 'text', name: 'datadog_site', | ||||
|         placeholder: DEFAULT_SITE, default: DEFAULT_SITE, | ||||
|         help: 'Choose the Datadog site to send data to. Set to "datadoghq.eu" to send data to the EU site', | ||||
|         required: false | ||||
|       }, | ||||
|       { | ||||
|         type: 'text', name: 'api_url', title: 'Custom URL', | ||||
|         help: '(Advanced) Define the full URL for your Datadog site directly', | ||||
|         required: false | ||||
|       }, | ||||
|       { | ||||
|         type: 'password', name: 'api_key', title: 'API key', | ||||
|         help: "<a href=\"#{api_keys_url}\" target=\"_blank\">API key</a> used for authentication with Datadog", | ||||
|         required: true | ||||
|       }, | ||||
|       { | ||||
|         type: 'text', name: 'datadog_service', title: 'Service', placeholder: 'gitlab-ci', | ||||
|         help: 'Name of this GitLab instance that all data will be tagged with' | ||||
|       }, | ||||
|       { | ||||
|         type: 'text', name: 'datadog_env', title: 'Env', | ||||
|         help: 'The environment tag that traces will be tagged with' | ||||
|       } | ||||
|     ] | ||||
|   end | ||||
| 
 | ||||
|   def compose_service_hook | ||||
|     hook = service_hook || build_service_hook | ||||
|     hook.url = hook_url | ||||
|     hook.save | ||||
|   end | ||||
| 
 | ||||
|   def hook_url | ||||
|     url = api_url.presence || sprintf(URL_TEMPLATE, datadog_site: datadog_site) | ||||
|     url = URI.parse(url) | ||||
|     url.path = File.join(url.path || '/', api_key) | ||||
|     query = { service: datadog_service, env: datadog_env }.compact | ||||
|     url.query = query.to_query unless query.empty? | ||||
|     url.to_s | ||||
|   end | ||||
| 
 | ||||
|   def api_keys_url | ||||
|     return URL_API_KEYS_DOCS unless datadog_site.presence | ||||
| 
 | ||||
|     sprintf(URL_TEMPLATE_API_KEYS, datadog_site: datadog_site) | ||||
|   end | ||||
| 
 | ||||
|   def execute(data) | ||||
|     return if project.disabled_services.include?(to_param) | ||||
| 
 | ||||
|     object_kind = data[:object_kind] | ||||
|     object_kind = 'job' if object_kind == 'build' | ||||
|     return unless supported_events.include?(object_kind) | ||||
| 
 | ||||
|     service_hook.execute(data, "#{object_kind} hook") | ||||
|   end | ||||
| 
 | ||||
|   def test(data) | ||||
|     begin | ||||
|       result = execute(data) | ||||
|       return { success: false, result: result[:message] } if result[:http_status] != 200 | ||||
|     rescue StandardError => error | ||||
|       return { success: false, result: error } | ||||
|     end | ||||
| 
 | ||||
|     { success: true, result: result[:message] } | ||||
|   end | ||||
| end | ||||
|  | @ -40,6 +40,10 @@ class PipelinesEmailService < Service | |||
|     %w[pipeline] | ||||
|   end | ||||
| 
 | ||||
|   def self.default_test_event | ||||
|     'pipeline' | ||||
|   end | ||||
| 
 | ||||
|   def execute(data, force: false) | ||||
|     return unless supported_events.include?(data[:object_kind]) | ||||
|     return unless force || should_pipeline_be_notified?(data) | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ class Service < ApplicationRecord | |||
|   include EachBatch | ||||
| 
 | ||||
|   SERVICE_NAMES = %w[ | ||||
|     alerts asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker discord | ||||
|     alerts asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker datadog discord | ||||
|     drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat hipchat irker jira | ||||
|     mattermost mattermost_slash_commands microsoft_teams packagist pipelines_email | ||||
|     pivotaltracker prometheus pushover redmine slack slack_slash_commands teamcity unify_circuit webex_teams youtrack | ||||
|  | @ -151,6 +151,10 @@ class Service < ApplicationRecord | |||
|     %w[commit push tag_push issue confidential_issue merge_request wiki_page] | ||||
|   end | ||||
| 
 | ||||
|   def self.default_test_event | ||||
|     'push' | ||||
|   end | ||||
| 
 | ||||
|   def self.event_description(event) | ||||
|     ServicesHelper.service_event_description(event) | ||||
|   end | ||||
|  | @ -390,6 +394,10 @@ class Service < ApplicationRecord | |||
|     self.class.supported_events | ||||
|   end | ||||
| 
 | ||||
|   def default_test_event | ||||
|     self.class.default_test_event | ||||
|   end | ||||
| 
 | ||||
|   def execute(data) | ||||
|     # implement inside child | ||||
|   end | ||||
|  |  | |||
|  | @ -16,9 +16,7 @@ module Integrations | |||
| 
 | ||||
|       def data | ||||
|         strong_memoize(:data) do | ||||
|           next pipeline_events_data if integration.is_a?(::PipelinesEmailService) | ||||
| 
 | ||||
|           case event | ||||
|           case event || integration.default_test_event | ||||
|           when 'push', 'tag_push' | ||||
|             push_events_data | ||||
|           when 'note', 'confidential_note' | ||||
|  | @ -37,8 +35,6 @@ module Integrations | |||
|             deployment_events_data | ||||
|           when 'release' | ||||
|             releases_events_data | ||||
|           else | ||||
|             push_events_data | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|  |  | |||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Fix overscroll for MR diffs in mobile view | ||||
| merge_request: 48091 | ||||
| author: | ||||
| type: fixed | ||||
|  | @ -0,0 +1,7 @@ | |||
| --- | ||||
| name: datadog_ci_integration | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46564 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284088 | ||||
| type: development | ||||
| group: group::ecosystem | ||||
| default_enabled: false | ||||
|  | @ -0,0 +1,9 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| ActiveRecord::Tasks::DatabaseTasks.structure_load_flags ||= [] | ||||
| 
 | ||||
| flag = '--single-transaction' | ||||
| 
 | ||||
| unless ActiveRecord::Tasks::DatabaseTasks.structure_load_flags.include?(flag) | ||||
|   ActiveRecord::Tasks::DatabaseTasks.structure_load_flags << flag | ||||
| end | ||||
|  | @ -20593,6 +20593,7 @@ enum ServiceType { | |||
|   CAMPFIRE_SERVICE | ||||
|   CONFLUENCE_SERVICE | ||||
|   CUSTOM_ISSUE_TRACKER_SERVICE | ||||
|   DATADOG_SERVICE | ||||
|   DISCORD_SERVICE | ||||
|   DRONE_CI_SERVICE | ||||
|   EMAILS_ON_PUSH_SERVICE | ||||
|  |  | |||
|  | @ -59807,6 +59807,12 @@ | |||
|               "isDeprecated": false, | ||||
|               "deprecationReason": null | ||||
|             }, | ||||
|             { | ||||
|               "name": "DATADOG_SERVICE", | ||||
|               "description": null, | ||||
|               "isDeprecated": false, | ||||
|               "deprecationReason": null | ||||
|             }, | ||||
|             { | ||||
|               "name": "DISCORD_SERVICE", | ||||
|               "description": null, | ||||
|  |  | |||
|  | @ -4427,6 +4427,7 @@ State of a Sentry error. | |||
| | `CAMPFIRE_SERVICE` |  | | ||||
| | `CONFLUENCE_SERVICE` |  | | ||||
| | `CUSTOM_ISSUE_TRACKER_SERVICE` |  | | ||||
| | `DATADOG_SERVICE` |  | | ||||
| | `DISCORD_SERVICE` |  | | ||||
| | `DRONE_CI_SERVICE` |  | | ||||
| | `EMAILS_ON_PUSH_SERVICE` |  | | ||||
|  |  | |||
|  | @ -304,6 +304,38 @@ module API | |||
|               desc: 'Project URL' | ||||
|             } | ||||
|           ], | ||||
|           'datadog' => [ | ||||
|             { | ||||
|               required: true, | ||||
|               name: :api_key, | ||||
|               type: String, | ||||
|               desc: 'API key used for authentication with Datadog' | ||||
|             }, | ||||
|             { | ||||
|               required: false, | ||||
|               name: :datadog_site, | ||||
|               type: String, | ||||
|               desc: 'Choose the Datadog site to send data to. Set to "datadoghq.eu" to send data to the EU site' | ||||
|             }, | ||||
|             { | ||||
|               required: false, | ||||
|               name: :api_url, | ||||
|               type: String, | ||||
|               desc: '(Advanced) Define the full URL for your Datadog site directly' | ||||
|             }, | ||||
|             { | ||||
|               required: false, | ||||
|               name: :datadog_service, | ||||
|               type: String, | ||||
|               desc: 'Name of this GitLab instance that all data will be tagged with' | ||||
|             }, | ||||
|             { | ||||
|               required: false, | ||||
|               name: :datadog_env, | ||||
|               type: String, | ||||
|               desc: 'The environment tag that traces will be tagged with' | ||||
|             } | ||||
|           ], | ||||
|           'discord' => [ | ||||
|             { | ||||
|               required: true, | ||||
|  | @ -784,6 +816,7 @@ module API | |||
|           ::ConfluenceService, | ||||
|           ::CampfireService, | ||||
|           ::CustomIssueTrackerService, | ||||
|           ::DatadogService, | ||||
|           ::DiscordService, | ||||
|           ::DroneCiService, | ||||
|           ::EmailsOnPushService, | ||||
|  |  | |||
|  | @ -350,6 +350,7 @@ project: | |||
| - services | ||||
| - campfire_service | ||||
| - confluence_service | ||||
| - datadog_service | ||||
| - discord_service | ||||
| - drone_ci_service | ||||
| - emails_on_push_service | ||||
|  |  | |||
|  | @ -0,0 +1,179 @@ | |||
| # frozen_string_literal: true | ||||
| require 'securerandom' | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe DatadogService, :model do | ||||
|   let_it_be(:project) { create(:project) } | ||||
|   let_it_be(:pipeline) { create(:ci_pipeline, project: project) } | ||||
|   let_it_be(:build) { create(:ci_build, project: project) } | ||||
| 
 | ||||
|   let(:active) { true } | ||||
|   let(:dd_site) { 'datadoghq.com' } | ||||
|   let(:default_url) { 'https://webhooks-http-intake.logs.datadoghq.com/v1/input/' } | ||||
|   let(:api_url) { nil } | ||||
|   let(:api_key) { SecureRandom.hex(32) } | ||||
|   let(:dd_env) { 'ci' } | ||||
|   let(:dd_service) { 'awesome-gitlab' } | ||||
| 
 | ||||
|   let(:expected_hook_url) { default_url + api_key + "?env=#{dd_env}&service=#{dd_service}" } | ||||
| 
 | ||||
|   let(:instance) do | ||||
|     described_class.new( | ||||
|       active: active, | ||||
|       project: project, | ||||
|       properties: { | ||||
|         datadog_site: dd_site, | ||||
|         api_url: api_url, | ||||
|         api_key: api_key, | ||||
|         datadog_env: dd_env, | ||||
|         datadog_service: dd_service | ||||
|       } | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   let(:saved_instance) do | ||||
|     instance.save! | ||||
|     instance | ||||
|   end | ||||
| 
 | ||||
|   let(:pipeline_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) } | ||||
|   let(:build_data) { Gitlab::DataBuilder::Build.build(build) } | ||||
| 
 | ||||
|   describe 'associations' do | ||||
|     it { is_expected.to belong_to(:project) } | ||||
|     it { is_expected.to have_one(:service_hook) } | ||||
|   end | ||||
| 
 | ||||
|   describe 'validations' do | ||||
|     subject { instance } | ||||
| 
 | ||||
|     context 'when service is active' do | ||||
|       let(:active) { true } | ||||
| 
 | ||||
|       it { is_expected.to validate_presence_of(:api_key) } | ||||
|       it { is_expected.to allow_value(api_key).for(:api_key) } | ||||
|       it { is_expected.not_to allow_value('87dab2403c9d462 87aec4d9214edb1e').for(:api_key) } | ||||
|       it { is_expected.not_to allow_value('................................').for(:api_key) } | ||||
| 
 | ||||
|       context 'when selecting site' do | ||||
|         let(:dd_site) { 'datadoghq.com' } | ||||
|         let(:api_url) { nil } | ||||
| 
 | ||||
|         it { is_expected.to validate_presence_of(:datadog_site) } | ||||
|         it { is_expected.not_to validate_presence_of(:api_url) } | ||||
|         it { is_expected.not_to allow_value('datadog hq.com').for(:datadog_site) } | ||||
|       end | ||||
| 
 | ||||
|       context 'with custom api_url' do | ||||
|         let(:dd_site) { nil } | ||||
|         let(:api_url) { 'https://webhooks-http-intake.logs.datad0g.com/v1/input/' } | ||||
| 
 | ||||
|         it { is_expected.not_to validate_presence_of(:datadog_site) } | ||||
|         it { is_expected.to validate_presence_of(:api_url) } | ||||
|         it { is_expected.to allow_value(api_url).for(:api_url) } | ||||
|         it { is_expected.not_to allow_value('example.com').for(:api_url) } | ||||
|       end | ||||
| 
 | ||||
|       context 'when missing site and api_url' do | ||||
|         let(:dd_site) { nil } | ||||
|         let(:api_url) { nil } | ||||
| 
 | ||||
|         it { is_expected.not_to be_valid } | ||||
|         it { is_expected.to validate_presence_of(:datadog_site) } | ||||
|         it { is_expected.to validate_presence_of(:api_url) } | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when service is not active' do | ||||
|       let(:active) { false } | ||||
| 
 | ||||
|       it { is_expected.to be_valid } | ||||
|       it { is_expected.not_to validate_presence_of(:api_key) } | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#hook_url' do | ||||
|     subject { instance.hook_url } | ||||
| 
 | ||||
|     context 'with standard site URL' do | ||||
|       it { is_expected.to eq(expected_hook_url) } | ||||
|     end | ||||
| 
 | ||||
|     context 'with custom URL' do | ||||
|       let(:api_url) { 'https://webhooks-http-intake.logs.datad0g.com/v1/input/' } | ||||
| 
 | ||||
|       it { is_expected.to eq(api_url + api_key + "?env=#{dd_env}&service=#{dd_service}") } | ||||
| 
 | ||||
|       context 'blank' do | ||||
|         let(:api_url) { '' } | ||||
| 
 | ||||
|         it { is_expected.to eq(expected_hook_url) } | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'without optional params' do | ||||
|       let(:dd_service) { nil } | ||||
|       let(:dd_env) { nil } | ||||
| 
 | ||||
|       it { is_expected.to eq(default_url + api_key) } | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#api_keys_url' do | ||||
|     subject { instance.api_keys_url } | ||||
| 
 | ||||
|     it { is_expected.to eq("https://app.#{dd_site}/account/settings#api") } | ||||
| 
 | ||||
|     context 'with unset datadog_site' do | ||||
|       let(:dd_site) { nil } | ||||
| 
 | ||||
|       it { is_expected.to eq("https://docs.datadoghq.com/account_management/api-app-keys/") } | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#test' do | ||||
|     context 'when request is succesful' do | ||||
|       subject { saved_instance.test(pipeline_data) } | ||||
| 
 | ||||
|       before do | ||||
|         stub_request(:post, expected_hook_url).to_return(body: 'OK') | ||||
|       end | ||||
|       it { is_expected.to eq({ success: true, result: 'OK' }) } | ||||
|     end | ||||
| 
 | ||||
|     context 'when request fails' do | ||||
|       subject { saved_instance.test(pipeline_data) } | ||||
| 
 | ||||
|       before do | ||||
|         stub_request(:post, expected_hook_url).to_return(body: 'CRASH!!!', status: 500) | ||||
|       end | ||||
|       it { is_expected.to eq({ success: false, result: 'CRASH!!!' }) } | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#execute' do | ||||
|     before do | ||||
|       stub_request(:post, expected_hook_url) | ||||
|       saved_instance.execute(data) | ||||
|     end | ||||
| 
 | ||||
|     context 'with pipeline data' do | ||||
|       let(:data) { pipeline_data } | ||||
|       let(:expected_headers) do | ||||
|         { WebHookService::GITLAB_EVENT_HEADER => 'Pipeline Hook' } | ||||
|       end | ||||
| 
 | ||||
|       it { expect(a_request(:post, expected_hook_url).with(headers: expected_headers)).to have_been_made } | ||||
|     end | ||||
| 
 | ||||
|     context 'with job data' do | ||||
|       let(:data) { build_data } | ||||
|       let(:expected_headers) do | ||||
|         { WebHookService::GITLAB_EVENT_HEADER => 'Job Hook' } | ||||
|       end | ||||
| 
 | ||||
|       it { expect(a_request(:post, expected_hook_url).with(headers: expected_headers)).to have_been_made } | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -5580,6 +5580,26 @@ RSpec.describe Project, factory_default: :keep do | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#disabled_services' do | ||||
|     subject { build(:project).disabled_services } | ||||
| 
 | ||||
|     context 'without datadog_ci_integration' do | ||||
|       before do | ||||
|         stub_feature_flags(datadog_ci_integration: false) | ||||
|       end | ||||
| 
 | ||||
|       it { is_expected.to include('datadog') } | ||||
|     end | ||||
| 
 | ||||
|     context 'with datadog_ci_integration' do | ||||
|       before do | ||||
|         stub_feature_flags(datadog_ci_integration: true) | ||||
|       end | ||||
| 
 | ||||
|       it { is_expected.not_to include('datadog') } | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#find_or_initialize_service' do | ||||
|     it 'avoids N+1 database queries' do | ||||
|       allow(Service).to receive(:available_services_names).and_return(%w[prometheus pushover]) | ||||
|  |  | |||
|  | @ -76,7 +76,9 @@ RSpec.describe API::Services do | |||
| 
 | ||||
|         required_attributes = service_attrs_list.select do |attr| | ||||
|           service_klass.validators_on(attr).any? do |v| | ||||
|             v.class == ActiveRecord::Validations::PresenceValidator | ||||
|             v.class == ActiveRecord::Validations::PresenceValidator && | ||||
|             # exclude presence validators with conditional since those are not really required | ||||
|             ![:if, :unless].any? { |cond| v.options.include?(cond) } | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,6 +16,8 @@ Service.available_services_names.each do |service| | |||
|           hash.merge!(k => 'secrettoken') | ||||
|         elsif service == 'confluence' && k == :confluence_url | ||||
|           hash.merge!(k => 'https://example.atlassian.net/wiki') | ||||
|         elsif service == 'datadog' && k == :datadog_site | ||||
|           hash.merge!(k => 'datadoghq.com') | ||||
|         elsif k =~ /^(.*_url|url|webhook)/ | ||||
|           hash.merge!(k => "http://example.com") | ||||
|         elsif service_klass.method_defined?("#{k}?") | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue