hearth 1.0.0.pre1 → 1.0.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -4
  3. data/VERSION +1 -1
  4. data/lib/hearth/anonymous_auth_resolver.rb +11 -0
  5. data/lib/hearth/api_error.rb +15 -1
  6. data/lib/hearth/auth_option.rb +21 -0
  7. data/lib/hearth/auth_schemes/anonymous.rb +21 -0
  8. data/lib/hearth/auth_schemes/http_api_key.rb +16 -0
  9. data/lib/hearth/auth_schemes/http_basic.rb +16 -0
  10. data/lib/hearth/auth_schemes/http_bearer.rb +16 -0
  11. data/lib/hearth/auth_schemes/http_digest.rb +16 -0
  12. data/lib/hearth/auth_schemes.rb +32 -0
  13. data/lib/hearth/checksums.rb +31 -0
  14. data/lib/hearth/client.rb +66 -0
  15. data/lib/hearth/client_stubs.rb +128 -0
  16. data/lib/hearth/config/env_provider.rb +53 -0
  17. data/lib/hearth/config/resolver.rb +53 -0
  18. data/lib/hearth/configuration.rb +15 -0
  19. data/lib/hearth/connection_pool.rb +77 -0
  20. data/lib/hearth/context.rb +29 -4
  21. data/lib/hearth/dns/host_address.rb +27 -0
  22. data/lib/hearth/dns/host_resolver.rb +92 -0
  23. data/lib/hearth/dns.rb +48 -0
  24. data/lib/hearth/endpoint_rules.rb +154 -0
  25. data/lib/hearth/http/api_error.rb +4 -8
  26. data/lib/hearth/http/client.rb +206 -59
  27. data/lib/hearth/http/error_inspector.rb +85 -0
  28. data/lib/hearth/http/error_parser.rb +18 -20
  29. data/lib/hearth/http/field.rb +49 -0
  30. data/lib/hearth/http/fields.rb +117 -0
  31. data/lib/hearth/http/header_list_builder.rb +42 -0
  32. data/lib/hearth/http/header_list_parser.rb +92 -0
  33. data/lib/hearth/http/middleware/content_length.rb +7 -4
  34. data/lib/hearth/http/middleware/content_md5.rb +30 -0
  35. data/lib/hearth/http/middleware/request_compression.rb +154 -0
  36. data/lib/hearth/http/middleware.rb +12 -0
  37. data/lib/hearth/http/networking_error.rb +1 -14
  38. data/lib/hearth/http/request.rb +83 -56
  39. data/lib/hearth/http/response.rb +42 -13
  40. data/lib/hearth/http.rb +16 -5
  41. data/lib/hearth/identities/anonymous.rb +8 -0
  42. data/lib/hearth/identities/http_api_key.rb +16 -0
  43. data/lib/hearth/identities/http_bearer.rb +16 -0
  44. data/lib/hearth/identities/http_login.rb +20 -0
  45. data/lib/hearth/identities.rb +21 -0
  46. data/lib/hearth/identity_provider.rb +17 -0
  47. data/lib/hearth/interceptor.rb +506 -0
  48. data/lib/hearth/interceptor_context.rb +40 -0
  49. data/lib/hearth/interceptor_list.rb +48 -0
  50. data/lib/hearth/interceptors.rb +76 -0
  51. data/lib/hearth/json.rb +4 -4
  52. data/lib/hearth/middleware/auth.rb +103 -0
  53. data/lib/hearth/middleware/build.rb +32 -1
  54. data/lib/hearth/middleware/endpoint.rb +79 -0
  55. data/lib/hearth/middleware/host_prefix.rb +11 -8
  56. data/lib/hearth/middleware/initialize.rb +57 -0
  57. data/lib/hearth/middleware/parse.rb +45 -7
  58. data/lib/hearth/middleware/retry.rb +105 -24
  59. data/lib/hearth/middleware/send.rb +137 -26
  60. data/lib/hearth/middleware/sign.rb +65 -0
  61. data/lib/hearth/middleware/validate.rb +11 -1
  62. data/lib/hearth/middleware.rb +20 -8
  63. data/lib/hearth/middleware_stack.rb +2 -44
  64. data/lib/hearth/networking_error.rb +18 -0
  65. data/lib/hearth/number_helper.rb +3 -3
  66. data/lib/hearth/output.rb +8 -4
  67. data/lib/hearth/plugin_list.rb +53 -0
  68. data/lib/hearth/query/param.rb +56 -0
  69. data/lib/hearth/query/param_list.rb +54 -0
  70. data/lib/hearth/query/param_matcher.rb +31 -0
  71. data/lib/hearth/refreshing_identity_provider.rb +63 -0
  72. data/lib/hearth/request.rb +22 -0
  73. data/lib/hearth/response.rb +36 -0
  74. data/lib/hearth/retry/adaptive.rb +60 -0
  75. data/lib/hearth/retry/capacity_not_available_error.rb +9 -0
  76. data/lib/hearth/retry/client_rate_limiter.rb +145 -0
  77. data/lib/hearth/retry/exponential_backoff.rb +15 -0
  78. data/lib/hearth/retry/retry_quota.rb +56 -0
  79. data/lib/hearth/retry/standard.rb +46 -0
  80. data/lib/hearth/retry.rb +29 -0
  81. data/lib/hearth/signers/anonymous.rb +16 -0
  82. data/lib/hearth/signers/http_api_key.rb +29 -0
  83. data/lib/hearth/signers/http_basic.rb +23 -0
  84. data/lib/hearth/signers/http_bearer.rb +19 -0
  85. data/lib/hearth/signers/http_digest.rb +19 -0
  86. data/lib/hearth/signers.rb +23 -0
  87. data/lib/hearth/structure.rb +7 -3
  88. data/lib/hearth/stubs.rb +38 -0
  89. data/lib/hearth/time_helper.rb +6 -5
  90. data/lib/hearth/validator.rb +60 -5
  91. data/lib/hearth/waiters/poller.rb +10 -9
  92. data/lib/hearth/waiters/waiter.rb +23 -9
  93. data/lib/hearth/xml/formatter.rb +11 -2
  94. data/lib/hearth/xml/node.rb +2 -3
  95. data/lib/hearth/xml/node_matcher.rb +0 -1
  96. data/lib/hearth.rb +37 -6
  97. data/sig/lib/hearth/aliases.rbs +6 -0
  98. data/sig/lib/hearth/anonymous_auth_resolver.rbs +5 -0
  99. data/sig/lib/hearth/api_error.rbs +13 -0
  100. data/sig/lib/hearth/auth_option.rbs +11 -0
  101. data/sig/lib/hearth/auth_schemes/anonymous.rbs +7 -0
  102. data/sig/lib/hearth/auth_schemes/http_api_key.rbs +7 -0
  103. data/sig/lib/hearth/auth_schemes/http_basic.rbs +7 -0
  104. data/sig/lib/hearth/auth_schemes/http_bearer.rbs +7 -0
  105. data/sig/lib/hearth/auth_schemes/http_digest.rbs +7 -0
  106. data/sig/lib/hearth/auth_schemes.rbs +13 -0
  107. data/sig/lib/hearth/block_io.rbs +9 -0
  108. data/sig/lib/hearth/client.rbs +9 -0
  109. data/sig/lib/hearth/client_stubs.rbs +5 -0
  110. data/sig/lib/hearth/configuration.rbs +7 -0
  111. data/sig/lib/hearth/dns/host_address.rbs +11 -0
  112. data/sig/lib/hearth/dns/host_resolver.rbs +19 -0
  113. data/sig/lib/hearth/endpoint_rules.rbs +17 -0
  114. data/sig/lib/hearth/http/api_error.rbs +13 -0
  115. data/sig/lib/hearth/http/client.rbs +9 -0
  116. data/sig/lib/hearth/http/field.rbs +19 -0
  117. data/sig/lib/hearth/http/fields.rbs +43 -0
  118. data/sig/lib/hearth/http/header_list_builder.rbs +15 -0
  119. data/sig/lib/hearth/http/header_list_parser.rbs +19 -0
  120. data/sig/lib/hearth/http/networking_error.rbs +6 -0
  121. data/sig/lib/hearth/http/request.rbs +25 -0
  122. data/sig/lib/hearth/http/response.rbs +21 -0
  123. data/sig/lib/hearth/identities/anonymous.rbs +6 -0
  124. data/sig/lib/hearth/identities/http_api_key.rbs +9 -0
  125. data/sig/lib/hearth/identities/http_bearer.rbs +9 -0
  126. data/sig/lib/hearth/identities/http_login.rbs +11 -0
  127. data/sig/lib/hearth/identities.rbs +9 -0
  128. data/sig/lib/hearth/identity_provider.rbs +7 -0
  129. data/sig/lib/hearth/interceptor.rbs +9 -0
  130. data/sig/lib/hearth/interceptor_context.rbs +17 -0
  131. data/sig/lib/hearth/interceptor_list.rbs +16 -0
  132. data/sig/lib/hearth/interfaces.rbs +87 -0
  133. data/sig/lib/hearth/json/parse_error.rbs +9 -0
  134. data/sig/lib/hearth/networking_error.rbs +7 -0
  135. data/sig/lib/hearth/output.rbs +11 -0
  136. data/sig/lib/hearth/plugin_list.rbs +13 -0
  137. data/sig/lib/hearth/query/param.rbs +17 -0
  138. data/sig/lib/hearth/query/param_list.rbs +25 -0
  139. data/sig/lib/hearth/refreshing_identity_provider.rbs +10 -0
  140. data/sig/lib/hearth/request.rbs +9 -0
  141. data/sig/lib/hearth/response.rbs +11 -0
  142. data/sig/lib/hearth/retry/adaptive.rbs +13 -0
  143. data/sig/lib/hearth/retry/exponential_backoff.rbs +7 -0
  144. data/sig/lib/hearth/retry/standard.rbs +13 -0
  145. data/sig/lib/hearth/retry/strategy.rbs +11 -0
  146. data/sig/lib/hearth/retry.rbs +9 -0
  147. data/sig/lib/hearth/signers/anonymous.rbs +9 -0
  148. data/sig/lib/hearth/signers/http_api_key.rbs +9 -0
  149. data/sig/lib/hearth/signers/http_basic.rbs +9 -0
  150. data/sig/lib/hearth/signers/http_bearer.rbs +9 -0
  151. data/sig/lib/hearth/signers/http_digest.rbs +9 -0
  152. data/sig/lib/hearth/signers.rbs +9 -0
  153. data/sig/lib/hearth/structure.rbs +6 -0
  154. data/sig/lib/hearth/stubs.rbs +9 -0
  155. data/sig/lib/hearth/union.rbs +5 -0
  156. data/sig/lib/hearth/waiters/waiter.rbs +17 -0
  157. data/sig/lib/hearth/xml/parse_error.rbs +9 -0
  158. metadata +151 -25
  159. data/lib/hearth/http/headers.rb +0 -70
  160. data/lib/hearth/middleware/around_handler.rb +0 -24
  161. data/lib/hearth/middleware/request_handler.rb +0 -24
  162. data/lib/hearth/middleware/response_handler.rb +0 -25
  163. data/lib/hearth/middleware_builder.rb +0 -246
  164. data/lib/hearth/stubbing/client_stubs.rb +0 -115
  165. data/lib/hearth/stubbing/stubs.rb +0 -32
  166. data/lib/hearth/waiters/errors.rb +0 -15
  167. data/sig/lib/seahorse/api_error.rbs +0 -10
  168. data/sig/lib/seahorse/document.rbs +0 -2
  169. data/sig/lib/seahorse/http/api_error.rbs +0 -21
  170. data/sig/lib/seahorse/http/headers.rbs +0 -47
  171. data/sig/lib/seahorse/http/response.rbs +0 -21
  172. data/sig/lib/seahorse/simple_delegator.rbs +0 -3
  173. data/sig/lib/seahorse/structure.rbs +0 -18
  174. data/sig/lib/seahorse/stubbing/client_stubs.rbs +0 -103
  175. data/sig/lib/seahorse/stubbing/stubs.rbs +0 -14
  176. data/sig/lib/seahorse/union.rbs +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b43b47b8abef04f52e1f151032c376b90b3112e23c3084fbba6dd777e3f95180
