diff options
Diffstat (limited to 'lib/api/integrations.rb')
-rw-r--r-- | lib/api/integrations.rb | 178 |
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') |