scorpio 0.4.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Scorpio
2
4
  module OpenAPI
5
+ class Error < StandardError
6
+ end
3
7
  # an error in the semantics of an openapi document. for example, an Operation with
4
8
  # two body parameters (in v2, not possible in v3) is a SemanticError. an Operation
5
9
  # with more than one parameter with the same 'name' and 'in' properties would also be
@@ -7,172 +11,219 @@ module Scorpio
7
11
  #
8
12
  # an instance of a SemanticError may or may not correspond to a validation error of
9
13
  # an OpenAPI document against the OpenAPI schema.
10
- class SemanticError
14
+ class SemanticError < Error
11
15
  end
12
16
 
13
17
  autoload :Operation, 'scorpio/openapi/operation'
14
18
  autoload :Document, 'scorpio/openapi/document'
19
+ autoload :Reference, 'scorpio/openapi/reference'
20
+ autoload :Tag, 'scorpio/openapi/tag'
15
21
  autoload :OperationsScope, 'scorpio/openapi/operations_scope'
16
22
 
17
23
  module V3
18
- openapi_schema = JSI::Schema.new(::YAML.load_file(Scorpio.root.join('documents/github.com/OAI/OpenAPI-Specification/blob/oas3-schema/schemas/v3.0/schema.yaml')))
19
- openapi_class = proc do |*key|
20
- JSI.class_for_schema(key.inject(openapi_schema, &:[]))
21
- end
24
+ openapi_document_schema = JSI::JSONSchemaOrgDraft04.new_schema(::YAML.load_file(Scorpio.root.join(
25
+ 'documents/github.com/OAI/OpenAPI-Specification/blob/oas3-schema/schemas/v3.0/schema.yaml'
26
+ )))
22
27
 
23
- Document = openapi_class.call()
28
+ # the schema represented by Scorpio::OpenAPI::V3::Schema will describe schemas itself, so we set it
29
+ # include on its schema module the jsi_schema_instance_modules that implement schema functionality.
30
+ describe_schema = [
31
+ openapi_document_schema.definitions['Schema'],
32
+ openapi_document_schema.definitions['SchemaReference'],
33
+ # instead of the Schema definition allowing boolean, properties['additionalProperties']
34
+ # is a oneOf which allows a Schema, SchemaReference, or boolean.
35
+ # instances of the former two already include the schema implementation (per the previous
36
+ # describes_schema entries), but the boolean does not.
37
+ # including in properties['additionalProperties'] applies to any additionalProperties.
38
+ # (including in properties['additionalProperties'].anyOf[2] would extend booleans too, without
39
+ # the redundant inclusion that results for Schema and SchemaRef, but redundant inclusion is not
40
+ # a problem, and this way also applies when none of the anyOf match due to schema errors.)
41
+ openapi_document_schema.definitions['Schema'].properties['additionalProperties'],
42
+ ]
43
+ describe_schema.each { |s| s.jsi_schema_instance_modules = [JSI::Schema::Draft04] }
44
+
45
+ Document = openapi_document_schema.jsi_schema_module
24
46
 
25
47
  # naming these is not strictly necessary, but is nice to have.
