Skip to content

[WIP] Alternative OAS3 JSON Schema - #1270

Merged
webron merged 33 commits into
OpenAPI.nextfrom
oas3-schema
Apr 18, 2019
Merged

[WIP] Alternative OAS3 JSON Schema#1270
webron merged 33 commits into
OpenAPI.nextfrom
oas3-schema

Conversation

@webron

Copy link
Copy Markdown
Member

This is a proposal for an alternative JSON Schema provided by #1236.

The work done in #1236 is good but:

  • It is too permissive - it allows things that should not validate, for example, a parameter with both content and schema (and a few other cases).
  • It doesn't cover types properly.
  • As far as I understand, it adds a couple of restrictions that are not provided in the spec itself.

The reasons for creating a new PR instead of suggesting fixes to #1236:

  • Editing JSON Schemas can be a pain, and trying to navigate through different styles is not easy.
  • The amount of changes to make it less permissive is quite large.
  • I had about 80% of the schema ready...

Odd things about this version:

  • It's a JSON Schema written in YAML. It's just easier to write and edit and.. read. Easy enough to convert to JSON if this PR is accepted.
  • To make it more restrictive, there are somewhat a lot of duplication in the schema. Using allOf to reuse things didn't work for me due to additionalProperties: false. If anyone wants to take a crack and minimizing it, by all means... I just wanted to get a working version.

What's missing:

  • It doesn't cover checking of style/explode inside Request Bodies where it's multipart. That was just too much for me for now.
  • Testing. I'm willing to bet there are bugs in it.
  • JSON Schema decorations (version, id, $schema and so on).

Pinging @darrelmiller as he asked to be pinged.

Pinging @MikeRalphson for assistance in testing - would really appreciate if you can take a crack at it.

I'm sure converting the YAML to JSON is going to be easy enough.

@fehguy

Copy link
Copy Markdown
Contributor

👍

@timburks

Copy link
Copy Markdown
Contributor

Glad to see effort going into this. The schema that I automatically generated from the spec is easy to reexport as YAML, which I'll append for comparison. The most apparent difference to me is that you've flattened out some intermediate structures.

