1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import type { PolicyStatement } from "../IAM/Policy.ts";
4import { isBindingHost } from "../Lambda/Function.ts";
5
6/**
7 * Shared HTTP scaffolding for the Comprehend Medical runtime bindings.
8 *
9 * Comprehend Medical is a pure pay-per-call service: none of its actions
10 * support resource-level IAM, so every binding is account-level and grants
11 * its action(s) on `Resource: ["*"]`. The only variation between bindings is
12 * the distilled operation, the IAM action, and whether the operation passes
13 * a data-access role to the service (the `Start*Job` operations, which
14 * additionally need `iam:PassRole` scoped to
15 * `comprehendmedical.amazonaws.com`).
16 *
17 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
18 * a `Layer.effect(Cap, makeComprehendMedicalHttpBinding({ … }))`.
19 */
20export const makeComprehendMedicalHttpBinding = <I, A, E, R>(options: {
21 /**
22 * Short capability name used in the binding sid and runtime span, e.g.
23 * `"DetectEntitiesV2"`.
24 */
25 capability: string;
26 /**
27 * IAM actions granted on `Resource: ["*"]` (Comprehend Medical has no
28 * resource-level IAM).
29 */
30 iamActions: readonly string[];
31 /**
32 * The distilled operation implementing the capability.
33 */
34 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
35 /**
36 * Grant `iam:PassRole` (conditioned to `comprehendmedical.amazonaws.com`)
37 * so the function can hand the service the `DataAccessRoleArn` a batch job
38 * assumes to read/write S3. Set on the `Start*Job` bindings.
39 */
40 passRole?: boolean;
41}) =>
42 Effect.gen(function* () {
43 const op = yield* options.operation;
44
45 return Effect.fn(function* () {
46 if (!globalThis.__ALCHEMY_RUNTIME__) {
47 const host = yield* Binding.Host;
48 if (isBindingHost(host)) {
49 const policyStatements: PolicyStatement[] = [
50 {
51 Effect: "Allow",
52 Action: [...options.iamActions],
53 // Comprehend Medical actions have no resource-level IAM.
54 Resource: ["*"],
55 },
56 ];
57 if (options.passRole) {
58 policyStatements.push({
59 Effect: "Allow",
60 Action: ["iam:PassRole"],
61 Resource: ["*"],
62 Condition: {
63 StringEquals: {
64 "iam:PassedToService": "comprehendmedical.amazonaws.com",
65 },
66 },
67 });
68 }
69 yield* host.bind`Allow(${host}, AWS.ComprehendMedical.${options.capability}())`(
70 { policyStatements },
71 );
72 }
73 }
74 return Effect.fn(`AWS.ComprehendMedical.${options.capability}`)(
75 function* (request: I) {
76 return yield* op(request);
77 },
78 );
79 });
80 });