lib/attune/client.rb in attune-0.0.3 vs lib/attune/client.rb in attune-0.0.4

- old
+ new

@@ -5,10 +5,16 @@ def initialize(message="Attune library disabled though config") super(message) end end + class AuthenticationException < Faraday::Error::ClientError + def initialize(message="Authentication credentials not accepted") + super(message) + end + end + class Client include Attune::Configurable # Initializes a new Client # @@ -17,17 +23,48 @@ # endpoint: "http://example.com:8080/", # timeout: 10 # ) # # @param [Hash] options Options for connection (see Attune::Configurable) - # @returns A new client object + # @return A new client object def initialize(options={}) Attune::Configurable::KEYS.each do |key| send("#{key}=", options[key] || Attune::Default.send(key)) end end + # Request an auth token + # + # @example Generate a new auth token + # token = client.get_auth_token("client id", "secret") + # @param [String] client_id The client identifier. + # @param [String] client_secret The secret key for the client. + # @return An auth token if credentials accepted + # @raise [ArgumentError] if client_id or client_secret is not provided + # @raise [AuthenticationException] if client_id or client_secret are not accepted + # @raise [Faraday::Error::ClientError] if the request fails or exceeds the timeout + def get_auth_token(client_id, client_secret) + raise ArgumentError, "client_id required" unless client_id + raise ArgumentError, "client_secret required" unless client_secret + + response = post_form("oauth/token", + client_id: client_id, + client_secret: client_secret, + grant_type: :client_credentials + ) + if response + body = JSON.parse(response.body) + if body['error'] + raise AuthenticationException, body['error_description'] + end + body['access_token'] + else + # Return a new UUID if there was an exception and we're in mock mode + SecureRandom.uuid + end + end + # Create an anonymous tracked user # # @example Generate a new id (preferred) # anonymous_id = client.create_anonymous( # user_agent: 'Mozilla/5.0' @@ -41,17 +78,18 @@ # @option options [String] :id optional. An id will be generated if this is not provided # @option options [String] :user_agent The user agent for the application used by the anonymous users # @return id [String] # @raise [ArgumentError] if user_agent is not provided # @raise [Faraday::Error::ClientError] if the request fails or exceeds the timeout + # @raise [AuthenticationException] if authorization header not accepted def create_anonymous(options) raise ArgumentError, "user_agent required" unless options[:user_agent] if id = options[:id] put("anonymous/#{id}", {user_agent: options[:user_agent]}) id else - if response = post("anonymous", {user_agent: options[:user_agent]}) + if response = post_json("anonymous", {user_agent: options[:user_agent]}) response[:location][/\Aurn:id:([a-z0-9\-]+)\Z/, 1] else # Return a new UUID if there was an exception and we're in mock mode SecureRandom.uuid end @@ -75,10 +113,11 @@ # @option options [String] :ip ip address of remote user. Used for geolocation (optional) # @option options [String] :customer id of customer (optional) # @return ranking [Array<String>] The entities in their ranked order # @raise [ArgumentError] if required parameters are missing # @raise [Faraday::Error::ClientError] if the request fails or exceeds the timeout + # @raise [AuthenticationException] if authorization header not accepted def get_rankings(options) qs = encoded_ranking_params(options) if response = get("rankings/#{qs}", customer: options.fetch(:customer, 'none')) JSON.parse(response.body)['ranking'] else @@ -105,10 +144,11 @@ # } # ]) # @param [Array<Hash>] multi_options An array of options (see #get_rankings) # @return [Array<Array<String>>] rankings # @raise [Faraday::Error::ClientError] if the request fails or exceeds the timeout + # @raise [AuthenticationException] if authorization header not accepted def multi_get_rankings(multi_options) requests = multi_options.map do |options| encoded_ranking_params(options) end if response = get("rankings", ids: requests) @@ -137,10 +177,11 @@ # rankings = client.bind( # '25892e17-80f6-415f-9c65-7395632f022', # 'cd171f7c-560d-4a62-8d65-16b87419a58' # ) # @raise [Faraday::Error::ClientError] if the request fails or exceeds the timeout + # @raise [AuthenticationException] if authorization header not accepted def bind(id, customer_id) put("bindings/anonymous=#{id}&customer=#{customer_id}") true end @@ -166,25 +207,41 @@ adapter.put(path, ::JSON.dump(params)) rescue Faraday::Error::ClientError => e handle_exception(e) end - def post(path, params={}) - adapter.post(path, ::JSON.dump(params)) + def post_form(path, params={}) + adapter.post(path, params) rescue Faraday::Error::ClientError => e handle_exception(e) end + def post_json(path, params={}) + adapter.post do |req| + req.url path + req.headers['Content-Type'] = 'application/json' + req.body = ::JSON.dump(params) + end + rescue Faraday::Error::ClientError => e + handle_exception(e) + end + def handle_exception e if exception_handler == :mock nil else - raise e + if e.response && e.response[:status] == 401 + raise AuthenticationException, e + else + raise e + end end end def adapter raise DisabledException if disabled? - Faraday.new(url: endpoint, builder: middleware, request: {timeout: timeout}) + conn = Faraday.new(url: endpoint, builder: middleware, request: {timeout: timeout}) + conn.authorization :Bearer, auth_token unless !auth_token + conn end end end