foreman_webhooks 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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