fulfil_api 0.6.3 → 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 +35 -2
- data/lib/fulfil_api/configuration.rb +36 -4
- data/lib/fulfil_api/tpl_client.rb +36 -6
- 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
|
@@ -109,7 +109,9 @@ module FulfilApi
|
|
|
109
109
|
# @return [Faraday::Connection]
|
|
110
110
|
def build_connection
|
|
111
111
|
Faraday.new(url: api_endpoint, request: configuration.request_options) do |connection|
|
|
112
|
-
connection.adapter
|
|
112
|
+
connection.adapter(:net_http_persistent, **adapter_options) do |http|
|
|
113
|
+
configure_persistent_connection(http)
|
|
114
|
+
end
|
|
113
115
|
|
|
114
116
|
# Configuration of the request middleware
|
|
115
117
|
connection.request :json
|
|
@@ -120,9 +122,40 @@ module FulfilApi
|
|
|
120
122
|
end
|
|
121
123
|
end
|
|
122
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
|
+
|
|
123
156
|
# @return [Array] The cache key identifying a unique connection.
|
|
124
157
|
def connection_cache_key
|
|
125
|
-
[configuration.merchant_id, configuration.request_options]
|
|
158
|
+
[configuration.merchant_id, configuration.request_options, configuration.connection_options]
|
|
126
159
|
end
|
|
127
160
|
|
|
128
161
|
# @param relative_path [String] The relative path to the API endpoint.
|
|
@@ -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
|
|
@@ -123,11 +123,10 @@ module FulfilApi
|
|
|
123
123
|
#
|
|
124
124
|
# @return [Faraday::Connection]
|
|
125
125
|
def build_connection
|
|
126
|
-
Faraday.new(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
connection.adapter :net_http_persistent
|
|
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
|
|
131
130
|
|
|
132
131
|
# Configuration of the request middleware
|
|
133
132
|
connection.request :json
|
|
@@ -138,9 +137,40 @@ module FulfilApi
|
|
|
138
137
|
end
|
|
139
138
|
end
|
|
140
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
|
+
|
|
141
171
|
# @return [Array] The cache key identifying a unique connection.
|
|
142
172
|
def connection_cache_key
|
|
143
|
-
[merchant_id, api_version, configuration.request_options]
|
|
173
|
+
[merchant_id, api_version, configuration.request_options, configuration.connection_options]
|
|
144
174
|
end
|
|
145
175
|
|
|
146
176
|
# @param relative_path [String] The relative path to the API endpoint.
|
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: []
|