1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import * as Output from "../../Output.ts";
4import { isBindingHost } from "../Lambda/Function.ts";
5import type { Workflow } from "./Workflow.ts";
6
7/**
8 * Shared scaffolding for the Amazon MWAA Serverless HTTP bindings.
9 *
10 * Every MWAA Serverless data-plane operation is addressed by a
11 * `WorkflowArn`, so every binding is scoped to a bound {@link Workflow}:
12 * the deploy-time half grants `actions` on the workflow's ARN (plus the
13 * `{workflowArn}/*` wildcard covering run- and task-scoped sub-resources),
14 * and the runtime half injects the workflow's ARN into every request.
15 *
16 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
17 * a `Layer.effect(Cap, makeMwaaServerlessHttpBinding({ … }))`.
18 */
19export const makeMwaaServerlessHttpBinding = <
20 I extends { WorkflowArn: string },
21 A,
22 E,
23 R,
24>(options: {
25 /** Fully-qualified binding tag, e.g. `AWS.MWAAServerless.StartWorkflowRun`. */
26 tag: string;
27 /** The distilled operation. */
28 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
29 /** IAM actions granted on the workflow ARN (+ `{arn}/*`). */
30 actions: readonly string[];
31}) =>
32 Effect.gen(function* () {
33 const op = yield* options.operation;
34
35 return Effect.fn(function* (workflow: Workflow) {
36 // Outputs yield a DEFERRED effect — resolve again per invocation below.
37 const WorkflowArn = yield* workflow.workflowArn;
38 if (!globalThis.__ALCHEMY_RUNTIME__) {
39 const host = yield* Binding.Host;
40 if (isBindingHost(host)) {
41 yield* host.bind`Allow(${host}, ${options.tag}(${workflow}))`({
42 policyStatements: [
43 {
44 Effect: "Allow",
45 Action: [...options.actions],
46 Resource: [
47 workflow.workflowArn,
48 // run/task-scoped operations authorize against
49 // sub-resources below the workflow ARN
50 Output.interpolate`${workflow.workflowArn}/*`,
51 ],
52 },
53 ],
54 });
55 }
56 }
57 return Effect.fn(`${options.tag}(${workflow.LogicalId})`)(function* (
58 request?: Omit<I, "WorkflowArn">,
59 ) {
60 return yield* op({
61 ...request,
62 WorkflowArn: yield* WorkflowArn,
63 } as I);
64 });
65 });
66 });