1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { InvestigationGroup } from "./InvestigationGroup.ts";
5
6/**
7 * Shared scaffolding for the CloudWatch investigations (AIOps) HTTP bindings.
8 *
9 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
10 * a `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the two builders
11 * below. Everything except the operation, the IAM action list, and (for
12 * group-scoped ops) how the group's ARN maps onto the request is boilerplate.
13 */
14
15/**
16 * Build the impl Effect for an AIOps operation scoped to an
17 * {@link InvestigationGroup}: the deploy-time half grants `actions` on the
18 * bound group's ARN, and the runtime half injects the group's ARN into the
19 * request via `input`. All group-scoped AIOps read requests consist solely of
20 * the group identifier, so the runtime callable takes no arguments.
21 */
22export const makeAIOpsGroupHttpBinding = <I, A, E, R>(options: {
23 /** Fully-qualified binding tag, e.g. `AWS.AIOps.GetInvestigationGroup`. */
24 tag: string;
25 /** The distilled operation. */
26 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
27 /** IAM actions granted on the investigation group ARN. */
28 actions: readonly string[];
29 /** Map the bound group's ARN onto the operation's request shape. */
30 input: (groupArn: string) => I;
31}) =>
32 Effect.gen(function* () {
33 const op = yield* options.operation;
34
35 return Effect.fn(function* (group: InvestigationGroup) {
36 const Arn = yield* group.arn;
37 if (!globalThis.__ALCHEMY_RUNTIME__) {
38 const host = yield* Binding.Host;
39 if (isBindingHost(host)) {
40 yield* host.bind`Allow(${host}, ${options.tag}(${group}))`({
41 policyStatements: [
42 {
43 Effect: "Allow",
44 Action: [...options.actions],
45 Resource: [group.arn],
46 },
47 ],
48 });
49 }
50 }
51 return Effect.fn(`${options.tag}(${group.LogicalId})`)(function* () {
52 const arn = yield* Arn;
53 return yield* op(options.input(arn));
54 });
55 });
56 });
57
58/**
59 * Build the impl Effect for an account-level AIOps operation (enumerating
60 * the Region's investigation groups). The deploy-time half grants `actions`
61 * on `*` — `aiops:ListInvestigationGroups` is a list action that is not
62 * scoped to a single group resource.
63 */
64export const makeAIOpsAccountHttpBinding = <I, A, E, R>(options: {
65 /** Fully-qualified binding tag, e.g. `AWS.AIOps.ListInvestigationGroups`. */
66 tag: string;
67 /** The distilled operation, invoked with the caller's request as-is. */
68 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
69 /** IAM actions granted on `*`. */
70 actions: readonly string[];
71}) =>
72 Effect.gen(function* () {
73 const op = yield* options.operation;
74
75 return Effect.fn(function* () {
76 if (!globalThis.__ALCHEMY_RUNTIME__) {
77 const host = yield* Binding.Host;
78 if (isBindingHost(host)) {
79 yield* host.bind`Allow(${host}, ${options.tag}())`({
80 policyStatements: [
81 {
82 Effect: "Allow",
83 Action: [...options.actions],
84 Resource: ["*"],
85 },
86 ],
87 });
88 }
89 }
90 return Effect.fn(options.tag)(function* (request?: I) {
91 return yield* op((request ?? {}) as I);
92 });
93 });
94 });