better-faraday 1.1.4 → 2.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30c3a63862a5faff00055d0f7fa905b8a67bcb85b3197929a0dcc1b7cce84d5c
4
- data.tar.gz: 1d21b6f8fc0ce3efa6dcfec7fcec79e490a27a7c8b48b8a5d97e6d27d196ef26
3
+ metadata.gz: c8f236710b44dc72e1efa535f4750f9cc782bce797be1d771da23d7f2ff3ded1
4
+ data.tar.gz: 4f9c6d1453f308abb928b234429f68c26345056c6b1ff8df2efc17dafce73bcf
5
5
  SHA512:
6
- metadata.gz: 181d98b5697219d6bc095d275c0335438b148dd0ddf3e0d08d97855c5f4457731d541d83e19d3c8b3b187023171621baffe13d03417a00d4be50a414809a3d0e
7
- data.tar.gz: fcaa327eb416c1ec95e8df522e580a92809ff927f043402f9c998a2797e19eda91b666261280edf89ccf08bfb8c58822399211ba6b8d899d9647b8cf28273001
6
+ metadata.gz: a3a4bb57f6938d021ebf37e9f854e06b55e4022f2ea983a62de1264085c64807a30d8eeeec72c8913b3dd6229c8777293ab2577b4aabd5cc41dd9e1c0b6d3db4
7
+ data.tar.gz: c0f632bb82dd8eeb84a071d12b1f957a9542494278eaa297e774ca71ef2c1a8a52bad868bec86410b74e9d9beafcb0f823b9eb64b8d4cefc8da840f47befc07f
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.6.1
1
+ 2.7.3
@@ -3,7 +3,7 @@
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "better-faraday"
6
- s.version = "1.1.4"
6
+ s.version = "2.0.1"
7
7
  s.author = "Yaroslav Konoplov"
8
8
  s.email = "eahome00@gmail.com"
9
9
  s.summary = "Extends Faraday with useful features."
@@ -13,6 +13,6 @@ Gem::Specification.new do |s|
13
13
  s.files = `git ls-files -z`.split("\x0")
14
14
  s.test_files = `git ls-files -z -- {test,spec,features}/*`.split("\x0")
15
15
  s.require_paths = ["lib"]
16
- s.add_dependency "faraday", ">= 0.17", "< 2.0"
16
+ s.add_dependency "faraday", ">= 1.0", "< 3.0"
17
17
  s.add_dependency "activesupport", ">= 4.0", "< 7.0"
18
18
  end
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "json"
5
+ require "net/http"
5
6
  require "faraday"
6
7
  require "faraday/error"
7
8
  require "faraday/options"
@@ -9,50 +10,83 @@ require "faraday/response"
9
10
  require "active_support/core_ext/object/deep_dup"
10
11
  require "active_support/core_ext/string/filters"
11
12
  require "active_support/core_ext/string/inflections"
13
+ require "active_support/core_ext/object/inclusion"
12
14
 
13
15
  Module.new do
14
- def call(env)
15
- env.instance_variable_set(:@request_sent_at, Time.now.utc)
16
+ def call(environment)
17
+ environment.instance_variable_set(:@bf_request_sent_at, Time.now.utc)
16
18
  super
17
19
  end
18
20
 
19
- def save_response(env, status, body, headers = nil, reason_phrase = nil)
20
- env.instance_variable_set(:@response_received_at, Time.now.utc)
21
- env.instance_variable_set(:@request_body, env.body.respond_to?(:read) ? env.body.read : env.body)
21
+ def save_response(environment, *)
22
+ environment.instance_variable_set \
23
+ :@bf_response_received_at,
24
+ Time.now.utc
25
+
26
+ body = environment.body
27
+ value = body.respond_to?(:read) ? body.read : body
28
+ value = value.to_s unless String === value
29
+
30
+ environment.instance_variable_set \
31
+ :@bf_request_body,
32
+ value
33
+
22
34
  super
23
35
  end
24
- end.tap { |m| Faraday::Adapter.send(:prepend, m) }
36
+ end.tap { |m| Faraday::Adapter.prepend(m) }
25
37
 
