check-openapi: Disallow siblings of $ref.

The specification says “any sibling elements of a $ref are ignored”,
so their presence, although not invalid, indicates a mistake.  yamole
incorrectly merges these siblings into the referenced object, but we
should not rely on this nonstandard behavior.

https://swagger.io/docs/specification/using-ref/#sibling

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2020-08-11 13:57:50 -07:00 committed by Tim Abbott
parent 7c17bdb9c5
commit c21818e093
1 changed files with 27 additions and 5 deletions

View File

@ -7,17 +7,39 @@ const jsyaml = require("js-yaml");
const ExampleValidator = require("openapi-examples-validator"); const ExampleValidator = require("openapi-examples-validator");
const SwaggerParser = require("swagger-parser"); const SwaggerParser = require("swagger-parser");
function checkRefSiblings(file, path, data) {
let ok = true;
if (typeof data === "object" && data !== null) {
if ("$ref" in data && Object.entries(data).length !== 1) {
console.error(
`${file}: Siblings of $ref have no effect at ${JSON.stringify(
path,
)}: ${JSON.stringify(data, undefined, 2)}`,
);
ok = false;
}
for (const [key, child] of Array.isArray(data) ? data.entries() : Object.entries(data)) {
if (!checkRefSiblings(file, [...path, key], child)) {
ok = false;
}
}
}
return ok;
}
(async () => { (async () => {
// Iterate through the changed files, passed in the arguments. // Iterate through the changed files, passed in the arguments.
// The two first arguments are the call to the Node interpreter and this // The two first arguments are the call to the Node interpreter and this
// script, hence the starting point at 2. // script, hence the starting point at 2.
for (const file of process.argv.slice(2)) { for (const file of process.argv.slice(2)) {
try { try {
if ( const data = jsyaml.safeLoad(await fs.promises.readFile(file, "utf8"), {
jsyaml.safeLoad(await fs.promises.readFile(file, "utf8"), { filename: file,
filename: file, });
}).openapi !== undefined if (data.openapi !== undefined) {
) { if (!checkRefSiblings(file, [], data)) {
process.exitCode = 1;
}
await SwaggerParser.validate(file); await SwaggerParser.validate(file);
const res = await ExampleValidator.validateFile(file); const res = await ExampleValidator.validateFile(file);
if (!res.valid) { if (!res.valid) {