title: A JSON Schema for OpenAPI 3.0.
id: http://openapis.org/v3/schema.json#
$schema: http://json-schema.org/draft-04/schema#
type: object
description: This is the root document object of the OpenAPI definition.
required:
- openapi
- info
- paths
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
openapi:
type: string
info:
$ref: '#/definitions/info'
servers:
type: array
items:
$ref: '#/definitions/server'
uniqueItems: true
paths:
$ref: '#/definitions/paths'
components:
$ref: '#/definitions/components'
security:
type: array
items:
$ref: '#/definitions/securityRequirement'
uniqueItems: true
tags:
type: array
items:
$ref: '#/definitions/tag'
uniqueItems: true
externalDocs:
$ref: '#/definitions/externalDocs'
definitions:
info:
type: object
description: The object provides metadata about the API. The metadata can be used
by the clients if needed, and can be presented in editing or documentation generation
tools for convenience.
required:
- title
- version
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
title:
type: string
description:
type: string
termsOfService:
type: string
contact:
$ref: '#/definitions/contact'
license:
$ref: '#/definitions/license'
version:
type: string
contact:
type: object
description: Contact information for the exposed API.
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
name:
type: string
url:
type: string
email:
type: string
license:
type: object
description: License information for the exposed API.
required:
- name
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
name:
type: string
url:
type: string
server:
type: object
description: An object representing a Server.
required:
- url
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
url:
type: string
description:
type: string
variables:
$ref: '#/definitions/serverVariables'
serverVariable:
type: object
description: An object representing a Server Variable for server URL template
substitution.
required:
- default
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
enum:
type: array
items:
type: string
uniqueItems: true
default:
type: string
description:
type: string
components:
type: object
description: Holds a set of reusable objects for different aspects of the OAS.
All objects defined within the components object will have no effect on the
API unless they are explicitly referenced from properties outside the components
object.
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
schemas:
$ref: '#/definitions/schemasOrReferences'
responses:
$ref: '#/definitions/responsesOrReferences'
parameters:
$ref: '#/definitions/parametersOrReferences'
examples:
$ref: '#/definitions/examplesOrReferences'
requestBodies:
$ref: '#/definitions/requestBodiesOrReferences'
headers:
$ref: '#/definitions/headersOrReferences'
securitySchemes:
$ref: '#/definitions/securitySchemesOrReferences'
links:
$ref: '#/definitions/linksOrReferences'
callbacks:
$ref: '#/definitions/callbacksOrReferences'
paths:
type: object
description: Holds the relative paths to the individual endpoints and their operations.
The path is appended to the URL from the `Server Object` in order to construct
the full URL. The Paths MAY be empty, due to ACL constraints.
additionalProperties: false
patternProperties:
^/:
$ref: '#/definitions/pathItem'
^x-:
$ref: '#/definitions/specificationExtension'
pathItem:
type: object
description: Describes the operations available on a single path. A Path Item
MAY be empty, due to ACL constraints. The path itself is still exposed to the
documentation viewer but they will not know which operations and parameters
are available.
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
$ref:
type: string
summary:
type: string
description:
type: string
get:
$ref: '#/definitions/operation'
put:
$ref: '#/definitions/operation'
post:
$ref: '#/definitions/operation'
delete:
$ref: '#/definitions/operation'
options:
$ref: '#/definitions/operation'
head:
$ref: '#/definitions/operation'
patch:
$ref: '#/definitions/operation'
trace:
$ref: '#/definitions/operation'
servers:
type: array
items:
$ref: '#/definitions/server'
uniqueItems: true
parameters:
type: array
items:
$ref: '#/definitions/parameterOrReference'
uniqueItems: true
operation:
type: object
description: Describes a single API operation on a path.
required:
- responses
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
tags:
type: array
items:
type: string
uniqueItems: true
summary:
type: string
description:
type: string
externalDocs:
$ref: '#/definitions/externalDocs'
operationId:
type: string
parameters:
type: array
items:
$ref: '#/definitions/parameterOrReference'
uniqueItems: true
requestBody:
$ref: '#/definitions/requestBodyOrReference'
responses:
$ref: '#/definitions/responses'
callbacks:
$ref: '#/definitions/callbacksOrReferences'
deprecated:
type: boolean
security:
type: array
items:
$ref: '#/definitions/securityRequirement'
uniqueItems: true
servers:
type: array
items:
$ref: '#/definitions/server'
uniqueItems: true
externalDocs:
type: object
description: Allows referencing an external resource for extended documentation.
required:
- url
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
description:
type: string
url:
type: string
parameter:
type: object
description: Describes a single operation parameter. A unique parameter is defined
by a combination of a name and location.
required:
- name
- in
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
name:
type: string
in:
type: string
description:
type: string
required:
type: boolean
deprecated:
type: boolean
allowEmptyValue:
type: boolean
style:
type: string
explode:
type: boolean
allowReserved:
type: boolean
schema:
$ref: '#/definitions/schemaOrReference'
example:
$ref: '#/definitions/any'
examples:
$ref: '#/definitions/examplesOrReferences'
content:
$ref: '#/definitions/mediaTypes'
requestBody:
type: object
description: Describes a single request body.
required:
- content
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
description:
type: string
content:
$ref: '#/definitions/mediaTypes'
required:
type: boolean
mediaType:
type: object
description: Each Media Type Object provides schema and examples for the media
type identified by its key.
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
schema:
$ref: '#/definitions/schemaOrReference'
example:
$ref: '#/definitions/any'
examples:
$ref: '#/definitions/examplesOrReferences'
encoding:
$ref: '#/definitions/encodings'
encoding:
type: object
description: A single encoding definition applied to a single schema property.
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
contentType:
type: string
headers:
$ref: '#/definitions/headers'
style:
type: string
explode:
type: boolean
allowReserved:
type: boolean
responses:
type: object
description: A container for the expected responses of an operation. The container
maps a HTTP response code to the expected response. The documentation is not
necessarily expected to cover all possible HTTP response codes because they
may not be known in advance. However, documentation is expected to cover a successful
operation response and any known errors. The `default` MAY be used as a default
response object for all HTTP codes that are not covered individually by the
specification. The `Responses Object` MUST contain at least one response code,
and it SHOULD be the response for a successful operation call.
additionalProperties: false
patternProperties:
^([0-9X]{3})$:
$ref: '#/definitions/responseOrReference'
^x-:
$ref: '#/definitions/specificationExtension'
properties:
default:
$ref: '#/definitions/responseOrReference'
response:
type: object
description: Describes a single response from an API Operation, including design-time,
static `links` to operations based on the response.
required:
- description
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
description:
type: string
headers:
$ref: '#/definitions/headersOrReferences'
content:
$ref: '#/definitions/mediaTypes'
links:
$ref: '#/definitions/linksOrReferences'
callback:
type: object
description: A map of possible out-of band callbacks related to the parent operation.
Each value in the map is a Path Item Object that describes a set of requests
that may be initiated by the API provider and the expected responses. The key
value used to identify the callback object is an expression, evaluated at runtime,
that identifies a URL to use for the callback operation.
additionalProperties: false
patternProperties:
^:
$ref: '#/definitions/pathItem'
^x-:
$ref: '#/definitions/specificationExtension'
example:
type: object
description: ""
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
summary:
type: string
description:
type: string
value:
$ref: '#/definitions/any'
externalValue:
type: string
link:
type: object
description: The `Link object` represents a possible design-time link for a response.
The presence of a link does not guarantee the caller's ability to successfully
invoke it, rather it provides a known relationship and traversal mechanism between
responses and other operations. Unlike _dynamic_ links (i.e. links provided
**in** the response payload), the OAS linking mechanism does not require link
information in the runtime response. For computing links, and providing instructions
to execute them, a runtime expression is used for accessing values in an operation
and using them as parameters while invoking the linked operation.
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
operationRef:
type: string
operationId:
type: string
parameters:
$ref: '#/definitions/anysOrExpressions'
requestBody:
$ref: '#/definitions/anyOrExpression'
description:
type: string
server:
$ref: '#/definitions/server'
header:
type: object
description: 'The Header Object follows the structure of the Parameter Object
with the following changes: 1. `name` MUST NOT be specified, it is given in
the corresponding `headers` map. 1. `in` MUST NOT be specified, it is implicitly
in `header`. 1. All traits that are affected by the location MUST be applicable
to a location of `header` (for example, `style`).'
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
description:
type: string
required:
type: boolean
deprecated:
type: boolean
allowEmptyValue:
type: boolean
style:
type: string
explode:
type: boolean
allowReserved:
type: boolean
schema:
$ref: '#/definitions/schemaOrReference'
example:
$ref: '#/definitions/any'
examples:
$ref: '#/definitions/examplesOrReferences'
content:
$ref: '#/definitions/mediaTypes'
tag:
type: object
description: Adds metadata to a single tag that is used by the Operation Object.
It is not mandatory to have a Tag Object per tag defined in the Operation Object
instances.
required:
- name
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
name:
type: string
description:
type: string
externalDocs:
$ref: '#/definitions/externalDocs'
examples:
type: object
description: ""
additionalProperties: false
reference:
type: object
description: A simple object to allow referencing other components in the specification,
internally and externally. The Reference Object is defined by JSON Reference
and follows the same structure, behavior and rules. For this specification,
reference resolution is accomplished as defined by the JSON Reference specification
and not by the JSON Schema specification.
required:
- $ref
additionalProperties: false
properties:
$ref:
type: string
schema:
type: object
description: The Schema Object allows the definition of input and output data
types. These types can be objects, but also primitives and arrays. This object
is an extended subset of the JSON Schema Specification Wright Draft 00. For
more information about the properties, see JSON Schema Core and JSON Schema
Validation. Unless stated otherwise, the property definitions follow the JSON
Schema.
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
nullable:
type: boolean
discriminator:
$ref: '#/definitions/discriminator'
readOnly:
type: boolean
writeOnly:
type: boolean
xml:
$ref: '#/definitions/xml'
externalDocs:
$ref: '#/definitions/externalDocs'
example:
$ref: '#/definitions/any'
deprecated:
type: boolean
title:
$ref: http://json-schema.org/draft-04/schema#/properties/title
multipleOf:
$ref: http://json-schema.org/draft-04/schema#/properties/multipleOf
maximum:
$ref: http://json-schema.org/draft-04/schema#/properties/maximum
exclusiveMaximum:
$ref: http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum
minimum:
$ref: http://json-schema.org/draft-04/schema#/properties/minimum
exclusiveMinimum:
$ref: http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum
maxLength:
$ref: http://json-schema.org/draft-04/schema#/properties/maxLength
minLength:
$ref: http://json-schema.org/draft-04/schema#/properties/minLength
pattern:
$ref: http://json-schema.org/draft-04/schema#/properties/pattern
maxItems:
$ref: http://json-schema.org/draft-04/schema#/properties/maxItems
minItems:
$ref: http://json-schema.org/draft-04/schema#/properties/minItems
uniqueItems:
$ref: http://json-schema.org/draft-04/schema#/properties/uniqueItems
maxProperties:
$ref: http://json-schema.org/draft-04/schema#/properties/maxProperties
minProperties:
$ref: http://json-schema.org/draft-04/schema#/properties/minProperties
required:
$ref: http://json-schema.org/draft-04/schema#/properties/required
enum:
$ref: http://json-schema.org/draft-04/schema#/properties/enum
type:
type: string
allOf:
type: array
items:
$ref: '#/definitions/schemaOrReference'
minItems: 1
oneOf:
type: array
items:
$ref: '#/definitions/schemaOrReference'
minItems: 1
anyOf:
type: array
items:
$ref: '#/definitions/schemaOrReference'
minItems: 1
not:
$ref: '#/definitions/schema'
items:
anyOf:
- $ref: '#/definitions/schemaOrReference'
- type: array
items:
$ref: '#/definitions/schemaOrReference'
minItems: 1
properties:
type: object
additionalProperties:
$ref: '#/definitions/schemaOrReference'
additionalProperties:
oneOf:
- $ref: '#/definitions/schemaOrReference'
- type: boolean
default:
$ref: '#/definitions/defaultType'
description:
type: string
format:
type: string
discriminator:
type: object
description: When request bodies or response payloads may be one of a number of
different schemas, a `discriminator` object can be used to aid in serialization,
deserialization, and validation. The discriminator is a specific object in
a schema which is used to inform the consumer of the specification of an alternative
schema based on the value associated with it. When using the discriminator,
_inline_ schemas will not be considered.
required:
- propertyName
additionalProperties: false
properties:
propertyName:
type: string
mapping:
$ref: '#/definitions/strings'
xml:
type: object
description: A metadata object that allows for more fine-tuned XML model definitions. When
using arrays, XML element names are *not* inferred (for singular/plural forms)
and the `name` property SHOULD be used to add that information. See examples
for expected behavior.
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
name:
type: string
namespace:
type: string
prefix:
type: string
attribute:
type: boolean
wrapped:
type: boolean
securityScheme:
type: object
description: Defines a security scheme that can be used by the operations. Supported
schemes are HTTP authentication, an API key (either as a header or as a query
parameter), OAuth2's common flows (implicit, password, application and access
code) as defined in RFC6749, and OpenID Connect Discovery.
required:
- type
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
type:
type: string
description:
type: string
name:
type: string
in:
type: string
scheme:
type: string
bearerFormat:
type: string
flows:
$ref: '#/definitions/oauthFlows'
openIdConnectUrl:
type: string
oauthFlows:
type: object
description: Allows configuration of the supported OAuth Flows.
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
implicit:
$ref: '#/definitions/oauthFlow'
password:
$ref: '#/definitions/oauthFlow'
clientCredentials:
$ref: '#/definitions/oauthFlow'
authorizationCode:
$ref: '#/definitions/oauthFlow'
oauthFlow:
type: object
description: Configuration details for a supported OAuth Flow
additionalProperties: false
patternProperties:
^x-:
$ref: '#/definitions/specificationExtension'
properties:
authorizationUrl:
type: string
tokenUrl:
type: string
refreshUrl:
type: string
scopes:
$ref: '#/definitions/strings'
securityRequirement:
type: object
description: Lists the required security schemes to execute this operation. The
name used for each property MUST correspond to a security scheme declared in
the Security Schemes under the Components Object. Security Requirement Objects
that contain multiple schemes require that all schemes MUST be satisfied for
a request to be authorized. This enables support for scenarios where multiple
query parameters or HTTP headers are required to convey security information. When
a list of Security Requirement Objects is defined on the Open API object or
Operation Object, only one of Security Requirement Objects in the list needs
to be satisfied to authorize the request.
additionalProperties: false
patternProperties:
^[a-zA-Z0-9\.\-_]+$:
type: array
items:
type: string
uniqueItems: true
anyOrExpression:
oneOf:
- $ref: '#/definitions/any'
- $ref: '#/definitions/expression'
callbackOrReference:
oneOf:
- $ref: '#/definitions/callback'
- $ref: '#/definitions/reference'
exampleOrReference:
oneOf:
- $ref: '#/definitions/example'
- $ref: '#/definitions/reference'
headerOrReference:
oneOf:
- $ref: '#/definitions/header'
- $ref: '#/definitions/reference'
linkOrReference:
oneOf:
- $ref: '#/definitions/link'
- $ref: '#/definitions/reference'
parameterOrReference:
oneOf:
- $ref: '#/definitions/parameter'
- $ref: '#/definitions/reference'
requestBodyOrReference:
oneOf:
- $ref: '#/definitions/requestBody'
- $ref: '#/definitions/reference'
responseOrReference:
oneOf:
- $ref: '#/definitions/response'
- $ref: '#/definitions/reference'
schemaOrReference:
oneOf:
- $ref: '#/definitions/schema'
- $ref: '#/definitions/reference'
securitySchemeOrReference:
oneOf:
- $ref: '#/definitions/securityScheme'
- $ref: '#/definitions/reference'
anysOrExpressions:
type: object
additionalProperties:
$ref: '#/definitions/anyOrExpression'
callbacksOrReferences:
type: object
additionalProperties:
$ref: '#/definitions/callbackOrReference'
encodings:
type: object
additionalProperties:
$ref: '#/definitions/encoding'
examplesOrReferences:
type: object
additionalProperties:
$ref: '#/definitions/exampleOrReference'
headers:
type: object
additionalProperties:
$ref: '#/definitions/header'
headersOrReferences:
type: object
additionalProperties:
$ref: '#/definitions/headerOrReference'
linksOrReferences:
type: object
additionalProperties:
$ref: '#/definitions/linkOrReference'
mediaTypes:
type: object
additionalProperties:
$ref: '#/definitions/mediaType'
parametersOrReferences:
type: object
additionalProperties:
$ref: '#/definitions/parameterOrReference'
requestBodiesOrReferences:
type: object
additionalProperties:
$ref: '#/definitions/requestBodyOrReference'
responsesOrReferences:
type: object
additionalProperties:
$ref: '#/definitions/responseOrReference'
schemasOrReferences:
type: object
additionalProperties:
$ref: '#/definitions/schemaOrReference'
securitySchemesOrReferences:
type: object
additionalProperties:
$ref: '#/definitions/securitySchemeOrReference'
serverVariables:
type: object
additionalProperties:
$ref: '#/definitions/serverVariable'
strings:
type: object
additionalProperties:
type: string
object:
type: object
additionalProperties: true
any:
additionalProperties: true
expression:
type: object
additionalProperties: true
specificationExtension:
description: Any property starting with x- is valid.
oneOf:
- type: "null"
- type: number
- type: boolean
- type: string
- type: object
- type: array
defaultType:
oneOf:
- type: "null"
- type: array
- type: object
- type: number
- type: boolean
- type: string

