@jaypie/express 0.1.1 → 0.1.3

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.
@@ -1,10 +1,389 @@
1
1
  'use strict';
2
2
 
3
+ var core = require('@jaypie/core');
4
+ var serverlessExpress = require('@codegenie/serverless-express');
5
+
6
+ //
7
+ //
8
+ // Helper Functions
9
+ //
10
+
11
+ // Adapter for the "@codegenie/serverless-express" uuid
12
+ function getServerlessExpressUuid() {
13
+ const currentInvoke = serverlessExpress.getCurrentInvoke();
14
+ if (
15
+ currentInvoke &&
16
+ currentInvoke.context &&
17
+ currentInvoke.context.awsRequestId
18
+ ) {
19
+ return currentInvoke.context.awsRequestId;
20
+ }
21
+ return undefined;
22
+ }
23
+
24
+ //
25
+ //
26
+ // Main
27
+ //
28
+
29
+ const getCurrentInvokeUuid = () => getServerlessExpressUuid();
30
+
31
+ //
32
+ //
33
+ // Main
34
+ //
35
+
36
+ const decorateResponse = (
37
+ res,
38
+ { handler = "", version = process.env.PROJECT_VERSION } = {},
39
+ ) => {
40
+ const log = core.log.lib({
41
+ lib: core.JAYPIE.LIB.EXPRESS,
42
+ });
43
+
44
+ //
45
+ //
46
+ // Validate
47
+ //
48
+ if (typeof res !== "object" || res === null) {
49
+ log.warn("decorateResponse called but response is not an object");
50
+ return; // eslint-disable-line no-useless-return
51
+ }
52
+
53
+ try {
54
+ //
55
+ //
56
+ // Decorate Headers
57
+ //
58
+
59
+ // X-Powered-By, override "Express" but nothing else
60
+ if (
61
+ !res.get(core.HTTP.HEADER.POWERED_BY) ||
62
+ res.get(core.HTTP.HEADER.POWERED_BY) === "Express"
63
+ ) {
64
+ res.set(core.HTTP.HEADER.POWERED_BY, core.JAYPIE.LIB.EXPRESS);
65
+ }
66
+
67
+ // X-Project-Environment
68
+ if (process.env.PROJECT_ENV) {
69
+ res.setHeader(core.HTTP.HEADER.PROJECT.ENVIRONMENT, process.env.PROJECT_ENV);
70
+ }
71
+
72
+ // X-Project-Handler
73
+ if (handler) {
74
+ res.setHeader(core.HTTP.HEADER.PROJECT.HANDLER, handler);
75
+ }
76
+
77
+ // X-Project-Invocation
78
+ const currentInvoke = getCurrentInvokeUuid();
79
+ if (currentInvoke) {
80
+ res.setHeader(core.HTTP.HEADER.PROJECT.INVOCATION, currentInvoke);
81
+ }
82
+
83
+ // X-Project-Key
84
+ if (process.env.PROJECT_KEY) {
85
+ res.setHeader(core.HTTP.HEADER.PROJECT.KEY, process.env.PROJECT_KEY);
86
+ }
87
+
88
+ // X-Project-Version
89
+ if (version) {
90
+ res.setHeader(core.HTTP.HEADER.PROJECT.VERSION, version);
91
+ }
92
+
93
+ //
94
+ //
95
+ // Error Handling
96
+ //
97
+ } catch (error) {
98
+ log.warn("decorateResponse caught an internal error");
99
+ log.var({ error });
100
+ }
101
+ };
102
+
103
+ //
3
104
  //
105
+ // Footnotes
4
106
  //
5
- // Export
107
+
108
+ // This is a "utility" function but it needs a lot of "context"
109
+ // about the environment's secret parameters, the special adapter,
110
+ // HTTP, etc. There must be a better way to organize this
111
+
112
+ //
113
+ //
114
+ // Function Definition
115
+ //
116
+
117
+ function summarizeRequest(req) {
118
+ // If body is a buffer, convert it to a string
119
+ let { body } = req;
120
+ if (Buffer.isBuffer(body)) {
121
+ body = body.toString();
122
+ }
123
+
124
+ return {
125
+ baseUrl: req.baseUrl,
126
+ body,
127
+ headers: req.headers,
128
+ method: req.method,
129
+ query: req.query,
130
+ url: req.url,
131
+ };
132
+ }
133
+
6
134
  //
