Read about our upcoming Code of Conduct on this issue

services.rb 22.4 KB
Newer Older
1
# frozen_string_literal: true
2
3
module API
  class Services < Grape::API
4
    CHAT_NOTIFICATION_SETTINGS = [
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
      {
        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'
      }
23
    ].freeze
24

25
    CHAT_NOTIFICATION_FLAGS = [
26
27
28
29
30
31
32
33
34
35
36
37
      {
        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'
      }
38
    ].freeze
39

40
    CHAT_NOTIFICATION_CHANNELS = [
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
      {
        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'
      }
89
    ].freeze
90

91
    CHAT_NOTIFICATION_EVENTS = [
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
      {
        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'
      }
140
    ].freeze
141

142
    services = {
Robert Schilling's avatar
Robert Schilling committed
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
      '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'
267
        }
Robert Schilling's avatar
Robert Schilling committed
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
      ],
      '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'
        }
      ],
blackst0ne's avatar
blackst0ne committed
301
302
303
304
305
306
307
308
      'discord' => [
        {
          required: true,
          name: :webhook,
          type: String,
          desc: 'Discord webhook. e.g. https://discordapp.com/api/webhooks/…'
        }
      ],
Robert Schilling's avatar
Robert Schilling committed
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
      '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'
        }
      ],
365
366
367
368
369
370
371
372
      'hangouts-chat' => [
        {
          required: true,
          name: :webhook,
          type: String,
          desc: 'The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces…'
        }
      ],
Robert Schilling's avatar
Robert Schilling committed
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
      '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,
Jarka Kadlecova's avatar
Jarka Kadlecova committed
448
449
450
451
452
453
454
          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'
