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 { Blueprint } from "./Blueprint.ts";
6import type { DataAutomationLibrary } from "./DataAutomationLibrary.ts";
7import type { DataAutomationProject } from "./DataAutomationProject.ts";
8
9/**
10 * Shared scaffolding for Bedrock Data Automation HTTP bindings.
11 *
12 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
13 * thin `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the two
14 * builders below. Everything except the operation and the IAM action list is
15 * boilerplate.
16 */
17
18/**
19 * Build the impl Effect for a project-scoped runtime operation (the sync and
20 * async invoke APIs). The runtime callable injects the bound
21 * {@link DataAutomationProject}'s ARN + stage as `dataAutomationConfiguration`
22 * and the deploy-time half grants `actions` on the project ARN plus the
23 * account's data automation profiles in every region — cross-region
24 * inference profiles (e.g. `us.data-automation-v1`) fan requests out to
25 * sibling regions, so the profile grant cannot be scoped to one region.
26 */
27export const makeBdaProjectHttpBinding = <
28 I extends {
29 dataAutomationConfiguration?: {
30 dataAutomationProjectArn: string;
31 stage?: string;
32 };
33 },
34 A,
35 E,
36 R,
37>(options: {
38 /** Fully-qualified binding tag, e.g. `AWS.BedrockDataAutomation.InvokeDataAutomationAsync`. */
39 tag: string;
40 /** The distilled operation; `dataAutomationConfiguration` is injected from the project. */
41 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
42 /** IAM actions granted on the project ARN + the account's profiles. */
43 actions: readonly string[];
44}) =>
45 Effect.gen(function* () {
46 const op = yield* options.operation;
47
48 return Effect.fn(function* (project: DataAutomationProject) {
49 const projectArn = yield* project.projectArn;
50 const projectStage = yield* project.projectStage;
51 if (!globalThis.__ALCHEMY_RUNTIME__) {
52 const host = yield* Binding.Host;
53 if (isBindingHost(host)) {
54 yield* host.bind`Allow(${host}, ${options.tag}(${project}))`({
55 policyStatements: [
56 {
57 Effect: "Allow",
58 Action: [...options.actions],
59 Resource: [
60 Output.interpolate`${project.projectArn}`,
61 // Cross-region inference profiles live in every region of
62 // the geography (us./eu./apac.), so the profile resource
63 // cannot be pinned to the deploy region.
64 "arn:aws:bedrock:*:*:data-automation-profile/*",
65 ],
66 },
67 ],
68 });
69 }
70 }
71 return Effect.fn(`${options.tag}(${project.LogicalId})`)(function* (
72 request: Omit<I, "dataAutomationConfiguration">,
73 ) {
74 return yield* op({
75 ...request,
76 dataAutomationConfiguration: {
77 dataAutomationProjectArn: yield* projectArn,
78 stage: yield* projectStage,
79 },
80 } as I);
81 });
82 });
83 });
84
85/**
86 * Build the impl Effect for a library-scoped runtime operation (entity reads
87 * and ingestion-job APIs). The runtime callable injects the bound
88 * {@link DataAutomationLibrary}'s ARN as `libraryArn` and the deploy-time
89 * half grants `actions` on the library ARN.
90 */
91export const makeBdaLibraryHttpBinding = <
92 I extends { libraryArn: string },
93 A,
94 E,
95 R,
96>(options: {
97 /** Fully-qualified binding tag, e.g. `AWS.BedrockDataAutomation.ListDataAutomationLibraryEntities`. */
98 tag: string;
99 /** The distilled operation; `libraryArn` is injected from the library. */
100 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
101 /** IAM actions granted on the library ARN (+ `additionalResources`). */
102 actions: readonly string[];
103 /**
104 * Extra resource ARNs the actions authorize against — the ingestion-job
105 * APIs check `…:data-automation-library-ingestion-job/{id}` (minted at
106 * runtime), not the library ARN.
107 */
108 additionalResources?: readonly string[];
109}) =>
110 Effect.gen(function* () {
111 const op = yield* options.operation;
112
113 return Effect.fn(function* (library: DataAutomationLibrary) {
114 const libraryArn = yield* library.libraryArn;
115 if (!globalThis.__ALCHEMY_RUNTIME__) {
116 const host = yield* Binding.Host;
117 if (isBindingHost(host)) {
118 yield* host.bind`Allow(${host}, ${options.tag}(${library}))`({
119 policyStatements: [
120 {
121 Effect: "Allow",
122 Action: [...options.actions],
123 Resource: [
124 Output.interpolate`${library.libraryArn}`,
125 ...(options.additionalResources ?? []),
126 ],
127 },
128 ],
129 });
130 }
131 }
132 return Effect.fn(`${options.tag}(${library.LogicalId})`)(function* (
133 request: Omit<I, "libraryArn">,
134 ) {
135 return yield* op({
136 ...request,
137 libraryArn: yield* libraryArn,
138 } as I);
139 });
140 });
141 });
142
143/**
144 * Build the impl Effect for a blueprint-scoped runtime operation (blueprint
145 * optimization). The runtime callable injects the bound {@link Blueprint}'s
146 * ARN + stage as `blueprint` and the deploy-time half grants `actions` on the
147 * blueprint ARN plus the account's data automation profiles in every region
148 * (cross-region inference profiles fan out to sibling regions).
149 */
150export const makeBdaBlueprintOptimizationHttpBinding = <
151 I extends { blueprint?: { blueprintArn: string; stage?: string } },
152 A,
153 E,
154 R,
155>(options: {
156 /** Fully-qualified binding tag, e.g. `AWS.BedrockDataAutomation.InvokeBlueprintOptimizationAsync`. */
157 tag: string;
158 /** The distilled operation; `blueprint` is injected from the blueprint. */
159 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
160 /** IAM actions granted on the blueprint ARN + the account's profiles. */
161 actions: readonly string[];
162}) =>
163 Effect.gen(function* () {
164 const op = yield* options.operation;
165
166 return Effect.fn(function* (blueprint: Blueprint) {
167 const blueprintArn = yield* blueprint.blueprintArn;
168 const blueprintStage = yield* blueprint.blueprintStage;
169 if (!globalThis.__ALCHEMY_RUNTIME__) {
170 const host = yield* Binding.Host;
171 if (isBindingHost(host)) {
172 yield* host.bind`Allow(${host}, ${options.tag}(${blueprint}))`({
173 policyStatements: [
174 {
175 Effect: "Allow",
176 Action: [...options.actions],
177 Resource: [
178 Output.interpolate`${blueprint.blueprintArn}`,
179 "arn:aws:bedrock:*:*:data-automation-profile/*",
180 // The invoke authorizes against the invocation resource
181 // (minted at runtime), not the blueprint ARN.
182 "arn:aws:bedrock:*:*:blueprint-optimization-invocation/*",
183 ],
184 },
185 ],
186 });
187 }
188 }
189 return Effect.fn(`${options.tag}(${blueprint.LogicalId})`)(function* (
190 request: Omit<I, "blueprint">,
191 ) {
192 return yield* op({
193 ...request,
194 blueprint: {
195 blueprintArn: yield* blueprintArn,
196 stage: yield* blueprintStage,
197 },
198 } as I);
199 });
200 });
201 });
202
203/**
204 * Build the impl Effect for a blueprint-scoped management operation (version
205 * snapshots and stage copies). The runtime callable injects the bound
206 * {@link Blueprint}'s ARN as `blueprintArn` and the deploy-time half grants
207 * `actions` on the blueprint ARN.
208 */
209export const makeBdaBlueprintHttpBinding = <
210 I extends { blueprintArn: string },
211 A,
212 E,
213 R,
214>(options: {
215 /** Fully-qualified binding tag, e.g. `AWS.BedrockDataAutomation.CreateBlueprintVersion`. */
216 tag: string;
217 /** The distilled operation; `blueprintArn` is injected from the blueprint. */
218 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
219 /** IAM actions granted on the blueprint ARN. */
220 actions: readonly string[];
221}) =>
222 Effect.gen(function* () {
223 const op = yield* options.operation;
224
225 return Effect.fn(function* (blueprint: Blueprint) {
226 const blueprintArn = yield* blueprint.blueprintArn;
227 if (!globalThis.__ALCHEMY_RUNTIME__) {
228 const host = yield* Binding.Host;
229 if (isBindingHost(host)) {
230 yield* host.bind`Allow(${host}, ${options.tag}(${blueprint}))`({
231 policyStatements: [
232 {
233 Effect: "Allow",
234 Action: [...options.actions],
235 Resource: [Output.interpolate`${blueprint.blueprintArn}`],
236 },
237 ],
238 });
239 }
240 }
241 return Effect.fn(`${options.tag}(${blueprint.LogicalId})`)(function* (
242 request: Omit<I, "blueprintArn">,
243 ) {
244 return yield* op({
245 ...request,
246 blueprintArn: yield* blueprintArn,
247 } as I);
248 });
249 });
250 });
251
252/**
253 * Build the impl Effect for an account-level runtime operation (job status
254 * polling). The deploy-time half grants `actions` on `*` — invocation ARNs
255 * (`…:data-automation-invocation/{id}`) are minted at runtime and unknowable
256 * at deploy time.
257 */
258export const makeBdaAccountHttpBinding = <I, A, E, R>(options: {
259 /** Fully-qualified binding tag, e.g. `AWS.BedrockDataAutomation.GetDataAutomationStatus`. */
260 tag: string;
261 /** The distilled operation, invoked with the caller's request as-is. */
262 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
263 /** IAM actions granted on `*`. */
264 actions: readonly string[];
265}) =>
266 Effect.gen(function* () {
267 const op = yield* options.operation;
268
269 return Effect.fn(function* () {
270 if (!globalThis.__ALCHEMY_RUNTIME__) {
271 const host = yield* Binding.Host;
272 if (isBindingHost(host)) {
273 yield* host.bind`Allow(${host}, ${options.tag}())`({
274 policyStatements: [
275 {
276 Effect: "Allow",
277 Action: [...options.actions],
278 Resource: ["*"],
279 },
280 ],
281 });
282 }
283 }
284 return Effect.fn(options.tag)(function* (request: I) {
285 return yield* op(request);
286 });
287 });
288 });