2018-02-26 12:21:41 -05:00
# frozen_string_literal: true
2013-10-29 10:39:46 -04:00
module API
class Services < Grape :: API
2018-02-26 12:21:41 -05:00
CHAT_NOTIFICATION_SETTINGS = [
2017-12-20 12:35:58 -05:00
{
required : true ,
name : :webhook ,
type : String ,
desc : 'The chat webhook'
} ,
{
required : false ,
name : :username ,
type : String ,
desc : 'The chat username'
} ,
{
required : false ,
name : :channel ,
type : String ,
desc : 'The default chat channel'
}
2018-02-26 12:21:41 -05:00
] . freeze
2017-12-20 12:35:58 -05:00
2018-02-26 12:21:41 -05:00
CHAT_NOTIFICATION_FLAGS = [
2017-12-20 12:35:58 -05:00
{
required : false ,
name : :notify_only_broken_pipelines ,
type : Boolean ,
desc : 'Send notifications for broken pipelines'
} ,
{
required : false ,
name : :notify_only_default_branch ,
type : Boolean ,
desc : 'Send notifications only for the default branch'
}
2018-02-26 12:21:41 -05:00
] . freeze
2017-12-20 12:35:58 -05:00
2018-02-26 12:21:41 -05:00
CHAT_NOTIFICATION_CHANNELS = [
2017-12-20 12:35:58 -05:00
{
required : false ,
name : :push_channel ,
type : String ,
desc : 'The name of the channel to receive push_events notifications'
} ,
{
required : false ,
name : :issue_channel ,
type : String ,
desc : 'The name of the channel to receive issues_events notifications'
} ,
{
required : false ,
name : :confidential_issue_channel ,
type : String ,
desc : 'The name of the channel to receive confidential_issues_events notifications'
} ,
{
required : false ,
name : :merge_request_channel ,
type : String ,
desc : 'The name of the channel to receive merge_requests_events notifications'
} ,
{
required : false ,
name : :note_channel ,
type : String ,
desc : 'The name of the channel to receive note_events notifications'
} ,
{
required : false ,
name : :tag_push_channel ,
type : String ,
desc : 'The name of the channel to receive tag_push_events notifications'
} ,
{
required : false ,
name : :pipeline_channel ,
type : String ,
desc : 'The name of the channel to receive pipeline_events notifications'
} ,
{
required : false ,
name : :wiki_page_channel ,
type : String ,
desc : 'The name of the channel to receive wiki_page_events notifications'
}
2018-02-26 12:21:41 -05:00
] . freeze
2017-12-20 12:35:58 -05:00
2018-02-26 12:21:41 -05:00
CHAT_NOTIFICATION_EVENTS = [
2017-12-20 12:35:58 -05:00
{
required : false ,
name : :push_events ,
type : Boolean ,
desc : 'Enable notifications for push_events'
} ,
{
required : false ,
name : :issues_events ,
type : Boolean ,
desc : 'Enable notifications for issues_events'
} ,
{
required : false ,
name : :confidential_issues_events ,
type : Boolean ,
desc : 'Enable notifications for confidential_issues_events'
} ,
{
required : false ,
name : :merge_requests_events ,
type : Boolean ,
desc : 'Enable notifications for merge_requests_events'
} ,
{
required : false ,
name : :note_events ,
type : Boolean ,
desc : 'Enable notifications for note_events'
} ,
{
required : false ,
name : :tag_push_events ,
type : Boolean ,
desc : 'Enable notifications for tag_push_events'
} ,
{
required : false ,
name : :pipeline_events ,
type : Boolean ,
desc : 'Enable notifications for pipeline_events'
} ,
{
required : false ,
name : :wiki_page_events ,
type : Boolean ,
desc : 'Enable notifications for wiki_page_events'
}
2018-02-26 12:21:41 -05:00
] . freeze
2017-12-20 12:35:58 -05:00
2018-02-27 11:04:34 -05:00
services = {
2016-12-05 09:40:53 -05:00
'asana' = > [
{
required : true ,
name : :api_key ,
type : String ,
desc : 'User API token'
} ,
{
required : false ,
name : :restrict_to_branch ,
type : String ,
desc : 'Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches'
}
] ,
'assembla' = > [
{
required : true ,
name : :token ,
type : String ,
desc : 'The authentication token'
} ,
{
required : false ,
name : :subdomain ,
type : String ,
desc : 'Subdomain setting'
}
] ,
'bamboo' = > [
{
required : true ,
name : :bamboo_url ,
type : String ,
desc : 'Bamboo root URL like https://bamboo.example.com'
} ,
{
required : true ,
name : :build_key ,
type : String ,
desc : 'Bamboo build plan key like'
} ,
{
required : true ,
name : :username ,
type : String ,
desc : 'A user with API access, if applicable'
} ,
{
required : true ,
name : :password ,
type : String ,
desc : 'Passord of the user'
}
] ,
'bugzilla' = > [
{
required : true ,
name : :new_issue_url ,
type : String ,
desc : 'New issue URL'
} ,
{
required : true ,
name : :issues_url ,
type : String ,
desc : 'Issues URL'
} ,
{
required : true ,
name : :project_url ,
type : String ,
desc : 'Project URL'
} ,
{
required : false ,
name : :description ,
type : String ,
desc : 'Description'
} ,
{
required : false ,
name : :title ,
type : String ,
desc : 'Title'
}
] ,
'buildkite' = > [
{
required : true ,
name : :token ,
type : String ,
desc : 'Buildkite project GitLab token'
} ,
{
required : true ,
name : :project_url ,
type : String ,
desc : 'The buildkite project URL'
} ,
{
required : false ,
name : :enable_ssl_verification ,
type : Boolean ,
desc : 'Enable SSL verification for communication'
}
] ,
'campfire' = > [
{
required : true ,
name : :token ,
type : String ,
desc : 'Campfire token'
} ,
{
required : false ,
name : :subdomain ,
type : String ,
desc : 'Campfire subdomain'
} ,
{
required : false ,
name : :room ,
type : String ,
desc : 'Campfire room'
2016-12-27 07:44:24 -05:00
}
2016-12-05 09:40:53 -05:00
] ,
'custom-issue-tracker' = > [
{
required : true ,
name : :new_issue_url ,
type : String ,
desc : 'New issue URL'
} ,
{
required : true ,
name : :issues_url ,
type : String ,
desc : 'Issues URL'
} ,
{
required : true ,
name : :project_url ,
type : String ,
desc : 'Project URL'
} ,
{
required : false ,
name : :description ,
type : String ,
desc : 'Description'
} ,
{
required : false ,
name : :title ,
type : String ,
desc : 'Title'
}
] ,
'drone-ci' = > [
{
required : true ,
name : :token ,
type : String ,
desc : 'Drone CI token'
} ,
{
required : true ,
name : :drone_url ,
type : String ,
desc : 'Drone CI URL'
} ,
{
required : false ,
name : :enable_ssl_verification ,
type : Boolean ,
desc : 'Enable SSL verification for communication'
}
] ,
'emails-on-push' = > [
{
required : true ,
name : :recipients ,
type : String ,
desc : 'Comma-separated list of recipient email addresses'
} ,
{
required : false ,
name : :disable_diffs ,
type : Boolean ,
desc : 'Disable code diffs'
} ,
{
required : false ,
name : :send_from_committer_email ,
type : Boolean ,
desc : 'Send from committer'
}
] ,
'external-wiki' = > [
{
required : true ,
name : :external_wiki_url ,
type : String ,
desc : 'The URL of the external Wiki'
}
] ,
'flowdock' = > [
{
required : true ,
name : :token ,
type : String ,
desc : 'Flowdock token'
}
] ,
'gemnasium' = > [
{
required : true ,
name : :api_key ,
type : String ,
desc : 'Your personal API key on gemnasium.com'
} ,
{
required : true ,
name : :token ,
type : String ,
desc : " The project's slug on gemnasium.com "
}
] ,
'hipchat' = > [
{
required : true ,
name : :token ,
type : String ,
desc : 'The room token'
} ,
{
required : false ,
name : :room ,
type : String ,
desc : 'The room name or ID'
} ,
{
required : false ,
name : :color ,
type : String ,
desc : 'The room color'
} ,
{
required : false ,
name : :notify ,
type : Boolean ,
desc : 'Enable notifications'
} ,
{
required : false ,
name : :api_version ,
type : String ,
desc : 'Leave blank for default (v2)'
} ,
{
required : false ,
name : :server ,
type : String ,
desc : 'Leave blank for default. https://hipchat.example.com'
}
] ,
'irker' = > [
{
required : true ,
name : :recipients ,
type : String ,
desc : 'Recipients/channels separated by whitespaces'
} ,
{
required : false ,
name : :default_irc_uri ,
type : String ,
desc : 'Default: irc://irc.network.net:6697'
} ,
{
required : false ,
name : :server_host ,
type : String ,
desc : 'Server host. Default localhost'
} ,
{
required : false ,
name : :server_port ,
type : Integer ,
desc : 'Server port. Default 6659'
} ,
{
required : false ,
name : :colorize_messages ,
type : Boolean ,
desc : 'Colorize messages'
}
] ,
'jira' = > [
{
required : true ,
name : :url ,
type : String ,
2017-05-23 05:14:43 -04:00
desc : 'The base URL to the JIRA instance web interface which is being linked to this GitLab project. E.g., https://jira.example.com'
} ,
{
required : false ,
name : :api_url ,
type : String ,
desc : 'The base URL to the JIRA instance API. Web URL value will be used if not set. E.g., https://jira-api.example.com'
2016-12-05 09:40:53 -05:00
} ,
{
2017-10-24 03:35:21 -04:00
required : true ,
2016-12-05 09:40:53 -05:00
name : :username ,
type : String ,
desc : 'The username of the user created to be used with GitLab/JIRA'
} ,
{
2017-10-24 03:35:21 -04:00
required : true ,
2016-12-05 09:40:53 -05:00
name : :password ,
type : String ,
desc : 'The password of the user created to be used with GitLab/JIRA'
} ,
{
required : false ,
name : :jira_issue_transition_id ,
type : Integer ,
desc : 'The ID of a transition that moves issues to a closed state. You can find this number under the JIRA workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column ([see screenshot][trans]). By default, this ID is set to `2`'
}
] ,
2016-12-08 11:36:26 -05:00
'kubernetes' = > [
{
required : true ,
name : :namespace ,
type : String ,
desc : 'The Kubernetes namespace to use'
} ,
{
required : true ,
name : :api_url ,
type : String ,
desc : 'The URL to the Kubernetes cluster API, e.g., https://kubernetes.example.com'
} ,
{
required : true ,
name : :token ,
type : String ,
desc : 'The service token to authenticate against the Kubernetes cluster with'
} ,
{
required : false ,
name : :ca_pem ,
type : String ,
desc : 'A custom certificate authority bundle to verify the Kubernetes cluster with (PEM format)'
2017-05-03 07:22:03 -04:00
}
2016-12-08 11:36:26 -05:00
] ,
2016-12-05 09:40:53 -05:00
'mattermost-slash-commands' = > [
{
required : true ,
name : :token ,
type : String ,
desc : 'The Mattermost token'
}
] ,
2016-12-19 09:40:06 -05:00
'slack-slash-commands' = > [
{
required : true ,
name : :token ,
type : String ,
desc : 'The Slack token'
}
] ,
2017-09-21 16:05:44 -04:00
'packagist' = > [
{
required : true ,
name : :username ,
type : String ,
desc : 'The username'
} ,
{
required : true ,
name : :token ,
type : String ,
desc : 'The Packagist API token'
} ,
{
required : false ,
name : :server ,
type : String ,
desc : 'The server'
}
] ,
2016-12-05 09:40:53 -05:00
'pipelines-email' = > [
{
required : true ,
name : :recipients ,
type : String ,
desc : 'Comma-separated list of recipient email addresses'
} ,
{
required : false ,
2017-03-17 19:06:11 -04:00
name : :notify_only_broken_pipelines ,
2016-12-05 09:40:53 -05:00
type : Boolean ,
2017-03-17 19:06:11 -04:00
desc : 'Notify only broken pipelines'
2016-12-05 09:40:53 -05:00
}
] ,
'pivotaltracker' = > [
{
required : true ,
name : :token ,
type : String ,
desc : 'The Pivotaltracker token'
} ,
{
required : false ,
name : :restrict_to_branch ,
type : String ,
desc : 'Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches.'
}
] ,
2017-03-07 11:57:42 -05:00
'prometheus' = > [
{
required : true ,
name : :api_url ,
type : String ,
desc : 'Prometheus API Base URL, like http://prometheus.example.com/'
}
] ,
2016-12-05 09:40:53 -05:00
'pushover' = > [
{
required : true ,
name : :api_key ,
type : String ,
desc : 'The application key'
} ,
{
required : true ,
name : :user_key ,
type : String ,
desc : 'The user key'
} ,
{
required : true ,
name : :priority ,
type : String ,
desc : 'The priority'
} ,
{
required : true ,
name : :device ,
type : String ,
desc : 'Leave blank for all active devices'
} ,
{
required : true ,
name : :sound ,
type : String ,
desc : 'The sound of the notification'
}
] ,
'redmine' = > [
{
required : true ,
name : :new_issue_url ,
type : String ,
desc : 'The new issue URL'
} ,
{
required : true ,
name : :project_url ,
type : String ,
desc : 'The project URL'
} ,
{
required : true ,
name : :issues_url ,
type : String ,
desc : 'The issues URL'
} ,
{
required : false ,
name : :description ,
type : String ,
desc : 'The description of the tracker'
}
] ,
2016-12-20 16:14:33 -05:00
'slack' = > [
2018-02-26 12:21:41 -05:00
CHAT_NOTIFICATION_SETTINGS ,
CHAT_NOTIFICATION_FLAGS ,
CHAT_NOTIFICATION_CHANNELS ,
CHAT_NOTIFICATION_EVENTS
2017-12-20 12:35:58 -05:00
] . flatten ,
2017-04-04 11:10:21 -04:00
'microsoft-teams' = > [
{
required : true ,
name : :webhook ,
type : String ,
desc : 'The Microsoft Teams webhook. e.g. https://outlook.office.com/webhook/…'
}
] ,
2016-12-20 16:29:39 -05:00
'mattermost' = > [
2018-02-26 12:21:41 -05:00
CHAT_NOTIFICATION_SETTINGS ,
CHAT_NOTIFICATION_FLAGS ,
CHAT_NOTIFICATION_CHANNELS ,
CHAT_NOTIFICATION_EVENTS
2017-12-20 12:35:58 -05:00
] . flatten ,
2016-12-05 09:40:53 -05:00
'teamcity' = > [
{
required : true ,
name : :teamcity_url ,
type : String ,
desc : 'TeamCity root URL like https://teamcity.example.com'
} ,
{
required : true ,
name : :build_type ,
type : String ,
desc : 'Build configuration ID'
} ,
{
required : true ,
name : :username ,
type : String ,
desc : 'A user with permissions to trigger a manual build'
} ,
{
required : true ,
name : :password ,
type : String ,
desc : 'The password of the user'
}
]
2018-02-27 11:04:34 -05:00
}
2016-12-27 07:44:24 -05:00
2018-02-27 11:04:34 -05:00
service_classes = [
2016-12-27 07:44:24 -05:00
AsanaService ,
AssemblaService ,
BambooService ,
BugzillaService ,
BuildkiteService ,
CampfireService ,
CustomIssueTrackerService ,
DroneCiService ,
EmailsOnPushService ,
ExternalWikiService ,
FlowdockService ,
GemnasiumService ,
HipchatService ,
IrkerService ,
JiraService ,
KubernetesService ,
MattermostSlashCommandsService ,
SlackSlashCommandsService ,
2017-09-21 16:05:44 -04:00
PackagistService ,
2016-12-27 07:44:24 -05:00
PipelinesEmailService ,
PivotaltrackerService ,
2017-03-07 11:57:42 -05:00
PrometheusService ,
2016-12-27 07:44:24 -05:00
PushoverService ,
RedmineService ,
SlackService ,
MattermostService ,
2017-04-04 11:10:21 -04:00
MicrosoftTeamsService ,
2017-05-03 07:22:03 -04:00
TeamcityService
2018-02-27 11:04:34 -05:00
]
2017-02-14 20:52:44 -05:00
if Rails . env . development?
2018-02-27 11:04:34 -05:00
services [ 'mock-ci' ] = [
2017-02-14 20:52:44 -05:00
{
required : true ,
name : :mock_service_url ,
type : String ,
desc : 'URL to the mock service'
}
]
2018-02-27 11:04:34 -05:00
services [ 'mock-deployment' ] = [ ]
services [ 'mock-monitoring' ] = [ ]
2017-02-14 20:52:44 -05:00
2018-02-27 11:04:34 -05:00
service_classes += [
2017-04-05 07:04:34 -04:00
MockCiService ,
MockDeploymentService ,
2017-05-03 07:22:03 -04:00
MockMonitoringService
2017-04-05 07:04:34 -04:00
]
2017-02-14 20:52:44 -05:00
end
2016-12-05 09:40:53 -05:00
2018-02-27 11:04:34 -05:00
SERVICES = services . freeze
SERVICE_CLASSES = service_classes . freeze
2018-02-26 12:21:41 -05:00
SERVICE_CLASSES . each do | service |
event_names = service . try ( :event_names ) || next
event_names . each do | event_name |
SERVICES [ service . to_param . tr ( " _ " , " - " ) ] << {
required : false ,
name : event_name . to_sym ,
type : String ,
2018-03-16 15:09:35 -04:00
desc : service . event_description ( event_name )
2018-02-26 12:21:41 -05:00
}
end
end
TRIGGER_SERVICES = {
2016-12-05 09:40:53 -05:00
'mattermost-slash-commands' = > [
{
name : :token ,
type : String ,
desc : 'The Mattermost token'
}
2016-12-29 04:48:18 -05:00
] ,
'slack-slash-commands' = > [
{
name : :token ,
type : String ,
desc : 'The Slack token'
}
2016-12-05 09:40:53 -05:00
]
} . freeze
2017-03-15 14:09:24 -04:00
params do
requires :id , type : String , desc : 'The ID of a project'
end
2017-08-31 07:44:49 -04:00
resource :projects , requirements : API :: PROJECT_ENDPOINT_REQUIREMENTS do
2016-11-14 09:10:35 -05:00
before { authenticate! }
before { authorize_admin_project }
2016-12-05 09:40:53 -05:00
helpers do
def service_attributes ( service )
service . fields . inject ( [ ] ) do | arr , hash |
arr << hash [ :name ] . to_sym
2015-08-26 19:58:49 -04:00
end
2016-12-05 09:40:53 -05:00
end
end
2014-10-14 13:07:34 -04:00
2018-02-26 12:21:41 -05:00
SERVICES . each do | service_slug , settings |
2016-12-05 09:40:53 -05:00
desc " Set #{ service_slug } service 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 " :id/services/ #{ service_slug } " do
service = user_project . find_or_initialize_service ( service_slug . underscore )
service_params = declared_params ( include_missing : false ) . merge ( active : true )
2014-10-14 13:07:34 -04:00
2016-12-05 09:40:53 -05:00
if service . update_attributes ( service_params )
2018-01-06 01:18:13 -05:00
present service , with : Entities :: ProjectService
2015-08-26 19:58:49 -04:00
else
2016-12-05 09:40:53 -05:00
render_api_error! ( '400 Bad Request' , 400 )
2015-08-26 19:58:49 -04:00
end
2014-10-14 13:07:34 -04:00
end
end
2016-12-05 09:40:53 -05:00
desc " Delete a service for project "
params do
2018-02-26 12:21:41 -05:00
requires :service_slug , type : String , values : SERVICES . keys , desc : 'The name of the service'
2016-12-05 09:40:53 -05:00
end
delete " :id/services/:service_slug " do
service = user_project . find_or_initialize_service ( params [ :service_slug ] . underscore )
2015-09-11 07:38:37 -04:00
2017-08-24 04:41:54 -04:00
destroy_conditionally! ( service ) do
attrs = service_attributes ( service ) . inject ( { } ) do | hash , key |
hash . merge! ( key = > nil )
end
2016-12-05 09:40:53 -05:00
2017-08-24 04:41:54 -04:00
unless service . update_attributes ( attrs . merge ( active : false ) )
render_api_error! ( '400 Bad Request' , 400 )
end
2014-10-14 13:07:34 -04:00
end
end
2015-09-03 09:38:54 -04:00
2016-12-05 09:40:53 -05:00
desc 'Get the service settings for project' do
success Entities :: ProjectService
end
params do
2018-02-26 12:21:41 -05:00
requires :service_slug , type : String , values : SERVICES . keys , desc : 'The name of the service'
2016-12-05 09:40:53 -05:00
end
get " :id/services/:service_slug " do
service = user_project . find_or_initialize_service ( params [ :service_slug ] . underscore )
2017-04-08 22:20:57 -04:00
present service , with : Entities :: ProjectService , include_passwords : current_user . admin?
2015-09-03 09:38:54 -04:00
end
2013-10-29 10:39:46 -04:00
end
2016-11-14 09:10:35 -05:00
2018-02-26 12:21:41 -05:00
TRIGGER_SERVICES . each do | service_slug , settings |
2017-01-30 06:29:55 -05:00
helpers do
2017-05-31 01:50:53 -04:00
def slash_command_service ( project , service_slug , params )
2017-01-30 06:29:55 -05:00
project . services . active . where ( template : false ) . find do | service |
service . try ( :token ) == params [ :token ] && service . to_param == service_slug . underscore
end
end
end
2016-12-05 09:40:53 -05:00
params do
requires :id , type : String , desc : 'The ID of a project'
2016-11-17 06:06:45 -05:00
end
2017-08-31 07:44:49 -04:00
resource :projects , requirements : API :: PROJECT_ENDPOINT_REQUIREMENTS do
2016-12-05 09:40:53 -05:00
desc " Trigger a slash command for #{ service_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 " :id/services/ #{ service_slug . underscore } /trigger " do
project = find_project ( params [ :id ] )
2016-11-14 09:10:35 -05:00
2016-12-05 09:40:53 -05:00
# This is not accurate, but done to prevent leakage of the project names
not_found! ( 'Service' ) unless project
2016-11-18 05:38:54 -05:00
2017-05-31 01:50:53 -04:00
service = slash_command_service ( project , service_slug , params )
2017-01-30 06:29:55 -05:00
result = service . try ( :trigger , params )
2016-11-14 09:10:35 -05:00
2016-12-05 09:40:53 -05:00
if result
status result [ :status ] || 200
present result
else
not_found! ( 'Service' )
end
2016-11-14 09:10:35 -05:00
end
end
end
2013-10-29 10:39:46 -04:00
end
end