my_api_client 2.1.0 → 2.2.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/.github/workflows/ci.yml +6 -6
- data/.github/workflows/release.yml +9 -3
- data/.github/workflows/rubocop_challenge.yml +1 -1
- data/AGENTS.md +2 -1
- data/CHANGELOG.md +32 -0
- data/Gemfile.lock +18 -18
- data/README.md +18 -5
- data/lib/my_api_client/request/executor.rb +3 -3
- data/lib/my_api_client/rspec/stub.rb +37 -25
- data/lib/my_api_client/version.rb +1 -1
- data/my_api/Gemfile +1 -1
- data/my_api/Gemfile.lock +93 -93
- data/my_api_client.gemspec +2 -2
- data/rails_app/rails_7.2/Gemfile.lock +65 -65
- data/rails_app/rails_8.0/Gemfile.lock +14 -14
- data/rails_app/rails_8.1/Gemfile +1 -1
- data/rails_app/rails_8.1/Gemfile.lock +73 -73
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 53a8927025699ceda8733b59f898f51e947c0ea5ea41c7146548d35e6d76242b
|
|
4
|
+
data.tar.gz: 2f4c3aeb97c42d615bdbf8edcd916ec3a9cc90dbd353b31a2b54825289923470
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f9c5ed6788547461bd09343dedb00d365636aae3e514e756dffdb6702af3f3572a34f5b3bed9e1b3f26f0f400fbbbf782ea33906efc06e712afc73141c8fe668
|
|
7
|
+
data.tar.gz: 5f6a73e3f65a6b5b11e76513d1f7c882676be79324f30d48b9b2d563e262e1e64b13bf9afaa69e373f067a6881f108c032c6da0cb23e861c08f59154daff38a4
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -33,7 +33,7 @@ jobs:
|
|
|
33
33
|
persist-credentials: false
|
|
34
34
|
|
|
35
35
|
- name: Set up Ruby ${{ matrix.ruby_version }}
|
|
36
|
-
uses: ruby/setup-ruby@
|
|
36
|
+
uses: ruby/setup-ruby@26e5b7d197ebf60545ee9667bcbace0be11ccaec # v1.334.0
|
|
37
37
|
with:
|
|
38
38
|
ruby-version: ${{ matrix.ruby_version }}
|
|
39
39
|
bundler-cache: false
|
|
@@ -105,7 +105,7 @@ jobs:
|
|
|
105
105
|
persist-credentials: false
|
|
106
106
|
|
|
107
107
|
- name: Set up Ruby ${{ matrix.ruby_version }}
|
|
108
|
-
uses: ruby/setup-ruby@
|
|
108
|
+
uses: ruby/setup-ruby@26e5b7d197ebf60545ee9667bcbace0be11ccaec # v1.334.0
|
|
109
109
|
with:
|
|
110
110
|
ruby-version: ${{ matrix.ruby_version }}
|
|
111
111
|
bundler-cache: false
|
|
@@ -233,7 +233,7 @@ jobs:
|
|
|
233
233
|
persist-credentials: false
|
|
234
234
|
|
|
235
235
|
- name: Set up Ruby
|
|
236
|
-
uses: ruby/setup-ruby@
|
|
236
|
+
uses: ruby/setup-ruby@26e5b7d197ebf60545ee9667bcbace0be11ccaec # v1.334.0
|
|
237
237
|
with:
|
|
238
238
|
ruby-version: '4.0'
|
|
239
239
|
bundler-cache: false
|
|
@@ -255,7 +255,7 @@ jobs:
|
|
|
255
255
|
persist-credentials: false
|
|
256
256
|
|
|
257
257
|
- name: Set up Ruby
|
|
258
|
-
uses: ruby/setup-ruby@
|
|
258
|
+
uses: ruby/setup-ruby@26e5b7d197ebf60545ee9667bcbace0be11ccaec # v1.334.0
|
|
259
259
|
with:
|
|
260
260
|
ruby-version: '4.0'
|
|
261
261
|
bundler-cache: false
|
|
@@ -296,7 +296,7 @@ jobs:
|
|
|
296
296
|
persist-credentials: false
|
|
297
297
|
|
|
298
298
|
- name: Set up Ruby
|
|
299
|
-
uses: ruby/setup-ruby@
|
|
299
|
+
uses: ruby/setup-ruby@26e5b7d197ebf60545ee9667bcbace0be11ccaec # v1.334.0
|
|
300
300
|
with:
|
|
301
301
|
ruby-version: '4.0'
|
|
302
302
|
bundler-cache: false
|
|
@@ -337,7 +337,7 @@ jobs:
|
|
|
337
337
|
|
|
338
338
|
- name: Upload coverage to Qlty
|
|
339
339
|
if: ${{ env.QLTY_COVERAGE_TOKEN != '' && hashFiles('coverage/coverage.json') != '' }}
|
|
340
|
-
uses: qltysh/qlty-action/coverage@
|
|
340
|
+
uses: qltysh/qlty-action/coverage@ebcd284bf32e36a030afe15f1e136c7be08b9506 # v2.5.0
|
|
341
341
|
with:
|
|
342
342
|
token: ${{ env.QLTY_COVERAGE_TOKEN }}
|
|
343
343
|
files: coverage/coverage.json
|
|
@@ -26,10 +26,16 @@ jobs:
|
|
|
26
26
|
ref: ${{ github.ref_name }}
|
|
27
27
|
|
|
28
28
|
- name: Set up Ruby
|
|
29
|
-
|
|
29
|
+
# bundler-cache is disabled to avoid a cache-poisoning attack surface:
|
|
30
|
+
# this workflow publishes a gem, so a poisoned dependency cache could
|
|
31
|
+
# contaminate the released artifact.
|
|
32
|
+
uses: ruby/setup-ruby@26e5b7d197ebf60545ee9667bcbace0be11ccaec # v1.334.0
|
|
30
33
|
with:
|
|
31
34
|
ruby-version: '4.0'
|
|
32
|
-
bundler-cache:
|
|
35
|
+
bundler-cache: false
|
|
36
|
+
|
|
37
|
+
- name: Install dependencies
|
|
38
|
+
run: bundle install
|
|
33
39
|
|
|
34
40
|
- name: Release gem via Trusted Publishing
|
|
35
|
-
uses: rubygems/release-gem@v1
|
|
41
|
+
uses: rubygems/release-gem@7f9650160c1a4e7989fdc9855807bdbd421d8b6b # v1.4.1
|
|
@@ -23,7 +23,7 @@ jobs:
|
|
|
23
23
|
- uses: actions/checkout@v7 # zizmor: ignore[artipacked]
|
|
24
24
|
|
|
25
25
|
- name: Set up Ruby
|
|
26
|
-
uses: ruby/setup-ruby@
|
|
26
|
+
uses: ruby/setup-ruby@26e5b7d197ebf60545ee9667bcbace0be11ccaec # v1.334.0
|
|
27
27
|
with:
|
|
28
28
|
ruby-version: '4.0'
|
|
29
29
|
|
data/AGENTS.md
CHANGED
|
@@ -126,7 +126,8 @@
|
|
|
126
126
|
## GitHub Actions Pinning Policy
|
|
127
127
|
|
|
128
128
|
- Pin third-party actions to a full commit SHA with a version comment (e.g. `uses: ruby/setup-ruby@<sha> # v1.321.0`) to satisfy zizmor `unpinned-uses`.
|
|
129
|
-
- GitHub-owned actions (`actions/*`, `github/*`, `
|
|
129
|
+
- GitHub-owned actions (`actions/*`, `github/*`, `dependabot/*`) may stay ref-pinned; Dependabot (`github-actions`) keeps the pins and version comments updated.
|
|
130
|
+
- Third-party actions outside those GitHub-owned orgs (e.g. `ruby/*`, `rubygems/*`) must be SHA-pinned; zizmor `unpinned-uses` flags them otherwise. Dependabot still keeps the SHA pins and version comments updated.
|
|
130
131
|
|
|
131
132
|
## Runbook
|
|
132
133
|
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Change log
|
|
2
2
|
|
|
3
|
+
## v2.2.0 (Oct 25, 2026)
|
|
4
|
+
|
|
5
|
+
### Feature
|
|
6
|
+
|
|
7
|
+
- [#1405](https://github.com/ryz310/my_api_client/pull/1405) Allow stubbing response headers with the `headers` option when `raise` is an error class ([@okumud](https://github.com/okumud))
|
|
8
|
+
- [#1406](https://github.com/ryz310/my_api_client/pull/1406) Include request duration in success or failure log lines instead of a separate `Duration` line. Update log parsers or alerts that depend on the previous format ([@okumud](https://github.com/okumud))
|
|
9
|
+
|
|
10
|
+
### Bugfix
|
|
11
|
+
|
|
12
|
+
- [#1405](https://github.com/ryz310/my_api_client/pull/1405) Reject `status_code` and `headers` options alongside an error instance instead of silently ignoring them. Pass an error class when combining these options; specs matching `MyApiClient::Stub::ERROR_MESSAGE` must also account for its updated text ([@okumud](https://github.com/okumud))
|
|
13
|
+
|
|
14
|
+
### Security
|
|
15
|
+
|
|
16
|
+
- [#1344](https://github.com/ryz310/my_api_client/pull/1344) Disable Bundler caching in the release workflow to prevent cache poisoning ([@ryz310](https://github.com/ryz310))
|
|
17
|
+
- [#1345](https://github.com/ryz310/my_api_client/pull/1345) Pin rubygems/release-gem to a commit SHA ([@ryz310](https://github.com/ryz310))
|
|
18
|
+
|
|
19
|
+
### Dependabot
|
|
20
|
+
|
|
21
|
+
- [#1392](https://github.com/ryz310/my_api_client/pull/1392) Bump activesupport from 8.1.3.1 to 8.1.4 ([@ryz310](https://github.com/ryz310))
|
|
22
|
+
- [#1371](https://github.com/ryz310/my_api_client/pull/1371) Bump faraday from 2.14.3 to 2.14.4 ([@ryz310](https://github.com/ryz310))
|
|
23
|
+
- [#1396](https://github.com/ryz310/my_api_client/pull/1396) Bump simplecov from 1.0.3 to 1.3.2 ([@ryz310](https://github.com/ryz310))
|
|
24
|
+
- [#1355](https://github.com/ryz310/my_api_client/pull/1355) Bump rubocop-performance from 1.26.1 to 1.27.0 ([@ryz310](https://github.com/ryz310))
|
|
25
|
+
- [#1395](https://github.com/ryz310/my_api_client/pull/1395) Bump rubocop-rspec_rails from 2.32.0 to 2.33.0 ([@ryz310](https://github.com/ryz310))
|
|
26
|
+
- [#1399](https://github.com/ryz310/my_api_client/pull/1399) Bump webmock from 3.26.2 to 3.31.0 ([@ryz310](https://github.com/ryz310))
|
|
27
|
+
- [#1400](https://github.com/ryz310/my_api_client/pull/1400) Bump ruby/setup-ruby from 1.321.0 to 1.334.0 ([@ryz310](https://github.com/ryz310))
|
|
28
|
+
- [#1350](https://github.com/ryz310/my_api_client/pull/1350) Bump rubygems/release-gem from 1.4.0 to 1.4.1 ([@ryz310](https://github.com/ryz310))
|
|
29
|
+
- [#1383](https://github.com/ryz310/my_api_client/pull/1383) Bump qltysh/qlty-action/coverage from 2.3.0 to 2.5.0 ([@ryz310](https://github.com/ryz310))
|
|
30
|
+
|
|
31
|
+
### Misc
|
|
32
|
+
|
|
33
|
+
- [#1375](https://github.com/ryz310/my_api_client/pull/1375) Clarify API client integration behavior ([@ryz310](https://github.com/ryz310))
|
|
34
|
+
|
|
3
35
|
## v2.1.0 (Aug 11, 2026)
|
|
4
36
|
|
|
5
37
|
### Security
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
my_api_client (2.
|
|
4
|
+
my_api_client (2.2.0)
|
|
5
5
|
activesupport (>= 7.2.0)
|
|
6
6
|
faraday (>= 0.17.1)
|
|
7
7
|
jsonpath
|
|
@@ -10,7 +10,7 @@ PATH
|
|
|
10
10
|
GEM
|
|
11
11
|
remote: https://rubygems.org/
|
|
12
12
|
specs:
|
|
13
|
-
activesupport (8.1.
|
|
13
|
+
activesupport (8.1.4)
|
|
14
14
|
base64
|
|
15
15
|
bigdecimal
|
|
16
16
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
@@ -27,7 +27,7 @@ GEM
|
|
|
27
27
|
public_suffix (>= 2.0.2, < 8.0)
|
|
28
28
|
ast (2.4.3)
|
|
29
29
|
base64 (0.3.0)
|
|
30
|
-
bigdecimal (4.1.
|
|
30
|
+
bigdecimal (4.1.3)
|
|
31
31
|
bugsnag (6.30.0)
|
|
32
32
|
concurrent-ruby (~> 1.0)
|
|
33
33
|
byebug (13.0.0)
|
|
@@ -40,7 +40,7 @@ GEM
|
|
|
40
40
|
rexml
|
|
41
41
|
diff-lcs (1.6.2)
|
|
42
42
|
drb (2.2.3)
|
|
43
|
-
faraday (2.14.
|
|
43
|
+
faraday (2.14.4)
|
|
44
44
|
faraday-net_http (>= 2.0, < 3.5)
|
|
45
45
|
json
|
|
46
46
|
logger
|
|
@@ -63,7 +63,7 @@ GEM
|
|
|
63
63
|
multi_json (1.21.1)
|
|
64
64
|
net-http (0.9.1)
|
|
65
65
|
uri (>= 0.11.1)
|
|
66
|
-
parallel (
|
|
66
|
+
parallel (2.3.0)
|
|
67
67
|
parser (3.3.12.0)
|
|
68
68
|
ast (~> 2.4.1)
|
|
69
69
|
racc
|
|
@@ -79,7 +79,7 @@ GEM
|
|
|
79
79
|
racc (1.8.1)
|
|
80
80
|
rainbow (3.1.1)
|
|
81
81
|
rake (13.4.2)
|
|
82
|
-
regexp_parser (2.
|
|
82
|
+
regexp_parser (2.13.1)
|
|
83
83
|
reline (0.6.3)
|
|
84
84
|
io-console (~> 0.5)
|
|
85
85
|
rexml (3.4.4)
|
|
@@ -98,7 +98,7 @@ GEM
|
|
|
98
98
|
rspec-support (3.13.7)
|
|
99
99
|
rspec_junit_formatter (0.6.0)
|
|
100
100
|
rspec-core (>= 2, < 4, != 2.12.0)
|
|
101
|
-
rubocop (1.
|
|
101
|
+
rubocop (1.89.0)
|
|
102
102
|
json (~> 2.3)
|
|
103
103
|
language_server-protocol (~> 3.17.0.2)
|
|
104
104
|
lint_roller (~> 1.1.0)
|
|
@@ -112,9 +112,9 @@ GEM
|
|
|
112
112
|
rubocop-ast (1.50.0)
|
|
113
113
|
parser (>= 3.3.7.2)
|
|
114
114
|
prism (~> 1.7)
|
|
115
|
-
rubocop-performance (1.
|
|
115
|
+
rubocop-performance (1.27.0)
|
|
116
116
|
lint_roller (~> 1.1)
|
|
117
|
-
rubocop (>= 1.
|
|
117
|
+
rubocop (>= 1.89.0, < 2.0)
|
|
118
118
|
rubocop-ast (>= 1.47.1, < 2.0)
|
|
119
119
|
rubocop-rake (0.7.1)
|
|
120
120
|
lint_roller (~> 1.1)
|
|
@@ -123,23 +123,23 @@ GEM
|
|
|
123
123
|
lint_roller (~> 1.1)
|
|
124
124
|
regexp_parser (>= 2.0)
|
|
125
125
|
rubocop (~> 1.86, >= 1.86.2)
|
|
126
|
-
rubocop-rspec_rails (2.
|
|
126
|
+
rubocop-rspec_rails (2.33.0)
|
|
127
127
|
lint_roller (~> 1.1)
|
|
128
|
-
rubocop (~> 1.
|
|
128
|
+
rubocop (~> 1.89)
|
|
129
129
|
rubocop-rspec (~> 3.5)
|
|
130
130
|
ruby-progressbar (1.13.0)
|
|
131
131
|
sawyer (0.9.3)
|
|
132
132
|
addressable (>= 2.3.5)
|
|
133
133
|
faraday (>= 0.17.3, < 3)
|
|
134
134
|
securerandom (0.4.1)
|
|
135
|
-
simplecov (1.
|
|
135
|
+
simplecov (1.3.2)
|
|
136
136
|
tzinfo (2.0.6)
|
|
137
137
|
concurrent-ruby (~> 1.0)
|
|
138
|
-
unicode-display_width (3.
|
|
139
|
-
unicode-emoji (~> 4.
|
|
140
|
-
unicode-emoji (4.
|
|
141
|
-
uri (1.1.
|
|
142
|
-
webmock (3.
|
|
138
|
+
unicode-display_width (3.3.0)
|
|
139
|
+
unicode-emoji (~> 4.3)
|
|
140
|
+
unicode-emoji (4.3.0)
|
|
141
|
+
uri (1.1.2)
|
|
142
|
+
webmock (3.31.0)
|
|
143
143
|
addressable (>= 2.8.0)
|
|
144
144
|
crack (>= 0.3.2)
|
|
145
145
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
@@ -161,7 +161,7 @@ DEPENDENCIES
|
|
|
161
161
|
rubocop-rake
|
|
162
162
|
rubocop-rspec
|
|
163
163
|
rubocop-rspec_rails
|
|
164
|
-
simplecov (= 1.
|
|
164
|
+
simplecov (= 1.3.2)
|
|
165
165
|
webmock
|
|
166
166
|
yard
|
|
167
167
|
|
data/README.md
CHANGED
|
@@ -87,8 +87,12 @@ api_client.get_users #=> #<Sawyer::Resource>
|
|
|
87
87
|
|
|
88
88
|
Next, define `#initialize` to set values such as an access token or API key. You can omit it if you do not need any instance state.
|
|
89
89
|
|
|
90
|
+
`MyApiClient::Base` intentionally does not define `#initialize`, so a class that directly inherits from it does not need to call `super()` from its own `#initialize`. If you inherit from another application-specific client class, follow that parent class's initialization contract.
|
|
91
|
+
|
|
90
92
|
Then define methods such as `#get_users` and `#post_user`. Inside those methods, call HTTP helpers like `#get` and `#post`. You can also use `#patch`, `#put`, and `#delete`.
|
|
91
93
|
|
|
94
|
+
Without a block, these HTTP helpers return the response body as a `Sawyer::Resource`. With a block, they yield the full `Sawyer::Response` and return the block's result. See [RSpec](#rspec) for the request and error-handling test helpers provided by this gem.
|
|
95
|
+
|
|
92
96
|
### Pagination
|
|
93
97
|
|
|
94
98
|
Some APIs include a URL for the next page in the response.
|
|
@@ -260,6 +264,8 @@ Also, `retry_on` is defined by default for `MyApiClient::NetworkError`.
|
|
|
260
264
|
|
|
261
265
|
Both can be overridden, so define `error_handling` as needed.
|
|
262
266
|
|
|
267
|
+
If more than one error handler matches a response, the handler defined last takes precedence. Handlers defined in a subclass are evaluated before inherited handlers, so a subclass can override a built-in handler with a more specific rule.
|
|
268
|
+
|
|
263
269
|
They are defined [here](https://github.com/ryz310/my_api_client/blob/master/lib/my_api_client/default_error_handlers.rb).
|
|
264
270
|
|
|
265
271
|
#### Use Symbol
|
|
@@ -343,6 +349,8 @@ end
|
|
|
343
349
|
|
|
344
350
|
When an API request is executed many times, network errors can occur. Sometimes the network is unavailable for a long time, but often the error is temporary. In MyApiClient, network-related exceptions are wrapped as `MyApiClient::NetworkError`. Using `retry_on`, you can handle such exceptions and retry requests with configurable wait time and attempt count, similar to `ActiveJob`.
|
|
345
351
|
|
|
352
|
+
`attempts` is the number of retries after the initial request, not the total number of requests. For example, `attempts: 0` sends at most one request, while `attempts: 3` sends at most four requests.
|
|
353
|
+
|
|
346
354
|
`retry_on MyApiClient::NetworkError` is enabled by default, so you do not need to define it unless you want custom `wait` or `attempts` values.
|
|
347
355
|
|
|
348
356
|
Unlike `ActiveJob`, retries are performed synchronously. In practice, this is most useful for short-lived network interruptions. You can also retry for API rate limits as in the example above, but handling that with `ActiveJob` may be a better fit depending on your workload.
|
|
@@ -426,8 +434,7 @@ MyApiClient wraps this logger with `MyApiClient::Request::Logger` and prefixes m
|
|
|
426
434
|
|
|
427
435
|
```text
|
|
428
436
|
API request `GET https://example.com/v1/users`: "Start"
|
|
429
|
-
API request `GET https://example.com/v1/users`: "
|
|
430
|
-
API request `GET https://example.com/v1/users`: "Success (200)"
|
|
437
|
+
API request `GET https://example.com/v1/users`: "Success (200) in 100.0 msec"
|
|
431
438
|
```
|
|
432
439
|
|
|
433
440
|
On failure, it logs:
|
|
@@ -662,7 +669,7 @@ stub_api_client_all(ExampleApiClient, request: { raise: MyApiClient::Error })
|
|
|
662
669
|
expect { ExampleApiClient.new.request(user_id: 1) }.to raise_error(MyApiClient::Error)
|
|
663
670
|
```
|
|
664
671
|
|
|
665
|
-
You can combine `raise`, `response`, and `
|
|
672
|
+
You can combine `raise`, `response`, `status_code`, and `headers`:
|
|
666
673
|
|
|
667
674
|
```ruby
|
|
668
675
|
stub_api_client_all(
|
|
@@ -671,17 +678,23 @@ stub_api_client_all(
|
|
|
671
678
|
raise: MyApiClient::Error,
|
|
672
679
|
response: { message: 'error' },
|
|
673
680
|
status_code: 429,
|
|
681
|
+
headers: { 'Retry-After' => '60' },
|
|
674
682
|
}
|
|
675
683
|
)
|
|
676
684
|
|
|
677
685
|
begin
|
|
678
686
|
ExampleApiClient.new.request(user_id: 1)
|
|
679
687
|
rescue MyApiClient::Error => e
|
|
680
|
-
e.params.response.data.to_h
|
|
681
|
-
e.params.response.status
|
|
688
|
+
e.params.response.data.to_h # => { message: "error" }
|
|
689
|
+
e.params.response.status # => 429
|
|
690
|
+
e.params.response.headers['retry-after'] # => "60"
|
|
682
691
|
end
|
|
683
692
|
```
|
|
684
693
|
|
|
694
|
+
Stubbed headers behave like real response headers: names are looked up case-insensitively and values are converted to Strings. Use String keys for header names, because Symbol keys are rewritten (e.g. `:retry_after` becomes `'Retry-After'`).
|
|
695
|
+
|
|
696
|
+
If the `raise` option is an error instance, `response`, `status_code`, and `headers` cannot be combined with it. Pass an error class instead.
|
|
697
|
+
|
|
685
698
|
#### `pageable` option
|
|
686
699
|
|
|
687
700
|
For `#pageable_get` (`#pget`), you can stub page-by-page responses:
|
|
@@ -32,13 +32,13 @@ module MyApiClient
|
|
|
32
32
|
def call
|
|
33
33
|
request_logger.info('Start')
|
|
34
34
|
response = api_request
|
|
35
|
-
|
|
35
|
+
duration = " in #{response.timing.in_milliseconds} msec"
|
|
36
36
|
verify(response)
|
|
37
37
|
rescue MyApiClient::Error => e
|
|
38
|
-
request_logger.warn("Failure (#{e.message})")
|
|
38
|
+
request_logger.warn("Failure (#{e.message})#{duration}")
|
|
39
39
|
raise
|
|
40
40
|
else
|
|
41
|
-
request_logger.info("Success (#{response.status})")
|
|
41
|
+
request_logger.info("Success (#{response.status})#{duration}")
|
|
42
42
|
response
|
|
43
43
|
end
|
|
44
44
|
|
|
@@ -4,9 +4,9 @@ module MyApiClient
|
|
|
4
4
|
# Test helper module for RSpec
|
|
5
5
|
module Stub
|
|
6
6
|
ERROR_MESSAGE =
|
|
7
|
-
'If you use the `raise` option as an error instance, the `response
|
|
8
|
-
'
|
|
9
|
-
'`raise` option as an error class.'
|
|
7
|
+
'If you use the `raise` option as an error instance, the `response`, ' \
|
|
8
|
+
'`status_code` and `headers` options are ignored. If you want to use them, ' \
|
|
9
|
+
'you need to specify the `raise` option as an error class.'
|
|
10
10
|
|
|
11
11
|
# Stubs all instance of arbitrary MyApiClient class.
|
|
12
12
|
# And returns a stubbed arbitrary MyApiClient instance.
|
|
@@ -22,13 +22,14 @@ module MyApiClient
|
|
|
22
22
|
# pageable: [ { id: 1 }, { id: 2 }] # for `#pageable_get`.
|
|
23
23
|
# },
|
|
24
24
|
# get_user: { response: { id: 1 } }, # Returns an arbitrary response.
|
|
25
|
-
# post_users: { id: 1 }, # You can
|
|
25
|
+
# post_users: { id: 1 }, # You can omit `response` keyword.
|
|
26
26
|
# patch_user: ->(params) { { id: params[:id] } }, # Returns calculated result as response.
|
|
27
27
|
# put_user: { raise: MyApiClient::ClientError } # Raises an arbitrary error.
|
|
28
28
|
# delete_user: {
|
|
29
29
|
# raise: MyApiClient::ClientError,
|
|
30
|
-
# response: { errors: [{ code: 10 }] }, # You can stub response and
|
|
30
|
+
# response: { errors: [{ code: 10 }] }, # You can stub response and status code
|
|
31
31
|
# status_code: 429, # with an arbitrary error.
|
|
32
|
+
# headers: { 'X-Request-Id' => '123' }, # You can also stub response headers.
|
|
32
33
|
# }
|
|
33
34
|
# )
|
|
34
35
|
# response = ExampleApiClient.new.get_user(id: 123)
|
|
@@ -54,13 +55,14 @@ module MyApiClient
|
|
|
54
55
|
# pageable: [ { id: 1 }, { id: 2 }] # for `#pageable_get`.
|
|
55
56
|
# },
|
|
56
57
|
# get_user: { response: { id: 1 } }, # Returns an arbitrary response.
|
|
57
|
-
# post_users: { id: 1 }, # You can
|
|
58
|
+
# post_users: { id: 1 }, # You can omit `response` keyword.
|
|
58
59
|
# patch_user: ->(params) { { id: params[:id] } }, # Returns calculated result as response.
|
|
59
60
|
# put_user: { raise: MyApiClient::ClientError } # Raises an arbitrary error.
|
|
60
61
|
# delete_user: {
|
|
61
62
|
# raise: MyApiClient::ClientError,
|
|
62
63
|
# response: { errors: [{ code: 10 }] }, # You can stub response and status code
|
|
63
64
|
# status_code: 403, # with exception.
|
|
65
|
+
# headers: { 'X-Request-Id' => '123' }, # You can also stub response headers.
|
|
64
66
|
# }
|
|
65
67
|
# )
|
|
66
68
|
# response = api_client.get_user(id: 123)
|
|
@@ -82,13 +84,13 @@ module MyApiClient
|
|
|
82
84
|
end
|
|
83
85
|
end
|
|
84
86
|
|
|
85
|
-
def generate_stubbed_response(options, *request) # rubocop:disable Metrics/
|
|
87
|
+
def generate_stubbed_response(options, *request) # rubocop:disable Metrics/MethodLength
|
|
86
88
|
case options
|
|
87
89
|
when Proc
|
|
88
90
|
stub_as_resource(options.call(*request))
|
|
89
91
|
when Hash
|
|
90
92
|
if options[:raise]
|
|
91
|
-
raise process_raise_option(options
|
|
93
|
+
raise process_raise_option(options)
|
|
92
94
|
elsif options[:response]
|
|
93
95
|
stub_as_resource(options[:response])
|
|
94
96
|
elsif options[:pageable].is_a?(Enumerable)
|
|
@@ -113,37 +115,47 @@ module MyApiClient
|
|
|
113
115
|
|
|
114
116
|
# Provides a shorthand for `raise` option.
|
|
115
117
|
# `MyApiClient::Error` requires `MyApiClient::Params::Params` instance on
|
|
116
|
-
# initialize, but it makes
|
|
118
|
+
# initialize, but it makes troublesome. `MyApiClient::NetworkError` is more.
|
|
117
119
|
# If given a error instance, it will return raw value without processing.
|
|
118
120
|
#
|
|
119
|
-
# @param
|
|
120
|
-
# @
|
|
121
|
-
# @
|
|
121
|
+
# @param options [Hash] Stubbing options.
|
|
122
|
+
# @option options [Class, MyApiClient::Error] :raise Processing target.
|
|
123
|
+
# @option options [Hash] :response Stubbed response body.
|
|
124
|
+
# @option options [Integer] :status_code Stubbed status code (defaults to 400).
|
|
125
|
+
# @option options [Hash] :headers
|
|
126
|
+
# Stubbed response headers. Keys are looked up case-insensitively like real
|
|
127
|
+
# response headers and values are converted to Strings, so prefer String keys
|
|
128
|
+
# (e.g. `'X-Request-Id'`).
|
|
122
129
|
# @return [MyApiClient::Error] Processed exception.
|
|
123
130
|
# @raise [RuntimeError] Unsupported error class was set.
|
|
124
|
-
def process_raise_option(
|
|
125
|
-
case
|
|
131
|
+
def process_raise_option(options)
|
|
132
|
+
case options[:raise]
|
|
126
133
|
when Class
|
|
127
|
-
|
|
128
|
-
if exception == MyApiClient::NetworkError
|
|
129
|
-
exception.new(params, Net::OpenTimeout.new)
|
|
130
|
-
else
|
|
131
|
-
exception.new(params)
|
|
132
|
-
end
|
|
134
|
+
build_stubbed_error(*options.values_at(:raise, :response, :status_code, :headers))
|
|
133
135
|
when MyApiClient::Error
|
|
134
|
-
raise ERROR_MESSAGE if response.present?
|
|
136
|
+
raise ERROR_MESSAGE if options.values_at(:response, :status_code, :headers).any?(&:present?)
|
|
137
|
+
|
|
138
|
+
options[:raise]
|
|
139
|
+
else
|
|
140
|
+
raise "Unsupported error class was set: #{options[:raise].inspect}"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
135
143
|
|
|
136
|
-
|
|
144
|
+
def build_stubbed_error(exception, response, status_code, headers)
|
|
145
|
+
stubbed_response = stub_as_response(response, status_code, headers)
|
|
146
|
+
params = MyApiClient::Params::Params.new(nil, stubbed_response)
|
|
147
|
+
if exception == MyApiClient::NetworkError
|
|
148
|
+
exception.new(params, Net::OpenTimeout.new)
|
|
137
149
|
else
|
|
138
|
-
|
|
150
|
+
exception.new(params)
|
|
139
151
|
end
|
|
140
152
|
end
|
|
141
153
|
|
|
142
|
-
def stub_as_response(params, status_code)
|
|
154
|
+
def stub_as_response(params, status_code, headers = nil)
|
|
143
155
|
instance_double(
|
|
144
156
|
Sawyer::Response,
|
|
145
157
|
status: status_code.presence || 400,
|
|
146
|
-
headers:
|
|
158
|
+
headers: Faraday::Utils::Headers.new(headers),
|
|
147
159
|
data: stub_as_resource(params),
|
|
148
160
|
body: params.to_json,
|
|
149
161
|
timing: 0.123
|