1import type * as Effect from "effect/Effect";
2import type * as Stream from "effect/Stream";
3import {
4 consumeBusEvents,
5 type EventRecord,
6 type EventRouteProps,
7} from "../EventBridge/EventSource.ts";
8
9/**
10 * The `detail` payload Amazon Data Lifecycle Manager delivers to
11 * EventBridge. State-change events describe the transition (`state`,
12 * `cause`, `policy_id`); pre/post script events describe the script
13 * execution outcome. Fields not shared by every event kind are optional
14 * (the schema grows over time).
15 */
16export interface LifecyclePolicyEventDetail {
17 /**
18 * State-change events: the state the policy transitioned to, e.g.
19 * `ERROR` when the execution role was deleted out-of-band.
20 */
21 state?: string;
22 /** State-change events: why the policy changed state. */
23 cause?: string;
24 /** The id or ARN of the lifecycle policy the event is about. */
25 policy_id?: string;
26 /** Additional event fields (the schema grows over time). */
27 [key: string]: unknown;
28}
29
30/** A Data Lifecycle Manager EventBridge event delivered to the handler. */
31export type LifecyclePolicyEvent = EventRecord<LifecyclePolicyEventDetail>;
32
33/** Which Data Lifecycle Manager notifications to subscribe to. */
34export type LifecyclePolicyEventKind = "state-change" | "pre-post-script";
35
36const DETAIL_TYPES: Record<LifecyclePolicyEventKind, string> = {
37 "state-change": "DLM Policy State Change",
38 "pre-post-script": "DLM Pre Post Script Notification",
39};
40
41export interface LifecyclePolicyEventSourceProps extends EventRouteProps {
42 /**
43 * Logical id for the backing EventBridge rule.
44 * @default "DLMPolicyEvents"
45 */
46 id?: string;
47 /**
48 * Which notifications to subscribe to: policy state changes (e.g. a
49 * policy entering `ERROR`) and/or pre/post script execution reports.
50 * @default ["state-change"]
51 */
52 kinds?: readonly LifecyclePolicyEventKind[];
53 /**
54 * Restrict to events about specific lifecycle policies (matched against
55 * the event's top-level `resources`, which includes the policy's ARN).
56 */
57 policyArns?: readonly string[];
58}
59
60/**
61 * Event source connecting Amazon Data Lifecycle Manager notifications to
62 * the hosting compute. DLM publishes lifecycle policy state changes (most
63 * importantly a policy dropping into `ERROR` and no longer creating
64 * snapshots) and pre/post script execution reports to the account's
65 * default EventBridge bus (source `aws.dlm`); this subscribes the host
66 * Function to those events so it can alert on stalled backup policies.
67 *
68 * DLM publishes to EventBridge automatically — no additional resource is
69 * created besides the EventBridge rule targeting the host. Provide the
70 * host-specific implementation layer (e.g. `AWS.Lambda.EventSource`) on
71 * the Function effect.
72 *
73 * ### Consuming Lifecycle Policy Events
74 * **Example:** Alert When A Policy Stops Running
75 * ```typescript
76 * import * as AWS from "alchemy/AWS";
77 *
78 * export default AlertFunction.make(
79 * { main: import.meta.url },
80 * Effect.gen(function* () {
81 * yield* AWS.DLM.consumePolicyEvents(
82 * { kinds: ["state-change"] },
83 * (events) =>
84 * Stream.runForEach(events, (event) =>
85 * event.detail.state === "ERROR"
86 * ? Effect.logError(
87 * `DLM policy ${event.detail.policy_id} failed: ${event.detail.cause}`,
88 * )
89 * : Effect.void,
90 * ),
91 * );
92 * return {};
93 * }).pipe(Effect.provide(AWS.Lambda.EventSource)),
94 * );
95 * ```
96 */
97export const consumePolicyEvents = <StreamReq = never, Req = never>(
98 props: LifecyclePolicyEventSourceProps,
99 process: (
100 events: Stream.Stream<LifecyclePolicyEvent, never, StreamReq>,
101 ) => Effect.Effect<void, never, Req>,
102) =>
103 consumeBusEvents(
104 props.id ?? "DLMPolicyEvents",
105 {
106 source: ["aws.dlm"],
107 "detail-type": (props.kinds ?? (["state-change"] as const)).map(
108 (kind) => DETAIL_TYPES[kind],
109 ),
110 ...(props.policyArns !== undefined
111 ? { resources: [...props.policyArns] }
112 : {}),
113 },
114 { description: props.description, state: props.state },
115 process,
116 );