Skip to content

Instantly share code, notes, and snippets.

@tchak
Last active August 31, 2019 15:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tchak/621d3a229131c59f10b0e27eed5b2a26 to your computer and use it in GitHub Desktop.
Save tchak/621d3a229131c59f10b0e27eed5b2a26 to your computer and use it in GitHub Desktop.
Orbit JSONSchema
import { Schema } from '@orbit/data';
import { JSONAPISerializer } from '@orbit/jsonapi';
import { Dict } from '@orbit/utils';
export interface JSONSchemaType {
type: 'object' | 'string' | 'number' | 'boolean';
pattern?: string;
}
export interface JSONSchemaRef {
$ref: string;
}
export interface JSONSchemaArray<T> {
type: 'array';
items: T;
uniqueItems: true;
}
export interface JSONSchemaNullable<T> {
oneOf: [{ type: 'null' }, T];
}
export interface JSONSchemaAllOf<T> {
allOf: T[];
}
export interface JSONSchemaHasOne {
data: JSONSchemaNullable<JSONSchemaRef>;
}
export interface JSONSchemaHasMany {
data: JSONSchemaArray<JSONSchemaRef>;
}
export type JSONSchemaProperty =
| JSONSchemaRef
| JSONSchemaType
| JSONSchemaHasOne
| JSONSchemaHasMany;
export interface JSONSchemaObjectDefinition {
$id: string;
type: 'object';
required?: string[];
properties: Dict<JSONSchemaProperty>;
additionalItems: boolean;
}
export interface JSONSchemaTypeDefinition extends JSONSchemaType {
$id: string;
}
export type JSONSchemaDefinition =
| JSONSchemaObjectDefinition
| JSONSchemaTypeDefinition;
export interface JSONSchemaProperties {
properties: Dict<JSONSchemaProperty>;
}
export interface JSONSchema {
$schema: string;
$id?: string;
definitions: Dict<
JSONSchemaDefinition | JSONSchemaAllOf<JSONSchemaProperties | JSONSchemaRef>
>;
}
export function ref(id: string) {
return `#/definitions/${id}`;
}
const IDENTIFIER_REF = ref('identifier');
export function makeJSONSchema(schema: Schema, serializer: JSONAPISerializer) {
const jsonSchema: JSONSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
definitions: {
identifier: {
$id: 'identifier',
type: 'object',
properties: {
id: { type: 'string' },
type: { type: 'string' }
},
required: ['type'],
additionalItems: true
}
}
};
for (let model in schema.models) {
let modelAttributesSchemaId = `${model}/attributes`;
let modelRelationshipsSchemaId = `${model}/relationships`;
let modelSchemaProperties: JSONSchemaProperties = { properties: {} };
let modelSchema = {
allOf: [{ $ref: IDENTIFIER_REF }, modelSchemaProperties]
};
let modelAttributesSchema: JSONSchemaObjectDefinition = {
$id: modelAttributesSchemaId,
type: 'object',
properties: {},
additionalItems: false
};
let modelRelationshipsSchema: JSONSchemaObjectDefinition = {
$id: modelRelationshipsSchemaId,
type: 'object',
properties: {},
additionalItems: false
};
schema.eachAttribute(model, (name, attribute) => {
const property = serializer.resourceAttribute(model, name);
const typeDefinition = jsonSchemaTypeDefinition(
model,
name,
attribute.type
);
jsonSchema.definitions[typeDefinition.$id] = typeDefinition;
modelAttributesSchema.properties[property] = {
$ref: ref(typeDefinition.$id)
};
});
schema.eachRelationship(model, (name, relationship) => {
const property = serializer.resourceRelationship(model, name);
if (relationship.type === 'hasOne') {
modelRelationshipsSchema.properties[property] = {
data: {
oneOf: [{ type: 'null' }, { $ref: IDENTIFIER_REF }]
}
};
} else {
modelRelationshipsSchema.properties[property] = {
data: {
type: 'array',
items: { $ref: IDENTIFIER_REF },
uniqueItems: true
}
};
}
});
if (Object.keys(modelAttributesSchema.properties).length) {
jsonSchema.definitions[modelAttributesSchemaId] = modelAttributesSchema;
modelSchemaProperties.properties.attributes = {
$ref: ref(modelAttributesSchemaId)
};
}
if (Object.keys(modelRelationshipsSchema.properties).length) {
jsonSchema.definitions[
modelRelationshipsSchemaId
] = modelRelationshipsSchema;
modelSchemaProperties.properties.relationships = {
$ref: ref(modelRelationshipsSchemaId)
};
}
jsonSchema.definitions[model] = modelSchema;
}
return jsonSchema;
}
function jsonSchemaTypeDefinition(
model: string,
name: string,
type?: string
): JSONSchemaTypeDefinition {
const $id = `${model}/attributes/${name}`;
switch (type) {
case 'string':
return { $id, type: 'string' };
case 'number':
return { $id, type: 'number' };
case 'boolean':
return { $id, type: 'boolean' };
case 'date':
return { $id, type: 'string', pattern: '' };
case 'datetime':
return { $id, type: 'string', pattern: '' };
default:
return { $id, type: 'object' };
}
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"identifier": {
"$id": "identifier",
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
}
},
"required": [
"type"
],
"additionalItems": true
},
"user/attributes/firstName": {
"$id": "user/attributes/firstName",
"type": "string"
},
"user/attributes/lastName": {
"$id": "user/attributes/lastName",
"type": "string"
},
"user/attributes/age": {
"$id": "user/attributes/age",
"type": "number"
},
"user/attributes": {
"$id": "user/attributes",
"type": "object",
"properties": {
"first-name": {
"$ref": "#/definitions/user/attributes/firstName"
},
"last-name": {
"$ref": "#/definitions/user/attributes/lastName"
},
"age": {
"$ref": "#/definitions/user/attributes/age"
}
},
"additionalItems": false
},
"user/relationships": {
"$id": "user/relationships",
"type": "object",
"properties": {
"blog-posts": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/identifier"
},
"uniqueItems": true
}
}
},
"additionalItems": false
},
"user": {
"allOf": [
{
"$ref": "#/definitions/identifier"
},
{
"properties": {
"attributes": {
"$ref": "#/definitions/user/attributes"
},
"relationships": {
"$ref": "#/definitions/user/relationships"
}
}
}
]
},
"blogPost/attributes/title": {
"$id": "blogPost/attributes/title",
"type": "string"
},
"blogPost/attributes/published": {
"$id": "blogPost/attributes/published",
"type": "boolean"
},
"blogPost/attributes": {
"$id": "blogPost/attributes",
"type": "object",
"properties": {
"title": {
"$ref": "#/definitions/blogPost/attributes/title"
},
"published": {
"$ref": "#/definitions/blogPost/attributes/published"
}
},
"additionalItems": false
},
"blogPost": {
"allOf": [
{
"$ref": "#/definitions/identifier"
},
{
"properties": {
"attributes": {
"$ref": "#/definitions/blogPost/attributes"
}
}
}
]
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment