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 AWS Resource Groups delivers to EventBridge for
11 * group lifecycle events. State-change events describe the group create/
12 * update/delete; membership-change events describe resources entering or
13 * leaving a group. Fields not shared by every event kind are optional (the
14 * schema grows over time).
15 */
16export interface GroupEventDetail {
17 /** Monotonic ordering hint for events about the same group. */
18 "event-sequence"?: number;
19 /** State-change events: the transition, e.g. `create`, `update`, `delete`. */
20 "state-change"?: string;
21 /** The group the event is about (`arn`, `name`, …). */
22 group?: {
23 arn?: string;
24 name?: string;
25 [key: string]: unknown;
26 };
27 /** Membership-change events: the resources that entered or left the group. */
28 resources?: {
29 arn?: string;
30 "membership-change"?: string;
31 [key: string]: unknown;
32 }[];
33 /** Additional event fields (the schema grows over time). */
34 [key: string]: unknown;
35}
36
37/** A Resource Groups EventBridge event delivered to the handler. */
38export type GroupEvent = EventRecord<GroupEventDetail>;
39
40/** Which Resource Groups group lifecycle notifications to subscribe to. */
41export type GroupEventKind = "state-change" | "membership-change";
42
43const DETAIL_TYPES: Record<GroupEventKind, string> = {
44 "state-change": "ResourceGroups Group State Change",
45 "membership-change": "ResourceGroups Group Membership Change",
46};
47
48export interface GroupEventSourceProps extends EventRouteProps {
49 /**
50 * Logical id for the backing EventBridge rule.
51 * @default "ResourceGroupsGroupEvents"
52 */
53 id?: string;
54 /**
55 * Which notifications to subscribe to: group state changes (create /
56 * update / delete) and/or membership changes (resources entering or
57 * leaving a group).
58 * @default ["state-change", "membership-change"]
59 */
60 kinds?: readonly GroupEventKind[];
61 /**
62 * Restrict to events about specific groups (matched against the event's
63 * top-level `resources`, which carries the group ARN).
64 */
65 groupArns?: readonly string[];
66}
67
68/**
69 * Event source connecting AWS Resource Groups group lifecycle events to
70 * the hosting compute. When the account setting `GroupLifecycleEvents` is
71 * `ACTIVE` (one-time opt-in via `UpdateAccountSettings`; check with the
72 * {@link GetAccountSettings} binding), Resource Groups publishes group
73 * state changes and membership changes to the account's default
74 * EventBridge bus (source `aws.resource-groups`); this subscribes the host
75 * Function to those events so it can react to resources entering or
76 * leaving a group.
77 *
78 * Resource Groups publishes to EventBridge automatically once the account
79 * setting is active — no additional resource is created besides the
80 * EventBridge rule targeting the host. Provide the host-specific
81 * implementation layer (e.g. `AWS.Lambda.EventSource`) on the Function
82 * effect.
83 *
84 * ### Consuming Group Lifecycle Events
85 * **Example:** React To Membership Changes
86 * ```typescript
87 * import * as AWS from "alchemy/AWS";
88 *
89 * export default MembershipFunction.make(
90 * { main: import.meta.url },
91 * Effect.gen(function* () {
92 * yield* AWS.ResourceGroups.consumeGroupEvents(
93 * { kinds: ["membership-change"] },
94 * (events) =>
95 * Stream.runForEach(events, (event) =>
96 * Effect.log(
97 * `group ${event.detail.group?.name} membership changed`,
98 * ),
99 * ),
100 * );
101 * return {};
102 * }).pipe(Effect.provide(AWS.Lambda.EventSource)),
103 * );
104 * ```
105 */
106export const consumeGroupEvents = <StreamReq = never, Req = never>(
107 props: GroupEventSourceProps,
108 process: (
109 events: Stream.Stream<GroupEvent, never, StreamReq>,
110 ) => Effect.Effect<void, never, Req>,
111) =>
112 consumeBusEvents(
113 props.id ?? "ResourceGroupsGroupEvents",
114 {
115 source: ["aws.resource-groups"],
116 "detail-type": (
117 props.kinds ?? (["state-change", "membership-change"] as const)
118 ).map((kind) => DETAIL_TYPES[kind]),
119 ...(props.groupArns !== undefined
120 ? { resources: [...props.groupArns] }
121 : {}),
122 },
123 { description: props.description, state: props.state },
124 process,
125 );