4
- data.tar.gz: a902f668849d1c28d0afd66ce2c3b80eac6a5626187e3c44a1d13d99234576e0
3
+ metadata.gz: 5a6aa5be0acf82ba6ba9b2d399c702fd0da402764dd4769cc3d72a632074dbb4
4
+ data.tar.gz: f7b14b1778860d004cb9fcea31c1fb0d230760c330447136414342f37cbd3a52
5
5
  SHA512:
6
- metadata.gz: 4958477705f77ead625d1bbb49b2c11b0e44542af63bf9aaa95d87bba82a73cdbed65b40461f56e6bc9388cb612cf4cb4c341e7ba1a9b2502076ffa809e3cf14
7
- data.tar.gz: 44f0d91c07b0f431b9660f459307daebbbbe8824ad90b4009cfdff63fa139c340201ed88667f80909bbd973bdc40216c74efd4601ce9fbbfb5d3c81e2f2bed2d
6
+ metadata.gz: dd6f92a2e98ab0942ddd68a6edb86cd4e274389c4d1595ec2ff360a6c6a7a9887a5534a58271009eef0697018bf8327b5c144cc47a541e102d57a0f3fe198be2
7
+ data.tar.gz: 7ae01e2405d9d7301ea2b9f6b8805fcd438de16d498b9e394f7e0ba5452a3acc6e681b5a32e7371b3cbe9fced1984ded25e4b16bb3512d1bf282f19079490614
data/CHANGELOG.md CHANGED
@@ -1,12 +1,17 @@
1
1
  Unreleased Changes
