scorpio 0.6.4 → 0.7.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -1
  3. data/CHANGELOG.md +10 -2
  4. data/LICENSE.md +2 -4
  5. data/README.md +81 -67
  6. data/documents/{github.com/OAI/OpenAPI-Specification/blob/oas3-schema/schemas/v3.0 → spec.openapis.org/oas/3.0}/schema.yaml +164 -628
  7. data/documents/spec.openapis.org/oas/3.1/dialect/base.schema.yaml +22 -0
  8. data/documents/spec.openapis.org/oas/3.1/meta/base.schema.yaml +71 -0
  9. data/documents/spec.openapis.org/oas/3.1/schema-base.yaml +21 -0
  10. data/documents/spec.openapis.org/oas/3.1/schema.yaml +980 -0
  11. data/documents/swagger.io/v2/schema.json +1 -1
  12. data/documents/www.googleapis.com/discovery/v1/apis/discovery/v1/rest.yml +44 -4
  13. data/lib/scorpio/google_api_document.rb +121 -193
  14. data/lib/scorpio/openapi/document.rb +63 -31
  15. data/lib/scorpio/openapi/operation.rb +114 -96
  16. data/lib/scorpio/openapi/operations_scope.rb +35 -19
  17. data/lib/scorpio/openapi/reference.rb +88 -23
  18. data/lib/scorpio/openapi/schema_elements/type_nullable.rb +38 -0
  19. data/lib/scorpio/openapi/schema_elements.rb +7 -0
  20. data/lib/scorpio/openapi/server.rb +34 -0
  21. data/lib/scorpio/openapi/tag.rb +19 -3
  22. data/lib/scorpio/openapi/v2/dialect.rb +66 -0
  23. data/lib/scorpio/openapi/v2.rb +124 -0
  24. data/lib/scorpio/openapi/v3_0/dialect.rb +76 -0
  25. data/lib/scorpio/openapi/v3_0.rb +130 -0
  26. data/lib/scorpio/openapi/v3_1.rb +243 -0
  27. data/lib/scorpio/openapi.rb +23 -203
  28. data/lib/scorpio/request.rb +67 -61
  29. data/lib/scorpio/resource_base.rb +57 -49
  30. data/lib/scorpio/response.rb +28 -10
  31. data/lib/scorpio/ur.rb +7 -3
  32. data/lib/scorpio/version.rb +1 -1
  33. data/lib/scorpio.rb +12 -6
  34. data/pages/Request_Configuration.md +69 -0
  35. data/pages/Security.md +50 -0
  36. data/scorpio.gemspec +6 -5
  37. metadata +28 -15
  38. data/documents/www.googleapis.com/discovery/v1/apis/discovery/v1/rest +0 -684
  39. data/lib/scorpio/openapi/v3/server.rb +0 -44
@@ -1,14 +1,30 @@
1
1
  module Scorpio
2
2
  module OpenAPI
3
3
  module Tag
4
+ include(Document::Descendent)
5
+
4
6
  # operations in the openapi document which have a tag with this tag's name
5
7
  # @return [Enumerable<Scorpio::OpenAPI::Operation>]
6
8
  def operations
7
- unless jsi_root_node.is_a?(OpenAPI::Document)
8
- raise("Tag#operations cannot be used on a Tag that is not inside an OpenAPI document")
9
+ return(@operations) if instance_variable_defined?(:@operations)
10
+ @operations = OperationsScope.new(each_operation)
11
+ end
12
+
13
+ # @yield [OpenAPI::Operation]
14
+ def each_operation(&block)
15
+ return(to_enum(__method__)) unless block
16
+
17
+ openapi_document.each_operation do |op|
18
+ yield(op) if op.tags.respond_to?(:to_ary) && op.tags.include?(name)
9
19
  end
20
+ end
21
+ end
10
22
 
11
- jsi_root_node.operations.select { |op| op.tags.respond_to?(:to_ary) && op.tags.include?(name) }
23
+ module Tags
24
+ # a tag with the given name
25
+ # @return [OpenAPI::Tag, nil]
26
+ def named(name)
27
+ detect { |tag| tag.name == name }
12
28
  end
13
29
  end