26
- # generated: `puts JSI::Schema.new(::YAML.load_file(Scorpio.root.join('documents/github.com/OAI/OpenAPI-Specification/blob/oas3-schema/schemas/v3.0/schema.yaml')))['definitions'].select { |k,v| ['object', nil].include?(v['type']) }.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = openapi_class.call('definitions', '#{k}')" }`
27
- Reference = openapi_class.call('definitions', 'Reference')
28
- Info = openapi_class.call('definitions', 'Info')
29
- Contact = openapi_class.call('definitions', 'Contact')
30
- License = openapi_class.call('definitions', 'License')
31
- Server = openapi_class.call('definitions', 'Server')
32
- ServerVariable = openapi_class.call('definitions', 'ServerVariable')
33
- Components = openapi_class.call('definitions', 'Components')
34
- Schema = openapi_class.call('definitions', 'Schema')
35
- Discriminator = openapi_class.call('definitions', 'Discriminator')
36
- XML = openapi_class.call('definitions', 'XML')
37
- Response = openapi_class.call('definitions', 'Response')
38
- MediaType = openapi_class.call('definitions', 'MediaType')
39
- MediaTypeWithExample = openapi_class.call('definitions', 'MediaTypeWithExample')
40
- MediaTypeWithExamples = openapi_class.call('definitions', 'MediaTypeWithExamples')
41
- Example = openapi_class.call('definitions', 'Example')
42
- Header = openapi_class.call('definitions', 'Header')
43
- HeaderWithSchema = openapi_class.call('definitions', 'HeaderWithSchema')
44
- HeaderWithSchemaWithExample = openapi_class.call('definitions', 'HeaderWithSchemaWithExample')
45
- HeaderWithSchemaWithExamples = openapi_class.call('definitions', 'HeaderWithSchemaWithExamples')
46
- HeaderWithContent = openapi_class.call('definitions', 'HeaderWithContent')
47
- Paths = openapi_class.call('definitions', 'Paths')
48
- PathItem = openapi_class.call('definitions', 'PathItem')
49
- Operation = openapi_class.call('definitions', 'Operation')
50
- Responses = openapi_class.call('definitions', 'Responses')
51
- SecurityRequirement = openapi_class.call('definitions', 'SecurityRequirement')
52
- Tag = openapi_class.call('definitions', 'Tag')
53
- ExternalDocumentation = openapi_class.call('definitions', 'ExternalDocumentation')
54
- Parameter = openapi_class.call('definitions', 'Parameter')
55
- ParameterWithSchema = openapi_class.call('definitions', 'ParameterWithSchema')
56
- ParameterWithSchemaWithExample = openapi_class.call('definitions', 'ParameterWithSchemaWithExample')
57
- ParameterWithSchemaWithExampleInPath = openapi_class.call('definitions', 'ParameterWithSchemaWithExampleInPath')
58
- ParameterWithSchemaWithExampleInQuery = openapi_class.call('definitions', 'ParameterWithSchemaWithExampleInQuery')
59
- ParameterWithSchemaWithExampleInHeader = openapi_class.call('definitions', 'ParameterWithSchemaWithExampleInHeader')
60
- ParameterWithSchemaWithExampleInCookie = openapi_class.call('definitions', 'ParameterWithSchemaWithExampleInCookie')
61
- ParameterWithSchemaWithExamples = openapi_class.call('definitions', 'ParameterWithSchemaWithExamples')
62
- ParameterWithSchemaWithExamplesInPath = openapi_class.call('definitions', 'ParameterWithSchemaWithExamplesInPath')
63
- ParameterWithSchemaWithExamplesInQuery = openapi_class.call('definitions', 'ParameterWithSchemaWithExamplesInQuery')
64
- ParameterWithSchemaWithExamplesInHeader = openapi_class.call('definitions', 'ParameterWithSchemaWithExamplesInHeader')
65
- ParameterWithSchemaWithExamplesInCookie = openapi_class.call('definitions', 'ParameterWithSchemaWithExamplesInCookie')
66
- ParameterWithContent = openapi_class.call('definitions', 'ParameterWithContent')
67
- ParameterWithContentInPath = openapi_class.call('definitions', 'ParameterWithContentInPath')
68
- ParameterWithContentNotInPath = openapi_class.call('definitions', 'ParameterWithContentNotInPath')
69
- RequestBody = openapi_class.call('definitions', 'RequestBody')
70
- SecurityScheme = openapi_class.call('definitions', 'SecurityScheme')
71
- APIKeySecurityScheme = openapi_class.call('definitions', 'APIKeySecurityScheme')
72
- HTTPSecurityScheme = openapi_class.call('definitions', 'HTTPSecurityScheme')
73
- NonBearerHTTPSecurityScheme = openapi_class.call('definitions', 'NonBearerHTTPSecurityScheme')
74
- BearerHTTPSecurityScheme = openapi_class.call('definitions', 'BearerHTTPSecurityScheme')
75
- OAuth2SecurityScheme = openapi_class.call('definitions', 'OAuth2SecurityScheme')
76
- OpenIdConnectSecurityScheme = openapi_class.call('definitions', 'OpenIdConnectSecurityScheme')
77
- OAuthFlows = openapi_class.call('definitions', 'OAuthFlows')
78
- ImplicitOAuthFlow = openapi_class.call('definitions', 'ImplicitOAuthFlow')
79
- PasswordOAuthFlow = openapi_class.call('definitions', 'PasswordOAuthFlow')
80
- ClientCredentialsFlow = openapi_class.call('definitions', 'ClientCredentialsFlow')
81
- AuthorizationCodeOAuthFlow = openapi_class.call('definitions', 'AuthorizationCodeOAuthFlow')
82
- Link = openapi_class.call('definitions', 'Link')
83
- LinkWithOperationRef = openapi_class.call('definitions', 'LinkWithOperationRef')
84
- LinkWithOperationId = openapi_class.call('definitions', 'LinkWithOperationId')
85
- Callback = openapi_class.call('definitions', 'Callback')
86
- Encoding = openapi_class.call('definitions', 'Encoding')
48
+ # generated: `puts Scorpio::OpenAPI::V3::Document.schema.definitions.select { |k,v| ['object', nil].include?(v['type']) }.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = Document.definitions['#{k}']" }`
49
+
50
+
51
+ Reference = Document.definitions['Reference']
52
+ SchemaReference = Document.definitions['SchemaReference']
53
+ Info = Document.definitions['Info']
54
+ Contact = Document.definitions['Contact']
55
+ License = Document.definitions['License']
56
+ Server = Document.definitions['Server']
57
+ ServerVariable = Document.definitions['ServerVariable']
58
+ Components = Document.definitions['Components']
59
+ Schema = Document.definitions['Schema']
60
+ Discriminator = Document.definitions['Discriminator']
61
+ XML = Document.definitions['XML']
62
+ Response = Document.definitions['Response']
63
+ MediaType = Document.definitions['MediaType']
64
+ MediaTypeWithExample = Document.definitions['MediaTypeWithExample']
65
+ MediaTypeWithExamples = Document.definitions['MediaTypeWithExamples']
66
+ Example = Document.definitions['Example']
67
+ Header = Document.definitions['Header']
68
+ HeaderWithSchema = Document.definitions['HeaderWithSchema']
69
+ HeaderWithSchemaWithExample = Document.definitions['HeaderWithSchemaWithExample']
70
+ HeaderWithSchemaWithExamples = Document.definitions['HeaderWithSchemaWithExamples']
71
+ HeaderWithContent = Document.definitions['HeaderWithContent']
72
+ Paths = Document.definitions['Paths']
73
+ PathItem = Document.definitions['PathItem']
74
+ Operation = Document.definitions['Operation']
75
+ Responses = Document.definitions['Responses']
76
+ SecurityRequirement = Document.definitions['SecurityRequirement']
77
+ Tag = Document.definitions['Tag']
78
+ ExternalDocumentation = Document.definitions['ExternalDocumentation']
79
+ Parameter = Document.definitions['Parameter']
80
+ ParameterWithSchema = Document.definitions['ParameterWithSchema']
81
+ ParameterWithSchemaWithExample = Document.definitions['ParameterWithSchemaWithExample']
82
+ ParameterWithSchemaWithExampleInPath = Document.definitions['ParameterWithSchemaWithExampleInPath']
83
+ ParameterWithSchemaWithExampleInQuery = Document.definitions['ParameterWithSchemaWithExampleInQuery']
84
+ ParameterWithSchemaWithExampleInHeader = Document.definitions['ParameterWithSchemaWithExampleInHeader']
85
+ ParameterWithSchemaWithExampleInCookie = Document.definitions['ParameterWithSchemaWithExampleInCookie']
86
+ ParameterWithSchemaWithExamples = Document.definitions['ParameterWithSchemaWithExamples']
87
+ ParameterWithSchemaWithExamplesInPath = Document.definitions['ParameterWithSchemaWithExamplesInPath']
88
+ ParameterWithSchemaWithExamplesInQuery = Document.definitions['ParameterWithSchemaWithExamplesInQuery']
89
+ ParameterWithSchemaWithExamplesInHeader = Document.definitions['ParameterWithSchemaWithExamplesInHeader']
90
+ ParameterWithSchemaWithExamplesInCookie = Document.definitions['ParameterWithSchemaWithExamplesInCookie']
91
+ ParameterWithContent = Document.definitions['ParameterWithContent']
92
+ ParameterWithContentInPath = Document.definitions['ParameterWithContentInPath']
93
+ ParameterWithContentNotInPath = Document.definitions['ParameterWithContentNotInPath']
94
+ RequestBody = Document.definitions['RequestBody']
95
+ SecurityScheme = Document.definitions['SecurityScheme']
96
+ APIKeySecurityScheme = Document.definitions['APIKeySecurityScheme']
97
+ HTTPSecurityScheme = Document.definitions['HTTPSecurityScheme']
98
+ NonBearerHTTPSecurityScheme = Document.definitions['NonBearerHTTPSecurityScheme']
99
+ BearerHTTPSecurityScheme = Document.definitions['BearerHTTPSecurityScheme']
100
+ OAuth2SecurityScheme = Document.definitions['OAuth2SecurityScheme']
101
+ OpenIdConnectSecurityScheme = Document.definitions['OpenIdConnectSecurityScheme']
102
+ OAuthFlows = Document.definitions['OAuthFlows']
103
+ ImplicitOAuthFlow = Document.definitions['ImplicitOAuthFlow']
104
+ PasswordOAuthFlow = Document.definitions['PasswordOAuthFlow']
105
+ ClientCredentialsFlow = Document.definitions['ClientCredentialsFlow']
106
+ AuthorizationCodeOAuthFlow = Document.definitions['AuthorizationCodeOAuthFlow']
107
+ Link = Document.definitions['Link']
108
+ LinkWithOperationRef = Document.definitions['LinkWithOperationRef']
109
+ LinkWithOperationId = Document.definitions['LinkWithOperationId']
110
+ Callback = Document.definitions['Callback']
111
+ Encoding = Document.definitions['Encoding']
112
+
113
+ raise(Bug) unless Schema < JSI::Schema
114
+ raise(Bug) unless SchemaReference < JSI::Schema
87
115
  end
