foreman_webhooks 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +30 -0
  4. data/Rakefile +49 -0
  5. data/app/controllers/api/v2/webhook_templates_controller.rb +110 -0
  6. data/app/controllers/api/v2/webhooks_controller.rb +61 -0
  7. data/app/controllers/concerns/foreman_webhooks/controller/parameters/webhook.rb +34 -0
  8. data/app/controllers/concerns/foreman_webhooks/controller/parameters/webhook_template.rb +34 -0
  9. data/app/controllers/webhook_templates_controller.rb +5 -0
  10. data/app/controllers/webhooks_controller.rb +39 -0
  11. data/app/jobs/foreman_webhooks/deliver_webhook_job.rb +27 -0
  12. data/app/lib/foreman_webhooks/renderer/scope/webhook_template.rb +36 -0
  13. data/app/models/webhook.rb +141 -0
  14. data/app/models/webhook_template.rb +59 -0
  15. data/app/services/foreman_webhooks/webhook_service.rb +111 -0
  16. data/app/subscribers/foreman_webhooks/event_subscriber.rb +9 -0
  17. data/app/views/api/v2/webhook_templates/base.json.rabl +5 -0
  18. data/app/views/api/v2/webhook_templates/create.json.rabl +5 -0
  19. data/app/views/api/v2/webhook_templates/index.json.rabl +5 -0
  20. data/app/views/api/v2/webhook_templates/main.json.rabl +7 -0
  21. data/app/views/api/v2/webhook_templates/show.json.rabl +11 -0
  22. data/app/views/api/v2/webhook_templates/update.json.rabl +5 -0
  23. data/app/views/api/v2/webhooks/base.json.rabl +5 -0
  24. data/app/views/api/v2/webhooks/create.json.rabl +5 -0
  25. data/app/views/api/v2/webhooks/index.json.rabl +5 -0
  26. data/app/views/api/v2/webhooks/main.json.rabl +8 -0
  27. data/app/views/api/v2/webhooks/show.json.rabl +17 -0
  28. data/app/views/api/v2/webhooks/update.json.rabl +5 -0
  29. data/app/views/foreman_webhooks/webhook_templates/ansible_tower_-_host_in_inventory.erb +20 -0
  30. data/app/views/foreman_webhooks/webhook_templates/empty_payload.erb +6 -0
  31. data/app/views/foreman_webhooks/webhook_templates/webhook_template_-_payload_default.erb +11 -0
  32. data/app/views/webhook_templates/_alerts.html.erb +3 -0
  33. data/app/views/webhook_templates/_custom_tab_headers.html.erb +1 -0
  34. data/app/views/webhook_templates/_custom_tabs.html.erb +5 -0
  35. data/app/views/webhook_templates/edit.html.erb +3 -0
  36. data/app/views/webhook_templates/index.html.erb +29 -0
  37. data/app/views/webhook_templates/new.html.erb +3 -0
  38. data/app/views/webhooks/_form.html.erb +37 -0
  39. data/app/views/webhooks/_templates.html.erb +5 -0
  40. data/app/views/webhooks/edit.html.erb +3 -0
  41. data/app/views/webhooks/new.html.erb +3 -0
  42. data/config/routes.rb +45 -0
  43. data/db/migrate/20191016100128_create_webhook_targets.rb +13 -0
  44. data/db/migrate/20200831194208_rename_webhook_targets_to_webhooks.rb +7 -0
  45. data/db/migrate/20200831194514_add_template_to_webhooks.rb +7 -0
  46. data/db/migrate/20200907232758_rename_webhook_permissions.rb +22 -0
  47. data/db/migrate/20200908004234_add_columns_to_webhooks.rb +13 -0
  48. data/db/migrate/20201014115147_rename_ca_file_column.rb +7 -0
  49. data/db/migrate/20201109135301_add_http_headers.rb +8 -0
  50. data/db/seeds.d/95_webhook_templates.rb +7 -0
  51. data/lib/foreman_webhooks.rb +6 -0
  52. data/lib/foreman_webhooks/engine.rb +80 -0
  53. data/lib/foreman_webhooks/version.rb +5 -0
  54. data/lib/tasks/foreman_webhooks_tasks.rake +47 -0
  55. data/package.json +45 -0
  56. data/test/controllers/api/v2/webhook_templates_controller_test.rb +200 -0
  57. data/test/controllers/api/v2/webhooks_controller_test.rb +108 -0
  58. data/test/factories/webhook.rb +20 -0
  59. data/test/factories/webhook_target.rb +9 -0
  60. data/test/factories/webhook_template.rb +16 -0
  61. data/test/jobs/foreman_webhooks/deliver_webhook_job_test.rb +17 -0
  62. data/test/models/webhook_test.rb +38 -0
  63. data/test/test_plugin_helper.rb +8 -0
  64. data/test/unit/foreman_webhooks/webhook_service_test.rb +53 -0
  65. data/webpack/ForemanWebhooks/Routes/ForemanWebhooksRoutes.js +12 -0
  66. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/EmptyWebhooksTable/index.js +29 -0
  67. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookDeleteModal.js +43 -0
  68. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/EnabledCell.js +16 -0
  69. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/__tests__/__snapshots__/enabledCellFormatter.test.js.snap +7 -0
  70. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/__tests__/enabledCellFormatter.test.js +7 -0
  71. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/enabledCellFormatter.js +6 -0
  72. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/index.js +1 -0
  73. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/__tests__/EnabledCell.test.js +14 -0
  74. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/__tests__/__snapshots__/EnabledCell.test.js.snap +17 -0
  75. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/WebhooksTable.js +75 -0
  76. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/WebhooksTableSchema.js +41 -0
  77. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/__tests__/WebhooksTable.test.js +57 -0
  78. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/__tests__/__snapshots__/WebhooksTable.test.js.snap +115 -0
  79. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/index.js +25 -0
  80. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/WebhooksIndexPage.js +87 -0
  81. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/WebhooksIndexPage.fixtures.js +74 -0
  82. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/WebhooksIndexPage.test.js +21 -0
  83. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/__snapshots__/WebhooksIndexPage.test.js.snap +68 -0
  84. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/index.js +51 -0
  85. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksPageActions.js +51 -0
  86. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksPageHelpers.js +29 -0
  87. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksPageSelectors.js +85 -0
  88. data/webpack/ForemanWebhooks/Routes/Webhooks/__tests__/WebhooksPageHelpers.test.js +20 -0
  89. data/webpack/ForemanWebhooks/Routes/Webhooks/__tests__/WebhooksPageSelectors.test.js +45 -0
  90. data/webpack/ForemanWebhooks/Routes/Webhooks/__tests__/__snapshots__/WebhooksPageSelectors.test.js.snap +50 -0
  91. data/webpack/ForemanWebhooks/Routes/Webhooks/constants.js +13 -0
  92. data/webpack/__mocks__/foremanReact/common/HOC.js +2 -0
  93. data/webpack/__mocks__/foremanReact/common/I18n.js +7 -0
  94. data/webpack/__mocks__/foremanReact/common/helpers.js +7 -0
  95. data/webpack/__mocks__/foremanReact/common/urlHelpers.js +1 -0
  96. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalActions.js +2 -0
  97. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalHooks.js +10 -0
  98. data/webpack/__mocks__/foremanReact/components/ForemanModal/index.js +18 -0
  99. data/webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js +2 -0
  100. data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +2 -0
  101. data/webpack/__mocks__/foremanReact/components/common/EmptyState.js +5 -0
  102. data/webpack/__mocks__/foremanReact/components/common/table.js +5 -0
  103. data/webpack/__mocks__/foremanReact/constants.js +24 -0
  104. data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +6 -0
  105. data/webpack/__mocks__/foremanReact/redux/API/index.js +10 -0
  106. data/webpack/__mocks__/foremanReact/redux/actions/toasts.js +8 -0
  107. data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +10 -0
  108. data/webpack/index.js +0 -0
  109. data/webpack/routes_index.js +4 -0
  110. metadata +195 -0
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ class WebhookTemplate < Template
4
+ audited
5
+
6
+ include Authorizable
7
+ extend FriendlyId
8
+ friendly_id :name
9
+ include Parameterizable::ByIdName
10
+
11
+ class << self
12
+ # we have to override the base_class because polymorphic associations does not detect it correctly, more details at
13
+ # http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many#1010-Polymorphic-has-many-within-inherited-class-gotcha
14
+ def base_class
15
+ self
16
+ end
17
+ end
18
+ self.table_name = 'templates'
19
+
20
+ before_destroy EnsureNotUsedBy.new(:webhooks)
21
+ has_many :webhooks, foreign_key: :webhook_template_id
22
+
23
+ validates :name, uniqueness: true
24
+
25
+ include Taxonomix
26
+ scoped_search on: :name, complete_value: true, default_order: true
27
+ scoped_search on: :locked, complete_value: { true: true, false: false }
28
+ scoped_search on: :snippet, complete_value: { true: true, false: false }
29
+ scoped_search on: :template
30
+ scoped_search on: :default, only_explicit: true, complete_value: { true: true, false: false }
31
+
32
+ # with proc support, default_scope can no longer be chained
33
+ # include all default scoping here
34
+ default_scope lambda {
35
+ with_taxonomy_scope do
36
+ order("#{Template.table_name}.name")
37
+ end
38
+ }
39
+
40
+ def self.default_render_scope_class
41
+ ForemanWebhooks::Renderer::Scope::WebhookTemplate
42
+ end
43
+
44
+ def taxonomy_foreign_conditions
45
+ { webhook_template_id: id }
46
+ end
47
+
48
+ def self.acceptable_template_input_types
49
+ [:user]
50
+ end
51
+
52
+ def self.log_render_results?
53
+ true
54
+ end
55
+
56
+ def support_single_host_render?
57
+ false
58
+ end
59
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanWebhooks
4
+ class WebhookService
5
+ attr_accessor :webhook, :event_name, :payload, :rendered_headers, :rendered_url
6
+
7
+ delegate :logger, to: Rails
8
+
9
+ def initialize(webhook:, event_name:, payload:, headers:, url:)
10
+ @webhook = webhook
11
+ @event_name = event_name
12
+ @payload = payload
13
+ @rendered_headers = headers
14
+ @rendered_url = url
15
+ end
16
+
17
+ def execute
18
+ logger.info("Performing '#{webhook.name}' webhook request for event '#{event_name}'")
19
+ Foreman::Logging.blob("Payload for '#{event_name}'", payload)
20
+ headers = {}
21
+ begin
22
+ headers = JSON.parse(rendered_headers)
23
+ rescue StandardError => e
24
+ logger.warn("Could not parse HTTP headers JSON, ignoring: #{e}")
25
+ logger.debug("Headers: #{rendered_headers}")
26
+ end
27
+
28
+ response = self.class.request(url: webhook.target_url,
29
+ payload: payload,
30
+ http_method: webhook.http_method,
31
+ user: webhook.user,
32
+ password: webhook.password,
33
+ content_type: webhook.http_content_type,
34
+ headers: headers,
35
+ ca_verify: webhook.verify_ssl?,
36
+ ca_string: webhook.ca_certs_store,
37
+ follow_redirects: true)
38
+
39
+ status = case response.code.to_i
40
+ when 400..599
41
+ logger.error("#{webhook.http_method.to_s.upcase} response was #{response.code}")
42
+ :error
43
+ else
44
+ logger.info("#{webhook.http_method.to_s.upcase} response was #{response.code}")
45
+ :success
46
+ end
47
+
48
+ {
49
+ status: status,
50
+ message: response.message,
51
+ http_status: response.code.to_i
52
+ }
53
+ rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH, Net::OpenTimeout, Net::ReadTimeout => e
54
+ Foreman::Logging.exception("Failed to execute the webhook #{webhook.name} -> #{event_name}", e)
55
+ {
56
+ status: :error,
57
+ message: e.to_s
58
+ }
59
+ end
60
+
61
+ def self.request(url:, payload: '', http_method: :GET, user: nil, password: nil, content_type: 'application/json', headers: {}, ca_string: nil, ca_verify: false, follow_redirects: true, redirect_limit: 3)
62
+ uri = URI.parse(url)
63
+
64
+ request = Object.const_get("Net::HTTP::#{http_method.to_s.capitalize}").new(uri.request_uri)
65
+ request.basic_auth(user, password) if user && password
66
+ request['Content-Type'] = content_type
67
+ request['X-Request-Id'] = ::Logging.mdc['request'] || SecureRandom.uuid
68
+ request['X-Session-Id'] = ::Logging.mdc['session'] || SecureRandom.uuid
69
+ headers.each_pair do |key, value|
70
+ request[key.to_s] = value.to_s
71
+ end
72
+ request.body = payload
73
+
74
+ Rails.logger.debug("Webhook #{http_method.to_s.upcase} request: #{uri}")
75
+ Rails.logger.debug("Headers: #{request.to_hash.inspect}")
76
+ Rails.logger.debug("Body: #{request.body.inspect}")
77
+
78
+ http = Net::HTTP.new(uri.host, uri.port)
79
+ http.open_timeout = Setting[:proxy_request_timeout]
80
+ http.read_timeout = Setting[:proxy_request_timeout]
81
+ http.ssl_timeout = Setting[:proxy_request_timeout]
82
+ if uri.scheme == 'https'
83
+ http.use_ssl = true
84
+ http.verify_mode = ca_verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
85
+ http.cert_store = ca_string if ca_string
86
+ end
87
+ http.request(request) do |response|
88
+ case response
89
+ when Net::HTTPRedirection then
90
+ new_location = response['location']
91
+ Rails.logger.debug "Redirected to #{new_location} (redirects left: #{redirect_limit})"
92
+ raise(::Foreman::Exception, N_(format('Too many HTTP redirects when calling %{uri}', uri: uri, code: response.code))) if redirect_limit <= 0
93
+
94
+ self.request(url: new_location,
95
+ payload: payload,
96
+ http_method: http_method,
97
+ user: user,
98
+ password: password,
99
+ content_type: content_type,
100
+ ca_string: ca_string,
101
+ ca_verify: ca_verify,
102
+ headers: headers,
103
+ follow_redirects: follow_redirects,
104
+ redirect_limit: redirect_limit - 1)
105
+ else
106
+ response
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanWebhooks
4
+ class EventSubscriber < ::Foreman::BaseSubscriber
5
+ def call(event)
6
+ ::Webhook.deliver(event_name: event.name, payload: event.payload)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ object @webhook_template
4
+
5
+ attributes :name, :id
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ object @webhook_template
4
+
5
+ extends 'api/v2/webhook_templates/show'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ collection @webhook_templates
4
+
5
+ extends 'api/v2/webhook_templates/main'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ object @webhook_template
4
+
5
+ extends 'api/v2/webhook_templates/base'
6
+
7
+ attributes :description, :created_at, :updated_at
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ object @webhook_template
4
+
5
+ extends 'api/v2/webhook_templates/main'
6
+
7
+ attributes :template, :default, :snippet, :locked
8
+
9
+ node do |webhook_template|
10
+ partial('api/v2/taxonomies/children_nodes', object: webhook_template)
11
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ object @webhook_template
4
+
5
+ extends 'api/v2/webhook_templates/show'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ object @webhook
4
+
5
+ attributes :id, :name
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ object @webhook
4
+
5
+ extends 'api/v2/webhooks/show'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ collection @webhooks
4
+
5
+ extends 'api/v2/webhooks/main'
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ object @webhook
4
+
5
+ extends 'api/v2/webhooks/base'
6
+ extends 'api/v2/layouts/permissions'
7
+
8
+ attributes :target_url, :enabled, :created_at, :updated_at
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ object @webhook
4
+
5
+ extends 'api/v2/webhooks/main'
6
+
7
+ attributes :target_url,
8
+ :event,
9
+ :http_method,
10
+ :http_content_type,
11
+ :enabled,
12
+ :verify_ssl,
13
+ :http_headers
14
+
15
+ child :webhook_template do
16
+ extends 'api/v2/webhook_templates/base'
17
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ object @webhook
4
+
5
+ extends 'api/v2/webhooks/show'
@@ -0,0 +1,20 @@
1
+ <%#
2
+ name: Ansible Tower - Host in Inventory
3
+ description: Create/update a host with associated inventory in Ansible Tower.
4
+ snippet: false
5
+ model: WebhookTemplate
6
+ -%>
7
+ <%#
8
+ Create URL: https://tower/api/v2/hosts
9
+ Update/Delete URL: https://tower/api/v2/hosts/$HOST_ID
10
+ -%>
11
+ <%=
12
+ payload({
13
+ "name": @object.name,
14
+ "description": "Created via Foreman Webhook",
15
+ "inventory": @object.host_param('ansible_tower_inventory_id') || 1,
16
+ "enabled": true,
17
+ "instance_id": "",
18
+ "variables": ""
19
+ }, with_defaults: false)
20
+ -%>
@@ -0,0 +1,6 @@
1
+ <%#
2
+ name: Empty Payload
3
+ description: Useful for Shellhooks which only takes arguments via HTTP parameters.
4
+ snippet: false
5
+ model: WebhookTemplate
6
+ -%>
@@ -0,0 +1,11 @@
1
+ <%#
2
+ name: Webhook Template - Payload Default
3
+ description: This template is used to define default content of payload for a webhook.
4
+ snippet: false
5
+ model: WebhookTemplate
6
+ -%>
7
+ <%=
8
+ payload({
9
+ id: @object.id
10
+ })
11
+ -%>
@@ -0,0 +1,3 @@
1
+ <% if @template.locked? -%>
2
+ <%= locked_warning(::Template.find(params[:id])) %>
3
+ <% end -%>
@@ -0,0 +1 @@
1
+ <li><a href='#template_type' data-toggle='tab'><%= _('Type') %></a></li>
@@ -0,0 +1,5 @@
1
+ <div class='tab-pane' id='template_type'>
2
+ <%= checkbox_f f, :snippet, onchange: 'tfm.templateInputs.snippetChanged(this)',
3
+ label: _('Snippet'),
4
+ disabled: @template.locked? %>
5
+ </div>
@@ -0,0 +1,3 @@
1
+ <% title _("Edit %s") % @template.to_s %>
2
+ <%= breadcrumbs(switcher_item_url: edit_webhook_template_path(':id')) %>
3
+ <%= render partial: 'form' %>
@@ -0,0 +1,29 @@
1
+ <% title _('Webhook Templates') %>
2
+
3
+ <% title_actions new_link(_('Create Template')) %>
4
+
5
+ <table class="<%= table_css_classes 'table-fixed' %>">
6
+ <thead>
7
+ <tr>
8
+ <th class="col-md-3"><%= sort :name, as: s_('WebhookTemplate|Name') %></th>
9
+ <th class="col-md-1"><%= sort :snippet, as: s_('WebhookTemplate|Snippet') %></th>
10
+ <th class="col-md-1"><%= sort :locked, as: s_('WebhookTemplate|Locked'), default: 'DESC' %></th>
11
+ <th class="col-md-1"><%= _('Actions') %></th>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ <% for webhook_template in @templates %>
16
+ <tr>
17
+ <td class="ellipsis"><%= link_to_if_authorized webhook_template,
18
+ hash_for_edit_webhook_template_path(id: webhook_template.to_param).
19
+ merge(auth_object: webhook_template, authorizer: authorizer, permission: 'edit_webhook_templates') %>
20
+ </td>
21
+ <td align='center'><%= checked_icon webhook_template.snippet %></td>
22
+ <td align='center'><%= locked_icon webhook_template.locked?, _('This template is locked for editing.') %>
23
+ </td>
24
+ <td><%= action_buttons(*permitted_actions(webhook_template)) %></td>
25
+ </tr>
26
+ <% end %>
27
+ </tbody>
28
+ </table>
29
+ <%= will_paginate_with_info @templates %>
@@ -0,0 +1,3 @@
1
+ <% title _('Create Template') %>
2
+
3
+ <%= render partial: 'form' %>
@@ -0,0 +1,37 @@
1
+ <%= form_for @webhook, url: (@webhook.new_record? ? webhooks_path : webhook_path(id: @webhook)) do |f| %>
2
+ <%= base_errors_for @webhook %>
3
+ <%= text_f f, :name %>
4
+ <%= text_f f, :target_url, help_inline: _('Target URL that should be called by Foreman') %>
5
+
6
+ <%= text_f f, :user, help_block: _('Authentication credentials') %>
7
+ <%= password_f f, :password, keep_value: true %>
8
+
9
+ <%= selectable_f f, :http_method, Webhook::ALLOWED_HTTP_METHODS,
10
+ { include_blank: false,
11
+ selected: @webhook.http_method },
12
+ { label: _('HTTP Method'),
13
+ label_help: _(f.object.try(:description)),
14
+ required: true } %>
15
+ <%= text_f f, :http_content_type %>
16
+
17
+ <%= render('templates', f: f) %>
18
+
19
+ <%= selectable_f f, :event, Webhook.available_events.sort.map { |e| e.delete_suffix(Webhook::EVENT_POSTFIX) },
20
+ { include_blank: false,
21
+ selected: @webhook.event&.delete_suffix(Webhook::EVENT_POSTFIX) },
22
+ { label: _('Subscribe to'),
23
+ label_help: _(f.object.try(:description)),
24
+ required: true } %>
25
+
26
+ <%= checkbox_f f, :enabled, help_inline: _('If unchecked, the webhook will be inactive') %>
27
+ <%= checkbox_f f, :verify_ssl, help_inline: _("Uncheck this option to disable validation of the receiver's SSL certificate") %>
28
+ <%= textarea_f f, :ssl_ca_certs, label: _('X509 Certification Authorities'),
29
+ size: 'col-md-8', rows: 10,
30
+ placeholder: _("Optional CAs in PEM format concatenated to verify the receiver's SSL certificate.") %>
31
+
32
+ <%= textarea_f f, :http_headers, label: _('Optional HTTP headers as JSON (ERB allowed)'),
33
+ size: 'col-md-8', rows: 6,
34
+ placeholder: "{\n\"X-Shellhook-Arg-1\": \"value\"\n}" %>
35
+
36
+ <%= submit_or_cancel(f, false, { react_cancel_button: true, cancel_path: '/webhooks' }) %>
37
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <%= selectable_f f, :webhook_template_id, WebhookTemplate.where(snippet: false).collect { |t| [t.name, t.id] },
2
+ { include_blank: false },
3
+ { label: _('Webhook Template'),
4
+ label_help: _(f.object.try(:description)),
5
+ required: true } %>
@@ -0,0 +1,3 @@
1
+ <% title _("Edit %s") % @webhook.to_s %>
2
+
3
+ <%= render partial: 'form' %>
@@ -0,0 +1,3 @@
1
+ <% title _('Create Webhook') %>
2
+
3
+ <%= render partial: 'form' %>
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.routes.draw do
4
+ resources :webhooks, except: %i[index show] do
5
+ collection do
6
+ get 'auto_complete_search'
7
+ end
8
+ end
9
+ match '/webhooks' => 'react#index', via: :get
10
+
11
+ namespace :api, defaults: { format: 'json' } do
12
+ scope '(:apiv)',
13
+ module: :v2,
14
+ defaults: { apiv: 'v2' },
15
+ apiv: /v1|v2/,
16
+ constraints: ApiConstraints.new(version: 2, default: true) do
17
+ resources :webhooks, only: %i[index show create update destroy]
18
+ resources :webhook_templates, except: %i[new edit] do
19
+ member do
20
+ post :clone
21
+ get :export
22
+ end
23
+ collection do
24
+ post :import
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ scope 'templates' do
31
+ resources :webhook_templates, except: :show do
32
+ member do
33
+ get 'clone_template'
34
+ get 'lock'
35
+ get 'unlock'
36
+ get 'export'
37
+ post 'preview'
38
+ end
39
+ collection do
40
+ post 'preview'
41
+ get 'auto_complete_search'
42
+ end
43
+ end
44
+ end
45
+ end