evil-client 0.3.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +0 -11
  3. data/.gitignore +1 -0
  4. data/.rspec +0 -1
  5. data/.rubocop.yml +22 -19
  6. data/.travis.yml +1 -0
  7. data/CHANGELOG.md +251 -6
  8. data/LICENSE.txt +3 -1
  9. data/README.md +47 -81
  10. data/docs/helpers/body.md +93 -0
  11. data/docs/helpers/connection.md +19 -0
  12. data/docs/helpers/headers.md +72 -0
  13. data/docs/helpers/http_method.md +39 -0
  14. data/docs/helpers/let.md +14 -0
  15. data/docs/helpers/logger.md +24 -0
  16. data/docs/helpers/middleware.md +56 -0
  17. data/docs/helpers/operation.md +103 -0
  18. data/docs/helpers/option.md +50 -0
  19. data/docs/helpers/path.md +37 -0
  20. data/docs/helpers/query.md +59 -0
  21. data/docs/helpers/response.md +40 -0
  22. data/docs/helpers/scope.md +121 -0
  23. data/docs/helpers/security.md +102 -0
  24. data/docs/helpers/validate.md +68 -0
  25. data/docs/index.md +70 -78
  26. data/docs/license.md +5 -1
  27. data/docs/rspec.md +96 -0
  28. data/evil-client.gemspec +10 -8
  29. data/lib/evil/client.rb +126 -72
  30. data/lib/evil/client/builder.rb +47 -0
  31. data/lib/evil/client/builder/operation.rb +40 -0
  32. data/lib/evil/client/builder/scope.rb +31 -0
  33. data/lib/evil/client/chaining.rb +17 -0
  34. data/lib/evil/client/connection.rb +60 -20
  35. data/lib/evil/client/container.rb +66 -0
  36. data/lib/evil/client/container/operation.rb +23 -0
  37. data/lib/evil/client/container/scope.rb +28 -0
  38. data/lib/evil/client/exceptions/definition_error.rb +15 -0
  39. data/lib/evil/client/exceptions/name_error.rb +32 -0
  40. data/lib/evil/client/exceptions/response_error.rb +42 -0
  41. data/lib/evil/client/exceptions/type_error.rb +29 -0
  42. data/lib/evil/client/exceptions/validation_error.rb +27 -0
  43. data/lib/evil/client/formatter.rb +49 -0
  44. data/lib/evil/client/formatter/form.rb +45 -0
  45. data/lib/evil/client/formatter/multipart.rb +33 -0
  46. data/lib/evil/client/formatter/part.rb +66 -0
  47. data/lib/evil/client/formatter/text.rb +21 -0
  48. data/lib/evil/client/resolver.rb +84 -0
  49. data/lib/evil/client/resolver/body.rb +22 -0
  50. data/lib/evil/client/resolver/format.rb +30 -0
  51. data/lib/evil/client/resolver/headers.rb +46 -0
  52. data/lib/evil/client/resolver/http_method.rb +34 -0
  53. data/lib/evil/client/resolver/middleware.rb +36 -0
  54. data/lib/evil/client/resolver/query.rb +39 -0
  55. data/lib/evil/client/resolver/request.rb +96 -0
  56. data/lib/evil/client/resolver/response.rb +26 -0
  57. data/lib/evil/client/resolver/security.rb +113 -0
  58. data/lib/evil/client/resolver/uri.rb +35 -0
  59. data/lib/evil/client/rspec.rb +127 -0
  60. data/lib/evil/client/schema.rb +105 -0
  61. data/lib/evil/client/schema/operation.rb +177 -0
  62. data/lib/evil/client/schema/scope.rb +73 -0
  63. data/lib/evil/client/settings.rb +172 -0
  64. data/lib/evil/client/settings/validator.rb +64 -0
  65. data/mkdocs.yml +21 -15
  66. data/spec/features/custom_connection_spec.rb +17 -0
  67. data/spec/features/operation/middleware_spec.rb +50 -0
  68. data/spec/features/operation/options_spec.rb +71 -0
  69. data/spec/features/operation/request_spec.rb +94 -0
  70. data/spec/features/operation/response_spec.rb +48 -0
  71. data/spec/features/scope/options_spec.rb +52 -0
  72. data/spec/fixtures/locales/en.yml +16 -0
  73. data/spec/fixtures/test_client.rb +76 -0
  74. data/spec/spec_helper.rb +18 -6
  75. data/spec/support/fixtures_helper.rb +7 -0
  76. data/spec/unit/builder/operation_spec.rb +90 -0
  77. data/spec/unit/builder/scope_spec.rb +84 -0
  78. data/spec/unit/client_spec.rb +137 -0
  79. data/spec/unit/connection_spec.rb +78 -0
  80. data/spec/unit/container/operation_spec.rb +81 -0
  81. data/spec/unit/container/scope_spec.rb +61 -0
  82. data/spec/unit/container_spec.rb +107 -0
  83. data/spec/unit/exceptions/definition_error_spec.rb +15 -0
  84. data/spec/unit/exceptions/name_error_spec.rb +77 -0
  85. data/spec/unit/exceptions/response_error_spec.rb +22 -0
  86. data/spec/unit/exceptions/type_error_spec.rb +71 -0
  87. data/spec/unit/exceptions/validation_error_spec.rb +13 -0
  88. data/spec/unit/formatter/form_spec.rb +27 -0
  89. data/spec/unit/formatter/multipart_spec.rb +23 -0
  90. data/spec/unit/formatter/part_spec.rb +49 -0
  91. data/spec/unit/formatter/text_spec.rb +37 -0
  92. data/spec/unit/formatter_spec.rb +46 -0
  93. data/spec/unit/resolver/body_spec.rb +65 -0
  94. data/spec/unit/resolver/format_spec.rb +66 -0
  95. data/spec/unit/resolver/headers_spec.rb +93 -0
  96. data/spec/unit/resolver/http_method_spec.rb +67 -0
  97. data/spec/unit/resolver/middleware_spec.rb +83 -0
  98. data/spec/unit/resolver/query_spec.rb +85 -0
  99. data/spec/unit/resolver/request_spec.rb +121 -0
  100. data/spec/unit/resolver/response_spec.rb +64 -0
  101. data/spec/unit/resolver/security_spec.rb +156 -0
  102. data/spec/unit/resolver/uri_spec.rb +117 -0
  103. data/spec/unit/rspec_spec.rb +342 -0
  104. data/spec/unit/schema/operation_spec.rb +309 -0
  105. data/spec/unit/schema/scope_spec.rb +110 -0
  106. data/spec/unit/schema_spec.rb +157 -0
  107. data/spec/unit/settings/validator_spec.rb +128 -0
  108. data/spec/unit/settings_spec.rb +248 -0
  109. metadata +192 -135
  110. data/docs/base_url.md +0 -38
  111. data/docs/documentation.md +0 -9
  112. data/docs/headers.md +0 -59
  113. data/docs/http_method.md +0 -31
  114. data/docs/model.md +0 -173
  115. data/docs/operation.md +0 -0
  116. data/docs/overview.md +0 -0
  117. data/docs/path.md +0 -48
  118. data/docs/query.md +0 -99
  119. data/docs/responses.md +0 -66
  120. data/docs/security.md +0 -102
  121. data/docs/settings.md +0 -32
  122. data/lib/evil/client/connection/net_http.rb +0 -57
  123. data/lib/evil/client/dsl.rb +0 -127
  124. data/lib/evil/client/dsl/base.rb +0 -26
  125. data/lib/evil/client/dsl/files.rb +0 -37
  126. data/lib/evil/client/dsl/headers.rb +0 -16
  127. data/lib/evil/client/dsl/http_method.rb +0 -24
  128. data/lib/evil/client/dsl/operation.rb +0 -91
  129. data/lib/evil/client/dsl/operations.rb +0 -41
  130. data/lib/evil/client/dsl/path.rb +0 -25
  131. data/lib/evil/client/dsl/query.rb +0 -16
  132. data/lib/evil/client/dsl/response.rb +0 -61
  133. data/lib/evil/client/dsl/responses.rb +0 -29
  134. data/lib/evil/client/dsl/scope.rb +0 -27
  135. data/lib/evil/client/dsl/security.rb +0 -57
  136. data/lib/evil/client/dsl/verifier.rb +0 -35
  137. data/lib/evil/client/middleware.rb +0 -81
  138. data/lib/evil/client/middleware/base.rb +0 -11
  139. data/lib/evil/client/middleware/merge_security.rb +0 -20
  140. data/lib/evil/client/middleware/normalize_headers.rb +0 -17
  141. data/lib/evil/client/middleware/stringify_form.rb +0 -40
  142. data/lib/evil/client/middleware/stringify_json.rb +0 -19
  143. data/lib/evil/client/middleware/stringify_multipart.rb +0 -36
  144. data/lib/evil/client/middleware/stringify_multipart/part.rb +0 -36
  145. data/lib/evil/client/middleware/stringify_query.rb +0 -35
  146. data/lib/evil/client/operation.rb +0 -34
  147. data/lib/evil/client/operation/request.rb +0 -26
  148. data/lib/evil/client/operation/response.rb +0 -39
  149. data/lib/evil/client/operation/response_error.rb +0 -13
  150. data/lib/evil/client/operation/unexpected_response_error.rb +0 -19
  151. data/spec/features/instantiation_spec.rb +0 -68
  152. data/spec/features/middleware_spec.rb +0 -79
  153. data/spec/features/operation_with_documentation_spec.rb +0 -41
  154. data/spec/features/operation_with_files_spec.rb +0 -40
  155. data/spec/features/operation_with_form_body_spec.rb +0 -158
  156. data/spec/features/operation_with_headers_spec.rb +0 -99
  157. data/spec/features/operation_with_http_method_spec.rb +0 -45
  158. data/spec/features/operation_with_json_body_spec.rb +0 -156
  159. data/spec/features/operation_with_nested_responses_spec.rb +0 -95
  160. data/spec/features/operation_with_path_spec.rb +0 -47
  161. data/spec/features/operation_with_query_spec.rb +0 -84
  162. data/spec/features/operation_with_security_spec.rb +0 -228
  163. data/spec/features/scoping_spec.rb +0 -48
  164. data/spec/support/test_client.rb +0 -15
  165. data/spec/unit/evil/client/connection/net_http_spec.rb +0 -38
  166. data/spec/unit/evil/client/dsl/files_spec.rb +0 -37
  167. data/spec/unit/evil/client/dsl/operation_spec.rb +0 -374
  168. data/spec/unit/evil/client/dsl/operations_spec.rb +0 -29
  169. data/spec/unit/evil/client/dsl/scope_spec.rb +0 -32
  170. data/spec/unit/evil/client/dsl/security_spec.rb +0 -135
  171. data/spec/unit/evil/client/middleware/merge_security_spec.rb +0 -32
  172. data/spec/unit/evil/client/middleware/normalize_headers_spec.rb +0 -17
  173. data/spec/unit/evil/client/middleware/stringify_form_spec.rb +0 -63
  174. data/spec/unit/evil/client/middleware/stringify_json_spec.rb +0 -61
  175. data/spec/unit/evil/client/middleware/stringify_multipart/part_spec.rb +0 -59
  176. data/spec/unit/evil/client/middleware/stringify_multipart_spec.rb +0 -62
  177. data/spec/unit/evil/client/middleware/stringify_query_spec.rb +0 -40
  178. data/spec/unit/evil/client/middleware_spec.rb +0 -46
  179. data/spec/unit/evil/client/operation/request_spec.rb +0 -49
  180. data/spec/unit/evil/client/operation/response_spec.rb +0 -63
