From eedd370feeb334238f9fa55433e17bca354ba812 Mon Sep 17 00:00:00 2001 From: Sam Sussman Date: Wed, 1 Mar 2023 08:56:20 -0600 Subject: [PATCH 1/2] feat: support api gateway cors --- .../@eventual/aws-cdk/src/command-service.ts | 65 ++++++++++++++++--- .../aws-cdk/src/deep-composite-principal.ts | 1 - packages/@eventual/aws-cdk/src/service.ts | 3 + .../src/handlers/command-worker.ts | 5 ++ 4 files changed, 65 insertions(+), 9 deletions(-) diff --git a/packages/@eventual/aws-cdk/src/command-service.ts b/packages/@eventual/aws-cdk/src/command-service.ts index 0a5d4058e..75b597b0c 100644 --- a/packages/@eventual/aws-cdk/src/command-service.ts +++ b/packages/@eventual/aws-cdk/src/command-service.ts @@ -1,16 +1,14 @@ import { HttpApi, + HttpRouteProps, HttpMethod, - HttpRouteProps + CorsHttpMethod, } from "@aws-cdk/aws-apigatewayv2-alpha"; import { HttpIamAuthorizer } from "@aws-cdk/aws-apigatewayv2-authorizers-alpha"; import { HttpLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations-alpha"; import { ENV_NAMES, sanitizeFunctionName } from "@eventual/aws-runtime"; -import { - commandRpcPath, - isDefaultNamespaceCommand -} from "@eventual/core"; -import { Arn, aws_iam, Lazy, Stack } from "aws-cdk-lib"; +import { commandRpcPath, isDefaultNamespaceCommand } from "@eventual/core"; +import { Arn, aws_iam, Duration, Lazy, Stack } from "aws-cdk-lib"; import { Effect, IGrantable, PolicyStatement } from "aws-cdk-lib/aws-iam"; import type { Function, FunctionProps } from "aws-cdk-lib/aws-lambda"; import { Construct } from "constructs"; @@ -20,7 +18,7 @@ import type { CommandFunction, InternalCommandFunction, InternalCommandName, - InternalCommands + InternalCommands, } from "./build-manifest"; import type { EventService } from "./event-service"; import { grant } from "./grant"; @@ -37,11 +35,47 @@ export type CommandProps = { default?: CommandHandlerProps; } & Partial>; +export interface CorsOptions { + /** + * Specifies whether credentials are included in the CORS request. + * @default false + */ + readonly allowCredentials?: boolean; + /** + * Represents a collection of allowed headers. + * @default - No Headers are allowed. + */ + readonly allowHeaders?: string[]; + /** + * Represents a collection of allowed HTTP methods. + * OPTIONS will be added automatically. + * + * @default - OPTIONS + */ + readonly allowMethods?: CorsHttpMethod[]; + /** + * Represents a collection of allowed origins. + * @default - No Origins are allowed. + */ + readonly allowOrigins?: string[]; + /** + * Represents a collection of exposed headers. + * @default - No Expose Headers are allowed. + */ + readonly exposeHeaders?: string[]; + /** + * The duration that the browser should cache preflight request results. + * @default Duration.seconds(0) + */ + readonly maxAge?: Duration; +} + export interface CommandsProps extends ServiceConstructProps { activityService: ActivityService; overrides?: CommandProps; eventService: EventService; workflowService: WorkflowService; + cors?: CorsOptions; } /** @@ -168,6 +202,17 @@ export class CommandService { "default", this.serviceCommands.default ), + corsPreflight: props.cors + ? { + ...props.cors, + allowMethods: Array.from( + new Set([ + ...(props.cors.allowMethods ?? []), + CorsHttpMethod.OPTIONS, + ]) + ), + } + : undefined, }); this.finalize(); @@ -240,7 +285,11 @@ export class CommandService { } if (command.path) { self.gateway.addRoutes({ - path: command.path, + // itty router supports paths in the form /*, but api gateway expects them in the form /{proxy+} + path: + command.path === "*" + ? "/{proxy+}" + : (command.path as string).replace(/\*/g, "{proxy+}"), methods: [ (command.method as HttpMethod | undefined) ?? HttpMethod.GET, ], diff --git a/packages/@eventual/aws-cdk/src/deep-composite-principal.ts b/packages/@eventual/aws-cdk/src/deep-composite-principal.ts index b4626e313..d1be36454 100644 --- a/packages/@eventual/aws-cdk/src/deep-composite-principal.ts +++ b/packages/@eventual/aws-cdk/src/deep-composite-principal.ts @@ -42,7 +42,6 @@ export class DeepCompositePrincipal extends CompositePrincipal { override addToPrincipalPolicy( statement: PolicyStatement ): AddToPrincipalPolicyResult { - console.log(statement); const res = this._principals.map((p) => p.addToPrincipalPolicy(statement)); const added = res.every((s) => s.statementAdded); if (added) { diff --git a/packages/@eventual/aws-cdk/src/service.ts b/packages/@eventual/aws-cdk/src/service.ts index d973fb81c..ecbc1331d 100644 --- a/packages/@eventual/aws-cdk/src/service.ts +++ b/packages/@eventual/aws-cdk/src/service.ts @@ -36,6 +36,7 @@ import { CommandService, Commands, SystemCommands, + CorsOptions, } from "./command-service"; import { DeepCompositePrincipal } from "./deep-composite-principal.js"; import { EventService } from "./event-service"; @@ -94,6 +95,7 @@ export interface ServiceProps { * Override properties of Subscription Functions within the Service. */ subscriptions?: SubscriptionOverrides; + cors?: CorsOptions; system?: { /** * Configuration properties for the workflow orchestrator @@ -239,6 +241,7 @@ export class Service extends Construct { overrides: props.commands, eventService: this.eventService, workflowService: workflowService, + cors: props.cors, ...serviceConstructProps, }); proxyCommandService._bind(this.commandService); diff --git a/packages/@eventual/core-runtime/src/handlers/command-worker.ts b/packages/@eventual/core-runtime/src/handlers/command-worker.ts index 400bd5efa..33eb40d40 100644 --- a/packages/@eventual/core-runtime/src/handlers/command-worker.ts +++ b/packages/@eventual/core-runtime/src/handlers/command-worker.ts @@ -50,6 +50,11 @@ export function createCommandWorker({ try { const response = await router.handle(request); if (response === undefined) { + if (request.method === "OPTIONS") { + return new HttpResponse(undefined, { + status: 204, + }); + } return new HttpResponse( `Not Found: ${request.method} ${request.url}`, { From 8620b4db78ec0e993a41e2fb49032a248e1824d6 Mon Sep 17 00:00:00 2001 From: Sam Sussman Date: Wed, 1 Mar 2023 14:29:24 -0600 Subject: [PATCH 2/2] feedback --- packages/@eventual/core-runtime/src/handlers/command-worker.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/@eventual/core-runtime/src/handlers/command-worker.ts b/packages/@eventual/core-runtime/src/handlers/command-worker.ts index 33eb40d40..da472ef56 100644 --- a/packages/@eventual/core-runtime/src/handlers/command-worker.ts +++ b/packages/@eventual/core-runtime/src/handlers/command-worker.ts @@ -52,6 +52,8 @@ export function createCommandWorker({ if (response === undefined) { if (request.method === "OPTIONS") { return new HttpResponse(undefined, { + // CORS expects a 204 or 200, using 204 to match API Gateway + // and accurately reflect NO CONTENT status: 204, }); }