require File.join(File.dirname(__FILE__), "service") require 'securerandom' module Charrington class TransformRedshift include Service attr_accessor :event attr_reader :top_level_keys Error = Class.new(StandardError) EventNil = Class.new(Error) TableNameNil = Class.new(Error) ColumnBlacklist = Class.new(Error) KEY_FILTER_BLACKLIST = ['host','path','jwt','sequence'] KEY_RAISE_BLACKLIST = ['inserted_at'] def initialize(event) raise EventNil, "Event is nil" if event.nil? event = event.to_hash @event = drop_keys(event) @top_level_keys = @event.keys check_blacklist end def call handle_event_key(event) add_id_to_event(event) handle_key_transform(event, "anonymous_id", "anonymous_user") handle_key_transform(event, "sent_at", "published_at") handle_key_transform(event, "original_timestamp", "sent_at") handle_key_transform(event, "received_at", "sent_at") handle_key_transform(event, "timestamp", "sent_at") handle_meta_section(event) transform_session_stuff(event) event.delete_if {|k, _v| ['session', 'meta', 'published_at', 'anonymous_user'].include?(k) } event end private def handle_key_transform(hash, key_that_should_be_there, key_to_take_value_from) unless hash.has_key?(key_that_should_be_there) hash[key_that_should_be_there] = hash[key_to_take_value_from] || "" else hash end end def add_id_to_event(hash) hash["id"] = SecureRandom.hex(10) end def handle_event_key(hash) event_name = hash["event"] || "" hash["event_text"] = event_name hash["event"] = underscore_event_name(event_name) end def underscore_event_name(event_name) event_name. gsub(/::/, '/'). gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). gsub(/([a-z\d])([A-Z])/,'\1_\2'). downcase. gsub(/[^a-z0-9]+/, "_"). gsub(/\A_+/, ""). gsub(/_+\z/, "") end def transform_session_stuff(hash) session_stuff = hash["session"] || {} session_stuff.each {|k, v| hash["context_#{k}"] = v } end def handle_meta_section(hash) meta_section = hash["meta"] || {} meta_section.each {|k, v| hash[k] = v } end def check_blacklist arr = [] KEY_RAISE_BLACKLIST.each { |k| arr << k if event.keys.include?(k) } raise ColumnBlacklist, "Event contains these blacklisted keys: #{arr.join(",")}" unless arr.empty? end def drop_keys(event) event.delete_if {|k, _v| k.start_with?("@") || KEY_FILTER_BLACKLIST.include?(k) } end def flatten_hash(hash) hash.each_with_object({}) do |(k, v), acc| if v.is_a? Hash flatten_hash(v).map do |h_k, h_v| acc["#{k}_#{h_k}"] = h_v end else acc[k] = v end end end end end