@@ -0,0 +1,33 @@
1
+ module Evil::Client::Formatter
2
+ #
3
+ # Utility module to format file (IO) as a part of multipart body
4
+ #
5
+ # @example
6
+ # Evil::Client::Formatter::Form.call foo: { bar: :baz }
7
+ # # => "foo[bar]=baz"
8
+ #
9
+ module Multipart
10
+ extend self
11
+ require_relative "part"
12
+
13
+ # Formats nested hash as a string
14
+ #
15
+ # @param [Array<IO>] value
16
+ # @option opts [String] :boundary
17
+ # @return [String]
18
+ #
19
+ def call(*sources, boundary:, **)
20
+ parts = sources.flatten.map.with_index(1) do |src, num|
21
+ "--#{boundary}\r\n#{part(src, num)}"
22
+ end
23
+
24
+ [nil, nil, parts, "--#{boundary}--", nil].join("\r\n")
25
+ end
26
+
27
+ private
28
+
29
+ def part(source, index)
30
+ Part.call(source, index)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,66 @@
1
+ module Evil::Client::Formatter
2
+ #
3
+ # Utility module to format source as a part of multipart body
4
+ #
5
+ module Part
6
+ extend self
7
+ require_relative "form"
8
+
9
+ # Formats nested hash as a string
10
+ #
11
+ # @param [IO, #to_s] source
12
+ # @param [Integer] number
13
+ # @return [String]
14
+ #
15
+ def call(source, number)
16
+ filename = extract_filename(source)
17
+ name = extract_name(filename, number)
18
+ path = Pathname.new(filename) if filename
19
+ content = extract_content(source)
20
+ mime = extract_mime(path)
21
+ charset = extract_charset(content)
22
+ headers = [disposition(name, filename), type(mime, charset), nil]
23
+
24
+ [*headers, content].join("\r\n")
25
+ end
26
+
27
+ private
28
+
29
+ def disposition(name, filename)
30
+ "Content-Disposition: form-data; name=\"#{name}\"".tap do |line|
31
+ line << "; filename=\"#{filename}\"" if filename
32
+ end
33
+ end
34
+
35
+ def type(mime, charset)
36
+ "Content-Type: #{mime}; charset=#{charset}"
37
+ end
38
+
39
+ def extract_name(filename, number)
40
+ filename ? filename : "Part#{number}"
41
+ end
42
+
43
+ def extract_content(source)
44
+ case source
45
+ when File, Tempfile then source.read
46
+ when StringIO then source.string
47
+ when Hash then Form.call(source)
48
+ else source.to_s
49
+ end
50
+ end
51
+
52
+ def extract_filename(source)
53
+ case source
54
+ when File, Tempfile then Pathname.new(source.path).basename.to_s
55
+ end
56
+ end
57
+
58
+ def extract_mime(path)
59
+ MIME::Types.type_for(path&.extname.to_s).first || "text/plain"
60
+ end
61
+
62
+ def extract_charset(content)
63
+ content.encoding.to_s.downcase
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,21 @@
1
+ module Evil::Client::Formatter
2
+ #
3
+ # Utility module to format data as a single text
4
+ #
5
+ module Text
6
+ extend self
7
+
8
+ # Formats data as a text
9
+ #
10
+ # @param [Object] source
11
+ # @return [String]
12
+ #
13
+ def call(source)
14
+ case source
15
+ when File, Tempfile then source.read
16
+ when StringIO then source.string
17
+ else source.to_s
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,84 @@
1
+ class Evil::Client
2
+ #
3
+ # @abstract
4
+ # Base class for resolvers of schema definition for a particular settings.
5
+ #
6
+ # Its every subclass responds for resolving a specific part of schema
7
+ # (request, middleware or response).
8
+ #
9
+ class Resolver
10
+ # Loads concrete implementation of the abstract resolver
11
+ require_relative "resolver/request"
12
+ require_relative "resolver/middleware"
13
+ require_relative "resolver/response"
14
+
15
+ # Builds and calls one-off resolver at once
16
+ #
17
+ # @param [Object] args The arguments of current class' constructor
18
+ # @return [Object] resolved definition
19
+ #
20
+ def self.call(*args)
21
+ new(*args).send(:__call__)
22
+ end
23
+
24
+ # Human-friendly representation of the resolver
25
+ #
26
+ # @return [String]
27
+ #
28
+ def to_s
29
+ "#{@__keys__.join(' ')} from #{@__schema__} for #{@__settings__}"
30
+ end
31
+ alias_method :to_str, :to_s
32
+ alias_method :inspect, :to_s
33
+
34
+ private
35
+
36
+ def initialize(schema, settings, *keys)
37
+ @__schema__ = schema
38
+ @__settings__ = settings
39
+ @__keys__ = keys
40
+ end
41
+
42
+ def __call__
43
+ logger = @__settings__.logger
44
+ yield.tap do |obj|
45
+ logger&.debug(self.class) { "resolved #{self} to #{obj.inspect}" }
46
+ end
47
+ rescue => err
48
+ logger&.error(self.class) { "failed to resolve #{self}: #{err.message}" }
49
+ raise
50
+ end
51
+
52
+ def __blocks__
53
+ @__blocks__ ||= [].tap do |blocks|
54
+ schema = @__schema__
55
+ loop do
56
+ break unless schema
57
+ block = schema.definitions.dig(*@__keys__)
58
+ schema = schema.parent
59
+ blocks.unshift block if block
60
+ end
61
+ end
62
+ end
63
+
64
+ def __definition_error__(text)
65
+ DefinitionError.new(@__schema__, @__keys__, @__settings__, text)
66
+ end
67
+
68
+ def __symbolize_keys__(hash)
69
+ hash.each_with_object({}) { |(key, val), obj| obj[key.to_sym] = val }
70
+ end
71
+
72
+ def __stringify_keys__(hash)
73
+ hash.each_with_object({}) { |(key, val), obj| obj[key.to_s] = val }
74
+ end
75
+
76
+ def respond_to_missing?(name, *)
77
+ @__settings__.respond_to? name
78
+ end
79
+
80
+ def method_missing(*args, &block)
81
+ respond_to_missing?(*args) ? @__settings__.send(*args, &block) : super
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,22 @@
1
+ class Evil::Client
2
+ #
3
+ # Resolves body of the request from operation schema for given settings.
4
+ #
5
+ # It uses last (nested) definition without any coercion or validation.
6
+ # Formatting and validation is made later by [Evil::Client::Resolver#__call__]
7
+ # because it depends from both :body and :format definitions.
8
+ #
9
+ # @private
10
+ #
11
+ class Resolver::Body < Resolver
12
+ private
13
+
14
+ def initialize(schema, settings)
15
+ super(schema, settings, :body)
16
+ end
17
+
18
+ def __call__
19
+ super { instance_exec(&__blocks__.last) if __blocks__.any? }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ class Evil::Client
2
+ #
3
+ # Resolves request format from operation settings and schema
4
+ # @private
5
+ #
6
+ class Resolver::Format < Resolver
7
+ private
8
+
9
+ def initialize(schema, settings)
10
+ super schema, settings, :format
11
+ end
12
+
13
+ def __call__
14
+ super do
15
+ value = instance_exec(&__blocks__.last)&.to_sym if __blocks__.any?
16
+ value = :json if value.to_s == ""
17
+ raise __invalid_error__(value) unless LIST.include? value
18
+ value
19
+ end
20
+ end
21
+
22
+ def __invalid_error__(value)
23
+ __definition_error__ "Format :#{value} not supported." \
24
+ " Use one of the following formats:" \
25
+ " :#{LIST.join(', :')}."
26
+ end
27
+
28
+ LIST = %i[json yaml form text multipart].freeze
29
+ end
30
+ end
@@ -0,0 +1,46 @@
1
+ class Evil::Client
2
+ #
3
+ # Resolves headers of the request from operation settings and schema
4
+ # by merging headers defined by schema and all its parents.
5
+ # @private
6
+ #
7
+ class Resolver::Headers < Resolver
8
+ private
9
+
10
+ def initialize(schema, settings)
11
+ super schema, settings, :headers
12
+ end
13
+
14
+ def __call__
15
+ super do
16
+ __blocks__.map { |block| __normalize__ instance_exec(&block) }
17
+ .reduce({}, :merge)
18
+ .reject { |_, value| value&.empty? }
19
+ end
20
+ end
21
+
22
+ def __normalize__(headers)
23
+ __check__(headers)
24
+ keys = __extract_keys__(headers)
25
+ values = __extract_values__(headers)
26
+ keys.zip(values).to_h
27
+ end
28
+
29
+ def __check__(data)
30
+ raise __definition_error__ "#{data} is not a hash" unless data.is_a? Hash
31
+ end
32
+
33
+ def __extract_keys__(data)
34
+ keys = data.keys.map(&:to_s)
35
+ wrong = keys.reject { |key| key[VALID_KEY] }.map(&:inspect)
36
+ return keys unless wrong.any?
37
+ raise __definition_error__ "inacceptable headers #{wrong.join(', ')}"
38
+ end
39
+
40
+ def __extract_values__(data)
41
+ data.values.map { |v| v.respond_to?(:map) ? v.map(&:to_s) : v.to_s }
42
+ end
43
+
44
+ VALID_KEY = /^.+$/
45
+ end
46
+ end
@@ -0,0 +1,34 @@
1
+ class Evil::Client
2
+ #
3
+ # Resolves a http_method for the request from operation settings and schema
4
+ # @private
5
+ #
6
+ class Resolver::HttpMethod < Resolver
7
+ private
8
+
9
+ def initialize(schema, settings)
10
+ super schema, settings, :http_method
11
+ end
12
+
13
+ def __call__
14
+ super do
15
+ value = instance_exec(&__blocks__.last)&.to_s&.upcase if __blocks__.any?
16
+ raise __not_defined_error__ if value.to_s == ""
17
+ raise __invalid_error__(value) unless LIST.include? value
18
+ value
19
+ end
20
+ end
21
+
22
+ def __not_defined_error__
23
+ __definition_error__ "HTTP method not defined"
24
+ end
25
+
26
+ def __invalid_error__(value)
27
+ __definition_error__ "Unknown HTTP method #{value}"
28
+ end
29
+
30
+ # @see https://tools.ietf.org/html/rfc7231#section-4
31
+ # @see https://tools.ietf.org/html/rfc5789#section-2
32
+ LIST = %w[GET POST PUT PATCH DELETE OPTIONS HEAD TRACE CONNECT].freeze
33
+ end
34
+ end
@@ -0,0 +1,36 @@
1
+ class Evil::Client
2
+ #
3
+ # Resolves scope/operation-specific middleware from schema for some settings
4
+ #
5
+ # New middleware are added to previously defined ones.
6
+ # To reset all predefined middleware, set value to nil.
7
+ #
8
+ # @private
9
+ #
10
+ class Resolver::Middleware < Resolver
11
+ private
12
+
13
+ def initialize(schema, settings)
14
+ super schema, settings, :middleware
15
+ end
16
+
17
+ def __call__
18
+ super do
19
+ __blocks__.map.with_object([]) do |block, obj|
20
+ list = __normalize__ instance_exec(&block)
21
+ obj.replace([]) unless list
22
+ obj.concat Array(list)
23
+ end.reverse
24
+ end
25
+ end
26
+
27
+ def __normalize__(value)
28
+ case value
29
+ when nil then nil
30
+ when Class then value
31
+ when Array then value.flatten.compact.map { |val| __normalize__(val) }
32
+ else raise __definition_error__("#{value} is neither class nor array")
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ class Evil::Client
2
+ #
3
+ # Resolves query of the request from operation settings and schema
4
+ # by deeply merging queries defined by schema and all its parents.
5
+ #
6
+ # @private
7
+ #
8
+ class Resolver::Query < Resolver
9
+ private
10
+
11
+ def initialize(schema, settings)
12
+ super schema, settings, :query
13
+ end
14
+
15
+ def __call__
16
+ super do
17
+ Hash __blocks__
18
+ .map { |block| __normalize__ instance_exec(&block) }
19
+ .reduce({}) { |left, right| __deep_merge__(left, right) }
20
+ end
21
+ end
22
+
23
+ def __normalize__(data)
24
+ return if data.nil?
25
+ raise __definition_error__("#{data} is not a hash") unless data.is_a? Hash
26
+ __stringify_keys__(data)
27
+ end
28
+
29
+ def __deep_merge__(left, right)
30
+ return right unless left.is_a?(Hash) && right.is_a?(Hash)
31
+
32
+ left = __stringify_keys__(left)
33
+ right = __stringify_keys__(right)
34
+ right.keys.each { |key| left[key] = __deep_merge__ left[key], right[key] }
35
+
36
+ left
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,96 @@
1
+ class Evil::Client
2
+ #
3
+ # Resolves request schema for given settings to the minimal Rack environment
4
+ #
5
+ # @return [Hash<String, Object>]
6
+ # @private
7
+ #
8
+ class Resolver::Request < Resolver
9
+ require_relative "uri"
10
+ require_relative "http_method"
11
+ require_relative "security"
12
+ require_relative "headers"
13
+ require_relative "query"
14
+ require_relative "body"
15
+ require_relative "format"
16
+
17
+ private
18
+
19
+ def initialize(schema, settings)
20
+ super schema, settings, :request
21
+ end
22
+
23
+ def __call__
24
+ super { environment }
25
+ end
26
+
27
+ # rubocop: disable Metrics/MethodLength
28
+ # rubocop: disable Metrics/AbcSize
29
+ def environment
30
+ {
31
+ "REQUEST_METHOD" => http_method,
32
+ "PATH_INFO" => uri.path,
33
+ "SCRIPT_NAME" => "",
34
+ "QUERY_STRING" => Formatter.call(query, :form),
35
+ "SERVER_NAME" => uri.host,
36
+ "SERVER_PORT" => uri.port,
37
+ "HTTP_Variables" => headers,
38
+ "rack.version" => Rack::VERSION,
39
+ "rack.url_scheme" => uri.scheme,
40
+ "rack.input" => Formatter.call(body, format, boundary: boundary),
41
+ "rack.multithread" => false,
42
+ "rack.multiprocess" => false,
43
+ "rack.run_once" => false,
44
+ "rack.hijack?" => false,
45
+ "rack.logger" => @__settings__&.logger
46
+ }
47
+ end
48
+ # rubocop: enable Metrics/MethodLength
49
+ # rubocop: enable Metrics/AbcSize
50
+
51
+ def uri
52
+ @uri ||= Resolver::Uri.call(@__schema__, @__settings__)
53
+ end
54
+
55
+ def http_method
56
+ @http_method ||= Resolver::HttpMethod.call(@__schema__, @__settings__)
57
+ end
58
+
59
+ def format
60
+ @format ||= Resolver::Format.call(@__schema__, @__settings__)
61
+ end
62
+
63
+ def security
64
+ @security ||= Resolver::Security.call(@__schema__, @__settings__)
65
+ end
66
+
67
+ def headers
68
+ @headers ||= Resolver::Headers.call(@__schema__, @__settings__)
69
+ .merge(security.fetch(:headers, {}))
70
+ .merge("Content-Type" => content_type)
71
+ end
72
+
73
+ def query
74
+ @query ||= Resolver::Query.call(@__schema__, @__settings__)
75
+ .merge(security.fetch(:query, {}))
76
+ end
77
+
78
+ def body
79
+ @body ||= Resolver::Body.call(@__schema__, @__settings__)
80
+ end
81
+
82
+ def boundary
83
+ @boundary ||= SecureRandom.hex(10)
84
+ end
85
+
86
+ def content_type
87
+ case format
88
+ when :text then "text/plain"
89
+ when :json then "application/json"
90
+ when :yaml then "application/yaml"
91
+ when :form then "application/x-www-form-urlencoded"
92
+ when :multipart then "multipart/form-data; boundary=#{boundary}"
93
+ end
94
+ end
95
+ end
96
+ end