From 19c1c2611ebcb7b4cd01c18afbfdc2477fc3440a Mon Sep 17 00:00:00 2001 From: Ivan Tymoshenko Date: Fri, 20 Oct 2023 22:46:20 +0200 Subject: [PATCH] deps: use json-schema-ref-resolver --- index.js | 27 +++++++++--- lib/ref-resolver.js | 104 -------------------------------------------- package.json | 3 +- test/ref.test.js | 17 ++++++-- 4 files changed, 36 insertions(+), 115 deletions(-) delete mode 100644 lib/ref-resolver.js diff --git a/index.js b/index.js index c514d73b..2c08bfba 100644 --- a/index.js +++ b/index.js @@ -5,11 +5,11 @@ const merge = require('@fastify/deepmerge')() const clone = require('rfdc')({ proto: true }) const { randomUUID } = require('node:crypto') +const { RefResolver } = require('json-schema-ref-resolver') const validate = require('./lib/schema-validator') const Serializer = require('./lib/serializer') const Validator = require('./lib/validator') -const RefResolver = require('./lib/ref-resolver') const Location = require('./lib/location') let largeArraySize = 2e4 @@ -53,8 +53,7 @@ function resolveRef (context, location, ref) { const jsonPointer = ref.slice(hashIndex) || '#' const schema = context.refResolver.getSchema(schemaId, jsonPointer) - - if (schema === undefined) { + if (schema === null) { throw new Error(`Cannot find reference "${ref}"`) } @@ -66,6 +65,13 @@ function resolveRef (context, location, ref) { return newLocation } +function getSchemaId (schema, rootSchemaId) { + if (schema.$id && schema.$id.charAt(0) !== '#') { + return schema.$id + } + return rootSchemaId +} + function build (schema, options) { isValidSchema(schema) @@ -82,12 +88,19 @@ function build (schema, options) { validatorSchemasIds: new Set() } - context.refResolver.addSchema(schema, context.rootSchemaId) + const schemaId = getSchemaId(schema, context.rootSchemaId) + if (!context.refResolver.hasSchema(schemaId)) { + context.refResolver.addSchema(schema, context.rootSchemaId) + } if (options.schema) { - for (const key of Object.keys(options.schema)) { - isValidSchema(options.schema[key], key) - context.refResolver.addSchema(options.schema[key], key) + for (const key in options.schema) { + const schema = options.schema[key] + const schemaId = getSchemaId(schema, key) + if (!context.refResolver.hasSchema(schemaId)) { + isValidSchema(schema, key) + context.refResolver.addSchema(schema, key) + } } } diff --git a/lib/ref-resolver.js b/lib/ref-resolver.js deleted file mode 100644 index 28a6f324..00000000 --- a/lib/ref-resolver.js +++ /dev/null @@ -1,104 +0,0 @@ -'use strict' - -const deepEqual = require('fast-deep-equal') - -class RefResolver { - constructor () { - this.schemas = {} - } - - addSchema (schema, schemaId) { - if (schema.$id !== undefined && schema.$id.charAt(0) !== '#') { - schemaId = schema.$id - } - if (this.getSchema(schemaId) === undefined) { - this.insertSchemaBySchemaId(schema, schemaId) - this.insertSchemaSubschemas(schema, schemaId) - } - } - - getSchema (schemaId, jsonPointer = '#') { - const schema = this.schemas[schemaId] - if (schema === undefined) { - return undefined - } - if (schema.anchors[jsonPointer] !== undefined) { - return schema.anchors[jsonPointer] - } - return getDataByJSONPointer(schema.schema, jsonPointer) - } - - getSchemaDependencies (schemaId, dependencies = {}) { - const schema = this.schemas[schemaId] - - for (const dependencySchemaId of schema.dependencies) { - if (dependencies[dependencySchemaId] !== undefined) continue - dependencies[dependencySchemaId] = this.getSchema(dependencySchemaId) - this.getSchemaDependencies(dependencySchemaId, dependencies) - } - - return dependencies - } - - insertSchemaBySchemaId (schema, schemaId) { - if ( - this.schemas[schemaId] !== undefined && - !deepEqual(schema, this.schemas[schemaId].schema) - ) { - throw new Error(`There is already another schema with id ${schemaId}`) - } - this.schemas[schemaId] = { schema, anchors: {}, dependencies: [] } - } - - insertSchemaByAnchor (schema, schemaId, anchor) { - const { anchors } = this.schemas[schemaId] - if ( - anchors[anchor] !== undefined && - !deepEqual(schema, anchors[anchor]) - ) { - throw new Error(`There is already another schema with id ${schemaId}#${anchor}`) - } - anchors[anchor] = schema - } - - insertSchemaSubschemas (schema, rootSchemaId) { - const schemaId = schema.$id - if (schemaId !== undefined && typeof schemaId === 'string') { - if (schemaId.charAt(0) === '#') { - this.insertSchemaByAnchor(schema, rootSchemaId, schemaId) - } else { - this.insertSchemaBySchemaId(schema, schemaId) - rootSchemaId = schemaId - } - } - - const ref = schema.$ref - if (ref !== undefined && typeof ref === 'string') { - if (ref.charAt(0) !== '#') { - const dependencySchemaId = ref.split('#', 1)[0] - this.schemas[rootSchemaId].dependencies.push(dependencySchemaId) - } - } - - for (const key in schema) { - if (typeof schema[key] === 'object' && schema[key] !== null) { - this.insertSchemaSubschemas(schema[key], rootSchemaId) - } - } - } -} - -function getDataByJSONPointer (data, jsonPointer) { - const parts = jsonPointer.split('/') - let current = data - for (const part of parts) { - if (part === '' || part === '#') continue - if (typeof current !== 'object' || current === null) { - return undefined - } - current = current[part] - } - return current -} - -module.exports = RefResolver diff --git a/package.json b/package.json index 4800dd3c..e52c4fce 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,8 @@ "ajv-formats": "^2.1.1", "fast-deep-equal": "^3.1.3", "fast-uri": "^2.1.0", - "rfdc": "^1.2.0" + "rfdc": "^1.2.0", + "json-schema-ref-resolver": "^1.0.1" }, "standard": { "ignore": [ diff --git a/test/ref.test.js b/test/ref.test.js index 886f7996..4163ad24 100644 --- a/test/ref.test.js +++ b/test/ref.test.js @@ -1377,7 +1377,10 @@ test('Bad key', t => { }) t.fail('Should throw') } catch (err) { - t.equal(err.message, 'Cannot find reference "extrenal#/definitions/projectId"') + t.equal( + err.message, + 'Cannot resolve ref "extrenal#/definitions/projectId". Schema with id "extrenal" is not found.' + ) } }) @@ -2076,7 +2079,11 @@ test('should throw an Error if two non-identical schemas with same id are provid ] } - t.throws(() => build(schema), new Error('There is already another schema with id inner_schema')) + try { + build(schema) + } catch (err) { + t.equal(err.message, 'There is already another schema with id "inner_schema".') + } }) test('ref internal - throw if schema has definition twice with different shape', (t) => { @@ -2115,5 +2122,9 @@ test('ref internal - throw if schema has definition twice with different shape', } } - t.throws(() => build(schema), Error('There is already another schema with id test##uri')) + try { + build(schema) + } catch (err) { + t.equal(err.message, 'There is already another anchor "#uri" in a schema "test".') + } })