Skip to content

Commit 7f20b57

Browse files
authored
feat: uses can overwrite def-0 logic (#5)
* feat: customize def definition * more test * upgrade tap 15 * more tests
1 parent 3919149 commit 7f20b57

File tree

8 files changed

+180
-44
lines changed

8 files changed

+180
-44
lines changed

README.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/)
55

66
Resolve all `$refs` in your [JSON schema](https://json-schema.org/specification.html)!
7-
This module will resolve the `$ref` keyword against the `externalSchemas` you will provide.
7+
This module will resolve the `$ref` keyword against the `externalSchemas` you will provide.
8+
By resolving the `$ref` keyword, means that you get back a single BIG inlined JSON schema that does not rely on any external schema.
89
If a reference is missing, it will not throw any error.
910

10-
The `$ref` will be modified to point to a local reference `#/definitions/<generated key>`.
11-
Moreover, the `definitions` keyword will be decorated with the external schemas to get only
12-
one JSON schema resolved as output.
1311

1412
## Install
1513

@@ -21,11 +19,24 @@ This plugin support Node.js >= 10
2119

2220
## Usage: resolve one schema against external schemas
2321

22+
The `$ref` string is going to be modified to point to a local reference URI: `#/definitions/<generated key>`.
23+
Moreover, the `definitions` keyword will be decorated with the external schemas to get only one JSON schema resolved as output.
24+
25+
By default the `<generated key>` has the `def-${index}` format.
26+
You can customize it by passing a `buildLocalReference` function as follows:
27+
2428
```js
2529
const RefResolver = require('json-schema-resolver')
2630

2731
const ref = RefResolver({
28-
clone: true // Clone the input schema without changing it. Default: false
32+
clone: true, // Clone the input schema without changing it. Default: false,
33+
buildLocalReference (json, baseUri, fragment, i) {
34+
// the `json` that is being resolved
35+
// the `baseUri` object of the schema. Its values is the parse result from https://www.npmjs.com/package/uri-js
36+
// the `fragment` is the `$ref` string when the `$ref` is a relative reference
37+
// the `i` is a local counter to generate a unique key
38+
return `def-${i}` // default value
39+
}
2940
})
3041

3142
const inputSchema = {
@@ -48,7 +59,7 @@ const addresSchema = {
4859
}
4960

5061
const singleSchema = ref.resolve(inputSchema, { externalSchemas: [addresSchema] })
51-
// mySchema is untouched thanks to clone:true
62+
// inputSchema is untouched thanks to clone:true
5263
```
5364

5465
`singleSchema` will be like:
@@ -115,7 +126,7 @@ const inputSchema = {
115126

116127
// the resolved schema DOES NOT have definitions added
117128
const singleSchema = ref.resolve(inputSchema)
118-
const anotherResolvedSchema = ref.resolve(input_2_Schema)
129+
const anotherResolvedSchema = ref.resolve(input_2_Schema) // resolve schemas within the same externalSchemas
119130

120131
// to get the definition you need only to call:
121132
const sharedDefinitions = ref.definitions()

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"scripts": {
77
"lint": "standard",
88
"lint:fix": "standard --fix",
9-
"test": "npm run lint && tap test/**/*.test.js --cov"
9+
"test": "npm run lint && tap test/**/*.test.js"
1010
},
1111
"engines": {
1212
"node": ">=10"
@@ -22,8 +22,8 @@
2222
},
2323
"homepage": "https://github.com/Eomm/json-schema-resolver#readme",
2424
"devDependencies": {
25-
"standard": "^14.3.3",
26-
"tap": "^12.7.0"
25+
"standard": "^16.0.3",
26+
"tap": "^15.0.9"
2727
},
2828
"dependencies": {
2929
"debug": "^4.1.1",

ref-resolver.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ const kConsumed = Symbol('json-schema-resolver.consumed') // when an external js
1717

1818
const defaultOpts = {
1919
target: 'draft-07',
20-
clone: false
20+
clone: false,
21+
buildLocalReference (json, baseUri, fragment, i) {
22+
return `def-${i}`
23+
}
2124
}
2225

2326
const targetSupported = ['draft-07'] // TODO , 'draft-08'
@@ -33,7 +36,13 @@ const targetCfg = {
3336
// logic: https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.appendix.B.1
3437
function jsonSchemaResolver (options) {
3538
const ee = new EventEmitter()
36-
const { clone, target, applicationUri, externalSchemas: rootExternalSchemas } = Object.assign({}, defaultOpts, options)
39+
const {
40+
clone,
41+
target,
42+
applicationUri,
43+
externalSchemas: rootExternalSchemas,
44+
buildLocalReference
45+
} = Object.assign({}, defaultOpts, options)
3746

3847
const allIds = new Map()
3948
let rolling = 0
@@ -130,14 +139,14 @@ function jsonSchemaResolver (options) {
130139
return rootSchema
131140
}
132141

133-
function collectIds (json, baseUri, relative) {
142+
function collectIds (json, baseUri, fragment) {
134143
if (json[kIgnore]) { return }
135144

136-
const rel = (relative && URI.serialize(relative)) || ''
145+
const rel = (fragment && URI.serialize(fragment)) || ''
137146
const id = URI.serialize(baseUri) + rel
138147
if (!allIds.has(id)) {
139148
debug('Collected $id %s', id)
140-
json[kRefToDef] = `def-${rolling++}`
149+
json[kRefToDef] = buildLocalReference(json, baseUri, fragment, rolling++)
141150
allIds.set(id, json)
142151
} else {
143152
debug('WARN duplicated id %s .. IGNORED - ', id)

test/example.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ test('readme example', t => {
3232

3333
const singleSchema = ref.resolve(inputSchema, { externalSchemas: [addresSchema] })
3434

35-
t.deepEqual(singleSchema, {
35+
t.same(singleSchema, {
3636
$id: 'http://example.com/SimplePerson',
3737
type: 'object',
3838
properties: {
@@ -100,7 +100,7 @@ test('readme example #2', t => {
100100
// to get the definition you need only to call:
101101
const sharedDefinitions = ref.definitions()
102102

103-
t.deepEqual(sharedDefinitions, {
103+
t.same(sharedDefinitions, {
104104
definitions: {
105105
'def-0': {
106106
$id: 'relativeAddress',
@@ -117,7 +117,7 @@ test('readme example #2', t => {
117117
}
118118
})
119119

120-
t.deepEqual(singleSchema, {
120+
t.same(singleSchema, {
121121
$id: 'my-application.org',
122122
type: 'object',
123123
properties: {

test/ref-as-is.test.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
2+
const { test } = require('tap')
3+
const URI = require('uri-js')
4+
const RefResolver = require('../ref-resolver')
5+
6+
test('resolving schema within the $id', t => {
7+
t.plan(2)
8+
9+
const schema = {
10+
description: 'Succesful response',
11+
type: 'object',
12+
properties: {
13+
greetings: { $ref: 'greetings#' }
14+
}
15+
}
16+
17+
const opts = {
18+
applicationUri: 'one-single-uri.to',
19+
externalSchemas: [
20+
{
21+
$id: 'greetings',
22+
type: 'object',
23+
properties: {
24+
hello: { type: 'string' }
25+
}
26+
}
27+
],
28+
buildLocalReference (json, baseUri, fragment, i) {
29+
return baseUri.path
30+
}
31+
}
32+
33+
const resolver = RefResolver(opts)
34+
const out = resolver.resolve(schema)
35+
const externalDef = resolver.definitions()
36+
t.equal(out.properties.greetings.$ref, '#/definitions/greetings')
37+
t.same(externalDef.definitions.greetings, opts.externalSchemas[0])
38+
})
39+
40+
test('resolving schema within the $id case #2', t => {
41+
t.plan(3)
42+
43+
const schema = {
44+
$id: 'urn:schema:ref',
45+
type: 'object',
46+
properties: {
47+
hello: { $ref: 'urn:schema:base#/definitions/hello' }
48+
}
49+
}
50+
51+
const opts = {
52+
applicationUri: schema.$id,
53+
externalSchemas: [
54+
{
55+
$id: 'urn:schema:base',
56+
definitions: { hello: { type: 'string' } },
57+
type: 'object',
58+
properties: { hello: { $ref: '#/definitions/hello' } }
59+
},
60+
schema
61+
],
62+
buildLocalReference (json, baseUri, fragment, i) {
63+
return escape(json.$id)
64+
}
65+
}
66+
67+
const resolver = RefResolver(opts)
68+
const out = resolver.resolve(schema)
69+
t.equal(out.properties.hello.$ref, '#/definitions/urn%3Aschema%3Abase/definitions/hello')
70+
71+
const externalDef = resolver.definitions()
72+
t.ok(externalDef.definitions['urn%3Aschema%3Abase'], 'buildLocalReference result')
73+
t.ok(externalDef.definitions['urn%3Aschema%3Aref'], 'buildLocalReference result')
74+
})
75+
76+
test('resolving schema within the $id relative', t => {
77+
t.plan(4)
78+
79+
const schema = {
80+
type: 'object',
81+
properties: {
82+
user: { $ref: 'http://hello.absolute/user.json#/definitions/name' },
83+
address: { $ref: 'adr#/definitions/street' }
84+
}
85+
}
86+
87+
const opts = {
88+
applicationUri: 'http://hello.relative',
89+
externalSchemas: [
90+
{
91+
$id: 'http://hello.absolute/user.json',
92+
type: 'object',
93+
definitions: { name: { type: 'string' } }
94+
},
95+
{
96+
$id: 'adr',
97+
type: 'object',
98+
definitions: { street: { type: 'string' } }
99+
}
100+
],
101+
buildLocalReference (json, baseUri, fragment, i) {
102+
t.equal(URI.serialize(baseUri), baseUriCheck.shift())
103+
return escape(json.$id)
104+
}
105+
}
106+
107+
const baseUriCheck = [
108+
opts.externalSchemas[0].$id,
109+
`${opts.applicationUri}/${opts.externalSchemas[1].$id}`
110+
]
111+
112+
const resolver = RefResolver(opts)
113+
const out = resolver.resolve(schema)
114+
t.equal(out.properties.user.$ref, '#/definitions/http%3A//hello.absolute/user.json/definitions/name')
115+
t.equal(out.properties.address.$ref, '#/definitions/adr/definitions/street')
116+
})

test/ref-fragment.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ test('Preserve $ref fragment', t => {
3030
}
3131
}, opts)
3232

33-
t.deepEqual(out, {
33+
t.same(out, {
3434
$id: 'my-schema',
3535
type: 'object',
3636
properties: {

0 commit comments

Comments
 (0)