2021-09-30 14:11:31 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
module API
|
|
|
|
class Integrations < ::API::Base
|
|
|
|
feature_category :integrations
|
|
|
|
|
|
|
|
integrations = Helpers::IntegrationsHelpers.integrations
|
|
|
|
integration_classes = Helpers::IntegrationsHelpers.integration_classes
|
|
|
|
|
|
|
|
if Rails.env.development?
|
|
|
|
integrations['mock-ci'] = [
|
|
|
|
{
|
|
|
|
required: true,
|
|
|
|
name: :mock_service_url,
|
|
|
|
type: String,
|
|
|
|
desc: 'URL to the mock integration'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
integrations['mock-deployment'] = []
|
|
|
|
integrations['mock-monitoring'] = []
|
|
|
|
|
|
|
|
integration_classes += Helpers::IntegrationsHelpers.development_integration_classes
|
|
|
|
end
|
|
|
|
|
|
|
|
INTEGRATIONS = integrations.freeze
|
|
|
|
|
|
|
|
integration_classes.each do |integration|
|
|
|
|
event_names = integration.try(:event_names) || next
|
|
|
|
event_names.each do |event_name|
|
|
|
|
INTEGRATIONS[integration.to_param.tr("_", "-")] << {
|
|
|
|
required: false,
|
|
|
|
name: event_name.to_sym,
|
|
|
|
type: String,
|
|
|
|
desc: IntegrationsHelper.integration_event_description(integration, event_name)
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
TRIGGER_INTEGRATIONS = {
|
|
|
|
'mattermost-slash-commands' => [
|
|
|
|
{
|
|
|
|
name: :token,
|
|
|
|
type: String,
|
|
|
|
desc: 'The Mattermost token'
|
|
|
|
}
|
|
|
|
],
|
|
|
|
'slack-slash-commands' => [
|
|
|
|
{
|
|
|
|
name: :token,
|
|
|
|
type: String,
|
|
|
|
desc: 'The Slack token'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}.freeze
|
|
|
|
|
|
|
|
helpers do
|
|
|
|
def integration_attributes(integration)
|
|
|
|
integration.fields.inject([]) do |arr, hash|
|
|
|
|
arr << hash[:name].to_sym
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# The API officially documents only the `:id/integrations` API paths.
|
|
|
|
# We support the older `id:/services` path for backwards-compatibility in API V4.
|
|
|
|
# The support for `:id/services` can be dropped if we create an API V5.
|
|
|
|
[':id/services', ':id/integrations'].each do |path|
|
|
|
|
params do
|
|
|
|
requires :id, type: String, desc: 'The ID of a project'
|
|
|
|
end
|
|
|
|
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
|
|
|
before { authenticate! }
|
|
|
|
before { authorize_admin_project }
|
|
|
|
|
|
|
|
desc 'Get all active project integrations' do
|
|
|
|
success Entities::ProjectIntegrationBasic
|
|
|
|
end
|
|
|
|
get path do
|
|
|
|
integrations = user_project.integrations.active
|
|
|
|
|
|
|
|
present integrations, with: Entities::ProjectIntegrationBasic
|
|
|
|
end
|
|
|
|
|
|
|
|
INTEGRATIONS.each do |slug, settings|
|
|
|
|
desc "Set #{slug} integration for project"
|
|
|
|
params do
|
|
|
|
settings.each do |setting|
|
|
|
|
if setting[:required]
|
|
|
|
requires setting[:name], type: setting[:type], desc: setting[:desc]
|
|
|
|
else
|
|
|
|
optional setting[:name], type: setting[:type], desc: setting[:desc]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
put "#{path}/#{slug}" do
|
|
|
|
integration = user_project.find_or_initialize_integration(slug.underscore)
|
|
|
|
params = declared_params(include_missing: false).merge(active: true)
|
|
|
|
|
|
|
|
if integration.update(params)
|
|
|
|
present integration, with: Entities::ProjectIntegration
|
|
|
|
else
|
|
|
|
render_api_error!('400 Bad Request', 400)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
desc "Delete an integration from a project"
|
|
|
|
params do
|
|
|
|
requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the integration'
|
|
|
|
end
|
|
|
|
delete "#{path}/:slug" do
|
|
|
|
integration = user_project.find_or_initialize_integration(params[:slug].underscore)
|
|
|
|
|
|
|
|
destroy_conditionally!(integration) do
|
|
|
|
attrs = integration_attributes(integration).index_with { nil }.merge(active: false)
|
|
|
|
|
|
|
|
render_api_error!('400 Bad Request', 400) unless integration.update(attrs)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
desc 'Get the integration settings for a project' do
|
|
|
|
success Entities::ProjectIntegration
|
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the integration'
|
|
|
|
end
|
|
|
|
get "#{path}/:slug" do
|
|
|
|
integration = user_project.find_or_initialize_integration(params[:slug].underscore)
|
|
|
|
|
|
|
|
not_found!('Integration') unless integration&.persisted?
|
|
|
|
|
|
|
|
present integration, with: Entities::ProjectIntegration
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
TRIGGER_INTEGRATIONS.each do |integration_slug, settings|
|
|
|
|
helpers do
|
|
|
|
def slash_command_integration(project, integration_slug, params)
|
|
|
|
project.integrations.active.find do |integration|
|
|
|
|
integration.try(:token) == params[:token] && integration.to_param == integration_slug.underscore
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
params do
|
|
|
|
requires :id, type: String, desc: 'The ID of a project'
|
|
|
|
end
|
|
|
|
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
|
|
|
desc "Trigger a slash command for #{integration_slug}" do
|
|
|
|
detail 'Added in GitLab 8.13'
|
|
|
|
end
|
|
|
|
params do
|
|
|
|
settings.each do |setting|
|
|
|
|
requires setting[:name], type: setting[:type], desc: setting[:desc]
|
|
|
|
end
|
|
|
|
end
|
2021-11-01 08:11:46 -04:00
|
|
|
post "#{path}/#{integration_slug.underscore}/trigger", urgency: :low do
|
2021-09-30 14:11:31 -04:00
|
|
|
project = find_project(params[:id])
|
|
|
|
|
|
|
|
# This is not accurate, but done to prevent leakage of the project names
|
|
|
|
not_found!('Integration') unless project
|
|
|
|
|
|
|
|
integration = slash_command_integration(project, integration_slug, params)
|
|
|
|
result = integration.try(:trigger, params)
|
|
|
|
|
|
|
|
if result
|
|
|
|
status result[:status] || 200
|
|
|
|
present result
|
|
|
|
else
|
|
|
|
not_found!('Integration')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
API::Integrations.prepend_mod_with('API::Integrations')
|