2
2
  ------------------
3
3
 
4
- 1.0.0.pre1 (2022-01-10)
4
+ 1.0.0.pre3 (2024-05-01)
5
5
  ------------------
6
6
 
7
- * Feature - Initial public pre-release for Smithy Ruby SDKs.
7
+ * Feature - Third initial public pre-release for Smithy Ruby SDKs.
8
+
9
+ 1.0.0.pre2 (2023-12-19)
10
+ ------------------
8
11
 
9
- 0.1.0 (2013-29-04)
12
+ * Feature - Second initial public pre-release for Smithy Ruby SDKs.
13
+
14
+ 1.0.0.pre1 (2022-01-10)
10
15
  ------------------
11
16
 
12
- Not intended for public usage. Used as an internal detail of AWS SDK For Ruby v2 and v3.
17
+ * Feature - Initial public pre-release for Smithy Ruby SDKs.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0.pre1
1
+ 1.0.0.pre3
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ # Always returns the Anonymous/noAuth auth scheme.
5
+ # Can be used to effectively disable/skip auth.
6
+ class AnonymousAuthResolver
7
+ def resolve(_params)
8
+ [Hearth::AuthOption.new(scheme_id: 'smithy.api#noAuth')]
9
+ end
10
+ end
11
+ end
@@ -4,12 +4,26 @@ module Hearth
4
4
  # Base class for errors returned from an API. This excludes networking
