Skip to content

Commit

Permalink
feat(type-safe-api): support configuring lambda handler runtime versions
Browse files Browse the repository at this point in the history
Users can now specify the runtime version for python, java and typescript (node) lambda handlers. As
well as the lambda runtime version, this configures settings for the project itself to target that
particular runtime, and is therefore configured in the projenrc.

Fixes #672
  • Loading branch information
cogwirrel committed Jan 23, 2024
1 parent 3338550 commit bff6c4e
Show file tree
Hide file tree
Showing 19 changed files with 501 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -415,3 +415,79 @@ If your lambda handlers rely on native dependencies, you will need to ensure you

!!!warning
For TypeScript and Java, you may need to override the `package` task for the handlers project to run the appropriate commands to build your handlers with their native dependencies, or consider consuming them using a [Lambda layer](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html).

## Runtime Versions

You can configure the desired runtime versions of your handler projects in your `.projenrc`. This adjusts the appropriate project settings (such as the language level and packaging command), as well as configures the function CDK constructs to target this runtime version.

For example:

=== "TS"

```ts
new TypeSafeApiProject({
...
handlers: {
languages: [Language.PYTHON, Language.JAVA, Language.TYPESCRIPT],
options: {
python: {
runtimeVersion: PythonVersion.PYTHON_3_12,
},
java: {
runtimeVersion: JavaVersion.JAVA_21,
},
typescript: {
runtimeVersion: NodeVersion.NODE_20,
},
}
}
});
```

=== "JAVA"

```java
new TypeSafeApiProject(TypeSafeApiProjectOptions.builder()
...
.handlers(HandlersConfiguration.builder()
.languages(Arrays.asList(Language.PYTHON))
.options(GeneratedHandlersCodeOptions.builder()
.python(GeneratedPythonHandlersOptions.builder()
.runtimeVersion(PythonVersion.PYTHON_3_12)
.build())
.java(GeneratedJavaHandlersOptions.builder()
.runtimeVersion(JavaVersion.JAVA_21)
.build())
.typescript(GeneratedTypeScriptHandlersOptions.builder()
.runtimeVersion(NodeVersion.NODE_20)
.build())
.build())
.build())
.build());
```

=== "PYTHON"

```python

TypeSafeApiProject(
...
handlers=HandlersConfiguration(
languages=[Language.PYTHON],
options=GeneratedHandlersCodeOptions(
python=GeneratedPythonHandlersOptions(
runtime_version=PythonVersion.PYTHON_3_12
),
java=GeneratedJavaHandlersOptions(
runtime_version=JavaVersion.JAVA_21
),
typescript=GeneratedTypeScriptHandlersOptions(
runtime_version=NodeVersion.NODE_20
),
)
),
)
```

