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 { FileSystem } from "./FileSystem.ts";
6
7/**
8 * Shared scaffolding for the EFS runtime bindings.
9 *
10 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
11 * thin `Layer.effect(Cap, makeEfs…HttpBinding({ … }))` over one of the two
12 * builders below. Everything except the operation, the IAM action list, and
13 * (for file-system-scoped operations) the injected `FileSystemId` is
14 * boilerplate.
15 */
16
17/**
18 * Build the impl Effect for a file-system-scoped EFS operation: the runtime
19 * callable injects the bound {@link FileSystem}'s ID as `FileSystemId` and
20 * the deploy-time half grants `actions` on the file system's ARN.
21 */
22export const makeEfsFileSystemHttpBinding = <
23 I extends { FileSystemId?: string },
24 A,
25 E,
26 R,
27>(options: {
28 /** Fully-qualified binding tag, e.g. `AWS.EFS.DescribeMountTargets`. */
29 tag: string;
30 /** The distilled operation; `FileSystemId` is injected from the file system. */
31 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
32 /** IAM actions granted on the file system ARN. */
33 actions: readonly string[];
34 /**
35 * Additional IAM actions granted on `*`. Used for tag-on-create
36 * (`elasticfilesystem:TagResource` authorizes against the ARN of the
37 * resource being created, which is unknowable at deploy time).
38 */
39 wildcardActions?: readonly string[];
40}) =>
41 Effect.gen(function* () {
42 const op = yield* options.operation;
43
44 return Effect.fn(function* (fileSystem: FileSystem) {
45 const FileSystemId = yield* fileSystem.fileSystemId;
46 if (!globalThis.__ALCHEMY_RUNTIME__) {
47 const host = yield* Binding.Host;
48 if (isBindingHost(host)) {
49 yield* host.bind`Allow(${host}, ${options.tag}(${fileSystem}))`({
50 policyStatements: [
51 {
52 Effect: "Allow",
53 Action: [...options.actions],
54 Resource: [Output.interpolate`${fileSystem.fileSystemArn}`],
55 },
56 ...(options.wildcardActions !== undefined &&
57 options.wildcardActions.length > 0
58 ? [
59 {
60 Effect: "Allow" as const,
61 Action: [...options.wildcardActions],
62 Resource: ["*"],
63 },
64 ]
65 : []),
66 ],
67 });
68 }
69 }
70 return Effect.fn(`${options.tag}(${fileSystem.LogicalId})`)(function* (
71 request?: Omit<I, "FileSystemId">,
72 ) {
73 return yield* op({
74 ...request,
75 FileSystemId: yield* FileSystemId,
76 } as I);
77 });
78 });
79 });
80
81/**
82 * Build the impl Effect for an account-level EFS operation (no file-system
83 * argument): the deploy-time half grants `actions` on `*`. Used for
84 * operations that authorize against ARNs unknowable at deploy time (e.g.
85 * `DeleteAccessPoint` authorizes on the access point's own ARN, and access
86 * points deleted at runtime are typically created at runtime too).
87 */
88export const makeEfsAccountHttpBinding = <I, A, E, R>(options: {
89 /** Fully-qualified binding tag, e.g. `AWS.EFS.DeleteAccessPoint`. */
90 tag: string;
91 /** The distilled operation, invoked with the caller's request as-is. */
92 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
93 /** IAM actions granted on `*`. */
94 actions: readonly string[];
95}) =>
96 Effect.gen(function* () {
97 const op = yield* options.operation;
98
99 return Effect.fn(function* () {
100 if (!globalThis.__ALCHEMY_RUNTIME__) {
101 const host = yield* Binding.Host;
102 if (isBindingHost(host)) {
103 yield* host.bind`Allow(${host}, ${options.tag}())`({
104 policyStatements: [
105 {
106 Effect: "Allow",
107 Action: [...options.actions],
108 Resource: ["*"],
109 },
110 ],
111 });
112 }
113 }
114 return Effect.fn(options.tag)(function* (request?: I) {
115 return yield* op((request ?? {}) as I);
116 });
117 });
118 });