1import * as Effect from "effect/Effect";
2import type * as Stream from "effect/Stream";
3import type { EventBus } from "../EventBridge/EventBus.ts";
4import {
5 consumeBusEvents,
6 type EventRecord,
7 type EventRouteProps,
8} from "../EventBridge/EventSource.ts";
9import { EventIntegration } from "./EventIntegration.ts";
10
11/** A partner event delivered to the handler through an event integration. */
12export type IntegrationEvent<Detail = unknown> = EventRecord<Detail>;
13
14export interface IntegrationEventsProps {
15 /**
16 * Physical name for the event integration. If omitted, a deterministic
17 * name is generated from the app, stage, and logical ID.
18 */
19 name?: string;
20 /**
21 * Description of the event integration (1-1000 characters).
22 */
23 description?: string;
24 /**
25 * The partner event source that pushes events to the EventBridge bus,
26 * e.g. `aws.partner/examplepartner.com`. Also used as the literal
27 * EventBridge rule pattern (`source`), so it must be a plain string —
28 * Output values cannot appear in event patterns because the pattern is
29 * also matched inside the deployed bundle at runtime.
30 */
31 source: string;
32 /**
33 * The EventBridge bus the partner events are delivered to. Omit to use
34 * the account's default event bus.
35 */
36 bus?: EventBus;
37 /**
38 * Additional `detail-type` values to narrow the EventBridge rule pattern.
39 * By default every event from `source` is delivered.
40 */
41 detailType?: string[];
42 /**
43 * Options forwarded to the backing EventBridge rule (description, state).
44 */
45 rule?: EventRouteProps;
46 /**
47 * Tags to apply to the event integration. Merged with internal Alchemy
48 * tags.
49 */
50 tags?: Record<string, string>;
51}
52
53/**
54 * Consume partner events on the host Function through an AppIntegrations
55 * {@link EventIntegration}. Creates the event integration (the metadata that
56 * connects the partner source to the EventBridge bus) and subscribes the
57 * host Function to the matching events via an EventBridge rule.
58 *
59 * Returns the created {@link EventIntegration} so it can be passed to other
60 * bindings (e.g. `ListEventIntegrationAssociations`).
61 *
62 * ### Consuming Integration Events
63 * **Example:** Consume Partner Events on a Lambda Function
64 * ```typescript
65 * import * as AWS from "alchemy/AWS";
66 * import { consumeIntegrationEvents } from "alchemy/AWS/AppIntegrations";
67 *
68 * // init — creates the EventIntegration + EventBridge rule and registers
69 * // the runtime handler (provide AWS.Lambda.EventSource on the Function)
70 * const integration = yield* consumeIntegrationEvents(
71 * "PartnerEvents",
72 * { source: "aws.partner/examplepartner.com" },
73 * (events) =>
74 * Stream.runForEach(events, (event) =>
75 * Effect.log(event["detail-type"], event.detail),
76 * ),
77 * );
78 * ```
79 *
80 * **Example:** Narrow by Detail Type on a Custom Bus
81 * ```typescript
82 * const bus = yield* AWS.EventBridge.EventBus("PartnerBus");
83 * yield* consumeIntegrationEvents(
84 * "PartnerEvents",
85 * {
86 * source: "aws.partner/examplepartner.com",
87 * bus,
88 * detailType: ["OrderCreated"],
89 * },
90 * (events) => Stream.runForEach(events, handleOrder),
91 * );
92 * ```
93 */
94export const consumeIntegrationEvents = <
95 Detail = unknown,
96 StreamReq = never,
97 Req = never,
98>(
99 id: string,
100 props: IntegrationEventsProps,
101 process: (
102 events: Stream.Stream<IntegrationEvent<Detail>, never, StreamReq>,
103 ) => Effect.Effect<void, never, Req>,
104) =>
105 Effect.gen(function* () {
106 // Persist the integration metadata connecting the partner source to the
107 // EventBridge bus.
108 const integration = yield* EventIntegration(id, {
109 name: props.name,
110 description: props.description,
111 source: props.source,
112 eventBridgeBus: props.bus ? props.bus.eventBusName : "default",
113 tags: props.tags,
114 });
115
116 // Subscribe the host Function to the matching partner events. The
117 // pattern uses only literal values so it round-trips through both the
118 // deploy-time rule and the runtime matcher.
119 const pattern = {
120 source: [props.source],
121 ...(props.detailType ? { "detail-type": props.detailType } : {}),
122 };
123 yield* props.bus
124 ? consumeBusEvents(
125 `${id}-Events`,
126 props.bus,
127 pattern,
128 props.rule,
129 process,
130 )
131 : consumeBusEvents(`${id}-Events`, pattern, props.rule, process);
132
133 return integration;
134 });