summaryrefslogtreecommitdiff
path: root/lib/api/integrations.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/api/integrations.rb')
-rw-r--r--lib/api/integrations.rb178
1 files changed, 178 insertions, 0 deletions
diff --git a/lib/api/integrations.rb b/lib/api/integrations.rb
new file mode 100644
index 00000000000..926cde340a0
--- /dev/null
+++ b/lib/api/integrations.rb
@@ -0,0 +1,178 @@
+# 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
+ post "#{path}/#{integration_slug.underscore}/trigger" do
+ 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')