-
Notifications
You must be signed in to change notification settings - Fork 3
/
handler-builder.ts
166 lines (155 loc) · 5.29 KB
/
handler-builder.ts
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/**
* @module root
*/
import {
argValidator as _argValidator,
schemaHelper as _schemaHelper
} from '@vamship/arg-utils';
import _configProvider from '@vamship/config';
import _loggerProvider from '@vamship/logger';
import { Promise } from 'bluebird';
import _dotProp from 'dot-prop';
import { Handler, NextFunction, Request, Response } from 'express';
import {
IInput,
InputMapper,
OutputMapper,
RequestHandler
} from './handler-types';
/**
* Class that can be used to build HTTP request handlers for express js.
* Breaks down requests into three distinct phases:
*
* (1) Request mapping: Generate a JSON object from the incoming HTTP request
*
* (2) Request processing: Process the JSON object and return a response
*
* (3) Response mapping: Generate an HTTP response based on the JSON response
*/
export default class HandlerBuilder {
private static DEFAULT_INPUT_MAPPER(): IInput {
return {};
}
private static DEFAULT_OUTPUT_MAPPER(data: any, res: Response) {
res.json(data);
}
private _inputMapper: InputMapper;
private _handler: RequestHandler;
private _outputMapper: OutputMapper;
private _handlerName: string;
private _schema?: {};
/**
* @param handler The request handler function
* @param handlerName An identifying string for the handler
*/
constructor(handlerName: string, handler: RequestHandler) {
_argValidator.checkString(
handlerName,
1,
'handlerName cannot be empty (arg #1)'
);
this._handler = handler;
this._handlerName = handlerName;
this._schema = undefined;
this._inputMapper = HandlerBuilder.DEFAULT_INPUT_MAPPER;
this._outputMapper = HandlerBuilder.DEFAULT_OUTPUT_MAPPER;
}
/**
* Builds a request handler function that can be assigned to expressjs
* routes.
*
* @return An expressjs request handler.
*/
public build(): Handler {
const logger = _loggerProvider.getLogger('handler-builder', {
request: this._handlerName
});
const config = _configProvider.getConfig();
const schemaChecker = this._schema
? _schemaHelper.createSchemaChecker(this._schema)
: () => {
logger.trace('No schema validations required');
return true;
};
return (req: Request, res: Response, next: NextFunction) => {
Promise.try(() => {
logger.trace('Mapping request to input object');
const input = this._inputMapper(req);
logger.trace('Performing schema validation', { input });
schemaChecker(input, true);
logger.trace('Invoking handler', { input });
return this._handler(
input,
{},
{
logger,
config,
alias: process.env.NODE_ENV || 'default'
}
);
})
.then((data) => {
logger.trace('Handler execution completed', { data });
return this._outputMapper(data, res, next);
})
.catch((ex) => {
next(ex);
});
};
}
/**
* Sets the input mapping for the handler.
*
* @param mapping A mapping function that maps the HTTP request to an input
* object, or, a map that maps input properties to the corresponding
* values from the HTTP request. Supported mapping values include:
* 1. params: Maps values from req.params to the input
* 2. body: Maps values from req.body to the input
*
* @returns A reference to the handler builder, to be used for function
* chaining.
*/
public setInputMapper(
mapping: { [prop: string]: string } | InputMapper
): HandlerBuilder {
if (typeof mapping === 'function') {
this._inputMapper = mapping;
} else {
this._inputMapper = (req: Request) => {
return Object.keys(mapping).reduce((result, prop) => {
const path = mapping[prop];
const value = _dotProp.get(req, path);
_dotProp.set(result, prop, value);
return result;
}, {});
};
}
return this;
}
/**
* Sets the schema to be used when validating mapped input objects.
*
* @param schema A JSON schema object that can be used to validate the
* mapped input.
*
* @returns A reference to the handler builder, to be used for function
* chaining.
*/
public setSchema(schema: {}): HandlerBuilder {
this._schema = schema;
return this;
}
/**
* Sets the output mapping for the handler.
*
* @param mapping A mapping function that maps the output of the handler
* function to an HTTP response object.
*
* @returns A reference to the handler builder, to be used for function
* chaining.
*/
public setOutputMapper(mapping: OutputMapper): HandlerBuilder {
this._outputMapper = mapping;
return this;
}
}