1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import type { Output } from "../../Output.ts";
4import type { ResourceLike } from "../../Resource.ts";
5import type { PolicyStatement } from "../IAM/Policy.ts";
6import { isBindingHost } from "../Lambda/Function.ts";
7
8/**
9 * Shared scaffolding for the Amazon HealthOmics HTTP bindings.
10 *
11 * Every Omics data-plane operation is either addressed by a store/workflow id
12 * (read-set jobs by `sequenceStoreId`, reference jobs by `referenceStoreId`,
13 * runs by `workflowId`) or account-level (run control by `runId`, a runtime
14 * value). Two builders cover both shapes; every thin `{Op}Http.ts` in this
15 * service is a `Layer.effect(Cap, make…HttpBinding({ … }))`.
16 *
17 * NOT exported from `index.ts` — leaking the generic helper names into the
18 * flat `AWS` namespace would collide across services.
19 */
20
21/**
22 * Build the impl Effect for a resource-scoped operation: the runtime callable
23 * injects the bound resource's id under `key` (`sequenceStoreId`,
24 * `referenceStoreId`, or `workflowId`) and the deploy-time half grants
25 * `actions` on the resource ARN. Operations that hand the service a role to
26 * assume (`Start*ImportJob`, `Start*ExportJob`, `StartRun`) set `passRole`.
27 */
28export const makeOmicsResourceHttpBinding = <
29 Res extends ResourceLike,
30 K extends string,
31 I extends { [P in K]?: string },
32 A,
33 E,
34 R,
35>(options: {
36 /** Fully-qualified binding tag, e.g. `AWS.Omics.GetReadSet`. */
37 tag: string;
38 /** The distilled operation; the resource id is injected under `key`. */
39 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
40 /** IAM actions granted on the resource ARN. */
41 actions: readonly string[];
42 /** The request field the resource id is injected under. */
43 key: K;
44 /**
45 * Resolve the injected id from the bound resource. `Req` is pinned to
46 * `never` (resource attributes are `Output<string, never>`): the default
47 * `Output<string>` widens `Req` to `any`, which leaks into the per-resource
48 * Effect's requirements and breaks the binding contract's `never`.
49 */
50 id: (res: Res) => Output<string, never>;
51 /** Resolve the ARN the IAM grant targets from the bound resource. */
52 arn: (res: Res) => Output<string> | string;
53 /** Grant `iam:PassRole` (conditioned to `omics.amazonaws.com`). */
54 passRole?: boolean;
55}) =>
56 Effect.gen(function* () {
57 const op = yield* options.operation;
58
59 return Effect.fn(function* (res: Res) {
60 // Outputs yield a DEFERRED effect — resolve again per invocation below.
61 const Id = yield* options.id(res);
62 if (!globalThis.__ALCHEMY_RUNTIME__) {
63 const host = yield* Binding.Host;
64 if (isBindingHost(host)) {
65 // Inline (not a `PolicyStatement[]`-typed local) so the bind data's
66 // `Input<…>` contextual typing accepts the `Output<string>` ARN.
67 yield* host.bind`Allow(${host}, ${options.tag}(${res}))`({
68 policyStatements: [
69 {
70 Effect: "Allow",
71 Action: [...options.actions],
72 Resource: [options.arn(res)],
73 },
74 ...(options.passRole
75 ? ([
76 {
77 Effect: "Allow",
78 Action: ["iam:PassRole"],
79 Resource: ["*"],
80 Condition: {
81 StringEquals: {
82 "iam:PassedToService": "omics.amazonaws.com",
83 },
84 },
85 },
86 ] satisfies PolicyStatement[])
87 : []),
88 ],
89 });
90 }
91 }
92 return Effect.fn(`${options.tag}(${res.LogicalId})`)(function* (
93 request?: Omit<I, K>,
94 ) {
95 return yield* op({ ...request, [options.key]: yield* Id } as I);
96 });
97 });
98 });
99
100/**
101 * Build the impl Effect for an account-level run-control operation
102 * (`GetRun`, `ListRuns`, `CancelRun`, `DeleteRun`, `GetRunTask`,
103 * `ListRunTasks`): the runtime callable passes the caller's request through
104 * unchanged (the `runId` is a runtime value) and the deploy-time half grants
105 * `actions` on `*`.
106 */
107export const makeOmicsAccountHttpBinding = <I, A, E, R>(options: {
108 /** Fully-qualified binding tag, e.g. `AWS.Omics.GetRun`. */
109 tag: string;
110 /** The distilled operation, invoked with the caller's request as-is. */
111 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
112 /** IAM actions granted on `*`. */
113 actions: readonly string[];
114}) =>
115 Effect.gen(function* () {
116 const op = yield* options.operation;
117
118 return Effect.fn(function* () {
119 if (!globalThis.__ALCHEMY_RUNTIME__) {
120 const host = yield* Binding.Host;
121 if (isBindingHost(host)) {
122 yield* host.bind`Allow(${host}, ${options.tag}())`({
123 policyStatements: [
124 {
125 Effect: "Allow",
126 Action: [...options.actions],
127 Resource: ["*"],
128 },
129 ],
130 });
131 }
132 }
133 return Effect.fn(options.tag)(function* (request?: I) {
134 return yield* op((request ?? {}) as I);
135 });
136 });
137 });