135
+ //
136
+ // Function Definition
137
+ //
138
+
139
+ function summarizeResponse(res, extras) {
140
+ const response = {
141
+ statusCode: res.statusCode,
142
+ statusMessage: res.statusMessage,
143
+ };
144
+ if (typeof res.getHeaders === "function") {
145
+ response.headers = res.getHeaders();
146
+ }
147
+ if (typeof extras === "object" && extras !== null) {
148
+ Object.assign(response, extras);
149
+ }
150
+ return response;
151
+ }
152
+
153
+ //
154
+ //
155
+ // Main
156
+ //
157
+
158
+ const expressHandler = (
159
+ handler,
160
+ { locals, name, setup = [], teardown = [], unavailable, validate } = {},
161
+ ) => {
162
+ //
163
+ //
164
+ // Validate
165
+ //
166
+ core.validate.function(handler);
167
+ core.validate.optional.object(locals);
168
+ setup = core.force.array(setup); // allows a single item
169
+ teardown = core.force.array(teardown); // allows a single item
170
+
171
+ //
172
+ //
173
+ // Setup
174
+ //
175
+
176
+ let jaypieFunction;
177
+
178
+ return async (req, res, ...params) => {
179
+ // * This is the first line of code that runs when a request is received
180
+
181
+ // Update the public logger with the request ID
182
+ core.log.tag({ invoke: getCurrentInvokeUuid() });
183
+
184
+ // Very low-level, internal sub-trace details
185
+ const libLogger = core.log.lib({
186
+ lib: core.JAYPIE.LIB.EXPRESS,
187
+ });
188
+ libLogger.trace("[jaypie] Express init");
189
+
190
+ // Top-level, important details that run at the same level as the main logger
191
+ const log = core.log.lib({
192
+ level: core.log.level,
193
+ lib: core.JAYPIE.LIB.EXPRESS,
194
+ });
195
+
196
+ // Set req.locals if it doesn't exist
197
+ if (!req.locals) req.locals = {};
198
+ if (!req.locals._jaypie) req.locals._jaypie = {};
199
+
200
+ // Set res.locals if it doesn't exist
201
+ if (!res.locals) res.locals = {};
202
+ if (!res.locals._jaypie) res.locals._jaypie = {};
203
+
204
+ const originalRes = {
205
+ attemptedCall: undefined,
206
+ attemptedParams: undefined,
207
+ end: res.end,
208
+ json: res.json,
209
+ send: res.send,
210
+ status: res.status,
211
+ statusSent: false,
212
+ };
213
+ res.end = (...params) => {
214
+ originalRes.attemptedCall = originalRes.end;
215
+ originalRes.attemptedParams = params;
216
+ log.warn(
217
+ "[jaypie] Illegal call to res.end(); prefer Jaypie response conventions",
218
+ );
219
+ };
220
+ res.json = (...params) => {
221
+ originalRes.attemptedCall = originalRes.json;
222
+ originalRes.attemptedParams = params;
223
+ log.warn(
224
+ "[jaypie] Illegal call to res.json(); prefer Jaypie response conventions",
225
+ );
226
+ };
227
+ res.send = (...params) => {
228
+ originalRes.attemptedCall = originalRes.send;
229
+ originalRes.attemptedParams = params;
230
+ log.warn(
231
+ "[jaypie] Illegal call to res.send(); prefer Jaypie response conventions",
232
+ );
233
+ };
234
+ res.status = (...params) => {
235
+ originalRes.statusSent = params;
236
+ return originalRes.status(...params);
237
+ };
238
+
239
+ //
240
+ //
241
+ // Preprocess
242
+ //
243
+
244
+ if (locals) {
245
+ // Locals
246
+ const keys = Object.keys(locals);
247
+ if (keys.length > 0) {
248
+ const localsSetup = async () => {
249
+ libLogger.trace("[jaypie] Locals");
250
+ for (let i = 0; i < keys.length; i += 1) {
251
+ const key = keys[i];
252
+ if (typeof locals[key] === "function") {
253
+ // eslint-disable-next-line no-await-in-loop
254
+ req.locals[key] = await locals[key](req, res);
255
+ } else {
256
+ req.locals[key] = locals[key];
257
+ }
258
+ }
259
+ };
260
+ setup.push(localsSetup);
261
+ }
262
+ }
263
+
264
+ if (!jaypieFunction) {
265
+ // Initialize after logging is set up
266
+ jaypieFunction = core.jaypieHandler(handler, {
267
+ name,
268
+ setup,
269
+ teardown,
270
+ unavailable,
271
+ validate,
272
+ });
273
+ }
274
+
275
+ let response;
276
+ let status = core.HTTP.CODE.OK;
277
+
278
+ try {
279
+ libLogger.trace("[jaypie] Lambda execution");
280
+ log.info.var({ req: summarizeRequest(req) });
281
+
282
+ //
283
+ //
284
+ // Process
285
+ //
286
+
287
+ response = await jaypieFunction(req, res, ...params);
288
+
289
+ //
290
+ //
291
+ // Error Handling
292
+ //
293
+ } catch (error) {
294
+ // In theory jaypieFunction has handled all errors
295
+ if (error.status) {
296
+ status = error.status;
297
+ }
298
+ if (typeof error.json === "function") {
299
+ response = error.json();
300
+ } else {
301
+ // This should never happen
302
+ const unhandledError = new core.UnhandledError();
303
+ response = unhandledError.json();
304
+ status = unhandledError.status;
305
+ }
306
+ }
307
+
308
+ //
309
+ //
310
+ // Postprocess
311
+ //
312
+
313
+ // Restore original res functions
314
+ res.end = originalRes.end;
315
+ res.json = originalRes.json;
316
+ res.send = originalRes.send;
317
+ res.status = originalRes.status;
318
+
319
+ // Decorate response
320
+ decorateResponse(res, { handler: name });
321
+
322
+ // Allow the sent status to override the status in the response
323
+ if (originalRes.statusSent) {
324
+ status = originalRes.statusSent;
325
+ }
326
+
327
+ // Send response
328
+ try {
329
+ if (!originalRes.attemptedCall) {
330
+ // Body
331
+ if (response) {
332
+ if (typeof response === "object") {
333
+ if (typeof response.json === "function") {
334
+ res.json(response.json());
335
+ } else {
336
+ res.status(status).json(response);
337
+ }
338
+ } else if (typeof response === "string") {
339
+ try {
340
+ res.status(status).json(JSON.parse(response));
341
+ } catch (error) {
342
+ res.status(status).send(response);
343
+ }
344
+ } else if (response === true) {
345
+ res.status(core.HTTP.CODE.CREATED).send();
346
+ } else {
347
+ res.status(status).send(response);
348
+ }
349
+ } else {
350
+ // No response
351
+ res.status(core.HTTP.CODE.NO_CONTENT).send();
352
+ }
353
+ } else {
354
+ // Resolve illegal call to res.end(), res.json(), or res.send()
355
+ log.debug("[jaypie] Resolving illegal call to res");
356
+ log.var({
357
+ attemptedCall: {
358
+ name: originalRes.attemptedCall.name,
359
+ params: originalRes.attemptedParams,
360
+ },
361
+ });
362
+ // Call the original function with the original parameters and the original `this` (res)
363
+ originalRes.attemptedCall.call(res, ...originalRes.attemptedParams);
364
+ }
365
+ } catch (error) {
366
+ log.fatal("Express encountered an error while sending the response");
367
+ log.var({ responseError: error });
368
+ }
369
+
370
+ // Log response
371
+ const extras = {};
372
+ if (response) extras.body = response;
373
+ log.info.var({
374
+ res: summarizeResponse(res, extras),
375
+ });
376
+
377
+ // Clean up the public logger
378
+ core.log.untag("handler");
379
+
380
+ //
381
+ //
382
+ // Return
383
+ //
7
384
 
8
- var index = {};
385
+ return response;
386
+ };
387
+ };
9
388
 
