1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import * as Output from "../../Output.ts";
4import { AWSEnvironment } from "../Environment.ts";
5import { isBindingHost } from "../Lambda/Function.ts";
6import type { Directory } from "./Directory.ts";
7
8/**
9 * Shared scaffolding for AWS Directory Service HTTP bindings.
10 *
11 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
12 * a `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the two builders
13 * below. Everything except the operation and the IAM action list is
14 * boilerplate.
15 */
16
17/**
18 * Build the impl Effect for an account-level Directory Service operation
19 * (enumerating directories, reading account limits). The deploy-time half
20 * grants `actions` on `*` — these actions do not support resource-level
21 * permissions.
22 */
23export const makeDirectoryServiceAccountHttpBinding = <I, A, E, R>(options: {
24 /** Fully-qualified binding tag, e.g. `AWS.DirectoryService.GetDirectoryLimits`. */
25 tag: string;
26 /** The distilled operation, invoked with the caller's request as-is. */
27 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
28 /** IAM actions granted on `*`. */
29 actions: readonly string[];
30}) =>
31 Effect.gen(function* () {
32 const op = yield* options.operation;
33
34 return Effect.fn(function* () {
35 if (!globalThis.__ALCHEMY_RUNTIME__) {
36 const host = yield* Binding.Host;
37 if (isBindingHost(host)) {
38 yield* host.bind`Allow(${host}, ${options.tag}())`({
39 policyStatements: [
40 {
41 Effect: "Allow",
42 Action: [...options.actions],
43 Resource: ["*"],
44 },
45 ],
46 });
47 }
48 }
49 return Effect.fn(options.tag)(function* (request?: I) {
50 return yield* op((request ?? {}) as I);
51 });
52 });
53 });
54
55/**
56 * Build the impl Effect for a Directory Service operation scoped to one
57 * {@link Directory}: the deploy-time half grants `actions` on the bound
58 * directory's ARN (`arn:aws:ds:{region}:{account}:directory/{id}`), and the
59 * runtime half injects the directory's `DirectoryId` into every request.
60 */
61export const makeDirectoryHttpBinding = <
62 I extends { DirectoryId?: string },
63 A,
64 E,
65 R,
66>(options: {
67 /** Fully-qualified binding tag, e.g. `AWS.DirectoryService.CreateSnapshot`. */
68 tag: string;
69 /** The distilled operation; `DirectoryId` is injected from the directory. */
70 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
71 /** IAM actions granted on the directory ARN. */
72 actions: readonly string[];
73}) =>
74 Effect.gen(function* () {
75 const op = yield* options.operation;
76
77 return Effect.fn(function* (directory: Directory) {
78 const DirectoryId = yield* directory.directoryId;
79 if (!globalThis.__ALCHEMY_RUNTIME__) {
80 const host = yield* Binding.Host;
81 if (isBindingHost(host)) {
82 const { accountId, region } =
83 yield* AWSEnvironment.current as unknown as Effect.Effect<{
84 accountId: string;
85 region: string;
86 }>;
87 yield* host.bind`Allow(${host}, ${options.tag}(${directory}))`({
88 policyStatements: [
89 {
90 Effect: "Allow",
91 Action: [...options.actions],
92 Resource: [
93 Output.interpolate`arn:aws:ds:${region}:${accountId}:directory/${directory.directoryId}`,
94 ],
95 },
96 ],
97 });
98 }
99 }
100 return Effect.fn(`${options.tag}(${directory.LogicalId})`)(function* (
101 request?: Omit<I, "DirectoryId">,
102 ) {
103 return yield* op({
104 ...request,
105 DirectoryId: yield* DirectoryId,
106 } as I);
107 });
108 });
109 });