UNPKG

@nestia/sdk

Version:

Nestia SDK and Swagger generator

363 lines (353 loc) 12.8 kB
import ts from "typescript"; import { ExpressionFactory } from "typia/lib/factories/ExpressionFactory"; import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory"; import { LiteralFactory } from "typia/lib/factories/LiteralFactory"; import { StatementFactory } from "typia/lib/factories/StatementFactory"; import { TypeFactory } from "typia/lib/factories/TypeFactory"; import { INestiaProject } from "../../structures/INestiaProject"; import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter"; import { ImportDictionary } from "./ImportDictionary"; import { SdkAliasCollection } from "./SdkAliasCollection"; import { SdkImportWizard } from "./SdkImportWizard"; export namespace SdkHttpSimulationProgrammer { export const random = (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.VariableStatement => { const output = SdkAliasCollection.responseBody(project)(importer)(route); return constant("random")( ts.factory.createArrowFunction( undefined, undefined, [ ts.factory.createParameterDeclaration( undefined, undefined, "g", ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createTypeReferenceNode( ts.factory.createIdentifier("Partial"), [ ts.factory.createTypeReferenceNode( `${SdkImportWizard.typia(importer)}.IRandomGenerator`, ), ], ), ), ], project.config.primitive === false ? output : ts.factory.createTypeReferenceNode( SdkImportWizard.Resolved(importer), [output], ), undefined, ts.factory.createCallExpression( IdentifierFactory.access( ts.factory.createIdentifier(SdkImportWizard.typia(importer)), "random", ), [output], [ts.factory.createIdentifier("g")], ), ), ); }; export const simulate = (project: INestiaProject) => (importer: ImportDictionary) => ( route: ITypedHttpRoute, props: { headers: ITypedHttpRouteParameter.IHeaders | undefined; query: ITypedHttpRouteParameter.IQuery | undefined; input: ITypedHttpRouteParameter.IBody | undefined; }, ): ts.VariableStatement => { const output: boolean = project.config.propagate === true || route.success.metadata.size() !== 0; const caller = () => ts.factory.createCallExpression( ts.factory.createIdentifier("random"), undefined, [ ts.factory.createConditionalExpression( ts.factory.createLogicalAnd( ts.factory.createStrictEquality( ts.factory.createStringLiteral("object"), ts.factory.createTypeOfExpression( ts.factory.createIdentifier("connection.simulate"), ), ), ts.factory.createStrictInequality( ts.factory.createNull(), ts.factory.createIdentifier("connection.simulate"), ), ), undefined, ts.factory.createIdentifier("connection.simulate"), undefined, ts.factory.createIdentifier("undefined"), ), ], ); return constant("simulate")( ts.factory.createArrowFunction( undefined, undefined, [ IdentifierFactory.parameter( "connection", ts.factory.createTypeReferenceNode( SdkImportWizard.IConnection(importer), route.parameters.some( (p) => p.category === "headers" && p.field === null, ) ? [ ts.factory.createTypeReferenceNode( `${route.name}.Headers`, ), ] : [], ), ), ...route.parameters .filter((p) => p.category !== "headers") .map((p) => ts.factory.createParameterDeclaration( [], undefined, p.name, p.metadata.optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, project.config.primitive !== false && (p === props.query || p === props.input) ? ts.factory.createTypeReferenceNode( `${route.name}.${p === props.query ? "Query" : "Input"}`, ) : project.config.clone === true ? SdkAliasCollection.from(project)(importer)(p.metadata) : SdkAliasCollection.name(p), ), ), ], ts.factory.createTypeReferenceNode(output ? "Output" : "void"), undefined, ts.factory.createBlock( [ ...assert(project)(importer)(route), ts.factory.createReturnStatement( project.config.propagate ? ts.factory.createAsExpression( ts.factory.createObjectLiteralExpression( [ ts.factory.createPropertyAssignment( "success", ts.factory.createTrue(), ), ts.factory.createPropertyAssignment( "status", ExpressionFactory.number( route.success.status ?? (route.method === "POST" ? 201 : 200), ), ), ts.factory.createPropertyAssignment( "headers", LiteralFactory.write({ "Content-Type": route.success.contentType, }), ), ts.factory.createPropertyAssignment("data", caller()), ], true, ), ts.factory.createTypeReferenceNode("Output"), ) : caller(), ), ], true, ), ), ); }; const assert = (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.Statement[] => { const parameters = route.parameters.filter( (p) => p.category !== "headers", ); if (parameters.length === 0) return []; const typia = SdkImportWizard.typia(importer); const validator = StatementFactory.constant({ name: "assert", value: ts.factory.createCallExpression( IdentifierFactory.access( ts.factory.createIdentifier( importer.external({ type: false, library: `@nestia/fetcher/lib/NestiaSimulator`, instance: "NestiaSimulator", }), ), "assert", ), undefined, [ ts.factory.createObjectLiteralExpression( [ ts.factory.createPropertyAssignment( "method", ts.factory.createIdentifier("METADATA.method"), ), ts.factory.createPropertyAssignment( "host", ts.factory.createIdentifier("connection.host"), ), ts.factory.createPropertyAssignment( "path", ts.factory.createCallExpression( ts.factory.createIdentifier("path"), undefined, route.parameters .filter( (p) => p.category === "param" || p.category === "query", ) .map((p) => ts.factory.createIdentifier(p.name)), ), ), ts.factory.createPropertyAssignment( "contentType", ts.factory.createIdentifier( JSON.stringify(route.success.contentType), ), ), ], true, ), ], ), }); const individual = parameters .map((p) => ts.factory.createCallExpression( (() => { const base = IdentifierFactory.access( ts.factory.createIdentifier("assert"), p.category, ); if (p.category !== "param") return base; return ts.factory.createCallExpression(base, undefined, [ ts.factory.createStringLiteral(p.name), ]); })(), undefined, [ ts.factory.createArrowFunction( undefined, undefined, [], undefined, undefined, ts.factory.createCallExpression( IdentifierFactory.access( ts.factory.createIdentifier(typia), "assert", ), undefined, [ts.factory.createIdentifier(p.name)], ), ), ], ), ) .map(ts.factory.createExpressionStatement); return [ validator, ...(project.config.propagate !== true ? individual : [tryAndCatch(importer)(individual)]), ]; }; const tryAndCatch = (importer: ImportDictionary) => (individual: ts.Statement[]) => ts.factory.createTryStatement( ts.factory.createBlock(individual, true), ts.factory.createCatchClause( "exp", ts.factory.createBlock( [ ts.factory.createIfStatement( ts.factory.createLogicalNot( ts.factory.createCallExpression( IdentifierFactory.access( ts.factory.createIdentifier( SdkImportWizard.typia(importer), ), "is", ), [ ts.factory.createTypeReferenceNode( SdkImportWizard.HttpError(importer), ), ], [ts.factory.createIdentifier("exp")], ), ), ts.factory.createThrowStatement( ts.factory.createIdentifier("exp"), ), ), ts.factory.createReturnStatement( ts.factory.createAsExpression( ts.factory.createObjectLiteralExpression( [ ts.factory.createPropertyAssignment( "success", ts.factory.createFalse(), ), ts.factory.createPropertyAssignment( "status", ts.factory.createIdentifier("exp.status"), ), ts.factory.createPropertyAssignment( "headers", ts.factory.createIdentifier("exp.headers"), ), ts.factory.createPropertyAssignment( "data", ts.factory.createIdentifier("exp.toJSON().message"), ), ], true, ), TypeFactory.keyword("any"), ), ), ], true, ), ), undefined, ); } const constant = (name: string) => (expression: ts.Expression) => ts.factory.createVariableStatement( [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList( [ ts.factory.createVariableDeclaration( ts.factory.createIdentifier(name), undefined, undefined, expression, ), ], ts.NodeFlags.Const, ), );