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,73 @@
1
+ require_relative "operation"
2
+
3
+ class Evil::Client
4
+ #
5
+ # Mutable container of definitions for sub-scopes and operations
6
+ # with DSL to configure those definitions.
7
+ #
8
+ class Schema::Scope < Schema::Operation
9
+ # Tells that this is a schema for a scope (not the final operation)
10
+ #
11
+ # @return [false]
12
+ #
13
+ def leaf?
14
+ false
15
+ end
16
+
17
+ # The collection of named sub-scope schemas
18
+ #
19
+ # Every sub-scope schema refers to the current one as a [#parent]
20
+ #
21
+ # @return [Hash<Symbol, Class>]
22
+ #
23
+ def scopes
24
+ @__children__.reject { |_, child| child.leaf? }
25
+ end
26
+
27
+ # The collection of named operation schemas
28
+ #
29
+ # @return [Hash<[Symbol, nil], Class>]
30
+ #
31
+ def operations
32
+ @__children__.select { |_, child| child.leaf? }
33
+ end
34
+
35
+ # Creates or updates sub-scope definition
36
+ #
37
+ # @param [#to_sym] name The unique name of subscope inside current scope
38
+ # @param [Proc] block The block containing definition for the subscope
39
+ # @return [self]
40
+ #
41
+ def scope(name, &block)
42
+ key = NameError.check!(name, RESERVED)
43
+ TypeError.check! self, key, :scope
44
+ @__children__[key] ||= self.class.new(self, key)
45
+ @__children__[key].instance_exec(&block)
46
+ self
47
+ end
48
+
49
+ # Creates or updates operation definition
50
+ #
51
+ # @param [#to_sym] name The unique name of operation inside the scope
52
+ # @param [Proc] block The block containing definition for the operation
53
+ # @return [self]
54
+ #
55
+ def operation(name, &block)
56
+ key = NameError.check!(name, RESERVED)
57
+ TypeError.check! self, key, :operation
58
+ @__children__[key] ||= self.class.superclass.new(self, key)
59
+ @__children__[key].instance_exec(&block)
60
+ self
61
+ end
62
+
63
+ private
64
+
65
+ def initialize(*)
66
+ super
67
+ @__children__ = {}
68
+ end
69
+
70
+ RESERVED = \
71
+ %i[operations scopes scope options schema settings inspect logger].freeze
72
+ end
73
+ end
@@ -0,0 +1,172 @@
1
+ class Evil::Client
2
+ #
3
+ # Container for settings assigned to some operation or scope.
4
+ #
5
+ class Settings
6
+ require_relative "settings/validator"
7
+
8
+ extend Dry::Initializer
9
+
10
+ class << self
11
+ # The schema klass settings belongs to
12
+ #
13
+ # @return [Class]
14
+ #
15
+ attr_reader :schema
16
+
17
+ # Only options can be defined for the settings container
18
+ # @private
19
+ def param(*args)
20
+ option(*args)
21
+ end
22
+
23
+ # Creates or updates the settings' initializer
24
+ #
25
+ # @see [http://dry-rb.org/gems/dry-initializer]
26
+ #
27
+ # @param [#to_sym] key Symbolic name of the option
28
+ # @param [#call] type Type coercer for the option
29
+ # @option opts [#call] :type Another way to assign type coercer
30
+ # @option opts [#call] :default Proc containing default value
31
+ # @option opts [Boolean] :optional Whether it can be missed
32
+ # @option opts [#to_sym] :as The name of settings variable
33
+ # @option opts [false, :private, :protected] :reader Reader method type
34
+ # @return [self]
35
+ #
36
+ def option(key, type = nil, as: key.to_sym, **opts)
37
+ NameError.check!(as, RESERVED)
38
+ super
39
+ self
40
+ end
41
+
42
+ # Creates or reloads memoized attribute
43
+ #
44
+ # @param [#to_sym] key The name of the attribute
45
+ # @param [Proc] block The body of new attribute
46
+ # @return [self]
47
+ #
48
+ def let(key, &block)
49
+ NameError.check!(key, RESERVED)
50
+ define_method(key) do
51
+ instance_variable_get(:"@#{key}") ||
52
+ instance_variable_set(:"@#{key}", instance_exec(&block))
53
+ end
54
+ self
55
+ end
56
+
57
+ # Define validator for the attribute
58
+ #
59
+ # @param [#to_sym] key The name of the attribute
60
+ # @param [Proc] block The body of new attribute
61
+ # @return [self]
62
+ #
63
+ def validate(key, &block)
64
+ validators[key] = Validator.new(@schema, key, &block)
65
+ self
66
+ end
67
+
68
+ # Collection of validators to check initialized settings
69
+ #
70
+ # @return [Hash<Symbol, Evil::Client::Validator>]
71
+ #
72
+ def validators
73
+ @validators ||= {}
74
+ end
75
+
76
+ # Human-friendly representation of settings class
77
+ #
78
+ # @return [String]
79
+ #
80
+ def name
81
+ super || @schema.to_s
82
+ end
83
+ alias_method :to_s, :name
84
+ alias_method :to_str, :name
85
+ alias_method :inspect, :name
86
+
87
+ # Builds settings with options
88
+ #
89
+ # @param [Logger, nil] logger
90
+ # @param [Hash<#to_sym, Object>, nil] opts
91
+ # @return [Evil::Client::Settings]
92
+ #
93
+ def new(logger, opts = {})
94
+ logger&.debug(self) { "initializing with options #{opts}..." }
95
+ opts = Hash(opts).each_with_object({}) { |(k, v), o| o[k.to_sym] = v }
96
+ super logger, opts
97
+ rescue => error
98
+ raise ValidationError, error.message
99
+ end
100
+
101
+ # @private
102
+ RESERVED = \
103
+ %i[options datetime scope logger basic_auth key_auth token_auth].freeze
104
+ end
105
+
106
+ # The processed hash of options contained by the instance of settings
107
+ #
108
+ # @return [Hash<Symbol, Object>]
109
+ #
110
+ def options
111
+ @__options__
112
+ end
113
+
114
+ # @!attribute logger
115
+ # @return [Logger, nil] The logger attached to current settings
116
+ attr_accessor :logger
117
+
118
+ # DSL helper to format datetimes following RFC7231/RFC2822
119
+ #
120
+ # @see https://tools.ietf.org/html/rfc7231#section-7.1.1.1
121
+ #
122
+ # @param [Date, String, nil] value Value to be formatted
123
+ # @return [String, nil]
124
+ #
125
+ def datetime(value)
126
+ return unless value
127
+
128
+ value = DateTime.parse(value) if value.is_a? String
129
+ value = value.to_datetime if value.respond_to? :to_datetime
130
+ raise "Cannot convert #{value} to DateTime" unless value.is_a?(DateTime)
131
+
132
+ value.rfc2822
133
+ end
134
+
135
+ # Human-readable representation of settings instance
136
+ #
137
+ # @return [String]
138
+ #
139
+ def inspect
140
+ number = super.match(/\>\:([^ ]+) /)[1]
141
+ params = options.map { |k, v| "@#{k}=#{v}" }.join(", ")
142
+ number ? "#<#{self.class}:#{number} #{params}>" : super
143
+ end
144
+ alias_method :to_str, :inspect
145
+ alias_method :to_s, :inspect
146
+
147
+ private
148
+
149
+ def initialize(logger, **options)
150
+ @logger = logger
151
+ super(options)
152
+
153
+ logger&.debug(self) { "initialized" }
154
+ __validate__!
155
+ end
156
+
157
+ def __validate__!
158
+ __validators__.reverse.each { |validator| validator.call(self) }
159
+ end
160
+
161
+ def __validators__
162
+ klass = self.class
163
+ [].tap do |list|
164
+ loop do
165
+ list.concat klass.validators.values
166
+ klass = klass.superclass
167
+ break if klass == Evil::Client::Settings
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,64 @@
1
+ class Evil::Client
2
+ #
3
+ # Validator to be called in context of initialized settings
4
+ #
5
+ class Settings::Validator
6
+ # Performs validation of initialized settings
7
+ #
8
+ # @param [Evil::Client::Settings]
9
+ # @return true
10
+ # @raise [Evil::Client::ValidationError] when a validation fails
11
+ #
12
+ def call(settings)
13
+ if validate!(settings, @block)
14
+ settings&.logger&.debug(self) { "passed for #{settings}" }
15
+ true
16
+ else
17
+ settings&.logger&.error(self) { "failed for #{settings}" }
18
+ raise ValidationError.new(@key, @schema, settings.options)
19
+ end
20
+ end
21
+
22
+ # Human-friendly representation of current validator
23
+ #
24
+ # @return [String]
25
+ #
26
+ def to_s
27
+ "#{@schema}.validator[:#{@key}]"
28
+ end
29
+ alias_method :to_str, :to_s
30
+ alias_method :inspect, :to_s
31
+
32
+ private
33
+
34
+ def initialize(schema, key, &block)
35
+ check_key! key
36
+ check_block! block
37
+
38
+ @schema = schema
39
+ @key = key.to_sym
40
+ @block = block
41
+ end
42
+
43
+ def check_key!(key)
44
+ message = if !key.respond_to? :to_sym
45
+ "Validator should have a symbolic name"
46
+ elsif key.empty?
47
+ "Validator name should not be empty"
48
+ end
49
+
50
+ raise ArgumentError.new(message) if message
51
+ end
52
+
53
+ def check_block!(block)
54
+ raise ArgumentError, "You should set block for validation" unless block
55
+ end
56
+
57
+ def validate!(settings, block)
58
+ settings.instance_eval(&block) && true
59
+ rescue => error
60
+ settings&.logger&.error(self) { "broken for #{settings} with #{error}" }
61
+ raise
62
+ end
63
+ end
64
+ end
data/mkdocs.yml CHANGED
@@ -1,21 +1,27 @@
1
+ # The structure of documentation at readthedocs.com
1
2
  ---
