lib/citygrid/api.rb in citygrid_api-0.0.5 vs lib/citygrid/api.rb in citygrid_api-0.0.5.1
- old
+ new
@@ -1,3 +1,190 @@
-%w{base response content}.each do |api|
- require "citygrid/api/#{api}"
-end
+require "httparty"
+require "json"
+
+class CityGrid
+ class API
+ include HTTParty
+ format :json
+ #debug_output $stderr
+
+ class << self
+ # server setting - :default or :ssl
+ def server val=nil
+ return @server || (superclass.respond_to?(:server) ? superclass.server : nil) unless val
+ @server = val
+ end
+
+ def hostname val=nil
+ return @hostname || (superclass.respond_to?(:hostname) ? superclass.hostname : nil) unless val
+ @hostname = val
+ end
+
+ def endpoint val=nil
+ return @endpoint unless val
+ @endpoint = val
+ end
+
+ def publisher
+ CityGrid.publisher
+ end
+
+ def request options = {}
+ method = (options.delete(:method) || :get).to_sym
+ query = options.merge :format => :json
+ request_and_handle method, endpoint, :query => query
+ end
+
+ def request_with_publisher options = {}
+ request options.merge(:publisher => publisher)
+ end
+
+ def mutate options = {}
+ token = extract_auth_token options
+ request_and_handle :post,
+ "#{endpoint}/mutate",
+ :body => options.to_json,
+ :headers => merge_headers("authToken" => token)
+ end
+
+ def search options = {}
+ token = extract_auth_token options
+ request_and_handle :get,
+ "#{endpoint}/get",
+ :query => options,
+ :headers => merge_headers("authToken" => token)
+ end
+
+ private
+ def extract_auth_token options = {}
+ options.delete(:token) #|| raise(MissingAuthToken)
+ end
+
+ def merge_headers options = {}
+ {"Accept" => "application/json",
+ "Content-Type" => "Application/JSON"}.merge options
+ end
+
+ def convert_to_querystring hash
+ hash.map do |k, v|
+ key = URI.escape k.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
+ val = URI.escape v.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
+ !value.empty? && !key.empty? ? "#{key}=#{value}" : nil
+ end.compact.join("&")
+ end
+
+ HTTP_METHODS = {
+ "put" => Net::HTTP::Put,
+ "get" => Net::HTTP::Get,
+ "post" => Net::HTTP::Post,
+ "delete" => Net::HTTP::Delete,
+ "head" => Net::HTTP::Head,
+ "options" => Net::HTTP::Options
+ }
+
+ # Transform response into API::Response object
+ # or throw exception if an error exists
+ def request_and_handle http_method, path, options
+ if http_method.is_a?(String) || http_method.is_a?(Symbol)
+ http_method = HTTP_METHODS[http_method.to_s]
+ raise "Unknown http method: #{http_method}" unless http_method
+ end
+
+ req_options = default_options.dup
+ req_options = req_options.merge({:base_uri => CityGrid.config[server]}) if !base_uri && server
+ req_options = req_options.merge(options)
+
+ req = HTTParty::Request.new http_method, path, req_options
+ error = nil
+
+ begin
+ response = req.perform
+ rescue => ex
+ puts "Something went wrong with Request.perform"
+ error = StandardError.new "Internal Error"
+ rescue Psych::SyntaxError => ex
+ puts "Something went wrong with Request.perform, Psych:SyntaxError"
+ error = StandardError.new "Internal Error"
+ end
+
+ if defined?(Rails.logger)
+ Rails.logger.info req.to_curl
+ else
+ puts req.to_curl
+ end
+
+ unless error
+ if !response.parsed_response.is_a?(Hash)
+ error = InvalidResponseFormat.new response
+ elsif response["errors"]
+ error = Error.new response["errors"], response
+ else
+ return CityGrid::API::Response.new response
+ end
+ end
+
+ if error
+ puts "an error happened"
+ puts req.to_json
+ #raise error
+ end
+ end
+ end
+
+ # ERRORS
+ class GenericError < StandardError
+ attr_reader :httparty, :message
+
+ def initialize msg, response = nil
+ @message = msg
+ @httparty = response
+ end
+ end
+
+ class Error < GenericError
+ def initialize errors, response
+ super errors.first["error"], response
+ end
+ end
+
+ class InvalidResponseFormat < GenericError
+ attr_accessor :server_msg, :description
+ def initialize response = nil
+ # parse Tomcat error report
+ if response.match /<title>Apache Tomcat.* - Error report<\/title>/
+ response.scan(/<p><b>(message|description)<\/b> *<u>(.*?)<\/u><\/p>/).each do |match|
+ case match[0]
+ when "message"
+ self.server_msg = match[1]
+ when "description"
+ self.description = match[1]
+ end
+ end
+
+ error_body = response.match(/<body>(.*?)<\/body>/m)[1]
+
+ msg = <<-EOS
+ Unexpected response format. Expected response to be a hash, but was instead:\n#{error_body}\n
+ EOS
+
+ super msg, error_body
+ else
+ msg = <<-EOS
+ Unexpected response format. Expected response to be a hash, but was instead:\n#{response.parsed_response}\n
+ EOS
+
+ super msg, response
+ end
+ end
+ end
+
+ class MissingAuthToken < GenericError
+ def initialize
+ super message
+ end
+
+ def message
+ "Missing authToken - token is required"
+ end
+ end
+ end
+ end
\ No newline at end of file