Class: Inquirex::Actions::Webhook
- Defined in:
- lib/inquirex/actions/webhook.rb
Overview
Declarative webhook effect: POSTs the completed answers as a JSON envelope (=> {...}) to a statically-declared URL.
Security posture — the URL must be auditable and its host covered by the definition's allowed_domains declaration:
- the URL is a literal string; {field} templates are rejected so the destination host can never depend on user input
- https only; plain http is permitted solely for localhost development
- userinfo (https://user@host/) is rejected
- redirects are not followed (Net::HTTP does not follow them)
- the host check runs in Definition#validate!, which every definition passes through — including JSON-rehydrated ones, so a tampered URL fails at load time, before anything executes
A non-2xx response raises Errors::ActionError, which the Runner records as a :failed result without blocking other actions.
Constant Summary collapse
- DEFAULT_TIMEOUT =
Default open/read timeout in seconds for the webhook POST.
10- LOCAL_HOSTS =
Hosts for which plain http is tolerated (local development only).
%w[localhost 127.0.0.1 ::1 [::1]].freeze
Instance Attribute Summary collapse
-
#headers ⇒ Object
readonly
Returns the value of attribute headers.
-
#timeout ⇒ Object
readonly
Returns the value of attribute timeout.
-
#url ⇒ Object
readonly
Returns the value of attribute url.
Class Method Summary collapse
Instance Method Summary collapse
-
#call(answers, _outbox) ⇒ Net::HTTPResponse
POSTs the answers envelope to the declared URL.
-
#host ⇒ String
Lowercase host of the webhook URL.
-
#initialize(url:, headers: {}, timeout: DEFAULT_TIMEOUT) ⇒ Webhook
constructor
A new instance of Webhook.
-
#to_h ⇒ Hash
Wire format, same shape .from_h accepts.
-
#validate_against(definition) ⇒ void
Enforced from Definition#validate!.
Methods inherited from Base
Constructor Details
#initialize(url:, headers: {}, timeout: DEFAULT_TIMEOUT) ⇒ Webhook
Returns a new instance of Webhook.
35 36 37 38 39 40 41 42 |
# File 'lib/inquirex/actions/webhook.rb', line 35 def initialize(url:, headers: {}, timeout: DEFAULT_TIMEOUT) super() @url = url.to_s @headers = headers.transform_keys(&:to_s).transform_values(&:to_s).freeze @timeout = timeout.to_i @uri = parse_and_check!(@url) freeze end |
Instance Attribute Details
#headers ⇒ Object (readonly)
Returns the value of attribute headers.
30 31 32 |
# File 'lib/inquirex/actions/webhook.rb', line 30 def headers @headers end |
#timeout ⇒ Object (readonly)
Returns the value of attribute timeout.
30 31 32 |
# File 'lib/inquirex/actions/webhook.rb', line 30 def timeout @timeout end |
#url ⇒ Object (readonly)
Returns the value of attribute url.
30 31 32 |
# File 'lib/inquirex/actions/webhook.rb', line 30 def url @url end |
Class Method Details
.from_h(hash) ⇒ Webhook
85 86 87 88 89 90 91 92 |
# File 'lib/inquirex/actions/webhook.rb', line 85 def self.from_h(hash) fetch = ->(key) { hash[key.to_s] || hash[key.to_sym] } new( url: fetch.call(:url), headers: fetch.call(:headers) || {}, timeout: fetch.call(:timeout) || DEFAULT_TIMEOUT ) end |
Instance Method Details
#call(answers, _outbox) ⇒ Net::HTTPResponse
POSTs the answers envelope to the declared URL.
53 54 55 56 57 58 59 60 |
# File 'lib/inquirex/actions/webhook.rb', line 53 def call(answers, _outbox) require "net/http" response = post(answers) code = response.code.to_i return response if (200..299).cover?(code) raise Errors::ActionError, "webhook #{host} responded with HTTP #{response.code}" end |
#host ⇒ String
Returns lowercase host of the webhook URL.
45 |
# File 'lib/inquirex/actions/webhook.rb', line 45 def host = @uri.host.downcase |
#to_h ⇒ Hash
Returns wire format, same shape .from_h accepts.
76 77 78 79 80 81 |
# File 'lib/inquirex/actions/webhook.rb', line 76 def to_h hash = { "type" => "webhook", "url" => @url } hash["headers"] = @headers unless @headers.empty? hash["timeout"] = @timeout unless @timeout == DEFAULT_TIMEOUT hash end |
#validate_against(definition) ⇒ void
This method returns an undefined value.
Enforced from Definition#validate!.
67 68 69 70 71 72 73 |
# File 'lib/inquirex/actions/webhook.rb', line 67 def validate_against(definition) return if definition.allowed_host?(host) raise Errors::DefinitionError, "webhook url host #{host.inspect} is not covered by allowed_domains " \ "#{definition.allowed_domains.inspect} — declare it at the top of the definition" end |