hearth 1.0.0.pre1 → 1.0.0.pre2

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.
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