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 { DBCluster } from "./DBCluster.ts";
6import type { DBInstance } from "./DBInstance.ts";
7
8/**
9 * Shared scaffolding for Amazon RDS HTTP bindings.
10 *
11 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
12 * thin `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the builders
13 * below. Everything except the operation, the identifier resolver, and the
14 * IAM action list is boilerplate.
15 */
16
17/**
18 * Build the impl Effect for an account-level operation (cluster/instance
19 * discovery, event history, snapshot administration). The deploy-time half
20 * grants `actions` on `*` — these operations span every database in the
21 * account and the identifiers they filter on are runtime data.
22 */
23export const makeRdsAccountHttpBinding = <I, A, E, R>(options: {
24 /** Fully-qualified binding tag, e.g. `AWS.RDS.DescribeEvents`. */
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 cluster-scoped operation: the runtime callable
57 * injects the bound {@link DBCluster}'s identifier as `DBClusterIdentifier`
58 * and the deploy-time half grants `actions` on the cluster ARN (plus any
59 * `extraResources`, e.g. the `cluster-snapshot` ARN pattern for snapshot
60 * creation).
61 */
62export const makeRdsClusterHttpBinding = <
63 I extends { DBClusterIdentifier?: string },
64 A,
65 E,
66 R,
67>(options: {
68 /** Fully-qualified binding tag, e.g. `AWS.RDS.FailoverDBCluster`. */
69 tag: string;
70 /** The distilled operation; `DBClusterIdentifier` is injected. */
71 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
72 /** IAM actions granted on the cluster ARN. */
73 actions: readonly string[];
74 /** Additional IAM resource ARNs derived from the cluster ARN. */
75 extraResources?: (clusterArn: string) => string[];
76}) =>
77 Effect.gen(function* () {
78 const op = yield* options.operation;
79
80 return Effect.fn(function* (cluster: DBCluster) {
81 const Identifier = yield* cluster.dbClusterIdentifier;
82 if (!globalThis.__ALCHEMY_RUNTIME__) {
83 const host = yield* Binding.Host;
84 if (isBindingHost(host)) {
85 const resources = options.extraResources;
86 yield* host.bind`Allow(${host}, ${options.tag}(${cluster}))`({
87 policyStatements: [
88 {
89 Effect: "Allow",
90 Action: [...options.actions],
91 Resource: resources
92 ? Output.map(cluster.dbClusterArn, (arn) => [
93 arn,
94 ...resources(arn),
95 ])
96 : [Output.interpolate`${cluster.dbClusterArn}`],
97 },
98 ],
99 });
100 }
101 }
102 return Effect.fn(`${options.tag}(${cluster.LogicalId})`)(function* (
103 request?: Omit<I, "DBClusterIdentifier">,
104 ) {
105 return yield* op({
106 ...request,
107 DBClusterIdentifier: yield* Identifier,
108 } as I);
109 });
110 });
111 });
112
113/**
114 * Build the impl Effect for an instance-scoped operation: the runtime
115 * callable injects the bound {@link DBInstance}'s identifier as
116 * `DBInstanceIdentifier` and the deploy-time half grants `actions` on the
117 * instance ARN (plus any `extraResources`, e.g. the `snapshot` ARN pattern
118 * for snapshot creation).
119 */
120export const makeRdsInstanceHttpBinding = <
121 I extends { DBInstanceIdentifier?: string },
122 A,
123 E,
124 R,
125>(options: {
126 /** Fully-qualified binding tag, e.g. `AWS.RDS.RebootDBInstance`. */
127 tag: string;
128 /** The distilled operation; `DBInstanceIdentifier` is injected. */
129 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
130 /** IAM actions granted on the instance ARN. */
131 actions: readonly string[];
132 /** Additional IAM resource ARNs derived from the instance ARN. */
133 extraResources?: (instanceArn: string) => string[];
134}) =>
135 Effect.gen(function* () {
136 const op = yield* options.operation;
137
138 return Effect.fn(function* (instance: DBInstance) {
139 const Identifier = yield* instance.dbInstanceIdentifier;
140 if (!globalThis.__ALCHEMY_RUNTIME__) {
141 const host = yield* Binding.Host;
142 if (isBindingHost(host)) {
143 const resources = options.extraResources;
144 yield* host.bind`Allow(${host}, ${options.tag}(${instance}))`({
145 policyStatements: [
146 {
147 Effect: "Allow",
148 Action: [...options.actions],
149 Resource: resources
150 ? Output.map(instance.dbInstanceArn, (arn) => [
151 arn,
152 ...resources(arn),
153 ])
154 : [Output.interpolate`${instance.dbInstanceArn}`],
155 },
156 ],
157 });
158 }
159 }
160 return Effect.fn(`${options.tag}(${instance.LogicalId})`)(function* (
161 request?: Omit<I, "DBInstanceIdentifier">,
162 ) {
163 return yield* op({
164 ...request,
165 DBInstanceIdentifier: yield* Identifier,
166 } as I);
167 });
168 });
169 });