Robert Schilling's avatar
Robert Schilling committed
455
456
        },
        {
457
          required: true,
Robert Schilling's avatar
Robert Schilling committed
458
459
460
461
462
          name: :username,
          type: String,
          desc: 'The username of the user created to be used with GitLab/JIRA'
        },
        {
463
          required: true,
Robert Schilling's avatar
Robert Schilling committed
464
465
466
467
468
469
470
471
472
473
474
          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`'
        }
      ],
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499

      '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)'
500
        }
501
      ],
Robert Schilling's avatar
Robert Schilling committed
502
503
504
505
506
507
508
509
      'mattermost-slash-commands' => [
        {
          required: true,
          name: :token,
          type: String,
          desc: 'The Mattermost token'
        }
      ],
Kamil Trzcinski's avatar
Kamil Trzcinski committed
510
511
512
513
514
515
516
517
      'slack-slash-commands' => [
        {
          required: true,
          name: :token,
          type: String,
          desc: 'The Slack token'
        }
      ],
Matt Coleman's avatar
Matt Coleman committed
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
      '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'
        }
      ],
Robert Schilling's avatar
Robert Schilling committed
538
539
540
541
542
543
544
545
546
      'pipelines-email' => [
        {
          required: true,
          name: :recipients,
          type: String,
          desc: 'Comma-separated list of recipient email addresses'
        },
        {
          required: false,
Kamil Trzciński's avatar
Kamil Trzciński committed
547
          name: :notify_only_broken_pipelines,
Robert Schilling's avatar
Robert Schilling committed
548
          type: Boolean,
Kamil Trzciński's avatar
Kamil Trzciński committed
549
          desc: 'Notify only broken pipelines'
Robert Schilling's avatar
Robert Schilling committed
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
        }
      ],
      '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.'
        }
      ],
566
567
568
569
570
571
572
573
      'prometheus' => [
        {
          required: true,
          name: :api_url,
          type: String,
          desc: 'Prometheus API Base URL, like http://prometheus.example.com/'
        }
      ],
Robert Schilling's avatar
Robert Schilling committed
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
      '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'
        }
      ],
632
      'slack' => [
633
634
635
636
        CHAT_NOTIFICATION_SETTINGS,
        CHAT_NOTIFICATION_FLAGS,
        CHAT_NOTIFICATION_CHANNELS,
        CHAT_NOTIFICATION_EVENTS
637
      ].flatten,
Tiago Botelho's avatar
Tiago Botelho committed
638
639
640
641
642
643
644
645
      'microsoft-teams' => [
        {
          required: true,
          name: :webhook,
          type: String,
          desc: 'The Microsoft Teams webhook. e.g. https://outlook.office.com/webhook/…'
        }
      ],
646
      'mattermost' => [
647
648
649
650
        CHAT_NOTIFICATION_SETTINGS,
        CHAT_NOTIFICATION_FLAGS,
        CHAT_NOTIFICATION_CHANNELS,
        CHAT_NOTIFICATION_EVENTS
651
      ].flatten,
Robert Schilling's avatar
Robert Schilling committed
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
      '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'
        }
      ]
678
    }
679

680
    service_classes = [
681
682
683
684
685
686
687
      AsanaService,
      AssemblaService,
      BambooService,
      BugzillaService,
      BuildkiteService,
      CampfireService,
      CustomIssueTrackerService,
blackst0ne's avatar
blackst0ne committed
688
      DiscordService,
689
690
691
692
      DroneCiService,
      EmailsOnPushService,
      ExternalWikiService,
      FlowdockService,
693
      HangoutsChatService,
694
695
696
697
698
699
      HipchatService,
      IrkerService,
      JiraService,
      KubernetesService,
      MattermostSlashCommandsService,
      SlackSlashCommandsService,
Matt Coleman's avatar
Matt Coleman committed
700
      PackagistService,
701
702
      PipelinesEmailService,
      PivotaltrackerService,
703
      PrometheusService,
704
705
706
707
      PushoverService,
      RedmineService,
      SlackService,
      MattermostService,
Tiago Botelho's avatar
Tiago Botelho committed
708
      MicrosoftTeamsService,
709
      TeamcityService
710
    ]
Eric Eastwood's avatar
Eric Eastwood committed
711
712

    if Rails.env.development?
713
      services['mock-ci'] = [
Eric Eastwood's avatar
Eric Eastwood committed
714
715
716
717
718
719
720
        {
          required: true,
          name: :mock_service_url,
          type: String,
          desc: 'URL to the mock service'
        }
      ]
721
722
      services['mock-deployment'] = []
      services['mock-monitoring'] = []
Eric Eastwood's avatar
Eric Eastwood committed
723

724
      service_classes += [
725
726
        MockCiService,
        MockDeploymentService,
727
        MockMonitoringService
728
      ]
Eric Eastwood's avatar
Eric Eastwood committed
729
    end
Robert Schilling's avatar
Robert Schilling committed
730

731
732
733
    SERVICES = services.freeze
    SERVICE_CLASSES = service_classes.freeze

734
735
736
737
738
739
740
    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,
Felipe Artur's avatar
Felipe Artur committed
741
          desc: service.event_description(event_name)
742
743
744
745
746
        }
      end
    end

    TRIGGER_SERVICES = {
Robert Schilling's avatar
Robert Schilling committed
747
748
749
750
751
752
      'mattermost-slash-commands' => [
        {
          name: :token,
          type: String,
          desc: 'The Mattermost token'
        }
753
754
755
756
757
758
759
      ],
      'slack-slash-commands' => [
        {
          name: :token,
          type: String,
          desc: 'The Slack token'
        }
Robert Schilling's avatar
Robert Schilling committed
760
761
762
      ]
    }.freeze

763
764
765
    params do
      requires :id, type: String, desc: 'The ID of a project'
    end
766
    resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS  do
767
768
769
      before { authenticate! }
      before { authorize_admin_project }

Robert Schilling's avatar
Robert Schilling committed
770
771
772
773
      helpers do
        def service_attributes(service)
          service.fields.inject([]) do |arr, hash|
            arr << hash[:name].to_sym
Kirilll Zaitsev's avatar
Kirilll Zaitsev committed
774
          end
Robert Schilling's avatar
Robert Schilling committed
775
776
        end
      end
Kevin Houdebert's avatar
Kevin Houdebert committed
777

778
      SERVICES.each do |service_slug, settings|
Robert Schilling's avatar
Robert Schilling committed
779
780
781
782
783
784
785
786
787
788
789
790
791
        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)
Kevin Houdebert's avatar
Kevin Houdebert committed
792

Lin Jen-Shin's avatar
Lin Jen-Shin committed
793
          if service.update(service_params)
Stan Hu's avatar
Stan Hu committed
794
            present service, with: Entities::ProjectService
Kirilll Zaitsev's avatar
Kirilll Zaitsev committed
795
          else
Robert Schilling's avatar
Robert Schilling committed
796
            render_api_error!('400 Bad Request', 400)
Kirilll Zaitsev's avatar
Kirilll Zaitsev committed
797
          end
Kevin Houdebert's avatar
Kevin Houdebert committed
798
799
800
        end
      end

Robert Schilling's avatar
Robert Schilling committed
801
802
      desc "Delete a service for project"
      params do
803
        requires :service_slug, type: String, values: SERVICES.keys, desc: 'The name of the service'
Robert Schilling's avatar
Robert Schilling committed
804
805
806
      end
      delete ":id/services/:service_slug" do
        service = user_project.find_or_initialize_service(params[:service_slug].underscore)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
807

Robert Schilling's avatar
Robert Schilling committed
808
809
810
811
        destroy_conditionally!(service) do
          attrs = service_attributes(service).inject({}) do |hash, key|
            hash.merge!(key => nil)
          end
Robert Schilling's avatar
Robert Schilling committed
812

Lin Jen-Shin's avatar
Lin Jen-Shin committed
813
          unless service.update(attrs.merge(active: false))
Robert Schilling's avatar
Robert Schilling committed
814
815
            render_api_error!('400 Bad Request', 400)
          end
Kevin Houdebert's avatar
Kevin Houdebert committed
816
817
        end
      end
818

Robert Schilling's avatar
Robert Schilling committed
819
820
821
822
      desc 'Get the service settings for project' do
        success Entities::ProjectService
      end
      params do
823
        requires :service_slug, type: String, values: SERVICES.keys, desc: 'The name of the service'
Robert Schilling's avatar
Robert Schilling committed
824
825
826
      end
      get ":id/services/:service_slug" do
        service = user_project.find_or_initialize_service(params[:service_slug].underscore)
blackst0ne's avatar
blackst0ne committed
827
        present service, with: Entities::ProjectService, include_passwords: current_user.admin?
828
      end
829
    end
830

831
    TRIGGER_SERVICES.each do |service_slug, settings|
832
      helpers do
833
        # rubocop: disable CodeReuse/ActiveRecord
834
        def slash_command_service(project, service_slug, params)
835
836
837
838
          project.services.active.where(template: false).find do |service|
            service.try(:token) == params[:token] && service.to_param == service_slug.underscore
          end
        end
839
        # rubocop: enable CodeReuse/ActiveRecord
840
841
      end

Robert Schilling's avatar
Robert Schilling committed
842
843
      params do
        requires :id, type: String, desc: 'The ID of a project'
844
      end
845
      resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS  do
Robert Schilling's avatar
Robert Schilling committed
846
847
848
849
850
851
852
853
854
855
        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])
856

Robert Schilling's avatar
Robert Schilling committed
857
858
          # This is not accurate, but done to prevent leakage of the project names
          not_found!('Service') unless project
859

860
          service = slash_command_service(project, service_slug, params)
861
          result = service.try(:trigger, params)
862

Robert Schilling's avatar
Robert Schilling committed
863
864
865
866
867
868
          if result
            status result[:status] || 200
            present result
          else
            not_found!('Service')
          end
869
870
871
        end
      end
    end
872
873
  end
end