Skip to content

Commit de7f3e2

Browse files
refactor: create json schema ref resolver
1 parent f8bdac3 commit de7f3e2

File tree

2 files changed

+77
-10
lines changed

2 files changed

+77
-10
lines changed

index.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const { randomUUID } = require('crypto')
99

1010
const validate = require('./schema-validator')
1111
const Serializer = require('./serializer')
12+
const RefResolver = require('./ref-resolver')
1213
const buildAjv = require('./ajv')
1314

1415
let largeArraySize = 2e4
@@ -58,19 +59,12 @@ function resolveRef (location, ref) {
5859
const jsonPointer = ref.slice(hashIndex) || '#'
5960

6061
const schemaRef = schemaId + jsonPointer
62+
const schema = refResolver.getSchema(schemaRef)
6163

62-
let ajvSchema
63-
try {
64-
ajvSchema = ajvInstance.getSchema(schemaRef)
65-
} catch (error) {
64+
if (schema === undefined) {
6665
throw new Error(`Cannot find reference "${ref}"`)
6766
}
6867

69-
if (ajvSchema === undefined) {
70-
throw new Error(`Cannot find reference "${ref}"`)
71-
}
72-
73-
const schema = ajvSchema.schema
7468
if (schema.$ref !== undefined) {
7569
return resolveRef({ schema, schemaId, jsonPointer }, schema.$ref)
7670
}
@@ -83,6 +77,7 @@ const objectReferenceSerializersMap = new Map()
8377

8478
let rootSchemaId = null
8579
let ajvInstance = null
80+
let refResolver = null
8681
let contextFunctions = null
8782

8883
function build (schema, options) {
@@ -95,11 +90,13 @@ function build (schema, options) {
9590
options = options || {}
9691

9792
ajvInstance = buildAjv(options.ajv)
93+
refResolver = new RefResolver()
9894
rootSchemaId = schema.$id || randomUUID()
9995

10096
isValidSchema(schema)
10197
extendDateTimeType(schema)
10298
ajvInstance.addSchema(schema, rootSchemaId)
99+
refResolver.addSchema(schema, rootSchemaId)
103100

104101
if (options.schema) {
105102
const externalSchemas = clone(options.schema)
@@ -114,8 +111,9 @@ function build (schema, options) {
114111
schemaKey = key + externalSchema.$id // relative URI
115112
}
116113

117-
if (ajvInstance.getSchema(schemaKey) === undefined) {
114+
if (refResolver.getSchema(schemaKey) === undefined) {
118115
ajvInstance.addSchema(externalSchema, schemaKey)
116+
refResolver.addSchema(externalSchema, key)
119117
}
120118
}
121119
}
@@ -178,6 +176,7 @@ function build (schema, options) {
178176
const stringifyFunc = contextFunc(ajvInstance, serializer)
179177

180178
ajvInstance = null
179+
refResolver = null
181180
rootSchemaId = null
182181
contextFunctions = null
183182
arrayItemsReferenceSerializersMap.clear()
@@ -493,6 +492,7 @@ function mergeAllOfSchema (location, schema, mergedSchema) {
493492

494493
mergedSchema.$id = `merged_${randomUUID()}`
495494
ajvInstance.addSchema(mergedSchema)
495+
refResolver.addSchema(mergedSchema)
496496
location.schemaId = mergedSchema.$id
497497
location.jsonPointer = '#'
498498
}

ref-resolver.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict'
2+
3+
class RefResolver {
4+
constructor () {
5+
this.refs = {}
6+
}
7+
8+
addSchema (schema, schemaKey) {
9+
if (schema === null) return
10+
11+
if (schema.$id !== undefined && schema.$id.charAt(0) !== '#') {
12+
schemaKey = schema.$id
13+
}
14+
15+
this.refs[schemaKey] = { schema, anchors: {} }
16+
this.resolveSchema(schema, schemaKey, '')
17+
}
18+
19+
getSchema (schemaReference) {
20+
const [schemaKey, jsonPointer] = schemaReference.split('#')
21+
const schema = this.refs[schemaKey]
22+
23+
if (schema === undefined) {
24+
return undefined
25+
}
26+
27+
return schema.anchors[jsonPointer] || getDataByJSONPointer(schema.schema, jsonPointer)
28+
}
29+
30+
resolveSchema (schema, schemaKey, jsonPointer) {
31+
const schemaId = schema.$id
32+
if (schemaId !== undefined && typeof schemaId === 'string') {
33+
if (schemaId.charAt(0) === '#') {
34+
this.refs[schemaKey].anchors[schemaId.slice(1)] = schema
35+
} else {
36+
this.refs[schemaId] = { schema, anchors: {} }
37+
}
38+
}
39+
40+
for (const key in schema) {
41+
if (typeof schema[key] === 'object' && schema[key] !== null) {
42+
this.resolveSchema(schema[key], schemaKey, jsonPointer + '/' + key)
43+
}
44+
}
45+
}
46+
}
47+
48+
function getDataByJSONPointer (data, jsonPointer) {
49+
const parts = jsonPointer.split('/')
50+
let current = data
51+
for (let i = 0; i < parts.length; i++) {
52+
const part = parts[i]
53+
if (part === '') {
54+
continue
55+
}
56+
if (part.charAt(0) === '#') {
57+
continue
58+
}
59+
if (current[part] === undefined) {
60+
return undefined
61+
}
62+
current = current[part]
63+
}
64+
return current
65+
}
66+
67+
module.exports = RefResolver

0 commit comments

Comments
 (0)