@autobe/agent
Version:
AI backend server code generator
171 lines (160 loc) • 5.73 kB
text/typescript
import {
AutoBeOpenApi,
AutoBeProgressEventBase,
AutoBeTestGenerateFunction,
AutoBeTestPrepareFunction,
AutoBeTestValidateEvent,
IAutoBeCompiler,
} from "@autobe/interface";
import { StringUtil } from "@autobe/utils";
import { NamingConvention } from "@typia/utils";
import path from "path";
import { IValidation } from "typia";
import { validateEmptyCode } from "../../../utils/validateEmptyCode";
import { IAutoBeTestArtifacts } from "../structures/IAutoBeTestArtifacts";
import { IAutoBeTestGenerateProcedure } from "../structures/IAutoBeTestGenerateProcedure";
import { AutoBeTestFunctionProgrammer } from "./AutoBeTestFunctionProgrammer";
import { AutoBeTestPrepareProgrammer } from "./AutoBeTestPrepareProgrammer";
export namespace AutoBeTestGenerateProgrammer {
/* ----------------------------------------------------------------
GETTERS
---------------------------------------------------------------- */
export function is(
document: AutoBeOpenApi.IDocument,
operation: AutoBeOpenApi.IOperation,
): boolean {
if (operation.requestBody === null) return false;
const schema: AutoBeOpenApi.IJsonSchema | undefined =
document.components.schemas[operation.requestBody.typeName];
if (schema === undefined) return false;
return AutoBeTestPrepareProgrammer.is(
operation.requestBody.typeName,
schema,
);
}
export function size(document: AutoBeOpenApi.IDocument): number {
return document.operations.filter((operation) =>
AutoBeTestGenerateProgrammer.is(document, operation),
).length;
}
export function getFunctionName(operation: AutoBeOpenApi.IOperation): string {
const accessor: string[] = operation.accessor!.map(NamingConvention.snake);
return `generate_random_${accessor.join("_")}`;
}
/* ----------------------------------------------------------------
WRITERS
---------------------------------------------------------------- */
export function writeTemplateCode(props: {
operation: AutoBeOpenApi.IOperation;
prepare: AutoBeTestPrepareFunction;
}): string {
const functionName: string = getFunctionName(props.operation);
const input: string = props.operation.requestBody?.typeName ?? "unknown";
const output: string = props.operation.responseBody?.typeName ?? "void";
return StringUtil.trim`
export async function ${functionName}(
connection: api.IConnection,
props: {
body?: DeepPartial<${input}> | undefined;
${writeParameterDeclarations(props.operation)}
}
): Promise<${output}> {
const prepared: ${input} = ${props.prepare.name}(
props.body
);
return await api.functional.${props.operation.accessor!.join(".")}(
connection,
{
body: prepared,
${writeParameterArguments(props.operation)}
},
);
}
`;
}
function writeParameterDeclarations(
operation: AutoBeOpenApi.IOperation,
): string {
if (operation.parameters.length === 0) return "";
return StringUtil.trim`
params: {
${operation.parameters.map((p) => ` ${p.name}: ${p.schema.type};`).join("\n")}
};
`
.split("\n")
.filter((line) => line.length !== 0)
.map((line) => ` ${line.trim()}`)
.join("\n");
}
function writeParameterArguments(
operation: AutoBeOpenApi.IOperation,
): string {
if (operation.parameters.length === 0) return "";
return operation.parameters
.map((p) => ` ${p.name}: props.params.${p.name},`)
.join("\n");
}
/* ----------------------------------------------------------------
COMPILERS
---------------------------------------------------------------- */
export function compile(props: {
compiler: IAutoBeCompiler;
procedure: IAutoBeTestGenerateProcedure;
progress: AutoBeProgressEventBase;
step: number;
}): Promise<AutoBeTestValidateEvent<AutoBeTestGenerateFunction>> {
return AutoBeTestFunctionProgrammer.compile({
compiler: props.compiler,
document: props.procedure.artifacts.document,
function: props.procedure.function,
files: {
[props.procedure.function.location]: props.procedure.function.content,
[props.procedure.prepare.location]: props.procedure.prepare.content,
},
progress: props.progress,
step: props.step,
});
}
export async function replaceImportStatements(props: {
compiler: IAutoBeCompiler;
artifacts: IAutoBeTestArtifacts;
prepare: AutoBeTestPrepareFunction;
location: string;
content: string;
}): Promise<string> {
let code: string = await props.compiler.typescript.removeImportStatements(
props.content,
);
const imports: string[] = [
...AutoBeTestFunctionProgrammer.writeImportStatements(
props.artifacts.document.components.schemas,
),
`import { ${props.prepare.name} } from "${path
.relative(
path.dirname(props.location),
props.prepare.location.replace(".ts", ""),
)
.replaceAll(path.sep, "/")}";`,
];
code = [...imports, code].join("\n");
return await props.compiler.typescript.beautify(code);
}
/* ----------------------------------------------------------------
VALIDATE
---------------------------------------------------------------- */
export function validate(props: {
procedure: IAutoBeTestGenerateProcedure;
draft: string;
revise: {
final: string | null;
};
}): IValidation.IError[] {
return validateEmptyCode({
path: "$input",
asynchronous: true,
name: props.procedure.function.name,
draft: props.draft,
revise: props.revise,
});
}
}