10
- module.exports = index;
389
+ exports.expressHandler = expressHandler;
@@ -1,8 +1,387 @@
1
+ import { log, JAYPIE, HTTP, validate, force, jaypieHandler, UnhandledError } from '@jaypie/core';
2
+ import { getCurrentInvoke } from '@codegenie/serverless-express';
3
+
4
+ //
5
+ //
6
+ // Helper Functions
7
+ //
8
+
9
+ // Adapter for the "@codegenie/serverless-express" uuid
10
+ function getServerlessExpressUuid() {
11
+ const currentInvoke = getCurrentInvoke();
12
+ if (
13
+ currentInvoke &&
14
+ currentInvoke.context &&
15
+ currentInvoke.context.awsRequestId
16
+ ) {
17
+ return currentInvoke.context.awsRequestId;
18
+ }
19
+ return undefined;
20
+ }
21
+
22
+ //
23
+ //
24
+ // Main
25
+ //
26
+
27
+ const getCurrentInvokeUuid = () => getServerlessExpressUuid();
28
+
29
+ //
30
+ //
31
+ // Main
32
+ //
33
+
34
+ const decorateResponse = (
35
+ res,
36
+ { handler = "", version = process.env.PROJECT_VERSION } = {},
37
+ ) => {
38
+ const log$1 = log.lib({
39
+ lib: JAYPIE.LIB.EXPRESS,
40
+ });
41
+
42
+ //
43
+ //
44
+ // Validate
45
+ //
46
+ if (typeof res !== "object" || res === null) {
47
+ log$1.warn("decorateResponse called but response is not an object");
48
+ return; // eslint-disable-line no-useless-return
49
+ }
50
+
51
+ try {
52
+ //
53
+ //
54
+ // Decorate Headers
55
+ //
56
+
57
+ // X-Powered-By, override "Express" but nothing else
58
+ if (
59
+ !res.get(HTTP.HEADER.POWERED_BY) ||
60
+ res.get(HTTP.HEADER.POWERED_BY) === "Express"
61
+ ) {
62
+ res.set(HTTP.HEADER.POWERED_BY, JAYPIE.LIB.EXPRESS);
63
+ }
64
+
65
+ // X-Project-Environment
66
+ if (process.env.PROJECT_ENV) {
67
+ res.setHeader(HTTP.HEADER.PROJECT.ENVIRONMENT, process.env.PROJECT_ENV);
68
+ }
69
+
70
+ // X-Project-Handler
71
+ if (handler) {
72
+ res.setHeader(HTTP.HEADER.PROJECT.HANDLER, handler);
73
+ }
74
+
75
+ // X-Project-Invocation
76
+ const currentInvoke = getCurrentInvokeUuid();
77
+ if (currentInvoke) {
78
+ res.setHeader(HTTP.HEADER.PROJECT.INVOCATION, currentInvoke);
79
+ }
80
+
81
+ // X-Project-Key
82
+ if (process.env.PROJECT_KEY) {
83
+ res.setHeader(HTTP.HEADER.PROJECT.KEY, process.env.PROJECT_KEY);
84
+ }
85
+
86
+ // X-Project-Version
87
+ if (version) {
88
+ res.setHeader(HTTP.HEADER.PROJECT.VERSION, version);
89
+ }
90
+
91
+ //
92
+ //
93
+ // Error Handling
94
+ //
95
+ } catch (error) {
96
+ log$1.warn("decorateResponse caught an internal error");
97
+ log$1.var({ error });
98
+ }
99
+ };
100
+
101
+ //
1
102
  //
