post_agent.rb 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. module Agents
  2. class PostAgent < Agent
  3. cannot_create_events!
  4. default_schedule "never"
  5. description <<-MD
  6. A PostAgent receives events from other agents (or runs periodically), merges those events with the [Liquid-interpolated](https://github.com/cantino/huginn/wiki/Formatting-Events-using-Liquid) contents of `payload`, and sends the results as POST (or GET) requests to a specified url. To skip merging in the incoming event, but still send the interpolated payload, set `no_merge` to `true`.
  7. The `post_url` field must specify where you would like to send requests. Please include the URI scheme (`http` or `https`).
  8. The `method` used can be any of `get`, `post`, `put`, `patch`, and `delete`.
  9. By default, non-GETs will be sent with form encoding (`application/x-www-form-urlencoded`). Change `content_type` to `json` to send JSON instead.
  10. The `headers` field is optional. When present, it should be a hash of headers to send with the request.
  11. MD
  12. event_description "Does not produce events."
  13. def default_options
  14. {
  15. 'post_url' => "http://www.example.com",
  16. 'expected_receive_period_in_days' => '1',
  17. 'content_type' => 'form',
  18. 'method' => 'post',
  19. 'payload' => {
  20. 'key' => 'value',
  21. 'something' => 'the event contained {{ somekey }}'
  22. },
  23. 'headers' => {}
  24. }
  25. end
  26. def working?
  27. last_receive_at && last_receive_at > interpolated['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs?
  28. end
  29. def method
  30. (interpolated['method'].presence || 'post').to_s.downcase
  31. end
  32. def headers
  33. interpolated['headers'].presence || {}
  34. end
  35. def validate_options
  36. unless options['post_url'].present? && options['expected_receive_period_in_days'].present?
  37. errors.add(:base, "post_url and expected_receive_period_in_days are required fields")
  38. end
  39. if options['payload'].present? && !options['payload'].is_a?(Hash)
  40. errors.add(:base, "if provided, payload must be a hash")
  41. end
  42. unless %w[post get put delete patch].include?(method)
  43. errors.add(:base, "method must be 'post', 'get', 'put', 'delete', or 'patch'")
  44. end
  45. if options['no_merge'].present? && !%[true false].include?(options['no_merge'].to_s)
  46. errors.add(:base, "if provided, no_merge must be 'true' or 'false'")
  47. end
  48. unless headers.is_a?(Hash)
  49. errors.add(:base, "if provided, headers must be a hash")
  50. end
  51. end
  52. def receive(incoming_events)
  53. incoming_events.each do |event|
  54. outgoing = interpolated(event)['payload'].presence || {}
  55. if interpolated['no_merge'].to_s == 'true'
  56. handle outgoing, event.payload
  57. else
  58. handle outgoing.merge(event.payload), event.payload
  59. end
  60. end
  61. end
  62. def check
  63. handle interpolated['payload'].presence || {}
  64. end
  65. def generate_uri(params = nil, payload = {})
  66. uri = URI interpolated(payload)[:post_url]
  67. uri.query = URI.encode_www_form(Hash[URI.decode_www_form(uri.query || '')].merge(params)) if params
  68. uri
  69. end
  70. private
  71. def handle(data, payload = {})
  72. if method == 'post'
  73. post_data(data, payload, Net::HTTP::Post)
  74. elsif method == 'put'
  75. post_data(data, payload, Net::HTTP::Put)
  76. elsif method == 'delete'
  77. post_data(data, payload, Net::HTTP::Delete)
  78. elsif method == 'patch'
  79. post_data(data, payload, Net::HTTP::Patch)
  80. elsif method == 'get'
  81. get_data(data, payload)
  82. else
  83. error "Invalid method '#{method}'"
  84. end
  85. end
  86. def post_data(data, payload, request_type = Net::HTTP::Post)
  87. uri = generate_uri(nil, payload)
  88. req = request_type.new(uri.request_uri, headers)
  89. if interpolated(payload)['content_type'] == 'json'
  90. req.set_content_type('application/json', 'charset' => 'utf-8')
  91. req.body = data.to_json
  92. else
  93. req.form_data = data
  94. end
  95. Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) }
  96. end
  97. def get_data(data, payload)
  98. uri = generate_uri(data, payload)
  99. req = Net::HTTP::Get.new(uri.request_uri, headers)
  100. Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) }
  101. end
  102. end
  103. end