better-faraday 1.2.0 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b4091f67de65133fa096eef795198cf51895c03ada658fb72e84fbfae2c14522
4
- data.tar.gz: c79c87d6051648a39a58e91259bb78309e78368d9874ca4605d09152388b908f
3
+ metadata.gz: a8c5cfa773e01c53223151db865f52ad18b5ab411ad0df1d67415a5bd4ef1e73
4
+ data.tar.gz: dde3e3df19d758f85d156ae357d96cb017e0a2746d7de2d14533a860d0dacd9c
5
5
  SHA512:
6
- metadata.gz: 80c822fc52bc8bf778d37b769bbce32e2e7c75e3c94fb9a2098659f8b4d66e37df8c6e02c8c34b9f9bedc9f3fcaa7c64fa30de7c491a5f86f00fda575d1c3d65
7
- data.tar.gz: bdca3821190b987e8de4f334c712c3380985b4c60f4703a261a821aceba6e4bb8cb7fef5b742153d65230f23d418ca87d77da0c4369957df56e67e002e2422c1
6
+ metadata.gz: e9affc8fce7fe302214440777db7778ecc429d9ca188385ae42b58297a76d4a9db5c6e051313b5bc2f211af287c729bdcf39226652c132ff40b2570ef2ee2ea3
7
+ data.tar.gz: c0baba390fcef6d87131569c948cf3492233e99cb317900a79b459dddb0fc7653b05811b279bf71bc7339384f7f2b581a34c69004af8e847718bf10fd2918ce6
@@ -3,7 +3,7 @@
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "better-faraday"
6
- s.version = "1.2.0"
6
+ s.version = "2.0.0"
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", ">= 1.0", "< 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,91 @@ 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
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_dump(bf_protect_data(bf_json_parse(request_body)))
117
+ end
79
118
 
