-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathon-request.ts
More file actions
131 lines (118 loc) · 3.78 KB
/
on-request.ts
File metadata and controls
131 lines (118 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { Effect, pipe, Schema } from 'effect';
import {
onRequest,
HttpsFunction,
HttpsOptions,
Request,
} from 'firebase-functions/https';
import { type Response } from 'express';
import { run, Runtime } from 'effect-firebase';
import { logger } from 'firebase-functions';
import { parseBody, sendJson } from './on-request-helpers.js';
interface RequestEffectOptions<R> extends HttpsOptions {
runtime: Runtime<R>;
}
interface RequestEffectOptionsWithBody<R, B extends Schema.Top>
extends RequestEffectOptions<R> {
bodySchema: B;
}
interface RequestEffectOptionsWithResponse<R, O extends Schema.Top>
extends RequestEffectOptions<R> {
responseSchema: O;
successStatus?: number;
}
interface RequestEffectOptionsWithBoth<
R,
B extends Schema.Top,
O extends Schema.Top
> extends RequestEffectOptions<R> {
bodySchema: B;
responseSchema: O;
successStatus?: number;
}
/**
* Create a Firebase Functions HTTP trigger that runs an effect.
*
* @param options - The options for the HTTP trigger including optional schemas.
* @param handler - The handler function that runs the effect.
* @returns The Firebase Functions HTTP trigger.
*/
// Overload: both body and response schemas (JSON API endpoint)
export function onRequestEffect<
R,
B extends Schema.Top,
O extends Schema.Top,
E
>(
options: RequestEffectOptionsWithBoth<R, B, O>,
handler: (
body: Schema.Schema.Type<B>,
request: Request,
response: Response
) => Effect.Effect<Schema.Schema.Type<O>, E, R>
): HttpsFunction;
// Overload: only body schema
export function onRequestEffect<R, B extends Schema.Top, E>(
options: RequestEffectOptionsWithBody<R, B>,
handler: (
body: Schema.Schema.Type<B>,
request: Request,
response: Response
) => Effect.Effect<void, E, R>
): HttpsFunction;
// Overload: only response schema
export function onRequestEffect<R, O extends Schema.Top, E>(
options: RequestEffectOptionsWithResponse<R, O>,
handler: (
request: Request,
response: Response
) => Effect.Effect<Schema.Schema.Type<O>, E, R>
): HttpsFunction;
// Overload: no schemas (full control)
export function onRequestEffect<R, E>(
options: RequestEffectOptions<R>,
handler: (request: Request, response: Response) => Effect.Effect<void, E, R>
): HttpsFunction;
// Implementation
export function onRequestEffect<R>(
options: RequestEffectOptions<R> & {
bodySchema?: Schema.Top;
responseSchema?: Schema.Top;
successStatus?: number;
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
handler: (...args: any[]) => Effect.Effect<unknown, unknown, R>
): HttpsFunction {
const { bodySchema, responseSchema, successStatus = 200 } = options;
return onRequest(options, async (request, response) => {
const effect = pipe(
// Step 1: Parse body if schema provided
bodySchema ? parseBody(bodySchema)(request) : Effect.succeed(request),
// Step 2: Run handler with parsed body or raw request
Effect.andThen((bodyOrRequest) => {
if (bodySchema) {
// Handler expects parsed body, request, response
return handler(bodyOrRequest, request, response);
} else {
// Handler expects request, response
return handler(request, response);
}
}),
// Step 3: Send JSON response if schema provided
Effect.andThen((output) =>
responseSchema
? sendJson(response, responseSchema, successStatus)(output)
: Effect.void
)
).pipe(Effect.withSpan('onRequestEffect'));
await run(options.runtime, effect as Effect.Effect<void, never, R>).catch(
(error) => {
logger.error('Defect in onRequest', {
inner: error,
stack: error instanceof Error ? error.stack : undefined,
});
response.status(500).send();
}
);
});
}