103
+ // Footnotes
2
104
  //
3
- // Export
105
+
106
+ // This is a "utility" function but it needs a lot of "context"
107
+ // about the environment's secret parameters, the special adapter,
108
+ // HTTP, etc. There must be a better way to organize this
109
+
110
+ //
111
+ //
112
+ // Function Definition
113
+ //
114
+
115
+ function summarizeRequest(req) {
116
+ // If body is a buffer, convert it to a string
117
+ let { body } = req;
118
+ if (Buffer.isBuffer(body)) {
119
+ body = body.toString();
120
+ }
121
+
122
+ return {
123
+ baseUrl: req.baseUrl,
124
+ body,
125
+ headers: req.headers,
126
+ method: req.method,
127
+ query: req.query,
128
+ url: req.url,
129
+ };
130
+ }
131
+
4
132
  //
133
+ //
134
+ // Function Definition
135
+ //
136
+
137
+ function summarizeResponse(res, extras) {
138
+ const response = {
139
+ statusCode: res.statusCode,
140
+ statusMessage: res.statusMessage,
141
+ };
142
+ if (typeof res.getHeaders === "function") {
143
+ response.headers = res.getHeaders();
144
+ }
145
+ if (typeof extras === "object" && extras !== null) {
146
+ Object.assign(response, extras);
147
+ }
148
+ return response;
149
+ }
150
+
151
+ //
152
+ //
153
+ // Main
154
+ //
155
+
156
+ const expressHandler = (
157
+ handler,
158
+ { locals, name, setup = [], teardown = [], unavailable, validate: validate$1 } = {},
159
+ ) => {
160
+ //
161
+ //
162
+ // Validate
163
+ //
164
+ validate.function(handler);
165
+ validate.optional.object(locals);
166
+ setup = force.array(setup); // allows a single item
167
+ teardown = force.array(teardown); // allows a single item
168
+
169
+ //
170
+ //
171
+ // Setup
172
+ //
173
+
174
+ let jaypieFunction;
175
+
176
+ return async (req, res, ...params) => {
177
+ // * This is the first line of code that runs when a request is received
178
+
179
+ // Update the public logger with the request ID
180
+ log.tag({ invoke: getCurrentInvokeUuid() });
181
+
182
+ // Very low-level, internal sub-trace details
183
+ const libLogger = log.lib({
184
+ lib: JAYPIE.LIB.EXPRESS,
185
+ });
186
+ libLogger.trace("[jaypie] Express init");
187
+
188
+ // Top-level, important details that run at the same level as the main logger
189
+ const log$1 = log.lib({
190
+ level: log.level,
191
+ lib: JAYPIE.LIB.EXPRESS,
192
+ });
193
+
194
+ // Set req.locals if it doesn't exist
195
+ if (!req.locals) req.locals = {};
196
+ if (!req.locals._jaypie) req.locals._jaypie = {};
197
+
198
+ // Set res.locals if it doesn't exist
199
+ if (!res.locals) res.locals = {};
200
+ if (!res.locals._jaypie) res.locals._jaypie = {};
201
+
202
+ const originalRes = {
203
+ attemptedCall: undefined,
204
+ attemptedParams: undefined,
205
+ end: res.end,
206
+ json: res.json,
207
+ send: res.send,
208
+ status: res.status,
209
+ statusSent: false,
210
+ };
211
+ res.end = (...params) => {
212
+ originalRes.attemptedCall = originalRes.end;
213
+ originalRes.attemptedParams = params;
214
+ log$1.warn(
215
+ "[jaypie] Illegal call to res.end(); prefer Jaypie response conventions",
216
+ );
217
+ };
218
+ res.json = (...params) => {
219
+ originalRes.attemptedCall = originalRes.json;
220
+ originalRes.attemptedParams = params;
221
+ log$1.warn(
222
+ "[jaypie] Illegal call to res.json(); prefer Jaypie response conventions",
223
+ );
224
+ };
225
+ res.send = (...params) => {
226
+ originalRes.attemptedCall = originalRes.send;
227
+ originalRes.attemptedParams = params;
228
+ log$1.warn(
229
+ "[jaypie] Illegal call to res.send(); prefer Jaypie response conventions",
230
+ );
231
+ };
232
+ res.status = (...params) => {
233
+ originalRes.statusSent = params;
234
+ return originalRes.status(...params);
235
+ };
236
+
237
+ //
238
+ //
239
+ // Preprocess
240
+ //
241
+
242
+ if (locals) {
243
+ // Locals
244
+ const keys = Object.keys(locals);
245
+ if (keys.length > 0) {
246
+ const localsSetup = async () => {
247
+ libLogger.trace("[jaypie] Locals");
248
+ for (let i = 0; i < keys.length; i += 1) {
249
+ const key = keys[i];
250
+ if (typeof locals[key] === "function") {
251
+ // eslint-disable-next-line no-await-in-loop
252
+ req.locals[key] = await locals[key](req, res);
253
+ } else {
254
+ req.locals[key] = locals[key];
255
+ }
256
+ }
257
+ };
258
+ setup.push(localsSetup);
259
+ }
260
+ }
261
+
262
+ if (!jaypieFunction) {
263
+ // Initialize after logging is set up
264
+ jaypieFunction = jaypieHandler(handler, {
265
+ name,
266
+ setup,
267
+ teardown,
268
+ unavailable,
269
+ validate: validate$1,
270
+ });
271
+ }
272
+
273
+ let response;
274
+ let status = HTTP.CODE.OK;
275
+
276
+ try {
277
+ libLogger.trace("[jaypie] Lambda execution");
278
+ log$1.info.var({ req: summarizeRequest(req) });
279
+
280
+ //
281
+ //
282
+ // Process
283
+ //
284
+
285
+ response = await jaypieFunction(req, res, ...params);
286
+
287
+ //
288
+ //
289
+ // Error Handling
290
+ //
291
+ } catch (error) {
292
+ // In theory jaypieFunction has handled all errors
293
+ if (error.status) {
294
+ status = error.status;
295
+ }
296
+ if (typeof error.json === "function") {
297
+ response = error.json();
298
+ } else {
299
+ // This should never happen
300
+ const unhandledError = new UnhandledError();
301
+ response = unhandledError.json();
302
+ status = unhandledError.status;
303
+ }
304
+ }
305
+
306
+ //
307
+ //
308
+ // Postprocess
309
+ //
310
+
311
+ // Restore original res functions
312
+ res.end = originalRes.end;
313
+ res.json = originalRes.json;
314
+ res.send = originalRes.send;
315
+ res.status = originalRes.status;
316
+
317
+ // Decorate response
318
+ decorateResponse(res, { handler: name });
319
+
320
+ // Allow the sent status to override the status in the response
321
+ if (originalRes.statusSent) {
322
+ status = originalRes.statusSent;
323
+ }
324
+
325
+ // Send response
326
+ try {
327
+ if (!originalRes.attemptedCall) {
328
+ // Body
329
+ if (response) {
330
+ if (typeof response === "object") {
331
+ if (typeof response.json === "function") {
332
+ res.json(response.json());
333
+ } else {
334
+ res.status(status).json(response);
335
+ }
336
+ } else if (typeof response === "string") {
337
+ try {
338
+ res.status(status).json(JSON.parse(response));
339
+ } catch (error) {
340
+ res.status(status).send(response);
341
+ }
342
+ } else if (response === true) {
343
+ res.status(HTTP.CODE.CREATED).send();
344
+ } else {
345
+ res.status(status).send(response);
346
+ }
347
+ } else {
348
+ // No response
349
+ res.status(HTTP.CODE.NO_CONTENT).send();
350
+ }
351
+ } else {
352
+ // Resolve illegal call to res.end(), res.json(), or res.send()
353
+ log$1.debug("[jaypie] Resolving illegal call to res");
354
+ log$1.var({
355
+ attemptedCall: {
356
+ name: originalRes.attemptedCall.name,
357
+ params: originalRes.attemptedParams,
358
+ },
359
+ });
360
+ // Call the original function with the original parameters and the original `this` (res)
361
+ originalRes.attemptedCall.call(res, ...originalRes.attemptedParams);
362
+ }
363
+ } catch (error) {
364
+ log$1.fatal("Express encountered an error while sending the response");
365
+ log$1.var({ responseError: error });
366
+ }
367
+
368
+ // Log response
369
+ const extras = {};
370
+ if (response) extras.body = response;
371
+ log$1.info.var({
372
+ res: summarizeResponse(res, extras),
373
+ });
374
+
375
+ // Clean up the public logger
376
+ log.untag("handler");
377
+
378
+ //
379
+ //
380
+ // Return
381
+ //
5
382
 
6
- var index = {};
383
+ return response;
384
+ };
385
+ };
7
386
 
