fulfil_api 0.6.2 → 0.6.3
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/lib/fulfil_api/client.rb +71 -11
- data/lib/fulfil_api/error.rb +14 -0
- data/lib/fulfil_api/tpl_client.rb +64 -8
- data/lib/fulfil_api/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e5088d4a6e17bfc91b7dbdbd841db0d649bbc7fc16e295fb63a1cf8d4e80436a
|
|
4
|
+
data.tar.gz: 30f89d00763dde2c6d2b1573ded61440097b87904dae062c7f8ee147de9621e0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 35aa477f5a29a3597d7567bcb8f6708fb89cb13d20c0f848cb06dd46f381bbff94dbddb36bf6fb95d9e544f01c7e94989f44ab872e1ed891f5ea254dfa358727
|
|
7
|
+
data.tar.gz: de45ced7efb091fc5cfcce224310afab14b233f97197887d94137f2c31007b43b363db424a1478108a8469e1813eeb980a71febe0d4c55f6b6e63d101a57c35b
|
data/lib/fulfil_api/client.rb
CHANGED
|
@@ -5,6 +5,36 @@ require "faraday/net_http_persistent"
|
|
|
5
5
|
|
|
6
6
|
module FulfilApi
|
|
7
7
|
class Client
|
|
8
|
+
@connection_cache = {}
|
|
9
|
+
@connection_cache_mutex = Mutex.new
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
# Looks up a memoized {Faraday::Connection} for the given cache key, or
|
|
13
|
+
# builds and stores one by yielding the block.
|
|
14
|
+
#
|
|
15
|
+
# The cache is process-wide, so the underlying `net_http_persistent` adapter
|
|
16
|
+
# can actually reuse its TCP/TLS connection pool across requests. Without
|
|
17
|
+
# this, each `FulfilApi.client` call would instantiate a fresh Faraday
|
|
18
|
+
# connection, defeating the purpose of the persistent adapter.
|
|
19
|
+
#
|
|
20
|
+
# @param cache_key [Array] A key uniquely identifying the connection.
|
|
21
|
+
# @yieldreturn [Faraday::Connection] The newly built connection.
|
|
22
|
+
# @return [Faraday::Connection]
|
|
23
|
+
def connection_for(cache_key)
|
|
24
|
+
@connection_cache_mutex.synchronize do
|
|
25
|
+
@connection_cache[cache_key] ||= yield
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Clears the memoized connection cache. Intended for use in test suites
|
|
30
|
+
# that need to isolate connection state between tests.
|
|
31
|
+
#
|
|
32
|
+
# @return [void]
|
|
33
|
+
def reset_connection_cache!
|
|
34
|
+
@connection_cache_mutex.synchronize { @connection_cache.clear }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
8
38
|
# @param configuration [FulfilApi::Configuration]
|
|
9
39
|
def initialize(configuration)
|
|
10
40
|
@configuration = configuration
|
|
@@ -56,13 +86,29 @@ module FulfilApi
|
|
|
56
86
|
@api_endpoint ||= "https://#{configuration.merchant_id}.fulfil.io"
|
|
57
87
|
end
|
|
58
88
|
|
|
89
|
+
# Returns a {Faraday::Connection} for the current configuration, reusing the
|
|
90
|
+
# memoized connection from the class-level cache whenever possible.
|
|
91
|
+
#
|
|
59
92
|
# @return [Faraday::Connection]
|
|
60
93
|
def connection
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
94
|
+
self.class.connection_for(connection_cache_key) { build_connection }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Builds a fresh {Faraday::Connection} for the current configuration.
|
|
98
|
+
#
|
|
99
|
+
# The connection carries no credentials — authentication headers are applied
|
|
100
|
+
# per-request in {#request}. This keeps tokens out of any long-lived data
|
|
101
|
+
# structure (cache keys, the connection's default headers) and lets a single
|
|
102
|
+
# connection be shared across configurations that target the same merchant
|
|
103
|
+
# with different access tokens.
|
|
104
|
+
#
|
|
105
|
+
# No `Content-Type` header is set on the connection. The `:json` request
|
|
106
|
+
# middleware adds it on requests that actually have a body, so bodyless
|
|
107
|
+
# verbs (GET, DELETE) are not sent with a misleading content type.
|
|
108
|
+
#
|
|
109
|
+
# @return [Faraday::Connection]
|
|
110
|
+
def build_connection
|
|
111
|
+
Faraday.new(url: api_endpoint, request: configuration.request_options) do |connection|
|
|
66
112
|
connection.adapter :net_http_persistent # TODO: Allow passing configuration options
|
|
67
113
|
|
|
68
114
|
# Configuration of the request middleware
|
|
@@ -74,6 +120,11 @@ module FulfilApi
|
|
|
74
120
|
end
|
|
75
121
|
end
|
|
76
122
|
|
|
123
|
+
# @return [Array] The cache key identifying a unique connection.
|
|
124
|
+
def connection_cache_key
|
|
125
|
+
[configuration.merchant_id, configuration.request_options]
|
|
126
|
+
end
|
|
127
|
+
|
|
77
128
|
# @param relative_path [String] The relative path to the API endpoint.
|
|
78
129
|
# @return [String] The absolute path for the request to the API endpoint.
|
|
79
130
|
def expand_relative_path(relative_path)
|
|
@@ -98,17 +149,26 @@ module FulfilApi
|
|
|
98
149
|
# @param relative_path [String] The relative path to the API endpoint.
|
|
99
150
|
# @return [Array, Hash, String] The parsed response body.
|
|
100
151
|
def request(method, relative_path, *args, **kwargs)
|
|
101
|
-
connection.send(method.to_sym, expand_relative_path(relative_path), *args, **kwargs)
|
|
152
|
+
response = connection.send(method.to_sym, expand_relative_path(relative_path), *args, **kwargs) do |req|
|
|
153
|
+
apply_authentication(req)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
response.body
|
|
102
157
|
rescue Faraday::Error => e
|
|
103
158
|
handle_request_error(e)
|
|
104
159
|
end
|
|
105
160
|
|
|
106
|
-
#
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
161
|
+
# Applies the configured access token to the request as an HTTP header.
|
|
162
|
+
#
|
|
163
|
+
# Authentication is set per-request rather than on the shared {#connection},
|
|
164
|
+
# so the connection cache does not need to know about credentials.
|
|
165
|
+
#
|
|
166
|
+
# @param request [Faraday::Request] The request being prepared.
|
|
167
|
+
# @return [void]
|
|
168
|
+
def apply_authentication(request)
|
|
169
|
+
return if configuration.access_token.nil?
|
|
110
170
|
|
|
111
|
-
|
|
171
|
+
configuration.access_token.to_http_header.each { |name, value| request.headers[name] = value }
|
|
112
172
|
end
|
|
113
173
|
end
|
|
114
174
|
|
data/lib/fulfil_api/error.rb
CHANGED
|
@@ -15,7 +15,21 @@ module FulfilApi
|
|
|
15
15
|
|
|
16
16
|
# @return [String]
|
|
17
17
|
def message
|
|
18
|
+
body_message = parsed_body_message
|
|
19
|
+
return "[FulfilApi::Error] #{body_message}" if body_message
|
|
20
|
+
|
|
18
21
|
"[FulfilApi::Error] #{super}"
|
|
19
22
|
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def parsed_body_message
|
|
27
|
+
body = details&.dig(:response_body)
|
|
28
|
+
return unless body
|
|
29
|
+
|
|
30
|
+
JSON.parse(body)
|
|
31
|
+
rescue JSON::ParserError
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
20
34
|
end
|
|
21
35
|
end
|
|
@@ -16,6 +16,36 @@ module FulfilApi
|
|
|
16
16
|
|
|
17
17
|
DEFAULT_API_VERSION = "v1"
|
|
18
18
|
|
|
19
|
+
@connection_cache = {}
|
|
20
|
+
@connection_cache_mutex = Mutex.new
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
# Looks up a memoized {Faraday::Connection} for the given cache key, or
|
|
24
|
+
# builds and stores one by yielding the block.
|
|
25
|
+
#
|
|
26
|
+
# The cache is process-wide, so the underlying `net_http_persistent` adapter
|
|
27
|
+
# can actually reuse its TCP/TLS connection pool across requests. Without
|
|
28
|
+
# this, each `FulfilApi.tpl_client` call would instantiate a fresh Faraday
|
|
29
|
+
# connection, defeating the purpose of the persistent adapter.
|
|
30
|
+
#
|
|
31
|
+
# @param cache_key [Array] A key uniquely identifying the connection.
|
|
32
|
+
# @yieldreturn [Faraday::Connection] The newly built connection.
|
|
33
|
+
# @return [Faraday::Connection]
|
|
34
|
+
def connection_for(cache_key)
|
|
35
|
+
@connection_cache_mutex.synchronize do
|
|
36
|
+
@connection_cache[cache_key] ||= yield
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Clears the memoized connection cache. Intended for use in test suites
|
|
41
|
+
# that need to isolate connection state between tests.
|
|
42
|
+
#
|
|
43
|
+
# @return [void]
|
|
44
|
+
def reset_connection_cache!
|
|
45
|
+
@connection_cache_mutex.synchronize { @connection_cache.clear }
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
19
49
|
# @param configuration [FulfilApi::Configuration]
|
|
20
50
|
def initialize(configuration)
|
|
21
51
|
@configuration = configuration
|
|
@@ -77,10 +107,23 @@ module FulfilApi
|
|
|
77
107
|
@api_endpoint ||= "https://#{merchant_id}.fulfil.io"
|
|
78
108
|
end
|
|
79
109
|
|
|
110
|
+
# Returns a {Faraday::Connection} for the current configuration, reusing the
|
|
111
|
+
# memoized connection from the class-level cache whenever possible.
|
|
112
|
+
#
|
|
80
113
|
# @return [Faraday::Connection]
|
|
81
114
|
def connection
|
|
82
|
-
|
|
83
|
-
|
|
115
|
+
self.class.connection_for(connection_cache_key) { build_connection }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Builds a fresh {Faraday::Connection} for the current configuration.
|
|
119
|
+
#
|
|
120
|
+
# The connection carries no credentials — the bearer token is applied
|
|
121
|
+
# per-request in {#request}, keeping it out of long-lived data structures
|
|
122
|
+
# such as cache keys or default connection headers.
|
|
123
|
+
#
|
|
124
|
+
# @return [Faraday::Connection]
|
|
125
|
+
def build_connection
|
|
126
|
+
Faraday.new(
|
|
84
127
|
url: api_endpoint,
|
|
85
128
|
request: configuration.request_options
|
|
86
129
|
) do |connection|
|
|
@@ -95,6 +138,11 @@ module FulfilApi
|
|
|
95
138
|
end
|
|
96
139
|
end
|
|
97
140
|
|
|
141
|
+
# @return [Array] The cache key identifying a unique connection.
|
|
142
|
+
def connection_cache_key
|
|
143
|
+
[merchant_id, api_version, configuration.request_options]
|
|
144
|
+
end
|
|
145
|
+
|
|
98
146
|
# @param relative_path [String] The relative path to the API endpoint.
|
|
99
147
|
# @return [String] The absolute path for the request to the API endpoint.
|
|
100
148
|
def expand_relative_path(relative_path)
|
|
@@ -119,16 +167,24 @@ module FulfilApi
|
|
|
119
167
|
# @param relative_path [String] The relative path to the API endpoint.
|
|
120
168
|
# @return [Array, Hash, String] The parsed response body.
|
|
121
169
|
def request(method, relative_path, *args, **kwargs)
|
|
122
|
-
connection.send(method.to_sym, expand_relative_path(relative_path), *args, **kwargs)
|
|
170
|
+
response = connection.send(method.to_sym, expand_relative_path(relative_path), *args, **kwargs) do |req|
|
|
171
|
+
apply_authentication(req)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
response.body
|
|
123
175
|
rescue Faraday::Error => e
|
|
124
176
|
handle_request_error(e)
|
|
125
177
|
end
|
|
126
178
|
|
|
127
|
-
#
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
179
|
+
# Applies the configured bearer token to the request as an HTTP header.
|
|
180
|
+
#
|
|
181
|
+
# Authentication is set per-request rather than on the shared {#connection},
|
|
182
|
+
# so the connection cache does not need to know about credentials.
|
|
183
|
+
#
|
|
184
|
+
# @param request [Faraday::Request] The request being prepared.
|
|
185
|
+
# @return [void]
|
|
186
|
+
def apply_authentication(request)
|
|
187
|
+
request.headers["Authorization"] = "Bearer #{auth_token}"
|
|
132
188
|
end
|
|
133
189
|
end
|
|
134
190
|
|
data/lib/fulfil_api/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fulfil_api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefan Vermaas
|
|
@@ -125,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
125
125
|
- !ruby/object:Gem::Version
|
|
126
126
|
version: '0'
|
|
127
127
|
requirements: []
|
|
128
|
-
rubygems_version: 4.0.
|
|
128
|
+
rubygems_version: 4.0.6
|
|
129
129
|
specification_version: 4
|
|
130
130
|
summary: A HTTP client to interact the Fulfil.io API
|
|
131
131
|
test_files: []
|