@MikeRalphsonMikeRalphson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the changes implied by these comments I was able to validate a converted v2 petstore definition. It is possible by making change X I have weakened the schema, thus missing problem Y, so I would appreciate your thoughts @webron on each individually.

Comment threadschemas/v3.0/schema.yaml Outdated
openapi:
type: string
enum:
- 3.0.0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced this with a regex pattern to allow testing of 3.0.0-rc2 compatible documents. This would keep schema churn to a minimum.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this should actually change to a pattern that matches anything starting with 3.0 as according to semver, any patch level changes should not affect validation. That said, I'm embarrassed to say I've avoided learning regex as much as I can 😁

Comment threadschemas/v3.0/schema.yaml Outdated
type: string
type:
type: string
enum:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect indentation of enum:. Yay, yaml FTW! :)

Comment threadschemas/v3.0/schema.yaml Outdated

Responses:
allOf:
- $ref: '#/definitions/Extensions'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is 'Extensions' defined anywhere? Should the following be part of the allOf array?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was from a previous life of the schema, and I just forgot to remove it. Will do.

Comment threadschemas/v3.0/schema.yaml Outdated
Reference:
type: object
properties:
$ref:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should $ref be a required property? Otherwise I get false matches of both halves of a oneOf.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, will fix.

type: object
patternProperties:
'^[a-zA-Z0-9\.\-_]+$':
oneOf:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both Schema and Reference include a $ref property and Reference allows additionalProperties. Either change to anyOf or disallow additionalProperties in Reference objects - which would be a change to the OAS? Multiple examples.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an excellent question. I've revisited the spec. I'll either make it anyOf or remove the $ref from the Schema definition and make sure it's always interchangeable with the Reference in the JSON Schema.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeRalphson - not that I recall, but it seems that right now, there's no $ref property in Schema and it stayed oneOf wherever a Schema is called. I think that should be fine. Please let me know what you're thinking - if it's fine, I'll create a definition for SchemaOrReference and use that across the board instead of repeating the oneOf everywhere.

