require "json" require "uri" require "time" require "net/http" require "net/https" module Keen class Client attr_accessor :storage_handler, :project_id, :auth_token, :options, :logging def base_url if @options[:base_url] @options[:base_url] else "https://api.keen.io/2.0" end end def initialize(project_id, auth_token, options = {}) raise "project_id must be string" unless project_id.kind_of? String raise "auth_token must be string" unless auth_token.kind_of? String default_options = { :logging => true, # should we cache events locally somewhere? if false, sends directly to Keen :cache_locally => false, # this is required if cache_locally is true: :storage_class => nil, # all keys will be prepended with this: :storage_namespace => "default", } options = default_options.update(options) @project_id = project_id @auth_token = auth_token @cache_locally = options[:cache_locally] if @cache_locally @storage_class = options[:storage_class] unless @storage_class and @storage_class < Keen::Async::Storage::BaseStorageHandler raise "The Storage Class you send must extend BaseStorageHandler. You sent: #{@storage_class}" end end @logging = options[:logging] @options = options end def storage_handler unless @storage_handler @storage_handler = @storage_class.new(self) end @storage_handler end def add_event(collection_name, event_body, timestamp=nil) # # `collection_name` should be a string # # `event_body` should be a JSON-serializable hash # # `timestamp` is optional. If sent, it should be a Time instance. # If it's not sent, we'll use the current time. validate_collection_name(collection_name) unless timestamp timestamp = Time.now end timestamp = timestamp.utc.iso8601 event = Keen::Event.new(timestamp, collection_name, event_body) if @cache_locally job = Keen::Async::Job.new(storage_handler, { :timestamp => event.timestamp, :project_id => @project_id, :auth_token => @auth_token, :collection_name => collection_name, :event_body => event.body, }) job.save else # build the request: url = "#{base_url}/projects/#{project_id}/#{collection_name}" uri = URI.parse(url) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.ca_file = Keen::Async::SSL_CA_FILE http.verify_mode = OpenSSL::SSL::VERIFY_PEER http.verify_depth = 5 request = Net::HTTP::Post.new(uri.path) request.body = JSON.generate({ :header => { :timestamp => event.timestamp, }, :body => event.body }) request["Content-Type"] = "application/json" request["Authorization"] = @auth_token response = http.request(request) JSON.parse response.body end end def send_batch(events) # make the request body: request_body = {} events.each { |event| unless request_body.has_key? event.collection_name request_body[event.collection_name] = [] end header = {"timestamp" => event.timestamp} body = event.body item = {"header" => header, "body" => body} request_body[event.collection_name].push(item) } request_body = request_body.to_json # build the request: url = "#{base_url}/projects/#{project_id}/_events" uri = URI.parse(url) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.ca_file = Keen::Async::SSL_CA_FILE http.verify_mode = OpenSSL::SSL::VERIFY_PEER http.verify_depth = 5 request = Net::HTTP::Post.new(uri.path) request.body = request_body request["Content-Type"] = "application/json" request["Authorization"] = auth_token resp = http.request(request) if @logging puts resp.body end return JSON.parse resp.body end def validate_collection_name(collection_name) # TODO end def self.create_new_storage_handler(storage_mode, client, logging) # This is shitty as hell. We shoudl take in a class reference pointing to the storage handler, not switch on string values case storage_mode.to_sym when :redis Keen::Async::Storage::RedisHandler.new(client, logging) else raise "Unknown storage_mode sent to client: `#{storage_mode}`" end end end end