@nestia/sdk
Version:
Nestia SDK and Swagger generator
302 lines (296 loc) • 11.5 kB
text/typescript
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)),
];
};
}