88
116
  module V2
89
- openapi_schema = JSI::Schema.new(::JSON.parse(Scorpio.root.join('documents/swagger.io/v2/schema.json').read))
90
- openapi_class = proc do |*key|
91
- JSI.class_for_schema(key.inject(openapi_schema, &:[]))
92
- end
117
+ openapi_document_schema = JSI.new_schema(::JSON.parse(Scorpio.root.join(
118
+ 'documents/swagger.io/v2/schema.json'
119
+ ).read))
120
+
121
+ # the schema represented by Scorpio::OpenAPI::V2::Schema will describe schemas itself, so we set it to
122
+ # include on its schema module the jsi_schema_instance_modules that implement schema functionality.
123
+ describe_schema = [
124
+ openapi_document_schema.definitions['schema'],
125
+ # comments above on v3's definitions['Schema'].properties['additionalProperties'] apply here too
126
+ openapi_document_schema.definitions['schema'].properties['additionalProperties'],
127
+ ]
128
+ describe_schema.each { |s| s.jsi_schema_instance_modules = [JSI::Schema::Draft04] }
93
129
 
94
- Document = openapi_class.call()
130
+ Document = openapi_document_schema.jsi_schema_module
95
131
 
96
132
  # naming these is not strictly necessary, but is nice to have.