Comment threadschemas/v3.0/schema.yaml Outdated
not:
type: object
patternProperties:
'^x-': {}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this requires an additionalProperties: false otherwise responses do match this sub-schema.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@webron@MikeRalphson why is this not: block here? Filtering out other stuff, you have

Responses:
type: objectpatternProperties:
'^x-': {}additionalProperties: falsenot:
type: objectpatternProperties:
'^x-': {}additionalProperties: false

which is an impossible schema. {"type": "object", "not": {"type": "object"}} alone makes it impossible. Any keywords that are the same inside and outside of a not by definition are impossible to satisfy.

Am I misreading something here?

Comment threadschemas/v3.0/schema.yaml Outdated
'^x-': {}
additionalProperties: false

ParameterWithSchemaWithExamples:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I seem to be having problems with these - as neither example or examples is required in ParameterWithSchemaWithExampleInQuery or ParameterWithSchemaWithExamplesInQuery and there is no ParameterWithSchemaWithoutExampleInQuery type, how is the correct type to match the oneOf to be determined?

I temporarily changed examples to be a required property in ParameterWithSchemaWithExamplesIn*.


Schema:
type: object
properties:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the Schema object missing the items property?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, will add.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
examples:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, I had to make examples a required property to make the oneOf detection work correctly.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this one?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I made this edit after another comment, but it appears earlier in the schema. I believe this is MediaTypeWithExample vs MediaTypeWithExamples. A media-type object with neither example or examples properties matches both definitions. Suggest making examples a required property of MediaTypeWithExamples.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeRalphson

