Skip to content

Commit b438361

Browse files
yndcmcollina
authored andcommitted
Support for external $ref plain name fragment (#161)
* Added resolve for fragment id in externalSchema * Added test and fixed undefined resolve * Changed the test to have a slash inside the schema * Fixed test and changed dereferencing logic * Removed ajv compiling when resolving refs * Updated tests * Added a test for duplicate schemas * Don't resolve to other schemas on explicit ref * Changed the $id in the last test * Better $id in the last test again
1 parent 147afa8 commit b438361

File tree

2 files changed

+210
-2
lines changed

2 files changed

+210
-2
lines changed

index.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,10 +483,18 @@ function refFinder (ref, schema, externalSchema) {
483483
// If it has a path
484484
if (ref[1]) {
485485
// ref[1] could contain a JSON pointer - ex: /definitions/num
486-
// or plan name fragment id without suffix # - ex: customId
486+
// or plain name fragment id without suffix # - ex: customId
487487
var walk = ref[1].split('/')
488488
if (walk.length === 1) {
489-
return idFinder(schema, `#${ref[1]}`)
489+
var targetId = `#${ref[1]}`
490+
var dereferenced = idFinder(schema, targetId)
491+
if (dereferenced === undefined && !ref[0]) {
492+
for (var key of Object.keys(externalSchema)) {
493+
dereferenced = idFinder(externalSchema[key], targetId)
494+
if (dereferenced !== undefined) break
495+
}
496+
}
497+
return dereferenced
490498
} else {
491499
for (var i = 1; i < walk.length; i++) {
492500
code += `['${walk[i]}']`

test/ref.test.js

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,206 @@ test('ref internal - plain name fragment', (t) => {
434434
t.equal(output, '{"obj":{"str":"test"}}')
435435
})
436436

437+
test('ref external - plain name fragment', (t) => {
438+
t.plan(2)
439+
440+
const externalSchema = {
441+
first: {
442+
$id: '#first-schema',
443+
type: 'object',
444+
properties: {
445+
str: {
446+
type: 'string'
447+
}
448+
}
449+
},
450+
second: {
451+
definitions: {
452+
second: {
453+
$id: '#second-schema',
454+
type: 'object',
455+
properties: {
456+
int: {
457+
type: 'integer'
458+
}
459+
}
460+
}
461+
}
462+
}
463+
}
464+
465+
const schema = {
466+
title: 'object with $ref to external plain name fragment',
467+
type: 'object',
468+
properties: {
469+
first: {
470+
$ref: '#first-schema'
471+
},
472+
second: {
473+
$ref: '#second-schema'
474+
}
475+
}
476+
}
477+
478+
const object = {
479+
first: {
480+
str: 'test'
481+
},
482+
second: {
483+
int: 42
484+
}
485+
}
486+
487+
const stringify = build(schema, { schema: externalSchema })
488+
const output = stringify(object)
489+
490+
try {
491+
JSON.parse(output)
492+
t.pass()
493+
} catch (e) {
494+
t.fail()
495+
}
496+
497+
t.equal(output, '{"first":{"str":"test"},"second":{"int":42}}')
498+
})
499+
500+
test('ref external - duplicate plain name fragment', (t) => {
501+
t.plan(2)
502+
503+
const externalSchema = {
504+
external: {
505+
$id: '#duplicateSchema',
506+
type: 'object',
507+
properties: {
508+
prop: {
509+
type: 'boolean'
510+
}
511+
}
512+
},
513+
other: {
514+
$id: '#otherSchema',
515+
type: 'object',
516+
properties: {
517+
prop: {
518+
type: 'integer'
519+
}
520+
}
521+
}
522+
}
523+
524+
const schema = {
525+
title: 'object with $ref to plain name fragment',
526+
type: 'object',
527+
definitions: {
528+
duplicate: {
529+
$id: '#duplicateSchema',
530+
type: 'object',
531+
properties: {
532+
prop: {
533+
type: 'string'
534+
}
535+
}
536+
}
537+
},
538+
properties: {
539+
local: {
540+
$ref: '#duplicateSchema'
541+
},
542+
external: {
543+
$ref: 'external#duplicateSchema'
544+
},
545+
other: {
546+
$ref: '#otherSchema'
547+
}
548+
}
549+
}
550+
551+
const object = {
552+
local: {
553+
prop: 'test'
554+
},
555+
external: {
556+
prop: true
557+
},
558+
other: {
559+
prop: 42
560+
}
561+
}
562+
563+
const stringify = build(schema, { schema: externalSchema })
564+
const output = stringify(object)
565+
566+
try {
567+
JSON.parse(output)
568+
t.pass()
569+
} catch (e) {
570+
t.fail()
571+
}
572+
573+
t.equal(output, '{"local":{"prop":"test"},"external":{"prop":true},"other":{"prop":42}}')
574+
})
575+
576+
test('ref external - explicit external plain name fragment must not fallback to other external schemas', (t) => {
577+
t.plan(1)
578+
579+
const externalSchema = {
580+
first: {
581+
$id: '#target',
582+
type: 'object',
583+
properties: {
584+
prop: {
585+
type: 'string'
586+
}
587+
}
588+
},
589+
second: {
590+
$id: '#wrong',
591+
type: 'object',
592+
properties: {
593+
prop: {
594+
type: 'integer'
595+
}
596+
}
597+
}
598+
}
599+
600+
const schema = {
601+
title: 'object with $ref to plain name fragment',
602+
type: 'object',
603+
definitions: {
604+
third: {
605+
$id: '#wrong',
606+
type: 'object',
607+
properties: {
608+
prop: {
609+
type: 'boolean'
610+
}
611+
}
612+
}
613+
},
614+
properties: {
615+
target: {
616+
$ref: 'first#wrong'
617+
}
618+
}
619+
}
620+
621+
const object = {
622+
target: {
623+
prop: 'test'
624+
}
625+
}
626+
627+
try {
628+
const stringify = build(schema, { schema: externalSchema })
629+
const output = stringify(object)
630+
JSON.parse(output)
631+
t.fail()
632+
} catch (e) {
633+
t.pass()
634+
}
635+
})
636+
437637
test('ref internal - multiple $ref format', (t) => {
438638
t.plan(2)
439639

0 commit comments

Comments
 (0)