patient_http 1.6.1 → 1.8.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/ARCHITECTURE.md +18 -8
- data/CHANGELOG.md +43 -0
- data/README.md +564 -508
- data/VERSION +1 -1
- data/lib/patient_http/callback_args.rb +53 -48
- data/lib/patient_http/callback_validator.rb +11 -7
- data/lib/patient_http/class_helper.rb +6 -7
- data/lib/patient_http/client.rb +53 -26
- data/lib/patient_http/client_pool.rb +134 -39
- data/lib/patient_http/completion_executor.rb +20 -20
- data/lib/patient_http/configuration.rb +405 -119
- data/lib/patient_http/connection_endpoint.rb +150 -0
- data/lib/patient_http/encryptor.rb +28 -18
- data/lib/patient_http/error.rb +24 -18
- data/lib/patient_http/external_storage.rb +42 -38
- data/lib/patient_http/http_error.rb +30 -26
- data/lib/patient_http/http_headers.rb +35 -29
- data/lib/patient_http/immediate_retries.rb +98 -0
- data/lib/patient_http/inline_task_handler.rb +15 -10
- data/lib/patient_http/lifecycle_manager.rb +39 -40
- data/lib/patient_http/outgoing_request.rb +25 -23
- data/lib/patient_http/payload.rb +28 -26
- data/lib/patient_http/payload_store/active_record_store.rb +31 -34
- data/lib/patient_http/payload_store/base.rb +42 -46
- data/lib/patient_http/payload_store/file_store.rb +22 -26
- data/lib/patient_http/payload_store/redis_store.rb +28 -34
- data/lib/patient_http/payload_store/s3_store.rb +25 -28
- data/lib/patient_http/payload_store.rb +2 -0
- data/lib/patient_http/processor.rb +111 -79
- data/lib/patient_http/processor_observer.rb +65 -59
- data/lib/patient_http/rails/engine.rb +13 -8
- data/lib/patient_http/redirect_error.rb +50 -41
- data/lib/patient_http/redirect_helper.rb +38 -38
- data/lib/patient_http/request.rb +117 -46
- data/lib/patient_http/request_error.rb +47 -42
- data/lib/patient_http/request_helper.rb +178 -119
- data/lib/patient_http/request_preparer.rb +13 -10
- data/lib/patient_http/request_task.rb +115 -84
- data/lib/patient_http/request_template.rb +119 -64
- data/lib/patient_http/response.rb +58 -52
- data/lib/patient_http/response_reader.rb +166 -118
- data/lib/patient_http/secret_manager.rb +34 -30
- data/lib/patient_http/secret_reference.rb +33 -26
- data/lib/patient_http/synchronous_executor.rb +84 -95
- data/lib/patient_http/task_handler.rb +23 -19
- data/lib/patient_http/time_helper.rb +8 -8
- data/lib/patient_http.rb +327 -186
- data/patient_http.gemspec +3 -2
- metadata +21 -5
|
@@ -1,43 +1,45 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module PatientHttp
|
|
4
|
-
#
|
|
4
|
+
# The HTTP response to an async request.
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
6
|
+
# A response holds the status, headers, and body, and details about the
|
|
7
|
+
# request that returned it. Responses can be serialized to JSON, so they can
|
|
8
|
+
# go through a job queue.
|
|
8
9
|
class Response
|
|
9
10
|
UNDEFINED = Object.new.freeze
|
|
10
11
|
private_constant :UNDEFINED
|
|
11
12
|
|
|
12
|
-
# @return [Integer] HTTP status code
|
|
13
|
+
# @return [Integer] The HTTP status code.
|
|
13
14
|
attr_reader :status
|
|
14
15
|
|
|
15
|
-
#
|
|
16
|
-
#
|
|
16
|
+
# The response headers. A header that occurs more than one time in the
|
|
17
|
+
# response, such as `set-cookie`, becomes one string with the values joined.
|
|
17
18
|
#
|
|
18
|
-
# @return [HttpHeaders] response headers
|
|
19
|
+
# @return [HttpHeaders] The response headers.
|
|
19
20
|
attr_reader :headers
|
|
20
21
|
|
|
21
|
-
# @return [Float] request duration in seconds
|
|
22
|
+
# @return [Float] The request duration in seconds.
|
|
22
23
|
attr_reader :duration
|
|
23
24
|
|
|
24
|
-
# @return [String] request ID
|
|
25
|
+
# @return [String] The request ID.
|
|
25
26
|
attr_reader :request_id
|
|
26
27
|
|
|
27
|
-
# @return [String] request URL
|
|
28
|
+
# @return [String] The request URL.
|
|
28
29
|
attr_reader :url
|
|
29
30
|
|
|
30
|
-
# @return [Symbol] HTTP method
|
|
31
|
+
# @return [Symbol] The HTTP method.
|
|
31
32
|
attr_reader :http_method
|
|
32
33
|
|
|
33
|
-
# @return [Array<String>] URLs
|
|
34
|
+
# @return [Array<String>] The URLs of the redirects that were followed, in
|
|
35
|
+
# order. Empty if no redirects were followed.
|
|
34
36
|
attr_reader :redirects
|
|
35
37
|
|
|
36
38
|
class << self
|
|
37
|
-
#
|
|
39
|
+
# Creates a response from its serialized form.
|
|
38
40
|
#
|
|
39
|
-
# @param hash [Hash] hash
|
|
40
|
-
# @return [Response]
|
|
41
|
+
# @param hash [Hash] The hash from {#as_json}.
|
|
42
|
+
# @return [Response] The response.
|
|
41
43
|
def load(hash)
|
|
42
44
|
new(
|
|
43
45
|
status: hash["status"],
|
|
@@ -53,17 +55,18 @@ module PatientHttp
|
|
|
53
55
|
end
|
|
54
56
|
end
|
|
55
57
|
|
|
56
|
-
#
|
|
58
|
+
# Creates a response.
|
|
57
59
|
#
|
|
58
|
-
# @param status [Integer] HTTP status code
|
|
59
|
-
# @param headers [Hash, HttpHeaders] response headers
|
|
60
|
-
# @param body [String, nil] response body
|
|
61
|
-
# @param duration [Float] request duration in seconds
|
|
62
|
-
# @param request_id [String]
|
|
63
|
-
# @param url [String]
|
|
64
|
-
# @param http_method [Symbol]
|
|
65
|
-
# @param callback_args [Hash, nil] callback arguments
|
|
66
|
-
# @param redirects [Array<String>, nil] URLs
|
|
60
|
+
# @param status [Integer] The HTTP status code.
|
|
61
|
+
# @param headers [Hash, HttpHeaders] The response headers.
|
|
62
|
+
# @param body [String, nil] The response body.
|
|
63
|
+
# @param duration [Float] The request duration in seconds.
|
|
64
|
+
# @param request_id [String] The request ID.
|
|
65
|
+
# @param url [String] The request URL.
|
|
66
|
+
# @param http_method [Symbol] The HTTP method.
|
|
67
|
+
# @param callback_args [Hash, nil] The callback arguments, with string keys.
|
|
68
|
+
# @param redirects [Array<String>, nil] The URLs of the redirects that were
|
|
69
|
+
# followed.
|
|
67
70
|
def initialize(status:, headers:, body:, duration:, request_id:, url:, http_method:, callback_args: nil, redirects: nil)
|
|
68
71
|
@status = status
|
|
69
72
|
@headers = HttpHeaders.new(headers)
|
|
@@ -80,76 +83,78 @@ module PatientHttp
|
|
|
80
83
|
@redirects = redirects || []
|
|
81
84
|
end
|
|
82
85
|
|
|
83
|
-
# Returns the callback arguments
|
|
86
|
+
# Returns the callback arguments that were passed with the request.
|
|
84
87
|
#
|
|
85
|
-
# @return [CallbackArgs]
|
|
88
|
+
# @return [CallbackArgs] The callback arguments.
|
|
86
89
|
def callback_args
|
|
87
90
|
@callback_args ||= CallbackArgs.load(@callback_args_data)
|
|
88
91
|
end
|
|
89
92
|
|
|
90
|
-
# Returns the response body
|
|
93
|
+
# Returns the response body. The body is decoded on first access.
|
|
91
94
|
#
|
|
92
|
-
# @return [String, nil] The
|
|
95
|
+
# @return [String, nil] The response body, or `nil` if the response has no
|
|
96
|
+
# body.
|
|
93
97
|
def body
|
|
94
98
|
@body = @payload&.value if @body.equal?(UNDEFINED)
|
|
95
99
|
@body
|
|
96
100
|
end
|
|
97
101
|
|
|
98
|
-
#
|
|
102
|
+
# Returns whether the status is 2xx.
|
|
99
103
|
#
|
|
100
|
-
# @return [Boolean]
|
|
104
|
+
# @return [Boolean] `true` if the request succeeded.
|
|
101
105
|
def success?
|
|
102
106
|
status >= 200 && status < 300
|
|
103
107
|
end
|
|
104
108
|
|
|
105
|
-
#
|
|
109
|
+
# Returns whether the status is 3xx.
|
|
106
110
|
#
|
|
107
|
-
# @return [Boolean]
|
|
111
|
+
# @return [Boolean] `true` if the response is a redirect.
|
|
108
112
|
def redirect?
|
|
109
113
|
status >= 300 && status < 400
|
|
110
114
|
end
|
|
111
115
|
|
|
112
|
-
#
|
|
116
|
+
# Returns whether the status is 4xx.
|
|
113
117
|
#
|
|
114
|
-
# @return [Boolean]
|
|
118
|
+
# @return [Boolean] `true` if the response is a client error.
|
|
115
119
|
def client_error?
|
|
116
120
|
status >= 400 && status < 500
|
|
117
121
|
end
|
|
118
122
|
|
|
119
|
-
#
|
|
123
|
+
# Returns whether the status is 5xx.
|
|
120
124
|
#
|
|
121
|
-
# @return [Boolean]
|
|
125
|
+
# @return [Boolean] `true` if the response is a server error.
|
|
122
126
|
def server_error?
|
|
123
127
|
status >= 500 && status < 600
|
|
124
128
|
end
|
|
125
129
|
|
|
126
|
-
#
|
|
130
|
+
# Returns whether the status is 4xx or 5xx.
|
|
127
131
|
#
|
|
128
|
-
# @return [Boolean]
|
|
132
|
+
# @return [Boolean] `true` if the response is an error.
|
|
129
133
|
def error?
|
|
130
134
|
status >= 400 && status < 600
|
|
131
135
|
end
|
|
132
136
|
|
|
133
|
-
#
|
|
137
|
+
# Returns the value of the `content-type` header.
|
|
134
138
|
#
|
|
135
|
-
# @return [String, nil]
|
|
139
|
+
# @return [String, nil] The content type, or `nil` if the header isn't set.
|
|
136
140
|
def content_type
|
|
137
141
|
headers["content-type"]
|
|
138
142
|
end
|
|
139
143
|
|
|
140
|
-
#
|
|
144
|
+
# Returns whether the `content-type` header identifies a JSON body.
|
|
141
145
|
#
|
|
142
|
-
# @return [Boolean]
|
|
146
|
+
# @return [Boolean] `true` if the body is JSON.
|
|
143
147
|
def json?
|
|
144
148
|
type = content_type.to_s.downcase
|
|
145
149
|
type.match?(%r{\Aapplication/[^ ]*json\b}) || type == "text/json"
|
|
146
150
|
end
|
|
147
151
|
|
|
148
|
-
#
|
|
152
|
+
# Parses the response body as JSON.
|
|
149
153
|
#
|
|
150
|
-
# @return [Hash, Array] parsed
|
|
151
|
-
# @raise [RuntimeError]
|
|
152
|
-
#
|
|
154
|
+
# @return [Hash, Array] The parsed body.
|
|
155
|
+
# @raise [RuntimeError] If the `content-type` header doesn't identify a JSON
|
|
156
|
+
# body.
|
|
157
|
+
# @raise [JSON::ParserError] If the body isn't valid JSON.
|
|
153
158
|
def json
|
|
154
159
|
unless json?
|
|
155
160
|
raise "Response Content-Type is not application/json (got: #{content_type.inspect})"
|
|
@@ -158,9 +163,9 @@ module PatientHttp
|
|
|
158
163
|
JSON.parse(body)
|
|
159
164
|
end
|
|
160
165
|
|
|
161
|
-
#
|
|
166
|
+
# Returns the response as a JSON-compatible hash.
|
|
162
167
|
#
|
|
163
|
-
# @return [Hash]
|
|
168
|
+
# @return [Hash] The serialized response.
|
|
164
169
|
def as_json
|
|
165
170
|
{
|
|
166
171
|
"status" => status,
|
|
@@ -175,10 +180,11 @@ module PatientHttp
|
|
|
175
180
|
}
|
|
176
181
|
end
|
|
177
182
|
|
|
178
|
-
#
|
|
183
|
+
# Returns the response as a JSON string.
|
|
179
184
|
#
|
|
180
|
-
# @param options [Hash] options
|
|
181
|
-
#
|
|
185
|
+
# @param options [Hash, nil] The options for `JSON.generate`. This parameter
|
|
186
|
+
# makes the method compatible with Active Support.
|
|
187
|
+
# @return [String] The JSON string.
|
|
182
188
|
def to_json(options = nil)
|
|
183
189
|
JSON.generate(as_json, options)
|
|
184
190
|
end
|