You can also make things mutually exclusive by forbidding the other property rather than requiring the property that you have:

oneOf:
- properties:
example:
properties:
...examples:
not: {}
- properties:
example:
not: {}examples:
patternProperties:
...

Comment threadschemas/v3.0/schema.yaml Outdated
attribute:
type: boolean
default: false
wrapperd:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo wrapperd for wrapped.

- number
- object
- string
not:

@MikeRalphsonMikeRalphsonJul 14, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we also missing definitions for the allOf, oneOf and anyOf arrays?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, added.

Comment threadschemas/v3.0/schema.yaml Outdated
default: {}
$ref:
type: string
format: uri-ref

@MikeRalphsonMikeRalphsonJul 14, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this uriref in Draft 05? Multiple occurences.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's a miss on my part. I actually looked it up before, and went with the wrong one 😕

Comment threadschemas/v3.0/schema.yaml Outdated
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
examples:
type: array

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct, or is it now a Map as elsewhere?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires a complete rework.

Comment threadschemas/v3.0/schema.yaml Outdated
description:
type: string
value:
type: string

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example.value should be an empty schema?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, will fix.

@MikeRalphson

MikeRalphson commented Jul 14, 2017

Copy link
Copy Markdown
Member

@webron with the changes implied by the above comments, I've now successfully validated 574 converted OpenAPI v2 definitions - with only one minor bug found in my converter (thank you).

