hearth 1.0.0.pre1 → 1.0.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -4
  3. data/VERSION +1 -1
  4. data/lib/hearth/api_error.rb +15 -1
  5. data/lib/hearth/auth_option.rb +21 -0
  6. data/lib/hearth/auth_schemes/anonymous.rb +21 -0
  7. data/lib/hearth/auth_schemes/http_api_key.rb +16 -0
  8. data/lib/hearth/auth_schemes/http_basic.rb +16 -0
  9. data/lib/hearth/auth_schemes/http_bearer.rb +16 -0
  10. data/lib/hearth/auth_schemes/http_digest.rb +16 -0
  11. data/lib/hearth/auth_schemes.rb +32 -0
  12. data/lib/hearth/checksums.rb +31 -0
  13. data/lib/hearth/client_stubs.rb +130 -0
  14. data/lib/hearth/config/env_provider.rb +53 -0
  15. data/lib/hearth/config/resolver.rb +52 -0
  16. data/lib/hearth/configuration.rb +15 -0
  17. data/lib/hearth/connection_pool.rb +77 -0
  18. data/lib/hearth/context.rb +28 -4
  19. data/lib/hearth/dns/host_address.rb +23 -0
  20. data/lib/hearth/dns/host_resolver.rb +92 -0
  21. data/lib/hearth/dns.rb +48 -0
  22. data/lib/hearth/http/api_error.rb +4 -8
  23. data/lib/hearth/http/client.rb +208 -59
  24. data/lib/hearth/http/error_inspector.rb +85 -0
  25. data/lib/hearth/http/error_parser.rb +18 -20
  26. data/lib/hearth/http/field.rb +64 -0
  27. data/lib/hearth/http/fields.rb +117 -0
  28. data/lib/hearth/http/middleware/content_length.rb +5 -2
  29. data/lib/hearth/http/middleware/content_md5.rb +31 -0
  30. data/lib/hearth/http/middleware/request_compression.rb +157 -0
  31. data/lib/hearth/http/middleware.rb +12 -0
  32. data/lib/hearth/http/networking_error.rb +1 -14
  33. data/lib/hearth/http/request.rb +83 -56
  34. data/lib/hearth/http/response.rb +42 -13
  35. data/lib/hearth/http.rb +14 -5
  36. data/lib/hearth/identities/anonymous.rb +8 -0
  37. data/lib/hearth/identities/http_api_key.rb +16 -0
  38. data/lib/hearth/identities/http_bearer.rb +16 -0
  39. data/lib/hearth/identities/http_login.rb +20 -0
  40. data/lib/hearth/identities.rb +21 -0
  41. data/lib/hearth/identity_resolver.rb +17 -0
  42. data/lib/hearth/interceptor.rb +506 -0
  43. data/lib/hearth/interceptor_context.rb +36 -0
  44. data/lib/hearth/interceptor_list.rb +48 -0
  45. data/lib/hearth/interceptors.rb +75 -0
  46. data/lib/hearth/middleware/auth.rb +100 -0
  47. data/lib/hearth/middleware/build.rb +32 -0
  48. data/lib/hearth/middleware/host_prefix.rb +10 -6
  49. data/lib/hearth/middleware/initialize.rb +58 -0
  50. data/lib/hearth/middleware/parse.rb +45 -6
  51. data/lib/hearth/middleware/retry.rb +97 -23
  52. data/lib/hearth/middleware/send.rb +137 -25
  53. data/lib/hearth/middleware/sign.rb +65 -0
  54. data/lib/hearth/middleware/validate.rb +11 -1
  55. data/lib/hearth/middleware.rb +19 -8
  56. data/lib/hearth/middleware_stack.rb +1 -43
  57. data/lib/hearth/networking_error.rb +18 -0
  58. data/lib/hearth/number_helper.rb +2 -2
  59. data/lib/hearth/output.rb +8 -4
  60. data/lib/hearth/plugin_list.rb +53 -0
  61. data/lib/hearth/query/param.rb +52 -0
  62. data/lib/hearth/query/param_list.rb +54 -0
  63. data/lib/hearth/query/param_matcher.rb +32 -0
  64. data/lib/hearth/refreshing_identity_resolver.rb +63 -0
  65. data/lib/hearth/request.rb +22 -0
  66. data/lib/hearth/response.rb +33 -0
  67. data/lib/hearth/retry/adaptive.rb +60 -0
  68. data/lib/hearth/retry/capacity_not_available_error.rb +9 -0
  69. data/lib/hearth/retry/client_rate_limiter.rb +143 -0
  70. data/lib/hearth/retry/exponential_backoff.rb +15 -0
  71. data/lib/hearth/retry/retry_quota.rb +56 -0
  72. data/lib/hearth/retry/standard.rb +46 -0
  73. data/lib/hearth/retry/strategy.rb +20 -0
  74. data/lib/hearth/retry.rb +16 -0
  75. data/lib/hearth/signers/anonymous.rb +16 -0
  76. data/lib/hearth/signers/http_api_key.rb +29 -0
  77. data/lib/hearth/signers/http_basic.rb +23 -0
  78. data/lib/hearth/signers/http_bearer.rb +19 -0
  79. data/lib/hearth/signers/http_digest.rb +19 -0
  80. data/lib/hearth/signers.rb +23 -0
  81. data/lib/hearth/stubs.rb +30 -0
  82. data/lib/hearth/time_helper.rb +5 -3
  83. data/lib/hearth/validator.rb +44 -5
  84. data/lib/hearth/waiters/poller.rb +6 -7
  85. data/lib/hearth/waiters/waiter.rb +17 -4
  86. data/lib/hearth/xml/formatter.rb +11 -2
  87. data/lib/hearth/xml/node.rb +2 -2
  88. data/lib/hearth.rb +32 -5
  89. data/sig/lib/hearth/aliases.rbs +4 -0
  90. data/sig/lib/hearth/api_error.rbs +13 -0
  91. data/sig/lib/hearth/auth_option.rbs +11 -0
  92. data/sig/lib/hearth/auth_schemes/anonymous.rbs +7 -0
  93. data/sig/lib/hearth/auth_schemes/http_api_key.rbs +7 -0
  94. data/sig/lib/hearth/auth_schemes/http_basic.rbs +7 -0
  95. data/sig/lib/hearth/auth_schemes/http_bearer.rbs +7 -0
  96. data/sig/lib/hearth/auth_schemes/http_digest.rbs +7 -0
  97. data/sig/lib/hearth/auth_schemes.rbs +13 -0
  98. data/sig/lib/hearth/block_io.rbs +9 -0
  99. data/sig/lib/hearth/client_stubs.rbs +5 -0
  100. data/sig/lib/hearth/configuration.rbs +7 -0
  101. data/sig/lib/hearth/dns/host_address.rbs +13 -0
  102. data/sig/lib/hearth/dns/host_resolver.rbs +19 -0
  103. data/sig/lib/hearth/http/api_error.rbs +13 -0
  104. data/sig/lib/hearth/http/client.rbs +9 -0
  105. data/sig/lib/hearth/http/field.rbs +19 -0
  106. data/sig/lib/hearth/http/fields.rbs +43 -0
  107. data/sig/lib/hearth/http/request.rbs +25 -0
  108. data/sig/lib/hearth/http/response.rbs +21 -0
  109. data/sig/lib/hearth/identities/anonymous.rbs +6 -0
  110. data/sig/lib/hearth/identities/http_api_key.rbs +9 -0
  111. data/sig/lib/hearth/identities/http_bearer.rbs +9 -0
  112. data/sig/lib/hearth/identities/http_login.rbs +11 -0
  113. data/sig/lib/hearth/identities.rbs +9 -0
  114. data/sig/lib/hearth/identity_resolver.rbs +7 -0
  115. data/sig/lib/hearth/interceptor.rbs +9 -0
  116. data/sig/lib/hearth/interceptor_context.rbs +15 -0
  117. data/sig/lib/hearth/interceptor_list.rbs +16 -0
  118. data/sig/lib/hearth/interfaces.rbs +65 -0
  119. data/sig/lib/hearth/output.rbs +11 -0
  120. data/sig/lib/hearth/plugin_list.rbs +15 -0
  121. data/sig/lib/hearth/query/param.rbs +17 -0
  122. data/sig/lib/hearth/query/param_list.rbs +25 -0
  123. data/sig/lib/hearth/request.rbs +9 -0
  124. data/sig/lib/hearth/response.rbs +11 -0
  125. data/sig/lib/hearth/retry/adaptive.rbs +13 -0
  126. data/sig/lib/hearth/retry/exponential_backoff.rbs +7 -0
  127. data/sig/lib/hearth/retry/standard.rbs +13 -0
  128. data/sig/lib/hearth/retry/strategy.rbs +11 -0
  129. data/sig/lib/hearth/retry.rbs +9 -0
  130. data/sig/lib/hearth/signers/anonymous.rbs +9 -0
  131. data/sig/lib/hearth/signers/http_api_key.rbs +9 -0
  132. data/sig/lib/hearth/signers/http_basic.rbs +9 -0
  133. data/sig/lib/hearth/signers/http_bearer.rbs +9 -0
  134. data/sig/lib/hearth/signers/http_digest.rbs +9 -0
  135. data/sig/lib/hearth/signers.rbs +9 -0
  136. data/sig/lib/hearth/structure.rbs +7 -0
  137. data/sig/lib/hearth/union.rbs +5 -0
  138. data/sig/lib/hearth/waiters/waiter.rbs +17 -0
  139. metadata +132 -22
  140. data/lib/hearth/http/headers.rb +0 -70
  141. data/lib/hearth/middleware/around_handler.rb +0 -24
  142. data/lib/hearth/middleware/request_handler.rb +0 -24
  143. data/lib/hearth/middleware/response_handler.rb +0 -25
  144. data/lib/hearth/middleware_builder.rb +0 -246
  145. data/lib/hearth/stubbing/client_stubs.rb +0 -115
  146. data/lib/hearth/stubbing/stubs.rb +0 -32
  147. data/lib/hearth/waiters/errors.rb +0 -15
  148. data/sig/lib/seahorse/api_error.rbs +0 -10
  149. data/sig/lib/seahorse/document.rbs +0 -2
  150. data/sig/lib/seahorse/http/api_error.rbs +0 -21
  151. data/sig/lib/seahorse/http/headers.rbs +0 -47
  152. data/sig/lib/seahorse/http/response.rbs +0 -21
  153. data/sig/lib/seahorse/simple_delegator.rbs +0 -3
  154. data/sig/lib/seahorse/structure.rbs +0 -18
  155. data/sig/lib/seahorse/stubbing/client_stubs.rbs +0 -103
  156. data/sig/lib/seahorse/stubbing/stubs.rbs +0 -14
  157. data/sig/lib/seahorse/union.rbs +0 -6
