scorpio 0.4.5 → 0.6.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.
@@ -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