Does

JSON Schema decorations

include descriptions ? It would be great to port those across to aid readability. Also required in future iteration: readme.

Comment threadschemas/v3.0/schema.yaml Outdated
type: string
namespace:
type: string
format: url

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this format be uri-ref / uriref not url ?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. We changed the namespace definition to be an absolute URI.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @webron, as far as I can tell, url isn't a format that's in the official specification. It sounds like you're implying here that it's like a uri, but required to be absolute?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uri is the correct format for an absolute URI.
uriref (which became uri-reference in the next draft so that is really preferred) is for relative URI references.

It would be really great to avoid url as having uri and url is horribly confusing, particularly as RFC 3986 goes out of its way to explain that there is no hard line between url and urn (e.g. you cannot make the distinction based on the scheme)

@handrewshandrewsNov 8, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@webron@MikeRalphson I'm planning to change this to uri-reference, even if we are saying that this schema is a draft-wright-*-00 (a.k.a. "draft-05") schema, and that draft used uriref. I'm doing this because:

  1. Literally no one has ever implemented a validator for draft-wright-*-00. Every implementation that I'm aware of skipped from draft-04 (draft-zyp-json-schema-04 + draft-fge-json-schema-validation-00) to draft-06 (draft-wright-*-01).
  2. There is no official meta-schema for draft-05, and there never will be, so there is no programmatic way to inform validators that you are using draft-05
  3. Format is extensible, so using uri-reference as a format is entirely valid even in draft-05

Any objections?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we could also publish a draft-06 version where uri-reference would be recognized, but format validation in general is such a hazy weird optional thing that, really, no one should rely on it unless they are using a specific validator and know it's exact behavior. e.g. some validators try to use a complex regex to validate the email format, but at least one popular validator just checks to see if there's an "@" character in it somewhere and doesn't bother with anything else.

@webron

Copy link
Copy Markdown
MemberAuthor

@MikeRalphson No, I wasn't referring to adding the descriptions as part of the decorations. While important, those are nice to have, and I don't necessarily have the capacity to add those right now all around the schema. If we decide to merge this schema (with it's JSON representation, of course), it'd be great if people helped contributing descriptions.

Right now I'm more focused on the functionality.

@webron

Copy link
Copy Markdown
MemberAuthor

@MikeRalphson Updated with your comments a possible a few additional things I've found. Would appreciate a second look.

Comment threadschemas/v3.0/schema.yaml Outdated
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
items:
type: array

@tfesenkotfesenkoJul 18, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering why array's item type is array and not an object(schema or reference). From the OAS3 spec:

items - Value MUST be an object and not an array. Inline or referenced schema MUST be of a Schema Object and not a standard JSON Schema. <...>

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's me being too tired. Good catch, will fix shortly.

Comment threadschemas/v3.0/schema.yaml Outdated
type: object
required:
- type
- openIdConnect

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think this should be openIdConnectUrl.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed.

Comment threadschemas/v3.0/schema.yaml Outdated
properties:
url:
type: string
format: uriref

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be a uri-template format, but this doesn't exist in JSON schema Draft 5. If we replace format with pattern a suitable regex seems to be (lifted from ajv which quotes https://tools.ietf.org/html/rfc6570)

/^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#.\/;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?:\:[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?:\:[1-9][0-9]{0,3}|\*)?)*\})*$/i

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not get into that. I'm not sure what the difference is between uriref and uri-template. If it's too permissive, in this case, I'm ok with it. If it's too constrict, we can drop the format altogether.

I find adding complex regex impossible to read and maintain. We could have added a regex to define media types as well and we didn't (we had a similar discussion about it around 2.0's schema).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. I'm seeing that it's too restrictive being a uriref, not allowing {} characters. Dropping the format seems the best option.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, will remove.