14
30
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scorpio
4
+ module OpenAPI::V2
5
+ VOCABULARY = JSI::Schema::Vocabulary.new(
6
+ elements: [
7
+ JSI::Schema::Elements::SELF[],
8
+
9
+ JSI::Schema::Elements::REF[exclusive: true],
10
+
11
+ JSI::Schema::Elements::MULTIPLE_OF[],
12
+
13
+ JSI::Schema::Elements::MAXIMUM_BOOLEAN_EXCLUSIVE[],
14
+
15
+ JSI::Schema::Elements::MINIMUM_BOOLEAN_EXCLUSIVE[],
16
+
17
+ JSI::Schema::Elements::MAX_LENGTH[],
18
+
19
+ JSI::Schema::Elements::MIN_LENGTH[],
20
+
21
+ JSI::Schema::Elements::PATTERN[],
22
+
23
+ # TODO this should not include additionalItems
24
+ JSI::Schema::Elements::ITEMS[],
25
+
26
+ JSI::Schema::Elements::MAX_ITEMS[],
27
+
28
+ JSI::Schema::Elements::MIN_ITEMS[],
29
+
30
+ JSI::Schema::Elements::UNIQUE_ITEMS[],
31
+
32
+ JSI::Schema::Elements::MAX_PROPERTIES[],
33
+
34
+ JSI::Schema::Elements::MIN_PROPERTIES[],
35
+
36
+ JSI::Schema::Elements::REQUIRED[],
37
+
38
+ # TODO this should not include patternProperties
39
+ JSI::Schema::Elements::PROPERTIES[],
40
+
41
+ JSI::Schema::Elements::ENUM[],
42
+
43
+ JSI::Schema::Elements::TYPE[],
44
+
45
+ JSI::Schema::Elements::ALL_OF[],
46
+
47
+ JSI::Schema::Elements::INFO_STRING[keyword: 'title'],
48
+ JSI::Schema::Elements::INFO_STRING[keyword: 'description'],
49
+
50
+ JSI::Schema::Elements::INFO_BOOL[keyword: 'readOnly'],
51
+
52
+ JSI::Schema::Elements::DEFAULT[],
53
+
54
+ JSI::Schema::Elements::FORMAT[],
55
+
56
+ JSI::Schema::Element.new(keyword: 'example') { }, # no actions
57
+ ],
58
+ )
59
+
60
+ # https://spec.openapis.org/oas/v2.0.html#schema-object
61
+ DIALECT = JSI::Schema::Dialect.new(
62
+ vocabularies: [VOCABULARY],
63
+ integer_disallows_0_fraction: true,
64
+ )
65
+ end
66
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scorpio
4
+ module OpenAPI
5
+ module V2
6
+ autoload(:DIALECT, 'scorpio/openapi/v2/dialect')
7
+
8
+ describe_schema_ptrs = Set[
9
+ # this schema (Scorpio::OpenAPI::V2::Schema) describes schemas in an OpenAPI document.
10
+ JSI::Ptr['definitions', 'schema'],
11
+ # this schema describes a boolean schema, only allowed for 'additionalProperties'
12
+ JSI::Ptr['definitions', 'schema', 'properties', 'additionalProperties', 'anyOf', 1],
13
+ ].freeze
14
+
15
+ Document = JSI.new_schema_module(JSON.parse(Scorpio.root.join(
16
+ 'documents/swagger.io/v2/schema.json'
17
+ ).read, freeze: true))
18
+
19
+ describe_schema_ptrs.each do |ptr|
20
+ (Document / ptr).describes_schema!(DIALECT)
21
+ end
22
+
23
+ # naming these is not strictly necessary, but is nice to have.
24
+ # generated: `puts Scorpio::OpenAPI::V2::Document.schema.definitions.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = Document.definitions['#{k}']" }`
25
+
26
+
27
+ Info = Document.definitions['info']
28
+ Contact = Document.definitions['contact']
29
+ License = Document.definitions['license']
30
+ Paths = Document.definitions['paths']
31
+ Definitions = Document.definitions['definitions']
32
+ ParameterDefinitions = Document.definitions['parameterDefinitions']
33
+ ResponseDefinitions = Document.definitions['responseDefinitions']
34
+ ExternalDocs = Document.definitions['externalDocs']
35
+ Examples = Document.definitions['examples']
36
+ MimeType = Document.definitions['mimeType']
37
+ Operation = Document.definitions['operation']
38
+ PathItem = Document.definitions['pathItem']
39
+ Responses = Document.definitions['responses']
40
+ ResponseValue = Document.definitions['responseValue']
41
+ Response = Document.definitions['response']
42
+ Headers = Document.definitions['headers']
43
+ Header = Document.definitions['header']
44
+ VendorExtension = Document.definitions['vendorExtension']
45
+ BodyParameter = Document.definitions['bodyParameter']
46
+ HeaderParameterSubSchema = Document.definitions['headerParameterSubSchema']
47
+ QueryParameterSubSchema = Document.definitions['queryParameterSubSchema']
48
+ FormDataParameterSubSchema = Document.definitions['formDataParameterSubSchema']
49
+ PathParameterSubSchema = Document.definitions['pathParameterSubSchema']
50
+ NonBodyParameter = Document.definitions['nonBodyParameter']
51
+ Parameter = Document.definitions['parameter']
52
+ Schema = Document.definitions['schema']
53
+ FileSchema = Document.definitions['fileSchema']
54
+ PrimitivesItems = Document.definitions['primitivesItems']
55
+ Security = Document.definitions['security']
56
+ SecurityRequirement = Document.definitions['securityRequirement']
57
+ Xml = Document.definitions['xml']
58
+ Tag = Document.definitions['tag']
59
+ SecurityDefinitions = Document.definitions['securityDefinitions']
60
+ BasicAuthenticationSecurity = Document.definitions['basicAuthenticationSecurity']
61
+ ApiKeySecurity = Document.definitions['apiKeySecurity']
62
+ OAuth2ImplicitSecurity = Document.definitions['oauth2ImplicitSecurity']
63
+ OAuth2PasswordSecurity = Document.definitions['oauth2PasswordSecurity']
64
+ OAuth2ApplicationSecurity = Document.definitions['oauth2ApplicationSecurity']
65
+ OAuth2AccessCodeSecurity = Document.definitions['oauth2AccessCodeSecurity']
66
+ OAuth2Scopes = Document.definitions['oauth2Scopes']
67
+ MediaTypeList = Document.definitions['mediaTypeList']
68
+ ParametersList = Document.definitions['parametersList']
69
+ SchemesList = Document.definitions['schemesList']
70
+ CollectionFormat = Document.definitions['collectionFormat']
71
+ CollectionFormatWithMulti = Document.definitions['collectionFormatWithMulti']
72
+ Title = Document.definitions['title']
73
+ Description = Document.definitions['description']
74
+ Default = Document.definitions['default']
75
+ MultipleOf = Document.definitions['multipleOf']
76
+ Maximum = Document.definitions['maximum']
77
+ ExclusiveMaximum = Document.definitions['exclusiveMaximum']
78
+ Minimum = Document.definitions['minimum']
79
+ ExclusiveMinimum = Document.definitions['exclusiveMinimum']
80
+ MaxLength = Document.definitions['maxLength']
81
+ MinLength = Document.definitions['minLength']
82
+ Pattern = Document.definitions['pattern']
83
+ MaxItems = Document.definitions['maxItems']
84
+ MinItems = Document.definitions['minItems']
85
+ UniqueItems = Document.definitions['uniqueItems']
86
+ Enum = Document.definitions['enum']
87
+ JsonReference = Document.definitions['jsonReference']
88
+
89
+ module Operation
90
+ include(OpenAPI::Operation::V2Methods)
91
+ end
92
+
93
+ # A document that defines or describes an API conforming to the OpenAPI Specification v2 (aka Swagger).
94
+ #
95
+ # The root document is known as the Swagger Object.
96
+ module Document
97
+ include(OpenAPI::Document::V2Methods)
98
+ end
99
+
100
+ module JsonReference
101
+ include(OpenAPI::Reference)
102
+ end
103
+
104
+ module Tag
105
+ include(OpenAPI::Tag)
106
+ end
107
+
108
+ Document.properties["tags"].include(OpenAPI::Tags)
109
+
110
+ module Paths
111
+ include(OpenAPI::Paths)
112
+ end
113
+
114
+ module PathItem
115
+ include(OpenAPI::PathItem)
116
+ include(OpenAPI::Reference)
117
+ end
118
+
119
+ module Response
120
+ include(OpenAPI::Response)
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scorpio
4
+ module OpenAPI::V3_0
5
+ VOCABULARY = JSI::Schema::Vocabulary.new(
6
+ elements: [
7
+ JSI::Schema::Elements::SELF[],
8
+
9
+ JSI::Schema::Elements::REF[exclusive: true],
10
+
11
+ JSI::Schema::Elements::MULTIPLE_OF[],
12
+
13
+ JSI::Schema::Elements::MAXIMUM_BOOLEAN_EXCLUSIVE[],
14
+
15
+ JSI::Schema::Elements::MINIMUM_BOOLEAN_EXCLUSIVE[],
16
+
17
+ JSI::Schema::Elements::MAX_LENGTH[],
18
+
19
+ JSI::Schema::Elements::MIN_LENGTH[],
20
+
21
+ JSI::Schema::Elements::PATTERN[],
22
+
23
+ JSI::Schema::Elements::MAX_ITEMS[],
24
+
25
+ JSI::Schema::Elements::MIN_ITEMS[],
26
+
27
+ JSI::Schema::Elements::UNIQUE_ITEMS[],
28
+
29
+ JSI::Schema::Elements::MAX_PROPERTIES[],
30
+
31
+ JSI::Schema::Elements::MIN_PROPERTIES[],
32
+
33
+ JSI::Schema::Elements::REQUIRED[],
34
+
35
+ JSI::Schema::Elements::ENUM[],
36
+
37
+ OpenAPI::SchemaElements::TYPE_NULLABLE,
38
+
39
+ JSI::Schema::Elements::ALL_OF[],
40
+ JSI::Schema::Elements::ONE_OF[],
41
+ JSI::Schema::Elements::ANY_OF[],
42
+
43
+ JSI::Schema::Elements::NOT[],
44
+
45
+ # TODO this should not include additionalItems
46
+ JSI::Schema::Elements::ITEMS[],
47
+
48
+ # TODO this should not include patternProperties
49
+ JSI::Schema::Elements::PROPERTIES[],
50
+
51
+ JSI::Schema::Elements::INFO_STRING[keyword: 'title'],
52
+ JSI::Schema::Elements::INFO_STRING[keyword: 'description'],
53
+
54
+ JSI::Schema::Elements::INFO_BOOL[keyword: 'readOnly'],
55
+ JSI::Schema::Elements::INFO_BOOL[keyword: 'writeOnly'],
56
+ JSI::Schema::Elements::INFO_BOOL[keyword: 'deprecated'],
57
+
58
+ JSI::Schema::Elements::DEFAULT[],
59
+
60
+ JSI::Schema::Elements::FORMAT[],
61
+
62
+ JSI::Schema::Element.new(keyword: 'externalDocs') { }, # no actions
63
+ JSI::Schema::Element.new(keyword: 'example') { }, # no actions
64
+
65
+ # TODO `discriminator`
66
+ # TODO `xml`
67
+ ],
68
+ )
69
+
70
+ # https://spec.openapis.org/oas/v3.0.4.html#schema-object
71
+ DIALECT = JSI::Schema::Dialect.new(
72
+ vocabularies: [VOCABULARY],
73
+ integer_disallows_0_fraction: true,
74
+ )
75
+ end
76
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scorpio
4
+ module OpenAPI
5
+ module V3_0
6
+ autoload(:DIALECT, 'scorpio/openapi/v3_0/dialect')
7
+
8
+ describe_schema_ptrs = Set[
9
+ # this schema (Scorpio::OpenAPI::V3_0::Schema) describes schemas in an OpenAPI document.
10
+ JSI::Ptr['definitions', 'Schema'],
11
+ # /definitions/Schema does not allow $ref; this schema describes a schema with a $ref.
12
+ JSI::Ptr['definitions', 'SchemaReference'],
13
+ # this schema describes a boolean schema, only allowed for 'additionalProperties'
14
+ JSI::Ptr['definitions', 'Schema', 'properties', 'additionalProperties', 'oneOf', 2],
15
+ ].freeze
16
+
17
+ Document = JSI::JSONSchemaDraft04.new_schema_module(YAML.safe_load(Scorpio.root.join(
18
+ 'documents/spec.openapis.org/oas/3.0/schema.yaml'
19
+ ).read))
20
+
21
+ describe_schema_ptrs.each do |ptr|
22
+ (Document / ptr).describes_schema!(DIALECT)
23
+ end
24
+
25
+ # naming these is not strictly necessary, but is nice to have.
26
+ # generated: `puts Scorpio::OpenAPI::V3_0::Document.schema.definitions.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = Document.definitions['#{k}']" }`
27
+
28
+
29
+ Reference = Document.definitions['Reference']
30
+ SchemaReference = Document.definitions['SchemaReference']
31
+ Info = Document.definitions['Info']
32
+ Contact = Document.definitions['Contact']
33
+ License = Document.definitions['License']
34
+ Server = Document.definitions['Server']
35
+ ServerVariable = Document.definitions['ServerVariable']
36
+ Components = Document.definitions['Components']
37
+ Schema = Document.definitions['Schema']
38
+ Discriminator = Document.definitions['Discriminator']
39
+ XML = Document.definitions['XML']
40
+ Response = Document.definitions['Response']
41
+ MediaType = Document.definitions['MediaType']
42
+ Example = Document.definitions['Example']
43
+ Header = Document.definitions['Header']
44
+ Paths = Document.definitions['Paths']
45
+ PathItem = Document.definitions['PathItem']
46
+ Operation = Document.definitions['Operation']
47
+ Responses = Document.definitions['Responses']
48
+ SecurityRequirement = Document.definitions['SecurityRequirement']
49
+ Tag = Document.definitions['Tag']
50
+ ExternalDocumentation = Document.definitions['ExternalDocumentation']
51
+ Parameter = Document.definitions['Parameter']
52
+ PathParameter = Document.definitions['PathParameter']
53
+ QueryParameter = Document.definitions['QueryParameter']
54
+ HeaderParameter = Document.definitions['HeaderParameter']
55
+ CookieParameter = Document.definitions['CookieParameter']
56
+ RequestBody = Document.definitions['RequestBody']
57
+ SecurityScheme = Document.definitions['SecurityScheme']
58
+ APIKeySecurityScheme = Document.definitions['APIKeySecurityScheme']
59
+ HTTPSecurityScheme = Document.definitions['HTTPSecurityScheme']
60
+ OAuth2SecurityScheme = Document.definitions['OAuth2SecurityScheme']
61
+ OpenIdConnectSecurityScheme = Document.definitions['OpenIdConnectSecurityScheme']
62
+ OAuthFlows = Document.definitions['OAuthFlows']
63
+ ImplicitOAuthFlow = Document.definitions['ImplicitOAuthFlow']
64
+ PasswordOAuthFlow = Document.definitions['PasswordOAuthFlow']
65
+ ClientCredentialsFlow = Document.definitions['ClientCredentialsFlow']
66
+ AuthorizationCodeOAuthFlow = Document.definitions['AuthorizationCodeOAuthFlow']
67
+ Link = Document.definitions['Link']
68
+ Callback = Document.definitions['Callback']
69
+ Encoding = Document.definitions['Encoding']
70
+
71
+ module Response
72
+ include(OpenAPI::Response)
73
+ end
74
+
75
+ # Describes a single API operation on a path.
76
+ #
77
+ # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject
78
+ module Operation
79
+ include(OpenAPI::Operation::V3Methods)
80
+ end
81
+
82
+ # A document that defines or describes an API conforming to the OpenAPI Specification v3.0.
83
+ #
84
+ # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#oasObject
85
+ module Document
86
+ include(OpenAPI::Document::V3Methods)
87
+
88
+ # @return [#to_hash] /components/schemas, if present, or an empty hash
89
+ def components_schemas
90
+ return JSI::Util::EMPTY_HASH unless key?('components') && components.respond_to?(:to_hash)
91
+ return JSI::Util::EMPTY_HASH unless components.key?('schemas') && components.schemas.respond_to?(:to_hash)
92
+ components.schemas
93
+ end
94
+ end
95
+
96
+ module Reference
97
+ include(OpenAPI::Reference)
98
+ end
99
+
100
+ module Tag
101
+ include(OpenAPI::Tag)
102
+ end
103
+
104
+ Document.properties["tags"].include(OpenAPI::Tags)
105
+
106
+ # An object representing a Server.
107
+ #
108
+ # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#serverObject
109
+ module Server
110
+ include(OpenAPI::Server)
111
+ end
112
+
113
+ module Paths
114
+ include(OpenAPI::Paths)
115
+ end
116
+
117
+ module PathItem
118
+ include(OpenAPI::PathItem)
119
+ include(OpenAPI::Reference)
120
+ end
121
+
122
+ module SecurityScheme
123
+ include(OpenAPI::SecurityScheme)
124
+ end
125
+ end
126
+
127
+ # @deprecated after v0.7
128
+ V3 = V3_0
129
+ end
130
+ end
@@ -0,0 +1,243 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scorpio
4
+ module OpenAPI
5
+ module V3_1
6
+ class << self
7
+ attr_accessor(:document_schema_modules_by_dialect_id)
8
+ end
9
+
10
+ self.document_schema_modules_by_dialect_id = {}
11
+
12
+ def self.document_name_subschemas(document_schema_module, namespace)
13
+ namespace.const_set(:Info, document_schema_module.defs['info'])
14
+ namespace.const_set(:Contact, document_schema_module.defs['contact'])
15
+ namespace.const_set(:License, document_schema_module.defs['license'])
16
+ namespace.const_set(:Server, document_schema_module.defs['server'])
17
+ namespace.const_set(:ServerVariable, document_schema_module.defs['server-variable'])
18
+ namespace.const_set(:Components, document_schema_module.defs['components'])
19
+ namespace.const_set(:Paths, document_schema_module.defs['paths'])
20
+ namespace.const_set(:PathItem, document_schema_module.defs['path-item'])
21
+ namespace.const_set(:Operation, document_schema_module.defs['operation'])
22
+ namespace.const_set(:ExternalDocumentation, document_schema_module.defs['external-documentation'])
23
+ namespace.const_set(:Parameter, document_schema_module.defs['parameter'])
24
+ namespace.const_set(:RequestBody, document_schema_module.defs['request-body'])
25
+ namespace.const_set(:Content, document_schema_module.defs['content'])
26
+ namespace.const_set(:MediaType, document_schema_module.defs['media-type'])
27
+ namespace.const_set(:Encoding, document_schema_module.defs['encoding'])
28
+ namespace.const_set(:Responses, document_schema_module.defs['responses'])
29
+ namespace.const_set(:Response, document_schema_module.defs['response'])
30
+ namespace.const_set(:Callbacks, document_schema_module.defs['callbacks'])
31
+ namespace.const_set(:Example, document_schema_module.defs['example'])
32
+ namespace.const_set(:Link, document_schema_module.defs['link'])
33
+ namespace.const_set(:Header, document_schema_module.defs['header'])
34
+ namespace.const_set(:Tag, document_schema_module.defs['tag'])
35
+ namespace.const_set(:Reference, document_schema_module.defs['reference'])
36
+ namespace.const_set(:Schema, document_schema_module.defs['schema'])
37
+ namespace.const_set(:SecurityScheme, document_schema_module.defs['security-scheme'])
38
+ namespace.const_set(:OAuthFlows, document_schema_module.defs['oauth-flows'])
39
+ namespace.const_set(:SecurityRequirement, document_schema_module.defs['security-requirement'])
40
+ namespace.const_set(:Examples, document_schema_module.defs['examples'])
41
+ namespace.const_set(:MapOfStrings, document_schema_module.defs['map-of-strings'])
42
+ namespace.const_set(:ExplodeForForm, document_schema_module.defs['explode-for-form'])
43
+ namespace.const_set(:SpecificationExtension, document_schema_module.defs['specification-extensions'].patternProperties["^x-"])
44
+ end
45
+
46
+ def self.set_up_document_schema_module(document_schema_module)
47
+ document_schema_module.include(OpenAPI::V3_1::Document)
48
+ document_schema_module.defs['response'].include(OpenAPI::Response)
49
+ document_schema_module.defs['operation'].include(OpenAPI::Operation::V3Methods)
50
+ document_schema_module.defs['reference'].include(OpenAPI::Reference)
51
+ document_schema_module.defs['tag'].include(OpenAPI::Tag)
52
+ document_schema_module.defs['server'].include(OpenAPI::Server)
53
+ document_schema_module.defs['paths'].include(OpenAPI::Paths)
54
+ document_schema_module.defs['path-item'].include(OpenAPI::PathItem)
55
+ document_schema_module.defs['path-item'].include(OpenAPI::Reference)
56
+ document_schema_module.defs['security-scheme'].include(OpenAPI::SecurityScheme)
57
+
58
+ document_schema_module
59
+ end
60
+
61
+ # This is pretty much: `Unscoped::Document.with_dynamic_scope_from(JSI.registry.find(dialect_id))`
62
+ # plus {.set_up_document_schema_module}.
63
+ #
64
+ # However, this also supports a dialect whose meta-schema isn't aware of dynamic scope and doesn't
65
+ # have a `$dynamicAnchor: "meta"`, e.g. `jsonSchemaDialect: "http://json-schema.org/draft-07/schema"`.
66
+ #
67
+ # A schema like {Ext::ExtDocument} exists to `$ref` to {Unscoped::Document} with anchor `meta`
68
+ # in dynamic scope, with the `$dynamicAnchor: "meta"` schema `$ref`ing to {Ext::MetaSchema}.
69
+ # This method obviates the need for such a schema, directly applying dynamic scope.
70
+ def self.document_schema_module_by_dialect_id(dialect_id)
71
+ dialect_uri = JSI::Util.uri(dialect_id)
72
+ document_schema_modules_by_dialect_id[dialect_uri] ||= begin
73
+ metaschema = JSI.registry.find(dialect_uri)
74
+ dynamic_anchor_map = metaschema.jsi_next_schema_dynamic_anchor_map
75
+ unless dynamic_anchor_map.key?('meta')
76
+ # hax: pretend that the identified meta-schema has `$dynamicAnchor: "meta"`
77
+ # this enables e.g. `jsonSchemaDialect: "http://json-schema.org/draft-07/schema"` to work
78
+ # this is non-API JSI internals.
79
+ dynamic_anchor_map = dynamic_anchor_map.merge({
80
+ 'meta' => [metaschema, [].freeze].freeze,
81
+ }).freeze
82
+ end
83
+ document_schema = Unscoped::Document.schema.jsi_with_schema_dynamic_anchor_map(dynamic_anchor_map)
84
+ set_up_document_schema_module(document_schema.jsi_schema_module)
85
+ end
86
+ end
87
+
88
+ # Instantiates `instance` v3.1 OAD with schemas of the dialect indicated by `jsonSchemaDialect`
89
+ # @param instance [#to_hash]
90
+ # @return [JSI::Base + Scorpio::OpenAPI::V3_1::Document]
91
+ def self.new_document(instance, **new_param)
92
+ #jsonSchemaDialect = Scorpio::OpenAPI::V3_1::Unscoped::Document.new_jsi(instance, **new_param).jsonSchemaDialect(use_default: true)
93
+ jsonSchemaDialect = instance.fetch('jsonSchemaDialect') { Unscoped::Document.properties['jsonSchemaDialect'].default }
94
+ document_schema_module = document_schema_module_by_dialect_id(jsonSchemaDialect)
95
+
96
+ document_schema_module.new_jsi(instance, **new_param)
97
+ end
98
+
99
+
100
+ module Document
101
+ include(OpenAPI::Document::V3Methods)
102
+ end
103
+
104
+
105
+ # namespace
106
+ module Unscoped
107
+ end
108
+
109
+ Unscoped::Document = JSI.new_schema_module(
110
+ YAML.safe_load(Scorpio.root.join('documents/spec.openapis.org/oas/3.1/schema.yaml').read),
111
+ )
112
+ # Schema module: describes an OpenAPI document, but not normally instantiated.
113
+ #
114
+ # This document schema has no dynamic scope pointing `$dynamicAnchor: "meta"` to a real
115
+ # meta-schema. Schemas in the document described by this are just `type: [object, boolean]`,
116
+ # have no dialect, and are not usable schemas.
117
+ #
118
+ # - $id: `https://spec.openapis.org/oas/3.1/schema/2025-11-23`
119
+ module Unscoped::Document
120
+ end
121
+
122
+
123
+ # "Ext" is abbreviation for the "OpenAPI extension schema dialect" that extends JSON Schema draft 2020-12
124
+ # and defines keywords: `discriminator`, `example`, `externalDocs`, `xml`.
125
+ # This module is a namespace for that.
126
+ module Ext
127
+ end
128
+
129
+ # vocabulary for implementation of keywords: `discriminator`, `example`, `externalDocs`, `xml`
130
+ Ext::VOCAB = JSI::Schema::Vocabulary.new(
131
+ id: "https://spec.openapis.org/oas/3.1/vocab/base",
132
+ elements: [
133
+ # TODO:
134
+ # - discriminator
135
+ # - example
136
+ # - externalDocs
137
+ # - xml
138
+ ],
139
+ )
140
+ JSI.registry.register_vocabulary(Ext::VOCAB)
141
+
142
+
143
+ Ext::ExtDocument = JSI.new_schema_module(
144
+ YAML.safe_load(Scorpio.root.join('documents/spec.openapis.org/oas/3.1/schema-base.yaml').read),
145
+ )
146
+ # Schema module: Describes an OAD with schemas of the OpenAPI extension schema dialect.
147
+ # This exists to dynamically scope the `meta` anchor
148
+ # for {Unscoped::Document} `<https://spec.openapis.org/oas/3.1/schema/2025-11-23>`
149
+ # to {Ext::MetaSchema} `<https://spec.openapis.org/oas/3.1/dialect/2024-11-10>`
150
+ # via `<#/$defs/schema>` {Ext::ExtDocument::Schema}.
151
+ #
152
+ # - $id: `https://spec.openapis.org/oas/3.1/schema-base/2025-11-23`
153
+ # - $ref: {Ext::Document} `<https://spec.openapis.org/oas/3.1/schema/2025-11-23>`
154
+ # - $dynamicAnchor: `meta` in `/$defs/schema` ({Ext::ExtDocument::Schema})
155
+ # - properties: jsonSchemaDialect const {Ext::MetaSchema} `<https://spec.openapis.org/oas/3.1/dialect/2024-11-10>`
156
+ module Ext::ExtDocument
157
+ end
158
+
159
+ Ext::ExtDocument::Schema = Ext::ExtDocument["$defs"]["schema"]
160
+ # Schema module: Describes schemas in an Ext::Document
161
+ #
162
+ # - $dynamicAnchor: `meta`
163
+ # - $ref: {Ext::MetaSchema} `<https://spec.openapis.org/oas/3.1/dialect/2024-11-10>`
164
+ # - properties: $schema const {Ext::MetaSchema} `<https://spec.openapis.org/oas/3.1/dialect/2024-11-10>`
165
+ module Ext::ExtDocument::Schema
166
+ end
167
+
168
+
169
+ # Some Ext schemas are used with dynamic scope from {Ext::ExtDocument}; Ext::Unscoped namespace
170
+ # contains those schemas without that dynamic scope. These are not normally instantiated.
171
+ module Ext::Unscoped
172
+ end
173
+
174
+ Ext::Unscoped::VocabSchema = JSI.new_schema_module(
175
+ YAML.safe_load(Scorpio.root.join('documents/spec.openapis.org/oas/3.1/meta/base.schema.yaml').read),
176
+ )
177
+ module Ext::Unscoped::VocabSchema
178
+ end
179
+
180
+ Ext::VocabSchema = Ext::Unscoped::VocabSchema.with_dynamic_scope_from(Ext::ExtDocument)
181
+ # Schema module: vocabulary schema for {Ext::VOCAB}
182
+ #
183
+ # - $id: `https://spec.openapis.org/oas/3.1/meta/2024-11-10`
184
+ # - $dynamicAnchor: `meta` (unused)
185
+ # - properties (schema keywords) discriminator, example, externalDocs, xml
186
+ module Ext::VocabSchema
187
+ end
188
+
189
+ Ext::Unscoped::MetaSchema = JSI.new_schema_module(
190
+ YAML.safe_load(Scorpio.root.join('documents/spec.openapis.org/oas/3.1/dialect/base.schema.yaml').read),
191
+ )
192
+ module Ext::Unscoped::MetaSchema
193
+ end
194
+
195
+ Ext::MetaSchema = Ext::Unscoped::MetaSchema.with_dynamic_scope_from(Ext::ExtDocument)
196
+ Ext::MetaSchema.describes_schema!
197
+ # Schema module: Meta-schema describing schemas within an OpenAPI document with the OpenAPI extension schema dialect
198
+ #
199
+ # - $id: `https://spec.openapis.org/oas/3.1/dialect/2024-11-10`
200
+ # - $dynamicAnchor: `meta` (overridden by dynamic scope with `meta` → {Ext::ExtDocument::Schema})
201
+ # - $vocabulary:
202
+ # - The draft/2020-12 vocabularies - core, applicator, validation, etc (required: true)
203
+ # - {Ext::VOCAB} `<https://spec.openapis.org/oas/3.1/vocab/base>` (required: false)
204
+ # - allOf:
205
+ # - $ref: {Ext::JSONSchemaDraft202012} `<https://json-schema.org/draft/2020-12/schema>`
206
+ # - $ref: {Ext::VocabSchema} `<https://spec.openapis.org/oas/3.1/meta/2024-11-10>`
207
+ module Ext::MetaSchema
208
+ end
209
+
210
+ Ext::Document = Unscoped::Document.with_dynamic_scope_from(Ext::ExtDocument)
211
+ # Schema module: Describes an OpenAPI document containing schemas of the Ext dialect.
212
+ # This is {Unscoped::Document}, with dynamic scope pointing `$dynamicAnchor: "meta"` to {Ext::ExtDocument::Schema}.
213
+ module Ext::Document
214
+ end
215
+
216
+ set_up_document_schema_module(Ext::Document)
217
+ document_name_subschemas(Ext::Document, Ext)
218
+ # note: without this mapping set, document_schema_module_by_dialect_id(Ext::MetaSchema.schema_uri)
219
+ # would be Unscoped::Document.with_dynamic_scope_from(Ext::Unscoped::MetaSchema)
220
+ # instead of Unscoped::Document.with_dynamic_scope_from(Ext::ExtDocument)
221
+ # schemas in OADs with this jsonSchemaDialect would have the right dialect, but
222
+ # Ext::ExtDocument does also validate OAD jsonSchemaDialect and schema $schema properties.
223
+ document_schema_modules_by_dialect_id[Ext::MetaSchema.schema_uri] = Ext::ExtDocument
224
+
225
+ Ext::JSONSchemaDraft202012 = JSI::JSONSchemaDraft202012.with_dynamic_scope_from(Ext::ExtDocument)
226
+ JSI::JSONSchemaDraft202012.name_vocab_schemas(Ext::JSONSchemaDraft202012)
227
+ # JSI::JSONSchemaDraft202012, with dynamic scope pointing `$dynamicAnchor: "meta"` to {Ext::ExtDocument::Schema}.
228
+ module Ext::JSONSchemaDraft202012
229
+ end
230
+
231
+
232
+ module JSONSchemaDraft202012
233
+ end
234
+
235
+ JSONSchemaDraft202012::Document = Unscoped::Document.with_dynamic_scope_from(JSI::JSONSchemaDraft202012)
236
+ # Describes an OAD with `jsonSchemaDialect: "https://json-schema.org/draft/2020-12/schema"`
237
+ module JSONSchemaDraft202012::Document
238
+ end
239
+
240
+ document_name_subschemas(JSONSchemaDraft202012::Document, JSONSchemaDraft202012)
241
+ end
242
+ end
243
+ end