!!!note
You will need to have the specified runtime version (or greater) installed on your system in order to make use of it.
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ public class {{operationIdCamelCase}}FunctionProps implements {{#startsWith vend
private final String handler = "{{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-handlers-java-package}}{{/apis.0}}{{/apiInfo}}.{{operationIdCamelCase}}Handler";
{{~/startsWith}}
{{#startsWith vendorExtensions.x-handler.language 'typescript' ~}}
private final Runtime runtime = Runtime.NODEJS_18_X;
private final Runtime runtime = Runtime.{{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-handlers-node-lambda-runtime-version}}{{/apis.0}}{{/apiInfo}};
{{~/startsWith}}{{#startsWith vendorExtensions.x-handler.language 'python' ~}}
private final Runtime runtime = Runtime.PYTHON_3_11;
private final Runtime runtime = Runtime.{{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-handlers-python-lambda-runtime-version}}{{/apis.0}}{{/apiInfo}};
{{~/startsWith}}{{#startsWith vendorExtensions.x-handler.language 'java' ~}}
private final Runtime runtime = Runtime.JAVA_17;
private final Runtime runtime = Runtime.{{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-handlers-java-lambda-runtime-version}}{{/apis.0}}{{/apiInfo}};
{{~/startsWith}}

// Props with defaults
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ class {{operationIdCamelCase}}Function({{#startsWith vendorExtensions.x-handler.
def __init__(self, scope, id, **kwargs):
super().__init__(scope, id,
{{#startsWith vendorExtensions.x-handler.language 'typescript' ~}}
runtime=Runtime.NODEJS_18_X,
runtime=Runtime.{{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-handlers-node-lambda-runtime-version}}{{/apis.0}}{{/apiInfo}},
{{~/startsWith}}{{#startsWith vendorExtensions.x-handler.language 'python' ~}}
runtime=Runtime.PYTHON_3_11,
runtime=Runtime.{{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-handlers-python-lambda-runtime-version}}{{/apis.0}}{{/apiInfo}},
{{~/startsWith}}{{#startsWith vendorExtensions.x-handler.language 'java' ~}}
runtime=Runtime.JAVA_17,
runtime=Runtime.{{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-handlers-java-lambda-runtime-version}}{{/apis.0}}{{/apiInfo}},
{{~/startsWith}}
{{#startsWith vendorExtensions.x-handler.language 'typescript' ~}}
handler="index.handler",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ export class {{operationIdCamelCase}}Function extends {{#startsWith vendorExtens
constructor(scope: Construct, id: string, props?: {{operationIdCamelCase}}FunctionProps) {
super(scope, id, {
{{#startsWith vendorExtensions.x-handler.language 'typescript' ~}}
runtime: Runtime.NODEJS_18_X,
runtime: Runtime.{{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-handlers-node-lambda-runtime-version}}{{/apis.0}}{{/apiInfo}},
{{~/startsWith}}{{#startsWith vendorExtensions.x-handler.language 'python' ~}}
runtime: Runtime.PYTHON_3_11,
runtime: Runtime.{{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-handlers-python-lambda-runtime-version}}{{/apis.0}}{{/apiInfo}},
{{~/startsWith}}{{#startsWith vendorExtensions.x-handler.language 'java' ~}}
runtime: Runtime.JAVA_17,
runtime: Runtime.{{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-handlers-java-lambda-runtime-version}}{{/apis.0}}{{/apiInfo}},
{{~/startsWith}}
{{#startsWith vendorExtensions.x-handler.language 'typescript' ~}}
handler: "index.handler",
Expand Down
10 changes: 10 additions & 0 deletions packages/type-safe-api/src/project/codegen/components/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as readPkg from "read-pkg-up";
import { Language, Library } from "../../languages";
import { MockResponseDataGenerationOptions } from "../../types";
import { GeneratedHandlersProjects } from "../generate";
import { RuntimeVersionUtils } from "../runtime-version-utils";

/**
* Enum for generator directories for non-runtime generators
Expand Down Expand Up @@ -244,4 +245,13 @@ export const getHandlersProjectVendorExtensions = (
`${java.pom.artifactId}-${java.pom.version}.jar`
)
: "",
"x-handlers-node-lambda-runtime-version": typescript
? RuntimeVersionUtils.NODE.getLambdaRuntime(typescript.runtimeVersion)
: "",
"x-handlers-python-lambda-runtime-version": python
? RuntimeVersionUtils.PYTHON.getLambdaRuntime(python.runtimeVersion)
: "",
"x-handlers-java-lambda-runtime-version": java
? RuntimeVersionUtils.JAVA.getLambdaRuntime(java.runtimeVersion)
: "",
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ SPDX-License-Identifier: Apache-2.0 */
import * as path from "path";
import { DependencyType, SampleDir } from "projen";
import { JavaProject } from "projen/lib/java";
import { JavaVersion } from "../../languages";
import {
CodeGenerationSourceOptions,
GeneratedJavaHandlersOptions,
Expand All @@ -19,6 +20,7 @@ import {
TypeSafeApiScript,
} from "../components/utils";
import { GeneratedJavaRuntimeProject } from "../runtime/generated-java-runtime-project";
import { RuntimeVersionUtils } from "../runtime-version-utils";

export interface GeneratedJavaHandlersProjectOptions
extends GeneratedJavaHandlersOptions,
Expand Down Expand Up @@ -53,14 +55,23 @@ export class GeneratedJavaHandlersProject extends JavaProject {
*/
public readonly packageName: string;

/**
* Java runtime version for the handlers
*/
public readonly runtimeVersion: JavaVersion;

constructor(options: GeneratedJavaHandlersProjectOptions) {
super({
sample: false,
junit: false,
compileOptions: RuntimeVersionUtils.JAVA.getMavenCompileOptions(
options.runtimeVersion
),
...(options as any),
});
TypeSafeApiCommandEnvironment.ensure(this);
this.options = options;
this.runtimeVersion = options.runtimeVersion ?? JavaVersion.JAVA_17;
this.packageName = `${this.pom.groupId}.${this.name}.handlers`;
this.srcDir = path.join(
"src",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ SPDX-License-Identifier: Apache-2.0 */
import * as path from "path";
import { DependencyType, SampleFile } from "projen";
import { PythonProject } from "projen/lib/python";
import { PythonVersion } from "../../languages";
import {
CodeGenerationSourceOptions,
GeneratedPythonHandlersOptions,
Expand All @@ -20,6 +21,7 @@ import {
TypeSafeApiScript,
} from "../components/utils";
import { GeneratedPythonRuntimeProject } from "../runtime/generated-python-runtime-project";
import { RuntimeVersionUtils } from "../runtime-version-utils";

export interface GeneratedPythonHandlersProjectOptions
extends GeneratedPythonHandlersOptions,
Expand All @@ -43,6 +45,11 @@ export class GeneratedPythonHandlersProject extends PythonProject {
*/
private readonly tstDir: string;

/**
* Python runtime version for the handlers
*/
public readonly runtimeVersion: PythonVersion;

constructor(options: GeneratedPythonHandlersProjectOptions) {
super({
pytest: true,
Expand All @@ -57,7 +64,7 @@ export class GeneratedPythonHandlersProject extends PythonProject {
});
TypeSafeApiCommandEnvironment.ensure(this);
this.options = options;

this.runtimeVersion = options.runtimeVersion ?? PythonVersion.PYTHON_3_11;
this.tstDir = "test";

if (options.pytest ?? true) {
Expand All @@ -70,7 +77,9 @@ export class GeneratedPythonHandlersProject extends PythonProject {
}

[
"python@^3.11",
RuntimeVersionUtils.PYTHON.getPythonDependencyVersion(
this.runtimeVersion
),
`${options.generatedPythonTypes.name}@{path="${path.relative(
this.outdir,
options.generatedPythonTypes.outdir
Expand Down Expand Up @@ -134,7 +143,9 @@ export class GeneratedPythonHandlersProject extends PythonProject {
? "manylinux2014_aarch64"
: "manylinux2014_x86_64";
this.packageTask.exec(
`pip install -r dist/lambda/requirements.txt --target dist/lambda --upgrade --platform ${platform} --only-binary :all:`
`pip install -r dist/lambda/requirements.txt --target dist/lambda --upgrade --platform ${platform} --only-binary :all: --python-version ${RuntimeVersionUtils.PYTHON.getPipPackagingPythonVersion(
this.runtimeVersion
)}`
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as path from "path";
import { DependencyType, IgnoreFile, SampleDir } from "projen";
import { NodePackageManager } from "projen/lib/javascript";
import { TypeScriptProject } from "projen/lib/typescript";
import { NodeVersion } from "../../languages";
import {
CodeGenerationSourceOptions,
GeneratedTypeScriptHandlersOptions,
Expand All @@ -20,6 +21,7 @@ import {
TypeSafeApiScript,
} from "../components/utils";
import { GeneratedTypescriptRuntimeProject } from "../runtime/generated-typescript-runtime-project";
import { RuntimeVersionUtils } from "../runtime-version-utils";

export interface GeneratedTypescriptHandlersProjectOptions
extends GeneratedTypeScriptHandlersOptions,
Expand All @@ -42,6 +44,11 @@ export class GeneratedTypescriptHandlersProject extends TypeScriptProject {
*/
private readonly options: GeneratedTypescriptHandlersProjectOptions;

/**
* Node runtime version for the handlers
*/
public readonly runtimeVersion: NodeVersion;

constructor(options: GeneratedTypescriptHandlersProjectOptions) {
super({
...(options as any),
Expand All @@ -59,6 +66,7 @@ export class GeneratedTypescriptHandlersProject extends TypeScriptProject {
npmignoreEnabled: false,
});
this.options = options;
this.runtimeVersion = options.runtimeVersion ?? NodeVersion.NODE_18;

TypeSafeApiCommandEnvironment.ensure(this);

Expand Down Expand Up @@ -122,10 +130,15 @@ export class GeneratedTypescriptHandlersProject extends TypeScriptProject {
// Note that every typescript file directly in src is bundled by default, but users may specify their own
// entry point globs if they prefer a different directory structure.
this.packageTask.exec(`mkdir -p dist/lambda && rm -rf dist/lambda/*`);

this.packageTask.exec(
`esbuild --bundle ${(
options.handlerEntryPoints ?? [`${this.srcdir}/*.ts`]
).join(" ")} --platform=node --outdir=dist/lambda`
).join(
" "
)} --platform=node --outdir=dist/lambda --target=${RuntimeVersionUtils.NODE.getEsbuildNodeTarget(
this.runtimeVersion
)}`
);
// Move each bundled file into a separate directory
this.packageTask.exec(
Expand Down
Loading

0 comments on commit bff6c4e

Please sign in to comment.