97
- # generated: `puts Scorpio::OpenAPI::V2::Document.schema['definitions'].select { |k,v| ['object', nil].include?(v['type']) }.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = openapi_class.call('definitions', '#{k}')" }`
98
- Info = openapi_class.call('definitions', 'info')
99
- Contact = openapi_class.call('definitions', 'contact')
100
- License = openapi_class.call('definitions', 'license')
101
- Paths = openapi_class.call('definitions', 'paths')
102
- Definitions = openapi_class.call('definitions', 'definitions')
103
- ParameterDefinitions = openapi_class.call('definitions', 'parameterDefinitions')
104
- ResponseDefinitions = openapi_class.call('definitions', 'responseDefinitions')
105
- ExternalDocs = openapi_class.call('definitions', 'externalDocs')
106
- Examples = openapi_class.call('definitions', 'examples')
107
- Operation = openapi_class.call('definitions', 'operation')
108
- PathItem = openapi_class.call('definitions', 'pathItem')
109
- Responses = openapi_class.call('definitions', 'responses')
110
- ResponseValue = openapi_class.call('definitions', 'responseValue')
111
- Response = openapi_class.call('definitions', 'response')
112
- Headers = openapi_class.call('definitions', 'headers')
113
- Header = openapi_class.call('definitions', 'header')
114
- VendorExtension = openapi_class.call('definitions', 'vendorExtension')
115
- BodyParameter = openapi_class.call('definitions', 'bodyParameter')
116
- HeaderParameterSubSchema = openapi_class.call('definitions', 'headerParameterSubSchema')
117
- QueryParameterSubSchema = openapi_class.call('definitions', 'queryParameterSubSchema')
118
- FormDataParameterSubSchema = openapi_class.call('definitions', 'formDataParameterSubSchema')
119
- PathParameterSubSchema = openapi_class.call('definitions', 'pathParameterSubSchema')
120
- NonBodyParameter = openapi_class.call('definitions', 'nonBodyParameter')
121
- Parameter = openapi_class.call('definitions', 'parameter')
122
- Schema = openapi_class.call('definitions', 'schema')
123
- FileSchema = openapi_class.call('definitions', 'fileSchema')
124
- PrimitivesItems = openapi_class.call('definitions', 'primitivesItems')
125
- SecurityRequirement = openapi_class.call('definitions', 'securityRequirement')
126
- Xml = openapi_class.call('definitions', 'xml')
127
- Tag = openapi_class.call('definitions', 'tag')
128
- SecurityDefinitions = openapi_class.call('definitions', 'securityDefinitions')
129
- BasicAuthenticationSecurity = openapi_class.call('definitions', 'basicAuthenticationSecurity')
130
- ApiKeySecurity = openapi_class.call('definitions', 'apiKeySecurity')
131
- Oauth2ImplicitSecurity = openapi_class.call('definitions', 'oauth2ImplicitSecurity')
132
- Oauth2PasswordSecurity = openapi_class.call('definitions', 'oauth2PasswordSecurity')
133
- Oauth2ApplicationSecurity = openapi_class.call('definitions', 'oauth2ApplicationSecurity')
134
- Oauth2AccessCodeSecurity = openapi_class.call('definitions', 'oauth2AccessCodeSecurity')
135
- Oauth2Scopes = openapi_class.call('definitions', 'oauth2Scopes')
136
- Title = openapi_class.call('definitions', 'title')
137
- Description = openapi_class.call('definitions', 'description')
138
- Default = openapi_class.call('definitions', 'default')
139
- MultipleOf = openapi_class.call('definitions', 'multipleOf')
140
- Maximum = openapi_class.call('definitions', 'maximum')
141
- ExclusiveMaximum = openapi_class.call('definitions', 'exclusiveMaximum')
142
- Minimum = openapi_class.call('definitions', 'minimum')
143
- ExclusiveMinimum = openapi_class.call('definitions', 'exclusiveMinimum')
144
- MaxLength = openapi_class.call('definitions', 'maxLength')
145
- MinLength = openapi_class.call('definitions', 'minLength')
146
- Pattern = openapi_class.call('definitions', 'pattern')
147
- MaxItems = openapi_class.call('definitions', 'maxItems')
148
- MinItems = openapi_class.call('definitions', 'minItems')
149
- UniqueItems = openapi_class.call('definitions', 'uniqueItems')
150
- Enum = openapi_class.call('definitions', 'enum')
151
- JsonReference = openapi_class.call('definitions', 'jsonReference')
133
+ # generated: `puts Scorpio::OpenAPI::V2::Document.schema.definitions.select { |k,v| ['object', nil].include?(v['type']) }.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = Document.definitions['#{k}']" }`
134
+
135
+
136
+ Info = Document.definitions['info']
137
+ Contact = Document.definitions['contact']
138
+ License = Document.definitions['license']
139
+ Paths = Document.definitions['paths']
140
+ Definitions = Document.definitions['definitions']
141
+ ParameterDefinitions = Document.definitions['parameterDefinitions']
142
+ ResponseDefinitions = Document.definitions['responseDefinitions']
143
+ ExternalDocs = Document.definitions['externalDocs']
144
+ Examples = Document.definitions['examples']
145
+ Operation = Document.definitions['operation']
146
+ PathItem = Document.definitions['pathItem']
147
+ Responses = Document.definitions['responses']
148
+ ResponseValue = Document.definitions['responseValue']
149
+ Response = Document.definitions['response']
150
+ Headers = Document.definitions['headers']
151
+ Header = Document.definitions['header']
152
+ VendorExtension = Document.definitions['vendorExtension']
153
+ BodyParameter = Document.definitions['bodyParameter']
154
+ HeaderParameterSubSchema = Document.definitions['headerParameterSubSchema']
155
+ QueryParameterSubSchema = Document.definitions['queryParameterSubSchema']
156
+ FormDataParameterSubSchema = Document.definitions['formDataParameterSubSchema']
157
+ PathParameterSubSchema = Document.definitions['pathParameterSubSchema']
158
+ NonBodyParameter = Document.definitions['nonBodyParameter']
159
+ Parameter = Document.definitions['parameter']
160
+ Schema = Document.definitions['schema']
161
+ FileSchema = Document.definitions['fileSchema']
162
+ PrimitivesItems = Document.definitions['primitivesItems']
163
+ SecurityRequirement = Document.definitions['securityRequirement']
164
+ Xml = Document.definitions['xml']
165
+ Tag = Document.definitions['tag']
166
+ SecurityDefinitions = Document.definitions['securityDefinitions']
167
+ BasicAuthenticationSecurity = Document.definitions['basicAuthenticationSecurity']
168
+ ApiKeySecurity = Document.definitions['apiKeySecurity']
169
+ Oauth2ImplicitSecurity = Document.definitions['oauth2ImplicitSecurity']
170
+ Oauth2PasswordSecurity = Document.definitions['oauth2PasswordSecurity']
171
+ Oauth2ApplicationSecurity = Document.definitions['oauth2ApplicationSecurity']
172
+ Oauth2AccessCodeSecurity = Document.definitions['oauth2AccessCodeSecurity']
173
+ Oauth2Scopes = Document.definitions['oauth2Scopes']
174
+ Title = Document.definitions['title']
175
+ Description = Document.definitions['description']
176
+ Default = Document.definitions['default']
177
+ MultipleOf = Document.definitions['multipleOf']
178
+ Maximum = Document.definitions['maximum']
179
+ ExclusiveMaximum = Document.definitions['exclusiveMaximum']
180
+ Minimum = Document.definitions['minimum']
181
+ ExclusiveMinimum = Document.definitions['exclusiveMinimum']
182
+ MaxLength = Document.definitions['maxLength']
183
+ MinLength = Document.definitions['minLength']
184
+ Pattern = Document.definitions['pattern']
185
+ MaxItems = Document.definitions['maxItems']
186
+ MinItems = Document.definitions['minItems']
187
+ UniqueItems = Document.definitions['uniqueItems']
188
+ Enum = Document.definitions['enum']
189
+ JsonReference = Document.definitions['jsonReference']
190
+
191
+ raise(Bug) unless Schema < JSI::Schema
152
192
  end
