Skip to content

Commit

Permalink
Consolidated error handling to return message, updated docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Slaven committed Sep 1, 2022
1 parent b9e948b commit 1b159fa
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 20 deletions.
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
coverage
lib
node_modules
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,15 @@ export type TransformValidateOptions = ValidatorOptions & ClassTransformOptions
## Validation and Injection
Behind the scenes **AWS Lambda Handyman** uses `class-validator` for validation, so if any validation goes wrong we
simply return a 400 with the `constraints` of
simply return a 400 with the concatenated `constraints` of
the [ValidationError[]](https://github.com/typestack/class-validator#validation-errors) :
```typescript
class BodyType {
@IsEmail()
email: string
userEmail: string
@IsInt({ message: 'My Custom error message 🥸' })
uuid: string
}

class SpamBot {
Expand All @@ -191,11 +193,13 @@ So if the preceding handler gets called with anything other than a body, contain

The following response is sent:

```json
```text
HTTP/1.1 400 Bad Request
content-type: application/json; charset=utf-8
[{"isEmail": "email must be an email"}]
{
"message":"userEmail must be an email. My Custom error message 🥸."
}
```

If there received request is correct, the decorated property is injected into the method parameter, is ready for use.
Expand Down Expand Up @@ -318,12 +322,12 @@ class SpamBot {

Returns:

```json
```text
HTTP/1.1 500 Internal Server Error
content-type: application/json; charset=utf-8
{
"message": "I've fallen... and I can't get up 🐸"
"message": "I've fallen... and I can't get up 🐸"
}
```

Expand All @@ -341,12 +345,12 @@ class SpamBot {

Which returns:

```json
```text
HTTP/1.1 501 Not Implemented
content-type: application/json; charset=utf-8
{
"message": "Oopsie Doopsie 🐸"
"message": "Oopsie Doopsie 🐸"
}
```

Expand Down Expand Up @@ -459,6 +463,7 @@ class IsBalloonInflated {
# TODO

- [ ] Documentation
- [ ] add optional example
- [ ] http responses
- [ ] http errors
- [ ] Linting
Expand Down
2 changes: 1 addition & 1 deletion jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const config: Config.InitialOptions = {
testEnvironment: 'node',
roots: ['./test'],
collectCoverage: true,
coverageReporters: ['json-summary'],
coverageReporters: ['json-summary', 'text', 'lcov'],
globals: {
'ts-jest': {
tsconfig: 'tsconfig.test.json'
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aws-lambda-handyman",
"version": "0.3.1",
"version": "0.4.0",
"description": "AWS Lambda TypeScript validation made easy 🏄",
"main": "./lib/cjs/src/index.js",
"typings": "./lib/types/src/index.d.ts",
Expand Down
18 changes: 11 additions & 7 deletions src/Decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,37 @@ export const bodyIsNotProperJSON = 'Provided body is not proper JSON 😬'
export const handlerNotAsyncMessage = '⚠️ The methods that you decorate with @Handler, need to be async / need to return a Promise ⚠️ '

export function Event() {
return function (target: Object, propertyKey: string | symbol, parameterIndex: number) {
return (target: Object, propertyKey: string | symbol, parameterIndex: number) => {
Reflect.defineMetadata(eventMetadataKey, parameterIndex, target, propertyKey)
}
}

export function Ctx() {
return function (target: Object, propertyKey: string | symbol, parameterIndex: number) {
return (target: Object, propertyKey: string | symbol, parameterIndex: number) => {
Reflect.defineMetadata(contextMetadataKey, parameterIndex, target, propertyKey)
}
}

export function Paths() {
return function (target: Object, propertyKey: string | symbol, parameterIndex: number) {
return (target: Object, propertyKey: string | symbol, parameterIndex: number) => {
Reflect.defineMetadata(pathsMetadataKey, parameterIndex, target, propertyKey)
}
}

export function Body() {
return function (target: Object, propertyKey: string | symbol, parameterIndex: number) {
return (target: Object, propertyKey: string | symbol, parameterIndex: number) => {
Reflect.defineMetadata(bodyMetadataKey, parameterIndex, target, propertyKey)
}
}

export function Queries() {
return function (target: Object, propertyKey: string | symbol, parameterIndex: number) {
return (target: Object, propertyKey: string | symbol, parameterIndex: number) => {
Reflect.defineMetadata(queriesMetadataKey, parameterIndex, target, propertyKey)
}
}

export function Handler(options?: TransformValidateOptions) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
let method = descriptor.value

descriptor.value = function () {
Expand Down Expand Up @@ -107,7 +107,7 @@ export function Handler(options?: TransformValidateOptions) {
return internalServerError({ message })
})
} catch (e) {
if (e instanceof Array && e?.[0] instanceof ValidationError) return badRequest(e.map((err) => err.constraints))
if (e instanceof Array && e?.[0] instanceof ValidationError) return badRequest({ message: validationErrorsToMessage(e) })

console.error('Oops 😬', e)
if (e instanceof TypeError && e.message === `Cannot read properties of undefined (reading 'catch')`) throw new Error(handlerNotAsyncMessage)
Expand All @@ -124,3 +124,7 @@ function transformValidateOrReject<T extends object, V extends object>(cls: Clas

return instance
}

function validationErrorsToMessage(errors: ValidationError[]) {
return errors.map((e) => Object.values(e.constraints || []).join(', ')).join('. ') + '.'
}
4 changes: 1 addition & 3 deletions test/Decorators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from '../src'
import * as mockData from '../test/mock/httpEventContext.json'
import { APIGatewayEventDefaultAuthorizerContext, APIGatewayProxyEventBase, Context } from 'aws-lambda'
import { IsBoolean, IsEmail, IsFQDN, IsHexColor, IsInt, IsSemVer, IsUUID } from 'class-validator'
import { IsBoolean, IsEmail, IsHexColor, IsInt, IsSemVer, IsUUID } from 'class-validator'
import * as ct from 'class-transformer'
import { Type } from 'class-transformer'

Expand Down Expand Up @@ -312,8 +312,6 @@ test('Handler has Queries param, and is called with unexpected payload', async (
version: string
@IsUUID()
uuid: string
@IsFQDN()
fqdn: boolean
}

class HandlerTest {
Expand Down

0 comments on commit 1b159fa

Please sign in to comment.