@@ -0,0 +1,506 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ # # Interceptors
5
+ # Interceptors are a generic extension point that allows injecting
6
+ # logic at specific stages of execution within the SDK. Logic injection
7
+ # is done with hooks that the interceptor implements.
8
+ #
9
+ # Hooks are either read-only or read/write.
10
+ # Read-only hooks allow an interceptor to read the input,
11
+ # transport request, transport response or output messages.
12
+ # Read/write hooks allow an interceptor to modify one of these messages.
13
+ #
14
+ # # Creating an Interceptor
15
+ # To create an Interceptor, you must provide a hash with each key being
16
+ # a hook name and each value being a callable with arity 1 (context).
17
+ # For example:
18
+ #
19
+ # callbacks = {
20
+ # read_before_execution: proc { |context| puts "Before execution" },
21
+ # read_after_execution: proc { |context| puts "After execution" }
22
+ # }
23
+ # interceptor = Interceptor.new(callbacks)
24
+ #
25
+ # More advanced interceptors can be created by implementing the hooks
26
+ # directly in a class. For example:
27
+ #
28
+ # class MyInterceptor
29
+ # def initialize
30
+ # # keep state or do other initialization
31
+ # end
32
+ #
33
+ # def read_before_execution(context)
34
+ # puts "Before execution"
35
+ # end
36
+ #
37
+ # def read_after_execution(context)
38
+ # puts "After execution"
39
+ # end
40
+ # end
41
+ #
42
+ # # Registering an Interceptor
43
+ # Interceptors are registered by appending them to an {InterceptorList}
44
+ # and passing it to the the Client's Config. For example:
45
+ #
46
+ # interceptors = InterceptorList.new([MyInterceptor.new])
47
+ # config = Organization::Config.new(interceptors: interceptors)
48
+ # client = Organization::Client.new(config: config)
49
+ # client.operation
50
+ # #=> "Before execution"
51
+ # #=> "After execution"
52
+ #
53
+ # # Available Hooks
54
+ # The available hooks can be retrieved by calling {Interceptor.hooks}.
55
+ #
56
+ # The following hooks are available:
57
+ # * :read_before_execution
58
+ # * :modify_before_serialization
59
+ # * :read_before_serialization
60
+ # * :read_after_serialization
61
+ # * :modify_before_retry_loop
62
+ # * :read_before_attempt
63
+ # * :modify_before_signing
64
+ # * :read_before_signing
65
+ # * :read_after_signing
66
+ # * :modify_before_transmit
67
+ # * :read_before_transmit
68
+ # * :read_after_transmit
69
+ # * :modify_before_deserialization
70
+ # * :read_before_deserialization
71
+ # * :read_after_deserialization
72
+ # * :modify_before_attempt_completion
73
+ # * :read_after_attempt
74
+ # * :modify_before_completion
75
+ # * :read_after_execution
76
+ #
77
+ # # Hook Details
78
+ # The following sections describe each hook in detail.
79
+ #
80
+ # ## :read_before_execution
81
+ # A hook called at the start of an execution, before the SDK
82
+ # does anything else.
83
+ #
84
+ # When: This will ALWAYS be called once per execution. The duration
85
+ # between invocation of this hook and :read_after_execution is very
86
+ # close to full duration of the execution.
87
+ #
88
+ # Available Information: The {InterceptorContext#input} is ALWAYS
89
+ # available. Other information WILL NOT be available.
90
+ #
91
+ # Error Behavior: Errors raised by this hook will be stored
92
+ # until all interceptors have had their :read_before_execution
93
+ # invoked. Other hooks will then be skipped and execution will jump to
94
+ # :modify_before_completion with the raised error as the
95
+ # {Output#error} in {InterceptorContext#output}. If multiple
96
+ # :read_before_execution methods raise errors, the latest
97
+ # will be used and earlier ones will be logged and dropped.
98
+ #
99
+ # ## :modify_before_serialization
100
+ # A hook called before the input message is marshalled into a
101
+ # transport message. This method has the ability to modify the
102
+ # input message.
103
+ #
104
+ # When: This will ALWAYS be called once per execution, except when a
105
+ # failure occurs earlier in the request pipeline.
106
+ #
107
+ # Available Information: The {InterceptorContext#input} is ALWAYS
108
+ # available. This input may have been modified by earlier
109
+ # :modify_before_serialization hooks, and may be modified further by
110
+ # later hooks. Other information WILL NOT be available.
111
+ #
112
+ # Error Behavior: If errors are raised by this hook, execution will
113
+ # jump to :modify_before_completion with the raised error as the
114
+ # {Output#error} in {InterceptorContext#output}.
115
+ #
116
+ # ## :read_before_serialization
117
+ # A hook called before the input message is marshalled into a transport
118
+ # message.
119
+ #
120
+ # When: This will ALWAYS be called once per execution, except when a
121
+ # failure occurs earlier in the request pipeline. The duration
122
+ # between invocation of this hook and :read_after_serialization is
123
+ # very close to the amount of time spent marshalling the request.
124
+ #
125
+ # Available Information: The {InterceptorContext#input} is
126
+ # ALWAYS available. Other information WILL NOT be available.
127
+ #
128
+ # Error Behavior: If errors are raised by this hook, execution will
129
+ # jump to :modify_before_completion with the raised error as the
130
+ # {Output#error} in {InterceptorContext#output}.
131
+ #
132
+ # ## :read_after_serialization
133
+ # A hook called after the input message is marshalled into a transport
134
+ # message.
135
+ #
136
+ # When: This will ALWAYS be called once per execution, except when a
137
+ # failure occurs earlier in the request pipeline. The duration
138
+ # between invocation of this hook and :read_before_serialization is
139
+ # very close to the amount of time spent marshalling the request.
140
+ #
141
+ # Available Information: The {InterceptorContext#input},
142
+ # {InterceptorContext#request} are ALWAYS available. Other
143
+ # information WILL NOT be available.
144
+ #
145
+ # Error Behavior: If errors are raised by this hook, execution will
146
+ # jump to :modify_before_completion with the raised error as the
147
+ # {Output#error} in {InterceptorContext#output}.
148
+ #
149
+ # ## :modify_before_retry_loop
150
+ # A hook called before the retry loop is entered. This method
151
+ # has the ability to modify the transport request message.
152
+ #
153
+ # When: This will ALWAYS be called once per execution, except when a
154
+ # failure occurs earlier in the request pipeline.
155
+ #
156
+ # Available Information: The {InterceptorContext#input} and
157
+ # {InterceptorContext#request} are ALWAYS available. Other
158
+ # information WILL NOT be available.
159
+ #
160
+ # Error Behavior: If errors are raised by this hook, execution will
161
+ # jump to :modify_before_completion with the raised error as the
162
+ # {Output#error} in {InterceptorContext#output}.
163
+ #
164
+ # ## :read_before_attempt
165
+ # A hook called before each attempt at sending the transmission
166
+ # request message to the service.
167
+ #
168
+ # When: This will ALWAYS be called once per attempt, except when a
169
+ # failure occurs earlier in the request pipeline. This method will be
170
+ # called multiple times in the event of retries.
171
+ #
172
+ # Available Information: The {InterceptorContext#input} and
173
+ # {InterceptorContext#request} are ALWAYS available. Other
174
+ # information WILL NOT be available. In the event of retries, the
175
+ # {InterceptorContext} WILL include changes made in previous
176
+ # attempts (e.g. by other interceptors).
177
+ #
178
+ # Error Behavior: Errors raised by this hook will be stored
179
+ # until all interceptors have had their :read_before_attempt
180
+ # invoked. Other hooks will then be skipped and execution will jump to
181
+ # :modify_before_attempt_completion with the raised error as the
182
+ # {Output#error} in {InterceptorContext#output}. If multiple
183
+ # :read_before_attempt methods raise errors, the latest will be used
184
+ # and earlier ones will be logged and dropped.
185
+ #
186
+ # ## :modify_before_signing
187
+ # A hook called before the transport request message is signed. This
188
+ # method has the ability to modify the transport request message.
189
+ #
190
+ # When: This will ALWAYS be called once per attempt, except when a
191
+ # failure occurs earlier in the request pipeline. This method may be
192
+ # called multiple times in the event of retries.
193
+ #
194
+ # Available Information: The {InterceptorContext#input} and
195
+ # {InterceptorContext#request} are ALWAYS available. The request may
196
+ # have been modified by earlier :modify_before_signing hooks, and may
197
+ # be modified further by later hooks. Other information WILL NOT be
198
+ # available. In the event of retries, the {InterceptorContext} WILL
199
+ # include changes made in previous attempts (e.g. by other middleware
200
+ # or interceptors).
201
+ #
202
+ # Error Behavior: If errors are raised by this hook, execution will
203
+ # jump to :modify_before_attempt_completion with the raised error as
204
+ # the {Output#error} in {InterceptorContext#output}.
205
+ #
206
+ # ## :read_before_signing
207
+ # A hook called before the transport request message is signed.
208
+ #
209
+ # When: This will ALWAYS be called once per attempt, except when a
210
+ # failure occurs earlier in the request pipeline. This method may be
211
+ # called multiple times in the event of retries. The duration between
212
+ # invocation of this hook and :read_after_signing is very close to
213
+ # the amount of time spent signing the request.
214
+ #
215
+ # Available Information: The {InterceptorContext#input} and
216
+ # {InterceptorContext#request} are ALWAYS available. Other
217
+ # information WILL NOT be available. In the event of retries, the
218
+ # {InterceptorContext} WILL include changes made in previous
219
+ # attempts (e.g. by other interceptors).
220
+ #
221
+ # Error Behavior: If errors are raised by this hook, execution will
222
+ # jump to :modify_before_attempt_completion with the raised error as
223
+ # the {Output#error} in {InterceptorContext#output}.
224
+ #
225
+ # ## :read_after_signing
226
+ # A hook called after the transport request message is signed.
227
+ #
228
+ # When: This will ALWAYS be called once per attempt, except when a
229
+ # failure occurs earlier in the request pipeline. This method may be
230
+ # called multiple times in the event of retries. The duration between
231
+ # invocation of this hook and :read_before_signing is very close to
232
+ # the amount of time spent signing the request.
233
+ #
234
+ # Available Information: The {InterceptorContext#input} and
235
+ # {InterceptorContext#request} are ALWAYS available. Other
236
+ # information WILL NOT be available. In the event of retries, the
237
+ # {InterceptorContext} WILL include changes made in previous
238
+ # attempts (e.g. by other interceptors).
239
+ #
240
+ # Error Behavior: If errors are raised by this hook, execution will
241
+ # jump to :modify_before_attempt_completion with the raised error as
242
+ # the {Output#error} in {InterceptorContext#output}.
243
+ #
244
+ # ## :modify_before_transmit
245
+ # A hook called before the transport request message is sent to the
246
+ # service. This method has the ability to modify the transport request
247
+ # message.
248
+ #
249
+ # When: This will ALWAYS be called once per attempt, except when a
250
+ # failure occurs earlier in the request pipeline. This method may be
251
+ # called multiple times in the event of retries.
252
+ #
253
+ # Available Information: The {InterceptorContext#input} and
254
+ # {InterceptorContext#request} are ALWAYS available. The request may
255
+ # have been modified by earlier :modify_before_transmit hooks, and
256
+ # may be modified further by later hooks. Other information WILL NOT be
257
+ # available. In the event of retries, the {InterceptorContext} WILL
258
+ # include changes made in previous attempts (e.g. by other interceptors).
259
+ #
260
+ # Error Behavior: If errors are raised by this hook, execution will
261
+ # jump to :modify_before_attempt_completion with the raised error as
262
+ # the {Output#error} in {InterceptorContext#output}.
263
+ #
264
+ # ## :read_before_transmit
265
+ # A hook called before the transport request message is sent to the
266
+ # service.
267
+ #
268
+ # When: This will ALWAYS be called once per attempt, except when a
269
+ # failure occurs earlier in the request pipeline. This method may be
270
+ # called multiple times in the event of retries. The duration between
271
+ # invocation of this hook and :read_after_transmit is very close to
272
+ # the amount of time spent communicating with the service. Depending on
273
+ # the protocol, the duration may not include the time spent reading the
274
+ # response data.
275
+ #
276
+ # Available Information: The {InterceptorContext#input} and
277
+ # {InterceptorContext#request} are ALWAYS available. Other
278
+ # information WILL NOT be available. In the event of retries, the
279
+ # {InterceptorContext} WILL include changes made in previous
280
+ # attempts (e.g. by other interceptors).
281
+ #
282
+ # Error Behavior: If errors are raised by this hook, execution will
283
+ # jump to :modify_before_attempt_completion with the raised error as
284
+ # the {Output#error} in {InterceptorContext#output}.
285
+ #
286
+ # ## :read_after_transmit
287
+ # A hook called after the transport response message is sent to the
288
+ # service and a transport response message is received.
289
+ #
290
+ # When: This will ALWAYS be called once per attempt, except when a
291
+ # failure occurs earlier in the request pipeline. This method may be
292
+ # called multiple times in the event of retries. The duration between
293
+ # invocation of this hook and :read_before_transmit is very close to
294
+ # the amount of time spent communicating with the service. Depending on
295
+ # the protocol, the duration may not include the time spent reading the
296
+ # response data.
297
+ #
298
+ # Available Information: The {InterceptorContext#input},
299
+ # {InterceptorContext#request}, and {InterceptorContext#response}
300
+ # are ALWAYS available. Other information WILL NOT be available. In the
301
+ # event of retries, the {InterceptorContext} WILL include changes
302
+ # made in previous attempts (e.g. by other interceptors).
303
+ #
304
+ # Error Behavior: If errors are raised by this hook, execution will
305
+ # jump to :modify_before_attempt_completion with the raised error as
306
+ # the {Output#error} in {InterceptorContext#output}.
307
+ #
308
+ # ## :modify_before_deserialization
309
+ # A hook called before the transport response message is unmarshalled.
310
+ # This method has the ability to modify the transport response message.
311
+ #
312
+ # When: This will ALWAYS be called once per attempt, except when a
313
+ # failure occurs earlier in the request pipeline. This method may be
314
+ # called multiple times in the event of retries.
315
+ #
316
+ # Available Information: The {InterceptorContext#input},
317
+ # {InterceptorContext#request}, and {InterceptorContext#response}
318
+ # are ALWAYS available. The response may have been modified by earlier
319
+ # :modify_before_deserialization hooks, and may be modified further by
320
+ # later hooks. Other information WILL NOT be available. In the event of
321
+ # retries, the {InterceptorContext} WILL include changes made in
322
+ # previous attempts (e.g. by other interceptors).
323
+ #
324
+ # Error Behavior: If errors are raised by this hook, execution will
325
+ # jump to :modify_before_attempt_completion with the raised error as
326
+ # the {Output#error} in {InterceptorContext#output}.
327
+ #
328
+ # ## :read_before_deserialization
329
+ # A hook called before the transport response message is unmarshalled.
330
+ #
331
+ # When: This will ALWAYS be called once per attempt, except when a
332
+ # failure occurs earlier in the request pipeline. This method may be
333
+ # called multiple times in the event of retries. The duration between
334
+ # invocation of this hook and :read_after_deserialization is very
335
+ # close to the amount of time spent unmarshalling the service response.
336
+ # Depending on the protocol and operation, the duration may include the
337
+ # time spent downloading the response data.
338
+ #
339
+ # Available Information: The {InterceptorContext#input},
340
+ # {InterceptorContext#request}, and {InterceptorContext#response}
341
+ # are ALWAYS available. Other information WILL NOT be available. In the
342
+ # event of retries, the {InterceptorContext} WILL include changes
343
+ # made in previous attempts (e.g. by other interceptors).
344
+ #
345
+ # Error Behavior: If errors are raised by this hook, execution will
346
+ # jump to :modify_before_attempt_completion with the raised error as
347
+ # the {Output#error} in {InterceptorContext#output}.
348
+ #
349
+ # ## :read_after_deserialization
350
+ # A hook called after the transport response message is unmarshalled.
351
+ #
352
+ # When: This will ALWAYS be called once per attempt, except when a
353
+ # failure occurs earlier in the request pipeline. The duration between
354
+ # invocation of this hook and :read_before_deserialization is very
355
+ # close to the amount of time spent unmarshalling the service response.
356
+ # Depending on the protocol and operation, the duration may include the
357
+ # time spent downloading the response data.
358
+ #
359
+ # Available Information: The {InterceptorContext#input},
360
+ # {InterceptorContext#request}, {InterceptorContext#response}
361
+ # and {InterceptorContext#output} are ALWAYS available. In the event
362
+ # of retries, the {InterceptorContext} WILL include changes made
363
+ # in previous attempts (e.g. by other interceptors).
364
+ #
365
+ # Error Behavior: If errors are raised by this hook, execution will
366
+ # jump to :modify_before_attempt_completion with the raised error as
367
+ # the {Output#error} in {InterceptorContext#output}.
368
+ #
369
+ # ## :modify_before_attempt_completion
370
+ # A hook called when an attempt is completed. This method has the
371
+ # ability to modify the output message or error matching
372
+ # the currently-executing operation.
373
+ #
374
+ # When: This will ALWAYS be called once per attempt, except when a
375
+ # failure occurs before :read_before_attempt. This method may
376
+ # be called multiple times in the event of retries.
377
+ #
378
+ # Available Information: The {InterceptorContext#input},
379
+ # {InterceptorContext#request}, {InterceptorContext#response} and
380
+ # {InterceptorContext#output} are ALWAYS available. In the event
381
+ # of retries, the {InterceptorContext} WILL include changes made
382
+ # in previous attempts (e.g. by other interceptors).
383
+ #
384
+ # Error Behavior: If errors are raised by this hook, execution will
385
+ # jump to :read_after_attempt with the raised error as the
386
+ # {Output#error} in {InterceptorContext#output}.
387
+ #
388
+ # ## :read_after_attempt
389
+ # A hook called when an attempt is completed.
390
+ #
391
+ # When: This will ALWAYS be called once per attempt, as long as
392
+ # :read_before_attempt has been executed.
393
+ #
394
+ # Available Information: The {InterceptorContext#input},
395
+ # {InterceptorContext#request}, {InterceptorContext#response} and
396
+ # {InterceptorContext#output} are ALWAYS available. The
397
+ # {InterceptorContext#response} is available if a response
398
+ # was received by the service for this attempt. In the event of retries,
399
+ # the {InterceptorContext} WILL include changes made in previous
400
+ # attempts (e.g. by other interceptors).
401
+ #
402
+ # Error Behavior: Errors raised by this hook will be stored
403
+ # until all interceptors have had their :read_after_attempt invoked.
404
+ # If multiple :read_after_attempt methods raise errors, the latest
405
+ # will be used and earlier ones will be logged and dropped. If the retry
406
+ # strategy determines that {Output#error} is a retryable error, execution
407
+ # will then jump to :read_before_attempt. Otherwise, execution will jump
408
+ # to :modify_before_completion.
409
+ #
410
+ # ## :modify_before_completion
411
+ # A hook called when an execution is completed. This method has the
412
+ # ability to modify the output message or error matching
413
+ # the currently-executing operation.
414
+ #
415
+ # When: This will ALWAYS be called once per execution.
416
+ #
417
+ # Available Information: The {InterceptorContext#input} and
418
+ # {InterceptorContext#output} are ALWAYS available. The
419
+ # {InterceptorContext#request} and {InterceptorContext#response}
420
+ # are available if the execution proceeded far enough for them to be
421
+ # generated.
422
+ #
423
+ # Error Behavior: If errors are raised by this hook, execution will
424
+ # jump to :read_after_execution with the raised error as the
425
+ # {Output#error} in {InterceptorContext#output}.
426
+ #
427
+ # ## :read_after_execution
428
+ # A hook called when an execution is completed.
429
+ #
430
+ # When: This will ALWAYS be called once per execution. The duration
431
+ # between invocation of this hook and :read_before_execution is very
432
+ # close to the full duration of the execution.
433
+ #
434
+ # Available Information: The {InterceptorContext#input} and
435
+ # {InterceptorContext#output} are ALWAYS available. The
436
+ # {InterceptorContext#request} and {InterceptorContext#response}
437
+ # are available if the execution proceeded far enough for them to be
438
+ # generated.
439
+ #
440
+ # Error Behavior: Errors raised by this hook will be stored until all
441
+ # interceptors have had their :read_after_execution invoked. The
442
+ # error will then be treated as the {Output#error} raised by the
443
+ # operation. If multiple :read_after_execution methods raise
444
+ # errors, the latest will be used and earlier ones will be logged and
445
+ # dropped.
446
+ #
447
+ class Interceptor
448
+ # @api private
449
+ @hooks = [
450
+ READ_BEFORE_EXECUTION = :read_before_execution,
451
+ MODIFY_BEFORE_SERIALIZATION = :modify_before_serialization,
452
+ READ_BEFORE_SERIALIZATION = :read_before_serialization,
453
+ READ_AFTER_SERIALIZATION = :read_after_serialization,
454
+ MODIFY_BEFORE_RETRY_LOOP = :modify_before_retry_loop,
455
+ READ_BEFORE_ATTEMPT = :read_before_attempt,
456
+ MODIFY_BEFORE_SIGNING = :modify_before_signing,
457
+ READ_BEFORE_SIGNING = :read_before_signing,
458
+ READ_AFTER_SIGNING = :read_after_signing,
459
+ MODIFY_BEFORE_TRANSMIT = :modify_before_transmit,
460
+ READ_BEFORE_TRANSMIT = :read_before_transmit,
461
+ READ_AFTER_TRANSMIT = :read_after_transmit,
462
+ MODIFY_BEFORE_DESERIALIZATION = :modify_before_deserialization,
463
+ READ_BEFORE_DESERIALIZATION = :read_before_deserialization,
464
+ READ_AFTER_DESERIALIZATION = :read_after_deserialization,
465
+ MODIFY_BEFORE_ATTEMPT_COMPLETION = :modify_before_attempt_completion,
466
+ READ_AFTER_ATTEMPT = :read_after_attempt,
467
+ MODIFY_BEFORE_COMPLETION = :modify_before_completion,
468
+ READ_AFTER_EXECUTION = :read_after_execution
469
+ ]
470
+
471
+ # Creates a new Interceptor.
472
+ #
473
+ # @param [Hash<Symbol, Proc>] callbacks
474
+ # A hash of hook names to callbacks. The callbacks will be invoked
475
+ # when the hook is called. The callback will be passed the
476
+ # {InterceptorContext} for the current execution.
477
+ def initialize(callbacks = {})
478
+ @callbacks = {}
479
+
480
+ Interceptor.hooks.each do |hook|
481
+ next unless callbacks[hook]
482
+
483
+ unless valid_callback?(callbacks[hook])
484
+ raise ArgumentError,
485
+ "#{hook} must be a callable with arity 1 (context)"
486
+ end
487
+
488
+ @callbacks[hook] = callbacks[hook]
489
+ define_singleton_method(hook) do |context|
490
+ @callbacks[hook].call(context)
491
+ end
492
+ end
493
+ end
494
+
495
+ class << self
496
+ # @return [Array<Symbol>] Returns a list of all available hooks.
497
+ attr_reader :hooks
498
+ end
499
+
500
+ private
501
+
502
+ def valid_callback?(callback)
503
+ callback.respond_to?(:call) && callback.arity == 1
504
+ end
505
+ end
506
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ # Context provided to interceptor hook methods. Interceptors can use this
5
+ # context to read and modify the input, request, response, and output.
6
+ # Attributes can be used to pass additional data between interceptors.
7
+ class InterceptorContext
8
+ # @param [Struct] input
9
+ # @param [Hearth::Request] request
10
+ # @param [Hearth::Response] response
11
+ # @param [Hearth::Output] output
12
+ # @param [Hash] attributes ({}) Additional interceptor data
13
+ def initialize(input:, request:, response:, output:, attributes: {})
14
+ @input = input
15
+ @request = request
16
+ @response = response
17
+ @output = output
18
+ @attributes = attributes
19
+ end
20
+
21
+ # @return [Struct] Modeled input, i.e. Types::<Operation>Input
22
+ attr_reader :input
23
+
24
+ # @return [Hearth::Request]
25
+ attr_reader :request
26
+
27
+ # @return [Hearth::Response]
28
+ attr_reader :response
29
+
30
+ # @return [Hearth::Output] Operation output
31
+ attr_reader :output
32
+
33
+ # @return [Hash] attributes Additional interceptor data
34
+ attr_reader :attributes
35
+ end
36
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ # A list of {Hearth::Interceptor}s or classes that respond to Interceptor
5
+ # hook methods.
6
+ class InterceptorList
7
+ include Enumerable
8
+
9
+ # @param [Array<Interceptor>] interceptors ([])
10
+ def initialize(interceptors = [])
11
+ unless interceptors.respond_to?(:each)
12
+ raise ArgumentError, 'Interceptors must be an enumerable'
13
+ end
14
+
15
+ @interceptors = []
16
+ interceptors.each { |i| append(i) }
17
+ end
18
+
19
+ # @param [Interceptor] interceptor
20
+ def append(interceptor)
21
+ unless valid_interceptor?(interceptor)
22
+ raise ArgumentError,
23
+ 'Invalid interceptor - must respond to any of: ' \
24
+ "#{Interceptor.hooks.join(', ')}"
25
+ end
26
+
27
+ @interceptors << interceptor
28
+ end
29
+ alias << append
30
+
31
+ # @param [InterceptorList] other
32
+ # @return [InterceptorList] self
33
+ def concat(other)
34
+ other.each { |i| append(i) }
35
+ self
36
+ end
37
+
38
+ def each(&block)
39
+ @interceptors.each(&block)
40
+ end
41
+
42
+ private
43
+
44
+ def valid_interceptor?(interceptor)
45
+ !(interceptor.methods & Interceptor.hooks).empty?
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ # @api private
5
+ module Interceptors
6
+ # Invoke all interceptors.
7
+ #
8
+ # @param [Symbol] hook The specific hook to invoke.
9
+ # @param input [Hearth::Structure] input
10
+ # @param [Hearth::Context] context
11
+ # @param [Hearth::Output] output
12
+ # @param [Boolean] aggregate_errors (false) When true, all interceptors are
13
+ # run and only the last error is returned. When false, returns immediately
14
+ # if an error is encountered.
15
+ # @return [nil, StandardError] nil if successful, a standard error otherwise
16
+ def self.invoke(hook:, input:, context:, output:, aggregate_errors: false)
17
+ i_ctx = interceptor_context(input, context, output)
18
+ last_error = nil
19
+
20
+ context.interceptors.each do |i|
21
+ next unless i.respond_to?(hook)
22
+
23
+ log_debug(context, i, "Invoking #{hook}")
24
+ i.send(hook, i_ctx)
25
+ log_debug(context, i, "Finished #{hook}")
26
+ rescue StandardError => e
27
+ log_debug(context, i, "Error in #{hook}: #{e} (#{e.class})")
28
+ log_last_interceptor_error(last_error, i, context)
29
+ last_error = e
30
+ break unless aggregate_errors
31
+ end
32
+
33
+ log_last_output_error(last_error, context, output)
34
+ last_error
35
+ end
36
+
37
+ class << self
38
+ private
39
+
40
+ def interceptor_context(input, context, output)
41
+ Hearth::InterceptorContext.new(
42
+ input: input,
43
+ request: context.request,
44
+ response: context.response,
45
+ output: output
46
+ )
47
+ end
48
+
49
+ def log_last_interceptor_error(last_error, interceptor, context)
50
+ return unless last_error
51
+
52
+ message = "Dropping last error: #{last_error} (#{last_error.class})"
53
+ context.logger.error(
54
+ "[#{context.invocation_id}] [#{interceptor.class}] #{message}"
55
+ )
56
+ end
57
+
58
+ def log_last_output_error(last_error, context, output)
59
+ return unless last_error && output
60
+ return unless output.error
61
+
62
+ message = "Dropping last error: #{output.error} (#{output.error.class})"
63
+ context.logger.error(
64
+ "[#{context.invocation_id}] [Interceptors] #{message}"
65
+ )
66
+ end
67
+
68
+ def log_debug(context, interceptor, message)
69
+ context.logger.debug(
70
+ "[#{context.invocation_id}] [#{interceptor.class}] #{message}"
71
+ )
72
+ end
73
+ end
74
+ end
75
+ end