@trayio/express 5.35.0-unstable.fd7158a → 6.0.0
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/http/ExpressHttpController.d.ts +3 -3
- package/dist/http/ExpressHttpController.d.ts.map +1 -1
- package/dist/http/ExpressHttpController.js +63 -37
- package/dist/http/ExpressHttpController.unit.test.js +36 -12
- package/dist/http/ExpressHttpServer.d.ts +1 -0
- package/dist/http/ExpressHttpServer.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as O from 'fp-ts/Option';
|
|
2
2
|
import { Router } from 'express-serve-static-core';
|
|
3
3
|
import { HttpController } from '@trayio/commons/http/HttpController';
|
|
4
4
|
import { Logging } from '@trayio/commons/logging/Logging';
|
|
5
5
|
export declare class ExpressHttpController {
|
|
6
6
|
protected controller: HttpController;
|
|
7
7
|
protected baseTmpPathForUploadedFiles: string;
|
|
8
|
-
protected logger:
|
|
8
|
+
protected logger: O.Option<Logging>;
|
|
9
9
|
protected maxFiles: number;
|
|
10
|
-
constructor(controller: HttpController, baseTmpPathForUploadedFiles: string
|
|
10
|
+
constructor(controller: HttpController, baseTmpPathForUploadedFiles: string, logger: O.Option<Logging>, maxFiles?: number);
|
|
11
11
|
private addRoute;
|
|
12
12
|
addRoutes: (router: Router) => Router;
|
|
13
13
|
private parseRequestBody;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpressHttpController.d.ts","sourceRoot":"","sources":["../../src/http/ExpressHttpController.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExpressHttpController.d.ts","sourceRoot":"","sources":["../../src/http/ExpressHttpController.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AASnD,OAAO,EACN,cAAc,EAEd,MAAM,qCAAqC,CAAC;AAG7C,OAAO,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAW1D,qBAAa,qBAAqB;IAEhC,SAAS,CAAC,UAAU,EAAE,cAAc;IACpC,SAAS,CAAC,2BAA2B,EAAE,MAAM;IAC7C,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IACnC,SAAS,CAAC,QAAQ,EAAE,MAAM;gBAHhB,UAAU,EAAE,cAAc,EAC1B,2BAA2B,EAAE,MAAe,EAC5C,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EACzB,QAAQ,GAAE,MAAU;IAG/B,OAAO,CAAC,QAAQ,CA8Fd;IAEF,SAAS,WAAY,MAAM,YAKzB;IAEF,OAAO,CAAC,gBAAgB,CAoBtB;IAGF,OAAO,CAAC,uBAAuB,CAI7B;IAEF,OAAO,CAAC,sBAAsB,CAmC3B;IAEH,OAAO,CAAC,aAAa,CAenB;IAEF,OAAO,CAAC,YAAY,CAkBlB;IAEF,OAAO,CAAC,+BAA+B,CAWpC;CACH"}
|
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
27
|
};
|
|
5
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
29
|
exports.ExpressHttpController = void 0;
|
|
7
|
-
const
|
|
30
|
+
const TE = __importStar(require("fp-ts/TaskEither"));
|
|
31
|
+
const E = __importStar(require("fp-ts/Either"));
|
|
32
|
+
const O = __importStar(require("fp-ts/Option"));
|
|
8
33
|
const Http_1 = require("@trayio/commons/http/Http");
|
|
9
34
|
const BufferExtensions_1 = require("@trayio/commons/buffer/BufferExtensions");
|
|
10
35
|
const formidable_1 = __importDefault(require("formidable"));
|
|
@@ -35,7 +60,7 @@ class ExpressHttpController {
|
|
|
35
60
|
requestBody = await this.parseRequestBody(req);
|
|
36
61
|
}
|
|
37
62
|
catch (err) {
|
|
38
|
-
if (
|
|
63
|
+
if (O.isSome(this.logger)) {
|
|
39
64
|
this.logger.value.warning(`Issue in the request: ${err.message}`, {
|
|
40
65
|
path: req.path,
|
|
41
66
|
});
|
|
@@ -50,13 +75,13 @@ class ExpressHttpController {
|
|
|
50
75
|
error: `Invalid request: ${err.message}`,
|
|
51
76
|
});
|
|
52
77
|
}
|
|
53
|
-
const httpResponse = await
|
|
78
|
+
const httpResponse = await endpoint.execute(this.logger)(Http_1.HttpRequest.create({
|
|
54
79
|
headers,
|
|
55
80
|
pathParams: req.params,
|
|
56
81
|
queryString: req.query,
|
|
57
82
|
body: requestBody,
|
|
58
|
-
clientIp:
|
|
59
|
-
})));
|
|
83
|
+
clientIp: O.fromNullable(clientIp),
|
|
84
|
+
}))();
|
|
60
85
|
const sanitizeHeaderValue = (value) => {
|
|
61
86
|
let processedValue = value;
|
|
62
87
|
if (Array.isArray(value)) {
|
|
@@ -105,7 +130,11 @@ class ExpressHttpController {
|
|
|
105
130
|
parseRequestBody = async (req) => {
|
|
106
131
|
const contentType = req.headers['content-type'];
|
|
107
132
|
if (contentType && contentType.startsWith('multipart/form-data')) {
|
|
108
|
-
|
|
133
|
+
const multiPartResponse = await this.parseMultipartFormData(req)();
|
|
134
|
+
const multiPartBody = E.getOrElse((error) => {
|
|
135
|
+
throw new Error(error.message);
|
|
136
|
+
})(multiPartResponse);
|
|
137
|
+
return multiPartBody;
|
|
109
138
|
}
|
|
110
139
|
return this.parseGeneralRequestBody(req);
|
|
111
140
|
};
|
|
@@ -114,37 +143,34 @@ class ExpressHttpController {
|
|
|
114
143
|
const body = Object.keys(req.body).length === 0 ? new ArrayBuffer(0) : req.body;
|
|
115
144
|
return BufferExtensions_1.BufferExtensions.arrayBufferToReadable(body);
|
|
116
145
|
};
|
|
117
|
-
parseMultipartFormData = (req) =>
|
|
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
|
-
catch: (error) => new Error(error.message),
|
|
147
|
-
});
|
|
146
|
+
parseMultipartFormData = (req) => TE.tryCatch(() => new Promise((resolve, reject) => {
|
|
147
|
+
/*
|
|
148
|
+
* NOTE: inorder to use any other underlying file storage other than node fs, we would have to use
|
|
149
|
+
* formidable's fileWriteStreamHandler option that enables formidable to write a file to a stream.
|
|
150
|
+
* When used the fileWriteStreamHandler option with passThrough stream, we ran into errors that we didn't have
|
|
151
|
+
* time to solve, but ideally, we would be using the FileStorage interface instead of letting formidable
|
|
152
|
+
* write to the file system directly.
|
|
153
|
+
*/
|
|
154
|
+
const form = (0, formidable_1.default)({
|
|
155
|
+
maxFiles: this.maxFiles,
|
|
156
|
+
maxFileSize: 100 * 1024 * 1024,
|
|
157
|
+
minFileSize: 0,
|
|
158
|
+
allowEmptyFiles: true,
|
|
159
|
+
uploadDir: this.baseTmpPathForUploadedFiles,
|
|
160
|
+
});
|
|
161
|
+
form.parse(req, (err, fields, files) => {
|
|
162
|
+
if (err) {
|
|
163
|
+
reject(err);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
const body = {
|
|
167
|
+
fields: this.flattenFields(fields),
|
|
168
|
+
files: this.flattenFiles(files),
|
|
169
|
+
};
|
|
170
|
+
resolve(body);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}), (error) => new Error(error.message));
|
|
148
174
|
flattenFields = (fields) => {
|
|
149
175
|
const flattenedFields = {};
|
|
150
176
|
Object.entries(fields).forEach(([key, value]) => {
|
|
@@ -1,7 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const effect_1 = require("effect");
|
|
4
26
|
const Http_1 = require("@trayio/commons/http/Http");
|
|
27
|
+
const O = __importStar(require("fp-ts/Option"));
|
|
28
|
+
const T = __importStar(require("fp-ts/Task"));
|
|
5
29
|
const stream_1 = require("stream");
|
|
6
30
|
const ExpressHttpController_1 = require("./ExpressHttpController");
|
|
7
31
|
describe('ExpressHttpController', () => {
|
|
@@ -18,7 +42,7 @@ describe('ExpressHttpController', () => {
|
|
|
18
42
|
mockHttpController = {
|
|
19
43
|
getEndpoints: jest.fn().mockReturnValue([mockEndpoint]),
|
|
20
44
|
};
|
|
21
|
-
expressHttpController = new ExpressHttpController_1.ExpressHttpController(mockHttpController, '/tmp',
|
|
45
|
+
expressHttpController = new ExpressHttpController_1.ExpressHttpController(mockHttpController, '/tmp', O.none);
|
|
22
46
|
mockRouter = {
|
|
23
47
|
get: jest.fn(),
|
|
24
48
|
post: jest.fn(),
|
|
@@ -82,7 +106,7 @@ describe('ExpressHttpController', () => {
|
|
|
82
106
|
headers: {},
|
|
83
107
|
body: mockResponse,
|
|
84
108
|
};
|
|
85
|
-
mockEndpoint.execute.mockReturnValue(() =>
|
|
109
|
+
mockEndpoint.execute.mockReturnValue(() => T.of(httpResponse));
|
|
86
110
|
expressHttpController.addRoutes(mockRouter);
|
|
87
111
|
// eslint-disable-next-line prefer-destructuring
|
|
88
112
|
routeHandler = mockRouter.get.mock.calls[0][1];
|
|
@@ -99,7 +123,7 @@ describe('ExpressHttpController', () => {
|
|
|
99
123
|
},
|
|
100
124
|
}),
|
|
101
125
|
};
|
|
102
|
-
mockEndpoint.execute.mockReturnValue(() =>
|
|
126
|
+
mockEndpoint.execute.mockReturnValue(() => T.of(httpResponse));
|
|
103
127
|
await routeHandler(mockReq, mockRes);
|
|
104
128
|
expect(mockRes.setHeader).toHaveBeenCalledWith('test-header', 'value_with_control_chars');
|
|
105
129
|
});
|
|
@@ -115,7 +139,7 @@ describe('ExpressHttpController', () => {
|
|
|
115
139
|
},
|
|
116
140
|
}),
|
|
117
141
|
};
|
|
118
|
-
mockEndpoint.execute.mockReturnValue(() =>
|
|
142
|
+
mockEndpoint.execute.mockReturnValue(() => T.of(httpResponse));
|
|
119
143
|
await routeHandler(mockReq, mockRes);
|
|
120
144
|
expect(mockRes.setHeader).toHaveBeenCalledWith('test-header', 'value_with_line__breaks');
|
|
121
145
|
});
|
|
@@ -131,7 +155,7 @@ describe('ExpressHttpController', () => {
|
|
|
131
155
|
},
|
|
132
156
|
}),
|
|
133
157
|
};
|
|
134
|
-
mockEndpoint.execute.mockReturnValue(() =>
|
|
158
|
+
mockEndpoint.execute.mockReturnValue(() => T.of(httpResponse));
|
|
135
159
|
await routeHandler(mockReq, mockRes);
|
|
136
160
|
expect(mockRes.setHeader).toHaveBeenCalledWith('test-header', 'value"with\\quotes');
|
|
137
161
|
});
|
|
@@ -147,7 +171,7 @@ describe('ExpressHttpController', () => {
|
|
|
147
171
|
},
|
|
148
172
|
}),
|
|
149
173
|
};
|
|
150
|
-
mockEndpoint.execute.mockReturnValue(() =>
|
|
174
|
+
mockEndpoint.execute.mockReturnValue(() => T.of(httpResponse));
|
|
151
175
|
await routeHandler(mockReq, mockRes);
|
|
152
176
|
expect(mockRes.setHeader).toHaveBeenCalledWith('test-header', 'value with spaces');
|
|
153
177
|
});
|
|
@@ -163,7 +187,7 @@ describe('ExpressHttpController', () => {
|
|
|
163
187
|
},
|
|
164
188
|
}),
|
|
165
189
|
};
|
|
166
|
-
mockEndpoint.execute.mockReturnValue(() =>
|
|
190
|
+
mockEndpoint.execute.mockReturnValue(() => T.of(httpResponse));
|
|
167
191
|
await routeHandler(mockReq, mockRes);
|
|
168
192
|
expect(mockRes.setHeader).toHaveBeenCalledWith('test-header', 'value1,value2,value3');
|
|
169
193
|
});
|
|
@@ -179,7 +203,7 @@ describe('ExpressHttpController', () => {
|
|
|
179
203
|
},
|
|
180
204
|
}),
|
|
181
205
|
};
|
|
182
|
-
mockEndpoint.execute.mockReturnValue(() =>
|
|
206
|
+
mockEndpoint.execute.mockReturnValue(() => T.of(httpResponse));
|
|
183
207
|
await routeHandler(mockReq, mockRes);
|
|
184
208
|
expect(mockRes.setHeader).toHaveBeenCalledWith('content-length', '123');
|
|
185
209
|
});
|
|
@@ -195,7 +219,7 @@ describe('ExpressHttpController', () => {
|
|
|
195
219
|
},
|
|
196
220
|
}),
|
|
197
221
|
};
|
|
198
|
-
mockEndpoint.execute.mockReturnValue(() =>
|
|
222
|
+
mockEndpoint.execute.mockReturnValue(() => T.of(httpResponse));
|
|
199
223
|
await routeHandler(mockReq, mockRes);
|
|
200
224
|
expect(mockRes.setHeader).toHaveBeenCalledWith('test-header', 'value_with__"multiple\\issues"');
|
|
201
225
|
});
|
|
@@ -211,7 +235,7 @@ describe('ExpressHttpController', () => {
|
|
|
211
235
|
},
|
|
212
236
|
}),
|
|
213
237
|
};
|
|
214
|
-
mockEndpoint.execute.mockReturnValue(() =>
|
|
238
|
+
mockEndpoint.execute.mockReturnValue(() => T.of(httpResponse));
|
|
215
239
|
await routeHandler(mockReq, mockRes);
|
|
216
240
|
expect(mockRes.setHeader).toHaveBeenCalledWith('test-header', 'value_with_em_dash');
|
|
217
241
|
});
|
|
@@ -233,7 +257,7 @@ describe('ExpressHttpController', () => {
|
|
|
233
257
|
status: jest.fn().mockReturnThis(),
|
|
234
258
|
json: jest.fn().mockReturnThis(),
|
|
235
259
|
};
|
|
236
|
-
expressHttpController = new ExpressHttpController_1.ExpressHttpController(mockHttpController, '/tmp',
|
|
260
|
+
expressHttpController = new ExpressHttpController_1.ExpressHttpController(mockHttpController, '/tmp', O.some({
|
|
237
261
|
warning: jest.fn(),
|
|
238
262
|
}));
|
|
239
263
|
expressHttpController.addRoutes(mockRouter);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpressHttpServer.d.ts","sourceRoot":"","sources":["../../src/http/ExpressHttpServer.ts"],"names":[],"mappings":"AAEA,OAAO,IAAI,GAAG,QAAQ,MAAM,CAAC,CAAC;AAE9B,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,UAAU,oBAAoB;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,CACpB,MAAM,EAAE,MAAM,CAAC,OAAO,IAAI,CAAC,eAAe,EAAE,OAAO,IAAI,CAAC,cAAc,CAAC,KACnE,SAAS,CAAC;CACf;AAED,eAAO,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"ExpressHttpServer.d.ts","sourceRoot":"","sources":["../../src/http/ExpressHttpServer.ts"],"names":[],"mappings":";AAEA,OAAO,IAAI,GAAG,QAAQ,MAAM,CAAC,CAAC;AAE9B,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,UAAU,oBAAoB;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,CACpB,MAAM,EAAE,MAAM,CAAC,OAAO,IAAI,CAAC,eAAe,EAAE,OAAO,IAAI,CAAC,cAAc,CAAC,KACnE,SAAS,CAAC;CACf;AAED,eAAO,MAAM,WAAW,gBACV,qBAAqB,EAAE,WAC3B,oBAAoB,yEA6D7B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trayio/express",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "Express extensions and implementations",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./*": "./dist/*.js"
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
"access": "public"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@trayio/commons": "
|
|
17
|
+
"@trayio/commons": "6.0.0",
|
|
18
18
|
"@types/express-serve-static-core": "4.17.41",
|
|
19
19
|
"cors": "2.8.5",
|
|
20
|
-
"effect": "3.22.2",
|
|
21
20
|
"express": "4.20.0",
|
|
22
|
-
"formidable": "3.5.1"
|
|
21
|
+
"formidable": "3.5.1",
|
|
22
|
+
"fp-ts": "2.12.3"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/cors": "2.8.17",
|