evil-client 0.3.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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