attune 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +9 -1
- data/attune.gemspec +1 -1
- data/lib/attune/client.rb +63 -6
- data/lib/attune/configurable.rb +4 -0
- data/lib/attune/default.rb +2 -0
- data/lib/attune/version.rb +1 -1
- data/spec/attune/client_spec.rb +25 -2
- data/spec/attune/json_logger_spec.rb +1 -1
- data/spec/remote_spec.rb +17 -2
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa77fa0fcbd44e02a0b3a26e722a76f3e7fabbff
|
4
|
+
data.tar.gz: bc15acc01376c94db3192f81a90d114aa6040e91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04272c55ef9a1f7095a2e77beaf008451aa73bf115fc5f99cf791f34e76e1dad2ffda8c3f1fde06b40203530fd6643822c563bd389772430b1f3ec9dbcb04b34
|
7
|
+
data.tar.gz: b95899dd52c6d526944569c775bfae2e942bfda23766b346792bfb68f28a0c6bc1103055ae9f5e86f1199842b788719348ef24f49d6fb888b2ba4585ce0600e6
|
data/README.md
CHANGED
@@ -77,12 +77,20 @@ class ProductsController
|
|
77
77
|
end
|
78
78
|
```
|
79
79
|
|
80
|
+
The client provides a way to request a new auth_token through the API
|
81
|
+
|
82
|
+
``` ruby
|
83
|
+
client = Attune::Client.new
|
84
|
+
auth_token = client.get_auth_token('my-client-id', 'my-client-secret')
|
85
|
+
```
|
86
|
+
|
80
87
|
### Configuration
|
81
88
|
|
82
89
|
Attune can be configured globally
|
83
90
|
|
84
91
|
``` ruby
|
85
92
|
Attune.configure do |c|
|
93
|
+
c.auth_token = "my-secure-auth-token"
|
86
94
|
c.endpoint = "http://example.com/"
|
87
95
|
c.timeout = 5
|
88
96
|
end
|
@@ -91,7 +99,7 @@ end
|
|
91
99
|
Settings can also be overridden on a client object
|
92
100
|
|
93
101
|
``` ruby
|
94
|
-
client = Attune::Client.new(timeout: 2)
|
102
|
+
client = Attune::Client.new(auth_token: "my-secure-auth-token", timeout: 2)
|
95
103
|
```
|
96
104
|
|
97
105
|
See the documentation for
|
data/attune.gemspec
CHANGED
@@ -24,6 +24,6 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency "rake"
|
25
25
|
spec.add_development_dependency "rspec"
|
26
26
|
spec.add_development_dependency "yard"
|
27
|
-
spec.add_development_dependency "redcarpet"
|
27
|
+
spec.add_development_dependency "redcarpet", ">= 3.1.1"
|
28
28
|
spec.add_development_dependency "simplecov"
|
29
29
|
end
|
data/lib/attune/client.rb
CHANGED
@@ -7,6 +7,12 @@ module Attune
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
+
class AuthenticationException < Faraday::Error::ClientError
|
11
|
+
def initialize(message="Authentication credentials not accepted")
|
12
|
+
super(message)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
10
16
|
class Client
|
11
17
|
include Attune::Configurable
|
12
18
|
|
@@ -19,13 +25,44 @@ module Attune
|
|
19
25
|
# )
|
20
26
|
#
|
21
27
|
# @param [Hash] options Options for connection (see Attune::Configurable)
|
22
|
-
# @
|
28
|
+
# @return A new client object
|
23
29
|
def initialize(options={})
|
24
30
|
Attune::Configurable::KEYS.each do |key|
|
25
31
|
send("#{key}=", options[key] || Attune::Default.send(key))
|
26
32
|
end
|
27
33
|
end
|
28
34
|
|
35
|
+
# Request an auth token
|
36
|
+
#
|
37
|
+
# @example Generate a new auth token
|
38
|
+
# token = client.get_auth_token("client id", "secret")
|
39
|
+
# @param [String] client_id The client identifier.
|
40
|
+
# @param [String] client_secret The secret key for the client.
|
41
|
+
# @return An auth token if credentials accepted
|
42
|
+
# @raise [ArgumentError] if client_id or client_secret is not provided
|
43
|
+
# @raise [AuthenticationException] if client_id or client_secret are not accepted
|
44
|
+
# @raise [Faraday::Error::ClientError] if the request fails or exceeds the timeout
|
45
|
+
def get_auth_token(client_id, client_secret)
|
46
|
+
raise ArgumentError, "client_id required" unless client_id
|
47
|
+
raise ArgumentError, "client_secret required" unless client_secret
|
48
|
+
|
49
|
+
response = post_form("oauth/token",
|
50
|
+
client_id: client_id,
|
51
|
+
client_secret: client_secret,
|
52
|
+
grant_type: :client_credentials
|
53
|
+
)
|
54
|
+
if response
|
55
|
+
body = JSON.parse(response.body)
|
56
|
+
if body['error']
|
57
|
+
raise AuthenticationException, body['error_description']
|
58
|
+
end
|
59
|
+
body['access_token']
|
60
|
+
else
|
61
|
+
# Return a new UUID if there was an exception and we're in mock mode
|
62
|
+
SecureRandom.uuid
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
29
66
|
# Create an anonymous tracked user
|
30
67
|
#
|
31
68
|
# @example Generate a new id (preferred)
|
@@ -43,13 +80,14 @@ module Attune
|
|
43
80
|
# @return id [String]
|
44
81
|
# @raise [ArgumentError] if user_agent is not provided
|
45
82
|
# @raise [Faraday::Error::ClientError] if the request fails or exceeds the timeout
|
83
|
+
# @raise [AuthenticationException] if authorization header not accepted
|
46
84
|
def create_anonymous(options)
|
47
85
|
raise ArgumentError, "user_agent required" unless options[:user_agent]
|
48
86
|
if id = options[:id]
|
49
87
|
put("anonymous/#{id}", {user_agent: options[:user_agent]})
|
50
88
|
id
|
51
89
|
else
|
52
|
-
if response =
|
90
|
+
if response = post_json("anonymous", {user_agent: options[:user_agent]})
|
53
91
|
response[:location][/\Aurn:id:([a-z0-9\-]+)\Z/, 1]
|
54
92
|
else
|
55
93
|
# Return a new UUID if there was an exception and we're in mock mode
|
@@ -77,6 +115,7 @@ module Attune
|
|
77
115
|
# @return ranking [Array<String>] The entities in their ranked order
|
78
116
|
# @raise [ArgumentError] if required parameters are missing
|
79
117
|
# @raise [Faraday::Error::ClientError] if the request fails or exceeds the timeout
|
118
|
+
# @raise [AuthenticationException] if authorization header not accepted
|
80
119
|
def get_rankings(options)
|
81
120
|
qs = encoded_ranking_params(options)
|
82
121
|
if response = get("rankings/#{qs}", customer: options.fetch(:customer, 'none'))
|
@@ -107,6 +146,7 @@ module Attune
|
|
107
146
|
# @param [Array<Hash>] multi_options An array of options (see #get_rankings)
|
108
147
|
# @return [Array<Array<String>>] rankings
|
109
148
|
# @raise [Faraday::Error::ClientError] if the request fails or exceeds the timeout
|
149
|
+
# @raise [AuthenticationException] if authorization header not accepted
|
110
150
|
def multi_get_rankings(multi_options)
|
111
151
|
requests = multi_options.map do |options|
|
112
152
|
encoded_ranking_params(options)
|
@@ -139,6 +179,7 @@ module Attune
|
|
139
179
|
# 'cd171f7c-560d-4a62-8d65-16b87419a58'
|
140
180
|
# )
|
141
181
|
# @raise [Faraday::Error::ClientError] if the request fails or exceeds the timeout
|
182
|
+
# @raise [AuthenticationException] if authorization header not accepted
|
142
183
|
def bind(id, customer_id)
|
143
184
|
put("bindings/anonymous=#{id}&customer=#{customer_id}")
|
144
185
|
true
|
@@ -168,8 +209,18 @@ module Attune
|
|
168
209
|
handle_exception(e)
|
169
210
|
end
|
170
211
|
|
171
|
-
def
|
172
|
-
adapter.post(path,
|
212
|
+
def post_form(path, params={})
|
213
|
+
adapter.post(path, params)
|
214
|
+
rescue Faraday::Error::ClientError => e
|
215
|
+
handle_exception(e)
|
216
|
+
end
|
217
|
+
|
218
|
+
def post_json(path, params={})
|
219
|
+
adapter.post do |req|
|
220
|
+
req.url path
|
221
|
+
req.headers['Content-Type'] = 'application/json'
|
222
|
+
req.body = ::JSON.dump(params)
|
223
|
+
end
|
173
224
|
rescue Faraday::Error::ClientError => e
|
174
225
|
handle_exception(e)
|
175
226
|
end
|
@@ -178,13 +229,19 @@ module Attune
|
|
178
229
|
if exception_handler == :mock
|
179
230
|
nil
|
180
231
|
else
|
181
|
-
|
232
|
+
if e.response && e.response[:status] == 401
|
233
|
+
raise AuthenticationException, e
|
234
|
+
else
|
235
|
+
raise e
|
236
|
+
end
|
182
237
|
end
|
183
238
|
end
|
184
239
|
|
185
240
|
def adapter
|
186
241
|
raise DisabledException if disabled?
|
187
|
-
Faraday.new(url: endpoint, builder: middleware, request: {timeout: timeout})
|
242
|
+
conn = Faraday.new(url: endpoint, builder: middleware, request: {timeout: timeout})
|
243
|
+
conn.authorization :Bearer, auth_token unless !auth_token
|
244
|
+
conn
|
188
245
|
end
|
189
246
|
end
|
190
247
|
end
|
data/lib/attune/configurable.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Attune
|
2
2
|
module Configurable
|
3
3
|
KEYS = [
|
4
|
+
:auth_token,
|
4
5
|
:endpoint,
|
5
6
|
:middleware,
|
6
7
|
:disabled,
|
@@ -8,6 +9,9 @@ module Attune
|
|
8
9
|
:timeout
|
9
10
|
]
|
10
11
|
|
12
|
+
# The Authorization token
|
13
|
+
attr_accessor :auth_token
|
14
|
+
|
11
15
|
# The HTTP endpoint to connect to
|
12
16
|
attr_accessor :endpoint
|
13
17
|
|
data/lib/attune/default.rb
CHANGED
data/lib/attune/version.rb
CHANGED
data/spec/attune/client_spec.rb
CHANGED
@@ -39,6 +39,15 @@ describe Attune::Client do
|
|
39
39
|
}.to raise_exception(Faraday::Error::ConnectionFailed)
|
40
40
|
stubs.verify_stubbed_calls
|
41
41
|
end
|
42
|
+
it "will raise AuthenticationException" do
|
43
|
+
stubs.post("oauth/token",
|
44
|
+
{:client_id=>"id", :client_secret=>"secret", grant_type: :client_credentials}
|
45
|
+
){ [200, {}, %[{"error":"invalid_client","error_description":"Bad client credentials"}]] }
|
46
|
+
expect {
|
47
|
+
client.get_auth_token("id", "secret")
|
48
|
+
}.to raise_exception(Attune::AuthenticationException)
|
49
|
+
stubs.verify_stubbed_calls
|
50
|
+
end
|
42
51
|
end
|
43
52
|
|
44
53
|
describe "disabled" do
|
@@ -52,6 +61,11 @@ describe Attune::Client do
|
|
52
61
|
end
|
53
62
|
context "with mock" do
|
54
63
|
let(:options){ {disabled: true, exception_handler: :mock} }
|
64
|
+
|
65
|
+
it "mocks get_auth_token" do
|
66
|
+
result = client.get_auth_token("id", "secret")
|
67
|
+
expect(result).to match(/^[a-z0-9\-]+$/)
|
68
|
+
end
|
55
69
|
it "mocks create_anonymous with an id" do
|
56
70
|
result = client.create_anonymous(id: '12345', user_agent: 'Mozilla/5.0')
|
57
71
|
expect(result).to eq('12345')
|
@@ -81,6 +95,15 @@ describe Attune::Client do
|
|
81
95
|
end
|
82
96
|
end
|
83
97
|
|
98
|
+
it "can get_auth_token requesting a token" do
|
99
|
+
stubs.post("oauth/token",
|
100
|
+
{:client_id=>"id", :client_secret=>"secret", grant_type: :client_credentials}
|
101
|
+
){ [200, {}, %[{"access_token":"secret-token","token_type":"bearer"}]] }
|
102
|
+
token = client.get_auth_token('id', 'secret')
|
103
|
+
|
104
|
+
expect(token).to eq('secret-token')
|
105
|
+
end
|
106
|
+
|
84
107
|
it "can create_anonymous generating an id" do
|
85
108
|
stubs.post("anonymous", %[{"user_agent":"Mozilla/5.0"}]){ [200, {location: 'urn:id:abcd123'}, nil] }
|
86
109
|
id = client.create_anonymous(user_agent: 'Mozilla/5.0')
|
@@ -104,7 +127,7 @@ describe Attune::Client do
|
|
104
127
|
end
|
105
128
|
|
106
129
|
it "can get_rankings" do
|
107
|
-
stubs.get("rankings/anonymous=abcd123&
|
130
|
+
stubs.get("rankings/anonymous=abcd123&entities=1001%2C%2C1002%2C%2C1003%2C%2C1004&entity_collection=products&ip=none&view=b%2Fmens-pants"){ [200, {}, %[{"ranking":["1004","1003","1002","1001"]}]] }
|
108
131
|
rankings = client.get_rankings(
|
109
132
|
id: 'abcd123',
|
110
133
|
view: 'b/mens-pants',
|
@@ -119,7 +142,7 @@ describe Attune::Client do
|
|
119
142
|
let(:req1){ CGI::escape 'anonymous=0cddbc0-6114-11e3-949a-0800200c9a66&view=b%2Fmens-pants&entity_collection=products&entities=1001%2C%2C1002%2C%2C1003%2C%2C1004&ip=none' }
|
120
143
|
let(:req2){ CGI::escape 'anonymous=0cddbc0-6114-11e3-949a-0800200c9a66&view=b%2Fmens-pants&entity_collection=products&entities=2001%2C%2C2002%2C%2C2003%2C%2C2004&ip=none' }
|
121
144
|
it "can multi_get_rankings" do
|
122
|
-
stubs.get("/rankings?ids=anonymous%3D0cddbc0-6114-11e3-949a-0800200c9a66%
|
145
|
+
stubs.get("/rankings?ids=anonymous%3D0cddbc0-6114-11e3-949a-0800200c9a66%26entities%3D1001%252C%252C1002%252C%252C1003%252C%252C1004%26entity_collection%3Dproducts%26ip%3Dnone%26view%3Db%252Fmens-pants&ids=anonymous%3D0cddbc0-6114-11e3-949a-0800200c9a66%26entities%3D2001%252C%252C2002%252C%252C2003%252C%252C2004%26entity_collection%3Dproducts%26ip%3Dnone%26view%3Db%252Fmens-pants") do
|
123
146
|
[200, {}, <<-JSON]
|
124
147
|
{
|
125
148
|
"errors": {},
|
@@ -24,6 +24,6 @@ describe Attune::JsonLogger do
|
|
24
24
|
response.status.should == 200
|
25
25
|
response.body.should == "foobar"
|
26
26
|
|
27
|
-
expect(logged).to
|
27
|
+
expect(logged).to match(/{"ref":null,"v":1,"protocol":"http","host":"example.com","path":"\/test","t":12345000,"r_id":"eaa45af2-efc3-45ef-90da-9bcb56758e77","status":200,"ua":"Faraday v\d+.\d+.\d+","method":"get","perf":{"total":120.0}}/)
|
28
28
|
end
|
29
29
|
end
|
data/spec/remote_spec.rb
CHANGED
@@ -2,8 +2,23 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "remote requests" do
|
4
4
|
let(:endpoint){ ENV['REMOTE_ENDPOINT'] }
|
5
|
-
|
6
|
-
|
5
|
+
let(:auth_token){ ENV['AUTH_TOKEN'] }
|
6
|
+
|
7
|
+
let(:client_id) { ENV['CLIENT_ID'] }
|
8
|
+
let(:client_secret) { ENV['CLIENT_SECRET'] }
|
9
|
+
|
10
|
+
before do
|
11
|
+
pending "REMOTE_ENDPOINT required for remote spec" unless endpoint
|
12
|
+
pending "AUTH_TOKEN required for remote spec" unless auth_token
|
13
|
+
end
|
14
|
+
let!(:client){ Attune::Client.new(endpoint: endpoint, auth_token: auth_token) }
|
15
|
+
|
16
|
+
it "can request an auth_token given a client id and secret" do
|
17
|
+
pending "CLIENT_ID required for get_auth_token spec" unless client_id
|
18
|
+
pending "CLIENT_SECRET required for get_auth_token spec" unless client_secret
|
19
|
+
token = client.get_auth_token(client_id, client_secret)
|
20
|
+
token.should =~ /[a-z0-9\-]+/
|
21
|
+
end
|
7
22
|
|
8
23
|
it "can create an anonymous user" do
|
9
24
|
id = client.create_anonymous(user_agent: 'Mozilla/5.0')
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attune
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Hawthorn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-03-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - '>='
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 3.1.1
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - '>='
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 3.1.1
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: simplecov
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -158,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
158
158
|
version: '0'
|
159
159
|
requirements: []
|
160
160
|
rubyforge_project:
|
161
|
-
rubygems_version: 2.1
|
161
|
+
rubygems_version: 2.2.1
|
162
162
|
signing_key:
|
163
163
|
specification_version: 4
|
164
164
|
summary: Client for the Attune product ranking API.
|