8
- export { index as default };
387
+ export { expressHandler };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaypie/express",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "author": "Finlayson Studio",
5
5
  "type": "module",
6
6
  "exports": {
@@ -29,8 +29,10 @@
29
29
  "prepublish": "npm run build",
30
30
  "test": "vitest",
31
31
  "test:spec:decorateResponse.helper": "vitest run ./src/__tests__/decorateResponse.helper.spec.js",
32
+ "test:spec:echo.handler": "vitest run ./src/__tests__/echo.handler.spec.js",
32
33
  "test:spec:expressHandler": "vitest run ./src/__tests__/expressHandler.spec.js",
33
34
  "test:spec:getCurrentInvokeUuid.adapter": "vitest run ./src/__tests__/getCurrentInvokeUuid.adapter.spec.js",
35
+ "test:spec:http.handler": "vitest run ./src/__tests__/http.handler.spec.js",
34
36
  "test:spec:index": "vitest run ./src/__tests__/index.spec.js",
35
37
  "test:spec:summarizeRequest.helper": "vitest run ./src/__tests__/summarizeRequest.helper.spec.js",
36
38
  "test:spec:summarizeResponse.helper": "vitest run ./src/__tests__/summarizeResponse.helper.spec.js",
@@ -0,0 +1,32 @@
1
+ import { validate } from "@jaypie/core";
2
+
3
+ import expressHandler from "./expressHandler.js";
4
+ import summarizeRequest from "./summarizeRequest.helper.js";
5
+
6
+ //
7
+ //
8
+ // Main
9
+ //
10
+
11
+ const echo = (context = {}) => {
12
+ validate.object(context);
13
+ // Give a default name if there isn't one
14
+ if (!context.name) {
15
+ context.name = "_echo";
16
+ }
17
+
18
+ // Return a function that will be used as an express route
19
+ return expressHandler(async (req) => {
20
+ console.log("req.body :>> ", req.body);
21
+ return {
22
+ req: summarizeRequest(req),
23
+ };
24
+ }, context);
25
+ };
26
+
27
+ //
28
+ //
29
+ // Export
30
+ //
31
+
32
+ export default echo;
@@ -95,7 +95,7 @@ const expressHandler = (
95
95
  );
96
96
  };
