Skip to content

Commit 81b29f6

Browse files
author
sam
committed
feat: lambda proxy integration
1 parent 6fba585 commit 81b29f6

File tree

11 files changed

+154
-13
lines changed

11 files changed

+154
-13
lines changed

.projen/deps.json

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.projenrc.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,12 @@ const project = new CustomTypescriptProject({
7575
bin: {
7676
functionless: "./bin/functionless.js",
7777
},
78-
deps: ["fs-extra", "minimatch", "@functionless/nodejs-closure-serializer"],
78+
deps: [
79+
"@types/aws-lambda",
80+
"fs-extra",
81+
"minimatch",
82+
"@functionless/nodejs-closure-serializer",
83+
],
7984
devDeps: [
8085
`@aws-cdk/aws-appsync-alpha@${MIN_CDK_VERSION}-alpha.0`,
8186
"@types/fs-extra",

package.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/compile-test-app.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@ const { tsc } = require("../lib/tsc");
33

44
(async function () {
55
await tsc(path.join(__dirname, "..", "test-app"));
6-
})().catch((err) => process.exit(1));
6+
})().catch((err) => {
7+
console.error(err);
8+
process.exit(1);
9+
});

src/api.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { aws_apigateway } from "aws-cdk-lib";
2+
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
23
import { Construct } from "constructs";
3-
import { isParameterDecl } from ".";
4-
import { FunctionDecl, isFunctionDecl } from "./declaration";
4+
import { FunctionDecl, isFunctionDecl, isParameterDecl } from "./declaration";
55
import { isErr } from "./error";
66
import {
77
Identifier,
@@ -20,6 +20,7 @@ import {
2020
ObjectLiteralExpr,
2121
PropAccessExpr,
2222
} from "./expression";
23+
import { Function } from "./function";
2324
import { findIntegration, IntegrationImpl } from "./integration";
2425
import { FunctionlessNode } from "./node";
2526
import { isReturnStmt, isVariableStmt, ReturnStmt } from "./statement";
@@ -83,7 +84,7 @@ export abstract class BaseApiIntegration {
8384
*/
8485
public abstract addMethod(
8586
httpMethod: HttpMethod,
86-
resource: aws_apigateway.Resource
87+
resource: aws_apigateway.IResource
8788
): aws_apigateway.Method;
8889
}
8990

@@ -225,7 +226,7 @@ export interface AwsApiIntegrationProps<
225226
* @see https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-integration-types.html
226227
*/
227228
export class AwsApiIntegration<
228-
Request,
229+
Request extends ApiRequest<any, any, any, any>,
229230
IntegrationResponse,
230231
MethodResponse
231232
> extends BaseApiIntegration {
@@ -553,3 +554,36 @@ export interface ApiGatewayVtlIntegration {
553554
responses: aws_apigateway.IntegrationResponse[]
554555
) => aws_apigateway.Integration;
555556
}
557+
558+
export interface LambdaProxyApiIntegrationProps
559+
extends Omit<
560+
aws_apigateway.LambdaIntegrationOptions,
561+
| "requestParameters"
562+
| "requestTemplates"
563+
| "integrationResponses"
564+
| "passthroughBehavior"
565+
| "proxy"
566+
> {
567+
function: Function<APIGatewayProxyEvent, APIGatewayProxyResult>;
568+
}
569+
570+
export class LambdaProxyApiIntegration extends BaseApiIntegration {
571+
readonly function;
572+
constructor(private readonly props: LambdaProxyApiIntegrationProps) {
573+
super();
574+
this.function = props.function;
575+
}
576+
577+
public addMethod(
578+
httpMethod: HttpMethod,
579+
resource: aws_apigateway.IResource
580+
): aws_apigateway.Method {
581+
return resource.addMethod(
582+
httpMethod,
583+
new aws_apigateway.LambdaIntegration(this.function.resource, {
584+
...this.props,
585+
proxy: true,
586+
})
587+
);
588+
}
589+
}

src/function.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,9 @@ export class Function<P, O> extends FunctionBase<P, O> {
241241
* To correctly resolve these for CDK synthesis, either use `asyncSynth()` or use `cdk synth` in the CDK cli.
242242
* https://twitter.com/samgoodwin89/status/1516887131108438016?s=20&t=7GRGOQ1Bp0h_cPsJgFk3Ww
243243
*/
244-
public static readonly promises = ((global as any)[PromisesSymbol] =
245-
(global as any)[PromisesSymbol] ?? []);
244+
public static readonly promises: Promise<any>[] = ((global as any)[
245+
PromisesSymbol
246+
] = (global as any)[PromisesSymbol] ?? []);
246247

247248
/**
248249
* Wrap a {@link aws_lambda.Function} with Functionless.

src/tsc.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export async function tsc(
3131
projectRoot: string = process.cwd(),
3232
props?: TscProps
3333
) {
34-
const tsConfigPath = path.join(projectRoot, "tsconfig.json");
34+
const tsConfigPath = path.join(projectRoot, "tsconfig.dev.json");
3535
let tsConfig: {
3636
include: string[];
3737
exclude?: string[];

test-app/yarn.lock

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,6 +1614,11 @@
16141614
resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.93.tgz#3e2c80894122477040aabf29b7320556f5702a76"
16151615
integrity sha512-Vsyi9ogDAY3REZDjYnXMRJJa62SDvxHXxJI5nGDQdZW058dDE+av/anynN2rLKbCKXDRNw3D/sQmqxVflZFi4A==
16161616

1617+
"@types/aws-lambda@^8.10.98":
1618+
version "8.10.98"
1619+
resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.98.tgz#2936975c19529011b8b5c08850d42157711c690d"
1620+
integrity sha512-dJ/R9qamtI2nNpxhNwPBTwsfYwbcCWsYBJxhpgGyMLCD0HxKpORcMpPpSrFP/FIceNEYfnS3R5EfjSZJmX2oJg==
1621+
16171622
"@types/js-yaml@^4.0.0":
16181623
version "4.0.5"
16191624
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138"
@@ -2695,6 +2700,7 @@ function.prototype.name@^1.1.5:
26952700
version "0.0.0"
26962701
dependencies:
26972702
"@functionless/nodejs-closure-serializer" "^0.0.2"
2703+
"@types/aws-lambda" "^8.10.98"
26982704
fs-extra "^10.1.0"
26992705
minimatch "^5.1.0"
27002706

test/api.localstack.test.ts

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { aws_apigateway } from "aws-cdk-lib";
2+
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
23
import axios from "axios";
3-
import { MockApiIntegration } from "../src";
4+
import {
5+
AwsApiIntegration,
6+
Function,
7+
LambdaProxyApiIntegration,
8+
MockApiIntegration,
9+
} from "../src";
410
import { localstackTestSuite } from "./localstack";
511

612
localstackTestSuite("apiGatewayStack", (test, stack) => {
@@ -40,4 +46,76 @@ localstackTestSuite("apiGatewayStack", (test, stack) => {
4046
});
4147
}
4248
);
49+
50+
test.skip(
51+
"lambda function integration",
52+
() => {
53+
const api = new aws_apigateway.RestApi(stack, "LambdaAPI");
54+
const func = new Function(stack, "Func", async (_input: any) => {
55+
return { key: "hello" };
56+
});
57+
58+
new AwsApiIntegration({
59+
request: (req: {
60+
pathParameters: {
61+
code: number;
62+
};
63+
}) =>
64+
func({
65+
input: req.pathParameters.code,
66+
}),
67+
response: (result) => ({
68+
result: result.key,
69+
}),
70+
}).addMethod("GET", api.root);
71+
72+
return {
73+
outputs: {
74+
endpoint: api.url,
75+
},
76+
};
77+
},
78+
async (context) => {
79+
const response = await axios.get(context.endpoint);
80+
expect(response.data).toEqual({ result: "hello" });
81+
}
82+
);
83+
84+
test(
85+
"lambda proxy integration",
86+
() => {
87+
const api = new aws_apigateway.RestApi(stack, "LambdaAPI");
88+
const func = new Function<APIGatewayProxyEvent, APIGatewayProxyResult>(
89+
stack,
90+
"Func",
91+
async (request) => {
92+
return {
93+
statusCode: 200,
94+
body: JSON.stringify({
95+
hello: "world",
96+
path: request.path,
97+
}),
98+
};
99+
}
100+
);
101+
102+
new LambdaProxyApiIntegration({
103+
function: func,
104+
}).addMethod("GET", api.root);
105+
106+
return {
107+
outputs: {
108+
endpoint: api.url,
109+
},
110+
};
111+
},
112+
async (context) => {
113+
const response = await axios.get(context.endpoint);
114+
expect(response.status).toEqual(200);
115+
expect(response.data).toMatchObject({
116+
hello: "world",
117+
path: "/",
118+
});
119+
}
120+
);
43121
});

test/localstack.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { CloudFormationDeployments } from "aws-cdk/lib/api/cloudformation-deploy
66
import { CloudFormation } from "aws-sdk";
77
import { Construct } from "constructs";
88
import { asyncSynth } from "../src/async-synth";
9+
import { Function } from "../src/function";
910

1011
export const clientConfig = {
1112
endpoint: "http://localhost:4566",
@@ -44,10 +45,11 @@ export const deployStack = async (app: App, stack: Stack) => {
4445
sdkProvider,
4546
});
4647

48+
const stackArtifact = cloudAssembly.getStackArtifact(
49+
stack.artifactId
50+
) as unknown as cxapi.CloudFormationStackArtifact;
4751
await cfn.deployStack({
48-
stack: cloudAssembly.getStackArtifact(
49-
stack.artifactId
50-
) as unknown as cxapi.CloudFormationStackArtifact,
52+
stack: stackArtifact,
5153
force: true,
5254
});
5355
};
@@ -125,6 +127,8 @@ export const localstackTestSuite = (
125127
return {};
126128
});
127129

130+
await Promise.all(Function.promises);
131+
128132
await deployStack(app, stack);
129133

130134
stackOutputs = (

0 commit comments

Comments
 (0)