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 { Assessment } from "./Assessment.ts";
6
7/**
8 * Shared scaffolding for Audit Manager HTTP bindings.
9 *
10 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
11 * thin `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the two
12 * builders below. Everything except the operation, the IAM action list, and
13 * (for assessment-scoped operations) the injected `assessmentId` is
14 * boilerplate.
15 */
16
17/**
18 * Build the impl Effect for an assessment-scoped operation: the runtime
19 * callable injects the bound {@link Assessment}'s id as `assessmentId` and
20 * the deploy-time half grants `actions` on the assessment ARN.
21 */
22export const makeAssessmentScopedHttpBinding = <
23 I extends { assessmentId: string },
24 A,
25 E,
26 R,
27>(options: {
28 /** Fully-qualified binding tag, e.g. `AWS.AuditManager.GetEvidence`. */
29 tag: string;
30 /** The distilled operation; `assessmentId` is injected from the resource. */
31 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
32 /** IAM actions granted on the assessment ARN. */
33 actions: readonly string[];
34}) =>
35 Effect.gen(function* () {
36 const op = yield* options.operation;
37
38 return Effect.fn(function* (assessment: Assessment) {
39 const AssessmentId = yield* assessment.assessmentId;
40 if (!globalThis.__ALCHEMY_RUNTIME__) {
41 const host = yield* Binding.Host;
42 if (isBindingHost(host)) {
43 yield* host.bind`Allow(${host}, ${options.tag}(${assessment}))`({
44 policyStatements: [
45 {
46 Effect: "Allow",
47 Action: [...options.actions],
48 Resource: [Output.interpolate`${assessment.arn}`],
49 },
50 ],
51 });
52 }
53 }
54 return Effect.fn(`${options.tag}(${assessment.LogicalId})`)(function* (
55 request?: Omit<I, "assessmentId">,
56 ) {
57 return yield* op({
58 ...request,
59 assessmentId: yield* AssessmentId,
60 } as I);
61 });
62 });
63 });
64
65/**
66 * Build the impl Effect for an account-level operation (no target assessment
67 * — registration status, account-wide insights, report listings, presigned
68 * evidence-upload URLs). The deploy-time half grants `actions` on `*`
69 * because these IAM actions do not support resource-level scoping.
70 */
71export const makeAuditManagerAccountHttpBinding = <I, A, E, R>(options: {
72 /** Fully-qualified binding tag, e.g. `AWS.AuditManager.GetInsights`. */
73 tag: string;
74 /** The distilled operation, invoked with the caller's request as-is. */
75 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
76 /** IAM actions granted on `*`. */
77 actions: readonly string[];
78}) =>
79 Effect.gen(function* () {
80 const op = yield* options.operation;
81
82 return Effect.fn(function* () {
83 if (!globalThis.__ALCHEMY_RUNTIME__) {
84 const host = yield* Binding.Host;
85 if (isBindingHost(host)) {
86 yield* host.bind`Allow(${host}, ${options.tag}())`({
87 policyStatements: [
88 {
89 Effect: "Allow",
90 Action: [...options.actions],
91 Resource: ["*"],
92 },
93 ],
94 });
95 }
96 }
97 return Effect.fn(options.tag)(function* (request?: I) {
98 return yield* op((request ?? {}) as I);
99 });
100 });
101 });