153
193
 
154
- begin
155
- # the autoloads for OpenAPI::Operation and OpenAPI::Document
156
- # should not be triggered until all the classes their files reference are defined (above)
157
- end # (this block is here just so the above informative comment is not interpreted as module doc)
194
+ # the autoloads for OpenAPI::Operation and OpenAPI::Document are triggered below. these
195
+ # should not be triggered until all the classes their files reference are defined (above).
196
+
158
197
 
159
198
  module V3
160
- class Operation
199
+ module Operation
161
200
  include OpenAPI::Operation
162
201
  end
163
- class Document
202
+ module Document
164
203
  include OpenAPI::Document
165
204
  end
205
+ module Reference
206
+ include OpenAPI::Reference
207
+ end
208
+ module Tag
209
+ include OpenAPI::Tag
210
+ end
166
211
  require 'scorpio/openapi/v3/server'
167
212
  end
168
213
 
169
214
  module V2
170
- class Operation
215
+ module Operation
171
216
  include OpenAPI::Operation
172
217
  end
173
- class Document
218
+ module Document
174
219
  include OpenAPI::Document
175
220
  end
221
+ module JsonReference
222
+ include OpenAPI::Reference
223
+ end
224
+ module Tag
225
+ include OpenAPI::Tag
226
+ end
176
227
  end
