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 isMetaClick = e => e.metaKey || e.ctrlKey || e.which === 2;
|
||||||
|
|
||||||
export const contentTop = () => {
|
export const contentTop = () => {
|
||||||
|
const isDesktop = breakpointInstance.isDesktop();
|
||||||
const heightCalculators = [
|
const heightCalculators = [
|
||||||
() => $('#js-peek').outerHeight(),
|
() => $('#js-peek').outerHeight(),
|
||||||
() => $('.navbar-gitlab').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(),
|
() => $('.merge-request-tabs').outerHeight(),
|
||||||
() => $('.js-diff-files-changed').outerHeight(),
|
() => $('.js-diff-files-changed').outerHeight(),
|
||||||
() => {
|
({ desktop }) => {
|
||||||
const isDesktop = breakpointInstance.isDesktop();
|
|
||||||
const diffsTabIsActive = window.mrTabs?.currentAction === 'diffs';
|
const diffsTabIsActive = window.mrTabs?.currentAction === 'diffs';
|
||||||
let size;
|
let size;
|
||||||
|
|
||||||
if (isDesktop && diffsTabIsActive) {
|
if (desktop && diffsTabIsActive) {
|
||||||
size = $('.diff-file .file-title-flex-parent:visible').outerHeight();
|
size = $('.diff-file .file-title-flex-parent:visible').outerHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
},
|
},
|
||||||
() => {
|
({ desktop }) => {
|
||||||
let size;
|
let size;
|
||||||
|
|
||||||
if (breakpointInstance.isDesktop()) {
|
if (desktop) {
|
||||||
size = $('.mr-version-controls').outerHeight();
|
size = $('.mr-version-controls').outerHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,7 +256,7 @@ export const contentTop = () => {
|
||||||
];
|
];
|
||||||
|
|
||||||
return heightCalculators.reduce((totalHeight, calculator) => {
|
return heightCalculators.reduce((totalHeight, calculator) => {
|
||||||
return totalHeight + (calculator() || 0);
|
return totalHeight + (calculator({ desktop: isDesktop }) || 0);
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,9 @@ module ServiceParams
|
||||||
:comment_detail,
|
:comment_detail,
|
||||||
:confidential_issues_events,
|
:confidential_issues_events,
|
||||||
:confluence_url,
|
:confluence_url,
|
||||||
|
:datadog_site,
|
||||||
|
:datadog_env,
|
||||||
|
:datadog_service,
|
||||||
:default_irc_uri,
|
:default_irc_uri,
|
||||||
:device,
|
:device,
|
||||||
:disable_diffs,
|
:disable_diffs,
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,7 @@ class Project < ApplicationRecord
|
||||||
# Project services
|
# Project services
|
||||||
has_one :alerts_service
|
has_one :alerts_service
|
||||||
has_one :campfire_service
|
has_one :campfire_service
|
||||||
|
has_one :datadog_service
|
||||||
has_one :discord_service
|
has_one :discord_service
|
||||||
has_one :drone_ci_service
|
has_one :drone_ci_service
|
||||||
has_one :emails_on_push_service
|
has_one :emails_on_push_service
|
||||||
|
|
@ -1353,6 +1354,8 @@ class Project < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def disabled_services
|
def disabled_services
|
||||||
|
return ['datadog'] unless Feature.enabled?(:datadog_ci_integration, self)
|
||||||
|
|
||||||
[]
|
[]
|
||||||
end
|
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]
|
%w[pipeline]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.default_test_event
|
||||||
|
'pipeline'
|
||||||
|
end
|
||||||
|
|
||||||
def execute(data, force: false)
|
def execute(data, force: false)
|
||||||
return unless supported_events.include?(data[:object_kind])
|
return unless supported_events.include?(data[:object_kind])
|
||||||
return unless force || should_pipeline_be_notified?(data)
|
return unless force || should_pipeline_be_notified?(data)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ class Service < ApplicationRecord
|
||||||
include EachBatch
|
include EachBatch
|
||||||
|
|
||||||
SERVICE_NAMES = %w[
|
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
|
drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat hipchat irker jira
|
||||||
mattermost mattermost_slash_commands microsoft_teams packagist pipelines_email
|
mattermost mattermost_slash_commands microsoft_teams packagist pipelines_email
|
||||||
pivotaltracker prometheus pushover redmine slack slack_slash_commands teamcity unify_circuit webex_teams youtrack
|
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]
|
%w[commit push tag_push issue confidential_issue merge_request wiki_page]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.default_test_event
|
||||||
|
'push'
|
||||||
|
end
|
||||||
|
|
||||||
def self.event_description(event)
|
def self.event_description(event)
|
||||||
ServicesHelper.service_event_description(event)
|
ServicesHelper.service_event_description(event)
|
||||||
end
|
end
|
||||||
|
|
@ -390,6 +394,10 @@ class Service < ApplicationRecord
|
||||||
self.class.supported_events
|
self.class.supported_events
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def default_test_event
|
||||||
|
self.class.default_test_event
|
||||||
|
end
|
||||||
|
|
||||||
def execute(data)
|
def execute(data)
|
||||||
# implement inside child
|
# implement inside child
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,7 @@ module Integrations
|
||||||
|
|
||||||
def data
|
def data
|
||||||
strong_memoize(:data) do
|
strong_memoize(:data) do
|
||||||
next pipeline_events_data if integration.is_a?(::PipelinesEmailService)
|
case event || integration.default_test_event
|
||||||
|
|
||||||
case event
|
|
||||||
when 'push', 'tag_push'
|
when 'push', 'tag_push'
|
||||||
push_events_data
|
push_events_data
|
||||||
when 'note', 'confidential_note'
|
when 'note', 'confidential_note'
|
||||||
|
|
@ -37,8 +35,6 @@ module Integrations
|
||||||
deployment_events_data
|
deployment_events_data
|
||||||
when 'release'
|
when 'release'
|
||||||
releases_events_data
|
releases_events_data
|
||||||
else
|
|
||||||
push_events_data
|
|
||||||
end
|
end
|
||||||
end
|
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
|
CAMPFIRE_SERVICE
|
||||||
CONFLUENCE_SERVICE
|
CONFLUENCE_SERVICE
|
||||||
CUSTOM_ISSUE_TRACKER_SERVICE
|
CUSTOM_ISSUE_TRACKER_SERVICE
|
||||||
|
DATADOG_SERVICE
|
||||||
DISCORD_SERVICE
|
DISCORD_SERVICE
|
||||||
DRONE_CI_SERVICE
|
DRONE_CI_SERVICE
|
||||||
EMAILS_ON_PUSH_SERVICE
|
EMAILS_ON_PUSH_SERVICE
|
||||||
|
|
|
||||||
|
|
@ -59807,6 +59807,12 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "DATADOG_SERVICE",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "DISCORD_SERVICE",
|
"name": "DISCORD_SERVICE",
|
||||||
"description": null,
|
"description": null,
|
||||||
|
|
|
||||||
|
|
@ -4427,6 +4427,7 @@ State of a Sentry error.
|
||||||
| `CAMPFIRE_SERVICE` | |
|
| `CAMPFIRE_SERVICE` | |
|
||||||
| `CONFLUENCE_SERVICE` | |
|
| `CONFLUENCE_SERVICE` | |
|
||||||
| `CUSTOM_ISSUE_TRACKER_SERVICE` | |
|
| `CUSTOM_ISSUE_TRACKER_SERVICE` | |
|
||||||
|
| `DATADOG_SERVICE` | |
|
||||||
| `DISCORD_SERVICE` | |
|
| `DISCORD_SERVICE` | |
|
||||||
| `DRONE_CI_SERVICE` | |
|
| `DRONE_CI_SERVICE` | |
|
||||||
| `EMAILS_ON_PUSH_SERVICE` | |
|
| `EMAILS_ON_PUSH_SERVICE` | |
|
||||||
|
|
|
||||||
|
|
@ -304,6 +304,38 @@ module API
|
||||||
desc: 'Project URL'
|
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' => [
|
'discord' => [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
|
|
@ -784,6 +816,7 @@ module API
|
||||||
::ConfluenceService,
|
::ConfluenceService,
|
||||||
::CampfireService,
|
::CampfireService,
|
||||||
::CustomIssueTrackerService,
|
::CustomIssueTrackerService,
|
||||||
|
::DatadogService,
|
||||||
::DiscordService,
|
::DiscordService,
|
||||||
::DroneCiService,
|
::DroneCiService,
|
||||||
::EmailsOnPushService,
|
::EmailsOnPushService,
|
||||||
|
|
|
||||||
|
|
@ -350,6 +350,7 @@ project:
|
||||||
- services
|
- services
|
||||||
- campfire_service
|
- campfire_service
|
||||||
- confluence_service
|
- confluence_service
|
||||||
|
- datadog_service
|
||||||
- discord_service
|
- discord_service
|
||||||
- drone_ci_service
|
- drone_ci_service
|
||||||
- emails_on_push_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
|
||||||
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
|
describe '#find_or_initialize_service' do
|
||||||
it 'avoids N+1 database queries' do
|
it 'avoids N+1 database queries' do
|
||||||
allow(Service).to receive(:available_services_names).and_return(%w[prometheus pushover])
|
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|
|
required_attributes = service_attrs_list.select do |attr|
|
||||||
service_klass.validators_on(attr).any? do |v|
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ Service.available_services_names.each do |service|
|
||||||
hash.merge!(k => 'secrettoken')
|
hash.merge!(k => 'secrettoken')
|
||||||
elsif service == 'confluence' && k == :confluence_url
|
elsif service == 'confluence' && k == :confluence_url
|
||||||
hash.merge!(k => 'https://example.atlassian.net/wiki')
|
hash.merge!(k => 'https://example.atlassian.net/wiki')
|
||||||
|
elsif service == 'datadog' && k == :datadog_site
|
||||||
|
hash.merge!(k => 'datadoghq.com')
|
||||||
elsif k =~ /^(.*_url|url|webhook)/
|
elsif k =~ /^(.*_url|url|webhook)/
|
||||||
hash.merge!(k => "http://example.com")
|
hash.merge!(k => "http://example.com")
|
||||||
elsif service_klass.method_defined?("#{k}?")
|
elsif service_klass.method_defined?("#{k}?")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue