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 { Farm } from "./Farm.ts";
6import type { Queue } from "./Queue.ts";
7
8/**
9 * Shared scaffolding for AWS Deadline Cloud 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 builders
13 * below. Everything except the operation, the IAM action list, and the
14 * injected identifiers is boilerplate.
15 */
16
17/**
18 * Build the impl Effect for a queue-scoped job operation. The runtime half
19 * injects the bound {@link Queue}'s `farmId` and `queueId` into every
20 * request; the deploy-time half grants `actions` on the queue's ARN and on
21 * its jobs (`{queueArn}/job/*`), since Deadline authorizes job-level actions
22 * (GetJob, UpdateJob, ListSteps, …) against the job resource.
23 */
24export const makeDeadlineQueueHttpBinding = <
25 I extends { farmId: string; queueId: string },
26 A,
27 E,
28 R,
29>(options: {
30 /** Fully-qualified binding tag, e.g. `AWS.Deadline.CreateJob`. */
31 tag: string;
32 /** The distilled operation; `farmId`/`queueId` are injected. */
33 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
34 /** IAM actions granted on the queue ARN and its jobs. */
35 actions: readonly string[];
36}) =>
37 Effect.gen(function* () {
38 const op = yield* options.operation;
39
40 return Effect.fn(function* (queue: Queue) {
41 const FarmId = yield* queue.farmId;
42 const QueueId = yield* queue.queueId;
43 if (!globalThis.__ALCHEMY_RUNTIME__) {
44 const host = yield* Binding.Host;
45 if (isBindingHost(host)) {
46 yield* host.bind`Allow(${host}, ${options.tag}(${queue}))`({
47 policyStatements: [
48 {
49 Effect: "Allow",
50 Action: [...options.actions],
51 Resource: [
52 queue.queueArn,
53 queue.queueArn.pipe(Output.map((arn) => `${arn}/job/*`)),
54 ],
55 },
56 ],
57 });
58 }
59 }
60 return Effect.fn(`${options.tag}(${queue.LogicalId})`)(function* (
61 request?: Omit<I, "farmId" | "queueId">,
62 ) {
63 return yield* op({
64 ...request,
65 farmId: yield* FarmId,
66 queueId: yield* QueueId,
67 } as I);
68 });
69 });
70 });
71
72/**
73 * Build the impl Effect for a queue-scoped search operation
74 * (`SearchJobs`/`SearchSteps`/`SearchTasks` take a `queueIds` array rather
75 * than a single `queueId`). The runtime half injects the bound
76 * {@link Queue}'s `farmId` and `queueIds: [queueId]`; the deploy-time half
77 * grants `actions` on the queue's ARN and its jobs.
78 */
79export const makeDeadlineQueueSearchHttpBinding = <
80 I extends { farmId: string; queueIds: string[] },
81 A,
82 E,
83 R,
84>(options: {
85 /** Fully-qualified binding tag, e.g. `AWS.Deadline.SearchJobs`. */
86 tag: string;
87 /** The distilled operation; `farmId`/`queueIds` are injected. */
88 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
89 /** IAM actions granted on the queue ARN and its jobs. */
90 actions: readonly string[];
91}) =>
92 Effect.gen(function* () {
93 const op = yield* options.operation;
94
95 return Effect.fn(function* (queue: Queue) {
96 const FarmId = yield* queue.farmId;
97 const QueueId = yield* queue.queueId;
98 if (!globalThis.__ALCHEMY_RUNTIME__) {
99 const host = yield* Binding.Host;
100 if (isBindingHost(host)) {
101 yield* host.bind`Allow(${host}, ${options.tag}(${queue}))`({
102 policyStatements: [
103 {
104 Effect: "Allow",
105 Action: [...options.actions],
106 Resource: [
107 queue.queueArn,
108 queue.queueArn.pipe(Output.map((arn) => `${arn}/job/*`)),
109 ],
110 },
111 ],
112 });
113 }
114 }
115 return Effect.fn(`${options.tag}(${queue.LogicalId})`)(function* (
116 request?: Omit<I, "farmId" | "queueIds">,
117 ) {
118 return yield* op({
119 ...request,
120 farmId: yield* FarmId,
121 queueIds: [yield* QueueId],
122 } as I);
123 });
124 });
125 });
126
127/**
128 * Build the impl Effect for a farm-scoped operation (usage statistics
129 * aggregations). The runtime half injects the bound {@link Farm}'s `farmId`;
130 * the deploy-time half grants `actions` on the farm's ARN and everything
131 * under it (queues/fleets referenced by the aggregation's `resourceIds`).
132 */
133export const makeDeadlineFarmHttpBinding = <
134 I extends { farmId: string },
135 A,
136 E,
137 R,
138>(options: {
139 /** Fully-qualified binding tag, e.g. `AWS.Deadline.GetSessionsStatisticsAggregation`. */
140 tag: string;
141 /** The distilled operation; `farmId` is injected. */
142 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
143 /** IAM actions granted on the farm ARN and its sub-resources. */
144 actions: readonly string[];
145}) =>
146 Effect.gen(function* () {
147 const op = yield* options.operation;
148
149 return Effect.fn(function* (farm: Farm) {
150 const FarmId = yield* farm.farmId;
151 if (!globalThis.__ALCHEMY_RUNTIME__) {
152 const host = yield* Binding.Host;
153 if (isBindingHost(host)) {
154 yield* host.bind`Allow(${host}, ${options.tag}(${farm}))`({
155 policyStatements: [
156 {
157 Effect: "Allow",
158 Action: [...options.actions],
159 Resource: [
160 farm.farmArn,
161 farm.farmArn.pipe(Output.map((arn) => `${arn}/*`)),
162 ],
163 },
164 ],
165 });
166 }
167 }
168 return Effect.fn(`${options.tag}(${farm.LogicalId})`)(function* (
169 request?: Omit<I, "farmId">,
170 ) {
171 return yield* op({ ...request, farmId: yield* FarmId } as I);
172 });
173 });
174 });