better-faraday 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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