Skip to content

fix: merged schemas caching #677

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
505 changes: 267 additions & 238 deletions index.js

Large diffs are not rendered by default.

31 changes: 1 addition & 30 deletions lib/location.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ class Location {
this.schema = schema
this.schemaId = schemaId
this.jsonPointer = jsonPointer
this.mergedSchemaId = null
}

getPropertyLocation (propertyName) {
Expand All @@ -14,39 +13,11 @@ class Location {
this.schemaId,
this.jsonPointer + '/' + propertyName
)

if (this.mergedSchemaId !== null) {
propertyLocation.addMergedSchema(
this.schema[propertyName],
this.mergedSchemaId,
this.jsonPointer + '/' + propertyName
)
}

return propertyLocation
}

// Use this method to get current schema location.
// Use it when you need to create reference to the current location.
getSchemaId () {
return this.mergedSchemaId || this.schemaId
}

// Use this method to get original schema id for resolving user schema $refs
// Don't join it with a JSON pointer to get the current location.
getOriginSchemaId () {
return this.schemaId
}

getSchemaRef () {
const schemaId = this.getSchemaId()
return schemaId + this.jsonPointer
}

addMergedSchema (mergedSchema, schemaId, jsonPointer = '#') {
this.schema = mergedSchema
this.mergedSchemaId = schemaId
this.jsonPointer = jsonPointer
return this.schemaId + this.jsonPointer
}
}

Expand Down
9 changes: 9 additions & 0 deletions lib/merge-schemas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict'

const { mergeSchemas: _mergeSchemas } = require('@fastify/merge-json-schemas')

function mergeSchemas (schemas) {
return _mergeSchemas(schemas, { onConflict: 'skip' })
}

module.exports = mergeSchemas
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@
"fast-json-stringify": "."
},
"dependencies": {
"@fastify/deepmerge": "^1.0.0",
"ajv": "^8.10.0",
"ajv-formats": "^2.1.1",
"fast-deep-equal": "^3.1.3",
"fast-uri": "^2.1.0",
"rfdc": "^1.2.0",
"json-schema-ref-resolver": "^1.0.1"
"json-schema-ref-resolver": "^1.0.1",
"@fastify/merge-json-schemas": "^0.1.0"
},
"standard": {
"ignore": [
Expand Down
151 changes: 134 additions & 17 deletions test/allof.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ test('object with allOf and multiple schema on the allOf', (t) => {
id: 1,
name: 'string',
tag: 'otherString'
}), '{"name":"string","tag":"otherString","id":1}')
}), '{"name":"string","id":1,"tag":"otherString"}')
})

test('object with allOf and one schema on the allOf', (t) => {
Expand Down Expand Up @@ -553,50 +553,167 @@ test('allof with local anchor reference', (t) => {
t.equal(stringify(data), JSON.stringify(data))
})

test('allOf: multiple nested $ref properties', (t) => {
t.plan(2)

const externalSchema1 = {
$id: 'externalSchema1',
oneOf: [
{ $ref: '#/definitions/id1' }
],
definitions: {
id1: {
type: 'object',
properties: {
id1: {
type: 'integer'
}
},
additionalProperties: false
}
}
}

const externalSchema2 = {
$id: 'externalSchema2',
oneOf: [
{ $ref: '#/definitions/id2' }
],
definitions: {
id2: {
type: 'object',
properties: {
id2: {
type: 'integer'
}
},
additionalProperties: false
}
}
}

const schema = {
anyOf: [
{ $ref: 'externalSchema1' },
{ $ref: 'externalSchema2' }
]
}

const stringify = build(schema, { schema: [externalSchema1, externalSchema2] })

t.equal(stringify({ id1: 1 }), JSON.stringify({ id1: 1 }))
t.equal(stringify({ id2: 2 }), JSON.stringify({ id2: 2 }))
})

test('allOf: throw Error if types mismatch ', (t) => {
t.plan(1)
t.plan(3)

const schema = {
allOf: [
{ type: 'string' },
{ type: 'number' }
]
}
t.throws(() => build(schema), new Error('allOf schemas have different type values'))
try {
build(schema)
t.fail('should throw the MergeError')
} catch (error) {
t.ok(error instanceof Error)
t.equal(error.message, 'Failed to merge "type" keyword schemas.')
t.same(error.schemas, [['string'], ['number']])
}
})

test('allOf: throw Error if format mismatch ', (t) => {
t.plan(1)
t.plan(3)

const schema = {
allOf: [
{ format: 'date' },
{ format: 'time' }
]
}
t.throws(() => build(schema), new Error('allOf schemas have different format values'))
try {
build(schema)
t.fail('should throw the MergeError')
} catch (error) {
t.ok(error instanceof Error)
t.equal(error.message, 'Failed to merge "format" keyword schemas.')
t.same(error.schemas, ['date', 'time'])
}
})

test('allOf: throw Error if nullable mismatch /1', (t) => {
test('recursive nested allOfs', (t) => {
t.plan(1)

const schema = {
allOf: [
{ nullable: true },
{ nullable: false }
]
type: 'object',
properties: {
foo: {
additionalProperties: false,
allOf: [{ $ref: '#' }]
}
}
}
t.throws(() => build(schema), new Error('allOf schemas have different nullable values'))

const data = { foo: {} }
const stringify = build(schema)
t.equal(stringify(data), JSON.stringify(data))
})

test('allOf: throw Error if nullable mismatch /2', (t) => {
test('recursive nested allOfs', (t) => {
t.plan(1)

const schema = {
allOf: [
{ nullable: false },
{ nullable: true }
]
type: 'object',
properties: {
foo: {
additionalProperties: false,
allOf: [{ allOf: [{ $ref: '#' }] }]
}
}
}

const data = { foo: {} }
const stringify = build(schema)
t.equal(stringify(data), JSON.stringify(data))
})

test('external recursive allOfs', (t) => {
t.plan(1)

const externalSchema = {
type: 'object',
properties: {
foo: {
properties: {
bar: { type: 'string' }
},
allOf: [{ $ref: '#' }]
}
}
}

const schema = {
type: 'object',
properties: {
a: { $ref: 'externalSchema#/properties/foo' },
b: { $ref: 'externalSchema#/properties/foo' }
}
}

const data = {
a: {
foo: {},
bar: '42',
baz: 42
},
b: {
foo: {},
bar: '42',
baz: 42
}
}
t.throws(() => build(schema), new Error('allOf schemas have different nullable values'))
const stringify = build(schema, { schema: { externalSchema } })
t.equal(stringify(data), '{"a":{"bar":"42","foo":{}},"b":{"bar":"42","foo":{}}}')
})
Loading