UNPKG

@nestia/sdk

Version:

Nestia SDK and Swagger generator

302 lines (296 loc) 11.5 kB
import ts from "typescript"; import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory"; import { TypeFactory } from "typia/lib/factories/TypeFactory"; import { INestiaProject } from "../../structures/INestiaProject"; import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter"; import { StringUtil } from "../../utils/StringUtil"; import { ImportDictionary } from "./ImportDictionary"; import { SdkAliasCollection } from "./SdkAliasCollection"; import { SdkImportWizard } from "./SdkImportWizard"; export namespace SdkHttpFunctionProgrammer { export const write = (project: INestiaProject) => (importer: ImportDictionary) => ( route: ITypedHttpRoute, props: { headers: ITypedHttpRouteParameter.IHeaders | undefined; query: ITypedHttpRouteParameter.IQuery | undefined; input: ITypedHttpRouteParameter.IBody | undefined; }, ): ts.FunctionDeclaration => ts.factory.createFunctionDeclaration( [ ts.factory.createModifier(ts.SyntaxKind.ExportKeyword), ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword), ], undefined, route.name, undefined, [ IdentifierFactory.parameter( "connection", ts.factory.createTypeReferenceNode( SdkImportWizard.IConnection(importer), props.headers ? [ts.factory.createTypeReferenceNode(`${route.name}.Headers`)] : undefined, ), ), ...route.parameters .filter((p) => p.category !== "headers") .map((p) => ts.factory.createParameterDeclaration( [], undefined, p.name, p.metadata.optional === true ? 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("Promise", [ project.config.propagate === true || route.success.metadata.size() !== 0 ? ts.factory.createTypeReferenceNode(`${route.name}.Output`) : ts.factory.createTypeReferenceNode("void"), ]), ts.factory.createBlock( write_body(project)(importer)(route, props), true, ), ); const write_body = (project: INestiaProject) => (importer: ImportDictionary) => ( route: ITypedHttpRoute, props: { headers: ITypedHttpRouteParameter.IHeaders | undefined; query: ITypedHttpRouteParameter.IQuery | undefined; input: ITypedHttpRouteParameter.IBody | undefined; }, ): ts.Statement[] => { const caller = () => ts.factory.createCallExpression( IdentifierFactory.access( ts.factory.createIdentifier( SdkImportWizard.Fetcher( !!props.input?.encrypted || route.success.encrypted, )(importer), ), project.config.propagate ? "propagate" : "fetch", ), project.config.propagate ? route.method.toLowerCase() === "get" || route.method.toLowerCase() === "head" ? [TypeFactory.keyword("any")] : [TypeFactory.keyword("any"), TypeFactory.keyword("any")] : undefined, [ props.input && props.input.contentType !== "multipart/form-data" ? ts.factory.createObjectLiteralExpression( [ ts.factory.createSpreadAssignment( ts.factory.createIdentifier("connection"), ), ts.factory.createPropertyAssignment( "headers", ts.factory.createObjectLiteralExpression( [ ts.factory.createSpreadAssignment( IdentifierFactory.access( ts.factory.createIdentifier("connection"), "headers", ), ), ts.factory.createPropertyAssignment( ts.factory.createStringLiteral("Content-Type"), ts.factory.createStringLiteral( props.input?.contentType ?? "application/json", ), ), ], true, ), ), ], true, ) : ts.factory.createIdentifier("connection"), ts.factory.createObjectLiteralExpression( [ ts.factory.createSpreadAssignment( IdentifierFactory.access( ts.factory.createIdentifier(route.name), "METADATA", ), ), ts.factory.createPropertyAssignment( "template", IdentifierFactory.access( IdentifierFactory.access( ts.factory.createIdentifier(route.name), "METADATA", ), "path", ), ), ts.factory.createPropertyAssignment( "path", ts.factory.createCallExpression( IdentifierFactory.access( ts.factory.createIdentifier(route.name), "path", ), undefined, route.parameters .filter( (p) => p.category === "param" || p.category === "query", ) .map((p) => ts.factory.createIdentifier(p.name)), ), ), ], true, ), ...(props.input ? [ts.factory.createIdentifier(props.input.name)] : []), ...(project.config.json && props.input !== undefined && (props.input.contentType === "application/json" || props.input.encrypted === true) ? [ts.factory.createIdentifier(`${route.name}.stringify`)] : []), ], ); const output = (awaiter: boolean) => project.config.simulate ? ts.factory.createConditionalExpression( ts.factory.createIdentifier("!!connection.simulate"), undefined, ts.factory.createCallExpression( ts.factory.createIdentifier(`${route.name}.simulate`), [], [ ts.factory.createIdentifier("connection"), ...route.parameters .filter((p) => p.category !== "headers") .map((p) => ts.factory.createIdentifier(p.name)), ], ), undefined, awaiter ? ts.factory.createAwaitExpression(caller()) : caller(), ) : awaiter ? ts.factory.createAwaitExpression(caller()) : caller(); return [ ...(project.config.assert ? route.parameters .filter((p) => p.category !== "headers") .map((p) => ts.factory.createExpressionStatement( ts.factory.createCallExpression( IdentifierFactory.access( ts.factory.createIdentifier( SdkImportWizard.typia(importer), ), "assert", ), [ ts.factory.createTypeQueryNode( ts.factory.createIdentifier(p.name), ), ], [ts.factory.createIdentifier(p.name)], ), ), ) : []), ...(route.success.setHeaders.length === 0 ? [ts.factory.createReturnStatement(output(false))] : write_set_headers(project)(importer)(route)(output(true))), ]; }; const write_set_headers = (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedHttpRoute) => (condition: ts.Expression): ts.Statement[] => { const accessor = (x: string) => (y: string) => x[0] === "[" ? `${x}${y}` : `${x}.${y}`; const output: string = StringUtil.escapeDuplicate([ "connection", ...route.parameters.map((p) => p.name), ])("output"); const headers: string = accessor("connection")("headers"); const data: string = project.config.propagate ? accessor(output)("data") : output; const assigners: ts.ExpressionStatement[] = [ ts.factory.createBinaryExpression( ts.factory.createIdentifier(headers), ts.factory.createToken(ts.SyntaxKind.QuestionQuestionEqualsToken), ts.factory.createObjectLiteralExpression([]), ), ...route.success.setHeaders.map((tuple) => tuple.type === "assigner" ? ts.factory.createCallExpression( ts.factory.createIdentifier("Object.assign"), [], [ ts.factory.createIdentifier(headers), ts.factory.createIdentifier(accessor(data)(tuple.source)), ], ) : ts.factory.createBinaryExpression( ts.factory.createIdentifier( accessor(headers)(tuple.target ?? tuple.source), ), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createIdentifier(accessor(data)(tuple.source)), ), ), ].map(ts.factory.createExpressionStatement); return [ ts.factory.createVariableStatement( [], ts.factory.createVariableDeclarationList( [ ts.factory.createVariableDeclaration( output, undefined, SdkAliasCollection.output(project)(importer)(route), condition, ), ], ts.NodeFlags.Const, ), ), ...(project.config.propagate ? [ ts.factory.createIfStatement( ts.factory.createIdentifier(accessor(output)("success")), assigners.length === 1 ? assigners[0] : ts.factory.createBlock(assigners, true), undefined, ), ] : assigners), ts.factory.createReturnStatement(ts.factory.createIdentifier(output)), ]; }; }