Comment threadschemas/v3.0/schema.yaml Outdated
$ref: '#/definitions/PathItem'
patternProperties:
'^x-': {}
additionalProperties: false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The additionalProperties: false should be removed, as it overrides the other additionalProperties property when converted to JSON. This is the only such occurrence.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@webron

Copy link
Copy Markdown
MemberAuthor

@MikeRalphson thanks for the review - any other comments or thoughts?

Comment threadschemas/v3.0/schema.yaml Outdated
callbacks:
type: object
additionalProperties:
$ref: '#/definitions/Callback'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not it be Callback or Reference:

callbacks | Map[string, Callback Object | Reference Object]

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup.

@MikeRalphson

Copy link
Copy Markdown
Member

@webron I'm seeing a rate of about 0.35% errors over 35,000 converted 'valid' OpenAPI 2.0 documents. All the failures appear to be correctly identified - i.e. things which have changed in the spec that the converter can't process automatically or that the 2.0 validators missed. This schema certainly validates more of the spec than the @timburks / #1236 schema, but I do think you may have made a rod for your own back by multiplying out the combinations of (for example) parameters in X with/without Y with/without a Z.

What I'm trying to say is that maintenance of this schema is going to be somewhat harder going forward (depending on what changes come along in 3.1 and subsequent minor versions) as it has more redundancy and a slightly different mental model to grok rather than modelling only those objects that the spec defines. When (if?) we have a test-suite and a reference parser/validator, some of these issues go away.

The schema seems to be performant even on large documents, though the use of oneOf means more verbose error messages, at least from the schema validator I'm using.

That said... add the id and schema properties and :shipit:

@webron

Copy link
Copy Markdown
MemberAuthor

@MikeRalphson Thanks for the feedback.

Trust me, I don't like the breakdown as it is now, and I agree it's harder to maintain. The issue is derived from the way JSON Schema combines allOf with additionalProperties: false. If I had a solution to that, it'd make things much much easier. I only need one example of making it work and I can convert the rest.

Comment threadschemas/v3.0/schema.yaml Outdated
- default
properties:
enum:
type: string

@MikeRalphsonMikeRalphsonJul 21, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serverVariables.enum should be an array of string. @webron

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, should be fixed now.

@hkosova

Copy link
Copy Markdown
Contributor

@webron In this schema, ParameterWithContentNotInPath object seems to be missing the allowReserved property. Is this a bug or by design (e.g. allowReserved is incompatible with content)?

@webron

Copy link
Copy Markdown
MemberAuthor

@hkosova it's by design. A parameter can have either content or a schema, and style, explode and so on are only applicable with schema.

@hkosova

Copy link
Copy Markdown
Contributor

@webronopenIdConnectUrl has format: url - shouldn't it be uriref (relative URL allowed) like tokenUrl etc?

@MikeRalphson

Copy link
Copy Markdown
Member

Thanks for clarifying @handrews - I obviously misremembered the situation.

Ronand others added 2 commits March 26, 2019 12:40
Reduce duplications and complexity in v3.0 JSON Schema
Added `id`, `$schema`, `description`
@webron

Copy link
Copy Markdown
MemberAuthor

@MikeRalphson, @handrews - I've updated the schema with the missing metadata (id, $schema, description). Can you please run final validation and tests on it?

If you give the thumbs up, and we get an official @OAI/tsc vote, I'll add a conversion of the schema to JSON, and merge it (at last).

@handrewshandrews left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newly added metadata looks good, @webron

@philsturgeon

Copy link
Copy Markdown
Contributor

🚢 🚢 🚢 🚢 🚢 🚢 🚢 🚢

@MikeRalphsonMikeRalphson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. No regressions found.

@webron

Copy link
Copy Markdown
MemberAuthor

Added the converted JSON file and a README with some relevant details.

Thanks everyone for making it happen.

@webron
webron merged commit ce93fe4 into OpenAPI.nextApr 18, 2019
@philsturgeon

Copy link
Copy Markdown
Contributor

giphy

Great work everyone, this is awesome.

@cebe

cebe commented Apr 18, 2019

Copy link
Copy Markdown
Contributor

this has been merged into OpenAPI.next, what is the process or what are the plans of getting it into master?

@webron

Copy link
Copy Markdown
MemberAuthor

@cebesigh just shows how old this PR was. I'll move the content to master as well. Thanks for catching it.

