Commit 7b9d46e5 authored by Andy Soiron's avatar Andy Soiron Committed by Luke Duncalfe
parent d4bf8953
......@@ -2,22 +2,22 @@
module API
module Entities
class ProjectService < Entities::ProjectServiceBasic
class ProjectIntegration < Entities::ProjectIntegrationBasic
# Expose serialized properties
expose :properties do |service, options|
expose :properties do |integration, options|
# TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
attributes =
if service.data_fields_present?
service.data_fields.as_json.keys
if integration.data_fields_present?
integration.data_fields.as_json.keys
else
service.properties.keys
integration.properties.keys
end
attributes &= service.api_field_names
attributes &= integration.api_field_names
attributes.each_with_object({}) do |attribute, hash|
hash[attribute] = service.public_send(attribute) # rubocop:disable GitlabSecurity/PublicSend
hash[attribute] = integration.public_send(attribute) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
......
......@@ -2,10 +2,10 @@
module API
module Entities
class ProjectServiceBasic < Grape::Entity
class ProjectIntegrationBasic < Grape::Entity
expose :id, :title
expose :slug do |service|
service.to_param.dasherize
expose :slug do |integration|
integration.to_param.dasherize
end
expose :created_at, :updated_at, :active
expose :commit_events, :push_events, :issues_events, :confidential_issues_events
......
......@@ -35,7 +35,7 @@ module API
end
end
TRIGGER_SERVICES = {
TRIGGER_INTEGRATIONS = {
'mattermost-slash-commands' => [
{
name: :token,
......@@ -60,24 +60,24 @@ module API
before { authorize_admin_project }
helpers do
def service_attributes(service)
service.fields.inject([]) do |arr, hash|
def integration_attributes(integration)
integration.fields.inject([]) do |arr, hash|
arr << hash[:name].to_sym
end
end
end
desc 'Get all active project services' do
success Entities::ProjectServiceBasic
desc 'Get all active project integrations' do
success Entities::ProjectIntegrationBasic
end
get ":id/services" do
services = user_project.integrations.active
integrations = user_project.integrations.active
present services, with: Entities::ProjectServiceBasic
present integrations, with: Entities::ProjectIntegrationBasic
end
INTEGRATIONS.each do |slug, settings|
desc "Set #{slug} service for project"
desc "Set #{slug} integration for project"
params do
settings.each do |setting|
if setting[:required]
......@@ -92,7 +92,7 @@ module API
params = declared_params(include_missing: false).merge(active: true)
if integration.update(params)
present integration, with: Entities::ProjectService
present integration, with: Entities::ProjectIntegration
else
render_api_error!('400 Bad Request', 400)
end
......@@ -107,14 +107,14 @@ module API
integration = user_project.find_or_initialize_integration(params[:slug].underscore)
destroy_conditionally!(integration) do
attrs = service_attributes(integration).index_with { nil }.merge(active: false)
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::ProjectService
success Entities::ProjectIntegration
end
params do
requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the service'
......@@ -124,15 +124,15 @@ module API
not_found!('Service') unless integration&.persisted?
present integration, with: Entities::ProjectService
present integration, with: Entities::ProjectIntegration
end
end
TRIGGER_SERVICES.each do |service_slug, settings|
TRIGGER_INTEGRATIONS.each do |integration_slug, settings|
helpers do
def slash_command_service(project, service_slug, params)
project.integrations.active.find do |service|
service.try(:token) == params[:token] && service.to_param == service_slug.underscore
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
......@@ -141,7 +141,7 @@ module API
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 #{service_slug}" do
desc "Trigger a slash command for #{integration_slug}" do
detail 'Added in GitLab 8.13'
end
params do
......@@ -149,14 +149,14 @@ module API
requires setting[:name], type: setting[:type], desc: setting[:desc]
end
end
post ":id/services/#{service_slug.underscore}/trigger" do
post ":id/services/#{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!('Service') unless project
service = slash_command_service(project, service_slug, params)
result = service.try(:trigger, params)
integration = slash_command_integration(project, integration_slug, params)
result = integration.try(:trigger, params)
if result
status result[:status] || 200
......
......@@ -31,7 +31,7 @@ RSpec.describe API::Services do
it "returns a list of all active integrations" do
get api("/projects/#{project.id}/services", user)
aggregate_failures 'expect successful response with all active services' do
aggregate_failures 'expect successful response with all active integrations' do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Array
expect(json_response.count).to eq(1)
......@@ -42,40 +42,38 @@ RSpec.describe API::Services do
end
end
Integration.available_integration_names.each do |service|
describe "PUT /projects/:id/services/#{service.dasherize}" do
include_context service
Integration.available_integration_names.each do |integration|
describe "PUT /projects/:id/services/#{integration.dasherize}" do
include_context integration
it "updates #{service} settings" do
put api("/projects/#{project.id}/services/#{dashed_service}", user), params: service_attrs
it "updates #{integration} settings" do
put api("/projects/#{project.id}/services/#{dashed_integration}", user), params: integration_attrs
expect(response).to have_gitlab_http_status(:ok)
current_service = project.integrations.first
events = current_service.event_names.empty? ? ["foo"].freeze : current_service.event_names
current_integration = project.integrations.first
events = current_integration.event_names.empty? ? ["foo"].freeze : current_integration.event_names
query_strings = []
events.each do |event|
query_strings << "#{event}=#{!current_service[event]}"
query_strings << "#{event}=#{!current_integration[event]}"
end
query_strings = query_strings.join('&')
put api("/projects/#{project.id}/services/#{dashed_service}?#{query_strings}", user), params: service_attrs
put api("/projects/#{project.id}/services/#{dashed_integration}?#{query_strings}", user), params: integration_attrs
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['slug']).to eq(dashed_service)
expect(json_response['slug']).to eq(dashed_integration)
events.each do |event|
next if event == "foo"
expect(project.integrations.first[event]).not_to eq(current_service[event]),
"expected #{!current_service[event]} for event #{event} for service #{current_service.title}, got #{current_service[event]}"
expect(project.integrations.first[event]).not_to eq(current_integration[event]),
"expected #{!current_integration[event]} for event #{event} for service #{current_integration.title}, got #{current_integration[event]}"
end
end
it "returns if required fields missing" do
attrs = service_attrs
required_attributes = service_attrs_list.select do |attr|
service_klass.validators_on(attr).any? do |v|
required_attributes = integration_attrs_list.select do |attr|
integration_klass.validators_on(attr).any? do |v|
v.instance_of?(ActiveRecord::Validations::PresenceValidator) &&
# exclude presence validators with conditional since those are not really required
![:if, :unless].any? { |cond| v.options.include?(cond) }
......@@ -85,74 +83,74 @@ RSpec.describe API::Services do
if required_attributes.empty?
expected_code = :ok
else
attrs.delete(required_attributes.sample)
integration_attrs.delete(required_attributes.sample)
expected_code = :bad_request
end
put api("/projects/#{project.id}/services/#{dashed_service}", user), params: attrs
put api("/projects/#{project.id}/services/#{dashed_integration}", user), params: integration_attrs
expect(response).to have_gitlab_http_status(expected_code)
end
end
describe "DELETE /projects/:id/services/#{service.dasherize}" do
include_context service
describe "DELETE /projects/:id/services/#{integration.dasherize}" do
include_context integration
before do
initialize_integration(service)
initialize_integration(integration)
end
it "deletes #{service}" do
delete api("/projects/#{project.id}/services/#{dashed_service}", user)
it "deletes #{integration}" do
delete api("/projects/#{project.id}/services/#{dashed_integration}", user)
expect(response).to have_gitlab_http_status(:no_content)
project.send(service_method).reload
expect(project.send(service_method).activated?).to be_falsey
project.send(integration_method).reload
expect(project.send(integration_method).activated?).to be_falsey
end
end
describe "GET /projects/:id/services/#{service.dasherize}" do
include_context service
describe "GET /projects/:id/services/#{integration.dasherize}" do
include_context integration
let!(:initialized_service) { initialize_integration(service, active: true) }
let!(:initialized_integration) { initialize_integration(integration, active: true) }
let_it_be(:project2) do
create(:project, creator_id: user.id, namespace: user.namespace)
end
def deactive_service!
return initialized_service.update!(active: false) unless initialized_service.is_a?(::Integrations::Prometheus)
def deactive_integration!
return initialized_integration.update!(active: false) unless initialized_integration.is_a?(::Integrations::Prometheus)
# Integrations::Prometheus sets `#active` itself within a `before_save`:
initialized_service.manual_configuration = false
initialized_service.save!
initialized_integration.manual_configuration = false
initialized_integration.save!
end
it 'returns authentication error when unauthenticated' do
get api("/projects/#{project.id}/services/#{dashed_service}")
get api("/projects/#{project.id}/services/#{dashed_integration}")
expect(response).to have_gitlab_http_status(:unauthorized)
end
it "returns all properties of active service #{service}" do
get api("/projects/#{project.id}/services/#{dashed_service}", user)
it "returns all properties of active service #{integration}" do
get api("/projects/#{project.id}/services/#{dashed_integration}", user)
expect(initialized_service).to be_active
expect(initialized_integration).to be_active
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['properties'].keys).to match_array(service_instance.api_field_names)
expect(json_response['properties'].keys).to match_array(integration_instance.api_field_names)
end
it "returns all properties of inactive integration #{service}" do
deactive_service!
it "returns all properties of inactive integration #{integration}" do
deactive_integration!
get api("/projects/#{project.id}/services/#{dashed_service}", user)
get api("/projects/#{project.id}/services/#{dashed_integration}", user)
expect(initialized_service).not_to be_active
expect(initialized_integration).not_to be_active
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['properties'].keys).to match_array(service_instance.api_field_names)
expect(json_response['properties'].keys).to match_array(integration_instance.api_field_names)
end
it "returns not found if integration does not exist" do
get api("/projects/#{project2.id}/services/#{dashed_service}", user)
get api("/projects/#{project2.id}/services/#{dashed_integration}", user)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Service Not Found')
......@@ -160,10 +158,10 @@ RSpec.describe API::Services do
it "returns not found if service exists but is in `Project#disabled_integrations`" do
expect_next_found_instance_of(Project) do |project|
expect(project).to receive(:disabled_integrations).at_least(:once).and_return([service])
expect(project).to receive(:disabled_integrations).at_least(:once).and_return([integration])
end
get api("/projects/#{project.id}/services/#{dashed_service}", user)
get api("/projects/#{project.id}/services/#{dashed_integration}", user)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Service Not Found')
......@@ -171,7 +169,7 @@ RSpec.describe API::Services do
it "returns error when authenticated but not a project owner" do
project.add_developer(user2)
get api("/projects/#{project.id}/services/#{dashed_service}", user2)
get api("/projects/#{project.id}/services/#{dashed_integration}", user2)
expect(response).to have_gitlab_http_status(:forbidden)
end
......@@ -235,8 +233,8 @@ RSpec.describe API::Services do
end
end
describe 'Slack Service' do
let(:service_name) { 'slack_slash_commands' }
describe 'Slack Integration' do
let(:integration_name) { 'slack_slash_commands' }
before do
project.create_slack_slash_commands_integration(
......@@ -246,7 +244,7 @@ RSpec.describe API::Services do
end
it 'returns status 200' do
post api("/projects/#{project.id}/services/#{service_name}/trigger"), params: { token: 'token', text: 'help' }
post api("/projects/#{project.id}/services/#{integration_name}/trigger"), params: { token: 'token', text: 'help' }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['response_type']).to eq("ephemeral")
......@@ -309,8 +307,8 @@ RSpec.describe API::Services do
end
end
describe 'Hangouts Chat service' do
let(:service_name) { 'hangouts-chat' }
describe 'Hangouts Chat integration' do
let(:integration_name) { 'hangouts-chat' }
let(:params) do
{
webhook: 'https://hook.example.com',
......@@ -326,21 +324,21 @@ RSpec.describe API::Services do
end
it 'accepts branches_to_be_notified for update', :aggregate_failures do
put api("/projects/#{project.id}/services/#{service_name}", user), params: params.merge(branches_to_be_notified: 'all')
put api("/projects/#{project.id}/services/#{integration_name}", user), params: params.merge(branches_to_be_notified: 'all')
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['properties']['branches_to_be_notified']).to eq('all')
end
it 'only requires the webhook param' do
put api("/projects/#{project.id}/services/#{service_name}", user), params: { webhook: 'https://hook.example.com' }
put api("/projects/#{project.id}/services/#{integration_name}", user), params: { webhook: 'https://hook.example.com' }
expect(response).to have_gitlab_http_status(:ok)
end
end
describe 'Pipelines Email Integration' do
let(:service_name) { 'pipelines-email' }
let(:integration_name) { 'pipelines-email' }
context 'notify_only_broken_pipelines property was saved as a string' do
before do
......@@ -354,7 +352,7 @@ RSpec.describe API::Services do
end
it 'returns boolean values for notify_only_broken_pipelines' do
get api("/projects/#{project.id}/services/#{service_name}", user)
get api("/projects/#{project.id}/services/#{integration_name}", user)
expect(json_response['properties']['notify_only_broken_pipelines']).to eq(true)
end
......
# frozen_string_literal: true
Integration.available_integration_names.each do |service|
RSpec.shared_context service do
include JiraServiceHelper if service == 'jira'
Integration.available_integration_names.each do |integration|
RSpec.shared_context integration do
include JiraServiceHelper if integration == 'jira'
let(:dashed_service) { service.dasherize }
let(:service_method) { Project.integration_association_name(service) }
let(:service_klass) { Integration.integration_name_to_model(service) }
let(:service_instance) { service_klass.new }
let(:service_fields) { service_instance.fields }
let(:service_attrs_list) { service_fields.inject([]) {|arr, hash| arr << hash[:name].to_sym } }
let(:service_attrs) do
service_attrs_list.inject({}) do |hash, k|
let(:dashed_integration) { integration.dasherize }
let(:integration_method) { Project.integration_association_name(integration) }
let(:integration_klass) { Integration.integration_name_to_model(integration) }
let(:integration_instance) { integration_klass.new }
let(:integration_fields) { integration_instance.fields }
let(:integration_attrs_list) { integration_fields.inject([]) {|arr, hash| arr << hash[:name].to_sym } }
let(:integration_attrs) do
integration_attrs_list.inject({}) do |hash, k|
if k =~ /^(token*|.*_token|.*_key)/
hash.merge!(k => 'secrettoken')
elsif service == 'confluence' && k == :confluence_url
elsif integration == 'confluence' && k == :confluence_url
hash.merge!(k => 'https://example.atlassian.net/wiki')
elsif service == 'datadog' && k == :datadog_site
elsif integration == 'datadog' && k == :datadog_site
hash.merge!(k => 'datadoghq.com')
elsif service == 'packagist' && k == :server
elsif integration == 'packagist' && k == :server
hash.merge!(k => 'https://packagist.example.com')
elsif k =~ /^(.*_url|url|webhook)/
hash.merge!(k => "http://example.com")
elsif service_klass.method_defined?("#{k}?")
elsif integration_klass.method_defined?("#{k}?")
hash.merge!(k => true)
elsif service == 'irker' && k == :recipients
elsif integration == 'irker' && k == :recipients
hash.merge!(k => 'irc://irc.network.net:666/#channel')
elsif service == 'irker' && k == :server_port
elsif integration == 'irker' && k == :server_port
hash.merge!(k => 1234)
elsif service == 'jira' && k == :jira_issue_transition_id
elsif integration == 'jira' && k == :jira_issue_transition_id
hash.merge!(k => '1,2,3')
elsif service == 'emails_on_push' && k == :recipients
elsif integration == 'emails_on_push' && k == :recipients
hash.merge!(k => 'foo@bar.com')
elsif service == 'slack' || service == 'mattermost' && k == :labels_to_be_notified_behavior
elsif integration == 'slack' || integration == 'mattermost' && k == :labels_to_be_notified_behavior
hash.merge!(k => "match_any")
else
hash.merge!(k => "someword")
......@@ -47,24 +47,24 @@ Integration.available_integration_names.each do |service|
end
before do
enable_license_for_service(service)
stub_jira_integration_test if service == 'jira'
enable_license_for_integration(integration)
stub_jira_integration_test if integration == 'jira'
end
def initialize_integration(integration, attrs = {})
record = project.find_or_initialize_integration(integration)
record.attributes = attrs
record.properties = service_attrs
record.properties = integration_attrs
record.save!
record
end
private
def enable_license_for_service(service)
def enable_license_for_integration(integration)
return unless respond_to?(:stub_licensed_features)
licensed_feature = licensed_features[service]
licensed_feature = licensed_features[integration]
return unless licensed_feature
stub_licensed_features(licensed_feature => true)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment