Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/aws/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,6 @@
},
"dependencies": {
"@notation/aws.iac": "workspace:*",
"@notation/core": "workspace:*",
"@notation/resource": "workspace:*",
"@notation/std.iac": "workspace:*",
"@types/aws-lambda": "^8.10.147",
Expand Down
8 changes: 2 additions & 6 deletions packages/aws/src/api-gateway/api.ts
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
import * as aws from "@notation/aws.iac";
import type { ResourceCollector } from "@notation/core";

export const api = (collector: ResourceCollector, rgConfig: { name: string }) => {
const apiGroup = new aws.AwsResourceGroup("API Gateway", {
...rgConfig,
collector,
});
export const api = (rgConfig: { name: string }) => {
const apiGroup = new aws.AwsResourceGroup("API Gateway", rgConfig);

const apiResource = apiGroup.add(
new aws.apiGateway.Api({
Expand Down
10 changes: 2 additions & 8 deletions packages/aws/src/api-gateway/route.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,13 +3,11 @@ import type {
JWTAuthorizedApiGatewayHandler,
} from "src/shared";
import * as aws from "@notation/aws.iac";
import type { ResourceCollector } from "@notation/core";
import { api } from "./api";
import { AuthorizerConfig } from "./auth";
import { mapAuthConfig, mapAuthType } from "./utils";

export const route = (
collector: ResourceCollector,
apiGroup: ReturnType<typeof api>,
method: string, // todo: http methods only
path: `/${string}`,
Expand All@@ -23,11 +21,8 @@ export const route = (
const apiResource = apiGroup.findResource(aws.apiGateway.Api)!;
const routeId = `${apiResource.id}-${method}-${path}`;

const lambdaGroup =
handler instanceof aws.AwsResourceGroup
? handler
: // at compile time, runtime module becomes infra resource group
(handler as any as aws.AwsResourceGroup);
// at compile time, a runtime handler import becomes an infra resource group
const lambdaGroup = handler as aws.AwsResourceGroup;

const lambdaResource = lambdaGroup.findResource(aws.lambda.LambdaFunction)!;

Expand DownExpand Up@@ -63,7 +58,6 @@ export const route = (

const routeGroup = new aws.AwsResourceGroup("API Gateway/Route", {
dependencies: { router: apiGroup.id, fn: lambdaGroup.id },
collector,
});

if (auth.type != "NONE") {
Expand Down
16 changes: 5 additions & 11 deletions packages/aws/src/api-gateway/router.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,16 +6,12 @@ import * as aws from "@notation/aws.iac";
import { route } from "./route";
import { api } from "./api";
import { AuthorizerConfig, JWTAuthorizerConfig, NO_AUTH } from "./auth";
import type { ResourceCollector } from "@notation/core";

export const router = (
collector: ResourceCollector,
apiGroup: ReturnType<typeof api>,
) => {
export const router = (apiGroup: ReturnType<typeof api>) => {
const createRouteCallback =
(method: string) =>
(path: `/${string}`, handler: ApiGatewayHandler | aws.AwsResourceGroup) => {
return route(collector, apiGroup, method, path, NO_AUTH, handler);
return route(apiGroup, method, path, NO_AUTH, handler);
};

return {
Expand All@@ -25,7 +21,7 @@ export const router = (
patch: createRouteCallback("PATCH"),
delete: createRouteCallback("DELETE"),
withJWTAuthorizer: <ClaimsType>(auth: JWTAuthorizerConfig) => {
const authorizer = new AuthorizedRouteBuilder(collector, apiGroup);
const authorizer = new AuthorizedRouteBuilder(apiGroup);
return authorizer.withJWTAuthorizer<ClaimsType>(auth);
},
};
Expand All@@ -34,10 +30,8 @@ export const router = (
class AuthorizedRouteBuilder {
auth: AuthorizerConfig = NO_AUTH;
apiGroup: ReturnType<typeof api>;
collector: ResourceCollector;

constructor(collector: ResourceCollector, apiGroup: ReturnType<typeof api>) {
this.collector = collector;
constructor(apiGroup: ReturnType<typeof api>) {
this.apiGroup = apiGroup;
}

Expand All@@ -47,7 +41,7 @@ class AuthorizedRouteBuilder {
path: `/${string}`,
handler: JWTAuthorizedApiGatewayHandler<ClaimsType>,
) => {
return route(this.collector, this.apiGroup, method, path, authorizer, handler);
return route(this.apiGroup, method, path, authorizer, handler);
};

withJWTAuthorizer<ClaimsType>(authorizer: JWTAuthorizerConfig) {
Expand Down
32 changes: 11 additions & 21 deletions packages/aws/src/event-bridge/event-bridge-schedule.ts
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,23 @@
import type { ResourceCollector } from "@notation/core";
import { EventBridgeHandler } from "src/shared/lambda.handler";
import { Schedule } from "./schedule";
import * as aws from "@notation/aws.iac";
import { toAwsScheduleExpression } from "./aws-conversions";

export const schedule = (
collector: ResourceCollector,
config: {
name: string;
schedule: Schedule;
handler:
| EventBridgeHandler<"Scheduled Event", any>
// todo: narrow to lambda group
| aws.AwsResourceGroup;
},
): aws.AwsResourceGroup => {
export const schedule = (config: {
name: string;
schedule: Schedule;
handler:
| EventBridgeHandler<"Scheduled Event", any>
// todo: narrow to lambda group
| aws.AwsResourceGroup;
}): aws.AwsResourceGroup => {
const eventBridgeScheduleGroup = new aws.AwsResourceGroup(
"aws/eventBridge/schedule",
{
...config,
collector,
},
config,
);

const lambdaGroup =
config.handler instanceof aws.AwsResourceGroup
? config.handler
: // at compile time, runtime module becomes infra resource group
(config.handler as any as aws.AwsResourceGroup);
// at compile time, a runtime handler import becomes an infra resource group
const lambdaGroup = config.handler as aws.AwsResourceGroup;

const lambdaResource = lambdaGroup.findResource(aws.lambda.LambdaFunction)!;

Expand Down
11 changes: 2 additions & 9 deletions packages/aws/src/lambda/lambda.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,6 @@ import * as aws from "@notation/aws.iac";
import * as std from "@notation/std.iac";
import crypto from "crypto";
import path from "path";
import type { ResourceCollector } from "@notation/core";

type LambdaConfig = {
id?: string;
Expand All@@ -14,14 +13,8 @@ type LambdaConfig = {
runtime?: aws.lambda.LambdaFunctionConfig["Runtime"];
};

export const lambda = (
collector: ResourceCollector,
config: LambdaConfig,
): aws.AwsResourceGroup => {
const functionGroup = new aws.AwsResourceGroup("Lambda", {
config,
collector,
});
export const lambda = (config: LambdaConfig): aws.AwsResourceGroup => {
const functionGroup = new aws.AwsResourceGroup("Lambda", { config });
const filePath = config.code.path;

let lambdaId = config.id;
Expand Down
37 changes: 8 additions & 29 deletions packages/aws/test/api-gateway.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,31 @@
import { test, expect, beforeEach } from "vitest";
import { ResourceCollector } from "@notation/core";
import { test, expect } from "vitest";
import { apiGateway } from "@notation/aws.iac";
import { NO_AUTH, api, router } from "src/api-gateway";
import { route } from "src/api-gateway/route";
import { lambda } from "src/lambda";

let collector: ResourceCollector;

beforeEach(() => {
collector = new ResourceCollector();
});

test("route resource group idempotency snapshot", () => {
const apiResourceGroup = api(collector, { name: "api" });
const fnResourceGroup = lambda(collector, {
const apiResourceGroup = api({ name: "api" });
const fnResourceGroup = lambda({
code: {
type: "file",
path: "src/fns/handler.fn.js",
},
handler: "handler.fn.js",
});

route(
collector,
apiResourceGroup,
"GET",
"/hello",
NO_AUTH,
fnResourceGroup as any,
);
route(apiResourceGroup, "GET", "/hello", NO_AUTH, fnResourceGroup as any);
const fnResourceGroupSnapshot = JSON.stringify(fnResourceGroup);
route(
collector,
apiResourceGroup,
"POST",
"/hello",
NO_AUTH,
fnResourceGroup as any,
);
route(apiResourceGroup, "POST", "/hello", NO_AUTH, fnResourceGroup as any);
const fnResourceGroupSnapshot2 = JSON.stringify(fnResourceGroup);

expect(fnResourceGroupSnapshot).toEqual(fnResourceGroupSnapshot2);
});

test("router provides methods for each HTTP verb", () => {
const apiResourceGroup = api(collector, { name: "api" });
const apiRouter = router(collector, apiResourceGroup);
const handler = lambda(collector, {
const apiResourceGroup = api({ name: "api" });
const apiRouter = router(apiResourceGroup);
const handler = lambda({
code: {
type: "file",
path: "src/fns/handler.fn.js",
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,4 +4,3 @@ export * from "./utils/paths";
export * from "./visualiser/chart";
export * from "./orchestrator/resource";
export * from "./orchestrator/resource-group";
export * from "./orchestrator/resource-collector";
37 changes: 16 additions & 21 deletions packages/core/src/orchestrator/graph.ts
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,23 @@
import path from "path";
import {
collectResourceGraph,
type BaseResource,
type ResourceGroup,
} from "@notation/resource";
import { filePaths } from "src/utils/paths";
import { ResourceCollector } from "./resource-collector";

export async function getResourceGraph(entryPoint: string) {
const collector = new ResourceCollector();
type ResourceGraph = {
resourceGroups: ResourceGroup[];
resources: BaseResource[];
};

export async function getResourceGraph(
entryPoint: string,
): Promise<ResourceGraph> {
const outFilePath = filePaths.dist.infra(entryPoint);

// todo: move into worker thread. this will cause memory leaks
const mod = await import(path.join(process.cwd(), `${outFilePath}?${Date.now()}`));

const register = (mod as any).register ?? (mod as any).default;

if (typeof register !== "function") {
throw new Error(
`Infra entrypoint must export register(collector) (or a default function). Received exports: ${Object.keys(mod).join(
", ",
)}`,
);
}

await register(collector);

return {
resourceGroups: collector.getResourceGroups(),
resources: collector.getResources(),
};
return collectResourceGraph(async () => {
await import(path.join(process.cwd(), `${outFilePath}?${Date.now()}`));
});
}
35 changes: 0 additions & 35 deletions packages/core/src/orchestrator/resource-collector.ts

This file was deleted.

Loading
Loading