177
228
  end
178
229
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'scorpio'
2
4
  require 'pickle'
3
5
 
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Scorpio
2
4
  class Request
3
- SUPPORTED_REQUEST_MEDIA_TYPES = ['application/json', 'application/x-www-form-urlencoded']
5
+ SUPPORTED_REQUEST_MEDIA_TYPES = ['application/json'.freeze, 'application/x-www-form-urlencoded'.freeze].freeze
6
+ FALLBACK_CONTENT_TYPE = 'application/x-www-form-urlencoded'.freeze
7
+
4
8
  def self.best_media_type(media_types)
5
9
  if media_types.size == 1
6
10
  media_types.first
@@ -116,12 +120,12 @@ module Scorpio
116
120
  def initialize(operation, configuration = {}, &b)
117
121
  @operation = operation
118
122
 
119
- configuration = JSI.stringify_symbol_keys(configuration)
123
+ configuration = JSI::Util.stringify_symbol_keys(configuration)
120
124
  params_set = Set.new # the set of params that have been set
121
125
  # do the Configurables first
122
126
  configuration.each do |name, value|
123
127
  if Configurables.public_method_defined?("#{name}=")
124
- Configurables.instance_method("#{name}=").bind(self).call(value)
128
+ Configurables.instance_method("#{name}=").bind_call(self, value)
125
129
  params_set << name
126
130
  end
127
131
  end
@@ -146,21 +150,23 @@ module Scorpio
146
150
  operation.openapi_document
147
151
  end
148
152
 
149
- # @return [Symbol] the http method for this request - :get, :post, etc.
153
+ # the http method for this request
154
+ # @return [String]
150
155
  def http_method
151
- operation.http_method.downcase.to_sym
156
+ operation.http_method
152
157
  end
153
158
 
154
- # @return [Addressable::Template] the template for the request's path, to be expanded
155
- # with path_params and appended to the request's base_url
159
+ # the template for the request's path, to be expanded with {Configurables#path_params} and appended to
160
+ # the request's {Configurables#base_url}
161
+ # @return [Addressable::Template]
156
162
  def path_template
157
163
  operation.path_template
158
164
  end
159
165
 
160
- # @return [Addressable::URI] an Addressable::URI containing only the path to append to
161
- # the base_url for this request
166
+ # an Addressable::URI containing only the path to append to the {Configurables#base_url} for this request
167
+ # @return [Addressable::URI]
162
168
  def path
