Skip to content

Commit 4961f40

Browse files
authored
fix(semval): add $ref location existential validator (#1705)
* tests: add failing cases * fix(semval): add $ref location existential validator * meta: words are hard * tests: add control-case tests for remote references
1 parent e434eae commit 4961f40

File tree

3 files changed

+171
-1
lines changed

3 files changed

+171
-1
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"immutable": "^3.x.x",
4949
"js-yaml": "^3.5.5",
5050
"json-beautify": "^1.0.1",
51+
"json-refs": "^3.0.4",
5152
"prop-types": "15.6.0",
5253
"react": "^15.6.2",
5354
"react-addons-css-transition-group": "^15.4.2",

src/plugins/validate-semantic/validators/2and3/refs.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import get from "lodash/get"
22
import { escapeJsonPointerToken } from "../../../refs-util"
3+
import { pathFromPtr } from "json-refs"
34

45
export const validate2And3RefHasNoSiblings = () => system => {
56
return system.validateSelectors.all$refs()
@@ -66,7 +67,6 @@ export const validate2And3RefPathFormatting = () => (system) => {
6667

6768
if(refPath && refPath[0] !== "/") {
6869
errors.push({
69-
// $ref instead of $$ref
7070
path: [...node.path.slice(0, -1), "$ref"],
7171
message: "$ref paths must begin with `#/`",
7272
level: "error"
@@ -78,3 +78,29 @@ export const validate2And3RefPathFormatting = () => (system) => {
7878
return errors
7979
})
8080
}
81+
82+
export const validate2And3RefPointersExist = () => (system) => {
83+
const json = system.specSelectors.specJson()
84+
return system.validateSelectors.all$refs()
85+
.then((refs) => {
86+
const errors = []
87+
88+
refs.forEach((node) => {
89+
const value = node.node
90+
if(typeof value === "string" && value[0] === "#") {
91+
// if pointer starts with "#", it is a local ref
92+
const path = pathFromPtr(value)
93+
94+
if(json.getIn(path) === undefined) {
95+
errors.push({
96+
path: [...node.path.slice(0, -1), "$ref"],
97+
message: "$refs must reference a valid location in the document",
98+
level: "error"
99+
})
100+
}
101+
}
102+
})
103+
104+
return errors
105+
})
106+
}

test/plugins/validate-semantic/2and3/refs.js

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,4 +342,147 @@ describe("validation plugin - semantic - 2and3 refs", function() {
342342
})
343343

344344
})
345+
describe("Nonexistent $ref pointers", () => {
346+
it("should return an error when a local JSON pointer does not exist in Swagger 2", () => {
347+
const spec = {
348+
swagger: "2.0",
349+
paths: {
350+
"/CoolPath": {
351+
$ref: "#/myObj/DoesNotExist"
352+
}
353+
},
354+
myObj: {
355+
abc: {
356+
type: "string"
357+
}
358+
}
359+
}
360+
361+
return validateHelper(spec)
362+
.then(system => {
363+
const allErrors = system.errSelectors.allErrors().toJS()
364+
expect(allErrors.length).toEqual(1)
365+
const firstError = allErrors[0]
366+
expect(firstError.message).toMatch("$refs must reference a valid location in the document")
367+
expect(firstError.level).toEqual("error")
368+
expect(firstError.path).toEqual(["paths", "/CoolPath", "$ref"])
369+
})
370+
})
371+
it("should return an error when a local JSON pointer does not exist in OpenAPI 3", () => {
372+
const spec = {
373+
openapi: "3.0.0",
374+
paths: {
375+
"/CoolPath": {
376+
$ref: "#/myObj/DoesNotExist"
377+
}
378+
},
379+
myObj: {
380+
abc: {
381+
type: "string"
382+
}
383+
}
384+
}
385+
386+
return validateHelper(spec)
387+
.then(system => {
388+
const allErrors = system.errSelectors.allErrors().toJS()
389+
expect(allErrors.length).toEqual(1)
390+
const firstError = allErrors[0]
391+
expect(firstError.message).toMatch("$refs must reference a valid location in the document")
392+
expect(firstError.level).toEqual("error")
393+
expect(firstError.path).toEqual(["paths", "/CoolPath", "$ref"])
394+
})
395+
})
396+
it("should return no errors when a JSON pointer exists in Swagger 2", () => {
397+
const spec = {
398+
swagger: "2.0",
399+
paths: {
400+
"/CoolPath": {
401+
$ref: "#/myObj/abc"
402+
},
403+
},
404+
myObj: {
405+
abc: {
406+
type: "string"
407+
}
408+
}
409+
}
410+
411+
return validateHelper(spec)
412+
.then(system => {
413+
const allSemanticErrors = system.errSelectors.allErrors().toJS()
414+
.filter(err => err.source !== "resolver")
415+
expect(allSemanticErrors).toEqual([])
416+
})
417+
})
418+
it("should return no errors when a JSON pointer exists in OpenAPI 3", () => {
419+
const spec = {
420+
openapi: "3.0.0",
421+
paths: {
422+
"/CoolPath": {
423+
$ref: "#/myObj/abc"
424+
},
425+
},
426+
myObj: {
427+
abc: {
428+
type: "string",
429+
properties: {
430+
$ref: "http://google.com/MyRegularURLReference"
431+
}
432+
}
433+
}
434+
}
435+
436+
return validateHelper(spec)
437+
.then(system => {
438+
const allSemanticErrors = system.errSelectors.allErrors().toJS()
439+
.filter(err => err.source !== "resolver")
440+
expect(allSemanticErrors).toEqual([])
441+
})
442+
})
443+
it("should return no errors when a JSON pointer is a remote reference in Swagger 2", () => {
444+
const spec = {
445+
swagger: "2.0",
446+
paths: {
447+
"/CoolPath": {
448+
$ref: "http://google.com#/myObj/abc"
449+
},
450+
},
451+
myObj: {
452+
abc: {
453+
type: "string"
454+
}
455+
}
456+
}
457+
458+
return validateHelper(spec)
459+
.then(system => {
460+
const allSemanticErrors = system.errSelectors.allErrors().toJS()
461+
.filter(err => err.source !== "resolver")
462+
expect(allSemanticErrors).toEqual([])
463+
})
464+
})
465+
it("should return no errors when a JSON pointer is a remote reference in OpenAPI 3", () => {
466+
const spec = {
467+
openapi: "3.0.0",
468+
paths: {
469+
"/CoolPath": {
470+
$ref: "http://google.com#/myObj/abc"
471+
},
472+
},
473+
myObj: {
474+
abc: {
475+
type: "string"
476+
}
477+
}
478+
}
479+
480+
return validateHelper(spec)
481+
.then(system => {
482+
const allSemanticErrors = system.errSelectors.allErrors().toJS()
483+
.filter(err => err.source !== "resolver")
484+
expect(allSemanticErrors).toEqual([])
485+
})
486+
})
487+
})
345488
})

0 commit comments

Comments
 (0)