1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { AnomalyMonitor } from "./AnomalyMonitor.ts";
5import { pinCe } from "./common.ts";
6import type { CostCategory } from "./CostCategory.ts";
7
8/**
9 * Shared HTTP scaffolding for the Cost Explorer runtime bindings.
10 *
11 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
12 * thin `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the builders
13 * below. Everything except the operation, the IAM action, and (for the
14 * scoped builders) the injected ARN is boilerplate.
15 *
16 * Cost Explorer is a global service served exclusively from the `us-east-1`
17 * endpoint, so every operation is resolved with the Region pinned via
18 * {@link pinCe} — exactly like the resource providers in `common.ts`.
19 *
20 * Per the `ce` service authorization reference, most query actions support
21 * no resource types, so the account-level builder grants on
22 * `Resource: ["*"]`. `ce:GetAnomalies` authorizes on the `anomalymonitor`
23 * resource type, so the anomaly-monitor builder grants on the bound
24 * monitor's ARN.
25 */
26
27/**
28 * Build the impl Effect for an account-level Cost Explorer operation (the
29 * cost/usage queries, forecasts, recommendations, and cost allocation tag
30 * operations — none of which are resource-scoped).
31 */
32export const makeCostExplorerHttpBinding = <
33 I extends object,
34 A,
35 // `{ _tag: string }` bound so `pinCe` (whose throttle retry inspects
36 // `_tag`) applies without collapsing the inferred error union.
37 E extends { _tag: string },
38 R,
39>(options: {
40 /**
41 * Short capability name used in the binding sid and runtime span, e.g.
42 * `"GetCostAndUsage"`.
43 */
44 capability: string;
45 /** IAM actions granted on `Resource: ["*"]`. */
46 iamActions: readonly string[];
47 /** The distilled operation implementing the capability. */
48 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
49}) =>
50 Effect.gen(function* () {
51 const op = yield* pinCe(options.operation);
52
53 return Effect.fn(function* () {
54 if (!globalThis.__ALCHEMY_RUNTIME__) {
55 const host = yield* Binding.Host;
56 if (isBindingHost(host)) {
57 yield* host.bind`Allow(${host}, AWS.CostExplorer.${options.capability}())`(
58 {
59 policyStatements: [
60 {
61 Effect: "Allow",
62 Action: [...options.iamActions],
63 Resource: ["*"],
64 },
65 ],
66 },
67 );
68 }
69 }
70 return Effect.fn(`AWS.CostExplorer.${options.capability}`)(function* (
71 request?: I,
72 ) {
73 // The region must also be pinned at the call site: the yield-time
74 // snapshot is only a fallback — the calling fiber's ambient Region
75 // (the host Function's own region) wins over it.
76 return yield* pinCe(op((request ?? {}) as I));
77 });
78 });
79 });
80
81/**
82 * Build the impl Effect for an operation scoped to one
83 * {@link AnomalyMonitor}. The runtime callable injects the bound monitor's
84 * ARN as the request's `MonitorArn`; the deploy-time half grants
85 * `iamActions` on the monitor's ARN.
86 */
87export const makeAnomalyMonitorHttpBinding = <
88 I extends { MonitorArn?: string },
89 A,
90 // See makeCostExplorerHttpBinding.
91 E extends { _tag: string },
92 R,
93>(options: {
94 /**
95 * Short capability name used in the binding sid and runtime span, e.g.
96 * `"GetAnomalies"`.
97 */
98 capability: string;
99 /** IAM actions granted on the monitor ARN. */
100 iamActions: readonly string[];
101 /** The distilled operation; `MonitorArn` is injected. */
102 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
103}) =>
104 Effect.gen(function* () {
105 const op = yield* pinCe(options.operation);
106
107 return Effect.fn(function* (monitor: AnomalyMonitor) {
108 const MonitorArn = yield* monitor.monitorArn;
109 if (!globalThis.__ALCHEMY_RUNTIME__) {
110 const host = yield* Binding.Host;
111 if (isBindingHost(host)) {
112 yield* host.bind`Allow(${host}, AWS.CostExplorer.${options.capability}(${monitor}))`(
113 {
114 policyStatements: [
115 {
116 Effect: "Allow",
117 Action: [...options.iamActions],
118 Resource: [monitor.monitorArn],
119 },
120 ],
121 },
122 );
123 }
124 }
125 return Effect.fn(
126 `AWS.CostExplorer.${options.capability}(${monitor.LogicalId})`,
127 )(function* (request: Omit<I, "MonitorArn">) {
128 // Call-site region pin — see makeCostExplorerHttpBinding above.
129 return yield* pinCe(
130 op({
131 ...request,
132 MonitorArn: yield* MonitorArn,
133 } as I),
134 );
135 });
136 });
137 });
138
139/**
140 * Build the impl Effect for an operation bound to one {@link CostCategory}.
141 * The runtime callable injects the bound category's ARN as the request's
142 * `CostCategoryArn`. The deploy-time half grants `iamActions` on
143 * `Resource: ["*"]` — per the `ce` service authorization reference,
144 * `ce:ListCostCategoryResourceAssociations` supports no resource types, so
145 * a grant scoped to the category ARN would never match.
146 */
147export const makeCostCategoryHttpBinding = <
148 I extends { CostCategoryArn?: string },
149 A,
150 // See makeCostExplorerHttpBinding.
151 E extends { _tag: string },
152 R,
153>(options: {
154 /**
155 * Short capability name used in the binding sid and runtime span, e.g.
156 * `"ListCostCategoryResourceAssociations"`.
157 */
158 capability: string;
159 /** IAM actions granted on `Resource: ["*"]` (no resource types). */
160 iamActions: readonly string[];
161 /** The distilled operation; `CostCategoryArn` is injected. */
162 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
163}) =>
164 Effect.gen(function* () {
165 const op = yield* pinCe(options.operation);
166
167 return Effect.fn(function* (category: CostCategory) {
168 const CostCategoryArn = yield* category.costCategoryArn;
169 if (!globalThis.__ALCHEMY_RUNTIME__) {
170 const host = yield* Binding.Host;
171 if (isBindingHost(host)) {
172 yield* host.bind`Allow(${host}, AWS.CostExplorer.${options.capability}(${category}))`(
173 {
174 policyStatements: [
175 {
176 Effect: "Allow",
177 Action: [...options.iamActions],
178 Resource: ["*"],
179 },
180 ],
181 },
182 );
183 }
184 }
185 return Effect.fn(
186 `AWS.CostExplorer.${options.capability}(${category.LogicalId})`,
187 )(function* (request?: Omit<I, "CostCategoryArn">) {
188 // Call-site region pin — see makeCostExplorerHttpBinding above.
189 return yield* pinCe(
190 op({
191 ...request,
192 CostCategoryArn: yield* CostCategoryArn,
193 } as I),
194 );
195 });
196 });
197 });