2
3
  site_name: Evil::Client
3
- site_description: Human-friendly DSL for building HTTP(s) clients in Ruby
4
+ site_description: Human-friendly DSL for building HTTP(s) clients to remote API
4
5
  repo_url: https://github.com/nepalez/evil-client
5
6
  site_author: Andrew Kozin
6
7
  theme: readthedocs
7
8
  pages:
8
- - 'Synopsis': 'index.md'
9
- - 'Details':
10
- - 'Overview': 'overview.md'
11
- - 'Model': 'model.md'
12
- - 'Settings': 'settings.md'
13
- - 'Base URL': 'base_url.md'
14
- - 'Operations':
15
- - 'HTTP method': 'http_method.md'
16
- - 'Path': 'path.md'
17
- - 'Security Definitions': 'security.md'
18
- - 'Headers': 'headers.md'
19
- - 'Query': 'query.md'
20
- - 'Responses': 'responses.md'
21
- - 'Documentation': 'documentation.md'
9
+ Synopsis: index.md
10
+ Helpers:
11
+ scope: helpers/scope.md
12
+ operation: helpers/operation.md
13
+ option: helpers/option.md
14
+ let: helpers/let.md
15
+ validate: helpers/validate.md
16
+ path: helpers/path.md
17
+ http_method: helpers/http_method.md
18
+ security: helpers/security.md
19
+ headers: helpers/headers.md
20
+ query: helpers/query.md
21
+ 'body and format': helpers/body.md
22
+ response: helpers/response.md
23
+ middleware: helpers/middleware.md
24
+ connection: helpers/connection.md
25
+ logger: helpers/logger.md
26
+ 'RSpec matcher': rspec.md
27
+ License: license.md
@@ -0,0 +1,17 @@
1
+ RSpec.describe "custom connection" do
2
+ let(:conn) { double call: response }
3
+ let(:response) { [200, { "Foo" => "Bar" }, ["Hello!"]] }
4
+ let(:params) { { subdomain: "europe", user: "andy", token: "foo" } }
5
+ let(:users) { Test::Client.new(params).crm(version: 4).users }
6
+
7
+ before do
8
+ load "spec/fixtures/test_client.rb"
9
+ Test::Client.connection = conn
10
+ end
11
+
12
+ subject { users.fetch id: 2 }
13
+
14
+ it "uses new connection" do
15
+ expect(subject).to eq response
16
+ end
17
+ end
@@ -0,0 +1,50 @@
1
+ RSpec.describe "operation request" do
2
+ before do
3
+ # Adds header tag to request/response
4
+ class Test::Middleware
5
+ extend Dry::Initializer
6
+ param :app
7
+
8
+ def call(env)
9
+ env["HTTP_Variables"].update tags
10
+ status, headers, body = app.call(env)
11
+ [status, headers.merge(tags), body]
12
+ end
13
+ end
14
+
15
+ class Foo < Test::Middleware
16
+ def tags; { "Tag" => "Foo" }; end
17
+ end
18
+
19
+ class Bar < Test::Middleware
20
+ def tags; { "Tag" => "Bar" }; end
21
+ end
22
+
23
+ load "spec/fixtures/test_client.rb"
24
+ class Test::Client < Evil::Client
25
+ middleware { Foo }
26
+
27
+ scope :crm do
28
+ middleware { Bar }
29
+ end
30
+ end
31
+
32
+ stub_request(:any, //).to_return(status: 200, body: [], headers: {})
33
+ end
34
+
35
+ subject do
36
+ Test::Client.new(subdomain: "europe", user: "andy", password: "foo")
37
+ .crm(version: 4)
38
+ .users
39
+ .fetch(id: 1)
40
+ end
41
+
42
+ it "applies middleware in a proper order" do
43
+ expected_request = \
44
+ a_request(:get, "https://europe.example.com/crm/v4/users/1")
45
+ .with headers: { "Tag" => "Bar" }
46
+
47
+ expect(subject).to eq [200, { "Tag" => "Foo" }, []]
48
+ expect(expected_request).to have_been_made
49
+ end
50
+ end
@@ -0,0 +1,71 @@
1
+ RSpec.describe "operation options" do
2
+ before { load "spec/fixtures/test_client.rb" }
3
+
4
+ let(:params) { { subdomain: "europe", user: "andy", token: "foo", foo: 0 } }
5
+ let(:users) { Test::Client.new(params).crm(version: 4).users }
6
+
7
+ shared_examples :valid_client do |details = "properly"|
8
+ it "[assigns operation options #{details}]" do
9
+ expect(subject.options).to eq options
10
+ end
11
+ end
12
+
13
+ it_behaves_like :valid_client, "with defined options" do
14
+ subject { users.operations[:fetch].new(id: 9, baz: :QUX) }
15
+ let(:options) do
16
+ { subdomain: "europe", user: "andy", token: "foo", version: 4, id: 9 }
17
+ end
18
+ end
19
+
20
+ it_behaves_like :valid_client, "with reloaded scope options" do
21
+ subject { users.operations[:fetch].new(id: 9, version: 8) }
22
+ let(:options) do
23
+ { subdomain: "europe", user: "andy", token: "foo", version: 8, id: 9 }
24
+ end
25
+ end
26
+
27
+ it_behaves_like :valid_client, "with reloaded root options" do
28
+ subject { users.operations[:fetch].new(id: 9, user: "leo") }
29
+ let(:options) do
30
+ { subdomain: "europe", user: "leo", token: "foo", version: 4, id: 9 }
31
+ end
32
+ end
33
+
34
+ it_behaves_like :valid_client, "with operation-specific options" do
35
+ subject { users.operations[:create].new(name: "Joe", language: "en") }
36
+ let(:options) do
37
+ {
38
+ subdomain: "europe",
39
+ user: "andy",
40
+ token: "foo",
41
+ version: 4,
42
+ name: "Joe",
43
+ language: "en"
44
+ }
45
+ end
46
+ end
47
+
48
+ context "when required options missed" do
49
+ subject { users.operations[:create].new(language: "it") }
50
+
51
+ it "raises Evil::Client::ValidationError" do
52
+ expect { subject }.to raise_error Evil::Client::ValidationError, /name/
53
+ end
54
+ end
55
+
56
+ context "when operation validation failed" do
57
+ subject { users.operations[:filter].new }
58
+
59
+ it "raises Evil::Client::ValidationError" do
60
+ expect { subject }.to raise_error Evil::Client::ValidationError, /id/
61
+ end
62
+ end
63
+
64
+ context "when scope validation failed" do
65
+ subject { users.operations[:fetch].new id: 8, token: nil }
66
+
67
+ it "raises Evil::Client::ValidationError" do
68
+ expect { subject }.to raise_error Evil::Client::ValidationError, /token/
69
+ end
70
+ end
71
+ end