Skip to content

Commit fa72189

Browse files
committed
consolidateed tests
1 parent f91d95f commit fa72189

File tree

8 files changed

+618
-783
lines changed

8 files changed

+618
-783
lines changed

packages/event-handler/src/http/Router.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ import type {
2323
ErrorConstructor,
2424
ErrorHandler,
2525
ErrorResolveOptions,
26+
HandlerOrOptions,
2627
HttpMethod,
2728
HttpResolveOptions,
2829
HttpRouteOptions,
2930
HttpRouterOptions,
3031
Middleware,
32+
MiddlewareOrHandler,
3133
Path,
3234
RequestContext,
3335
ResolveStreamOptions,
@@ -71,19 +73,6 @@ import {
7173
resolvePrefixedPath,
7274
} from './utils.js';
7375

74-
type MiddlewareOrHandler<
75-
TReqBody = never,
76-
TResBody extends HandlerResponse = HandlerResponse,
77-
> = Middleware[] | RouteHandler | TypedRouteHandler<TReqBody, TResBody>;
78-
79-
type HandlerOrOptions<
80-
TReqBody = never,
81-
TResBody extends HandlerResponse = HandlerResponse,
82-
> =
83-
| RouteHandler
84-
| TypedRouteHandler<TReqBody, TResBody>
85-
| { validation: ValidationConfig<TReqBody, TResBody> };
86-
8776
class Router {
8877
protected context: Record<string, unknown>;
8978

packages/event-handler/src/http/errors.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { JSONValue } from '@aws-lambda-powertools/commons/types';
2+
import { isDevMode } from '@aws-lambda-powertools/commons/utils/env';
23
import type { HandlerResponse, HttpStatusCode } from '../types/http.js';
34
import { HttpStatusCodes } from './constants.js';
45

@@ -197,9 +198,8 @@ class RequestValidationError extends HttpError {
197198
public readonly originalError?: Error,
198199
details?: Record<string, unknown>
199200
) {
200-
const isDev = process.env.POWERTOOLS_DEV === 'true';
201201
const errorDetails =
202-
isDev && originalError
202+
isDevMode() && originalError
203203
? { ...details, validationError: originalError.message }
204204
: details;
205205

@@ -218,9 +218,8 @@ class ResponseValidationError extends HttpError {
218218
public readonly originalError?: Error,
219219
details?: Record<string, unknown>
220220
) {
221-
const isDev = process.env.POWERTOOLS_DEV === 'true';
222221
const errorDetails =
223-
isDev && originalError
222+
isDevMode() && originalError
224223
? { ...details, validationError: originalError.message }
225224
: details;
226225

packages/event-handler/src/http/middleware/validation.ts

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -48,41 +48,40 @@ async function validateRequestData<TReqBody>(
4848
reqSchemas: NonNullable<ValidationConfig<TReqBody, HandlerResponse>['req']>
4949
): Promise<void> {
5050
if (reqSchemas.body) {
51-
const bodyData = await extractRequestBody(typedReqCtx.req);
52-
const validatedBody = await validateRequest(
51+
const bodyData = await extractBody(typedReqCtx.req);
52+
typedReqCtx.valid.req.body = await validateRequest<TReqBody>(
5353
reqSchemas.body,
5454
bodyData,
5555
'body'
5656
);
57-
typedReqCtx.valid.req.body = validatedBody as TReqBody;
5857
}
5958

6059
if (reqSchemas.headers) {
6160
const headers = Object.fromEntries(typedReqCtx.req.headers.entries());
62-
typedReqCtx.valid.req.headers = (await validateRequest(
61+
typedReqCtx.valid.req.headers = await validateRequest(
6362
reqSchemas.headers,
6463
headers,
6564
'headers'
66-
)) as Record<string, string>;
65+
);
6766
}
6867

6968
if (reqSchemas.path) {
70-
typedReqCtx.valid.req.path = (await validateRequest(
69+
typedReqCtx.valid.req.path = await validateRequest(
7170
reqSchemas.path,
7271
typedReqCtx.params,
7372
'path'
74-
)) as Record<string, string>;
73+
);
7574
}
7675

7776
if (reqSchemas.query) {
7877
const query = Object.fromEntries(
7978
new URL(typedReqCtx.req.url).searchParams.entries()
8079
);
81-
typedReqCtx.valid.req.query = (await validateRequest(
80+
typedReqCtx.valid.req.query = await validateRequest(
8281
reqSchemas.query,
8382
query,
8483
'query'
85-
)) as Record<string, string>;
84+
);
8685
}
8786
}
8887

@@ -93,53 +92,65 @@ async function validateResponseData<TResBody extends HandlerResponse>(
9392
const response = typedReqCtx.res;
9493

9594
if (resSchemas.body && response.body) {
96-
const bodyData = await extractResponseBody(response);
97-
typedReqCtx.valid.res.body = (await validateResponse(
95+
const bodyData = await extractBody(response);
96+
typedReqCtx.valid.res.body = await validateResponse<TResBody>(
9897
resSchemas.body,
9998
bodyData,
10099
'body'
101-
)) as TResBody;
100+
);
102101
}
103102

104103
if (resSchemas.headers) {
105104
const headers = Object.fromEntries(response.headers.entries());
106-
typedReqCtx.valid.res.headers = (await validateResponse(
105+
typedReqCtx.valid.res.headers = await validateResponse(
107106
resSchemas.headers,
108107
headers,
109108
'headers'
110-
)) as Record<string, string>;
109+
);
111110
}
112111
}
113112

114-
async function extractRequestBody(req: Request): Promise<unknown> {
115-
const clonedRequest = req.clone();
116-
const contentType = req.headers.get('content-type');
113+
async function extractBody(source: Request | Response): Promise<unknown> {
114+
const cloned = source.clone();
115+
const contentType = source.headers.get('content-type');
117116

118117
if (contentType?.includes('application/json')) {
119118
try {
120-
return await clonedRequest.json();
119+
return await cloned.json();
121120
} catch {
122-
return await req.clone().text();
121+
if (source instanceof Request) {
122+
throw new RequestValidationError(
123+
'Validation failed for request body',
124+
'body',
125+
new Error('Invalid JSON')
126+
);
127+
}
128+
throw new ResponseValidationError(
129+
'Validation failed for response body',
130+
'body',
131+
new Error('Invalid JSON')
132+
);
123133
}
124134
}
125135

126-
return await clonedRequest.text();
127-
}
128-
129-
async function extractResponseBody(response: Response): Promise<unknown> {
130-
const clonedResponse = response.clone();
131-
const contentType = response.headers.get('content-type');
132-
133-
return contentType?.includes('application/json')
134-
? await clonedResponse.json()
135-
: await clonedResponse.text();
136+
return await cloned.text();
136137
}
137138

139+
async function validateRequest<T>(
140+
schema: StandardSchemaV1,
141+
data: unknown,
142+
component: 'body'
143+
): Promise<T>;
138144
async function validateRequest(
145+
schema: StandardSchemaV1,
146+
data: unknown,
147+
component: 'headers' | 'path' | 'query'
148+
): Promise<Record<string, string>>;
149+
async function validateRequest<T>(
139150
schema: StandardSchemaV1,
140151
data: unknown,
141152
component: 'body' | 'headers' | 'path' | 'query'
142-
): Promise<unknown> {
153+
): Promise<T | Record<string, string>> {
143154
const result = await schema['~standard'].validate(data);
144155

145156
if ('issues' in result) {
@@ -148,14 +159,24 @@ async function validateRequest(
148159
throw new RequestValidationError(message, component, error);
149160
}
150161

151-
return result.value;
162+
return result.value as T | Record<string, string>;
152163
}
153164

165+
async function validateResponse<T>(
166+
schema: StandardSchemaV1,
167+
data: unknown,
168+
component: 'body'
169+
): Promise<T>;
154170
async function validateResponse(
171+
schema: StandardSchemaV1,
172+
data: unknown,
173+
component: 'headers'
174+
): Promise<Record<string, string>>;
175+
async function validateResponse<T>(
155176
schema: StandardSchemaV1,
156177
data: unknown,
157178
component: 'body' | 'headers'
158-
): Promise<unknown> {
179+
): Promise<T | Record<string, string>> {
159180
const result = await schema['~standard'].validate(data);
160181

161182
if ('issues' in result) {
@@ -164,5 +185,5 @@ async function validateResponse(
164185
throw new ResponseValidationError(message, component, error);
165186
}
166187

167-
return result.value;
188+
return result.value as T | Record<string, string>;
168189
}

packages/event-handler/src/types/http.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,25 @@ type ValidationErrorDetail = {
362362
message: string;
363363
};
364364

365+
/**
366+
* Union type for middleware array or route handler
367+
*/
368+
type MiddlewareOrHandler<
369+
TReqBody = never,
370+
TResBody extends HandlerResponse = HandlerResponse,
371+
> = Middleware[] | RouteHandler | TypedRouteHandler<TReqBody, TResBody>;
372+
373+
/**
374+
* Union type for route handler or validation options
375+
*/
376+
type HandlerOrOptions<
377+
TReqBody = never,
378+
TResBody extends HandlerResponse = HandlerResponse,
379+
> =
380+
| RouteHandler
381+
| TypedRouteHandler<TReqBody, TResBody>
382+
| { validation: ValidationConfig<TReqBody, TResBody> };
383+
365384
export type {
366385
BinaryResult,
367386
ExtendedAPIGatewayProxyResult,
@@ -403,4 +422,6 @@ export type {
403422
ValidatedRequest,
404423
ValidatedResponse,
405424
TypedRouteHandler,
425+
MiddlewareOrHandler,
426+
HandlerOrOptions,
406427
};

0 commit comments

Comments
 (0)