require 'json' require 'httpi' require 'snap_search/connection_exception' require 'snap_search/validation_exception' module SnapSearch # The Client sends an authenticated HTTP request to the SnapChat API and returns the `content` # field from the JSON response body. class Client attr_reader :email, :key, :parameters, :api_url, :ca_cert_file # Create a new Client instance. # # @param [Hash, #to_h] options The options to create the Client with. # @option options [String, #to_s] :email The email to authenticate with. # @option options [String, #to_s] :key The secret authentication key. # @option options [Hash, #to_h] :parameters ({}) The parameters to send with the HTTP request. # @option options [String, #to_s] :api_url (https://snapsearch.io/api/v1/robot) The URL to send the HTTP request to. # @option options [String, #to_s] :ca_cert_file (ROOT/resources/cacert.pem) The CA cert file to use with request. def initialize(options={}) initialize_attributes(options) end # Validate and set the value of the `email` attribute. # # @param [String, #to_s] value The value to set the attribute to. # @return [String] The new value of the attribute. def email=(value) raise TypeError, 'email must be a String or respond to #to_s' unless value.is_a?(String) || respond_to?(:to_s) value = value.to_s raise ArgumentError, 'email must be an email address' unless value.include?(?@) @email = value end # Validate and set the value of the `key` attribute. # # @param [String, #to_s] value The value to set the attribute to. # @return [String] The new value of the attribute. def key=(value) @key = value.nil? ? nil : value.to_s end # Validate and set the value of the `parameters` attribute. # # @param [Hash, #to_h] value The value to set the attribute to. # @return [Hash] The new value of the attribute. def parameters=(value) raise TypeError, 'parameters must be a Hash or respond to #to_h or #to_hash' unless value.is_a?(Hash) || value.respond_to?(:to_h) || value.respond_to?(:to_hash) value = value.to_h rescue value.to_hash @parameters = value.to_h end # Validate and set the value of the `api_url` attribute. # # @param [String, #to_s] value The value to set the attribute to. # @return [String] The new value of the attribute. def api_url=(value) raise TypeError, 'api_url must be a String or respond_to #to_s' unless value.is_a?(String) || value.respond_to?(:to_s) @api_url = value.to_s end # Validate and set the value of the `ca_cert_file` attribute. # # @param [String, #to_s] value The value to set the attribute to. # @return [String] The new value of the attribute. def ca_cert_file=(value) raise TypeError, 'ca_cert_file must be a String or respond_to #to_s' unless value.is_a?(String) || value.respond_to?(:to_s) @ca_cert_file = value.to_s end # Send an authenticated HTTP request to the `api_url` and return the `content` field from the JSON response body. # # @param [String, #to_s] url The url to send in the parameters to the `api_url`. # @return [String] The `content` field from the JSON response body. def request(url) raise TypeError, 'url must be a String or respond_to #to_s' unless url.is_a?(String) || url.respond_to?(:to_s) @parameters['url'] = url.to_s # The URL must contain the entire URL with the _escaped_fragment_ parsed out content_from_response(send_request) end protected # Initialize this instance based on options passed. # # @param [Hash, #to_h] options The options to create the Client with. # @option options [String, #to_s] :email The email to authenticate with. # @option options [String, #to_s] :key The secret authentication key. # @option options [Hash, #to_h] :parameters ({}) The parameters to send with the HTTP request. # @option options [String, #to_s] :api_url (https://snapsearch.io/api/v1/robot) The URL to send the HTTP request to. # @option options [String, #to_s] :ca_cert_file (ROOT/resources/cacert.pem) The CA cert file to use with request. def initialize_attributes(options) raise TypeError, 'options must be a Hash or respond to #to_h' unless options.is_a?(Hash) || options.respond_to?(:to_h) || options.respond_to?(:to_hash) options = options.to_h rescue options.to_hash options = { parameters: {}, api_url: 'https://snapsearch.io/api/v1/robot', ca_cert_file: SnapSearch.root.join('resources', 'cacert.pem') }.merge(options.to_h) self.email, self.key, self.parameters, self.api_url, self.ca_cert_file = options.values_at(:email, :key, :parameters, :api_url, :ca_cert_file) end # Send an authenticated HTTP POST request encoded in JSON to the API URL # using the HTTP client adapter of the developer's choice. # # @return [HTTPI::Response] The HTTP response object. def send_request request = HTTPI::Request.new request.url = api_url request.auth.basic(email, key) request.auth.ssl.ca_cert_file = ca_cert_file request.auth.ssl.verify_mode = :peer request.open_timeout = 30 request.headers['Content-Type'] = 'application/json' request.headers['Accept-Encoding'] = 'gzip, deflate, identity' request.body = @parameters.to_json HTTPI.post(request) rescue raise ConnectionException end # Retrieve the `content` or raise an error based on the `code` field in the JSON response. # # @return [HTTPI::Response] The HTTP response object. def content_from_response(response) body = JSON.parse(response.body) case body['code'] when 'success' then body['content'] when 'validation_error' then raise( ValidationException, body['content'] ) else; false # System error on SnapSearch; Nothing we can do # TODO: Raise exception? end end end end