fulfil_api 0.6.2 → 0.7.0
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/CHANGELOG.md +4 -0
- data/README.md +23 -1
- data/lib/fulfil_api/client.rb +105 -12
- data/lib/fulfil_api/configuration.rb +36 -4
- data/lib/fulfil_api/error.rb +14 -0
- data/lib/fulfil_api/tpl_client.rb +98 -12
- data/lib/fulfil_api/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c2dd30142d140730335b3f184bc28c0fd897af19246a8794ec5d416b1a335b2a
|
|
4
|
+
data.tar.gz: b81436ad499dfdd14ea84f5651d4670395907ad8090ab4e9eed432c6195da86c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6dee8c45eea430b8f6e7bfa82b03c3d737dd424cdda363c74680f4acedc9e896c28cfc3bc746e35d33035a863ffed9f995877b267a59e0afae0f2e67f7047d20
|
|
7
|
+
data.tar.gz: 5873899ac0f65bb6a9e1ecbd74aebd1f45806cee1faed5716c1649e158d83396de15139a93da9347b8b20ba080172c7e2cfa59aca36b6f89a89a4a57207e3167
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
- Re-enable Ruby's built-in retry for idempotent requests on the persistent connection, which the `net_http_persistent` adapter disables by forcing `max_retries` to `0`. This recovers stale keep-alive sockets transparently instead of surfacing them as read timeouts.
|
|
4
|
+
- Add a `connection_options` configuration option to tune the persistent connection (`max_retries`, `idle_timeout`, `pool_size`).
|
|
5
|
+
- `FulfilApi.with_config` now merges the temporary options over the active configuration instead of replacing it, so a block inherits credentials and other unspecified settings rather than resetting them to their defaults.
|
|
6
|
+
|
|
3
7
|
## [0.1.0] - 2024-08-10
|
|
4
8
|
|
|
5
9
|
- Initial release
|
data/README.md
CHANGED
|
@@ -41,6 +41,8 @@ end
|
|
|
41
41
|
|
|
42
42
|
#### Using a Dynamic Configuration
|
|
43
43
|
|
|
44
|
+
`with_config` temporarily applies options **on top of the currently active configuration** (per thread) and reverts when the block returns. The options you pass are merged over the active config, so you only need to specify what changes — credentials and other settings are inherited rather than reset to their defaults.
|
|
45
|
+
|
|
44
46
|
```ruby
|
|
45
47
|
FulfilApi.with_config(
|
|
46
48
|
access_token: FulfilApi::AccessToken.new(ENV["FULFIL_API_KEY"]),
|
|
@@ -50,6 +52,17 @@ FulfilApi.with_config(
|
|
|
50
52
|
end
|
|
51
53
|
```
|
|
52
54
|
|
|
55
|
+
This makes it easy to use different settings in contexts with different constraints. For example, a web request bound by a 30s timeout can keep tight defaults globally, while a background job (which has more time) overrides just the timeouts and retries without re-passing credentials:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
FulfilApi.with_config(
|
|
59
|
+
request_options: { open_timeout: 5, read_timeout: 60, write_timeout: 30 },
|
|
60
|
+
connection_options: { max_retries: 3, idle_timeout: 10 }
|
|
61
|
+
) do
|
|
62
|
+
# Long-running work against the Fulfil API
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
53
66
|
#### Available Configuration Options
|
|
54
67
|
|
|
55
68
|
The following configuration options are (currently) available throught both configuration methods:
|
|
@@ -60,7 +73,16 @@ The following configuration options are (currently) available throught both conf
|
|
|
60
73
|
|
|
61
74
|
- `merchant_id` (`String`): The `merchant_id` is the subdomain that the Fulfil instance is hosted on. This configuration option is required to be able to query Fulfil's API endpoints.
|
|
62
75
|
|
|
63
|
-
- `request_options` (`Hash`): The `request_options` are the
|
|
76
|
+
- `request_options` (`Hash`): The `request_options` are the per-request timeout options for the HTTP client. See [https://lostisland.github.io/faraday/#/customization/request-options](https://lostisland.github.io/faraday/#/customization/request-options) in `faraday`.
|
|
77
|
+
|
|
78
|
+
> **NOTE:** With the persistent (keep-alive) adapter there is no single whole-request `timeout`; Faraday resolves `read_timeout`, `open_timeout`, and `write_timeout` independently. `read_timeout` is the value that governs a slow or stalled response.
|
|
79
|
+
|
|
80
|
+
- `connection_options` (`Hash`): Tuning for the persistent (keep-alive) connection. Supported keys:
|
|
81
|
+
- `max_retries` (default `1`): Re-enables Ruby's built-in retry for **idempotent** requests (`GET`/`HEAD`/`PUT`/`DELETE`/`OPTIONS`). The `net_http_persistent` adapter disables this by forcing it to `0`, which makes a keep-alive socket the server has already dropped surface as a read timeout instead of being retried transparently on a fresh socket. `POST` is never auto-retried, so this is side-effect safe. Set to `0` to restore the adapter's default behaviour.
|
|
82
|
+
- `idle_timeout` (`Integer`, optional): Seconds a pooled socket may sit idle before it is recycled. Lower this towards your server's keep-alive window to shrink the stale-socket window for non-idempotent requests.
|
|
83
|
+
- `pool_size` (`Integer`, optional): Maximum number of concurrent connections kept in the pool.
|
|
84
|
+
|
|
85
|
+
> **NOTE:** When retries are enabled, the worst-case time for a request is roughly `(max_retries + 1) × read_timeout`. On platforms with a hard request cap (e.g. Heroku's 30s router limit), keep `read_timeout` low enough that this product stays under the cap.
|
|
64
86
|
|
|
65
87
|
### Querying the Fulfil API
|
|
66
88
|
|
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,14 +86,32 @@ 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
|
-
|
|
66
|
-
|
|
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|
|
|
112
|
+
connection.adapter(:net_http_persistent, **adapter_options) do |http|
|
|
113
|
+
configure_persistent_connection(http)
|
|
114
|
+
end
|
|
67
115
|
|
|
68
116
|
# Configuration of the request middleware
|
|
69
117
|
connection.request :json
|
|
@@ -74,6 +122,42 @@ module FulfilApi
|
|
|
74
122
|
end
|
|
75
123
|
end
|
|
76
124
|
|
|
125
|
+
# Initialization options for the persistent adapter. Only `pool_size` is
|
|
126
|
+
# accepted here; `idle_timeout` and `max_retries` are applied to the live
|
|
127
|
+
# Net::HTTP::Persistent instance in {#configure_persistent_connection}.
|
|
128
|
+
#
|
|
129
|
+
# @return [Hash]
|
|
130
|
+
def adapter_options
|
|
131
|
+
options = {}
|
|
132
|
+
options[:pool_size] = configuration.connection_options[:pool_size] if configuration.connection_options[:pool_size]
|
|
133
|
+
options
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Tunes the underlying Net::HTTP::Persistent connection.
|
|
137
|
+
#
|
|
138
|
+
# The `net_http_persistent` adapter forces `max_retries` to 0 on every
|
|
139
|
+
# request, which disables Ruby's built-in retry for idempotent requests.
|
|
140
|
+
# Restoring it lets a stale keep-alive socket — one the server has already
|
|
141
|
+
# closed — be retried transparently on a fresh socket instead of surfacing
|
|
142
|
+
# as a read timeout. The config block runs after the adapter zeroes the
|
|
143
|
+
# value, so this takes effect.
|
|
144
|
+
#
|
|
145
|
+
# @param http [Net::HTTP::Persistent] The live persistent connection.
|
|
146
|
+
# @return [void]
|
|
147
|
+
def configure_persistent_connection(http)
|
|
148
|
+
if configuration.connection_options[:idle_timeout]
|
|
149
|
+
http.idle_timeout = configuration.connection_options[:idle_timeout]
|
|
150
|
+
end
|
|
151
|
+
return if configuration.connection_options[:max_retries].nil?
|
|
152
|
+
|
|
153
|
+
http.max_retries = configuration.connection_options[:max_retries]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# @return [Array] The cache key identifying a unique connection.
|
|
157
|
+
def connection_cache_key
|
|
158
|
+
[configuration.merchant_id, configuration.request_options, configuration.connection_options]
|
|
159
|
+
end
|
|
160
|
+
|
|
77
161
|
# @param relative_path [String] The relative path to the API endpoint.
|
|
78
162
|
# @return [String] The absolute path for the request to the API endpoint.
|
|
79
163
|
def expand_relative_path(relative_path)
|
|
@@ -98,17 +182,26 @@ module FulfilApi
|
|
|
98
182
|
# @param relative_path [String] The relative path to the API endpoint.
|
|
99
183
|
# @return [Array, Hash, String] The parsed response body.
|
|
100
184
|
def request(method, relative_path, *args, **kwargs)
|
|
101
|
-
connection.send(method.to_sym, expand_relative_path(relative_path), *args, **kwargs)
|
|
185
|
+
response = connection.send(method.to_sym, expand_relative_path(relative_path), *args, **kwargs) do |req|
|
|
186
|
+
apply_authentication(req)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
response.body
|
|
102
190
|
rescue Faraday::Error => e
|
|
103
191
|
handle_request_error(e)
|
|
104
192
|
end
|
|
105
193
|
|
|
106
|
-
#
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
194
|
+
# Applies the configured access token to the request as an HTTP header.
|
|
195
|
+
#
|
|
196
|
+
# Authentication is set per-request rather than on the shared {#connection},
|
|
197
|
+
# so the connection cache does not need to know about credentials.
|
|
198
|
+
#
|
|
199
|
+
# @param request [Faraday::Request] The request being prepared.
|
|
200
|
+
# @return [void]
|
|
201
|
+
def apply_authentication(request)
|
|
202
|
+
return if configuration.access_token.nil?
|
|
110
203
|
|
|
111
|
-
|
|
204
|
+
configuration.access_token.to_http_header.each { |name, value| request.headers[name] = value }
|
|
112
205
|
end
|
|
113
206
|
end
|
|
114
207
|
|
|
@@ -7,10 +7,23 @@ module FulfilApi
|
|
|
7
7
|
# to these settings.
|
|
8
8
|
class Configuration
|
|
9
9
|
attr_accessor :access_token, :api_version, :merchant_id, :request_options, :tpl
|
|
10
|
+
attr_reader :connection_options
|
|
10
11
|
|
|
11
12
|
DEFAULT_API_VERSION = "v2"
|
|
12
13
|
DEFAULT_REQUEST_OPTIONS = { open_timeout: 1, read_timeout: 5, write_timeout: 5, timeout: 5 }.freeze
|
|
13
14
|
|
|
15
|
+
# Tuning for the persistent (keep-alive) HTTP connection.
|
|
16
|
+
#
|
|
17
|
+
# `max_retries` re-enables Ruby's built-in retry for idempotent requests
|
|
18
|
+
# (GET/HEAD/PUT/DELETE/OPTIONS). The `net_http_persistent` adapter forces
|
|
19
|
+
# it to 0, which means a keep-alive socket the server has already dropped
|
|
20
|
+
# surfaces as a read timeout instead of being transparently retried on a
|
|
21
|
+
# fresh socket. POST is never auto-retried, so this is side-effect safe.
|
|
22
|
+
#
|
|
23
|
+
# `idle_timeout` and `pool_size` are passed through to the underlying
|
|
24
|
+
# Net::HTTP::Persistent connection when set.
|
|
25
|
+
DEFAULT_CONNECTION_OPTIONS = { max_retries: 1 }.freeze
|
|
26
|
+
|
|
14
27
|
# Initializes the configuration with optional settings.
|
|
15
28
|
#
|
|
16
29
|
# @param options [Hash, nil] An optional list of configuration options.
|
|
@@ -25,6 +38,16 @@ module FulfilApi
|
|
|
25
38
|
set_default_options
|
|
26
39
|
end
|
|
27
40
|
|
|
41
|
+
# Merges the provided connection options over the defaults so that, for
|
|
42
|
+
# example, setting only `idle_timeout` still keeps the default
|
|
43
|
+
# `max_retries`. Assigning `nil` resets to the defaults.
|
|
44
|
+
#
|
|
45
|
+
# @param options [Hash, nil] The connection options to apply.
|
|
46
|
+
# @return [void]
|
|
47
|
+
def connection_options=(options)
|
|
48
|
+
@connection_options = DEFAULT_CONNECTION_OPTIONS.merge(options || {})
|
|
49
|
+
end
|
|
50
|
+
|
|
28
51
|
private
|
|
29
52
|
|
|
30
53
|
# Sets the default options for the gem configuration.
|
|
@@ -36,6 +59,7 @@ module FulfilApi
|
|
|
36
59
|
def set_default_options
|
|
37
60
|
self.api_version = DEFAULT_API_VERSION if api_version.nil?
|
|
38
61
|
self.request_options = DEFAULT_REQUEST_OPTIONS if request_options.nil?
|
|
62
|
+
self.connection_options = nil if connection_options.nil?
|
|
39
63
|
end
|
|
40
64
|
end
|
|
41
65
|
|
|
@@ -75,15 +99,23 @@ module FulfilApi
|
|
|
75
99
|
end
|
|
76
100
|
end
|
|
77
101
|
|
|
78
|
-
# Temporarily applies the provided configuration options
|
|
79
|
-
# and then reverts
|
|
102
|
+
# Temporarily applies the provided configuration options on top of the
|
|
103
|
+
# currently active configuration, and then reverts after the block executes.
|
|
104
|
+
#
|
|
105
|
+
# The temporary options are merged over a copy of the active configuration, so
|
|
106
|
+
# a block only needs to specify what it overrides — credentials and other
|
|
107
|
+
# settings (`access_token`, `merchant_id`, ...) are inherited rather than
|
|
108
|
+
# reset to their defaults.
|
|
80
109
|
#
|
|
81
110
|
# @param temporary_options [Hash] A hash of temporary configuration options.
|
|
82
111
|
# @yield Executes the block with the temporary configuration.
|
|
83
112
|
# @return [void]
|
|
84
113
|
def self.with_config(temporary_options)
|
|
85
|
-
original_configuration = configuration
|
|
86
|
-
|
|
114
|
+
original_configuration = configuration
|
|
115
|
+
|
|
116
|
+
self.configuration = original_configuration.dup.tap do |config|
|
|
117
|
+
temporary_options.each { |key, value| config.public_send(:"#{key}=", value) }
|
|
118
|
+
end
|
|
87
119
|
|
|
88
120
|
yield
|
|
89
121
|
ensure
|
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,14 +107,26 @@ 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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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(url: api_endpoint, request: configuration.request_options) do |connection|
|
|
127
|
+
connection.adapter(:net_http_persistent, **adapter_options) do |http|
|
|
128
|
+
configure_persistent_connection(http)
|
|
129
|
+
end
|
|
88
130
|
|
|
89
131
|
# Configuration of the request middleware
|
|
90
132
|
connection.request :json
|
|
@@ -95,6 +137,42 @@ module FulfilApi
|
|
|
95
137
|
end
|
|
96
138
|
end
|
|
97
139
|
|
|
140
|
+
# Initialization options for the persistent adapter. Only `pool_size` is
|
|
141
|
+
# accepted here; `idle_timeout` and `max_retries` are applied to the live
|
|
142
|
+
# Net::HTTP::Persistent instance in {#configure_persistent_connection}.
|
|
143
|
+
#
|
|
144
|
+
# @return [Hash]
|
|
145
|
+
def adapter_options
|
|
146
|
+
options = {}
|
|
147
|
+
options[:pool_size] = configuration.connection_options[:pool_size] if configuration.connection_options[:pool_size]
|
|
148
|
+
options
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Tunes the underlying Net::HTTP::Persistent connection.
|
|
152
|
+
#
|
|
153
|
+
# The `net_http_persistent` adapter forces `max_retries` to 0 on every
|
|
154
|
+
# request, which disables Ruby's built-in retry for idempotent requests.
|
|
155
|
+
# Restoring it lets a stale keep-alive socket — one the server has already
|
|
156
|
+
# closed — be retried transparently on a fresh socket instead of surfacing
|
|
157
|
+
# as a read timeout. The config block runs after the adapter zeroes the
|
|
158
|
+
# value, so this takes effect.
|
|
159
|
+
#
|
|
160
|
+
# @param http [Net::HTTP::Persistent] The live persistent connection.
|
|
161
|
+
# @return [void]
|
|
162
|
+
def configure_persistent_connection(http)
|
|
163
|
+
if configuration.connection_options[:idle_timeout]
|
|
164
|
+
http.idle_timeout = configuration.connection_options[:idle_timeout]
|
|
165
|
+
end
|
|
166
|
+
return if configuration.connection_options[:max_retries].nil?
|
|
167
|
+
|
|
168
|
+
http.max_retries = configuration.connection_options[:max_retries]
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# @return [Array] The cache key identifying a unique connection.
|
|
172
|
+
def connection_cache_key
|
|
173
|
+
[merchant_id, api_version, configuration.request_options, configuration.connection_options]
|
|
174
|
+
end
|
|
175
|
+
|
|
98
176
|
# @param relative_path [String] The relative path to the API endpoint.
|
|
99
177
|
# @return [String] The absolute path for the request to the API endpoint.
|
|
100
178
|
def expand_relative_path(relative_path)
|
|
@@ -119,16 +197,24 @@ module FulfilApi
|
|
|
119
197
|
# @param relative_path [String] The relative path to the API endpoint.
|
|
120
198
|
# @return [Array, Hash, String] The parsed response body.
|
|
121
199
|
def request(method, relative_path, *args, **kwargs)
|
|
122
|
-
connection.send(method.to_sym, expand_relative_path(relative_path), *args, **kwargs)
|
|
200
|
+
response = connection.send(method.to_sym, expand_relative_path(relative_path), *args, **kwargs) do |req|
|
|
201
|
+
apply_authentication(req)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
response.body
|
|
123
205
|
rescue Faraday::Error => e
|
|
124
206
|
handle_request_error(e)
|
|
125
207
|
end
|
|
126
208
|
|
|
127
|
-
#
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
209
|
+
# Applies the configured bearer token to the request as an HTTP header.
|
|
210
|
+
#
|
|
211
|
+
# Authentication is set per-request rather than on the shared {#connection},
|
|
212
|
+
# so the connection cache does not need to know about credentials.
|
|
213
|
+
#
|
|
214
|
+
# @param request [Faraday::Request] The request being prepared.
|
|
215
|
+
# @return [void]
|
|
216
|
+
def apply_authentication(request)
|
|
217
|
+
request.headers["Authorization"] = "Bearer #{auth_token}"
|
|
132
218
|
end
|
|
133
219
|
end
|
|
134
220
|
|
data/lib/fulfil_api/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fulfil_api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefan Vermaas
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-06-29 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: activesupport
|
|
@@ -111,6 +112,7 @@ metadata:
|
|
|
111
112
|
source_code_uri: https://www.github.com/codeturebv/fulfil_api
|
|
112
113
|
changelog_uri: https://www.github.com/codeturebv/fulfil_api/blob/main/CHANGELOG.md
|
|
113
114
|
rubygems_mfa_required: 'true'
|
|
115
|
+
post_install_message:
|
|
114
116
|
rdoc_options: []
|
|
115
117
|
require_paths:
|
|
116
118
|
- lib
|
|
@@ -125,7 +127,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
125
127
|
- !ruby/object:Gem::Version
|
|
126
128
|
version: '0'
|
|
127
129
|
requirements: []
|
|
128
|
-
rubygems_version:
|
|
130
|
+
rubygems_version: 3.5.11
|
|
131
|
+
signing_key:
|
|
129
132
|
specification_version: 4
|
|
130
133
|
summary: A HTTP client to interact the Fulfil.io API
|
|
131
134
|
test_files: []
|