better-faraday 3.1.1 → 3.1.2
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/Gemfile.lock +2 -2
- data/better-faraday.gemspec +1 -1
- data/lib/better_faraday.rb +69 -20
- data/spec/json_middleware_spec.rb +46 -0
- data/spec/json_parsing_spec.rb +55 -0
- data/spec/reason_phrase_spec.rb +56 -0
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6bf92306e4c791a1171310958827a2236faef36480dbed3c111b544035797a68
|
|
4
|
+
data.tar.gz: 36208257d64565f775bc395beea0b1c72905ad60f9f4c04ea6107375f4ebf2a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6c47f524f903183214f43302ab03126b69d09b44660daf4034b5285ef8072d98bfc59fb5a6d614c9863faad704e9e4f9f8afab9e26938ec38295f264df1d974b
|
|
7
|
+
data.tar.gz: 52e00284afec4daa2797d836f61819b9aee8821cc51daa18f40e19438401001b82e0076e7086c4607a2f9e645a77dbd9f2a9c061709d0f2319a25b787e1025d9
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
better-faraday (3.1.
|
|
4
|
+
better-faraday (3.1.2)
|
|
5
5
|
faraday (>= 1.0, < 3.0)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -86,7 +86,7 @@ DEPENDENCIES
|
|
|
86
86
|
|
|
87
87
|
CHECKSUMS
|
|
88
88
|
addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
|
|
89
|
-
better-faraday (3.1.
|
|
89
|
+
better-faraday (3.1.2)
|
|
90
90
|
bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
|
|
91
91
|
bundler (4.0.22) sha256=d8d5ec84c8555e0af71db63ed7aee4d1a8fb839ec46d84212d61979242a5d75a
|
|
92
92
|
byebug (13.0.0) sha256=d2263efe751941ca520fa29744b71972d39cbc41839496706f5d9b22e92ae05d
|
data/better-faraday.gemspec
CHANGED
data/lib/better_faraday.rb
CHANGED
|
@@ -188,6 +188,18 @@ module Faraday
|
|
|
188
188
|
|
|
189
189
|
private
|
|
190
190
|
|
|
191
|
+
# Fallback for missing reason phrases in the response
|
|
192
|
+
def bf_reason_phrase
|
|
193
|
+
phrase = reason_phrase.to_s.strip
|
|
194
|
+
return phrase.upcase unless phrase.empty? || phrase.casecmp("unknown").zero?
|
|
195
|
+
|
|
196
|
+
klass = ::Net::HTTPResponse::CODE_TO_OBJ[status.to_s]
|
|
197
|
+
return "UNKNOWN" unless klass
|
|
198
|
+
|
|
199
|
+
# Converts "Net::HTTPUnprocessableEntity" -> "Unprocessable Entity" -> "UNPROCESSABLE ENTITY"
|
|
200
|
+
klass.name.split("::").last.sub(/\AHTTP/, "").gsub(/([a-z])([A-Z])/, '\1 \2').upcase
|
|
201
|
+
end
|
|
202
|
+
|
|
191
203
|
def bf_truncate(str, limit = 2048, omission = "... (truncated)")
|
|
192
204
|
return str if str.length <= limit
|
|
193
205
|
|
|
@@ -238,14 +250,32 @@ module Faraday
|
|
|
238
250
|
end
|
|
239
251
|
request_headers_hash = {} if request_headers_hash.nil?
|
|
240
252
|
|
|
241
|
-
|
|
242
|
-
|
|
253
|
+
# Safely handle bodies that were already parsed by Faraday middlewares (like f.request :json)
|
|
254
|
+
request_body = env.bf_request_body
|
|
255
|
+
request_body_json = nil
|
|
243
256
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
257
|
+
if request_body.is_a?(Hash) || request_body.is_a?(Array)
|
|
258
|
+
request_body_json = bf_json_dump(bf_protect_data(request_body))
|
|
259
|
+
request_body_string = request_body.to_json
|
|
260
|
+
request_byte_count = request_body_string.bytesize
|
|
261
|
+
else
|
|
262
|
+
request_body_is_json = request_headers_hash.any? do |k, v|
|
|
263
|
+
k.to_s.casecmp("content-type").zero? && v.to_s.match?(%r{\b(?:application|text)/json\b}i)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
request_body_string = if request_body.is_a?(String)
|
|
267
|
+
request_body
|
|
268
|
+
else
|
|
269
|
+
request_body.respond_to?(:to_json) ? request_body.to_json : request_body.to_s
|
|
270
|
+
end
|
|
247
271
|
|
|
248
|
-
|
|
272
|
+
request_byte_count = request_body_string.bytesize
|
|
273
|
+
|
|
274
|
+
if request_body_is_json
|
|
275
|
+
parsed = bf_json_parse(request_body_string)
|
|
276
|
+
request_body_json = bf_json_dump(bf_protect_data(parsed)) unless parsed.nil?
|
|
277
|
+
end
|
|
278
|
+
end
|
|
249
279
|
|
|
250
280
|
request_byte_label = request_byte_count == 1 ? "byte" : "bytes"
|
|
251
281
|
|
|
@@ -255,19 +285,37 @@ module Faraday
|
|
|
255
285
|
end
|
|
256
286
|
response_headers_hash = {} if response_headers_hash.nil?
|
|
257
287
|
|
|
258
|
-
|
|
259
|
-
|
|
288
|
+
# Safely handle bodies that were already parsed by Faraday middlewares (like f.response :json)
|
|
289
|
+
response_body = env.body
|
|
290
|
+
response_body_json = nil
|
|
260
291
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
292
|
+
if response_body.is_a?(Hash) || response_body.is_a?(Array)
|
|
293
|
+
response_body_json = bf_json_dump(bf_protect_data(response_body))
|
|
294
|
+
response_body_string = response_body.to_json
|
|
295
|
+
response_byte_count = response_body_string.bytesize
|
|
296
|
+
else
|
|
297
|
+
response_body_is_json = response_headers_hash.any? do |k, v|
|
|
298
|
+
k.to_s.casecmp("content-type").zero? && v.to_s.match?(%r{\b(?:application|text)/json\b}i)
|
|
299
|
+
end
|
|
264
300
|
|
|
265
|
-
|
|
301
|
+
response_body_string = if response_body.is_a?(String)
|
|
302
|
+
response_body
|
|
303
|
+
else
|
|
304
|
+
response_body.respond_to?(:to_json) ? response_body.to_json : response_body.to_s
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
response_byte_count = response_body_string.bytesize
|
|
308
|
+
|
|
309
|
+
if response_body_is_json
|
|
310
|
+
parsed = bf_json_parse(response_body_string)
|
|
311
|
+
response_body_json = bf_json_dump(bf_protect_data(parsed)) unless parsed.nil?
|
|
312
|
+
end
|
|
313
|
+
end
|
|
266
314
|
|
|
267
315
|
response_byte_label = response_byte_count == 1 ? "byte" : "bytes"
|
|
268
316
|
|
|
269
317
|
lines = [
|
|
270
|
-
"-- HTTP #{status} #{
|
|
318
|
+
"-- HTTP #{status} #{bf_reason_phrase} --".gsub(/\s+/, " ").upcase,
|
|
271
319
|
"",
|
|
272
320
|
"-- Request URL --",
|
|
273
321
|
env.url.to_s,
|
|
@@ -284,10 +332,10 @@ module Faraday
|
|
|
284
332
|
"",
|
|
285
333
|
|
|
286
334
|
%[-- Request Body (#{request_byte_count} #{request_byte_label}) --],
|
|
287
|
-
if !
|
|
288
|
-
|
|
335
|
+
if !request_body_json.nil?
|
|
336
|
+
request_body_json
|
|
289
337
|
else
|
|
290
|
-
bf_dump(
|
|
338
|
+
bf_dump(request_body_string)
|
|
291
339
|
end.then { bf_truncate(_1) },
|
|
292
340
|
"",
|
|
293
341
|
|
|
@@ -304,10 +352,10 @@ module Faraday
|
|
|
304
352
|
"",
|
|
305
353
|
|
|
306
354
|
%[-- Response Body (#{response_byte_count} #{response_byte_label}) --],
|
|
307
|
-
if !
|
|
308
|
-
|
|
355
|
+
if !response_body_json.nil?
|
|
356
|
+
response_body_json
|
|
309
357
|
else
|
|
310
|
-
bf_dump(
|
|
358
|
+
bf_dump(response_body_string)
|
|
311
359
|
end.then { bf_truncate(_1) },
|
|
312
360
|
""
|
|
313
361
|
]
|
|
@@ -328,6 +376,7 @@ module Faraday
|
|
|
328
376
|
end
|
|
329
377
|
end
|
|
330
378
|
|
|
379
|
+
|
|
331
380
|
module BetterFaraday
|
|
332
381
|
class HTTPError < Faraday::Error
|
|
333
382
|
def initialize(response)
|
|
@@ -419,7 +468,7 @@ module BetterFaraday
|
|
|
419
468
|
# Various tokens (access_token, refresh_token, csrf_token, bearer_token, auth_token, or just token)
|
|
420
469
|
/(?:access|refresh|auth|bearer|csrf|authorization)?#{separator}token/i,
|
|
421
470
|
# API keys (api_key, apikey, x_api_key)
|
|
422
|
-
/api#{separator}key/i,
|
|
471
|
+
/api#{separator}(?:key|id)/i,
|
|
423
472
|
# Private keys
|
|
424
473
|
/private#{separator}key/i,
|
|
425
474
|
# Crypto mnemonics
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "BetterFaraday with Faraday JSON Middlewares" do
|
|
6
|
+
let(:conn) do
|
|
7
|
+
Faraday.new(url: "https://api.example.com") do |f|
|
|
8
|
+
f.request :json
|
|
9
|
+
f.response :json
|
|
10
|
+
|
|
11
|
+
f.adapter :test do |stub|
|
|
12
|
+
stub.post("/process") do |env|
|
|
13
|
+
[
|
|
14
|
+
200,
|
|
15
|
+
{ "Content-Type" => "application/json" },
|
|
16
|
+
'{"status": "success", "access_token": "secret_token_123", "user_id": 42}'
|
|
17
|
+
]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "safely inspects and masks data when bodies are transformed by JSON middlewares" do
|
|
24
|
+
request_payload = {
|
|
25
|
+
action: "login",
|
|
26
|
+
password: "my_super_secret_password"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
response = conn.post("/process", request_payload)
|
|
30
|
+
|
|
31
|
+
expect(response.body).to be_a(Hash)
|
|
32
|
+
expect(response.body["status"]).to eq("success")
|
|
33
|
+
|
|
34
|
+
inspection = response.inspect
|
|
35
|
+
|
|
36
|
+
expect(inspection).to include('"password": "SECRET"')
|
|
37
|
+
expect(inspection).not_to include("my_super_secret_password")
|
|
38
|
+
expect(inspection).to include('"action": "login"')
|
|
39
|
+
|
|
40
|
+
expect(inspection).to include('"access_token": "SECRET"')
|
|
41
|
+
expect(inspection).not_to include("secret_token_123")
|
|
42
|
+
expect(inspection).to include('"user_id": 42')
|
|
43
|
+
|
|
44
|
+
expect(inspection).not_to include("=>")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "BetterFaraday Pre-parsed Bodies & Status Formatting" do
|
|
6
|
+
let(:env) do
|
|
7
|
+
Faraday::Env.from(
|
|
8
|
+
method: :post,
|
|
9
|
+
url: URI("https://api.example.com/json"),
|
|
10
|
+
status: 422,
|
|
11
|
+
reason_phrase: "UNKNOWN", # Faraday sometimes defaults to UNKNOWN
|
|
12
|
+
request_headers: { "Content-Type" => "application/json" },
|
|
13
|
+
# Simulate body already parsed by Faraday JSON request middleware
|
|
14
|
+
request_body: { "account_type" => "trading", "password" => "secret_123" },
|
|
15
|
+
response_headers: { "Content-Type" => "application/json; charset=UTF-8" },
|
|
16
|
+
# Simulate body already parsed by Faraday JSON response middleware
|
|
17
|
+
body: { "code" => 40010001, "message" => "Invalid currency" }
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
let(:response) { Faraday::Response.new(env) }
|
|
22
|
+
|
|
23
|
+
before do
|
|
24
|
+
response.env.instance_variable_set(:@bf_request_headers, { "Content-Type" => "application/json" })
|
|
25
|
+
# This is what gets captured by the middleware
|
|
26
|
+
response.env.instance_variable_set(:@bf_request_body, { "account_type" => "trading", "password" => "secret_123" })
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "formats 'UNKNOWN' reason phrase correctly to UNPROCESSABLE ENTITY" do
|
|
30
|
+
inspection = response.inspect
|
|
31
|
+
expect(inspection).to include("-- HTTP 422 UNPROCESSABLE ENTITY --")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "formats pre-parsed Hash response bodies to JSON strings and masks data" do
|
|
35
|
+
inspection = response.inspect
|
|
36
|
+
|
|
37
|
+
# Request Hash
|
|
38
|
+
expect(inspection).to include('"password": "SECRET"')
|
|
39
|
+
expect(inspection).to include('"account_type": "trading"')
|
|
40
|
+
|
|
41
|
+
# Response Hash
|
|
42
|
+
expect(inspection).to include('"code": 40010001')
|
|
43
|
+
expect(inspection).to include('"message": "Invalid currency"')
|
|
44
|
+
|
|
45
|
+
# Ensure Ruby hash notation is absolutely gone
|
|
46
|
+
expect(inspection).not_to include("=>")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "resolves empty reason phrases to their Net::HTTP default strings" do
|
|
50
|
+
response.env.status = 201
|
|
51
|
+
response.env.reason_phrase = ""
|
|
52
|
+
|
|
53
|
+
expect(response.inspect).to include("-- HTTP 201 CREATED --")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "BetterFaraday Reason Phrase Formatting" do
|
|
6
|
+
let(:env) do
|
|
7
|
+
Faraday::Env.from(
|
|
8
|
+
method: :get,
|
|
9
|
+
url: URI("https://api.example.com/status"),
|
|
10
|
+
status: status,
|
|
11
|
+
reason_phrase: reason_phrase,
|
|
12
|
+
request_headers: {},
|
|
13
|
+
request_body: "",
|
|
14
|
+
response_headers: {},
|
|
15
|
+
body: ""
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let(:response) { Faraday::Response.new(env) }
|
|
20
|
+
|
|
21
|
+
context "when reason_phrase is UNKNOWN" do
|
|
22
|
+
let(:status) { 422 }
|
|
23
|
+
let(:reason_phrase) { "UNKNOWN" }
|
|
24
|
+
|
|
25
|
+
it "resolves to UNPROCESSABLE ENTITY using Net::HTTP mapping" do
|
|
26
|
+
expect(response.inspect).to include("-- HTTP 422 UNPROCESSABLE ENTITY --")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
context "when reason_phrase is empty" do
|
|
31
|
+
let(:status) { 201 }
|
|
32
|
+
let(:reason_phrase) { " " }
|
|
33
|
+
|
|
34
|
+
it "resolves to CREATED using Net::HTTP mapping" do
|
|
35
|
+
expect(response.inspect).to include("-- HTTP 201 CREATED --")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context "when reason_phrase is valid" do
|
|
40
|
+
let(:status) { 418 }
|
|
41
|
+
let(:reason_phrase) { "I Am A Teapot" }
|
|
42
|
+
|
|
43
|
+
it "uses the provided reason phrase" do
|
|
44
|
+
expect(response.inspect).to include("-- HTTP 418 I AM A TEAPOT --")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context "when status code is completely unknown" do
|
|
49
|
+
let(:status) { 999 }
|
|
50
|
+
let(:reason_phrase) { "UNKNOWN" }
|
|
51
|
+
|
|
52
|
+
it "safely falls back to UNKNOWN if no Net::HTTP mapping exists" do
|
|
53
|
+
expect(response.inspect).to include("-- HTTP 999 UNKNOWN --")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: better-faraday
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.1.
|
|
4
|
+
version: 3.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yaroslav Konoplov
|
|
@@ -152,10 +152,13 @@ files:
|
|
|
152
152
|
- spec/http_error_spec.rb
|
|
153
153
|
- spec/inspect_truncation_spec.rb
|
|
154
154
|
- spec/invalid_json_spec.rb
|
|
155
|
+
- spec/json_middleware_spec.rb
|
|
156
|
+
- spec/json_parsing_spec.rb
|
|
155
157
|
- spec/logger_spec.rb
|
|
156
158
|
- spec/middleware_spec.rb
|
|
157
159
|
- spec/multipart_spec.rb
|
|
158
160
|
- spec/nested_sensitive_data_spec.rb
|
|
161
|
+
- spec/reason_phrase_spec.rb
|
|
159
162
|
- spec/sensitive_data_signs_spec.rb
|
|
160
163
|
- spec/spec_helper.rb
|
|
161
164
|
- spec/timing_inspect_spec.rb
|
|
@@ -192,10 +195,13 @@ test_files:
|
|
|
192
195
|
- spec/http_error_spec.rb
|
|
193
196
|
- spec/inspect_truncation_spec.rb
|
|
194
197
|
- spec/invalid_json_spec.rb
|
|
198
|
+
- spec/json_middleware_spec.rb
|
|
199
|
+
- spec/json_parsing_spec.rb
|
|
195
200
|
- spec/logger_spec.rb
|
|
196
201
|
- spec/middleware_spec.rb
|
|
197
202
|
- spec/multipart_spec.rb
|
|
198
203
|
- spec/nested_sensitive_data_spec.rb
|
|
204
|
+
- spec/reason_phrase_spec.rb
|
|
199
205
|
- spec/sensitive_data_signs_spec.rb
|
|
200
206
|
- spec/spec_helper.rb
|
|
201
207
|
- spec/timing_inspect_spec.rb
|