163
- path_params = JSI.stringify_symbol_keys(self.path_params)
169
+ path_params = JSI::Util.stringify_symbol_keys(self.path_params)
164
170
  missing_variables = path_template.variables - path_params.keys
165
171
  if missing_variables.any?
166
172
  raise(ArgumentError, "path #{operation.path_template_str} for operation #{operation.human_id} requires path_params " +
@@ -172,24 +178,26 @@ module Scorpio
172
178
  "which were empty: #{empty_variables.inspect}")
173
179
  end
174
180
 
175
- path_template.expand(path_params).tap do |path|
176
- if query_params
177
- path.query_values = query_params
178
- end
181
+ path = path_template.expand(path_params)
182
+ if query_params
183
+ path.query_values = query_params
179
184
  end
185
+ path.freeze
180
186
  end
181
187
 
182
- # @return [Addressable::URI] the full URL for this request
188
+ # the full URL for this request
189
+ # @return [Addressable::URI]
183
190
  def url
184
191
  unless base_url
185
192
  raise(ArgumentError, "no base_url has been specified for request")
186
193
  end
187
194
  # we do not use Addressable::URI#join as the paths should just be concatenated, not resolved.
188
195
  # we use File.join just to deal with consecutive slashes.
189
- Addressable::URI.parse(File.join(base_url, path))
196
+ Addressable::URI.parse(File.join(base_url, path)).freeze
190
197
  end
191
198
 
192
- # @return [::Ur::ContentType] the value of the request Content-Type header
199
+ # the value of the request Content-Type header
200
+ # @return [::Ur::ContentType]
193
201
  def content_type_header
194
202
  headers.each do |k, v|
195
203
  return ::Ur::ContentType.new(v) if k =~ /\Acontent[-_]type\z/i
@@ -197,10 +205,11 @@ module Scorpio
197
205
  nil
198
206
  end
199
207
 
200
- # @return [::Ur::ContentType] Content-Type for this request, taken from request headers if
201
- # present, or the request media_type.
208
+ # Content-Type for this request, taken from request headers if present, or the
209
+ # request {Configurables#media_type}.
210
+ # @return [::Ur::ContentType]
202
211
  def content_type
203
- content_type_header || ::Ur::ContentType.new(media_type)
212
+ content_type_header || (media_type ? ::Ur::ContentType.new(media_type) : nil)
204
213
  end
205
214
 
206
215
  # @return [::JSI::Schema]
@@ -208,11 +217,6 @@ module Scorpio
208
217
  operation.request_schema(media_type: media_type)
209
218
  end
210
219
 
211
- # @return [Class subclassing JSI::Base]
212
- def request_schema_class(media_type: self.media_type)
213
- JSI.class_for_schema(request_schema(media_type: media_type))
214
- end
215
-
216
220
  # builds a Faraday connection with this Request's faraday_builder and faraday_adapter.
217
221
  # passes a given proc yield_ur to middleware to yield an Ur for requests made with the connection.
218
222
  #
@@ -222,8 +226,9 @@ module Scorpio
222
226
  Faraday.new do |faraday_connection|
223
227
  faraday_builder.call(faraday_connection)
224
228
  if yield_ur
225
- ::Ur::Faraday # autoload trigger
226
- faraday_connection.response(:yield_ur, ur_class: Scorpio::Ur, logger: self.logger, &yield_ur)
229
+ -> { ::Ur::Faraday }.() # autoload trigger
230
+
231
+ faraday_connection.response(:yield_ur, schemas: Set[Scorpio::Ur.schema], logger: self.logger, &yield_ur)
227
232
  end
228
233
  faraday_connection.adapter(*faraday_adapter)
229
234
  end
@@ -234,16 +239,17 @@ module Scorpio
234
239
  # @param name [String, Symbol] the 'name' property of one applicable parameter
235
240
  # @param value [Object] the applicable parameter will be applied to the request with the given value.
236
241
  # @return [Object] echoes the value param
237
- # @raise [Scorpio::AmbiguousParameter] if more than one parameter has the given name
242
+ # @raise (see #param_for!)
238
243
  def set_param(name, value)
239
244
  param = param_for!(name)
240
245
  set_param_from(param['in'], param['name'], value)
241
246
  value
242
247
  end
243
248
 
249
+ # returns the value of the named parameter on this request
244
250
  # @param name [String, Symbol] the 'name' property of one applicable parameter
245
- # @return [Object] the value of the named parameter on this request
246
- # @raise [Scorpio::AmbiguousParameter] if more than one parameter has the given name
251
+ # @return [Object]
252
+ # @raise (see #param_for!)
247
253
  def get_param(name)
248
254
  param = param_for!(name)