80
- if env.response_headers
81
- response_headers = __protect_data(env.response_headers.deep_dup)
82
- end
119
+ if env.response_headers
120
+ response_headers = bf_protect_data(env.response_headers.to_hash)
121
+ end
83
122
 
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
123
+ if env.response_headers && env.response_headers["content-type"].to_s.match?(/\bapplication\/json\b/i)
124
+ response_json = bf_json_dump(bf_protect_data(bf_json_parse(response_body)))
125
+ end
87
126
 
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
111
- 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
- "",
118
-
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
- "",
127
+ lines = [
128
+ "-- #{status} #{reason_phrase} --".upcase,
129
+ "",
130
+ "-- Request URL --",
131
+ env.url.to_s,
132
+ "",
133
+ "-- Request Method --",
134
+ env.method.to_s.upcase,
135
+ "",
136
+ "-- Request Headers --",
137
+ bf_json_dump(request_headers).truncate(2048, omission: "... (truncated)"),
138
+ "",
126
139
 
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)"
140
+ %[-- Request Body (#{request_body_bytes_count} #{"byte".pluralize(request_body_bytes_count)}) --],
141
+ if request_json
142
+ request_json
134
143
  else
135
- body
136
- end
137
- end.yield_self { |t| t.truncate(2048, omission: "... (truncated, full length: #{t.length})") }
138
- ]
144
+ # String#inspect returns \x{XXXX} for the encoding other than Unicode.
145
+ # [1..-2] removed leading and trailing " added by String#inspect.
146
+ # gsub(/\\"/, "\"") unescapes ".
147
+ request_body.inspect.gsub(/\\"/, "\"")[1..-2]
148
+ end.truncate(2048, omission: "... (truncated)"),
149
+ "",
139
150
 
140
- if env.response_received_at
141
- lines.concat [
151
+ "-- Request Sent At --",
152
+ env.bf_request_sent_at.strftime("%Y-%m-%d %H:%M:%S.%3N") + " UTC",
142
153
  "",
143
- "-- Response received at --",
144
- env.response_received_at.strftime("%Y-%m-%d %H:%M:%S.%2N") + " UTC",
154
+
155
+ "-- Response Headers --",
156
+ if response_headers
157
+ bf_json_dump(response_headers)
158
+ else
159
+ env.response_headers.to_s.inspect.gsub(/\\"/, "\"")[1..-2]
160
+ end.truncate(2048, omission: "... (truncated)"),
145
161
  "",
146
- "-- Response received in --",
147
- "#{((env.response_received_at.to_f - env.request_sent_at.to_f) * 1000.0).round(2)}ms"
162
+
163
+ %[-- Response Body (#{response_body_bytes_count} #{"byte".pluralize(response_body_bytes_count)}) --],
164
+ if response_json
165
+ response_json
166
+ else
167
+ response_body.inspect.gsub(/\\"/, "\"")[1..-2]
168
+ end.truncate(2048, omission: "... (truncated)"),
169
+ ""
148
170
  ]
171
+
172
+ if env.bf_response_received_at
173
+ lines.concat [
174
+ "-- Response Received At --",
175
+ env.bf_response_received_at.strftime("%Y-%m-%d %H:%M:%S.%3N") + " UTC",
176
+ "",
177
+ "-- Response Received In --",
178
+ "#{((env.bf_response_received_at.to_f - env.bf_request_sent_at.to_f) * 1000.0).ceil(3)}ms",
179
+ ""
180
+ ]
181
+ end
182
+
183
+ lines.join("\n").freeze
149
184
  end
150
185
 
151
- lines.join("\n") + "\n"
186
+ @inspection_text.dup
152
187
  end
153
188
 
154
- alias inspect describe
155
-
156
189
  private
157
190
 
158
- def __parse_json(json)
191
+ def bf_json_parse(json)
159
192
  return nil unless ::String === json
160
193
  data = ::JSON.parse(json)
161
194
  data if ::Hash === data || ::Array === data
@@ -163,42 +196,58 @@ module Faraday
163
196
  nil
164
197
  end
165
198
 
166
- def __protect_data(data)
167
- return data.map(&method(:__protect_data)) if ::Array === data
199
+ def bf_json_dump(data)
200
+ ::JSON.generate(data, space: " ", object_nl: " ", array_nl: " ")
201
+ end
202
+
203
+ def bf_protect_data(data)
204
+ return data.map(&method(:bf_protect_data)) if ::Array === data
168
205
  return data unless ::Hash === data
206
+
207
+ signs = BetterFaraday.sensitive_data_signs
208
+
169
209
  data.each_with_object({}) do |(key, value), memo|
170
- memo[key] = if key.to_s.underscore.tr("_", " ").yield_self { |k| Faraday::Inspection.secrets.any? { |s| k.match?(s) } }
210
+ memo[key] = if key.to_s.underscore.tr("_", " ").yield_self { |k| signs.any? { |r| k.match?(r) } }
171
211
  "SECRET"
172
212
  else
173
- __protect_data(value)
213
+ bf_protect_data(value)
174
214
  end
175
215
  end
176
216
  end
177
217
  end
218
+ end
219
+
220
+ module BetterFaraday
221
+ class HTTPError < Faraday::Error
222
+ def initialize(response)
223
+ super(response.inspect, response)
224
+ end
225
+
226
+ def inspect
227
+ %[#{self.class}\n\n#{response.inspect}]
228
+ end
229
+ end
178
230
 
179
- class HTTP4xx < Error; end
231
+ class HTTP4xx < HTTPError; end
180
232
  class HTTP400 < HTTP4xx; end
181
233
  class HTTP401 < HTTP4xx; end
182
234
  class HTTP403 < HTTP4xx; end
183
235
  class HTTP404 < HTTP4xx; end
184
236
  class HTTP422 < HTTP4xx; end
185
237
  class HTTP429 < HTTP4xx; end
186
-
187
- module Inspection
188
- class << self
189
- attr_accessor :secrets
190
- end
191
-
192
- self.secrets = [/\bpass(?:word|phrase)\b/i, /\bauthorization\b/i, /\bsecret\b/i, /\b(:?access)?token\b/i]
193
- end
238
+ class HTTP5xx < HTTPError; end
239
+ class HTTP500 < HTTP5xx; end
240
+ class HTTP502 < HTTP5xx; end
241
+ class HTTP503 < HTTP5xx; end
194
242
 
195
243
  class << self
196
- def secrets
197
- Inspection.secrets
198
- end
199
-
200
- def secrets=(value)
201
- Inspection.secrets = value
202
- end
244
+ attr_accessor :sensitive_data_signs
203
245
  end
246
+
247
+ self.sensitive_data_signs = [
248
+ /\bpass(?:word|phrase)\b/i,
249
+ /\bauthorization\b/i,
250
+ /\bsecret\b/i,
251
+ /\b(:?access)?token\b/i
252
+ ]
204
253
  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.2.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Konoplov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-19 00:00:00.000000000 Z
11
+ date: 2022-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -19,7 +19,7 @@ dependencies:
19
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
@@ -29,7 +29,7 @@ dependencies:
29
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