@webronwebron mentioned this pull request Apr 18, 2019
shockey added a commit to swagger-api/swagger-editor that referenced this pull request Apr 18, 2019
* adopt @webron's OpenAPI 3.0 schema from OAI/OpenAPI-Specification#1270
permalink: https://github.com/OAI/OpenAPI-Specification/blob/92e15eba1d4591ebfe8c11898c48241e72854381/schemas/v3.0/schema.yaml
* add ajv-errors
* address error messages for #1808's Swagger 2.0 example
clarifies the schema and adds custom error messages for unclear error conditions
* address error messages for #1808's OpenAPI 3.0 example
* restrict underlying JSON Schema `type` field to simple types only (for #1832)
* fix limitation in JSON Pointer conversion helper
* add clear `not` error message (for #1489)
* add additionalProperties message (for #1394)
* add ajv-keywords
* use `switch` to intelligently identify inline vs referenced content (for #1853)
* use `switch` to XOR `schema` and `content` (for #1853)
* use `switch` to pivot security scheme based on type
(for #1672)
* use switch to fall-through to inline security scheme validation (for #1672)
* rewrite more Reference oneOfs (for #1519)
* add custom message for `Schema.required` type error (for #1519)
* rewrite Response/Reference oneOf (for #1489)
* use switch in ParameterLocation validation (for #1797)
* define pivot key switches for SecurityDefinitions (for #1711)
* give helpful `format: uri` messages for SecurityDefinitions (for #1711)
* eliminate NonBodyParameter; pivot on `Parameter.in` with a switch (for #1511)
* oneOf -> switch for Parameters.items reference
* (for #1711)
* remove redundant semantic validator (for #1511)
* adjust wording of custom error message (for #1853)
* add regression tests for all related issues
* revert to expect@^1.20.2
* linter fixes
* fix messaging flaw for #1832
* improve messaging for #1394
* use literal key for `$ref` in Reference Object
* remove commented legacy data from OAS3 schema
* remove superfluous quotation marks
* normalize test case paths to `/`
* normalize openapi fields to 3.0.0
* drop unused `paths` information
* ensure clear errors for 3.0 Parameter style/content exclusivity
* add `required` assertions to switch statements that pivot on a key's value
this prevents false positives when the pivot key is missing entirely
* remove stray space
@cebe

cebe commented Apr 19, 2019

Copy link
Copy Markdown
Contributor

hehe, I thought this was part of your workflow :)

shockey added a commit to shockey/swagger-editor that referenced this pull request May 23, 2019
shockey added a commit to shockey/swagger-editor that referenced this pull request May 23, 2019
…i#1985)
* adopt @webron's OpenAPI 3.0 schema from OAI/OpenAPI-Specification#1270
permalink: https://github.com/OAI/OpenAPI-Specification/blob/92e15eba1d4591ebfe8c11898c48241e72854381/schemas/v3.0/schema.yaml
* add ajv-errors
* address error messages for swagger-api#1808's Swagger 2.0 example
clarifies the schema and adds custom error messages for unclear error conditions
* address error messages for swagger-api#1808's OpenAPI 3.0 example
* restrict underlying JSON Schema `type` field to simple types only (for swagger-api#1832)
* fix limitation in JSON Pointer conversion helper
* add clear `not` error message (for swagger-api#1489)
* add additionalProperties message (for swagger-api#1394)
* add ajv-keywords
* use `switch` to intelligently identify inline vs referenced content (for swagger-api#1853)
* use `switch` to XOR `schema` and `content` (for swagger-api#1853)
* use `switch` to pivot security scheme based on type
(for swagger-api#1672)
* use switch to fall-through to inline security scheme validation (for swagger-api#1672)
* rewrite more Reference oneOfs (for swagger-api#1519)
* add custom message for `Schema.required` type error (for swagger-api#1519)
* rewrite Response/Reference oneOf (for swagger-api#1489)
* use switch in ParameterLocation validation (for swagger-api#1797)
* define pivot key switches for SecurityDefinitions (for swagger-api#1711)
* give helpful `format: uri` messages for SecurityDefinitions (for swagger-api#1711)
* eliminate NonBodyParameter; pivot on `Parameter.in` with a switch (for swagger-api#1511)
* oneOf -> switch for Parameters.items reference
* (for swagger-api#1711)
* remove redundant semantic validator (for swagger-api#1511)
* adjust wording of custom error message (for swagger-api#1853)
* add regression tests for all related issues
* revert to expect@^1.20.2
* linter fixes
* fix messaging flaw for swagger-api#1832
* improve messaging for swagger-api#1394
* use literal key for `$ref` in Reference Object
* remove commented legacy data from OAS3 schema
* remove superfluous quotation marks
* normalize test case paths to `/`
* normalize openapi fields to 3.0.0
* drop unused `paths` information
* ensure clear errors for 3.0 Parameter style/content exclusivity
* add `required` assertions to switch statements that pivot on a key's value
this prevents false positives when the pivot key is missing entirely
* remove stray space
@webron
webron deleted the oas3-schema branch April 23, 2020 16:08
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

14 participants

@webron@fehguy@timburks@MikeRalphson@hkosova@handrews@deathiop@philsturgeon@robertmassaioli@darrelmiller@cebe@brandur@tfesenko@vearutop