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 { Job } from "./Job.ts";
6import type { Project } from "./Project.ts";
7import type { Recipe } from "./Recipe.ts";
8
9/**
10 * Shared scaffolding for AWS Glue DataBrew 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 builders
14 * below. Every DataBrew data-plane operation addresses its resource by a
15 * `Name` path parameter and authorizes against that resource's ARN, so each
16 * builder injects the bound resource's name and grants `actions` on its ARN;
17 * everything except the operation and the IAM action is boilerplate.
18 */
19
20/**
21 * Build the impl Effect for a job-anchored operation: the runtime callable
22 * injects the bound {@link Job}'s name as `Name` and the deploy-time half
23 * grants `actions` on the job ARN.
24 */
25export const makeDataBrewJobHttpBinding = <
26 I extends { Name?: string },
27 A,
28 E,
29 R,
30>(options: {
31 /** Fully-qualified binding tag, e.g. `AWS.DataBrew.StartJobRun`. */
32 tag: string;
33 /** The distilled operation; `Name` is injected from the job. */
34 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
35 /** IAM actions granted on the job ARN. */
36 actions: readonly string[];
37}) =>
38 Effect.gen(function* () {
39 const op = yield* options.operation;
40
41 return Effect.fn(function* <J extends Job>(job: J) {
42 // Outputs yield a DEFERRED effect — resolve again per invocation below.
43 const JobName = yield* job.jobName;
44 if (!globalThis.__ALCHEMY_RUNTIME__) {
45 const host = yield* Binding.Host;
46 if (isBindingHost(host)) {
47 yield* host.bind`Allow(${host}, ${options.tag}(${job}))`({
48 policyStatements: [
49 {
50 Effect: "Allow",
51 Action: [...options.actions],
52 Resource: [Output.interpolate`${job.jobArn}`],
53 },
54 ],
55 });
56 }
57 }
58 return Effect.fn(`${options.tag}(${job.LogicalId})`)(function* (
59 request?: Omit<I, "Name">,
60 ) {
61 return yield* op({ ...request, Name: yield* JobName } as I);
62 });
63 });
64 });
65
66/**
67 * Build the impl Effect for a project-anchored operation: the runtime
68 * callable injects the bound {@link Project}'s name as `Name` and the
69 * deploy-time half grants `actions` on the project ARN.
70 */
71export const makeDataBrewProjectHttpBinding = <
72 I extends { Name?: string },
73 A,
74 E,
75 R,
76>(options: {
77 /** Fully-qualified binding tag, e.g. `AWS.DataBrew.StartProjectSession`. */
78 tag: string;
79 /** The distilled operation; `Name` is injected from the project. */
80 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
81 /** IAM actions granted on the project ARN. */
82 actions: readonly string[];
83}) =>
84 Effect.gen(function* () {
85 const op = yield* options.operation;
86
87 return Effect.fn(function* <P extends Project>(project: P) {
88 const ProjectName = yield* project.projectName;
89 if (!globalThis.__ALCHEMY_RUNTIME__) {
90 const host = yield* Binding.Host;
91 if (isBindingHost(host)) {
92 yield* host.bind`Allow(${host}, ${options.tag}(${project}))`({
93 policyStatements: [
94 {
95 Effect: "Allow",
96 Action: [...options.actions],
97 Resource: [Output.interpolate`${project.projectArn}`],
98 },
99 ],
100 });
101 }
102 }
103 return Effect.fn(`${options.tag}(${project.LogicalId})`)(function* (
104 request?: Omit<I, "Name">,
105 ) {
106 return yield* op({ ...request, Name: yield* ProjectName } as I);
107 });
108 });
109 });
110
111/**
112 * Build the impl Effect for a recipe-anchored operation: the runtime
113 * callable injects the bound {@link Recipe}'s name as `Name` and the
114 * deploy-time half grants `actions` on the recipe ARN.
115 */
116export const makeDataBrewRecipeHttpBinding = <
117 I extends { Name?: string },
118 A,
119 E,
120 R,
121>(options: {
122 /** Fully-qualified binding tag, e.g. `AWS.DataBrew.PublishRecipe`. */
123 tag: string;
124 /** The distilled operation; `Name` is injected from the recipe. */
125 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
126 /** IAM actions granted on the recipe ARN. */
127 actions: readonly string[];
128}) =>
129 Effect.gen(function* () {
130 const op = yield* options.operation;
131
132 return Effect.fn(function* <Rec extends Recipe>(recipe: Rec) {
133 const RecipeName = yield* recipe.recipeName;
134 if (!globalThis.__ALCHEMY_RUNTIME__) {
135 const host = yield* Binding.Host;
136 if (isBindingHost(host)) {
137 yield* host.bind`Allow(${host}, ${options.tag}(${recipe}))`({
138 policyStatements: [
139 {
140 Effect: "Allow",
141 Action: [...options.actions],
142 Resource: [Output.interpolate`${recipe.recipeArn}`],
143 },
144 ],
145 });
146 }
147 }
148 return Effect.fn(`${options.tag}(${recipe.LogicalId})`)(function* (
149 request?: Omit<I, "Name">,
150 ) {
151 return yield* op({ ...request, Name: yield* RecipeName } as I);
152 });
153 });
154 });