3scale_client 2.10.0 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a0c46a8b42107a91987d9861498f796287909fb5
4
- data.tar.gz: 5d43b1ee44766fe2057fec8eea21f5a30a8ac83f
3
+ metadata.gz: 2d32c601eff7116122dfc1e2054d2a25a7d8717d
4
+ data.tar.gz: b1cf03f30513e43ef8662724a7c869a50d061e68
5
5
  SHA512:
6
- metadata.gz: 1c3b09b26c8ec29367f68fc231b52c3d7af036d8360179b968d50d231a2a1332ba5b0c11139539e4af6a711cb2716fe47972eaa34e66fb4ba8addc29c876e33b
7
- data.tar.gz: 3d3136b451d4694fc00291d013e07dd1fd562f4c605caf1bc32a7c4f1e5ca6c6bb9808eed46f54909d7e551fea5f7260363d6ef5c75aca9ef4f00b85f9d6c717
6
+ metadata.gz: 032857a37711005b6790446648c4794c133fd98edd7508f53e89d0d1f95c6e9d6ffde13030567538d089004108f9a9c9ba7445e95c2bb8919e38f8474e9f83eb
7
+ data.tar.gz: f853e1abaceb96ee439cbafbaa1a420e004cf29888db70bcef5e1acd9f3df97f22117571bb772e86d2b928746f0e5d2dc777bac4098d65f91adfc51e3fdb5344
@@ -1,6 +1,19 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [2.11.0] - 2017-01-20
5
+ ### Added
6
+ - Added support for (Service Tokens)[https://support.3scale.net/docs/accounts/tokens]
7
+ Just instantiate the client with `ThreeScale::Client.new(service_tokens: true)`
8
+ and specify in each call the `service_token` and `service_id` parameters.
9
+ - Deprecated usage of `provider_key` when instantiating the client.
10
+ - Deprecated usage of the provided Rack Auth middleware. You should write your own.
11
+ - Added optional parameter `warn_deprecated` defaulting to `true` to be able to
12
+ opt out of deprecation warnings. It is encouraged to not turn this off unless
13
+ you are sure you understand all deprecation warnings you get, and even so good
14
+ practice suggests you should turn it back on each time you upgrade this client
15
+ to check for new warnings.
16
+
4
17
  ## [2.10.0] - 2016-11-25
5
18
  ### Added
6
19
  - Added support for 3scale extensions (experimental or non-standard
data/README.md CHANGED
@@ -6,9 +6,9 @@
6
6
  3scale is an API Infrastructure service which handles API Keys, Rate Limiting, Analytics, Billing Payments and Developer Management. Includes a configurable API dashboard and developer portal CMS. More product stuff at http://www.3scale.net/, support information at http://support.3scale.net/.
7
7
 
8
8
  ### Tutorials
9
- Plugin Setup: https://support.3scale.net/howtos/api-configuration/plugin-setup
10
- Rate Limiting: https://support.3scale.net/howtos/basics/provision-rate-limits
11
- Analytics Setup: https://support.3scale.net/quickstarts/3scale-api-analytics
9
+ * Plugin Setup: https://support.3scale.net/howtos/api-configuration/plugin-setup
10
+ * Rate Limiting: https://support.3scale.net/howtos/basics/provision-rate-limits
11
+ * Analytics Setup: https://support.3scale.net/quickstarts/3scale-api-analytics
12
12
 
13
13
  ## Installation
14
14
 
@@ -36,28 +36,53 @@ Otherwise, require the gem in whatever way is natural to your framework of choic
36
36
 
37
37
  ## Usage
38
38
 
39
- First, create an instance of the client, giving it your provider API key:
39
+ First, create an instance of the client:
40
40
 
41
41
  ```ruby
42
- client = ThreeScale::Client.new(:provider_key => "your provider key")
42
+ ThreeScale::Client.new(service_tokens: true)
43
43
  ```
44
+
45
+ > NOTE: unless you specify `service_tokens: true` you will be expected to specify
46
+ a `provider_key` parameter, which is deprecated in favor of Service Tokens:
47
+ ```ruby
48
+ client = ThreeScale::Client.new(provider_key: 'your_provider_key')
49
+ ```
50
+
44
51
  Because the object is stateless, you can create just one and store it globally.
45
52
 
53
+ Then you can perform calls in the client:
54
+
55
+ ```ruby
56
+ client.authorize(service_token: 'token', service_id: '123', usage: usage)
57
+ client.report(service_token: 'token', service_id: '123', usage: usage)
58
+ ```
59
+
60
+ If you had configured a (deprecated) provider key, you would instead use:
61
+
62
+ ```ruby
63
+ client.authrep(service_id: '123', usage: usage)
64
+ ```
65
+
66
+ > NOTE: `service_id` is mandatory since November 2016, both when using service
67
+ tokens and when using provider keys
68
+
69
+ > NOTE: You might use the option `warn_deprecated: false` to avoid deprecation
70
+ warnings. This is enabled by default.
46
71
 
47
72
  ### SSL and Persistence
48
73
 
49
- Starting with version 2.4.0 you can use two more options: `:secure` and `:persistent` like:
74
+ Starting with version 2.4.0 you can use two more options: `secure` and `persistent` like:
50
75
 
51
76
  ```ruby
52
- client = ThreeScale::Client.new(:provider_key => '...', :secure => true, :persistent => true)
77
+ client = ThreeScale::Client.new(provider_key: '...', secure: true, persistent: true)
53
78
  ```
54
79
 
55
- #### :secure
80
+ #### `secure`
56
81
 
57
82
  Enabling secure will force all traffic going through HTTPS.
58
- Because estabilishing SSL/TLS for every call is expensive, there is `:persistent`.
83
+ Because estabilishing SSL/TLS for every call is expensive, there is `persistent`.
59
84
 
60
- #### :persistent
85
+ #### `persistent`
61
86
 
62
87
  Enabling persistent will use HTTP Keep-Alive to keep open connection to our servers.
63
88
  This option requires installing gem `net-http-persistent`.
@@ -67,14 +92,13 @@ This option requires installing gem `net-http-persistent`.
67
92
  Authrep is a 'one-shot' operation to authorize an application and report the associated transaction at the same time.
68
93
  The main difference between this call and the regular authorize call is that usage will be reported if the authorization is successful. Read more about authrep at the [active docs page on the 3scale's support site](https://support.3scale.net/reference/activedocs#operation/66)
69
94
 
70
- You can make request to this backend operation like this:
95
+ You can make request to this backend operation using `service_token` and `service_id`, and an authentication pattern like `user_key`, or `app_id` with an optional key, like this:
71
96
 
72
97
  ```ruby
73
- response = client.authrep(:app_id => "the app id", :app_key => "the app key")
98
+ response = client.authrep(service_token: 'token', service_id: 'service_id', app_id: 'app_id', app_key: 'app_key')
74
99
  ```
75
100
 
76
- Then call the +success?+ method on the returned object to see if the authorization was
77
- successful.
101
+ Then call the `success?` method on the returned object to see if the authorization was successful.
78
102
 
79
103
  ```ruby
80
104
  if response.success?
@@ -84,7 +108,7 @@ else
84
108
  end
85
109
  ```
86
110
 
87
- The example is using the app_id authentication pattern, but you can also use other patterns.
111
+ The example is using the `app_id` authentication pattern, but you can also use other patterns such as `user_key`.
88
112
 
89
113
  #### A rails example
90
114
 
@@ -95,19 +119,25 @@ class ApplicationController < ActionController
95
119
  before_filter :authenticate
96
120
 
97
121
  # You only need to instantiate a new Client once and store it as a global variable
98
- # You should store your provider key in the environment because this key is secret!
122
+ # If you used a provider key it is advisable to fetch it from the environment, as
123
+ # it is secret.
99
124
  def create_client
100
- @@threescale_client ||= ThreeScale::Client.new(:provider_key => ENV['PROVIDER_KEY'])
125
+ @@threescale_client ||= ThreeScale::Client.new(service_tokens: true)
101
126
  end
102
127
 
103
128
  # To record usage, create a new metric in your application plan. You will use the
104
129
  # "system name" that you specifed on the metric/method to pass in as the key to the usage hash.
105
130
  # The key needs to be a symbol.
106
131
  # A way to pass the metric is to add a parameter that will pass the name of the metric/method along
132
+ #
133
+ # Note that you don't always want to retrieve the service token and service id from
134
+ # the parameters - this will depend on your application.
107
135
  def authenticate
108
- response = create_client.authrep(:app_id => params["app_id"],
109
- :app_key => params["app_key"],
110
- :usage => { params[:metric].to_sym => 1 })
136
+ response = create_client.authrep(service_token: params['service_token']
137
+ service_id: params['service_id'],
138
+ app_id: params['app_id'],
139
+ app_key: params['app_key'],
140
+ usage: { params['metric'].to_sym => 1 })
111
141
  if response.success?
112
142
  return true
113
143
  # All fine, the usage will be reported automatically. Proceeed.
@@ -122,15 +152,13 @@ end
122
152
 
123
153
  ### Authorize
124
154
 
125
- To authorize an application, call the +authorize+ method passing it the application's id and
126
- optionally a key:
155
+ To authorize an application, call the `authorize` method passing it the `service_token` and `service_id`, as well as a supported pattern for application authentication:
127
156
 
128
157
  ```ruby
129
- response = client.authorize(:app_id => "the app id", :app_key => "the app key")
158
+ response = client.authorize(service_token: 'token', service_id: 'service_id', user_key: 'user_key')
130
159
  ```
131
160
 
132
- Then call the +success?+ method on the returned object to see if the authorization was
133
- successful.
161
+ Then call the `success?` method on the returned object to see if the authorization was successful.
134
162
 
135
163
  ```ruby
136
164
  if response.success?
@@ -140,16 +168,14 @@ else
140
168
  end
141
169
  ```
142
170
 
143
- If both provider key and app id are valid, the response object contains additional
144
- information about the status of the application:
171
+ If the service (provided with the token and its id, or otherwise the id if the provider key was specified at instantiation time) and the application are valid, the response object contains additional information about the application's status:
145
172
 
146
173
  ```ruby
147
174
  # Returns the name of the plan the application is signed up to.
148
175
  response.plan
149
176
  ```
150
177
 
151
- If the plan has defined usage limits, the response contains details about the usage broken
152
- down by the metrics and usage limit periods.
178
+ If the plan has defined usage limits, the response contains details about the usage broken down by the metrics and usage limit periods.
153
179
 
154
180
  ```ruby
155
181
  # The usage_reports array contains one element per each usage limit defined on the plan.
@@ -173,8 +199,7 @@ usage_report.max_value # 10000
173
199
  usage_report.exceeded? # false
174
200
  ```
175
201
 
176
- If the authorization failed, the +error_code+ returns system error code and +error_message+
177
- human readable error description:
202
+ If the authorization failed, the `error_code` returns system error code and `error_message` human readable error description:
178
203
 
179
204
  ```ruby
180
205
  response.error_code # "usage_limits_exceeded"
@@ -183,13 +208,13 @@ response.error_message # "Usage limits are exceeded"
183
208
 
184
209
  ### OAuth Authorize
185
210
 
186
- To authorize an application with OAuth, call the +oauth_authorize+ method passing it the application's id.
211
+ To authorize an application with OAuth, call the `oauth_authorize` method passing it the `service_token` with `service_id` and the `app_id`.
187
212
 
188
213
  ```ruby
189
- response = client.oauth_authorize(:app_id => "the app id")
214
+ response = client.oauth_authorize(service_token: 'token', service_id: 'service_id', app_id: 'app_id')
190
215
  ```
191
216
 
192
- If the authorization is successful, the response will contain the +app_key+ and +redirect_url+ defined for this application:
217
+ If the authorization is successful, the response will contain the `app_key` and `redirect_url` defined for this application:
193
218
 
194
219
  ```ruby
195
220
  response.app_key
@@ -198,45 +223,34 @@ response.redirect_url
198
223
 
199
224
  ### Report
200
225
 
201
- To report usage, use the +report+ method. You can report multiple transactions at the same time:
226
+ To report usage, use the `report` method. You can report multiple transactions at the same time:
202
227
 
203
228
  ```ruby
204
229
  response = client.report(
205
- :transactions => [{:app_id => "first app id", :usage => {'hits' => 1}},
206
- {:app_id => "second app id", :usage => {'hits' => 1}}])
230
+ service_token: 'token',
231
+ service_id: 'service_id',
232
+ transactions: [{app_id: '1st app_id', usage: { 'hits' => 1 }},
233
+ {app_id: '2nd app_id', usage: { 'hits' => 1 }}])
207
234
  ```
208
235
 
209
- To specify a service other than the default one:
210
- ```ruby
211
- response = client.report(
212
- :transactions => [{:app_id => "first app id", :usage => {'hits' => 1}},
213
- {:app_id => "second app id", :usage => {'hits' => 1}}],
214
- :service_id => 'service_123')
215
- ```
216
-
217
- The :app_id and :usage parameters are required. Additionaly, you can specify a timestamp
218
- of a transaction:
236
+ The `app_id` and `usage` parameters are required. Additionally, you can specify a timestamp of a transaction:
219
237
 
220
238
  ```ruby
221
239
  response = client.report(
222
- :transactions => [{:app_id => "app id",
223
- :usage => {'hits' => 1},
224
- :timestamp => Time.local(2010, 4, 28, 12, 36)}])
240
+ :transactions => [{app_id: 'app_id',
241
+ usage: { 'hits' => 1 },
242
+ timestamp: Time.local(2010, 4, 28, 12, 36)}])
225
243
  ```
226
244
 
227
- The timestamp can be either a Time object (from ruby's standard library) or something that
228
- "quacks" like it (for example, the ActiveSupport::TimeWithZone from Rails) or a string. The
229
- string has to be in a format parseable by the Time.parse method. For example:
245
+ The timestamp can be either a `Time` object (from ruby's standard library) or something that _quacks_ like it (for example, the `ActiveSupport::TimeWithZone` from Rails) or a string. Such string has to be in a format parseable by the `Time.parse` method. For example:
230
246
 
231
247
  ```ruby
232
248
  "2010-04-28 12:38:33 +0200"
233
249
  ```
234
250
 
235
- If the timestamp is not in UTC, you have to specify a time offset. That's the "+0200"
236
- (two hours ahead of the Universal Coordinate Time) in the example abowe.
251
+ If the timestamp is not in UTC, you have to specify a time offset. That's the "+0200" (two hours ahead of the Universal Coordinate Time) in the example abowe.
237
252
 
238
- Then call the +success?+ method on the returned response object to see if the report was
239
- successful.
253
+ Then call the `success?` method on the returned response object to see if the report was successful.
240
254
 
241
255
  ```ruby
242
256
  if response.success?
@@ -246,19 +260,20 @@ successful.
246
260
  end
247
261
  ```
248
262
 
249
- In case of error, the +error_code+ returns system error code and +error_message+
250
- human readable error description:
263
+ In case of error, the `error_code` returns system error code and `error_message` human readable error description:
251
264
 
252
265
  ```ruby
253
266
  response.error_code # "provider_key_invalid"
254
267
  response.error_message # "provider key \"foo\" is invalid"
255
268
  ```
256
269
 
257
-
258
270
  ## Rack Middleware
259
271
 
260
272
  You can use our Rack middleware to automatically authenticate your Rack applications.
261
273
 
274
+ > NOTE: this is deprecated. Please observe that there is no support for multiple
275
+ services nor for service tokens.
276
+
262
277
  ```ruby
263
278
  require '3scale/middleware'
264
279
  use ThreeScale::Middleware, provider_key, :user_key # or :app_id
@@ -7,7 +7,7 @@ require '3scale/client/version'
7
7
 
8
8
  require '3scale/response'
9
9
  require '3scale/authorize_response'
10
- require '3scale/rack_query'
10
+ require '3scale/client/rack_query'
11
11
 
12
12
  module ThreeScale
13
13
  Error = Class.new(RuntimeError)
@@ -25,9 +25,9 @@ module ThreeScale
25
25
  #
26
26
  # == Example
27
27
  #
28
- # client = ThreeScale::Client.new(:provider_key => "your provider key")
28
+ # client = ThreeScale::Client.new(service_tokens: true)
29
29
  #
30
- # response = client.authorize(:app_id => "an app id", :app_key => "a secret key")
30
+ # response = client.authorize(service_token: 'token', service_id: '123', app_id: 'an app id', app_key: 'a secret key')
31
31
  #
32
32
  # if response.success?
33
33
  # response = client.report(:app_id => "some app id", :usage => {"hits" => 1})
@@ -39,9 +39,15 @@ module ThreeScale
39
39
  # end
40
40
  # end
41
41
  #
42
+ # Note: Provider Keys are deprecated in favor of Service Tokens with Service IDs
43
+ # The next major release of this client will default to use Service Tokens.
44
+ #
42
45
  class Client
43
46
  DEFAULT_HOST = 'su1.3scale.net'
44
47
 
48
+ DEPRECATION_MSG_PROVIDER_KEY = 'provider keys are deprecated - ' \
49
+ 'please switch at your earliest convenience to use service tokens'.freeze
50
+ private_constant :DEPRECATION_MSG_PROVIDER_KEY
45
51
  DEPRECATION_MSG_OLD_REPORT = 'warning: def report(*transactions) is '\
46
52
  'deprecated. In next versions, the signature of the report method is '\
47
53
  'going to be: '\
@@ -52,24 +58,24 @@ module ThreeScale
52
58
  private_constant :EXTENSIONS_HEADER
53
59
 
54
60
  def initialize(options)
55
- if options[:provider_key].nil? || options[:provider_key] =~ /^\s*$/
56
- raise ArgumentError, 'missing :provider_key'
57
- end
58
-
59
61
  @provider_key = options[:provider_key]
62
+ @service_tokens = options[:service_tokens]
63
+ @warn_deprecated = options.fetch(:warn_deprecated, true)
64
+
65
+ generate_creds_params
60
66
 
61
67
  @host = options[:host] ||= DEFAULT_HOST
62
68
 
63
69
  @http = ThreeScale::Client::HTTPClient.new(options)
64
70
  end
65
71
 
66
- attr_reader :provider_key, :host, :http
72
+ attr_reader :provider_key, :service_tokens, :host, :http
67
73
 
68
74
  # Authorize and report an application.
69
75
  # TODO (in the mean time read authorize comments or head over to https://support.3scale.net/reference/activedocs#operation/66 for details
70
76
  #
71
77
  def authrep(options)
72
- path = "/transactions/authrep.xml?provider_key=#{CGI.escape(provider_key)}"
78
+ path = "/transactions/authrep.xml?#{creds_params(options)}"
73
79
 
74
80
  options_usage = options.delete :usage
75
81
  options_log = options.delete :log
@@ -108,9 +114,9 @@ module ThreeScale
108
114
  #
109
115
  # == Parameters
110
116
  #
111
- # Hash with two fields:
117
+ # Hash with up to three fields:
112
118
  #
113
- # transactions:: It is required. It is an enumerable. Each element is a hash with the fields:
119
+ # transactions:: Required. Enumerable. Each element is a hash with the fields:
114
120
  # app_id: ID of the application to report the transaction for. This parameter is
115
121
  # required.
116
122
  # usage: Hash of usage values. The keys are metric names and values are
@@ -123,8 +129,9 @@ module ThreeScale
123
129
  # from the UTC. For example, "US Pacific Time" has offset -0800, "Tokyo"
124
130
  # has offset +0900. This parameter is optional, and if not provided, equals
125
131
  # to the current time.
126
- # service_id:: ID of the service. It is optional. When not specified, the transactions
127
- # are reported to the default service.
132
+ # service_id:: ID of the service. It is optional. When not specified, the transactions
133
+ # are reported to the default service.
134
+ # service_token:: Token granting access to the specified service ID.
128
135
  #
129
136
  # == Return
130
137
  #
@@ -154,7 +161,7 @@ module ThreeScale
154
161
  # The signature of this method is a bit complicated because we decided to
155
162
  # keep backwards compatibility with a previous version of the method:
156
163
  # def report(*transactions)
157
- def report(*reports, transactions: [], service_id: nil, extensions: nil, **rest)
164
+ def report(*reports, transactions: [], service_id: nil, extensions: nil, service_token: nil, **rest)
158
165
  if (!transactions || transactions.empty?) && rest.empty?
159
166
  raise ArgumentError, 'no transactions to report'
160
167
  end
@@ -162,12 +169,17 @@ module ThreeScale
162
169
  transactions = transactions.concat(reports)
163
170
 
164
171
  unless rest.empty?
165
- warn(DEPRECATION_MSG_OLD_REPORT)
172
+ warn DEPRECATION_MSG_OLD_REPORT if @warn_deprecated
166
173
  transactions.concat([rest])
167
174
  end
168
175
 
169
176
  payload = encode_transactions(transactions)
170
- payload['provider_key'] = CGI.escape(provider_key)
177
+ if @service_tokens
178
+ raise ArgumentError, "service_token or service_id not specified" unless service_token && service_id
179
+ payload['service_token'] = CGI.escape(service_token)
180
+ else
181
+ payload['provider_key'] = CGI.escape(@provider_key)
182
+ end
171
183
  payload['service_id'] = CGI.escape(service_id.to_s) if service_id
172
184
 
173
185
  headers = extensions_to_header extensions if extensions
@@ -189,14 +201,15 @@ module ThreeScale
189
201
  #
190
202
  # Hash with options:
191
203
  #
192
- # app_id:: id of the application to authorize. This is required.
193
- # app_key:: secret key assigned to the application. Required only if application has
194
- # a key defined.
195
- # service_id:: id of the service (required if you have more than one service)
196
- # usage:: predicted usage. It is optional. It is a hash where the keys are metrics
197
- # and the values their predicted usage.
198
- # Example: {'hits' => 1, 'my_metric' => 100}
199
- # extensions:: Optional. Hash of extension keys and values.
204
+ # service_token:: token granting access to the specified service_id.
205
+ # app_id:: id of the application to authorize. This is required.
206
+ # app_key:: secret key assigned to the application. Required only if application has
207
+ # a key defined.
208
+ # service_id:: id of the service (required if you have more than one service)
209
+ # usage:: predicted usage. It is optional. It is a hash where the keys are metrics
210
+ # and the values their predicted usage.
211
+ # Example: {'hits' => 1, 'my_metric' => 100}
212
+ # extensions:: Optional. Hash of extension keys and values.
200
213
  #
201
214
  # == Return
202
215
  #
@@ -219,7 +232,8 @@ module ThreeScale
219
232
  #
220
233
  def authorize(options)
221
234
  extensions = options.delete :extensions
222
- path = "/transactions/authorize.xml" + options_to_params(options, ALL_PARAMS)
235
+ creds = creds_params(options)
236
+ path = "/transactions/authorize.xml" + options_to_params(options, ALL_PARAMS) + '&' + creds
223
237
 
224
238
  headers = extensions_to_header extensions if extensions
225
239
  http_response = @http.get(path, headers: headers)
@@ -240,11 +254,12 @@ module ThreeScale
240
254
  #
241
255
  # Hash with options:
242
256
  #
243
- # app_id:: id of the application to authorize. This is required.
244
- # service_id:: id of the service (required if you have more than one service)
245
- # usage:: predicted usage. It is optional. It is a hash where the keys are metrics
246
- # and the values their predicted usage.
247
- # Example: {'hits' => 1, 'my_metric' => 100}
257
+ # service_token:: token granting access to the specified service_id.
258
+ # app_id:: id of the application to authorize. This is required.
259
+ # service_id:: id of the service (required if you have more than one service)
260
+ # usage:: predicted usage. It is optional. It is a hash where the keys are metrics
261
+ # and the values their predicted usage.
262
+ # Example: {'hits' => 1, 'my_metric' => 100}
248
263
  #
249
264
  # == Return
250
265
  #
@@ -270,7 +285,8 @@ module ThreeScale
270
285
  #
271
286
  def oauth_authorize(options)
272
287
  extensions = options.delete :extensions
273
- path = "/transactions/oauth_authorize.xml" + options_to_params(options, OAUTH_PARAMS)
288
+ creds = creds_params(options)
289
+ path = "/transactions/oauth_authorize.xml" + options_to_params(options, OAUTH_PARAMS) + '&' + creds
274
290
 
275
291
  headers = extensions_to_header extensions if extensions
276
292
  http_response = @http.get(path, headers: headers)
@@ -292,7 +308,7 @@ module ThreeScale
292
308
  REPORT_PARAMS = [:user_key, :app_id, :service_id, :timestamp]
293
309
 
294
310
  def options_to_params(options, allowed_keys)
295
- params = { :provider_key => provider_key }
311
+ params = {}
296
312
 
297
313
  (allowed_keys - [:usage]).each do |key|
298
314
  params[key] = options[key] if options.has_key?(key)
@@ -397,5 +413,25 @@ module ThreeScale
397
413
  def extensions_to_header(extensions)
398
414
  { EXTENSIONS_HEADER => RackQuery.encode(extensions) }
399
415
  end
416
+
417
+ # helper to generate the creds_params method
418
+ def generate_creds_params
419
+ define_singleton_method :creds_params,
420
+ if @service_tokens
421
+ lambda do |options|
422
+ token = options.delete(:service_token)
423
+ service_id = options[:service_id]
424
+ raise ArgumentError, "need to specify a service_token and a service_id" unless token && service_id
425
+ 'service_token='.freeze + CGI.escape(token)
426
+ end
427
+ elsif @provider_key
428
+ warn DEPRECATION_MSG_PROVIDER_KEY if @warn_deprecated
429
+ lambda do |_|
430
+ "provider_key=#{CGI.escape @provider_key}".freeze
431
+ end
432
+ else
433
+ raise ArgumentError, 'missing credentials - either use "service_tokens: true" or specify a provider_key value'
434
+ end
435
+ end
400
436
  end
401
437
  end
@@ -0,0 +1,41 @@
1
+ # A simple module to encode hashes of param keys and values as expected by
2
+ # Rack in its nested queries parsing.
3
+ #
4
+ module ThreeScale
5
+ class Client
6
+ module RackQuery
7
+ class << self
8
+ def encode(hash)
9
+ hash.flat_map do |hk, hv|
10
+ encode_value(CGI.escape(hk.to_s), hv)
11
+ end.join('&'.freeze)
12
+ end
13
+
14
+ private
15
+
16
+ def encode_value(rack_param, val)
17
+ if val.is_a? Array
18
+ encode_array(rack_param, val)
19
+ elsif val.is_a? Hash
20
+ encode_hash(rack_param, val)
21
+ else
22
+ "#{rack_param}=#{CGI.escape(val.to_s)}"
23
+ end
24
+ end
25
+
26
+ def encode_array(rack_param, val)
27
+ rack_param = rack_param + '[]'
28
+ val.flat_map do |v|
29
+ encode_value(rack_param, v)
30
+ end
31
+ end
32
+
33
+ def encode_hash(rack_param, val)
34
+ val.flat_map do |k, v|
35
+ encode_value(rack_param + "[#{CGI.escape(k.to_s)}]", v)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,5 +1,5 @@
1
1
  module ThreeScale
2
2
  class Client
3
- VERSION = '2.10.0'
3
+ VERSION = '2.11.0'
4
4
  end
5
5
  end
@@ -2,12 +2,21 @@ require 'benchmark'
2
2
 
3
3
  require '3scale/client'
4
4
 
5
- provider_key = ENV['TEST_3SCALE_PROVIDER_KEY']
5
+ provider_key = ENV['TEST_3SCALE_PROVIDER_KEY'] or raise 'No provider key set'
6
+ warn_deprecated = ENV['WARN_DEPRECATED'] == '1'
6
7
 
7
- client = ThreeScale::Client.new(:provider_key => provider_key)
8
- persistent_client = ThreeScale::Client.new(:provider_key => provider_key, :persistent => true)
9
- persistent_ssl_client = ThreeScale::Client.new(:provider_key => provider_key, :secure => true, :persistent => true)
10
- ssl_client = ThreeScale::Client.new(:provider_key => provider_key, :secure => true)
8
+ client = ThreeScale::Client.new(provider_key: provider_key,
9
+ warn_deprecated: warn_deprecated)
10
+ persistent_client = ThreeScale::Client.new(provider_key: provider_key,
11
+ warn_deprecated: warn_deprecated,
12
+ persistent: true)
13
+ persistent_ssl_client = ThreeScale::Client.new(provider_key: provider_key,
14
+ warn_deprecated: warn_deprecated,
15
+ secure: true,
16
+ persistent: true)
17
+ ssl_client = ThreeScale::Client.new(provider_key: provider_key,
18
+ warn_deprecated: warn_deprecated,
19
+ secure: true)
11
20
 
12
21
  auth = { :app_id => ENV['TEST_3SCALE_APP_IDS'], :app_key => ENV['TEST_3SCALE_APP_KEYS'] }
13
22
 
@@ -7,8 +7,11 @@ require '3scale/client'
7
7
 
8
8
  class ThreeScale::ClientTest < MiniTest::Test
9
9
 
10
+ WARN_DEPRECATED = ENV['WARN_DEPRECATED'] == '1'
11
+
10
12
  def client(options = {})
11
- ThreeScale::Client.new({:provider_key => '1234abcd'}.merge(options))
13
+ ThreeScale::Client.new({ provider_key: '1234abcd',
14
+ warn_deprecated: WARN_DEPRECATED }.merge(options))
12
15
  end
13
16
 
14
17
  def setup
@@ -19,36 +22,53 @@ class ThreeScale::ClientTest < MiniTest::Test
19
22
  @host = ThreeScale::Client::DEFAULT_HOST
20
23
  end
21
24
 
22
- def test_raises_exception_if_provider_key_is_missing
25
+ def test_raises_exception_if_no_credentials_are_specified
23
26
  assert_raises ArgumentError do
24
27
  ThreeScale::Client.new({})
25
28
  end
29
+ assert_raises ArgumentError do
30
+ ThreeScale::Client.new(service_tokens: false)
31
+ end
32
+ end
33
+
34
+ def test_does_not_raise_if_some_credentials_are_specified
35
+ assert ThreeScale::Client.new(provider_key: 'some_key',
36
+ warn_deprecated: WARN_DEPRECATED)
37
+ assert ThreeScale::Client.new(service_tokens: true)
26
38
  end
27
39
 
28
40
  def test_default_host
29
- client = ThreeScale::Client.new(:provider_key => '1234abcd')
41
+ client = ThreeScale::Client.new(provider_key: '1234abcd',
42
+ warn_deprecated: WARN_DEPRECATED)
30
43
 
31
44
  assert_equal 'su1.3scale.net', client.host
32
45
  end
33
46
 
34
47
  def test_custom_host
35
- client = ThreeScale::Client.new(:provider_key => '1234abcd', :host => "example.com")
48
+ client = ThreeScale::Client.new(provider_key: '1234abcd',
49
+ warn_deprecated: WARN_DEPRECATED,
50
+ host: "example.com")
36
51
 
37
52
  assert_equal 'example.com', client.host
38
53
  end
39
54
 
40
55
  def test_default_protocol
41
- client = ThreeScale::Client.new(:provider_key => 'test')
56
+ client = ThreeScale::Client.new(provider_key: 'test',
57
+ warn_deprecated: WARN_DEPRECATED)
42
58
  assert_equal false, client.http.use_ssl?
43
59
  end
44
60
 
45
61
  def test_insecure_protocol
46
- client = ThreeScale::Client.new(:provider_key => 'test', :secure => false)
62
+ client = ThreeScale::Client.new(provider_key: 'test',
63
+ warn_deprecated: WARN_DEPRECATED,
64
+ secure: false)
47
65
  assert_equal false, client.http.use_ssl?
48
66
  end
49
67
 
50
68
  def test_secure_protocol
51
- client = ThreeScale::Client.new(:provider_key => 'test', :secure => true)
69
+ client = ThreeScale::Client.new(provider_key: 'test',
70
+ warn_deprecated: WARN_DEPRECATED,
71
+ secure: true)
52
72
  assert_equal true, client.http.use_ssl?
53
73
  end
54
74
 
@@ -631,7 +651,8 @@ class ThreeScale::ClientTest < MiniTest::Test
631
651
  :status => ['403', 'Forbidden'],
632
652
  :body => error_body)
633
653
 
634
- client = ThreeScale::Client.new(:provider_key => 'foo')
654
+ client = ThreeScale::Client.new(provider_key: 'foo',
655
+ warn_deprecated: WARN_DEPRECATED)
635
656
  transactions = [{ :app_id => 'abc', :usage => { 'hits' => 1 } }]
636
657
  response = client.report(transactions: transactions)
637
658
 
@@ -659,7 +680,8 @@ class ThreeScale::ClientTest < MiniTest::Test
659
680
  :status => ['200', 'OK'],
660
681
  :body => success_body)
661
682
 
662
- client = ThreeScale::Client.new(:provider_key => 'foo')
683
+ client = ThreeScale::Client.new(provider_key: 'foo',
684
+ warn_deprecated: WARN_DEPRECATED)
663
685
  response = client.authorize(:app_id => 'foo')
664
686
 
665
687
  assert response.success?
@@ -676,7 +698,8 @@ class ThreeScale::ClientTest < MiniTest::Test
676
698
  FakeWeb.register_uri(:post, "http://#{@host}/transactions.xml",
677
699
  :status => ['200', 'OK'],
678
700
  :body => success_body)
679
- client = ThreeScale::Client.new(:provider_key => 'foo')
701
+ client = ThreeScale::Client.new(provider_key: 'foo',
702
+ warn_deprecated: WARN_DEPRECATED)
680
703
  transactions = [{ :app_id => 'abc', :usage => { 'hits' => 1 } }]
681
704
  client.report(transactions: transactions)
682
705
 
@@ -692,7 +715,8 @@ class ThreeScale::ClientTest < MiniTest::Test
692
715
  :status => ['200', 'OK'],
693
716
  :body => success_body)
694
717
 
695
- client = ThreeScale::Client.new(:provider_key => 'foo')
718
+ client = ThreeScale::Client.new(provider_key: 'foo',
719
+ warn_deprecated: WARN_DEPRECATED)
696
720
  response = client.authrep(:app_id => 'abc')
697
721
 
698
722
  assert response.success?
@@ -761,6 +785,56 @@ class ThreeScale::ClientTest < MiniTest::Test
761
785
  assert_equal EXTENSIONS_STR, request['3scale-options']
762
786
  end
763
787
 
788
+ def test_client_initialized_with_sevice_tokens_uses_percall_specified_token
789
+ body = '<status>
790
+ <authorized>true</authorized>
791
+ <plan>Ultimate</plan>
792
+ </status>'
793
+ transactions = [{ app_id: 'foo',
794
+ timestamp: Time.local(2010, 4, 27, 15, 00),
795
+ usage: {'hits' => 1 } }]
796
+ usage = { 'metric1' => 1, 'metric2' => 2}
797
+
798
+ FakeWeb.register_uri(:get, "http://#{@host}/transactions/authorize.xml?user_key=foo&service_id=1&service_token=newtoken", status: ['200', 'OK'], body: body)
799
+ FakeWeb.register_uri(:get, "http://#{@host}/transactions/authrep.xml?service_token=newtoken&user_key=foo&service_id=1&%5Busage%5D%5Bhits%5D=1", status: ['200', 'OK'], body: body)
800
+ FakeWeb.register_uri(:post, "http://#{@host}/transactions.xml", parameters: {service_token: 'newtoken', service_id: '1', transactions: transactions}, status: ['200', 'OK'])
801
+ FakeWeb.register_uri(:get, "http://#{@host}/transactions/oauth_authorize.xml?service_id=1&%5Busage%5D%5Bmetric1%5D=1&%5Busage%5D%5Bmetric2%5D=2&service_token=newtoken",
802
+ status: ['200', 'OK'], body: body)
803
+
804
+ client = ThreeScale::Client.new(service_tokens: true)
805
+
806
+ response = client.authorize(user_key: 'foo', service_token: 'newtoken', service_id: 1)
807
+ assert response.success?
808
+ response = client.authrep(user_key: 'foo', service_token: 'newtoken', service_id: 1)
809
+ assert response.success?
810
+ response = client.report(transactions: transactions, service_token: 'newtoken', service_id: 1)
811
+ assert response.success?
812
+ response = client.oauth_authorize(access_token: 'oauth', usage: usage, service_token: 'newtoken', service_id: 1)
813
+ assert response.success?
814
+ end
815
+
816
+ def test_client_initialized_with_service_tokens_raises_if_unspecified_percall
817
+ transactions = [{ app_id: 'foo',
818
+ timestamp: Time.local(2010, 4, 27, 15, 00),
819
+ usage: {'hits' => 1 } }]
820
+ usage = { 'metric1' => 1, 'metric2' => 2}
821
+
822
+ client = ThreeScale::Client.new(service_tokens: true)
823
+
824
+ assert_raises ArgumentError do
825
+ client.authorize(user_key: 'foo', service_id: 1)
826
+ end
827
+ assert_raises ArgumentError do
828
+ client.authrep(user_key: 'foo', service_id: 1)
829
+ end
830
+ assert_raises ArgumentError do
831
+ client.report(transactions: transactions, service_id: 1)
832
+ end
833
+ assert_raises ArgumentError do
834
+ client.oauth_authorize(user_key: 'foo', usage: usage, service_id: 1)
835
+ end
836
+ end
837
+
764
838
  private
765
839
 
766
840
  #OPTIMIZE this tricky test helper relies on fakeweb catching the urls requested by the client
@@ -789,7 +863,10 @@ end
789
863
  class ThreeScale::NetHttpPersistentClientTest < ThreeScale::ClientTest
790
864
  def client(options = {})
791
865
  ThreeScale::Client::HTTPClient.persistent_backend = ThreeScale::Client::HTTPClient::NetHttpPersistent
792
- ThreeScale::Client.new({:provider_key => '1234abcd', :persistent => true}.merge(options))
866
+ ThreeScale::Client.new({ provider_key: '1234abcd',
867
+ warn_deprecated: WARN_DEPRECATED,
868
+ persistent: true,
869
+ }.merge(options))
793
870
  end
794
871
  end
795
872
 
@@ -39,7 +39,7 @@ class ThreeScale::MiddlewareTest < MiniTest::Test
39
39
  def test_nil_authenticator
40
40
  authenticator = ThreeScale::Middleware::NilAuthenticator.new(mock)
41
41
  assert authenticator.provided?
42
- assert_equal(nil, authenticator.credentials)
42
+ assert_nil authenticator.credentials
43
43
  assert authenticator.to_proc.call(nil, nil)
44
44
  end
45
45
  end
@@ -5,6 +5,8 @@ require 'mocha/setup'
5
5
 
6
6
  if ENV['TEST_3SCALE_PROVIDER_KEY'] && ENV['TEST_3SCALE_APP_IDS'] && ENV['TEST_3SCALE_APP_KEYS']
7
7
  class ThreeScale::NetHttpPersistenceTest < MiniTest::Test
8
+ WARN_DEPRECATED = ENV['WARN_DEPRECATED'] == '1'
9
+
8
10
  def setup
9
11
  ThreeScale::Client::HTTPClient.persistent_backend = ThreeScale::Client::HTTPClient::NetHttpPersistent
10
12
 
@@ -13,7 +15,9 @@ if ENV['TEST_3SCALE_PROVIDER_KEY'] && ENV['TEST_3SCALE_APP_IDS'] && ENV['TEST_3S
13
15
  @app_id = ENV['TEST_3SCALE_APP_IDS']
14
16
  @app_key = ENV['TEST_3SCALE_APP_KEYS']
15
17
 
16
- @client = ThreeScale::Client.new(provider_key: provider_key, persistence: true)
18
+ @client = ThreeScale::Client.new(provider_key: provider_key,
19
+ warn_deprecated: WARN_DEPRECATED,
20
+ persistence: true)
17
21
 
18
22
  if defined?(FakeWeb)
19
23
  FakeWeb.allow_net_connect = true
@@ -5,6 +5,8 @@ if ENV['TEST_3SCALE_PROVIDER_KEY'] &&
5
5
  ENV['TEST_3SCALE_APP_IDS'] &&
6
6
  ENV['TEST_3SCALE_APP_KEYS']
7
7
  class ThreeScale::RemoteTest < MiniTest::Test
8
+ WARN_DEPRECATED = ENV['WARN_DEPRECATED'] == '1'
9
+
8
10
  def setup
9
11
  @provider_key = ENV['TEST_3SCALE_PROVIDER_KEY']
10
12
 
@@ -13,7 +15,8 @@ if ENV['TEST_3SCALE_PROVIDER_KEY'] &&
13
15
  @app_ids = ENV['TEST_3SCALE_APP_IDS'].split(',').map(&stripper)
14
16
  @app_keys = ENV['TEST_3SCALE_APP_KEYS'].split(',').map(&stripper)
15
17
 
16
- @client = ThreeScale::Client.new(:provider_key => @provider_key)
18
+ @client = ThreeScale::Client.new(provider_key: @provider_key,
19
+ warn_deprecated: WARN_DEPRECATED)
17
20
 
18
21
  if defined?(FakeWeb)
19
22
  FakeWeb.clean_registry
@@ -39,7 +42,9 @@ if ENV['TEST_3SCALE_PROVIDER_KEY'] &&
39
42
  end
40
43
 
41
44
  def test_successful_secure_authrep
42
- @client = ThreeScale::Client.new(:provider_key => @provider_key, :secure => true)
45
+ @client = ThreeScale::Client.new(provider_key: @provider_key,
46
+ warn_deprecated: WARN_DEPRECATED,
47
+ secure: true)
43
48
  test_successful_authrep
44
49
  end
45
50
 
@@ -96,7 +101,8 @@ if ENV['TEST_3SCALE_PROVIDER_KEY'] &&
96
101
  {:app_id => app_id, :usage => {'hits' => 1}}
97
102
  end
98
103
 
99
- client = ThreeScale::Client.new(:provider_key => 'invalid-key')
104
+ client = ThreeScale::Client.new(provider_key: 'invalid-key',
105
+ warn_deprecated: WARN_DEPRECATED)
100
106
  response = client.report(transactions: transactions)
101
107
  assert !response.success?
102
108
  assert_equal 'provider_key_invalid', response.error_code
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: 3scale_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.10.0
4
+ version: 2.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Cichra
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2016-11-25 00:00:00.000000000 Z
15
+ date: 2017-01-20 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: bundler
@@ -176,9 +176,9 @@ files:
176
176
  - lib/3scale/authorize_response.rb
177
177
  - lib/3scale/client.rb
178
178
  - lib/3scale/client/http_client.rb
179
+ - lib/3scale/client/rack_query.rb
179
180
  - lib/3scale/client/version.rb
180
181
  - lib/3scale/middleware.rb
181
- - lib/3scale/rack_query.rb
182
182
  - lib/3scale/response.rb
183
183
  - lib/3scale_client.rb
184
184
  - test/benchmark.rb
@@ -206,7 +206,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
206
206
  version: '0'
207
207
  requirements: []
208
208
  rubyforge_project:
209
- rubygems_version: 2.6.4
209
+ rubygems_version: 2.6.8
210
210
  signing_key:
211
211
  specification_version: 4
212
212
  summary: Client for 3scale Web Service Management System API
@@ -1,37 +0,0 @@
1
- # A simple module to encode hashes of param keys and values as expected by
2
- # Rack in its nested queries parsing.
3
- #
4
- module RackQuery
5
- class << self
6
- def encode(hash)
7
- hash.flat_map do |hk, hv|
8
- encode_value(CGI.escape(hk.to_s), hv)
9
- end.join('&'.freeze)
10
- end
11
-
12
- private
13
-
14
- def encode_value(rack_param, val)
15
- if val.is_a? Array
16
- encode_array(rack_param, val)
17
- elsif val.is_a? Hash
18
- encode_hash(rack_param, val)
19
- else
20
- "#{rack_param}=#{CGI.escape(val.to_s)}"
21
- end
22
- end
23
-
24
- def encode_array(rack_param, val)
25
- rack_param = rack_param + '[]'
26
- val.flat_map do |v|
27
- encode_value(rack_param, v)
28
- end
29
- end
30
-
31
- def encode_hash(rack_param, val)
32
- val.flat_map do |k, v|
33
- encode_value(rack_param + "[#{CGI.escape(k.to_s)}]", v)
34
- end
35
- end
36
- end
37
- end