1import type * as Effect from "effect/Effect";
2import type * as Stream from "effect/Stream";
3import type { Input } from "../../Input.ts";
4import {
5 consumeBusEvents,
6 type EventRecord,
7 type EventRouteProps,
8} from "../EventBridge/EventSource.ts";
9
10/**
11 * The `detail` payload Amazon CloudWatch Internet Monitor delivers to
12 * EventBridge when a monitor creates or resolves a health event. Fields not
13 * shared by every event are optional (the schema grows over time).
14 */
15export interface HealthEventDetail {
16 /** The ARN of the health event. */
17 eventArn?: string;
18 /** The id of the health event. */
19 eventId?: string;
20 /** Human-readable summary of the health event. */
21 summary?: string;
22 /** When the health event started (ISO 8601). */
23 startedAt?: string;
24 /** When the health event ended, for resolved events (ISO 8601). */
25 endedAt?: string;
26 /**
27 * The impact type: `AVAILABILITY`, `PERFORMANCE`, `LOCAL_AVAILABILITY`,
28 * or `LOCAL_PERFORMANCE`.
29 */
30 impactType?: string;
31 /** The health event status: `ACTIVE` or `RESOLVED`. */
32 status?: string;
33 /** Percentage of the application's total traffic that is impacted. */
34 percentOfTotalTrafficImpacted?: number;
35 /** The client city-networks impacted by the health event. */
36 impactedLocations?: unknown[];
37 /** Additional event fields (the schema grows over time). */
38 [key: string]: unknown;
39}
40
41/** An Internet Monitor health event delivered to the handler. */
42export type HealthEventRecord = EventRecord<HealthEventDetail>;
43
44export interface HealthEventSourceProps extends EventRouteProps {
45 /**
46 * Logical id for the backing EventBridge rule.
47 * @default "InternetMonitorHealthEvents"
48 */
49 id?: string;
50 /**
51 * Restrict to health events emitted by the given monitors (matched
52 * against the event's `resources` — the monitor ARNs). Pass
53 * `monitor.monitorArn`.
54 * @default all monitors in the account
55 */
56 monitorArns?: readonly Input<string>[];
57 /**
58 * Restrict to health events of the given impact types (matched against
59 * the event detail's `impactType`: `AVAILABILITY`, `PERFORMANCE`,
60 * `LOCAL_AVAILABILITY`, `LOCAL_PERFORMANCE`).
61 */
62 impactTypes?: readonly string[];
63 /**
64 * Restrict to health events with the given statuses (matched against the
65 * event detail's `status`: `ACTIVE`, `RESOLVED`).
66 */
67 statuses?: readonly string[];
68}
69
70/**
71 * Event source connecting Amazon CloudWatch Internet Monitor health events
72 * to the hosting compute. When a monitor detects an availability or
73 * performance issue between your application and your users' city-networks,
74 * Internet Monitor publishes a health event to the account's default
75 * EventBridge bus (source `aws.internetmonitor`); this subscribes the host
76 * Function to those events so it can page, reroute traffic, or annotate
77 * incidents.
78 *
79 * Internet Monitor publishes to EventBridge automatically — no additional
80 * resource is created besides the EventBridge rule targeting the host.
81 * Provide the host-specific implementation layer (e.g.
82 * `AWS.Lambda.EventSource`) on the Function effect.
83 *
84 * ### Consuming Health Events
85 * **Example:** Page on Availability Drops
86 * ```typescript
87 * import * as AWS from "alchemy/AWS";
88 *
89 * export default AlertFunction.make(
90 * { main: import.meta.url },
91 * Effect.gen(function* () {
92 * const monitor = yield* AWS.InternetMonitor.Monitor("AppMonitor", {
93 * resources: [vpcArn],
94 * maxCityNetworksToMonitor: 100,
95 * });
96 *
97 * yield* AWS.InternetMonitor.consumeHealthEvents(
98 * { monitorArns: [monitor.monitorArn], impactTypes: ["AVAILABILITY"] },
99 * (events) =>
100 * Stream.runForEach(events, (event) =>
101 * Effect.logError(
102 * `Internet Monitor health event: ${event.detail.summary}`,
103 * ),
104 * ),
105 * );
106 * return {};
107 * }).pipe(Effect.provide(AWS.Lambda.EventSource)),
108 * );
109 * ```
110 */
111export const consumeHealthEvents = <StreamReq = never, Req = never>(
112 props: HealthEventSourceProps,
113 process: (
114 events: Stream.Stream<HealthEventRecord, never, StreamReq>,
115 ) => Effect.Effect<void, never, Req>,
116) => {
117 const detail = {
118 ...(props.impactTypes !== undefined
119 ? { impactType: [...props.impactTypes] }
120 : {}),
121 ...(props.statuses !== undefined ? { status: [...props.statuses] } : {}),
122 };
123 return consumeBusEvents(
124 props.id ?? "InternetMonitorHealthEvents",
125 {
126 source: ["aws.internetmonitor"],
127 "detail-type": ["Internet Monitor Health Event"],
128 ...(props.monitorArns !== undefined
129 ? { resources: [...props.monitorArns] }
130 : {}),
131 ...(Object.keys(detail).length > 0 ? { detail } : {}),
132 },
133 { description: props.description, state: props.state },
134 process,
135 );
136};