1import type * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import type { Input } from "../../Input.ts";
4import type { EventFilter } from "./Trigger.ts";
5
6/**
7 * A CloudEvent delivered by Eventarc (binary content mode: attributes
8 * arrive as `ce-*` headers, `data` as the body).
9 */
10export interface CloudEvent {
11 /** Event id, unique per source. */
12 id: string;
13 /** CloudEvents `type`, e.g. `google.cloud.storage.object.v1.finalized`. */
14 type: string;
15 /** Event source, e.g. `//storage.googleapis.com/projects/_/buckets/b`. */
16 source: string;
17 /** Subject within the source, e.g. `objects/photo.jpg`. */
18 subject: string | undefined;
19 /** RFC3339 event time. */
20 time: string | undefined;
21 /** Every `ce-*` attribute, without the prefix (includes extensions). */
22 attributes: Record<string, string>;
23 /**
24 * The payload: parsed JSON for JSON content types, a string for
25 * `text/*`, and the raw bytes (`Uint8Array`) otherwise — e.g. Firestore
26 * events, which are `application/protobuf`.
27 */
28 data: unknown;
29}
30
31export interface EventarcEventSourceProps {
32 /**
33 * CloudEvents attribute filters; a `type` filter is required. Every
34 * event must match all filters, e.g.
35 * `[{ attribute: "type", value: "google.cloud.storage.object.v1.finalized" },
36 * { attribute: "bucket", value: bucket.bucketName }]` — values may be
37 * deploy-time Outputs.
38 */
39 eventFilters: Input<EventFilter>[];
40 /**
41 * Eventarc location. Must match the event source's region (the bucket
42 * region for Storage, `global` for Audit Log events of global services).
43 * @default the host's region
44 */
45 location?: string;
46 /**
47 * Payload encoding for events that support more than one, e.g.
48 * `application/json` or `application/protobuf` for Firestore events.
49 * @default `application/protobuf` for Firestore events, otherwise the
50 * event type's default
51 */
52 eventDataContentType?: string;
53 /** Delivery path on the host. Defaults to `/__alchemy/eventarc/{id}`. */
54 path?: string;
55}
56
57export type EventarcEventSourceService = <Req = never>(
58 id: string,
59 props: EventarcEventSourceProps,
60 process: (event: CloudEvent) => Effect.Effect<void, never, Req>,
61) => Effect.Effect<void, never, never>;
62
63/**
64 * Event source routing Eventarc events — Cloud Storage, Audit Log
65 * (`google.cloud.audit.log.v1.written`, i.e. any Google API call),
66 * Firestore, Pub/Sub, and partner channels — to the hosting compute.
67 *
68 * The HTTP implementation is `GCP.Run.EventarcEventSource` for
69 * `GCP.Run.Service` / `GCP.Function` and `GCP.CloudFunctions.Function`:
70 * it creates an Eventarc trigger delivering to the host with the host's
71 * runtime service account as the trigger identity, grants that account
72 * `roles/eventarc.eventReceiver` on the project and `roles/run.invoker` on
73 * the host, and verifies the OIDC token on every delivery. The deploy
74 * blocks until Eventarc reports the trigger healthy.
75 *
76 * Consume it through {@link consumeEvents}.
77 *
78 * ### Consuming Eventarc events
79 * **Example:** Storage finalize events
80 * ```typescript
81 * yield* GCP.Eventarc.consumeEvents(
82 * "Uploads",
83 * {
84 * eventFilters: [
85 * { attribute: "type", value: "google.cloud.storage.object.v1.finalized" },
86 * { attribute: "bucket", value: bucket.bucketName },
87 * ],
88 * },
89 * (event) => Effect.log(`${event.type} ${event.subject}`),
90 * );
91 * // …provided with Effect.provide(GCP.Run.EventarcEventSource)
92 * ```
93 *
94 * @binding
95 * @category Eventarc
96 */
97export interface EventarcEventSource extends Binding.Service<
98 EventarcEventSource,
99 "GCP.Eventarc.EventSource",
100 EventarcEventSourceService
101> {}
102
103export const EventarcEventSource = Binding.Service<EventarcEventSource>(
104 "GCP.Eventarc.EventSource",
105);
106
107/**
108 * Subscribe an Effect handler to Eventarc events matching `eventFilters`.
109 * See {@link EventarcEventSource} for the host implementations.
110 */
111export const consumeEvents = <Req = never>(
112 id: string,
113 props: EventarcEventSourceProps,
114 process: (event: CloudEvent) => Effect.Effect<void, never, Req>,
115): Effect.Effect<void, never, EventarcEventSource> =>
116 EventarcEventSource.use((source) => source(id, props, process));