249
255
  get_param_from(param['in'], param['name'])
@@ -251,6 +257,7 @@ module Scorpio
251
257
 
252
258
  # @param name [String, Symbol] the 'name' property of one applicable parameter
253
259
  # @return [#to_hash, nil]
260
+ # @raise [Scorpio::AmbiguousParameter] if more than one parameter has the given name
254
261
  def param_for(name)
255
262
  name = name.to_s if name.is_a?(Symbol)
256
263
  params = operation.inferred_parameters.select { |p| p['name'] == name }
@@ -265,17 +272,21 @@ module Scorpio
265
272
  end
266
273
  end
267
274
 
268
- # @param name [String, Symbol] the name or {in}.{name} (e.g. "query.search") for the applicable parameter.
275
+ # @param name [String, Symbol] the 'name' property of one applicable parameter
269
276
  # @return [#to_hash]
277
+ # @raise [Scorpio::ParameterError] if no parameter has the given name
278
+ # @raise (see #param_for)
270
279
  def param_for!(name)
271
280
  param_for(name) || raise(ParameterError, "There is no parameter named #{name} on operation #{operation.human_id}:\n#{operation.pretty_inspect.chomp}")
272
281
  end
273
282
 
274
- # @param in [String, Symbol] one of 'path', 'query', 'header', or 'cookie' - where to apply the named value
283
+ # applies the named value to the appropriate parameter of the request
284
+ # @param param_in [String, Symbol] one of 'path', 'query', 'header', or 'cookie' - where to apply
285
+ # the named value
275
286
  # @param name [String, Symbol] the parameter name to apply the value to
276
287
  # @param value [Object] the value
277
288
  # @return [Object] echoes the value param
278
- # @raise [ArgumentError] invalid 'in' parameter
289
+ # @raise [ArgumentError] invalid `param_in` parameter
279
290
  # @raise [NotImplementedError] cookies aren't implemented
280
291
  def set_param_from(param_in, name, value)
281
292
  param_in = param_in.to_s if param_in.is_a?(Symbol)
@@ -294,10 +305,12 @@ module Scorpio
294
305
  value
295
306
  end
296
307
 
297
- # @param in [String, Symbol] one of 'path', 'query', 'header', or 'cookie' - where to apply the named value
308
+ # returns the value of the named parameter from the specified `param_in` on this request
309
+ # @param param_in [String, Symbol] one of 'path', 'query', 'header', or 'cookie' - where to retrieve
310
+ # the named value
298
311
  # @param name [String, Symbol] the parameter name
299
- # @return [Object] the value of the named parameter on this request
300
- # @raise [ArgumentError] invalid 'in' parameter
312
+ # @return [Object]
313
+ # @raise [ArgumentError] invalid `param_in` parameter
301
314
  # @raise [NotImplementedError] cookies aren't implemented
302
315
  def get_param_from(param_in, name)
303
316
  if param_in == 'path'
@@ -322,24 +335,30 @@ module Scorpio
322
335
  if user_agent
323
336
  headers['User-Agent'] = user_agent
324
337
  end
325
- if media_type && !content_type_header
326
- headers['Content-Type'] = media_type
338
+ if !content_type_header
339
+ if media_type
340
+ headers['Content-Type'] = media_type
341
+ else
342
+ # I'd rather not have a default content-type, but if none is set then the HTTP adapter sets this to
343
+ # application/x-www-form-urlencoded and issues a warning about it.
344
+ headers['Content-Type'] = FALLBACK_CONTENT_TYPE
345
+ end
327
346
  end
328
347
  if self.headers
329
348
  headers.update(self.headers)
330
349
  end
331
350
  ur = nil
332
- faraday_connection(-> (yur) { ur = yur }).run_request(http_method, url, body, headers)
351
+ faraday_connection(-> (yur) { ur = yur }).run_request(http_method.downcase.to_sym, url, body, headers)
333
352
  ur.scorpio_request = self
334
353
  ur
335
354
  end
336
355
 
337
356
  # runs this request. returns the response body object - that is, the response body
338
357
  # parsed according to an understood media type, and instantiated with the applicable
339
- # response schema if one is specified. see Scorpio::Response#body_object for more detail.
358
+ # response schema if one is specified. see {Scorpio::Response#body_object} for more detail.
340
359
  #
341
360
  # @raise [Scorpio::HTTPError] if the request returns a 4xx or 5xx status, the appropriate
342
- # error is raised - see Scorpio::HTTPErrors
361
+ # error is raised - see {Scorpio::HTTPErrors}
343
362
  def run
344
363
  ur = run_ur
345
364
  ur.raise_on_http_error