zulip/tools/check-openapi

78 lines
2.6 KiB
Plaintext
Raw Normal View History

#!/usr/bin/env node
"use strict";
const fs = require("fs");
const jsyaml = require("js-yaml");
const ExampleValidator = require("openapi-examples-validator");
const SwaggerParser = require("swagger-parser");
function checkRefSiblings(file, path, data) {
let ok = true;
if (typeof data === "object" && data !== null) {
if (
"allOf" in data &&
Object.values(data.allOf).filter((subschema) => !("$ref" in subschema)).length !== 1
) {
console.error(
`${file}: Too many inline allOf subschemas at ${JSON.stringify(
path,
)}: ${JSON.stringify(data, undefined, 2)}`,
);
ok = false;
}
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 () => {
// Iterate through the changed files, passed in the arguments.
// The two first arguments are the call to the Node interpreter and this
// script, hence the starting point at 2.
for (const file of process.argv.slice(2)) {
try {
const data = jsyaml.load(await fs.promises.readFile(file, "utf8"), {
filename: file,
});
if (data.openapi !== undefined) {
if (!checkRefSiblings(file, [], data)) {
process.exitCode = 1;
}
await SwaggerParser.validate(file);
const res = await ExampleValidator.validateFile(file);
if (!res.valid) {
for (const error of res.errors) {
console.error(error);
}
process.exitCode = 1;
}
}
} catch (error) {
if (error instanceof jsyaml.YAMLException) {
console.error(error.message);
} else if (error instanceof SyntaxError) {
console.error(`${file}: ${error.message}`);
} else {
throw error;
}
process.exitCode = 1;
}
}
})().catch((error) => {
console.error(error);
process.exit(1);
});