lib/3scale/client.rb in 3scale_client-2.10.0 vs lib/3scale/client.rb in 3scale_client-2.11.0

- old
+ new

@@ -5,11 +5,11 @@ require '3scale/client/http_client' require '3scale/client/version' require '3scale/response' require '3scale/authorize_response' -require '3scale/rack_query' +require '3scale/client/rack_query' module ThreeScale Error = Class.new(RuntimeError) class ServerError < Error @@ -23,13 +23,13 @@ # Wrapper for 3scale Web Service Management API. # # == Example # - # client = ThreeScale::Client.new(:provider_key => "your provider key") + # client = ThreeScale::Client.new(service_tokens: true) # - # response = client.authorize(:app_id => "an app id", :app_key => "a secret key") + # response = client.authorize(service_token: 'token', service_id: '123', app_id: 'an app id', app_key: 'a secret key') # # if response.success? # response = client.report(:app_id => "some app id", :usage => {"hits" => 1}) # # if response.success? @@ -37,41 +37,47 @@ # else # # something's wrong. # end # end # + # Note: Provider Keys are deprecated in favor of Service Tokens with Service IDs + # The next major release of this client will default to use Service Tokens. + # class Client DEFAULT_HOST = 'su1.3scale.net' + DEPRECATION_MSG_PROVIDER_KEY = 'provider keys are deprecated - ' \ + 'please switch at your earliest convenience to use service tokens'.freeze + private_constant :DEPRECATION_MSG_PROVIDER_KEY DEPRECATION_MSG_OLD_REPORT = 'warning: def report(*transactions) is '\ 'deprecated. In next versions, the signature of the report method is '\ 'going to be: '\ 'def report(transactions: [], service_id: nil).'.freeze private_constant :DEPRECATION_MSG_OLD_REPORT EXTENSIONS_HEADER = '3scale-options'.freeze private_constant :EXTENSIONS_HEADER def initialize(options) - if options[:provider_key].nil? || options[:provider_key] =~ /^\s*$/ - raise ArgumentError, 'missing :provider_key' - end - @provider_key = options[:provider_key] + @service_tokens = options[:service_tokens] + @warn_deprecated = options.fetch(:warn_deprecated, true) + generate_creds_params + @host = options[:host] ||= DEFAULT_HOST @http = ThreeScale::Client::HTTPClient.new(options) end - attr_reader :provider_key, :host, :http + attr_reader :provider_key, :service_tokens, :host, :http # Authorize and report an application. # TODO (in the mean time read authorize comments or head over to https://support.3scale.net/reference/activedocs#operation/66 for details # def authrep(options) - path = "/transactions/authrep.xml?provider_key=#{CGI.escape(provider_key)}" + path = "/transactions/authrep.xml?#{creds_params(options)}" options_usage = options.delete :usage options_log = options.delete :log extensions = options.delete :extensions @@ -106,13 +112,13 @@ # Report transaction(s). # # == Parameters # - # Hash with two fields: + # Hash with up to three fields: # - # transactions:: It is required. It is an enumerable. Each element is a hash with the fields: + # transactions:: Required. Enumerable. Each element is a hash with the fields: # app_id: ID of the application to report the transaction for. This parameter is # required. # usage: Hash of usage values. The keys are metric names and values are # corresponding numeric values. Example: {'hits' => 1, 'transfer' => 1024}. # This parameter is required. @@ -121,12 +127,13 @@ # (if the time is in the UTC), or a string in # the "YYYY-MM-DD HH:MM:SS ZZZZZ" format, where the ZZZZZ is the time offset # from the UTC. For example, "US Pacific Time" has offset -0800, "Tokyo" # has offset +0900. This parameter is optional, and if not provided, equals # to the current time. - # service_id:: ID of the service. It is optional. When not specified, the transactions - # are reported to the default service. + # service_id:: ID of the service. It is optional. When not specified, the transactions + # are reported to the default service. + # service_token:: Token granting access to the specified service ID. # # == Return # # A Response object with method +success?+ that returns true if the report was successful, # or false if there was an error. See ThreeScale::Response class for more information. @@ -152,24 +159,29 @@ # == Note # # The signature of this method is a bit complicated because we decided to # keep backwards compatibility with a previous version of the method: # def report(*transactions) - def report(*reports, transactions: [], service_id: nil, extensions: nil, **rest) + def report(*reports, transactions: [], service_id: nil, extensions: nil, service_token: nil, **rest) if (!transactions || transactions.empty?) && rest.empty? raise ArgumentError, 'no transactions to report' end transactions = transactions.concat(reports) unless rest.empty? - warn(DEPRECATION_MSG_OLD_REPORT) + warn DEPRECATION_MSG_OLD_REPORT if @warn_deprecated transactions.concat([rest]) end payload = encode_transactions(transactions) - payload['provider_key'] = CGI.escape(provider_key) + if @service_tokens + raise ArgumentError, "service_token or service_id not specified" unless service_token && service_id + payload['service_token'] = CGI.escape(service_token) + else + payload['provider_key'] = CGI.escape(@provider_key) + end payload['service_id'] = CGI.escape(service_id.to_s) if service_id headers = extensions_to_header extensions if extensions http_response = @http.post('/transactions.xml', payload, headers: headers) @@ -187,18 +199,19 @@ # # == Parameters # # Hash with options: # - # app_id:: id of the application to authorize. This is required. - # app_key:: secret key assigned to the application. Required only if application has - # a key defined. - # service_id:: id of the service (required if you have more than one service) - # usage:: predicted usage. It is optional. It is a hash where the keys are metrics - # and the values their predicted usage. - # Example: {'hits' => 1, 'my_metric' => 100} - # extensions:: Optional. Hash of extension keys and values. + # service_token:: token granting access to the specified service_id. + # app_id:: id of the application to authorize. This is required. + # app_key:: secret key assigned to the application. Required only if application has + # a key defined. + # service_id:: id of the service (required if you have more than one service) + # usage:: predicted usage. It is optional. It is a hash where the keys are metrics + # and the values their predicted usage. + # Example: {'hits' => 1, 'my_metric' => 100} + # extensions:: Optional. Hash of extension keys and values. # # == Return # # An ThreeScale::AuthorizeResponse object. It's +success?+ method returns true if # the authorization is successful, false otherwise. It contains additional information @@ -217,11 +230,12 @@ # # All good. Proceed... # end # def authorize(options) extensions = options.delete :extensions - path = "/transactions/authorize.xml" + options_to_params(options, ALL_PARAMS) + creds = creds_params(options) + path = "/transactions/authorize.xml" + options_to_params(options, ALL_PARAMS) + '&' + creds headers = extensions_to_header extensions if extensions http_response = @http.get(path, headers: headers) case http_response @@ -238,15 +252,16 @@ # # == Parameters # # Hash with options: # - # app_id:: id of the application to authorize. This is required. - # service_id:: id of the service (required if you have more than one service) - # usage:: predicted usage. It is optional. It is a hash where the keys are metrics - # and the values their predicted usage. - # Example: {'hits' => 1, 'my_metric' => 100} + # service_token:: token granting access to the specified service_id. + # app_id:: id of the application to authorize. This is required. + # service_id:: id of the service (required if you have more than one service) + # usage:: predicted usage. It is optional. It is a hash where the keys are metrics + # and the values their predicted usage. + # Example: {'hits' => 1, 'my_metric' => 100} # # == Return # # A ThreeScale::AuthorizeResponse object. It's +success?+ method returns true if # the authorization is successful, false otherwise. It contains additional information @@ -268,11 +283,12 @@ # # All good. Proceed... # end # def oauth_authorize(options) extensions = options.delete :extensions - path = "/transactions/oauth_authorize.xml" + options_to_params(options, OAUTH_PARAMS) + creds = creds_params(options) + path = "/transactions/oauth_authorize.xml" + options_to_params(options, OAUTH_PARAMS) + '&' + creds headers = extensions_to_header extensions if extensions http_response = @http.get(path, headers: headers) case http_response @@ -290,11 +306,11 @@ OAUTH_PARAMS = [:app_id, :app_key, :service_id, :redirect_url, :usage] ALL_PARAMS = [:user_key, :app_id, :app_key, :service_id, :redirect_url, :usage] REPORT_PARAMS = [:user_key, :app_id, :service_id, :timestamp] def options_to_params(options, allowed_keys) - params = { :provider_key => provider_key } + params = {} (allowed_keys - [:usage]).each do |key| params[key] = options[key] if options.has_key?(key) end @@ -394,8 +410,28 @@ end # Encode extensions header def extensions_to_header(extensions) { EXTENSIONS_HEADER => RackQuery.encode(extensions) } + end + + # helper to generate the creds_params method + def generate_creds_params + define_singleton_method :creds_params, + if @service_tokens + lambda do |options| + token = options.delete(:service_token) + service_id = options[:service_id] + raise ArgumentError, "need to specify a service_token and a service_id" unless token && service_id + 'service_token='.freeze + CGI.escape(token) + end + elsif @provider_key + warn DEPRECATION_MSG_PROVIDER_KEY if @warn_deprecated + lambda do |_| + "provider_key=#{CGI.escape @provider_key}".freeze + end + else + raise ArgumentError, 'missing credentials - either use "service_tokens: true" or specify a provider_key value' + end end end end