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

feat: Simplifies error catching. #5

Merged
merged 3 commits into from
Mar 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
},
"dependencies": {
"@js-entity-repos/core": "^6.0.2",
"http-status-codes": "^1.3.0"
"http-status-codes": "^1.3.0",
"uuid": "^3.2.1"
},
"devDependencies": {
"@ht2-labs/semantic-release": "1.0.31",
Expand All @@ -37,6 +38,7 @@
"@types/express": "4.11.1",
"@types/mocha": "2.2.48",
"@types/source-map-support": "0.4.0",
"@types/uuid": "3.4.3",
"assert-rejects": "0.1.1",
"axios": "0.18.0",
"dotenv": "5.0.1",
Expand Down
17 changes: 11 additions & 6 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,17 @@ const todosFacade = factory<TodoEntity>({
},
// Optional property.
defaultPaginationLimit: 10,
// Optional property that catches errors from handlers.
errorCatcher: (handler) => (req, res) => {
handler(req, res).catch((err) => {
res.status(500).send();
});
},
// Optional property to handle transactions.
handleTransaction: async ({ req, res }, handler) => {
// The transactionId allow items found in logs to be matched with responses to users.
const transactionId = uuid();
try {
await handler({ transactionId });
} catch (err) {
console.error({ err, req, res, transactionId})
res.status(500).send(transactionId);
}
};
service,
});
```
4 changes: 2 additions & 2 deletions src/FacadeConfig.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import Facade from '@js-entity-repos/core/dist/Facade';
import Entity from '@js-entity-repos/core/dist/types/Entity';
import Filter from '@js-entity-repos/core/dist/types/Filter';
import ErrorCatcher from './utils/ErrorCatcher';
import TransactionHandler from './utils/TransactionHandler';

export default interface FacadeConfig<E extends Entity> {
readonly constructFilter: (filter: Filter<E>) => any;
readonly service: Facade<E>;
readonly errorCatcher: ErrorCatcher;
readonly handleTransaction: TransactionHandler;
readonly defaultPaginationLimit: number;
}
4 changes: 2 additions & 2 deletions src/FactoryConfig.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import Facade from '@js-entity-repos/core/dist/Facade';
import Entity from '@js-entity-repos/core/dist/types/Entity';
import { Filter } from '@js-entity-repos/core/dist/types/Filter';
import ErrorCatcher from './utils/ErrorCatcher';
import TransactionHandler from './utils/TransactionHandler';

export default interface FactoryConfig<E extends Entity> {
readonly constructFilter?: (filter: Filter<E>) => any;
readonly service: Facade<E>;
readonly errorCatcher?: ErrorCatcher;
readonly handleTransaction?: TransactionHandler;
readonly defaultPaginationLimit?: number;
}
4 changes: 2 additions & 2 deletions src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import patchEntities from './functions/patchEntities';
import removeEntities from './functions/removeEntities';
import removeEntity from './functions/removeEntity';
import replaceEntity from './functions/replaceEntity';
import catchErrors from './utils/catchErrors';
import handleTransaction from './utils/handleTransaction';

export default <E extends Entity>(factoryConfig: FactoryConfig<E>): Router => {
const facadeConfig: FacadeConfig<E> = {
constructFilter: (filter) => filter,
defaultPaginationLimit: 10,
errorCatcher: catchErrors,
handleTransaction,
...factoryConfig,
};
const router = Router();
Expand Down
13 changes: 7 additions & 6 deletions src/functions/countEntities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import Entity from '@js-entity-repos/core/dist/types/Entity';
import { Request, Response } from 'express';
import { OK } from 'http-status-codes';
import FacadeConfig from '../FacadeConfig';
import catchErrors from '../utils/catchErrors';
import getJsonQueryParam from '../utils/getJsonQueryParam';

export default <E extends Entity>(config: FacadeConfig<E>) => {
return catchErrors(async (req: Request, res: Response) => {
const { count } = await config.service.countEntities({
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
return async (req: Request, res: Response) => {
await config.handleTransaction({ req, res }, async () => {
const { count } = await config.service.countEntities({
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
});
res.status(OK).json(count);
});
res.status(OK).json(count);
});
};
};
15 changes: 8 additions & 7 deletions src/functions/createEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import Entity from '@js-entity-repos/core/dist/types/Entity';
import { Request, Response } from 'express';
import { OK } from 'http-status-codes';
import FacadeConfig from '../FacadeConfig';
import catchErrors from '../utils/catchErrors';

export default <E extends Entity>(config: FacadeConfig<E>) => {
return catchErrors(async (req: Request, res: Response) => {
const { entity } = await config.service.createEntity({
entity: req.body,
id: req.body.id,
return async (req: Request, res: Response) => {
await config.handleTransaction({ req, res }, async () => {
const { entity } = await config.service.createEntity({
entity: req.body,
id: req.body.id,
});
res.status(OK).json(entity);
});
res.status(OK).json(entity);
});
};
};
41 changes: 21 additions & 20 deletions src/functions/getEntities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,30 @@ import Entity from '@js-entity-repos/core/dist/types/Entity';
import { Request, Response } from 'express';
import { OK } from 'http-status-codes';
import FacadeConfig from '../FacadeConfig';
import catchErrors from '../utils/catchErrors';
import getJsonQueryParam from '../utils/getJsonQueryParam';
import getNumberQueryParam from '../utils/getNumberQueryParam';

export default <E extends Entity>(config: FacadeConfig<E>) => {
return catchErrors(async (req: Request, res: Response) => {
const limit = getNumberQueryParam(req.query, 'limit', config.defaultPaginationLimit);
const result = await config.service.getEntities({
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
pagination: {
cursor: req.query.cursor,
forward: req.query.forward === 'true',
limit,
},
sort: getJsonQueryParam(req.query, 'sort'),
return async (req: Request, res: Response) => {
await config.handleTransaction({ req, res }, async () => {
const limit = getNumberQueryParam(req.query, 'limit', config.defaultPaginationLimit);
const result = await config.service.getEntities({
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
pagination: {
cursor: req.query.cursor,
forward: req.query.forward === 'true',
limit,
},
sort: getJsonQueryParam(req.query, 'sort'),
});
res.status(OK);
if (result.nextCursor !== undefined) {
res.setHeader('x-entities-next-cursor', result.nextCursor);
}
if (result.previousCursor !== undefined) {
res.setHeader('x-entities-previous-cursor', result.previousCursor);
}
res.json(result.entities);
});
res.status(OK);
if (result.nextCursor !== undefined) {
res.setHeader('x-entities-next-cursor', result.nextCursor);
}
if (result.previousCursor !== undefined) {
res.setHeader('x-entities-previous-cursor', result.previousCursor);
}
res.json(result.entities);
});
};
};
15 changes: 8 additions & 7 deletions src/functions/getEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import Entity from '@js-entity-repos/core/dist/types/Entity';
import { Request, Response } from 'express';
import { OK } from 'http-status-codes';
import FacadeConfig from '../FacadeConfig';
import catchErrors from '../utils/catchErrors';
import getJsonQueryParam from '../utils/getJsonQueryParam';

export default <E extends Entity>(config: FacadeConfig<E>) => {
return catchErrors(async (req: Request, res: Response) => {
const { entity } = await config.service.getEntity({
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
id: req.params.id,
return async (req: Request, res: Response) => {
await config.handleTransaction({ req, res }, async () => {
const { entity } = await config.service.getEntity({
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
id: req.params.id,
});
res.status(OK).json(entity);
});
res.status(OK).json(entity);
});
};
};
17 changes: 9 additions & 8 deletions src/functions/patchEntities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import Entity from '@js-entity-repos/core/dist/types/Entity';
import { Request, Response } from 'express';
import { OK } from 'http-status-codes';
import FacadeConfig from '../FacadeConfig';
import catchErrors from '../utils/catchErrors';
import getJsonQueryParam from '../utils/getJsonQueryParam';

export default <E extends Entity>(config: FacadeConfig<E>) => {
return catchErrors(async (req: Request, res: Response) => {
const { entity } = await config.service.patchEntity({
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
id: req.params.id,
patch: req.body,
return async (req: Request, res: Response) => {
await config.handleTransaction({ req, res }, async () => {
const { entity } = await config.service.patchEntity({
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
id: req.params.id,
patch: req.body,
});
res.status(OK).json(entity);
});
res.status(OK).json(entity);
});
};
};
13 changes: 7 additions & 6 deletions src/functions/removeEntities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import Entity from '@js-entity-repos/core/dist/types/Entity';
import { Request, Response } from 'express';
import { NO_CONTENT } from 'http-status-codes';
import FacadeConfig from '../FacadeConfig';
import catchErrors from '../utils/catchErrors';
import getJsonQueryParam from '../utils/getJsonQueryParam';

export default <E extends Entity>(config: FacadeConfig<E>) => {
return catchErrors(async (req: Request, res: Response) => {
await config.service.removeEntities({
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
return async (req: Request, res: Response) => {
await config.handleTransaction({ req, res }, async () => {
await config.service.removeEntities({
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
});
res.status(NO_CONTENT).send();
});
res.status(NO_CONTENT).send();
});
};
};
15 changes: 8 additions & 7 deletions src/functions/removeEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import Entity from '@js-entity-repos/core/dist/types/Entity';
import { Request, Response } from 'express';
import { NO_CONTENT } from 'http-status-codes';
import FacadeConfig from '../FacadeConfig';
import catchErrors from '../utils/catchErrors';
import getJsonQueryParam from '../utils/getJsonQueryParam';

export default <E extends Entity>(config: FacadeConfig<E>) => {
return catchErrors(async (req: Request, res: Response) => {
await config.service.removeEntity({
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
id: req.params.id,
return async (req: Request, res: Response) => {
await config.handleTransaction({ req, res }, async () => {
await config.service.removeEntity({
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
id: req.params.id,
});
res.status(NO_CONTENT).send();
});
res.status(NO_CONTENT).send();
});
};
};
17 changes: 9 additions & 8 deletions src/functions/replaceEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import Entity from '@js-entity-repos/core/dist/types/Entity';
import { Request, Response } from 'express';
import { OK } from 'http-status-codes';
import FacadeConfig from '../FacadeConfig';
import catchErrors from '../utils/catchErrors';
import getJsonQueryParam from '../utils/getJsonQueryParam';

export default <E extends Entity>(config: FacadeConfig<E>) => {
return catchErrors(async (req: Request, res: Response) => {
const { entity } = await config.service.replaceEntity({
entity: req.body,
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
id: req.params.id,
return async (req: Request, res: Response) => {
await config.handleTransaction({ req, res }, async () => {
const { entity } = await config.service.replaceEntity({
entity: req.body,
filter: config.constructFilter(getJsonQueryParam(req.query, 'filter')),
id: req.params.id,
});
res.status(OK).json(entity);
});
res.status(OK).json(entity);
});
};
};
7 changes: 0 additions & 7 deletions src/utils/ErrorCatcher.ts

This file was deleted.

12 changes: 12 additions & 0 deletions src/utils/ErrorHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Request, Response } from 'express';

export interface Opts {
readonly req: Request;
readonly res: Response;
readonly err: any;
readonly transactionId: string;
}

type ErrorHandler = (opts: Opts) => void;

export default ErrorHandler;
17 changes: 17 additions & 0 deletions src/utils/TransactionHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Response } from 'express';
import { Request } from 'express-serve-static-core';

export interface Opts {
readonly req: Request;
readonly res: Response;
}

export interface HandlerOpts {
readonly transactionId: string;
}

export type Handler = (opts: HandlerOpts) => Promise<void>;

type TransactionHandler = (opts: Opts, handler: Handler) => Promise<void>;

export default TransactionHandler;
Loading