5
5
  # errors and errors generated on the client-side.
6
6
  class ApiError < StandardError
7
- def initialize(error_code:, message: nil)
7
+ def initialize(error_code:, metadata: {}, message: nil)
8
8
  @error_code = error_code
9
+ @metadata = metadata
9
10
  super(message)
10
11
  end
11
12
 
12
13
  # @return [String]
13
14
  attr_reader :error_code
15
+
16
+ # @return [Hash]
17
+ attr_reader :metadata
18
+
19
+ # @return [Boolean]
20
+ def retryable?
21
+ false
22
+ end
23
+
24
+ # @return [Boolean]
25
+ def throttling?
26
+ false
27
+ end
14
28
  end
15
29
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ # Object that represents an auth option, returned by Auth Resolvers.
5
+ class AuthOption
6
+ def initialize(scheme_id:, identity_properties: {}, signer_properties: {})
7
+ @scheme_id = scheme_id
8
+ @identity_properties = identity_properties
9
+ @signer_properties = signer_properties
10
+ end
11
+
12
+ # @return [String]
13
+ attr_reader :scheme_id
14
+
15
+ # @return [Hash]
16
+ attr_reader :identity_properties
17
+
18
+ # @return [Hash]
19
+ attr_reader :signer_properties
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ module AuthSchemes
5
+ # Anonymous authentication scheme.
6
+ class Anonymous < AuthSchemes::Base
7
+ def initialize
8
+ super(
9
+ scheme_id: 'smithy.api#noAuth',
10
+ signer: Signers::Anonymous.new,
11
+ identity_type: Identities::Anonymous
12
+ )
13
+ end
14
+
15
+ # @return [IdentityProvider, nil]
16
+ def identity_provider(_identity_providers = {})
17
+ Hearth::IdentityProvider.new(proc { Identities::Anonymous.new })
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ module AuthSchemes
5
+ # HTTP API Key authentication scheme.
6
+ class HTTPApiKey < AuthSchemes::Base
7
+ def initialize
8
+ super(
9
+ scheme_id: 'smithy.api#httpApiKeyAuth',
10
+ signer: Signers::HTTPApiKey.new,
11
+ identity_type: Identities::HTTPApiKey
12
+ )
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ module AuthSchemes
5
+ # HTTP Basic authentication scheme.
6
+ class HTTPBasic < AuthSchemes::Base
7
+ def initialize
8
+ super(
9
+ scheme_id: 'smithy.api#httpBasicAuth',
10
+ signer: Signers::HTTPBasic.new,
11
+ identity_type: Identities::HTTPLogin
12
+ )
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ module AuthSchemes
5
+ # HTTP Bearer authentication scheme.
6
+ class HTTPBearer < AuthSchemes::Base
7
+ def initialize
8
+ super(
9
+ scheme_id: 'smithy.api#httpBearerAuth',
10
+ signer: Signers::HTTPBearer.new,
11
+ identity_type: Identities::HTTPBearer
12
+ )
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ module AuthSchemes
5
+ # HTTP Digest authentication scheme.
6
+ class HTTPDigest < AuthSchemes::Base
7
+ def initialize
8
+ super(
9
+ scheme_id: 'smithy.api#httpDigestAuth',
10
+ signer: Signers::HTTPDigest.new,
11
+ identity_type: Identities::HTTPLogin
12
+ )
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ # Namespace for all AuthScheme classes.
5
+ module AuthSchemes
6
+ # Base class for all AuthScheme classes.
7
+ class Base
8
+ def initialize(scheme_id:, signer:, identity_type:)
9
+ @scheme_id = scheme_id
10
+ @signer = signer
11
+ @identity_type = identity_type
12
+ end
13
+
14
+ # @return [String]
15
+ attr_reader :scheme_id
16
+
17
+ # @return [IdentityProvider, nil]
18
+ def identity_provider(identity_provider = {})
19
+ identity_provider[@identity_type]
20
+ end
21
+
22
+ # @return [Signers::Base]
23
+ attr_reader :signer
24
+ end
25
+ end
26
+ end
27
+
28
+ require_relative 'auth_schemes/anonymous'
29
+ require_relative 'auth_schemes/http_api_key'
30
+ require_relative 'auth_schemes/http_basic'
31
+ require_relative 'auth_schemes/http_bearer'
32
+ require_relative 'auth_schemes/http_digest'
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+
5
+ module Hearth
6
+ # A utility module for calculating checksums.
7
+ # @api private
8
+ module Checksums
9
+ CHUNK_SIZE = 1 * 1024 * 1024 # one MB
10
+
11
+ # @param [File, Tempfile, StringIO#read, String] value
12
+ # @return [String<MD5>]
13
+ def self.md5(value)
14
+ if value.is_a?(File) || value.is_a?(Tempfile)
15
+ OpenSSL::Digest.new('MD5').file(value).base64digest
16
+ elsif value.respond_to?(:read)
17
+ md5 = OpenSSL::Digest.new('MD5')
18
+ loop do
19
+ chunk = value.read(CHUNK_SIZE)
20
+ break unless chunk
21
+
22
+ md5.update(chunk)
23
+ end
24
+ value.rewind
25
+ md5.base64digest
26
+ else
27
+ OpenSSL::Digest.new('MD5').base64digest(value)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'client_stubs'
4
+
5
+ module Hearth
6
+ # Base Client class for all generated SDK clients.
7
+ class Client
8
+ include ClientStubs
9
+
10
+ # Plugins applied to all instances of this client.
11
+ # @return [Hearth::PluginList]
12
+ def self.plugins
13
+ @plugins ||= PluginList.new
14
+ end
15
+
16
+ # @param [Hash] options
17
+ # Options used to construct an instance of {Config}
18
+ # @param [Class] config_class
19
+ # The configuration class to use.
20
+ def initialize(options, config_class)
21
+ @config = initialize_config(options, config_class)
22
+ end
23
+
24
+ # @return [Configuration]
25
+ attr_reader :config
26
+
27
+ private
28
+
29
+ def initialize_config(options, config_class)
30
+ client_interceptors = options.delete(:interceptors) || []
31
+ config = config_class.new(**options)
32
+ config.validate!
33
+ self.class.plugins.each { |p| p.call(config) }
34
+ config.plugins.each { |p| p.call(config) }
35
+ config.interceptors.concat(client_interceptors)
36
+ config.validate!
37
+ config.freeze
38
+ end
39
+
40
+ def operation_config(options)
41
+ return @config if options.empty?
42
+
43
+ if options.include?(:stub_responses) || options.include?(:stubs)
44
+ msg = 'Overriding stubs or stub_responses on ' \
45
+ 'operations is not allowed'
46
+ raise ArgumentError, msg
47
+ end
48
+
49
+ operation_plugins = options.delete(:plugins)
50
+ operation_interceptors = options.delete(:interceptors) || []
51
+ config = @config.merge(options)
52
+ config.validate!
53
+ operation_plugins&.each { |p| p.call(config) }
54
+ config.interceptors.concat(operation_interceptors)
55
+ config.validate!
56
+ config.freeze
57
+ end
58
+
59
+ def output_stream(options = {}, &block)
60
+ return options.delete(:output_stream) if options[:output_stream]
61
+ return Hearth::BlockIO.new(block) if block
62
+
63
+ ::StringIO.new
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ # This module provides the ability to specify the data and/or errors to
5
+ # return when a client is using stubbed responses.
6
+ # This module should be included in generated service clients.
7
+ #
8
+ # Pass `stub_responses: true` to a Client's Config constructor
9
+ # to enable this behavior.
10
+ module ClientStubs
11
+ # Configures what data / errors should be returned from the named operation
12
+ # when response stubbing is enabled.
13
+ #
14
+ # ## Basic usage
15
+ #
16
+ # When you enable response stubbing, the client will generate fake
17
+ # responses and will not make any HTTP requests. The SDK will default
18
+ # to generate fake responses with placeholder values. You can override
19
+ # the data returned using {#stub_responses}. You can also specify errors
20
+ # (with error data) that it should raise.
21
+ #
22
+ # client = Service::Client.new(stub_responses: true)
23
+ # client.operation
24
+ # #=> #<struct Service:Types::Operation param1=[], param2=nil>
25
+ #
26
+ # You can specify the modeled stub data using the :data key.
27
+ #
28
+ # client = Service::Client.new(stub_responses: true)
29
+ # client.stub_responses(
30
+ # :operation,
31
+ # data: { param1: [{ name: 'value1' }] }
32
+ # )
33
+ # client.operation.param1.map(&:name)
34
+ # #=> ['value1']
35
+ #
36
+ # Stub data can also be provided as an output Type.
37
+ #
38
+ # client = Service::Client.new(stub_responses: true)
39
+ # output = Service::Types::OperationOutput.new(
40
+ # param1: [{ name: 'value1' }]
41
+ # )
42
+ # client.stub_responses(:operation, output)
43
+ # client.operation(param1: 'value')
44
+ # #=> #<struct Service:Types::OperationOutput ..>
45
+ #
46
+ # You can also specify modeled errors or exceptions it should raise using
47
+ # the :error key. The error hash must have a :class and optionally any
48
+ # :data to populate the error with.
49
+ #
50
+ # client = Service::Client.new(stub_responses: true)
51
+ # client.stub_responses(
52
+ # :operation,
53
+ # error: { class: ModeledError, data: { message: 'error message' } }
54
+ # )
55
+ # #=> raises ModeledError.new('error message')
56
+ #
57
+ # Constructed Exceptions will also be raised if provided.
58
+ #
59
+ # client.stub_responses(:operation, Hearth::NetworkingError.new)
60
+ # client.operation(param1: 'value')
61
+ # #=> raises Hearth::NetworkingError
62
+ #
63
+ # ## Dynamic Stubbing
64
+ #
65
+ # In addition to creating static stubs, it's also possible to generate
66
+ # stubs dynamically based on the input with which operations were
67
+ # called, by passing a `Proc` object:
68
+ #
69
+ # client.stub_responses(:operation, -> (input) {
70
+ # if input[:param] == 'foo'
71
+ # # return a data stub
72
+ # { data: { param1: [{ name: 'value1'}]} }
73
+ # else
74
+ # # return an error stub
75
+ # { error: Service::Errors::NotFound }
76
+ # end
77
+ # })
78
+ #
79
+ # ## Stubbing Raw Protocol Responses
80
+ #
81
+ # As an alternative to providing the response data, you can provide an
82
+ # instance of Hearth::Response to stub with.
83
+ #
84
+ # response = Hearth::HTTP::Response.new(
85
+ # status: 200,
86
+ # body: StringIO.new('{param1: "value1"}'),
87
+ # )
88
+ # client.stub_responses(:operation, response)
89
+ # #=> #<struct Service:Types::OperationOutput param1="value1">
90
+ #
91
+ # ## Stubbing Multiple Responses
92
+ #
93
+ # Calling an operation multiple times will return similar responses.
94
+ # You can configure multiple stubs and they will be returned in sequence.
95
+ #
96
+ # client.stub_responses(
97
+ # :operation,
98
+ # { error: Service::Errors::NotFound },
99
+ # { data: { content_length: 150 } },
100
+ # ])
101
+ #
102
+ # client.operation(param1: 'value1')
103
+ # #=> raises Service::Errors::NotFound
104
+ #
105
+ # resp = client.operation(param1: 'value2')
106
+ # resp.data.content_length #=> 150
107
+ #
108
+ # @param [Symbol] operation_name
109
+ #
110
+ # @param [Mixed] stubs One or more responses to return from the named
111
+ # operation.
112
+ #
113
+ # @return [void]
114
+ #
115
+ # @raise [RuntimeError] Raises a runtime error when called
116
+ # on a client that has not enabled response stubbing via
117
+ # `:stub_responses => true`.
118
+ def stub_responses(operation_name, *stubs)
119
+ if @config.stub_responses
120
+ @config.stubs.set_stubs(operation_name, stubs.flatten)
121
+ else
122
+ msg = 'Stubbing is not enabled. Enable stubbing in Config ' \
123
+ 'with `stub_responses: true`'
124
+ raise ArgumentError, msg
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ # @api private
5
+ module Config
6
+ # Parses and validates values provided in ENV
7
+ class EnvProvider
8
+ def initialize(env_key, type: 'String')
9
+ @env_key = env_key
10
+ @type = type
11
+ end
12
+
13
+ def call(_cfg)
14
+ return unless (value = ENV.fetch(@env_key, nil))
15
+
16
+ case @type
17
+ when 'Float' then parse_float(value)
18
+ when 'Integer' then parse_integer(value)
19
+ when 'Boolean' then parse_boolean(value)
20
+ else
21
+ value
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def parse_float(value)
28
+ Float(value)
29
+ rescue ArgumentError
30
+ raise_error(value)
31
+ end
32
+
33
+ def parse_integer(value)
34
+ Integer(value)
35
+ rescue ArgumentError
36
+ raise_error(value)
37
+ end
38
+
39
+ def parse_boolean(value)
40
+ case value.downcase
41
+ when 'true' then true
42
+ when 'false' then false
43
+ else raise_error(value)
44
+ end
45
+ end
46
+
47
+ def raise_error(value)
48
+ raise ArgumentError,
49
+ "Expected ENV['#{@env_key}'] to be a #{@type}, got #{value}."
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ # @api private
5
+ module Config
6
+ # Resolves configuration options.
7
+ class Resolver
8
+ private_class_method :new
9
+
10
+ # @param config [Struct]
11
+ # @param options [Hash]
12
+ # @param defaults [Hash<Array<Proc>>]
13
+ # @return [Struct]
14
+ def self.resolve(config, options, defaults = {})
15
+ new(config).send(:resolve, options, defaults)
16
+ end
17
+
18
+ def key(key)
19
+ @options[key] = resolve_default(key) unless @options.key?(key)
20
+ @options[key]
21
+ end
22
+ alias [] key
23
+
24
+ private
25
+
26
+ # @param config [Struct]
27
+ def initialize(config)
28
+ @config = config
29
+ end
30
+
31
+ def resolve(options, defaults)
32
+ @options = options
33
+ @defaults = defaults
34
+ @config.members.each do |key|
35
+ @config[key] = key(key)
36
+ end
37
+ end
38
+
39
+ def resolve_default(key)
40
+ @defaults[key]&.each do |default|
41
+ value =
42
+ if default.respond_to?(:call)
43
+ default.call(self)
44
+ else
45
+ default
46
+ end
47
+ return value unless value.nil?
48
+ end
49
+ nil
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ # A module mixed into Config structs that resolves default value providers.
5
+ module Configuration
6
+ def initialize(**options)
7
+ Hearth::Config::Resolver.resolve(self, options, defaults)
8
+ super
9
+ end
10
+
11
+ def merge(configuration)
12
+ self.class.new(**to_h.merge(configuration.to_h))
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hearth
4
+ # @api private
5
+ class ConnectionPool
6
+ @pools_mutex = Mutex.new
7
+ @pools = {}
8
+
9
+ class << self
10
+ # @return [ConnectionPool]
11
+ def for(config = {})
12
+ @pools_mutex.synchronize do
13
+ @pools[config] ||= new
14
+ end
15
+ end
16
+
17
+ # @return [Array<ConnectionPool>] Returns a list of the
18
+ # constructed connection pools.
19
+ def pools
20
+ @pools_mutex.synchronize do
21
+ @pools.values
22
+ end
23
+ end
24
+ end
25
+
26
+ # @api private
27
+ def initialize
28
+ @pool_mutex = Mutex.new
29
+ @pool = {}
30
+ end
31
+
32
+ # @param [URI::HTTP, URI::HTTPS] endpoint The HTTP(S) endpoint
33
+ # to connect to (e.g. 'https://domain.com').
34
+ # @param [Proc] block A block that returns a new connection if
35
+ # there are no connections present.
36
+ # @return [Connection, nil]
37
+ def connection_for(endpoint, &block)
38
+ connection = nil
39
+ endpoint = remove_path_and_query(endpoint)
40
+ # attempt to recycle an already open connection
41
+ @pool_mutex.synchronize do
42
+ clean
43
+ connection = @pool[endpoint].shift if @pool.key?(endpoint)
44
+ end
45
+ connection || (block.call if block_given?)
46
+ end
47
+
48
+ # @param [URI::HTTP, URI::HTTPS] endpoint The HTTP(S) endpoint
49
+ # @param [Object] connection The connection to check back into the pool.
50
+ # @return [nil]
51
+ def offer(endpoint, connection)
52
+ endpoint = remove_path_and_query(endpoint)
53
+ @pool_mutex.synchronize do
54
+ @pool[endpoint] = [] unless @pool.key?(endpoint)
55
+ @pool[endpoint] << connection
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ # Removes stale connections from the pool. This method *must* be called
62
+ # @note **Must** be called behind a `@pool_mutex` synchronize block.
63
+ def clean
64
+ @pool.each_pair do |_endpoint, connections|
65
+ connections.delete_if(&:stale?)
66
+ end
67
+ end
68
+
69
+ # Connection pools should be keyed by endpoint and port.
70
+ def remove_path_and_query(endpoint)
71
+ endpoint.dup.tap do |e|
72
+ e.path = ''
73
+ e.query = nil
74
+ end.to_s
75
+ end
76
+ end
77
+ end