@autobe/agent
Version:
AI backend server code generator
173 lines (163 loc) • 7.66 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.OperationValidator = void 0;
const utils_1 = require("@autobe/utils");
const tstl_1 = require("tstl");
const Escaper_1 = require("typia/lib/utils/Escaper");
const emplaceMap_1 = require("../../../utils/emplaceMap");
const JsonSchemaValidator_1 = require("./JsonSchemaValidator");
var OperationValidator;
(function (OperationValidator) {
OperationValidator.validate = (props) => {
props.operations.forEach((op, i) => {
// get method has request body
if (op.method === "get" && op.requestBody !== null)
props.errors.push({
path: `${props.path}[${i}].requestBody`,
expected: "GET method should not have request body. Change method, or re-design the operation.",
value: op.requestBody,
});
// operation name
if (Escaper_1.Escaper.variable(op.name) === false)
props.errors.push({
path: `${props.path}[${i}].name`,
expected: "<valid_variable_name>",
value: op.name,
description: utils_1.StringUtil.trim `
The operation name will be converted to the API controller method
(function) name, so the operation.name must be a valid JavaScript
variable/function name.
However, what you've configured value ${JSON.stringify(op.name)}
is not a valid JavaScript variable/function name. Please change
it to a valid variable/function name.
`,
});
// validate types
if (op.requestBody !== null) {
validatePrimitiveBody({
kind: "requestBody",
errors: props.errors,
path: `${props.path}[${i}].requestBody`,
body: op.requestBody,
});
JsonSchemaValidator_1.JsonSchemaValidator.validateKey({
errors: props.errors,
path: `${props.path}[${i}].requestBody.typeName`,
key: op.requestBody.typeName,
});
}
if (op.responseBody !== null) {
validatePrimitiveBody({
kind: "responseBody",
errors: props.errors,
path: `${props.path}[${i}].responseBody`,
body: op.responseBody,
});
JsonSchemaValidator_1.JsonSchemaValidator.validateKey({
errors: props.errors,
path: `${props.path}[${i}].responseBody.typeName`,
key: op.responseBody.typeName,
});
}
});
// validate duplicated endpoints
const endpoints = new tstl_1.HashMap(utils_1.AutoBeOpenApiEndpointComparator.hashCode, utils_1.AutoBeOpenApiEndpointComparator.equals);
props.operations.forEach((op, i) => {
const key = {
path: op.path,
method: op.method,
};
const it = endpoints.find(key);
if (it.equals(endpoints.end()) === false) {
const indexes = it.second;
props.errors.push({
path: `${props.path}[${i}].{"path"|"method"}`,
expected: "Unique endpoint (path and method)",
value: key,
description: utils_1.StringUtil.trim `
Duplicated endpoint detected (method: ${op.method}, path: ${op.path}).
The duplicated endpoints of others are located in below accessors.
Check them, and consider which operation endpoint would be proper to modify.
${indexes
.map((idx) => `- ${props.path}.[${idx}].{"path"|"method"}`)
.join("\n")}
`,
});
indexes.push(i);
}
else
endpoints.emplace(key, [i]);
});
// validate duplicated method names
const accessors = new Map();
props.operations.forEach((op, i) => {
const key = op.path
.split("/")
.filter((e) => e[0] !== "{" && e.at(-1) !== "}")
.filter((e) => e.length !== 0)
.join(".") + `.${op.name}`;
const indexes = (0, emplaceMap_1.emplaceMap)(accessors, key, () => []);
if (indexes.length !== 0) {
props.errors.push({
path: `${props.path}[${i}].name`,
expected: "Unique name in the same accessor scope.",
value: op.name,
description: utils_1.StringUtil.trim `
Duplicated operation accessor detected (name: ${op.name}, accessor: ${key}).
The operation name must be unique within the parent accessor.
In other worlds, the operation accessor determined by the name
must be unique in the OpenAPI document.
Here is the list of elements of duplicated operation names.
Check them, and consider which operation name would be proper to modify.
${indexes
.map((idx) => `- ${props.operations[idx].name} (accessor: ${key})`)
.join("\n")}
`,
});
}
indexes.push(i);
});
};
const validatePrimitiveBody = (props) => {
if (props.body.typeName === "undefined" || props.body.typeName === "null")
props.errors.push({
path: props.path,
value: props.body,
expected: "null",
description: utils_1.StringUtil.trim `
Type ${props.body.typeName} does not mean anything in ${props.kind}.
Change it to \`null\` if you want to set empty ${props.kind}.
`,
});
else if (props.body.typeName === "number" ||
props.body.typeName === "string" ||
props.body.typeName === "boolean")
props.errors.push({
path: `${props.path}.typeName`,
value: props.body.typeName,
expected: "An object reference type encapsulating the primitive type",
description: utils_1.StringUtil.trim `
Primitive type ${props.body.typeName} is not allowed as the ${props.kind} type.
If you want to use primitive type in the ${props.kind},
encapsulate it in an object reference type. For example, instead of using
\`${props.body.typeName}\`, define an object reference type like below:
- ${props.body.typeName[0].toUpperCase()}${props.body.typeName.slice(1)}Value
`,
});
else if (props.body.typeName === "object" ||
props.body.typeName === "any" ||
props.body.typeName === "interface")
props.errors.push({
path: `${props.path}.typeName`,
value: props.body.typeName,
expected: "An object reference type",
description: utils_1.StringUtil.trim `
Type \`${props.body.typeName}\` is preserved word in the programming languages.
Change the type name to other one.
`,
});
else if (props.body.typeName.startsWith("I") === false) {
}
};
})(OperationValidator || (exports.OperationValidator = OperationValidator = {}));
//# sourceMappingURL=OperationValidator.js.map