Skip to content

Commit

Permalink
ruining colin's life
Browse files Browse the repository at this point in the history
  • Loading branch information
TheRealAmazonKendra committed Jun 6, 2024
1 parent 0fa3b1e commit df9727c
Show file tree
Hide file tree
Showing 33 changed files with 1,468 additions and 189 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This framework allows for the creation of three component types:
Code generating one of these three component types requires adding the component properties to the [config](./config.ts) file by providing `ComponentProps`. The `ComponentProps` are responsible for code generating the specified `ComponentType` with the `handler`, `runtime`, `code`, and `codeDirectory` properties set internally. `ComponentProps` includes the following properties:
- `type` - the framework component type to generate.
- `sourceCode` - the source code that will be excuted by the framework component.
- `runtime` - the runtime that is compatible with the framework component's source code. This is an optional property with a default node runtime maintained by the framework.
- `runtime` - the runtime that is compatible with the framework component's source code. This is an optional property with a default node runtime that will be the latest available node runtime in the `Stack` deployment region. In general, you should not configure this property unless a `runtime` override is absolutely necessary.
- `handler` - the name of the method with the source code that the framework component will call. This is an optional property and the default is `index.handler`.
- `minifyAndBundle` - whether the source code should be minified and bundled. This an optional property and the default is `true`. This should only be set to `false` for python files or for typescript/javascript files with a require import.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* eslint-disable import/no-extraneous-dependencies */
import { $E, Expression, IScope, ThingSymbol, expr } from '@cdklabs/typewriter';

