-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidateInput.ts
More file actions
82 lines (79 loc) · 2.34 KB
/
validateInput.ts
File metadata and controls
82 lines (79 loc) · 2.34 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
import type { MiddlewareObj } from '@middy/core'
import {
formatTypeBoxErrors,
validateWithTypeBox,
} from '@nrfcloud/validate-with-typebox'
import type { Static, StaticDecode, TSchema } from '@sinclair/typebox'
import type { ValueError } from '@sinclair/typebox/compiler'
import { Value } from '@sinclair/typebox/value'
import type {
APIGatewayProxyEventV2,
APIGatewayProxyStructuredResultV2,
Context,
} from 'aws-lambda'
import { parseHeaders } from './parseHeaders.ts'
import { tryAsJSON } from './tryAsJSON.ts'
export class ValidationFailedError extends Error {
public readonly errors: ValueError[]
constructor(errors: ValueError[], message = 'Validation failed') {
super(message)
this.errors = errors
this.name = 'ValidationFailedError'
}
}
export const validateInput = <Schema extends TSchema>(
schema: Schema,
mapInput?: (e: APIGatewayProxyEventV2) => unknown,
): MiddlewareObj<
APIGatewayProxyEventV2,
APIGatewayProxyStructuredResultV2,
Error,
Context & ValidInput<Schema>
> => {
const v = validateWithTypeBox(schema)
return {
before: async (req) => {
let reqBody = {}
const headers = parseHeaders(req.event.headers)
const contentType = headers.get('content-type') ?? ''
if (
contentType.includes('application/json') &&
(req.event.body?.length ?? 0) >= 2 // should be at least '{}'
) {
reqBody = tryAsJSON(req.event.body) ?? {}
}
const input = mapInput?.(req.event) ?? {
...(req.event.pathParameters ?? {}),
...(req.event.queryStringParameters ?? {}),
...reqBody,
}
console.debug(`[validateInput]`, 'input', JSON.stringify(input))
const maybeValidInput = v(input)
if ('errors' in maybeValidInput) {
console.debug(
`[validateInput]`,
`Input not valid`,
JSON.stringify({
input,
errors: formatTypeBoxErrors(maybeValidInput.errors),
}),
schema.title,
)
throw new ValidationFailedError(maybeValidInput.errors)
}
console.debug(`[validateInput]`, `Input is valid`, schema.title)
req.context.validInput = maybeValidInput.value
req.context.decodedInput = Value.Decode(schema, maybeValidInput.value)
return undefined
},
}
}
export type ValidInput<Schema extends TSchema> = {
/**
* @deprecated Use `decodedInput` instead
*
* This will be removed in the next major version.
*/
validInput: Static<Schema>
decodedInput: StaticDecode<Schema>
}