class HypertrackV3::Engine::WebhookParser

Public Class Methods

call(env) click to toggle source
# File lib/hypertrack_v3.rb, line 222
def self.call(env)
  request = parse_request(env)

  case request.headers['x_amz_sns_message_type']
  when 'SubscriptionConfirmation'
    self.register_with_hypertrack request
  when 'Notification'
    self.dispatch request
  else
    HypertrackV3.log_error(
      "invalid message-type header",
      {
        message_type: request.header['x_amz_sns_messate_type'],
      }.to_json
    )
    self.serve(400, {error: "invalid message-type header"})
  end
end
client() click to toggle source
# File lib/hypertrack_v3.rb, line 137
def self.client
  @@client ||= Faraday.new do |conn|
    conn.use Faraday::Response::Logger, HypertrackV3.logger, bodies: true
    conn.use :instrumentation
    conn.adapter Faraday.default_adapter
  end
end
dispatch(request) click to toggle source
# File lib/hypertrack_v3.rb, line 168
def self.dispatch(request)
  if (cached = Rails.cache.fetch('/hypertrack_v3/subscription_arn')) != request.headers['x_amz_sns_subscription_arn']
    HypertrackV3.log_error(
      "invalid subscription-arn header",
      {
        subscription_arn: {
          request: request.header,
          cache: cached,
        }
      }.to_json
    )
    return self.serve(400, {error: "invalid subscription-arn header"})
  end
  if (cached = Rails.cache.fetch("/hypertrack_v3/#{request.headers['x_amz_sns_message_id']}")).present?
    HypertrackV3.log_error(
      "Message Id already seen",
      {
        sns_message_id: {
          request: request.headers['x_amz_sns_message_id'],
          cache: cached,
        }
      }
    )
    return self.serve(400)
  end
  Rails.cache.write(
    "/hypertrack_v3/#{request.headers['x_amz_sns_message_id']}", true,
    expires_in: 1.hour
  )

  begin
    data = JSON.parse(request.body, object_class: OpenStruct)
  rescue JSON::ParserError => err
    HypertrackV3.log_exception(err)
  end

  res = true
  data.each do |datum|
    if @@hooks.include? datum.type.to_sym
      res &= @@hooks[datum.type.to_sym].(datum.device_id, datum.data, datum.created_at, datum.recorded_at)
    else
      res = false
    end
  end

  self.serve(res ? 200 : 400)
end
parse_request(env) click to toggle source
# File lib/hypertrack_v3.rb, line 241
def self.parse_request(env)
  OpenStruct.new({
    headers: env.select {|k,v| k.to_s.start_with? 'HTTP_'}
                .collect {|key, val| [key.to_s.sub(/^HTTP_/, '').downcase, val]}.to_h,
    params: env['rack.request.query_hash'],
    body: env['rack.input']&.read
  })
end
register_hook(name, callable) click to toggle source
# File lib/hypertrack_v3.rb, line 216
def self.register_hook(name, callable)
  raise RegisterHookError("Invalid name argument: #{name}") unless @@hooks.keys.include? name.to_sym
  raise RegisterHookError("Invalid callable argument: #{callable}") unless callable.respond_to? :call
  @@hooks[name.to_sym] = callable
end
register_with_hypertrack(request) click to toggle source
# File lib/hypertrack_v3.rb, line 155
def self.register_with_hypertrack(request)
  register_data = JSON.parse(request.body)

  resp = self.client.get register_data["SubscribeURL"]
  data = Nokogiri::XML(resp.body)
  data.remove_namespaces!
  token = data.at_xpath('//SubscriptionArn')&.content
  return self.serve(400, {error: 'SubscriptionArn not found'}) if token.empty?
  Rails.cache.write('/hypertrack_v3/subscription_arn', token, expires_in: 100.years)

  self.serve(200)
end
serve(code, params={}) click to toggle source
# File lib/hypertrack_v3.rb, line 250
def self.serve(code, params={})
  [
    code,
    {"Content-Type" => "application/json; charset=utf-8"},
    [params.to_json]
  ]
end