1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { Flow } from "./Flow.ts";
5
6/**
7 * Shared scaffolding for AWS Elemental MediaConnect HTTP bindings.
8 *
9 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service
10 * is a `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the two
11 * builders below. Everything except the operation and the IAM action list
12 * is boilerplate: flow-scoped bindings inject the bound flow's ARN as the
13 * request's `FlowArn` and grant `actions` on that ARN; account-scoped
14 * bindings pass the request through and grant `actions` on `*`.
15 */
16
17/**
18 * Build the impl Effect for a MediaConnect operation scoped to a
19 * {@link Flow}: the deploy-time half grants `actions` on the bound flow's
20 * ARN (plus any `extraResources` — e.g. the entitlement wildcard for
21 * Grant/RevokeFlowEntitlement, whose IAM resource types are both the flow
22 * AND the entitlement, a sibling ARN not derived from the flow ARN), and
23 * the runtime half injects the flow's ARN into every request as `FlowArn`.
24 */
25export const makeMediaConnectFlowHttpBinding = <
26 I extends { FlowArn: string },
27 A,
28 E,
29 R,
30>(options: {
31 /** Fully-qualified binding tag, e.g. `AWS.MediaConnect.StartFlow`. */
32 tag: string;
33 /** The distilled operation; `FlowArn` is injected from the flow. */
34 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
35 /** IAM actions granted on the flow ARN + `extraResources`. */
36 actions: readonly string[];
37 /**
38 * Additional IAM resource ARNs granted alongside the flow ARN — for
39 * operations whose IAM resource types include more than the flow (e.g.
40 * entitlement ops act on `…:entitlement:*` ARNs too).
41 */
42 extraResources?: readonly string[];
43}) =>
44 Effect.gen(function* () {
45 const op = yield* options.operation;
46
47 return Effect.fn(function* (flow: Flow) {
48 const FlowArn = yield* flow.flowArn;
49 if (!globalThis.__ALCHEMY_RUNTIME__) {
50 const host = yield* Binding.Host;
51 if (isBindingHost(host)) {
52 yield* host.bind`Allow(${host}, ${options.tag}(${flow}))`({
53 policyStatements: [
54 {
55 Effect: "Allow",
56 Action: [...options.actions],
57 Resource: [flow.flowArn, ...(options.extraResources ?? [])],
58 },
59 ],
60 });
61 }
62 }
63 return Effect.fn(`${options.tag}(${flow.LogicalId})`)(function* (
64 request?: Omit<I, "FlowArn">,
65 ) {
66 const flowArn = yield* FlowArn;
67 return yield* op({ ...request, FlowArn: flowArn } as I);
68 });
69 });
70 });
71
72/**
73 * Build the impl Effect for an account-level MediaConnect operation (e.g.
74 * enumerating the account's flows or granted entitlements). The
75 * deploy-time half grants `actions` on `*` — these list operations are
76 * not scoped to a single flow resource.
77 */
78export const makeMediaConnectAccountHttpBinding = <I, A, E, R>(options: {
79 /** Fully-qualified binding tag, e.g. `AWS.MediaConnect.ListFlows`. */
80 tag: string;
81 /** The distilled operation, invoked with the caller's request as-is. */
82 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
83 /** IAM actions granted on `*`. */
84 actions: readonly string[];
85}) =>
86 Effect.gen(function* () {
87 const op = yield* options.operation;
88
89 return Effect.fn(function* () {
90 if (!globalThis.__ALCHEMY_RUNTIME__) {
91 const host = yield* Binding.Host;
92 if (isBindingHost(host)) {
93 yield* host.bind`Allow(${host}, ${options.tag}())`({
94 policyStatements: [
95 {
96 Effect: "Allow",
97 Action: [...options.actions],
98 Resource: ["*"],
99 },
100 ],
101 });
102 }
103 }
104 return Effect.fn(options.tag)(function* (request?: I) {
105 return yield* op((request ?? {}) as I);
106 });
107 });
108 });