@jaypie/express 1.1.18-rc.0 → 1.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/expressHandler.d.ts +8 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/esm/expressHandler.d.ts +8 -4
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js.map +1 -1
- package/package.json +3 -2
- package/dist/cjs/__tests__/constants.spec.d.ts +0 -1
- package/dist/cjs/__tests__/cors.helper.spec.d.ts +0 -1
- package/dist/cjs/__tests__/cors.helper.supertest.spec.d.ts +0 -1
- package/dist/cjs/__tests__/decorateResponse.helper.spec.d.ts +0 -1
- package/dist/cjs/__tests__/echo.handler.spec.d.ts +0 -1
- package/dist/cjs/__tests__/expressHandler.spec.d.ts +0 -1
- package/dist/cjs/__tests__/getCurrentInvokeUuid.adapter.spec.d.ts +0 -1
- package/dist/cjs/__tests__/http.handler.spec.d.ts +0 -1
- package/dist/cjs/__tests__/index.spec.d.ts +0 -1
- package/dist/cjs/__tests__/routes.spec.d.ts +0 -1
- package/dist/cjs/__tests__/summarizeRequest.helper.spec.d.ts +0 -1
- package/dist/cjs/__tests__/summarizeResponse.helper.spec.d.ts +0 -1
- package/dist/cjs/__tests__/supertest.spec.d.ts +0 -1
- package/dist/esm/__tests__/constants.spec.d.ts +0 -1
- package/dist/esm/__tests__/cors.helper.spec.d.ts +0 -1
- package/dist/esm/__tests__/cors.helper.supertest.spec.d.ts +0 -1
- package/dist/esm/__tests__/decorateResponse.helper.spec.d.ts +0 -1
- package/dist/esm/__tests__/echo.handler.spec.d.ts +0 -1
- package/dist/esm/__tests__/expressHandler.spec.d.ts +0 -1
- package/dist/esm/__tests__/getCurrentInvokeUuid.adapter.spec.d.ts +0 -1
- package/dist/esm/__tests__/http.handler.spec.d.ts +0 -1
- package/dist/esm/__tests__/index.spec.d.ts +0 -1
- package/dist/esm/__tests__/routes.spec.d.ts +0 -1
- package/dist/esm/__tests__/summarizeRequest.helper.spec.d.ts +0 -1
- package/dist/esm/__tests__/summarizeResponse.helper.spec.d.ts +0 -1
- package/dist/esm/__tests__/supertest.spec.d.ts +0 -1
- package/dist/module.cjs +0 -718
- package/dist/module.esm.js +0 -705
package/dist/module.esm.js
DELETED
|
@@ -1,705 +0,0 @@
|
|
|
1
|
-
import { CorsError } from '@jaypie/errors';
|
|
2
|
-
import { force, envBoolean, log, JAYPIE, HTTP, validate, getHeaderFrom, jaypieHandler, UnhandledError, GatewayTimeoutError, UnavailableError, BadGatewayError, InternalError, TeapotError, GoneError, MethodNotAllowedError, NotFoundError, ForbiddenError, UnauthorizedError, BadRequestError, NotImplementedError } from '@jaypie/core';
|
|
3
|
-
import expressCors from 'cors';
|
|
4
|
-
import { hasDatadogEnv, submitMetric, DATADOG } from '@jaypie/datadog';
|
|
5
|
-
import { getCurrentInvoke } from '@codegenie/serverless-express';
|
|
6
|
-
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// Constants
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
const EXPRESS = {
|
|
13
|
-
PATH: {
|
|
14
|
-
ANY: "*",
|
|
15
|
-
ID: "/:id",
|
|
16
|
-
ROOT: /^\/?$/,
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// Constants
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
const HTTP_PROTOCOL = "http://";
|
|
26
|
-
const HTTPS_PROTOCOL = "https://";
|
|
27
|
-
const SANDBOX_ENV = "sandbox";
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
// Helper Functions
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
const ensureProtocol = (url) => {
|
|
35
|
-
if (!url) return url;
|
|
36
|
-
if (url.startsWith(HTTP_PROTOCOL) || url.startsWith(HTTPS_PROTOCOL))
|
|
37
|
-
return url;
|
|
38
|
-
return HTTPS_PROTOCOL + url;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const dynamicOriginCallbackHandler = (origin) => {
|
|
42
|
-
return (requestOrigin, callback) => {
|
|
43
|
-
// Handle wildcard origin
|
|
44
|
-
if (origin === "*") {
|
|
45
|
-
callback(null, true);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Allow requests with no origin (like mobile apps, curl, etc)
|
|
50
|
-
if (!requestOrigin) {
|
|
51
|
-
callback(null, true);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const allowedOrigins = [];
|
|
56
|
-
if (process.env.BASE_URL) {
|
|
57
|
-
allowedOrigins.push(ensureProtocol(process.env.BASE_URL));
|
|
58
|
-
}
|
|
59
|
-
if (process.env.PROJECT_BASE_URL) {
|
|
60
|
-
allowedOrigins.push(ensureProtocol(process.env.PROJECT_BASE_URL));
|
|
61
|
-
}
|
|
62
|
-
if (origin) {
|
|
63
|
-
const additionalOrigins = force.array(origin);
|
|
64
|
-
allowedOrigins.push(...additionalOrigins);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Add localhost origins in sandbox
|
|
68
|
-
if (
|
|
69
|
-
process.env.PROJECT_ENV === SANDBOX_ENV ||
|
|
70
|
-
envBoolean("PROJECT_SANDBOX_MODE")
|
|
71
|
-
) {
|
|
72
|
-
allowedOrigins.push("http://localhost");
|
|
73
|
-
allowedOrigins.push(/^http:\/\/localhost:\d+$/);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const isAllowed = allowedOrigins.some((allowed) => {
|
|
77
|
-
if (allowed instanceof RegExp) {
|
|
78
|
-
return allowed.test(requestOrigin);
|
|
79
|
-
}
|
|
80
|
-
return requestOrigin.includes(allowed);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
if (isAllowed) {
|
|
84
|
-
callback(null, true);
|
|
85
|
-
} else {
|
|
86
|
-
callback(new CorsError());
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
// Main
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
const corsHelper = (config = {}) => {
|
|
97
|
-
const { origin, overrides = {} } = config;
|
|
98
|
-
|
|
99
|
-
const options = {
|
|
100
|
-
origin: dynamicOriginCallbackHandler(origin),
|
|
101
|
-
// * The default behavior is to allow any headers and methods so they are not included here
|
|
102
|
-
...overrides,
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
return expressCors(options);
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
// Export
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
var cors_helper = (config) => {
|
|
114
|
-
const cors = corsHelper(config);
|
|
115
|
-
return (req, res, next) => {
|
|
116
|
-
cors(req, res, (error) => {
|
|
117
|
-
if (error) {
|
|
118
|
-
res.status(error.status);
|
|
119
|
-
res.setHeader("Content-Type", "application/json");
|
|
120
|
-
return res.json(error.body());
|
|
121
|
-
}
|
|
122
|
-
next();
|
|
123
|
-
});
|
|
124
|
-
};
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
// Helper Functions
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
// Adapter for the "@codegenie/serverless-express" uuid
|
|
133
|
-
function getServerlessExpressUuid() {
|
|
134
|
-
const currentInvoke = getCurrentInvoke();
|
|
135
|
-
if (
|
|
136
|
-
currentInvoke &&
|
|
137
|
-
currentInvoke.context &&
|
|
138
|
-
currentInvoke.context.awsRequestId
|
|
139
|
-
) {
|
|
140
|
-
return currentInvoke.context.awsRequestId;
|
|
141
|
-
}
|
|
142
|
-
return undefined;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
// Main
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
const getCurrentInvokeUuid = () => getServerlessExpressUuid();
|
|
151
|
-
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
// Main
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
const decorateResponse = (
|
|
158
|
-
res,
|
|
159
|
-
{ handler = "", version = process.env.PROJECT_VERSION } = {},
|
|
160
|
-
) => {
|
|
161
|
-
const log$1 = log.lib({
|
|
162
|
-
lib: JAYPIE.LIB.EXPRESS,
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
// Validate
|
|
168
|
-
//
|
|
169
|
-
if (typeof res !== "object" || res === null) {
|
|
170
|
-
log$1.warn("decorateResponse called but response is not an object");
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
try {
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
// Decorate Headers
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
// X-Powered-By, override "Express" but nothing else
|
|
181
|
-
if (
|
|
182
|
-
!res.get(HTTP.HEADER.POWERED_BY) ||
|
|
183
|
-
res.get(HTTP.HEADER.POWERED_BY) === "Express"
|
|
184
|
-
) {
|
|
185
|
-
res.set(HTTP.HEADER.POWERED_BY, JAYPIE.LIB.EXPRESS);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// X-Project-Environment
|
|
189
|
-
if (process.env.PROJECT_ENV) {
|
|
190
|
-
res.set(HTTP.HEADER.PROJECT.ENVIRONMENT, process.env.PROJECT_ENV);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// X-Project-Handler
|
|
194
|
-
if (handler) {
|
|
195
|
-
res.set(HTTP.HEADER.PROJECT.HANDLER, handler);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// X-Project-Invocation
|
|
199
|
-
const currentInvoke = getCurrentInvokeUuid();
|
|
200
|
-
if (currentInvoke) {
|
|
201
|
-
res.set(HTTP.HEADER.PROJECT.INVOCATION, currentInvoke);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// X-Project-Key
|
|
205
|
-
if (process.env.PROJECT_KEY) {
|
|
206
|
-
res.set(HTTP.HEADER.PROJECT.KEY, process.env.PROJECT_KEY);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// X-Project-Version
|
|
210
|
-
if (version) {
|
|
211
|
-
res.set(HTTP.HEADER.PROJECT.VERSION, version);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
// Error Handling
|
|
217
|
-
//
|
|
218
|
-
} catch (error) {
|
|
219
|
-
log$1.warn("decorateResponse caught an internal error");
|
|
220
|
-
log$1.var({ error });
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
// Footnotes
|
|
227
|
-
//
|
|
228
|
-
|
|
229
|
-
// This is a "utility" function but it needs a lot of "context"
|
|
230
|
-
// about the environment's secret parameters, the special adapter,
|
|
231
|
-
// HTTP, etc. There must be a better way to organize this
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
// Function Definition
|
|
236
|
-
//
|
|
237
|
-
|
|
238
|
-
function summarizeRequest(req) {
|
|
239
|
-
// If body is a buffer, convert it to a string
|
|
240
|
-
let { body } = req;
|
|
241
|
-
if (Buffer.isBuffer(body)) {
|
|
242
|
-
body = body.toString();
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return {
|
|
246
|
-
baseUrl: req.baseUrl,
|
|
247
|
-
body,
|
|
248
|
-
headers: req.headers,
|
|
249
|
-
method: req.method,
|
|
250
|
-
query: req.query,
|
|
251
|
-
url: req.url,
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
// Function Definition
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
function summarizeResponse(res, extras) {
|
|
261
|
-
const response = {
|
|
262
|
-
statusCode: res.statusCode,
|
|
263
|
-
statusMessage: res.statusMessage,
|
|
264
|
-
};
|
|
265
|
-
if (typeof res.getHeaders === "function") {
|
|
266
|
-
response.headers = res.getHeaders();
|
|
267
|
-
}
|
|
268
|
-
if (typeof extras === "object" && extras !== null) {
|
|
269
|
-
Object.assign(response, extras);
|
|
270
|
-
}
|
|
271
|
-
return response;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
// Main
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
const expressHandler = (handler, options = {}) => {
|
|
280
|
-
// If handler is an object and options is a function, swap them
|
|
281
|
-
if (typeof handler === "object" && typeof options === "function") {
|
|
282
|
-
const temp = handler;
|
|
283
|
-
handler = options;
|
|
284
|
-
options = temp;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
//
|
|
288
|
-
//
|
|
289
|
-
// Validate
|
|
290
|
-
//
|
|
291
|
-
let {
|
|
292
|
-
chaos,
|
|
293
|
-
locals,
|
|
294
|
-
name,
|
|
295
|
-
setup = [],
|
|
296
|
-
teardown = [],
|
|
297
|
-
unavailable,
|
|
298
|
-
validate: validate$1,
|
|
299
|
-
} = options;
|
|
300
|
-
validate.function(handler);
|
|
301
|
-
validate.optional.object(locals);
|
|
302
|
-
setup = force.array(setup); // allows a single item
|
|
303
|
-
teardown = force.array(teardown); // allows a single item
|
|
304
|
-
|
|
305
|
-
//
|
|
306
|
-
//
|
|
307
|
-
// Setup
|
|
308
|
-
//
|
|
309
|
-
|
|
310
|
-
let jaypieFunction;
|
|
311
|
-
|
|
312
|
-
return async (req, res, ...params) => {
|
|
313
|
-
// * This is the first line of code that runs when a request is received
|
|
314
|
-
|
|
315
|
-
// Set default chaos value
|
|
316
|
-
if (chaos === undefined) {
|
|
317
|
-
chaos =
|
|
318
|
-
process.env.PROJECT_CHAOS ||
|
|
319
|
-
getHeaderFrom(HTTP.HEADER.PROJECT.CHAOS, req);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Re-init the logger
|
|
323
|
-
log.init();
|
|
324
|
-
// Very low-level, internal sub-trace details
|
|
325
|
-
const libLogger = log.lib({
|
|
326
|
-
lib: JAYPIE.LIB.EXPRESS,
|
|
327
|
-
});
|
|
328
|
-
// Top-level, important details that run at the same level as the main logger
|
|
329
|
-
const log$1 = log.lib({
|
|
330
|
-
level: log.level,
|
|
331
|
-
lib: JAYPIE.LIB.EXPRESS,
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
// Update the public logger with the request ID
|
|
335
|
-
const invokeUuid = getCurrentInvokeUuid();
|
|
336
|
-
if (invokeUuid) {
|
|
337
|
-
log.tag({ invoke: invokeUuid });
|
|
338
|
-
log.tag({ shortInvoke: invokeUuid.slice(0, 8) });
|
|
339
|
-
// TODO: in theory this is redundant
|
|
340
|
-
libLogger.tag({ invoke: invokeUuid });
|
|
341
|
-
libLogger.tag({ shortInvoke: invokeUuid.slice(0, 8) });
|
|
342
|
-
log$1.tag({ invoke: invokeUuid });
|
|
343
|
-
log$1.tag({ shortInvoke: invokeUuid.slice(0, 8) });
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if (!name) {
|
|
347
|
-
// If handler has a name, use it
|
|
348
|
-
if (handler.name) {
|
|
349
|
-
name = handler.name;
|
|
350
|
-
} else {
|
|
351
|
-
name = JAYPIE.UNKNOWN;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
log.tag({ handler: name });
|
|
355
|
-
// TODO: in theory this is redundant
|
|
356
|
-
libLogger.tag({ handler: name });
|
|
357
|
-
log$1.tag({ handler: name });
|
|
358
|
-
|
|
359
|
-
libLogger.trace("[jaypie] Express init");
|
|
360
|
-
|
|
361
|
-
// Set req.locals if it doesn't exist
|
|
362
|
-
if (!req.locals) req.locals = {};
|
|
363
|
-
if (!req.locals._jaypie) req.locals._jaypie = {};
|
|
364
|
-
|
|
365
|
-
// Set res.locals if it doesn't exist
|
|
366
|
-
if (!res.locals) res.locals = {};
|
|
367
|
-
if (!res.locals._jaypie) res.locals._jaypie = {};
|
|
368
|
-
|
|
369
|
-
const originalRes = {
|
|
370
|
-
attemptedCall: undefined,
|
|
371
|
-
attemptedParams: undefined,
|
|
372
|
-
end: res.end,
|
|
373
|
-
json: res.json,
|
|
374
|
-
send: res.send,
|
|
375
|
-
status: res.status,
|
|
376
|
-
statusSent: false,
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
res.end = (...params) => {
|
|
380
|
-
originalRes.attemptedCall = originalRes.end;
|
|
381
|
-
originalRes.attemptedParams = params;
|
|
382
|
-
log$1.warn(
|
|
383
|
-
"[jaypie] Illegal call to res.end(); prefer Jaypie response conventions",
|
|
384
|
-
);
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
res.json = (...params) => {
|
|
388
|
-
originalRes.attemptedCall = originalRes.json;
|
|
389
|
-
originalRes.attemptedParams = params;
|
|
390
|
-
log$1.warn(
|
|
391
|
-
"[jaypie] Illegal call to res.json(); prefer Jaypie response conventions",
|
|
392
|
-
);
|
|
393
|
-
};
|
|
394
|
-
|
|
395
|
-
res.send = (...params) => {
|
|
396
|
-
originalRes.attemptedCall = originalRes.send;
|
|
397
|
-
originalRes.attemptedParams = params;
|
|
398
|
-
log$1.warn(
|
|
399
|
-
"[jaypie] Illegal call to res.send(); prefer Jaypie response conventions",
|
|
400
|
-
);
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
res.status = (...params) => {
|
|
404
|
-
originalRes.statusSent = params[0];
|
|
405
|
-
return originalRes.status(...params);
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
// Preprocess
|
|
411
|
-
//
|
|
412
|
-
|
|
413
|
-
if (locals) {
|
|
414
|
-
// Locals
|
|
415
|
-
const keys = Object.keys(locals);
|
|
416
|
-
if (keys.length > 0) {
|
|
417
|
-
const localsSetup = async (localsReq, localsRes) => {
|
|
418
|
-
for (let i = 0; i < keys.length; i += 1) {
|
|
419
|
-
const key = keys[i];
|
|
420
|
-
libLogger.trace(`[jaypie] Locals: ${key}`);
|
|
421
|
-
if (typeof locals[key] === "function") {
|
|
422
|
-
localsReq.locals[key] = await locals[key](localsReq, localsRes);
|
|
423
|
-
} else {
|
|
424
|
-
localsReq.locals[key] = locals[key];
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
};
|
|
428
|
-
setup.push(localsSetup);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
let response;
|
|
433
|
-
let status = HTTP.CODE.OK;
|
|
434
|
-
|
|
435
|
-
try {
|
|
436
|
-
log$1.info.var({ req: summarizeRequest(req) });
|
|
437
|
-
|
|
438
|
-
// Initialize after logging is set up
|
|
439
|
-
jaypieFunction = jaypieHandler(handler, {
|
|
440
|
-
chaos,
|
|
441
|
-
name,
|
|
442
|
-
setup,
|
|
443
|
-
teardown,
|
|
444
|
-
unavailable,
|
|
445
|
-
validate: validate$1,
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
libLogger.trace("[jaypie] Express execution");
|
|
449
|
-
|
|
450
|
-
//
|
|
451
|
-
//
|
|
452
|
-
// Process
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
response = await jaypieFunction(req, res, ...params);
|
|
456
|
-
|
|
457
|
-
//
|
|
458
|
-
//
|
|
459
|
-
// Error Handling
|
|
460
|
-
//
|
|
461
|
-
} catch (error) {
|
|
462
|
-
// In theory jaypieFunction has handled all errors
|
|
463
|
-
if (error.status) {
|
|
464
|
-
status = error.status;
|
|
465
|
-
}
|
|
466
|
-
if (typeof error.json === "function") {
|
|
467
|
-
response = error.json();
|
|
468
|
-
} else {
|
|
469
|
-
// This should never happen
|
|
470
|
-
const unhandledError = new UnhandledError();
|
|
471
|
-
response = unhandledError.json();
|
|
472
|
-
status = unhandledError.status;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
// Postprocess
|
|
479
|
-
//
|
|
480
|
-
|
|
481
|
-
// Restore original res functions
|
|
482
|
-
res.end = originalRes.end;
|
|
483
|
-
res.json = originalRes.json;
|
|
484
|
-
res.send = originalRes.send;
|
|
485
|
-
res.status = originalRes.status;
|
|
486
|
-
|
|
487
|
-
// Decorate response
|
|
488
|
-
decorateResponse(res, { handler: name });
|
|
489
|
-
|
|
490
|
-
// Allow the sent status to override the status in the response
|
|
491
|
-
if (originalRes.statusSent) {
|
|
492
|
-
status = originalRes.statusSent;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// Send response
|
|
496
|
-
try {
|
|
497
|
-
if (!originalRes.attemptedCall) {
|
|
498
|
-
// Body
|
|
499
|
-
if (response) {
|
|
500
|
-
if (typeof response === "object") {
|
|
501
|
-
if (typeof response.json === "function") {
|
|
502
|
-
res.json(response.json());
|
|
503
|
-
} else {
|
|
504
|
-
res.status(status).json(response);
|
|
505
|
-
}
|
|
506
|
-
} else if (typeof response === "string") {
|
|
507
|
-
try {
|
|
508
|
-
res.status(status).json(JSON.parse(response));
|
|
509
|
-
// eslint-disable-next-line no-unused-vars
|
|
510
|
-
} catch (error) {
|
|
511
|
-
res.status(status).send(response);
|
|
512
|
-
}
|
|
513
|
-
} else if (response === true) {
|
|
514
|
-
res.status(HTTP.CODE.CREATED).send();
|
|
515
|
-
} else {
|
|
516
|
-
res.status(status).send(response);
|
|
517
|
-
}
|
|
518
|
-
} else {
|
|
519
|
-
// No response
|
|
520
|
-
res.status(HTTP.CODE.NO_CONTENT).send();
|
|
521
|
-
}
|
|
522
|
-
} else {
|
|
523
|
-
// Resolve illegal call to res.end(), res.json(), or res.send()
|
|
524
|
-
log$1.debug("[jaypie] Resolving illegal call to res");
|
|
525
|
-
log$1.var({
|
|
526
|
-
attemptedCall: {
|
|
527
|
-
name: originalRes.attemptedCall.name,
|
|
528
|
-
params: originalRes.attemptedParams,
|
|
529
|
-
},
|
|
530
|
-
});
|
|
531
|
-
// Call the original function with the original parameters and the original `this` (res)
|
|
532
|
-
originalRes.attemptedCall.call(res, ...originalRes.attemptedParams);
|
|
533
|
-
}
|
|
534
|
-
} catch (error) {
|
|
535
|
-
log$1.fatal("Express encountered an error while sending the response");
|
|
536
|
-
log$1.var({ responseError: error });
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
// Log response
|
|
540
|
-
const extras = {};
|
|
541
|
-
if (response) extras.body = response;
|
|
542
|
-
log$1.info.var({
|
|
543
|
-
res: summarizeResponse(res, extras),
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
// Submit metric if Datadog is configured
|
|
547
|
-
if (hasDatadogEnv()) {
|
|
548
|
-
// Construct path from baseUrl and url
|
|
549
|
-
let path = (req.baseUrl || "") + (req.url || "");
|
|
550
|
-
// Ensure path starts with /
|
|
551
|
-
if (!path.startsWith("/")) {
|
|
552
|
-
path = "/" + path;
|
|
553
|
-
}
|
|
554
|
-
// Remove trailing slash unless it's the root path
|
|
555
|
-
if (path.length > 1 && path.endsWith("/")) {
|
|
556
|
-
path = path.slice(0, -1);
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// Replace UUIDs with :id for better aggregation
|
|
560
|
-
// Matches standard UUID v4 format (8-4-4-4-12 hex characters)
|
|
561
|
-
path = path.replace(
|
|
562
|
-
/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi,
|
|
563
|
-
":id",
|
|
564
|
-
);
|
|
565
|
-
|
|
566
|
-
// Determine metric name based on environment variables
|
|
567
|
-
let metricPrefix = "project";
|
|
568
|
-
if (process.env.PROJECT_SPONSOR) {
|
|
569
|
-
metricPrefix = process.env.PROJECT_SPONSOR;
|
|
570
|
-
} else if (process.env.PROJECT_KEY) {
|
|
571
|
-
metricPrefix = `project.${process.env.PROJECT_KEY}`;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
await submitMetric({
|
|
575
|
-
name: `${metricPrefix}.api.response`,
|
|
576
|
-
type: DATADOG.METRIC.TYPE.COUNT,
|
|
577
|
-
value: 1,
|
|
578
|
-
tags: {
|
|
579
|
-
status: res.statusCode,
|
|
580
|
-
path,
|
|
581
|
-
},
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// Clean up the public logger
|
|
586
|
-
log.untag("handler");
|
|
587
|
-
|
|
588
|
-
//
|
|
589
|
-
//
|
|
590
|
-
// Return
|
|
591
|
-
//
|
|
592
|
-
|
|
593
|
-
return response;
|
|
594
|
-
};
|
|
595
|
-
};
|
|
596
|
-
|
|
597
|
-
//
|
|
598
|
-
//
|
|
599
|
-
// Main
|
|
600
|
-
//
|
|
601
|
-
|
|
602
|
-
const httpHandler = (statusCode = HTTP.CODE.OK, context = {}) => {
|
|
603
|
-
// Give a default name if there isn't one
|
|
604
|
-
if (!context.name) {
|
|
605
|
-
context.name = "_http";
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
// Return a function that will be used as an express route
|
|
609
|
-
return expressHandler(async (req, res) => {
|
|
610
|
-
// Map the most throwable status codes to errors and throw them!
|
|
611
|
-
const error = {
|
|
612
|
-
[HTTP.CODE.BAD_REQUEST]: BadRequestError,
|
|
613
|
-
[HTTP.CODE.UNAUTHORIZED]: UnauthorizedError,
|
|
614
|
-
[HTTP.CODE.FORBIDDEN]: ForbiddenError,
|
|
615
|
-
[HTTP.CODE.NOT_FOUND]: NotFoundError,
|
|
616
|
-
[HTTP.CODE.METHOD_NOT_ALLOWED]: MethodNotAllowedError,
|
|
617
|
-
[HTTP.CODE.GONE]: GoneError,
|
|
618
|
-
[HTTP.CODE.TEAPOT]: TeapotError,
|
|
619
|
-
[HTTP.CODE.INTERNAL_ERROR]: InternalError,
|
|
620
|
-
[HTTP.CODE.BAD_GATEWAY]: BadGatewayError,
|
|
621
|
-
[HTTP.CODE.UNAVAILABLE]: UnavailableError,
|
|
622
|
-
[HTTP.CODE.GATEWAY_TIMEOUT]: GatewayTimeoutError,
|
|
623
|
-
};
|
|
624
|
-
|
|
625
|
-
// If this maps to an error, throw it
|
|
626
|
-
if (error[statusCode]) {
|
|
627
|
-
log.trace(
|
|
628
|
-
`@knowdev/express: gracefully throwing ${statusCode} up to projectHandler`,
|
|
629
|
-
);
|
|
630
|
-
throw new error[statusCode]();
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// If this is an error and we didn't get thrown, log a warning
|
|
634
|
-
if (statusCode >= 400) {
|
|
635
|
-
log.warn(
|
|
636
|
-
`@knowdev/express: status code ${statusCode} not mapped as throwable`,
|
|
637
|
-
);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// Send the response
|
|
641
|
-
res.status(statusCode);
|
|
642
|
-
return statusCode === HTTP.CODE.NO_CONTENT ? null : {};
|
|
643
|
-
}, context);
|
|
644
|
-
};
|
|
645
|
-
|
|
646
|
-
//
|
|
647
|
-
//
|
|
648
|
-
// Main
|
|
649
|
-
//
|
|
650
|
-
|
|
651
|
-
const echoHandler = (context = {}) => {
|
|
652
|
-
validate.object(context);
|
|
653
|
-
// Give a default name if there isn't one
|
|
654
|
-
if (!context.name) {
|
|
655
|
-
context.name = "_echo";
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
// Return a function that will be used as an express route
|
|
659
|
-
return expressHandler(async (req) => {
|
|
660
|
-
return {
|
|
661
|
-
req: summarizeRequest(req),
|
|
662
|
-
};
|
|
663
|
-
}, context);
|
|
664
|
-
};
|
|
665
|
-
|
|
666
|
-
//
|
|
667
|
-
//
|
|
668
|
-
// Functions
|
|
669
|
-
//
|
|
670
|
-
|
|
671
|
-
const routes = {
|
|
672
|
-
badRequestRoute: httpHandler(HTTP.CODE.BAD_REQUEST, { name: "_badRequest" }),
|
|
673
|
-
echoRoute: echoHandler(),
|
|
674
|
-
forbiddenRoute: httpHandler(HTTP.CODE.FORBIDDEN, { name: "_forbidden" }),
|
|
675
|
-
goneRoute: httpHandler(HTTP.CODE.GONE, { name: "_gone" }),
|
|
676
|
-
methodNotAllowedRoute: httpHandler(HTTP.CODE.METHOD_NOT_ALLOWED, {
|
|
677
|
-
name: "_methodNotAllowed",
|
|
678
|
-
}),
|
|
679
|
-
noContentRoute: httpHandler(HTTP.CODE.NO_CONTENT, { name: "_noContent" }),
|
|
680
|
-
notFoundRoute: httpHandler(HTTP.CODE.NOT_FOUND, { name: "_notFound" }),
|
|
681
|
-
notImplementedRoute: expressHandler(
|
|
682
|
-
() => {
|
|
683
|
-
throw new NotImplementedError();
|
|
684
|
-
},
|
|
685
|
-
{ name: "_notImplemented" },
|
|
686
|
-
),
|
|
687
|
-
};
|
|
688
|
-
|
|
689
|
-
//
|
|
690
|
-
//
|
|
691
|
-
// Export
|
|
692
|
-
//
|
|
693
|
-
|
|
694
|
-
const {
|
|
695
|
-
badRequestRoute,
|
|
696
|
-
echoRoute,
|
|
697
|
-
forbiddenRoute,
|
|
698
|
-
goneRoute,
|
|
699
|
-
methodNotAllowedRoute,
|
|
700
|
-
noContentRoute,
|
|
701
|
-
notFoundRoute,
|
|
702
|
-
notImplementedRoute,
|
|
703
|
-
} = routes;
|
|
704
|
-
|
|
705
|
-
export { EXPRESS, badRequestRoute, cors_helper as cors, echoRoute, expressHandler, httpHandler as expressHttpCodeHandler, forbiddenRoute, goneRoute, methodNotAllowedRoute, noContentRoute, notFoundRoute, notImplementedRoute };
|