26
- module Faraday
27
- class Env
28
- attr_reader :request_body, :request_sent_at, :response_received_at
38
+ Module.new do
39
+ def end_transport(request, response)
40
+ headers = request.to_hash
41
+ headers.keys.each { |k| headers[k] = headers[k].join(", ") }
42
+ response.instance_variable_set :@bf_request_headers, headers
43
+ super
29
44
  end
45
+ end.tap { |m| Net::HTTP.prepend(m) }
30
46
 
31
- class Error
32
- attr_reader :response
33
-
34
- def inspect
35
- super.gsub(/\s*\(\s*\)\s*\z/, "")
47
+ Module.new do
48
+ def perform_request(connection, environment)
49
+ super(connection, environment).tap do |response|
50
+ environment.instance_variable_set \
51
+ :@bf_request_headers,
52
+ response.instance_variable_get(:@bf_request_headers)
36
53
  end
37
54
  end
55
+ end.tap { |m| Faraday::Adapter::NetHttp.prepend(m) }
56
+
57
+ module Faraday
58
+ class Env
59
+ attr_reader :bf_request_headers, :bf_request_body, :bf_request_sent_at, :bf_response_received_at
60
+ end
38
61
 
39
62
  class Response
40
- def assert_2xx!
41
- return self if status_2xx?
63
+ def assert_status!(code_or_range)
64
+ within_range = if Range === code_or_range
65
+ status.in?(code_or_range)
66
+ else
67
+ status == code_or_range
68
+ end
69
+
70
+ return self if within_range
42
71
 
43
72
  klass = if status_4xx?
44
- "Faraday::HTTP#{status}".safe_constantize || Faraday::HTTP4xx
73
+ "BetterFaraday::HTTP#{status}".safe_constantize || BetterFaraday::HTTP4xx
74
+ elsif status_5xx?
75
+ "BetterFaraday::HTTP#{status}".safe_constantize || BetterFaraday::HTTP5xx
45
76
  else
46
- Faraday::Error
77
+ BetterFaraday::HTTPError
47
78
  end
48
79
 
49
- error = klass.new("\n#{describe}")
50
- error.instance_variable_set(:@response, self)
51
- raise error
80
+ raise klass.new(self)
81
+ end
82
+
83
+ def assert_2xx!
84
+ assert_status!(200..299)
52
85
  end
53
86
 
54
- alias ok! assert_2xx! # Short name.
55
- alias assert_success! assert_2xx! # Compatibility.
87
+ def assert_200!
88
+ assert_status! 200
89
+ end
56
90
 
57
91
  def status_2xx?
58
92
  status >= 200 && status <= 299
@@ -70,92 +104,95 @@ module Faraday
70
104
  status >= 500 && status <= 599
71
105
  end
72
106
 
73
- def describe
74
- request_headers = __protect_data(env.request_headers.deep_dup)
75
-
76
- if env.request_headers["Content-Type"].to_s.match?(/\bapplication\/json\b/)
77
- request_json = __protect_data(__parse_json(env.request_body.dup))
78
- end
79
-
80
- if env.response_headers
81
- response_headers = __protect_data(env.response_headers.deep_dup)
82
- end
107
+ def inspect
108
+ @inspection_text ||= begin
109
+ request_headers = bf_protect_data(env.bf_request_headers.dup)
110
+ request_body = env.bf_request_body.yield_self { |body| String === body ? body : body.to_s }
111
+ request_body_bytes_count = env.bf_request_body.bytesize
112
+ response_body = env.body.yield_self { |body| String === body ? body : body.to_s }
113
+ response_body_bytes_count = response_body.bytesize
114
+
115
+ if env.bf_request_headers["content-type"].to_s.match?(/\bapplication\/json\b/i)
116
+ request_json = bf_json_parse(request_body).yield_self do |data|
117
+ bf_json_dump(bf_protect_data(data)) if data
118
+ end
119
+ end
83
120
 
84
- if env.response_headers && env.response_headers["Content-Type"].to_s.match?(/\bapplication\/json\b/)
85
- response_json = __protect_data(__parse_json(env.body.dup))
86
- end
121
+ if env.response_headers
122
+ response_headers = bf_protect_data(env.response_headers.to_hash)
123
+ end
87
124
 