/**
* A class representing an expression proxy which builds a JavaScript object that
* will mirror the JavaScript operations done to it in an expression tree.
*/
export class CallableExpr {
/**
* Creates a new CallableExpr that can be called with the specified name.
*/
public static fromName(scope: IScope, name: string) {
return new CallableExpr(scope, name);
}

private readonly expr: Expression;

private constructor(readonly scope: IScope, private readonly name: string) {
this.expr = $E(expr.sym(new ThingSymbol(name, scope)));
}

/**
* Calls the expression proxy with the provided arguments.
*/
public call(...args: Expression[]) {
return this.expr.call(...args);
}

/**
* The name of the expression proxy.
*/
public toString() {
return this.name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,23 @@ import {
expr,
Type,
Splat,
ExternalModule,
PropertySpec,
InterfaceSpec,
InterfaceType,
ObjectLiteral,
MemberVisibility,
SuperInitializer,
Expression,
ClassSpec,
$T,
} from '@cdklabs/typewriter';
import { Runtime } from './config';
import { HandlerFrameworkModule } from './framework';
import {
PATH_MODULE,
CONSTRUCTS_MODULE,
LAMBDA_MODULE,
CORE_MODULE,
CORE_INTERNAL_STACK,
CORE_INTERNAL_CR_PROVIDER,
PATH_MODULE,
} from './modules';
import { toLambdaRuntime } from './utils/framework-utils';

Expand Down Expand Up @@ -70,14 +69,22 @@ export interface HandlerFrameworkClassProps {
readonly codeDirectory: string;

/**
* The runtime environment for the framework component.
* The name of the method within your code that framework component calls.
*/
readonly runtime: Runtime;
readonly handler: string;

/**
* The name of the method within your code that framework component calls.
* The runtime environment for the framework component.
*
* @default - the latest Lambda runtime available in the region.
*/
readonly handler: string;
readonly runtime?: Runtime;
}

interface BuildRuntimePropertyOptions {
readonly runtime?: Runtime;
readonly isCustomResourceProvider?: boolean;
readonly isEvalNodejsProvider?: boolean;
}

export abstract class HandlerFrameworkClass extends ClassType {
Expand All @@ -86,22 +93,26 @@ export abstract class HandlerFrameworkClass extends ClassType {
*/
public static buildFunction(scope: HandlerFrameworkModule, props: HandlerFrameworkClassProps): HandlerFrameworkClass {
return new (class Function extends HandlerFrameworkClass {
protected readonly externalModules = [PATH_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE];

public constructor() {
super(scope, {
name: props.name,
extends: LAMBDA_MODULE.Function,
export: true,
});

this.importExternalModulesInto(scope);
if (scope.isAlphaModule) {
scope.registerImport(LAMBDA_MODULE, { fromLocation: 'aws-cdk-lib/aws-lambda' });
} else {
scope.registerImport(LAMBDA_MODULE);
}

const superProps = new ObjectLiteral([
new Splat(expr.ident('props')),
['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)],
['code', $T(LAMBDA_MODULE.Code).fromAsset(
PATH_MODULE.join.call(expr.directCode(`__dirname, '${props.codeDirectory}'`)),
)],
['handler', expr.lit(props.handler)],
['runtime', expr.directCode(toLambdaRuntime(props.runtime))],
['runtime', this.buildRuntimeProperty(scope, { runtime: props.runtime })],
]);
this.buildConstructor({
constructorPropsType: LAMBDA_MODULE.FunctionOptions,
Expand All @@ -118,18 +129,22 @@ export abstract class HandlerFrameworkClass extends ClassType {
*/
public static buildSingletonFunction(scope: HandlerFrameworkModule, props: HandlerFrameworkClassProps): HandlerFrameworkClass {
return new (class SingletonFunction extends HandlerFrameworkClass {
protected readonly externalModules = [PATH_MODULE, CONSTRUCTS_MODULE, LAMBDA_MODULE];

public constructor() {
super(scope, {
name: props.name,
extends: LAMBDA_MODULE.SingletonFunction,
export: true,
});

if (scope.isAlphaModule) {
scope.registerImport(LAMBDA_MODULE, { fromLocation: 'aws-cdk-lib/aws-lambda' });
} else {
scope.registerImport(LAMBDA_MODULE);
}

const isEvalNodejsProvider = this.fqn.includes('eval-nodejs-provider');

this.importExternalModulesInto(scope);
scope.registerImport(LAMBDA_MODULE);

const uuid: PropertySpec = {
name: 'uuid',
Expand Down Expand Up @@ -181,9 +196,11 @@ export abstract class HandlerFrameworkClass extends ClassType {

const superProps = new ObjectLiteral([
new Splat(expr.ident('props')),
['code', expr.directCode(`lambda.Code.fromAsset(path.join(__dirname, '${props.codeDirectory}'))`)],
['code', $T(LAMBDA_MODULE.Code).fromAsset(
PATH_MODULE.join.call(expr.directCode(`__dirname, '${props.codeDirectory}'`)),
)],
['handler', expr.lit(props.handler)],
['runtime', expr.directCode(`${isEvalNodejsProvider ? 'props.runtime ?? ' : ''}${toLambdaRuntime(props.runtime)}`)],
['runtime', this.buildRuntimeProperty(scope, { runtime: props.runtime, isEvalNodejsProvider })],
]);
this.buildConstructor({
constructorPropsType: _interface.type,
Expand All @@ -199,23 +216,34 @@ export abstract class HandlerFrameworkClass extends ClassType {
*/
public static buildCustomResourceProvider(scope: HandlerFrameworkModule, props: HandlerFrameworkClassProps): HandlerFrameworkClass {
return new (class CustomResourceProvider extends HandlerFrameworkClass {
protected readonly externalModules: ExternalModule[] = [PATH_MODULE, CONSTRUCTS_MODULE];

public constructor() {
super(scope, {
name: props.name,
extends: scope.coreInternal
? CORE_INTERNAL_CR_PROVIDER.CustomResourceProviderBase
: CORE_MODULE.CustomResourceProviderBase,
extends: CORE_MODULE.CustomResourceProviderBase,
export: true,
});

if (scope.coreInternal) {
this.externalModules.push(...[CORE_INTERNAL_STACK, CORE_INTERNAL_CR_PROVIDER]);
if (scope.isCoreInternal) {
scope.registerImport(CORE_MODULE, {
targets: [CORE_MODULE.Stack],
fromLocation: '../../stack',
});
scope.registerImport(CORE_MODULE, {
targets: [
CORE_MODULE.CustomResourceProviderBase,
CORE_MODULE.CustomResourceProviderOptions,
],
fromLocation: '../../custom-resource-provider',
});
} else {
this.externalModules.push(CORE_MODULE);
scope.registerImport(CORE_MODULE, {
targets: [
CORE_MODULE.Stack,
CORE_MODULE.CustomResourceProviderBase,
CORE_MODULE.CustomResourceProviderOptions,
],
});
}
this.importExternalModulesInto(scope);

const getOrCreateMethod = this.addMethod({
name: 'getOrCreate',
Expand All @@ -235,9 +263,7 @@ export abstract class HandlerFrameworkClass extends ClassType {
});
getOrCreateMethod.addParameter({
name: 'props',
type: scope.coreInternal
? CORE_INTERNAL_CR_PROVIDER.CustomResourceProviderOptions
: CORE_MODULE.CustomResourceProviderOptions,
type: CORE_MODULE.CustomResourceProviderOptions,
optional: true,
});
getOrCreateMethod.addBody(
Expand All @@ -252,7 +278,7 @@ export abstract class HandlerFrameworkClass extends ClassType {
summary: 'Returns a stack-level singleton for the custom resource provider.',
},
});
getOrCreateProviderMethod.addParameter({
const _scope = getOrCreateProviderMethod.addParameter({
name: 'scope',
type: CONSTRUCTS_MODULE.Construct,
});
Expand All @@ -262,27 +288,26 @@ export abstract class HandlerFrameworkClass extends ClassType {
});
getOrCreateProviderMethod.addParameter({
name: 'props',
type: scope.coreInternal
? CORE_INTERNAL_CR_PROVIDER.CustomResourceProviderOptions
: CORE_MODULE.CustomResourceProviderOptions,
type: CORE_MODULE.CustomResourceProviderOptions,
optional: true,
});
getOrCreateProviderMethod.addBody(
stmt.constVar(expr.ident('id'), expr.directCode('`${uniqueid}CustomResourceProvider`')),
stmt.constVar(expr.ident('stack'), expr.directCode('Stack.of(scope)')),
stmt.constVar(expr.ident('stack'), $T(CORE_MODULE.Stack).of(expr.directCode(_scope.spec.name))),
stmt.constVar(expr.ident('existing'), expr.directCode(`stack.node.tryFindChild(id) as ${this.type}`)),
stmt.ret(expr.directCode(`existing ?? new ${this.name}(stack, id, props)`)),
);

const superProps = new ObjectLiteral([
new Splat(expr.ident('props')),
['codeDirectory', expr.directCode(`path.join(__dirname, '${props.codeDirectory}')`)],
['runtimeName', expr.lit(props.runtime)],
['codeDirectory', PATH_MODULE.join.call(expr.directCode(`__dirname, '${props.codeDirectory}'`))],
['runtimeName', this.buildRuntimeProperty(scope, {
runtime: props.runtime,
isCustomResourceProvider: true,
})],
]);
this.buildConstructor({
constructorPropsType: scope.coreInternal
? CORE_INTERNAL_CR_PROVIDER.CustomResourceProviderOptions
: CORE_MODULE.CustomResourceProviderOptions,
constructorPropsType: CORE_MODULE.CustomResourceProviderOptions,
superProps,
constructorVisbility: MemberVisibility.Private,
optionalConstructorProps: true,
Expand All @@ -291,54 +316,12 @@ export abstract class HandlerFrameworkClass extends ClassType {
})();
}

/**
* External modules that this class depends on.
*/
protected abstract readonly externalModules: ExternalModule[];

private importExternalModulesInto(scope: HandlerFrameworkModule) {
for (const module of this.externalModules) {
if (!scope.hasExternalModule(module)) {
scope.addExternalModule(module);
this.importExternalModuleInto(scope, module);
}
}
}

private importExternalModuleInto(scope: HandlerFrameworkModule, module: ExternalModule) {
switch (module.fqn) {
case PATH_MODULE.fqn: {
PATH_MODULE.import(scope, 'path');
return;
}
case CONSTRUCTS_MODULE.fqn: {
CONSTRUCTS_MODULE.importSelective(scope, ['Construct']);
return;
}
case CORE_MODULE.fqn: {
CORE_MODULE.importSelective(scope, [
'Stack',
'CustomResourceProviderBase',
'CustomResourceProviderOptions',
]);
return;
}
case CORE_INTERNAL_CR_PROVIDER.fqn: {
CORE_INTERNAL_CR_PROVIDER.importSelective(scope, [
'CustomResourceProviderBase',
'CustomResourceProviderOptions',
]);
return;
}
case CORE_INTERNAL_STACK.fqn: {
CORE_INTERNAL_STACK.importSelective(scope, ['Stack']);
return;
}
case LAMBDA_MODULE.fqn: {
LAMBDA_MODULE.import(scope, 'lambda');
return;
}
}
protected constructor(scope: HandlerFrameworkModule, spec: ClassSpec) {
super(scope, spec);
scope.registerImport(PATH_MODULE);
scope.registerImport(CONSTRUCTS_MODULE, {
targets: [CONSTRUCTS_MODULE.Construct],
});
}

private getOrCreateInterface(scope: HandlerFrameworkModule, spec: InterfaceSpec) {
Expand Down Expand Up @@ -373,4 +356,30 @@ export abstract class HandlerFrameworkClass extends ClassType {
const superInitializerArgs: Expression[] = [scope, id, props.superProps];
init.addBody(new SuperInitializer(...superInitializerArgs));
}

private buildRuntimeProperty(scope: HandlerFrameworkModule, options: BuildRuntimePropertyOptions = {}) {
const { runtime, isCustomResourceProvider, isEvalNodejsProvider } = options;

if (runtime) {
return isCustomResourceProvider ? expr.lit(runtime) : expr.directCode(toLambdaRuntime(runtime));
}

if (isCustomResourceProvider) {
scope.registerImport(CORE_MODULE, {
targets: [CORE_MODULE.determineLatestNodeRuntimeName],
fromLocation: scope.isCoreInternal
? '../../custom-resource-provider'
: '../../../core',
});
}

const _scope = expr.ident('scope');
const call = isCustomResourceProvider
? CORE_MODULE.determineLatestNodeRuntimeName.call(_scope)
: LAMBDA_MODULE.determineLatestNodeRuntime.call(_scope);

return isEvalNodejsProvider
? expr.cond(expr.directCode('props.runtime'), expr.directCode('props.runtime'), call)
: call;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,16 @@ export type HandlerFrameworkConfig = { [module: string]: { [identifier: string]:

export const config: HandlerFrameworkConfig = {
'aws-amplify-alpha': {
'asset-deployment-handler': [
'asset-deployment-provider': [
{
type: ComponentType.NO_OP,
type: ComponentType.FUNCTION,
sourceCode: path.resolve(__dirname, '..', 'aws-amplify-alpha', 'asset-deployment-handler', 'index.ts'),
handler: 'index.onEvent',
},
{
type: ComponentType.FUNCTION,
sourceCode: path.resolve(__dirname, '..', 'aws-amplify-alpha', 'asset-deployment-handler', 'index.ts'),
handler: 'index.isComplete',
},
],
},
Expand Down
Loading

0 comments on commit df9727c

Please sign in to comment.