Skip to content
This repository was archived by the owner on Jun 22, 2021. It is now read-only.

Commit 52fd562

Browse files
committed
feat: Adds error catcher and makes filter and sort optional.
1 parent cfe81a4 commit 52fd562

17 files changed

+95
-27
lines changed

readme.md

+9-10
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
1. Install it with `npm i @js-entity-repos/express`.
66
1. For each entity you will need to do the following.
77
1. [Create an Entity interface](#entity-interface).
8-
1. [Create a factory config](#factory-config).
98
1. [Construct the facade](#construct-the-facade).
109
1. [Use the facade](https://github.com/js-entity-repos/core/blob/master/docs/facade.md).
1110

@@ -22,18 +21,18 @@ export interface TodoEntity extends Entity {
2221
}
2322
```
2423

25-
### Factory Config
26-
27-
```ts
28-
import FactoryConfig from '@js-entity-repos/express/dist/FactoryConfig';
29-
30-
const todoFactoryConfig: FactoryConfig<TodoEntity> = { service };
31-
```
32-
3324
### Construct the Facade
3425

3526
```ts
3627
import factory from '@js-entity-repos/express/dist/factory';
3728

38-
const todosFacade = factory(todoFactoryConfig);
29+
const todosFacade = factory<TodoEntity>({
30+
// Optional property that catches errors from handlers.
31+
errorCatcher: (handler) => (req, res) => {
32+
handler(req, res).catch((err) => {
33+
res.status(500).send();
34+
});
35+
},
36+
service,
37+
});
3938
```

src/FacadeConfig.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import Facade from '@js-entity-repos/core/dist/Facade';
22
import Entity from '@js-entity-repos/core/dist/types/Entity';
3+
import ErrorCatcher from './utils/ErrorCatcher';
34

45
export default interface FacadeConfig<E extends Entity> {
56
readonly service: Facade<E>;
7+
readonly errorCatcher: ErrorCatcher;
68
}

src/FactoryConfig.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import Facade from '@js-entity-repos/core/dist/Facade';
22
import Entity from '@js-entity-repos/core/dist/types/Entity';
3+
import ErrorCatcher from './utils/ErrorCatcher';
34

45
export default interface FactoryConfig<E extends Entity> {
56
readonly service: Facade<E>;
7+
readonly errorCatcher?: ErrorCatcher;
68
}

src/factory.test.ts

+21-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ sourceMapSupport.install();
44
import axiosFactory from '@js-entity-repos/axios/dist/factory';
55
import facadeTest from '@js-entity-repos/core/dist/tests';
66
import { TestEntity } from '@js-entity-repos/core/dist/tests/utils/testEntity';
7+
import * as assert from 'assert';
78
import axios from 'axios';
89
import { config } from 'dotenv';
10+
import { BAD_REQUEST, OK } from 'http-status-codes';
911
import 'mocha'; // tslint:disable-line:no-import-side-effect
1012
import createTestServer from './utils/createTestServer';
1113
config();
@@ -31,9 +33,25 @@ after(async () => {
3133
server.close();
3234
});
3335

36+
const axiosClient = axios.create({
37+
baseURL: `http://localhost:${testServerPort}${testServerRoute}`,
38+
});
39+
3440
facadeTest(axiosFactory<TestEntity>({
35-
axios: axios.create({
36-
baseURL: `http://localhost:${testServerPort}${testServerRoute}`,
37-
}),
41+
axios: axiosClient,
3842
entityName: 'Test Entity',
3943
}));
44+
45+
describe('facade', () => {
46+
it('should not throw JSON error', async () => {
47+
const response = await axiosClient.get('/');
48+
assert.equal(response.status, OK);
49+
});
50+
it('should throw JSON error when using invalid filter', async () => {
51+
await axiosClient.get('/?filter=invalid_json').then((response) => {
52+
return { response };
53+
}).catch((err) => {
54+
assert.equal(err.response.status, BAD_REQUEST);
55+
});
56+
});
57+
});

src/factory.ts

+2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import patchEntities from './functions/patchEntities';
1111
import removeEntities from './functions/removeEntities';
1212
import removeEntity from './functions/removeEntity';
1313
import replaceEntity from './functions/replaceEntity';
14+
import catchErrors from './utils/catchErrors';
1415

1516
export default <E extends Entity>(factoryConfig: FactoryConfig<E>): Router => {
1617
const facadeConfig: FacadeConfig<E> = {
18+
errorCatcher: catchErrors,
1719
...factoryConfig,
1820
};
1921
const router = Router();

src/functions/countEntities.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { Request, Response } from 'express';
33
import { OK } from 'http-status-codes';
44
import FacadeConfig from '../FacadeConfig';
55
import catchErrors from '../utils/catchErrors';
6+
import getJsonQueryParam from '../utils/getJsonQueryParam';
67

78
export default <E extends Entity>(config: FacadeConfig<E>) => {
89
return catchErrors(async (req: Request, res: Response) => {
910
const { count } = await config.service.countEntities({
10-
filter: JSON.parse(req.query.filter),
11+
filter: getJsonQueryParam(req.query, 'filter'),
1112
});
1213
res.status(OK).json(count);
1314
});

src/functions/getEntities.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@ import { Request, Response } from 'express';
33
import { OK } from 'http-status-codes';
44
import FacadeConfig from '../FacadeConfig';
55
import catchErrors from '../utils/catchErrors';
6+
import getJsonQueryParam from '../utils/getJsonQueryParam';
67

78
export default <E extends Entity>(config: FacadeConfig<E>) => {
89
return catchErrors(async (req: Request, res: Response) => {
910
const result = await config.service.getEntities({
10-
filter: JSON.parse(req.query.filter),
11+
filter: getJsonQueryParam(req.query, 'filter'),
1112
pagination: {
1213
cursor: req.query.cursor,
1314
forward: req.query.forward === 'true',
1415
limit: Number(req.query.limit),
1516
},
16-
sort: JSON.parse(req.query.sort),
17+
sort: getJsonQueryParam(req.query, 'sort'),
1718
});
1819
res.status(OK).json(result);
1920
});

src/functions/getEntity.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { Request, Response } from 'express';
33
import { OK } from 'http-status-codes';
44
import FacadeConfig from '../FacadeConfig';
55
import catchErrors from '../utils/catchErrors';
6+
import getJsonQueryParam from '../utils/getJsonQueryParam';
67

78
export default <E extends Entity>(config: FacadeConfig<E>) => {
89
return catchErrors(async (req: Request, res: Response) => {
910
const { entity } = await config.service.getEntity({
10-
filter: JSON.parse(req.query.filter),
11+
filter: getJsonQueryParam(req.query, 'filter'),
1112
id: req.params.id,
1213
});
1314
res.status(OK).json(entity);

src/functions/patchEntities.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { Request, Response } from 'express';
33
import { OK } from 'http-status-codes';
44
import FacadeConfig from '../FacadeConfig';
55
import catchErrors from '../utils/catchErrors';
6+
import getJsonQueryParam from '../utils/getJsonQueryParam';
67

78
export default <E extends Entity>(config: FacadeConfig<E>) => {
89
return catchErrors(async (req: Request, res: Response) => {
910
const { entity } = await config.service.patchEntity({
10-
filter: JSON.parse(req.query.filter),
11+
filter: getJsonQueryParam(req.query, 'filter'),
1112
id: req.params.id,
1213
patch: req.body,
1314
});

src/functions/removeEntities.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { Request, Response } from 'express';
33
import { NO_CONTENT } from 'http-status-codes';
44
import FacadeConfig from '../FacadeConfig';
55
import catchErrors from '../utils/catchErrors';
6+
import getJsonQueryParam from '../utils/getJsonQueryParam';
67

78
export default <E extends Entity>(config: FacadeConfig<E>) => {
89
return catchErrors(async (req: Request, res: Response) => {
910
await config.service.removeEntities({
10-
filter: JSON.parse(req.query.filter),
11+
filter: getJsonQueryParam(req.query, 'filter'),
1112
});
1213
res.status(NO_CONTENT).send();
1314
});

src/functions/removeEntity.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { Request, Response } from 'express';
33
import { NO_CONTENT } from 'http-status-codes';
44
import FacadeConfig from '../FacadeConfig';
55
import catchErrors from '../utils/catchErrors';
6+
import getJsonQueryParam from '../utils/getJsonQueryParam';
67

78
export default <E extends Entity>(config: FacadeConfig<E>) => {
89
return catchErrors(async (req: Request, res: Response) => {
910
await config.service.removeEntity({
10-
filter: JSON.parse(req.query.filter),
11+
filter: getJsonQueryParam(req.query, 'filter'),
1112
id: req.params.id,
1213
});
1314
res.status(NO_CONTENT).send();

src/functions/replaceEntity.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import { Request, Response } from 'express';
33
import { OK } from 'http-status-codes';
44
import FacadeConfig from '../FacadeConfig';
55
import catchErrors from '../utils/catchErrors';
6+
import getJsonQueryParam from '../utils/getJsonQueryParam';
67

78
export default <E extends Entity>(config: FacadeConfig<E>) => {
89
return catchErrors(async (req: Request, res: Response) => {
910
const { entity } = await config.service.replaceEntity({
1011
entity: req.body,
11-
filter: JSON.parse(req.query.filter),
12+
filter: getJsonQueryParam(req.query, 'filter'),
1213
id: req.params.id,
1314
});
1415
res.status(OK).json(entity);

src/utils/ErrorCatcher.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Request, Response } from 'express';
2+
3+
export type Handler = (req: Request, res: Response) => Promise<void>;
4+
5+
type ErrorCatcher = (handler: Handler) => (req: Request, res: Response) => void;
6+
7+
export default ErrorCatcher;

src/utils/JsonSyntaxError.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// tslint:disable:no-class
2+
import { BaseError } from 'make-error';
3+
4+
export default class JsonSyntaxError extends BaseError {
5+
constructor(public path: string[]) {
6+
super();
7+
}
8+
}

src/utils/catchErrors.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
import ConflictingEntityError from '@js-entity-repos/core/dist/errors/ConflictingEntityError';
22
import MissingEntityError from '@js-entity-repos/core/dist/errors/MissingEntityError';
3-
import { Request, Response } from 'express';
4-
import { CONFLICT, INTERNAL_SERVER_ERROR, NOT_FOUND } from 'http-status-codes';
3+
import { BAD_REQUEST, CONFLICT, INTERNAL_SERVER_ERROR, NOT_FOUND } from 'http-status-codes';
4+
import ErrorCatcher from './ErrorCatcher';
5+
import JsonSyntaxError from './JsonSyntaxError';
56

6-
export type Handler = (req: Request, res: Response) => Promise<void>;
7-
8-
export default (handler: Handler) => {
9-
return (req: Request, res: Response) => {
7+
const errorCatcher: ErrorCatcher = (handler) => {
8+
return (req, res) => {
109
handler(req, res).catch((err) => {
1110
if (err instanceof ConflictingEntityError) {
1211
return res.status(CONFLICT).send();
1312
}
1413
if (err instanceof MissingEntityError) {
1514
return res.status(NOT_FOUND).send();
1615
}
16+
if (err instanceof JsonSyntaxError) {
17+
return res.status(BAD_REQUEST).send();
18+
}
1719
/* istanbul ignore next */
1820
return res.status(INTERNAL_SERVER_ERROR).send();
1921
});
2022
};
2123
};
24+
25+
export default errorCatcher;

src/utils/getJsonQueryParam.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import parseJson from './parseJson';
2+
3+
export default (data: { readonly [k: string]: string | undefined }, paramName: string) => {
4+
const paramValue = data[paramName];
5+
return paramValue === undefined ? undefined : parseJson(paramValue, [paramName]);
6+
};

src/utils/parseJson.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import JsonSyntaxError from './JsonSyntaxError';
2+
3+
export default (data: string, path: string[]) => {
4+
try {
5+
return JSON.parse(data);
6+
} catch (err) {
7+
if (err instanceof SyntaxError) {
8+
throw new JsonSyntaxError(path);
9+
}
10+
/* istanbul ignore next */
11+
throw err;
12+
}
13+
};

0 commit comments

Comments
 (0)