97
97
  res.status = (...params) => {
98
- originalRes.statusSent = true;
98
+ originalRes.statusSent = params;
99
99
  return originalRes.status(...params);
100
100
  };
101
101
 
@@ -136,7 +136,7 @@ const expressHandler = (
136
136
  }
137
137
 
138
138
  let response;
139
- let status;
139
+ let status = HTTP.CODE.OK;
140
140
 
141
141
  try {
142
142
  libLogger.trace("[jaypie] Lambda execution");
@@ -182,13 +182,13 @@ const expressHandler = (
182
182
  // Decorate response
183
183
  decorateResponse(res, { handler: name });
184
184
 
185
+ // Allow the sent status to override the status in the response
186
+ if (originalRes.statusSent) {
187
+ status = originalRes.statusSent;
188
+ }
189
+
185
190
  // Send response
186
191
  try {
187
- // Status
188
- if (status && !originalRes.statusSent) {
189
- res.status(status);
190
- }
191
-
192
192
  if (!originalRes.attemptedCall) {
193
193
  // Body
194
194
  if (response) {
@@ -196,19 +196,18 @@ const expressHandler = (
196
196
  if (typeof response.json === "function") {
197
197
  res.json(response.json());
198
198
  } else {
199
- res.json(response);
199
+ res.status(status).json(response);
200
200
  }
201
201
  } else if (typeof response === "string") {
202
202
  try {
203
- res.json(JSON.parse(response));
203
+ res.status(status).json(JSON.parse(response));
204
204
  } catch (error) {
205
- res.send(response);
205
+ res.status(status).send(response);
206
206
  }
207
207
  } else if (response === true) {
208
- res.status(HTTP.CODE.CREATED);
209
- res.send();
208
+ res.status(HTTP.CODE.CREATED).send();
210
209
  } else {
211
- res.send(response);
210
+ res.status(status).send(response);
212
211
  }
213
212
  } else {
214
213
  // No response
@@ -0,0 +1,73 @@
1
+ import {
2
+ BadGatewayError,
3
+ BadRequestError,
4
+ ForbiddenError,
5
+ GatewayTimeoutError,
6
+ GoneError,
7
+ HTTP,
8
+ InternalError,
9
+ log,
10
+ MethodNotAllowedError,
11
+ NotFoundError,
12
+ TeapotError,
13
+ UnauthorizedError,
14
+ UnavailableError,
15
+ } from "@jaypie/core";
16
+
17
+ import expressHandler from "./expressHandler.js";
18
+
19
+ //
20
+ //
21
+ // Main
22
+ //
23
+
24
+ const http = (statusCode = HTTP.CODE.OK, context = {}) => {
25
+ // Give a default name if there isn't one
26
+ if (!context.name) {
27
+ context.name = "_http";
28
+ }
29
+
30
+ // Return a function that will be used as an express route
31
+ return expressHandler(async (req, res) => {
32
+ // Map the most throwable status codes to errors and throw them!
33
+ const error = {
34
+ [HTTP.CODE.BAD_REQUEST]: BadRequestError,
35
+ [HTTP.CODE.UNAUTHORIZED]: UnauthorizedError,
36
+ [HTTP.CODE.FORBIDDEN]: ForbiddenError,
37
+ [HTTP.CODE.NOT_FOUND]: NotFoundError,
38
+ [HTTP.CODE.METHOD_NOT_ALLOWED]: MethodNotAllowedError,
39
+ [HTTP.CODE.GONE]: GoneError,
40
+ [HTTP.CODE.TEAPOT]: TeapotError,
41
+ [HTTP.CODE.INTERNAL_ERROR]: InternalError,
42
+ [HTTP.CODE.BAD_GATEWAY]: BadGatewayError,
43
+ [HTTP.CODE.UNAVAILABLE]: UnavailableError,
44
+ [HTTP.CODE.GATEWAY_TIMEOUT]: GatewayTimeoutError,
45
+ };
46
+
47
+ // If this maps to an error, throw it
48
+ if (error[statusCode]) {
49
+ log.trace(
50
+ `@knowdev/express: gracefully throwing ${statusCode} up to projectHandler`,
51
+ );
52
+ throw new error[statusCode]();
53
+ }
54
+
55
+ // If this is an error and we didn't get thrown, log a warning
56
+ if (statusCode >= 400) {
57
+ log.warn(
58
+ `@knowdev/express: status code ${statusCode} not mapped as throwable`,
59
+ );
60
+ }
61
+
62
+ // Send the response
63
+ res.status(statusCode);
64
+ return statusCode === HTTP.CODE.NO_CONTENT ? null : {};
65
+ }, context);
66
+ };
67
+
68
+ //
69
+ //
70
+ // Export
71
+ //
72
+
73
+ export default http;
package/src/index.js CHANGED
@@ -1,6 +1 @@
1
- //
2
- //
3
- // Export
4
- //
5
-
6
- export default {};
1
+ export { default as expressHandler } from "./expressHandler.js";