88
- lines = [
89
- "",
90
- "-- #{status} #{reason_phrase} --".upcase,
91
- "",
92
- "-- Request URL --",
93
- env.url.to_s,
94
- "",
95
- "-- Request method --",
96
- env.method.to_s.upcase,
97
- "",
98
- "-- Request headers --",
99
- ::JSON.generate(request_headers).yield_self { |t| t.truncate(2048, omission: "... (truncated, full length: #{t.length})") },
100
- "",
101
-
102
- "-- Request body --",
103
- if request_json
104
- ::JSON.generate(request_json)
105
- else
106
- body = env.request_body.to_s.dup
107
- if body.encoding.name == "ASCII-8BIT"
108
- "Binary (#{body.size} bytes)"
109
- else
110
- body
125
+ if env.response_headers && env.response_headers["content-type"].to_s.match?(/\bapplication\/json\b/i)
126
+ response_json = bf_json_parse(response_body).yield_self do |data|
127
+ bf_json_dump(bf_protect_data(data)) if data
111
128
  end
112
- end.yield_self { |t| t.truncate(1024, omission: "... (truncated, full length: #{t.length})") },
113
- "",
114
-
115
- "-- Request sent at --",
116
- env.request_sent_at.strftime("%Y-%m-%d %H:%M:%S.%2N") + " UTC",
117
- "",
129
+ end
118
130
 
119
- "-- Response headers --",
120
- if response_headers
121
- ::JSON.generate(response_headers)
122
- else
123
- env.response_headers.to_s
124
- end.yield_self { |t| t.truncate(2048, omission: "... (truncated, full length: #{t.length})") },
125
- "",
131
+ lines = [
132
+ "-- #{status} #{reason_phrase} --".upcase,
133
+ "",
134
+ "-- Request URL --",
135
+ env.url.to_s,
136
+ "",
137
+ "-- Request Method --",
138
+ env.method.to_s.upcase,
139
+ "",
140
+ "-- Request Headers --",
141
+ bf_json_dump(request_headers).truncate(2048, omission: "... (truncated)"),
142
+ "",
126
143
 
127
- "-- Response body --",
128
- if response_json
129
- ::JSON.generate(response_json)
130
- else
131
- body = env.body.to_s.dup
132
- if body.encoding.name == "ASCII-8BIT"
133
- "Binary (#{body.size} bytes)"
144
+ %[-- Request Body (#{request_body_bytes_count} #{"byte".pluralize(request_body_bytes_count)}) --],
145
+ if request_json
146
+ request_json
134
147
  else
135
- body
136
- end
137
- end.yield_self { |t| t.truncate(2048, omission: "... (truncated, full length: #{t.length})") }
138
- ]
148
+ # String#inspect returns \x{XXXX} for the encoding other than Unicode.
149
+ # [1..-2] removed leading and trailing " added by String#inspect.
150
+ # gsub(/\\"/, "\"") unescapes ".
151
+ request_body.inspect.gsub(/\\"/, "\"")[1..-2]
152
+ end.truncate(2048, omission: "... (truncated)"),
153
+ "",
139
154
 
140
- if env.response_received_at
141
- lines.concat [
155
+ "-- Request Sent At --",
156
+ env.bf_request_sent_at.strftime("%Y-%m-%d %H:%M:%S.%3N") + " UTC",
142
157
  "",
143
- "-- Response received at --",
144
- env.response_received_at.strftime("%Y-%m-%d %H:%M:%S.%2N") + " UTC",
158
+
159
+ "-- Response Headers --",
160
+ if response_headers
161
+ bf_json_dump(response_headers)
162
+ else
163
+ env.response_headers.to_s.inspect.gsub(/\\"/, "\"")[1..-2]
164
+ end.truncate(2048, omission: "... (truncated)"),
145
165
  "",
146
- "-- Response received in --",
147
- "#{((env.response_received_at.to_f - env.request_sent_at.to_f) * 1000.0).round(2)}ms"
166
+
167
+ %[-- Response Body (#{response_body_bytes_count} #{"byte".pluralize(response_body_bytes_count)}) --],
168
+ if response_json
169
+ response_json
170
+ else
171
+ response_body.inspect.gsub(/\\"/, "\"")[1..-2]
172
+ end.truncate(2048, omission: "... (truncated)"),
173
+ ""
148
174
  ]
175
+
176
+ if env.bf_response_received_at
177
+ lines.concat [
178
+ "-- Response Received At --",
179
+ env.bf_response_received_at.strftime("%Y-%m-%d %H:%M:%S.%3N") + " UTC",
180
+ "",
181
+ "-- Response Received In --",
182
+ "#{((env.bf_response_received_at.to_f - env.bf_request_sent_at.to_f) * 1000.0).ceil(3)}ms",
183
+ ""
184
+ ]
185
+ end
186
+
187
+ lines.join("\n").freeze
149
188
  end
150
189
 
151
- lines.join("\n") + "\n"
190
+ @inspection_text.dup
152
191
  end
153
192
 
154
- alias inspect describe
155
-
156
193
  private
157
194
 
158
- def __parse_json(json)
195
+ def bf_json_parse(json)
159
196
  return nil unless ::String === json
160
197
  data = ::JSON.parse(json)
161
198
  data if ::Hash === data || ::Array === data
@@ -163,30 +200,58 @@ module Faraday
163
200
  nil
164
201
  end
165
202
 
166
- def __protect_data(data)
167
- return data.map(&method(:__protect_data)) if ::Array === data
203
+ def bf_json_dump(data)
204
+ ::JSON.generate(data, space: " ", object_nl: " ", array_nl: " ")
205
+ end
206
+
207
+ def bf_protect_data(data)
208
+ return data.map(&method(:bf_protect_data)) if ::Array === data
168
209
  return data unless ::Hash === data
210
+
211
+ signs = BetterFaraday.sensitive_data_signs
212
+
169
213
  data.each_with_object({}) do |(key, value), memo|
170
- memo[key] = if key.to_s.underscore.tr("_", " ").yield_self { |k| Faraday.secrets.any? { |s| k.match?(s) } }
214
+ memo[key] = if key.to_s.underscore.tr("_", " ").yield_self { |k| signs.any? { |r| k.match?(r) } }
171
215
  "SECRET"
172
216
  else
173
- __protect_data(value)
217
+ bf_protect_data(value)
174
218
  end
175
219
  end
176
220
  end
177
221
  end
222
+ end
223
+
224
+ module BetterFaraday
225
+ class HTTPError < Faraday::Error
226
+ def initialize(response)
227
+ super(response.inspect, response)
228
+ end
229
+
230
+ def inspect
231
+ %[#{self.class}\n\n#{response.inspect}]
232
+ end
233
+ end
178
234
 
179
- class HTTP4xx < Error; end
235
+ class HTTP4xx < HTTPError; end
180
236
  class HTTP400 < HTTP4xx; end
181
237
  class HTTP401 < HTTP4xx; end
182
238
  class HTTP403 < HTTP4xx; end
183
239
  class HTTP404 < HTTP4xx; end
184
240
  class HTTP422 < HTTP4xx; end
185
241
  class HTTP429 < HTTP4xx; end
242
+ class HTTP5xx < HTTPError; end
243
+ class HTTP500 < HTTP5xx; end
244
+ class HTTP502 < HTTP5xx; end
245
+ class HTTP503 < HTTP5xx; end
186
246
 
187
247
  class << self
188
- attr_accessor :secrets
248
+ attr_accessor :sensitive_data_signs
189
249
  end
190
250
 
191
- self.secrets = [/\bpass(?:word|phrase)\b/i, /\bauthorization\b/i, /\bsecret\b/i, /\b(:?access)?token\b/i]
251
+ self.sensitive_data_signs = [
252
+ /\bpass(?:word|phrase)\b/i,
253
+ /\bauthorization\b/i,
254
+ /\bsecret\b/i,
255
+ /\b(:?access)?token\b/i
256
+ ]
192
257
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: better-faraday
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Konoplov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-26 00:00:00.000000000 Z
11
+ date: 2022-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0.17'
19
+ version: '1.0'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '2.0'
22
+ version: '3.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '0.17'
29
+ version: '1.0'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '2.0'
32
+ version: '3.0'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: activesupport
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -82,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
82
82
  - !ruby/object:Gem::Version
83
83
  version: '0'
84
84
  requirements: []
85
- rubygems_version: 3.1.2
85
+ rubygems_version: 3.1.6
86
86
  signing_key:
87
87
  specification_version: 4
88
88
  summary: Extends Faraday with useful features.