3scale_client 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ pkg
@@ -0,0 +1,55 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{3scale_client}
8
+ s.version = "2.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Adam Cig\303\241nek"]
12
+ s.date = %q{2010-04-28}
13
+ s.description = %q{This gem allows to easily connect an application that provides a Web Service with the 3scale API Management System to authorize it's users and report the usage.
14
+ }
15
+ s.email = %q{adam@3scale.net}
16
+ s.extra_rdoc_files = [
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "3scale_client.gemspec",
22
+ "LICENCE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/three_scale/authorize_response.rb",
27
+ "lib/three_scale/client.rb",
28
+ "lib/three_scale/response.rb",
29
+ "test/client_test.rb",
30
+ "test/remote_test.rb"
31
+ ]
32
+ s.homepage = %q{http://www.3scale.net}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.6}
36
+ s.summary = %q{Client for 3scale Web Service Management System API}
37
+ s.test_files = [
38
+ "test/remote_test.rb",
39
+ "test/client_test.rb"
40
+ ]
41
+
42
+ if s.respond_to? :specification_version then
43
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
47
+ s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
48
+ else
49
+ s.add_dependency(%q<nokogiri>, [">= 0"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<nokogiri>, [">= 0"])
53
+ end
54
+ end
55
+
data/LICENCE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2010 3scale networks S.L.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
data/README.rdoc ADDED
@@ -0,0 +1,131 @@
1
+ = Client for 3scale web service management system API
2
+
3
+ This library provides client for the 3scale web service management API.
4
+
5
+ == Installation
6
+
7
+ This library is distributed as a gem. First, add gemcutter to your gem source list,
8
+ unless it's already there:
9
+
10
+ gem source --add http://gemcutter.org
11
+
12
+ Then install the gem:
13
+
14
+ gem install 3scale_client
15
+
16
+ Or alternatively, download the source code from github:
17
+ http://github.com/3scale/3scale_ws_api_for_ruby
18
+
19
+ If you are using Rails, put this into your config/environment.rb
20
+
21
+ config.gem "3scale_client"
22
+
23
+ Otherwise, require the gem in whatever way is natural to your framework of choice.
24
+
25
+ == Usage
26
+
27
+ First, create an instance of the client, giving it your provider API key:
28
+
29
+ client = ThreeScale::Client.new(:provider_key => "your provider key")
30
+
31
+ Because the object is stateless, you can create just one and store it globally.
32
+
33
+ === Authorize
34
+
35
+ To authorize a particular user, call the +authorize+ method passing it the user key
36
+ identifiing the user:
37
+
38
+ response = client.authorize(:user_key => "the user key")
39
+
40
+ Then call the +success?+ method on the returned object to see if the authorization was
41
+ successful.
42
+
43
+ if response.success?
44
+ # All fine, proceeed.
45
+ else
46
+ # Something's wrong with this user.
47
+ end
48
+
49
+ If the authorization succeeded, the response object contains additional information about
50
+ the status of the user:
51
+
52
+ # Returns the name of the plan the user is signed up to.
53
+ response.plan
54
+
55
+ If the plan has defined usage limits, the response contains details about how close the user
56
+ is to meet these limits:
57
+
58
+ # The usages array contains one element per each usage limit defined on the plan.
59
+ usage = response.usages[0]
60
+
61
+ # The metric
62
+ usage.metric # "hits"
63
+
64
+ # The period the limit applies to
65
+ usage.period # :day
66
+ usage.period_start # "Wed Apr 28 00:00:00 +0200 2010"
67
+ usage.period_end # "Wed Apr 28 23:59:59 +0200 2010"
68
+
69
+ # The current value the user already consumed in the period
70
+ usage.current_value # 8032
71
+
72
+ # The maximal value allowed by the limit in the period
73
+ usage.max_value # 10000
74
+
75
+ If the authorization failed, the +errors+ method contains one or more errors with more
76
+ detailed information:
77
+
78
+ error = response.errors[0]
79
+
80
+ # Error code
81
+ error.code # "user.exceeded_limits"
82
+
83
+ # Human readable error message
84
+ error.message # "Usage limits are exceeded"
85
+
86
+ === Report
87
+
88
+ To report usage, use the +report+ method. You can report multiple transaction at the same time:
89
+
90
+ response = client.report({:user_key => "first user's key", :usage => {'hits' => 1}},
91
+ {:user_key => "second user's key", :usage => {'hits' => 1}})
92
+
93
+ The :user_key and :usage parameters are required. Additionaly, you can specify a timestamp
94
+ of transaction:
95
+
96
+ response = client.report({:user_key => "user's key", :usage => {'hits' => 1},
97
+ :timestamp => Time.local(2010, 4, 28, 12, 36)})
98
+
99
+ The timestamp can be either a Time object (from ruby's standard library) or something that
100
+ "quacks" like it (for example, the ActiveSupport::TimeWithZone from Rails) or a string. The
101
+ string has to be in a format parseable by the Time.parse method. For example:
102
+
103
+ "2010-04-28 12:38:33 +0200"
104
+
105
+ If the timestamp is not in UTC, you have to specify a time offset. That's the "+0200"
106
+ (two hours ahead of the Universal Coordinate Time) in the example abowe.
107
+
108
+ Then call the +success?+ method on the returned response object to see if the report was
109
+ successful.
110
+
111
+ if response.success?
112
+ # All OK.
113
+ else
114
+ # There were some errors
115
+ end
116
+
117
+ If the report was successful, you are done. Otherwise, the +errors+ method will return array
118
+ of errors with more detailed information. Each of these errors has the +index+ attribute that
119
+ returns numeric index of the transaction this error corresponds to. For example, if you report
120
+ three transactions, first two are ok and the last one has invalid user key, there will be
121
+ an error in the +errors+ array with the +index+ equal to 2 (the indices start at 0):
122
+
123
+ error = response.errors[0]
124
+ error.index # 2
125
+ error.code # "user.invalid_key"
126
+ error.message # "User key is invalid"
127
+
128
+ == Legal
129
+
130
+ Copyright (c) 2010 3scale networks S.L., released under the MIT license.
131
+
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Run unit tests.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.pattern = 'test/**/*_test.rb'
11
+ t.verbose = true
12
+ t.ruby_opts << '-rubygems'
13
+ end
14
+
15
+ desc 'Generate documentation.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = '3scale API Management Client'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
23
+
24
+ begin
25
+ require 'jeweler'
26
+
27
+ Jeweler::Tasks.new do |gemspec|
28
+ gemspec.name = '3scale_client'
29
+ gemspec.summary = 'Client for 3scale Web Service Management System API'
30
+ gemspec.description = <<END
31
+ This gem allows to easily connect an application that provides a Web Service with the 3scale API Management System to authorize it's users and report the usage.
32
+ END
33
+ gemspec.email = 'adam@3scale.net'
34
+ gemspec.homepage = 'http://www.3scale.net'
35
+ gemspec.authors = ['Adam Cigánek']
36
+
37
+ gemspec.add_dependency 'nokogiri'
38
+ end
39
+
40
+ Jeweler::GemcutterTasks.new
41
+ rescue LoadError
42
+ puts "Jeweler not available. Install it with: gem install jeweler"
43
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.0.1
@@ -0,0 +1,39 @@
1
+ require 'time'
2
+
3
+ module ThreeScale
4
+ class AuthorizeResponse < Response
5
+ def initialize(options = {})
6
+ super({:success => true}.merge(options))
7
+ @usages = options[:usages] || []
8
+ end
9
+
10
+ attr_accessor :plan
11
+
12
+ class Usage
13
+ attr_reader :metric
14
+ attr_reader :period
15
+ attr_reader :current_value
16
+ attr_reader :max_value
17
+
18
+ def initialize(options = {})
19
+ options.each do |name, value|
20
+ instance_variable_set("@#{name}", value)
21
+ end
22
+ end
23
+
24
+ def period_start
25
+ @parsed_period_start ||= @period_start && Time.parse(@period_start)
26
+ end
27
+
28
+ def period_end
29
+ @parsed_period_end ||= @period_end && Time.parse(@period_end)
30
+ end
31
+ end
32
+
33
+ attr_reader :usages
34
+
35
+ def add_usage(options)
36
+ @usages << Usage.new(options)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,213 @@
1
+ require 'cgi'
2
+ require 'uri'
3
+ require 'net/http'
4
+ require 'nokogiri'
5
+
6
+ require 'three_scale/response'
7
+ require 'three_scale/authorize_response'
8
+
9
+ module ThreeScale
10
+ Error = Class.new(RuntimeError)
11
+
12
+ class ServerError < Error
13
+ def initialize(response)
14
+ super('server error')
15
+ @response = response
16
+ end
17
+
18
+ attr_reader :response
19
+ end
20
+
21
+ # Wrapper for 3scale Web Service Management API.
22
+ #
23
+ # == Example
24
+ #
25
+ # client = ThreeScale::Client.new(:provider_key => "your provider key")
26
+ #
27
+ # response = client.authorize(:user_key => "yout user's key")
28
+ #
29
+ # if response.success?
30
+ # response = client.report(:user_key => "your user's key", :usage => {"hits" => 1})
31
+ #
32
+ # if response.success?
33
+ # # all fine.
34
+ # else
35
+ # # something's wrong.
36
+ # end
37
+ # end
38
+ #
39
+ class Client
40
+ DEFAULT_HOST = 'server.3scale.net'
41
+
42
+ def initialize(options)
43
+ if options[:provider_key].nil? || options[:provider_key] =~ /^\s*$/
44
+ raise ArgumentError, 'missing :provider_key'
45
+ end
46
+
47
+ @provider_key = options[:provider_key]
48
+ @host = options[:host] || DEFAULT_HOST
49
+ end
50
+
51
+ attr_reader :provider_key
52
+ attr_reader :host
53
+
54
+ # Report transaction(s).
55
+ #
56
+ # == Parameters
57
+ #
58
+ # The parameters the transactions to report. Each transaction is a hash with
59
+ # these elements:
60
+ #
61
+ # user_key:: API key of the user to report the transaction for. This parameter is
62
+ # required.
63
+ # usage:: Hash of usage values. The keys are metric names and values are
64
+ # correspoding numeric values. Example: {'hits' => 1, 'transfer' => 1024}.
65
+ # This parameter is required.
66
+ # timestamp:: Timestamp of the transaction. This can be either a object of the
67
+ # ruby's Time class, or a string in the "YYYY-MM-DD HH:MM:SS" format
68
+ # (if the time is in the UTC), or a string in
69
+ # the "YYYY-MM-DD HH:MM:SS ZZZZZ" format, where the ZZZZZ is the time offset
70
+ # from the UTC. For example, "US Pacific Time" has offset -0800, "Tokyo"
71
+ # has offset +0900. This parameter is optional, and if not provided, equals
72
+ # to the current time.
73
+ #
74
+ # == Return
75
+ #
76
+ # A Response object with method +success?+ that returns true if the report was successful,
77
+ # or false if there was an error. See ThreeScale::Response class for more information.
78
+ #
79
+ # In case of unexpected internal server error, this method raises a ThreeScale::ServerError
80
+ # exception.
81
+ #
82
+ # == Examples
83
+ #
84
+ # # Report two transactions of two users.
85
+ # client.report({:user_key => 'foo', :usage => {'hits' => 1}},
86
+ # {:user_key => 'bar', :usage => {'hits' => 1}})
87
+ #
88
+ # # Report one transaction with timestamp.
89
+ # client.report({:user_key => 'foo',
90
+ # :timestamp => Time.local(2010, 4, 27, 15, 14),
91
+ # :usage => {'hits' => 1})
92
+ #
93
+ def report(*transactions)
94
+ raise ArgumentError, 'no transactions to report' if transactions.empty?
95
+
96
+ payload = encode_transactions(transactions)
97
+ payload['provider_key'] = CGI.escape(provider_key)
98
+
99
+ uri = URI.parse("http://#{host}/transactions.xml")
100
+
101
+ process_response(Net::HTTP.post_form(uri, payload)) do |http_response|
102
+ Response.new(:success => true)
103
+ end
104
+ end
105
+
106
+ # Authorize a user.
107
+ #
108
+ # == Parameters
109
+ #
110
+ # Hash with options:
111
+ #
112
+ # user_key:: API key of the user to authorize. This is required.
113
+ #
114
+ # == Return
115
+ #
116
+ # An ThreeScale::AuthorizeResponse object. It's +success?+ method returns true if
117
+ # the authorization is successful. In that case, it contains additional information
118
+ # about the status of the use. See the ThreeScale::AuthorizeResponse for more information.
119
+ # In case of error, the +success?+ method returns false and the +errors+ contains list
120
+ # of errors with more details.
121
+ #
122
+ # In case of unexpected internal server error, this method raises a ThreeScale::ServerError
123
+ # exception.
124
+ #
125
+ # == Examples
126
+ #
127
+ # response = client.authorize(:user_key => 'foo')
128
+ #
129
+ # if response.success?
130
+ # # All good. Proceed...
131
+ # end
132
+ #
133
+ def authorize(options)
134
+ path = "/transactions/authorize.xml" +
135
+ "?provider_key=#{CGI.escape(provider_key)}" +
136
+ "&user_key=#{CGI.escape(options[:user_key].to_s)}"
137
+
138
+ uri = URI.parse("http://#{host}#{path}")
139
+
140
+ process_response(Net::HTTP.get_response(uri)) do |http_response|
141
+ build_authorize_response(http_response.body)
142
+ end
143
+ end
144
+
145
+ private
146
+
147
+ def process_response(http_response)
148
+ case http_response
149
+ when Net::HTTPSuccess
150
+ yield(http_response)
151
+ when Net::HTTPClientError
152
+ build_error_response(http_response.body)
153
+ else
154
+ raise ServerError.new(http_response)
155
+ end
156
+ end
157
+
158
+ def encode_transactions(transactions)
159
+ result = {}
160
+
161
+ transactions.each_with_index do |transaction, index|
162
+ append_encoded_value(result, index, [:user_key], transaction[:user_key])
163
+ append_encoded_value(result, index, [:timestamp], transaction[:timestamp])
164
+ append_encoded_value(result, index, [:client_ip], transaction[:client_ip])
165
+
166
+ transaction[:usage].each do |name, value|
167
+ append_encoded_value(result, index, [:usage, name], value)
168
+ end
169
+ end
170
+
171
+ result
172
+ end
173
+
174
+ def append_encoded_value(result, index, names, value)
175
+ result["transactions[#{index}][#{names.join('][')}]"] = CGI.escape(value.to_s) if value
176
+ end
177
+
178
+ def build_authorize_response(body)
179
+ response = AuthorizeResponse.new
180
+ doc = Nokogiri::XML(body)
181
+
182
+ response.plan = doc.at_css('plan').content.to_s.strip
183
+
184
+ doc.css('usage').each do |node|
185
+ response.add_usage(:metric => node['metric'].to_s.strip,
186
+ :period => node['period'].to_s.strip.to_sym,
187
+ :period_start => node.at('period_start').content,
188
+ :period_end => node.at('period_end').content,
189
+ :current_value => node.at('current_value').content.to_i,
190
+ :max_value => node.at('max_value').content.to_i)
191
+ end
192
+
193
+ response
194
+ end
195
+
196
+ def build_error_response(body)
197
+ response = Response.new(:success => false)
198
+ doc = Nokogiri::XML(body)
199
+
200
+ doc.css('error').each do |node|
201
+ response.add_error(node['index'].to_i,
202
+
203
+ # Backwards compatibility: error code is sometimes in
204
+ # the "id" attribute.
205
+ node['code'] || node['id'],
206
+
207
+ node.content.to_s.strip)
208
+ end
209
+
210
+ response
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,21 @@
1
+ module ThreeScale
2
+ class Response
3
+ def initialize(options)
4
+ @success = options[:success]
5
+ @errors = options[:errors] || []
6
+ end
7
+
8
+ def success?
9
+ @success
10
+ end
11
+
12
+ Error = Struct.new(:index, :code, :message)
13
+
14
+ attr_reader :errors
15
+
16
+ def add_error(*args)
17
+ @errors << Error.new(*args)
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,169 @@
1
+ require 'test/unit'
2
+ require 'fakeweb'
3
+ require 'mocha'
4
+
5
+ require 'three_scale/client'
6
+
7
+ class ThreeScale::ClientTest < Test::Unit::TestCase
8
+ def setup
9
+ FakeWeb.clean_registry
10
+ FakeWeb.allow_net_connect = false
11
+
12
+ @client = ThreeScale::Client.new(:provider_key => '1234abcd')
13
+ end
14
+
15
+ def test_raises_exception_if_provider_key_is_missing
16
+ assert_raise ArgumentError do
17
+ ThreeScale::Client.new({})
18
+ end
19
+ end
20
+
21
+ def test_default_host
22
+ client = ThreeScale::Client.new(:provider_key => '1234abcd')
23
+
24
+ assert_equal 'server.3scale.net', client.host
25
+ end
26
+
27
+ def test_successful_authorize
28
+ body = '<status>
29
+ <plan>Ultimate</plan>
30
+
31
+ <usage metric="hits" period="day">
32
+ <period_start>2010-04-26 00:00:00</period_start>
33
+ <period_end>2010-04-26 23:59:59</period_end>
34
+ <current_value>10023</current_value>
35
+ <max_value>50000</max_value>
36
+ </usage>
37
+
38
+ <usage metric="hits" period="month">
39
+ <period_start>2010-04-01 00:00:00</period_start>
40
+ <period_end>2010-04-30 23:59:59</period_end>
41
+ <current_value>999872</current_value>
42
+ <max_value>150000</max_value>
43
+ </usage>
44
+ </status>'
45
+
46
+ FakeWeb.register_uri(:get, 'http://server.3scale.net/transactions/authorize.xml?provider_key=1234abcd&user_key=foo', :status => ['200', 'OK'], :body => body)
47
+
48
+ response = @client.authorize(:user_key => 'foo')
49
+
50
+ assert response.success?
51
+ assert_equal 'Ultimate', response.plan
52
+ assert_equal 2, response.usages.size
53
+
54
+ assert_equal :day, response.usages[0].period
55
+ assert_equal Time.local(2010, 4, 26), response.usages[0].period_start
56
+ assert_equal Time.local(2010, 4, 26, 23, 59, 59), response.usages[0].period_end
57
+ assert_equal 10023, response.usages[0].current_value
58
+ assert_equal 50000, response.usages[0].max_value
59
+
60
+ assert_equal :month, response.usages[1].period
61
+ assert_equal Time.local(2010, 4, 1), response.usages[1].period_start
62
+ assert_equal Time.local(2010, 4, 30, 23, 59, 59), response.usages[1].period_end
63
+ assert_equal 999872, response.usages[1].current_value
64
+ assert_equal 150000, response.usages[1].max_value
65
+ end
66
+
67
+ def test_failed_authorize
68
+ error_body = '<error code="user.exceeded_limits">
69
+ usage limits are exceeded
70
+ </error>'
71
+
72
+ FakeWeb.register_uri(:get, 'http://server.3scale.net/transactions/authorize.xml?provider_key=1234abcd&user_key=foo', :status => ['403', 'Forbidden'], :body => error_body)
73
+
74
+ response = @client.authorize(:user_key => 'foo')
75
+
76
+ assert !response.success?
77
+ assert_equal 1, response.errors.size
78
+ assert_equal 'user.exceeded_limits', response.errors[0].code
79
+ assert_equal 'usage limits are exceeded', response.errors[0].message
80
+ end
81
+
82
+ def test_authorize_with_server_error
83
+ FakeWeb.register_uri(:get, 'http://server.3scale.net/transactions/authorize.xml?provider_key=1234abcd&user_key=foo', :status => ['500', 'Internal Server Error'], :body => 'OMG! WTF!')
84
+
85
+ assert_raise ThreeScale::ServerError do
86
+ @client.authorize(:user_key => 'foo')
87
+ end
88
+ end
89
+
90
+ def test_report_raises_an_exception_if_no_transactions_given
91
+ assert_raise ArgumentError do
92
+ @client.report
93
+ end
94
+ end
95
+
96
+ def test_successful_report
97
+ FakeWeb.register_uri(:post, 'http://server.3scale.net/transactions.xml',
98
+ :status => ['200', 'OK'])
99
+
100
+ response = @client.report({:user_key => 'foo',
101
+ :timestamp => Time.local(2010, 4, 27, 15, 00),
102
+ :usage => {'hits' => 1}})
103
+
104
+ assert response.success?
105
+ end
106
+
107
+ def test_report_encodes_transactions
108
+ http_response = stub
109
+ Net::HTTPSuccess.stubs(:===).with(http_response).returns(true)
110
+
111
+ Net::HTTP.expects(:post_form).
112
+ with(anything,
113
+ 'provider_key' => '1234abcd',
114
+ 'transactions[0][user_key]' => 'foo',
115
+ 'transactions[0][usage][hits]' => '1',
116
+ 'transactions[0][timestamp]' => CGI.escape('2010-04-27 15:42:17 0200'),
117
+ 'transactions[1][user_key]' => 'bar',
118
+ 'transactions[1][usage][hits]' => '1',
119
+ 'transactions[1][timestamp]' => CGI.escape('2010-04-27 15:55:12 0200')).
120
+ returns(http_response)
121
+
122
+ @client.report({:user_key => 'foo',
123
+ :usage => {'hits' => 1},
124
+ :timestamp => '2010-04-27 15:42:17 0200'},
125
+
126
+ {:user_key => 'bar',
127
+ :usage => {'hits' => 1},
128
+ :timestamp => '2010-04-27 15:55:12 0200'})
129
+ end
130
+
131
+ def test_failed_report
132
+ error_body = '<errors>
133
+ <error code="user.invalid_key" index="0">
134
+ user key is invalid
135
+ </error>
136
+ <error code="provider.invalid_metric" index="1">
137
+ metric does not exist
138
+ </error>
139
+ </errors>'
140
+
141
+ FakeWeb.register_uri(:post, 'http://server.3scale.net/transactions.xml',
142
+ :status => ['403', 'Forbidden'],
143
+ :body => error_body)
144
+
145
+ response = @client.report({:user_key => 'bogus', :usage => {'hits' => 1}},
146
+ {:user_key => 'bar', :usage => {'monkeys' => 1000000000}})
147
+
148
+ assert !response.success?
149
+ assert_equal 2, response.errors.size
150
+
151
+ assert_equal 0, response.errors[0].index
152
+ assert_equal 'user.invalid_key', response.errors[0].code
153
+ assert_equal 'user key is invalid', response.errors[0].message
154
+
155
+ assert_equal 1, response.errors[1].index
156
+ assert_equal 'provider.invalid_metric', response.errors[1].code
157
+ assert_equal 'metric does not exist', response.errors[1].message
158
+ end
159
+
160
+ def test_report_with_server_error
161
+ FakeWeb.register_uri(:post, 'http://server.3scale.net/transactions.xml',
162
+ :status => ['500', 'Internal Server Error'],
163
+ :body => 'OMG! WTF!')
164
+
165
+ assert_raise ThreeScale::ServerError do
166
+ @client.report({:user_key => 'foo', :usage => {'hits' => 1}})
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,41 @@
1
+ require 'test/unit'
2
+ require 'three_scale/client'
3
+
4
+ if ENV['TEST_3SCALE_PROVIDER_KEY'] && ENV['TEST_3SCALE_USER_KEYS']
5
+ class ThreeScale::RemoteTest < Test::Unit::TestCase
6
+ def setup
7
+ @provider_key = ENV['TEST_3SCALE_PROVIDER_KEY']
8
+ @user_keys = ENV['TEST_3SCALE_USER_KEYS'].split(',').map { |key| key.strip }
9
+
10
+ @client = ThreeScale::Client.new(:provider_key => @provider_key)
11
+
12
+ if defined?(FakeWeb)
13
+ FakeWeb.clean_registry
14
+ FakeWeb.allow_net_connect = true
15
+ end
16
+ end
17
+
18
+ def test_successful_authorize
19
+ response = @client.authorize(:user_key => @user_keys[0])
20
+ assert response.success?
21
+ end
22
+
23
+ def test_failed_authorize
24
+ response = @client.authorize(:user_key => 'invalid-user-key')
25
+ assert !response.success?
26
+ assert_equal 'user.invalid_key', response.errors[0].code
27
+ end
28
+
29
+ def test_successful_report
30
+ transactions = @user_keys.map do |user_key|
31
+ {:user_key => user_key, :usage => {'hits' => 1}}
32
+ end
33
+
34
+ response = @client.report(*transactions)
35
+ assert response.success?
36
+ end
37
+ end
38
+
39
+ else
40
+ puts "You need to set enviroment variables TEST_3SCALE_PROVIDER_KEY and TEST_3SCALE_USER_KEYS to run this remote test."
41
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: 3scale_client
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 2
7
+ - 0
8
+ - 1
9
+ version: 2.0.1
10
+ platform: ruby
11
+ authors:
12
+ - "Adam Cig\xC3\xA1nek"
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-28 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: nokogiri
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ description: |
33
+ This gem allows to easily connect an application that provides a Web Service with the 3scale API Management System to authorize it's users and report the usage.
34
+
35
+ email: adam@3scale.net
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - README.rdoc
42
+ files:
43
+ - .gitignore
44
+ - 3scale_client.gemspec
45
+ - LICENCE
46
+ - README.rdoc
47
+ - Rakefile
48
+ - VERSION
49
+ - lib/three_scale/authorize_response.rb
50
+ - lib/three_scale/client.rb
51
+ - lib/three_scale/response.rb
52
+ - test/client_test.rb
53
+ - test/remote_test.rb
54
+ has_rdoc: true
55
+ homepage: http://www.3scale.net
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --charset=UTF-8
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.3.6
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Client for 3scale Web Service Management System API
84
+ test_files:
85
+ - test/remote_test.rb
86
+ - test/client_test.rb