Skip to content

Commit

Permalink
fix(JSON Schema): Check for conflicting names and @ids
Browse files Browse the repository at this point in the history
  • Loading branch information
nokome committed Aug 21, 2019
1 parent d9a6291 commit 645f736
Showing 1 changed file with 43 additions and 5 deletions.
48 changes: 43 additions & 5 deletions ts/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ export const build = async (): Promise<void> => {
const schemata = Array.from(schemas.values())

// Check each schema is valid
const types: string[] = []
const properties: {[key: string]: string} = {}
const ids: {[key: string]: string} = {}
const fails = schemata
.map(schema => checkSchema(schemas, schema))
.map(schema => checkSchema(schemas, schema, types, properties, ids))
.reduce((fails, ok) => (!ok ? fails + 1 : fails), 0)
if (fails > 0) {
log.error(`Errors in ${fails} schemas, please see messages above`)
Expand Down Expand Up @@ -84,24 +87,39 @@ export const readSchema = async (type: string): Promise<Schema> => {
/**
* Check that a schema is valid, including that,
*
* - no duplicate `title`s
* - is valid JSON Schema v7
* - all type schemas (those with `properties`) have a `@id` and `description`
* - all property schemas (those that define a property) have a `@id` and `description`
* - each property name is associated with only one `@id`
* - each `@id` is associated with only one property name or type `title`
* - that other schemas that are referred to in `extends` or `$ref` exist
*
* @param schemas A map of all the schemas
* @param schema The schema being checked
*/
const checkSchema = (schemas: Map<string, Schema>, schema: Schema): boolean => {
const checkSchema = (
schemas: Map<string, Schema>,
schema: Schema,
allTypes: string[],
allProperties: { [key: string]: string },
allIds: { [key: string]: string }
): boolean => {
let valid = true
const { title, extends: extends_, description, properties } = schema

log.debug(`Checking type schema "${title}".`)
if (title === undefined) return true

const error = (message: string): void => {
log.error(message)
valid = false
}

// No type with same title already
if (allTypes.includes(title))
error(`Type ${title} already exists`)

// Is a valid schema?
if (validateSchema(schema) !== true) {
const message = (betterAjvErrors(
Expand All @@ -125,7 +143,14 @@ const checkSchema = (schemas: Map<string, Schema>, schema: Schema): boolean => {

// Type schemas have necessary properties and extends is valid
if (properties !== undefined) {
if (schema['@id'] === undefined) error(`${title} is missing @id`)
const id = schema['@id']
if (id === undefined) error(`${title} is missing @id`)
else {
if (allIds[id] !== undefined && allIds[id] !== title)
error(`@id "${id}" is associated with more than one name "${allIds[id]}", "${title}"`)
else
allIds[id] = title
}

if (extends_ !== undefined) {
if (!schemas.has(extends_))
Expand All @@ -134,8 +159,21 @@ const checkSchema = (schemas: Map<string, Schema>, schema: Schema): boolean => {

// Property schemas have necessary properties
for (const [name, property] of Object.entries(properties)) {
if (property['@id'] === undefined)
error(`${title}.${name} is missing @id`)
const id = property['@id']
if (id === undefined) error(`${title}.${name} is missing @id`)
else {
if (allIds[id] !== undefined && allIds[id] !== name)
error(`@id "${id}" is associated with more than one name "${allIds[id]}", "${name}"`)
else
allIds[id] = name
}

if (allProperties[name] !== undefined) {
if (allProperties[name] !== id)
error(`Property "${name}" is associated with more than one @id "${id}", "${allProperties[name]}"`)
} else if (id !== undefined){
allProperties[name] = id
}

if (property.description === undefined)
error(`${title}.${name} is missing description`)
Expand Down

0 comments on commit 645f736

Please sign in to comment.