Skip to content

Latest commit

 

History

History
149 lines (121 loc) · 5.22 KB

schema_object.md

File metadata and controls

149 lines (121 loc) · 5.22 KB

Schema Object

In OpenAPIKit, the Schema Object from the OpenAPI Specification is represented by the JSONSchema type. Indeed, OpenAPI 3.1.x fully supports the JSON Schema specification, though OpenAPIKit does not (yet) have 100% complete JSON Schema specification support.

A JSONSchema can be:

  1. Any of the fundamental types (boolean, number, integer, string, and in OpenAPI v3.1 null).
  2. An array of other schemas or and object with properties that are other schemas.
  3. A reference to a schema in the Components Object or elsewhere.
  4. all(of:), one(of:), or any(of:) a list of other schemas.
  5. not another schema.
  6. fragment (which means the type of schema is not specified).

The fundamental schema types and arrays and objects all share a common set of properties (accessible from their coreContext) and each (except for boolean and null) also has some properties that only apply to that one type (accessible from properties named after the type like objectContext, arrayContext, integerContext, etc.).

You can also extract these properties with pattern matching on a JSONSchema's value.

IMPORTANT: JSONSchema is a struct with static convenience functions on it; when pattern matching, always pattern match on the value property of the JSONSchema type.

let schema: JSONSchema = ...
switch schema.value {
  case .boolean(let coreContext):
    break
  case .object(let coreContext, let objectContext):
    break
  case .array(let coreContext, let arrayContext):
    break
  case .number(let coreContext, let numberContext):
    break
  case .integer(let coreContext, let integerContext):
    break
  case .string(let coreContext, let stringContext):
    break
  ...
}

When creating a new schema, convenience constructors make it possible to specify any or all of the properties that relate to a given schema type without building the context object(s) directly.

let stringSchema = JSONSchema.string
let otherStringSchema = JSONSchema.string(format: .binary, required: false, title: "test", maxLength: 10) //... and many more properties

Certain aspects of a schema can be built out from existing schemas.

let stringSchema = JSONSchema.string // required and non-nullible by default
let optionalStringSchema =  stringSchema.optionalSchemaObject()
let nullableStringSchema = stringSchema.nullableSchemaObject()

// an optional and nullable string with only 3 allowed values
let buildOut = stringSchema
  .optionalSchemaObject()
  .nullableSchemaObject()
  .with(allowedValues: "red", "green", "blue")

Most properties of schemas are encoded as properties of the respective JSON/YAML object. Once notable exception is the nullable property of OpenAPIKit JSONSchema when encoded for OpenAPI v3.1. See how the following Swift is encoded:

JSONSchema.string(nullable: true)

OpenAPI v3.0

{
  "type": "string",
  "nullable": true
}

OpenAPI v3.1

{
  "type": ["string", "null"]
}

Simplifying Schemas

The support for this feature is in its early stages with some gaps in what can be successfully simplified and a lot of room for additional heuristics.

You can take any JSONSchema and dereference it with .dereferenced() or .derefererenced(in:). You can then take the DereferencedJSONSchema and simplify it with .simplified(). Simplification will try to take a schema and make it into the simplest alternative schema that still has the same meaning. Many schemas are already in their simplest form, but when you start using schema components like any, all, one, etc. you open the door to schemas that are more complicated than other equivalent schemas.

For example, the following schema with .all(of:) the given fragments add up to a simpler .object schema with the same rules:

{
  "allOf": [
    {
      "type": "object",
      "properties": {
        "hello": {
          "type": "string"
        }
      }
    },
    {
      "type": "object",
      "additionalProperties": {
        "type": "integer"
      }
    }
  ]
}

The above schema gets simplified to the below schema.

{
  "type": "object",
  "properties": {
    "hello": {
      "type": "string"
    }
  },
  "additionalProperties": {
    "type": "integer"
  }
}

Generating Schemas from Swift Types

Some schemas can be easily generated from Swift types. Many of the fundamental Swift types support schema representations out-of-box.

For example, the following are true

String.openAPISchema == JSONSchema.string

Bool.openAPISchema == JSONSchema.boolean

Double.openAPISchema == JSONSchema.number(format: .double)

Float.openAPISchema == JSONSchema.number(format: .float)
...

Array and Optional are supported out-of-box. For example, the following are true

[String].openAPISchema == .array(items: .string)

[Int].openAPISchema == .array(items: .integer)

Int32?.openAPISchema == .integer(format: .int32, required: false)

[String?].openAPISchema == .array(items: .string(required: false))
...

Additional schema generation support can be found in the mattpolzin/OpenAPIReflection library.

You can conform your own types to OpenAPISchemaType to make it convenient to generate JSONSchemas from them.