require 'net/http' require 'net/https' require 'uri' require 'multi_json' require 'hashie' require 'addressable/uri' require 'totter/client/users' require 'totter/client/decisions' require 'totter/client/slugs' require 'totter/client/timelines' require 'totter/client/choices' require 'totter/client/votes' require 'totter/client/avatars' module Totter # API client for interacting with the Seesaw API class Client include Totter::Client::Decisions include Totter::Client::Users include Totter::Client::Slugs include Totter::Client::Timelines include Totter::Client::Choices include Totter::Client::Votes include Totter::Client::Avatars attr_reader :access_token attr_reader :api_scheme attr_reader :api_host attr_reader :api_version # Initialize a new client. # # @param options [Hash] optionally specify `:access_token`, `:api_scheme`, `:api_host`, `:api_version`, or `:client_token` def initialize(options = {}) options = { access_token: options } if options.is_a? String @access_token = options[:access_token] if options[:access_token] @api_scheme = (options[:api_scheme] or 'https') @api_host = (options[:api_host] or 'api.seesaw.co') @api_version = (options[:api_version] or 1) @client_token = options[:client_token] if options[:client_token] end # API base URL. # # @return [String] API base URL def base_url "#{@api_scheme}://#{@api_host}/v#{@api_version}/" end # Is the client has an access token. # # @return [Boolean] true if it is using one and false if it is not def authenticated? @access_token != nil and @access_token.length > 0 end # Is the client using SSL. # # @return [Boolean] true if it is using SSL and false if it is not def ssl? @api_scheme == 'https' end private def http return @http if @http uri = URI.parse(self.base_url) @http = Net::HTTP.new(uri.host, uri.port) @http.use_ssl = self.ssl? @http end def request(method, path, params = nil) uri = Addressable::URI.parse("#{self.base_url}#{path}") # if the request requires parameters in the query string, merge them in if params and !can_post_data?(method) uri.query_values = (uri.query_values || {}).merge(params) end # Build request request = build_request(method, uri) # Add headers request['Authorization'] = "Bearer #{self.access_token}" if authenticated? request['X-Seesaw-Client-Token'] = @client_token if @client_token request['Content-Type'] = 'application/json' # Add params as JSON if they exist request.body = MultiJson.dump(params) if can_post_data?(method) and params # Request response = http.request(request) # Check for errors handle_error(response) # Return the raw response object response end def build_request(method, uri) case method when :get Net::HTTP::Get.new(uri.request_uri) when :post Net::HTTP::Post.new(uri.request_uri) when :put Net::HTTP::Put.new(uri.request_uri) when :delete Net::HTTP::Delete.new(uri.request_uri) end end def handle_error(response) # Find error or return return unless error = Totter::ERROR_MAP[response.code.to_i] # Try to add a useful message message = nil begin message = MultiJson.load(response.body)['error_description'] rescue MultiJson::DecodeError => e end # Raise error raise error.new(message) end def json_request(*args) # Preform request response = request(*args) # Parse JSON object = MultiJson.load(response.body) # Hash return Hashie::Mash.new(object) if object.is_a? Hash # Array return object.map { |h| Hashie::Mash.new(h) } if object.is_a? Array # Fallback incase it's not a hash or array object end def boolean_from_response(*args) response = request(*args) (200..299).include? response.code.to_i end def can_post_data?(method) [:post, :put].include?(method) end [:get, :post, :put, :delete].each do |method| define_method method do |*args| json_request(method, *args) end end end end