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 Neptune 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 * Neptune shares the RDS control plane, so its management IAM actions live
17 * under the `rds:` service prefix (`rds:FailoverDBCluster`,
18 * `rds:DescribeDBClusters`, …) and its ARNs use the `rds` service segment.
19 */
20
21/**
22 * Build the impl Effect for an account-level operation (cluster discovery,
23 * event history, snapshot administration). The deploy-time half grants
24 * `actions` on `*` — these operations span every cluster in the account and
25 * the identifiers they filter on are runtime data.
26 */
27export const makeNeptuneAccountHttpBinding = <I, A, E, R>(options: {
28 /** Fully-qualified binding tag, e.g. `AWS.Neptune.DescribeEvents`. */
29 tag: string;
30 /** The distilled operation, invoked with the caller's request as-is. */
31 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
32 /** IAM actions granted on `*`. */
33 actions: readonly string[];
34}) =>
35 Effect.gen(function* () {
36 const op = yield* options.operation;
37
38 return Effect.fn(function* () {
39 if (!globalThis.__ALCHEMY_RUNTIME__) {
40 const host = yield* Binding.Host;
41 if (isBindingHost(host)) {
42 yield* host.bind`Allow(${host}, ${options.tag}())`({
43 policyStatements: [
44 {
45 Effect: "Allow",
46 Action: [...options.actions],
47 Resource: ["*"],
48 },
49 ],
50 });
51 }
52 }
53 return Effect.fn(options.tag)(function* (request?: I) {
54 return yield* op((request ?? {}) as I);
55 });
56 });
57 });
58
59/**
60 * Build the impl Effect for a cluster-scoped operation: the runtime callable
61 * injects the bound {@link DBCluster}'s identifier as `DBClusterIdentifier`
62 * and the deploy-time half grants `actions` on the cluster ARN (plus any
63 * `extraResources`, e.g. the `cluster-snapshot` ARN pattern for snapshot
64 * creation).
65 */
66export const makeNeptuneClusterHttpBinding = <
67 I extends { DBClusterIdentifier?: string },
68 A,
69 E,
70 R,
71>(options: {
72 /** Fully-qualified binding tag, e.g. `AWS.Neptune.FailoverDBCluster`. */
73 tag: string;
74 /** The distilled operation; `DBClusterIdentifier` is injected. */
75 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
76 /** IAM actions granted on the cluster ARN. */
77 actions: readonly string[];
78 /** Additional IAM resource ARNs derived from the cluster ARN. */
79 extraResources?: (clusterArn: string) => string[];
80}) =>
81 Effect.gen(function* () {
82 const op = yield* options.operation;
83
84 return Effect.fn(function* (cluster: DBCluster) {
85 const Identifier = yield* cluster.dbClusterIdentifier;
86 if (!globalThis.__ALCHEMY_RUNTIME__) {
87 const host = yield* Binding.Host;
88 if (isBindingHost(host)) {
89 const resources = options.extraResources;
90 yield* host.bind`Allow(${host}, ${options.tag}(${cluster}))`({
91 policyStatements: [
92 {
93 Effect: "Allow",
94 Action: [...options.actions],
95 Resource: resources
96 ? Output.map(cluster.dbClusterArn, (arn) => [
97 arn,
98 ...resources(arn),
99 ])
100 : [Output.interpolate`${cluster.dbClusterArn}`],
101 },
102 ],
103 });
104 }
105 }
106 return Effect.fn(`${options.tag}(${cluster.LogicalId})`)(function* (
107 request?: Omit<I, "DBClusterIdentifier">,
108 ) {
109 return yield* op({
110 ...request,
111 DBClusterIdentifier: yield* Identifier,
112 } as I);
113 });
114 });
115 });
116
117/**
118 * Build the impl Effect for an instance-scoped operation: the runtime
119 * callable injects the bound {@link DBInstance}'s identifier as
120 * `DBInstanceIdentifier` and the deploy-time half grants `actions` on the
121 * instance ARN.
122 */
123export const makeNeptuneInstanceHttpBinding = <
124 I extends { DBInstanceIdentifier?: string },
125 A,
126 E,
127 R,
128>(options: {
129 /** Fully-qualified binding tag, e.g. `AWS.Neptune.RebootDBInstance`. */
130 tag: string;
131 /** The distilled operation; `DBInstanceIdentifier` is injected. */
132 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
133 /** IAM actions granted on the instance ARN. */
134 actions: readonly string[];
135}) =>
136 Effect.gen(function* () {
137 const op = yield* options.operation;
138
139 return Effect.fn(function* (instance: DBInstance) {
140 const Identifier = yield* instance.dbInstanceIdentifier;
141 if (!globalThis.__ALCHEMY_RUNTIME__) {
142 const host = yield* Binding.Host;
143 if (isBindingHost(host)) {
144 yield* host.bind`Allow(${host}, ${options.tag}(${instance}))`({
145 policyStatements: [
146 {
147 Effect: "Allow",
148 Action: [...options.actions],
149 Resource: [Output.interpolate`${instance.dbInstanceArn}`],
150 },
151 ],
152 });
153 }
154 }
155 return Effect.fn(`${options.tag}(${instance.LogicalId})`)(function* (
156 request?: Omit<I, "DBInstanceIdentifier">,
157 ) {
158 return yield* op({
159 ...request,
160 DBInstanceIdentifier: yield* Identifier,
161 } as I);
162 });
163 });
164 });