systemlynx 1.10.8 → 1.12.8

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/client.js ADDED
@@ -0,0 +1,21 @@
1
+ //These are all the abstractions that make up SystemLynx
2
+ const createClient = require("./systemlynx/Client/Client");
3
+ const createHttpClient = require("./systemlynx/HttpClient/HttpClient");
4
+ const createDispatcher = require("./systemlynx/Dispatcher/Dispatcher");
5
+
6
+ const HttpClient = createHttpClient();
7
+ const Client = createClient();
8
+ const Dispatcher = createDispatcher();
9
+
10
+ module.exports = {
11
+ //Export these pre-created objects for convenient object destructuring
12
+ //These are the main utilities for app development
13
+ HttpClient,
14
+ Client,
15
+ Dispatcher,
16
+ //export all modules themselves
17
+ //all these modules export a functions
18
+ createClient,
19
+ createHttpClient,
20
+ createDispatcher,
21
+ };
package/index.test.js CHANGED
@@ -115,13 +115,13 @@ describe("SystemLynx Objects", () => {
115
115
  .that.has.all.keys([
116
116
  "startService",
117
117
  "addModule",
118
- "addRouteHandler",
118
+ "addMiddleware",
119
119
  "server",
120
120
  "WebSocket",
121
121
  ])
122
122
  .that.respondsTo("startService")
123
123
  .that.respondsTo("addModule")
124
- .that.respondsTo("addRouteHandler");
124
+ .that.respondsTo("addMiddleware");
125
125
  });
126
126
 
127
127
  it("should return a new instance of a Service", () => {
package/package.json CHANGED
@@ -1,17 +1,20 @@
1
1
  {
2
2
  "name": "systemlynx",
3
- "version": "1.10.8",
3
+ "version": "1.12.8",
4
4
  "description": "",
5
5
  "main": "index.js",
6
+ "browser": {
7
+ "./index.js": "./client.js"
8
+ },
6
9
  "scripts": {
7
10
  "start": "node index.js",
8
11
  "test": "jest --verbose"
9
12
  },
10
13
  "dependencies": {
14
+ "axios": "^1.5.0",
11
15
  "express": "^4.18.2",
12
16
  "mime": "^2.4.0",
13
17
  "multer": "^1.4.2",
14
- "request": "^2.88.0",
15
18
  "shortid": "^2.2.14",
16
19
  "socket.io": "^2.2.0"
17
20
  },
@@ -36,5 +39,10 @@
36
39
  "commitizen": {
37
40
  "path": "./node_modules/cz-conventional-changelog"
38
41
  }
42
+ },
43
+ "jest": {
44
+ "moduleNameMapper": {
45
+ "axios": "axios/dist/node/axios.cjs"
46
+ }
39
47
  }
40
48
  }
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- const { isNode } = require("../../utils/ProcessChecker");
3
2
  const createService = require("../Service/Service");
4
3
  const createDispatcher = require("../Dispatcher/Dispatcher");
5
4
  const initializeApp = require("./components/initializeApp");
@@ -11,6 +10,7 @@ module.exports = function createApp(server, WebSocket, customClient) {
11
10
  const systemContext = SystemLynxContext(system);
12
11
  const App = new createDispatcher(undefined, systemContext);
13
12
  const plugins = [];
13
+
14
14
  const init = () => {
15
15
  plugins.forEach((plugin) => {
16
16
  if (typeof plugin === "function") plugin.apply({}, [App, system]);
@@ -23,32 +23,30 @@ module.exports = function createApp(server, WebSocket, customClient) {
23
23
  init();
24
24
  }, 0);
25
25
 
26
- if (isNode) {
27
- system.Service = createService(server, WebSocket, systemContext);
26
+ system.Service = createService(server, WebSocket, systemContext);
27
+ App.server = system.Service.server;
28
+ App.WebSocket = system.Service.WebSocket;
28
29
 
29
- App.startService = (options) => {
30
- system.routing = options;
31
- if (!timeoutId) {
32
- timeoutId = setTimeout(() => {
33
- timeoutId = null;
34
- init();
35
- }, 0);
36
- }
37
- return App;
38
- };
30
+ App.startService = (options) => {
31
+ system.routing = options;
32
+ if (!timeoutId) {
33
+ timeoutId = setTimeout(() => {
34
+ timeoutId = null;
35
+ init();
36
+ }, 0);
37
+ }
38
+ return App;
39
+ };
39
40
 
40
- App.module = (name, __constructor) => {
41
- system.modules.push({ name, __constructor });
42
- return App;
43
- };
41
+ App.module = (name, __constructor) => {
42
+ system.modules.push({ name, __constructor });
43
+ return App;
44
+ };
44
45
 
45
- App.before = (...args) => {
46
- system.Service.before(...args);
47
- return App;
48
- };
49
- App.server = system.Service.server;
50
- App.WebSocket = system.Service.WebSocket;
51
- }
46
+ App.before = (...args) => {
47
+ system.Service.before(...args);
48
+ return App;
49
+ };
52
50
 
53
51
  App.loadService = (name, url) => {
54
52
  system.services.push({ name, url, onLoad: null, client: {} });
@@ -75,5 +73,6 @@ module.exports = function createApp(server, WebSocket, customClient) {
75
73
  plugins.push(plugin);
76
74
  return App;
77
75
  };
76
+
78
77
  return App;
79
78
  };
@@ -7,9 +7,9 @@ module.exports = async function initApp(system, App, customClient, systemContext
7
7
  if (!configComplete)
8
8
  console.warn(
9
9
  `
10
- continuationERROR: Failed to call continuation function in App Configuariotn module
10
+ continuationERROR: Failed to call next function in App.config() callback
11
11
 
12
- Fix: Must call next function during App.config( constructor(=>next) )
12
+ Fix: Must call next function during App.config( callback(=>next) )
13
13
 
14
14
  `
15
15
  );
@@ -24,17 +24,14 @@ module.exports = function SystemLynxClientModule(
24
24
  const reconnectModule = async (cb) => {
25
25
  try {
26
26
  const url = connectionData.serviceUrl + `?modules=${name}`;
27
- const { modules, port, host } = await loadConnectionData(url);
28
- const { namespace, route } = modules[0];
29
- ClientModule.__setConnection(host, port, route, namespace);
27
+ const { modules, port, host } = await loadConnectionData(url);
28
+ const { namespace, route } = modules[0];
29
+ ClientModule.__setConnection(host, port, route, namespace);
30
30
 
31
- if (typeof cb === "function") cb();
31
+ if (typeof cb === "function") cb();
32
32
  } catch (error) {
33
- console.error(
34
- `[SystemLynx][ClientModule]: Failed to reconnect service @${connData.serviceUrl}`
35
- );
33
+ console.error(`[SystemLynx][ClientModule]: Failed to reconnect service @${url}`);
36
34
  }
37
-
38
35
  };
39
36
  const protocol = getProtocol(serviceUrl);
40
37
  methods.forEach(({ method, fn }) => {
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
+
3
+ const { isNode } = require("../../../utils/ProcessChecker");
4
+ const { convertToReadStream } = require("../components/convertToReadStream");
2
5
  const isObject = (value) =>
3
6
  typeof value === "object" ? (!value ? false : !Array.isArray(value)) : false;
4
7
  const isEmpty = (obj) => Object.getOwnPropertyNames(obj).length === 0;
@@ -15,48 +18,55 @@ module.exports = function ServiceRequestHandler(
15
18
  Service,
16
19
  reconnectModule
17
20
  ) {
18
- const ClientModule = this;
19
21
  return function sendRequest() {
20
22
  const __arguments = Array.from(arguments);
21
23
 
22
24
  const tryRequest = (cb, errCount = 0) => {
23
- const { route, port, host } = ClientModule.__connectionData();
25
+ const { route, port, host } = this.__connectionData();
24
26
  const singleFileURL = `${protocol}${host}:${port}/sf${route}/${fn}`;
25
27
  const multiFileURL = `${protocol}${host}:${port}/mf${route}/${fn}`;
26
28
  const defaultURL = `${protocol}${host}:${port}${route}/${fn}`;
27
29
  const { file, files } = __arguments[0] || {};
28
30
  const url = file ? singleFileURL : files ? multiFileURL : defaultURL;
29
- const defaultHeaders = ClientModule.headers();
31
+ const defaultHeaders = this.headers();
30
32
  const headers = !isEmpty(defaultHeaders) ? defaultHeaders : Service.headers();
31
- if (url === defaultURL)
33
+ if (url === defaultURL) {
34
+ const query =
35
+ method === "get" && isObject(__arguments[0]) ? makeQuery(__arguments[0]) : "";
32
36
  httpClient
33
37
  .request({
34
- url: `${url}${
35
- method === "get" && isObject(__arguments[0])
36
- ? makeQuery(__arguments[0])
37
- : ""
38
- }`,
38
+ url: `${url}${query}`,
39
39
  method,
40
40
  body: { __arguments },
41
41
  headers,
42
42
  })
43
43
  .then((results) => cb(null, results))
44
44
  .catch((err) => ErrorHandler(err, errCount, cb));
45
- else
45
+ } else {
46
+ delete __arguments[0].file;
47
+ delete __arguments[0].files;
48
+ const formData = {};
49
+ formData.__arguments = __arguments;
50
+ if (file) formData.file = isNode ? convertToReadStream(file) : file;
51
+ if (Array.isArray(files))
52
+ formData.files = isNode ? files.map(convertToReadStream) : files;
46
53
  httpClient
47
54
  .upload({
48
55
  url,
49
56
  method,
50
- formData: { ...__arguments[0], __arguments },
57
+ formData,
51
58
  headers,
52
59
  })
53
60
  .then((results) => cb(null, results))
54
61
  .catch((err) => ErrorHandler(err, errCount, cb));
62
+ }
55
63
  };
56
64
 
57
65
  const ErrorHandler = (err, errCount, cb) => {
58
- if (err.SystemLynxService) {
59
- cb(err);
66
+ if (!err.isAxiosError) {
67
+ throw err;
68
+ } else if (err.response.data.SystemLynxService) {
69
+ cb(err.response.data);
60
70
  } else if (errCount <= 3) {
61
71
  errCount++;
62
72
  if (reconnectModule) reconnectModule(() => tryRequest(cb, errCount));
@@ -0,0 +1,41 @@
1
+ const fs = require("fs");
2
+ const { Readable } = require("stream");
3
+
4
+ function convertStringToReadStream(path) {
5
+ try {
6
+ const stats = fs.statSync(path);
7
+ if (stats.isFile()) {
8
+ return fs.createReadStream(path);
9
+ } else {
10
+ throw new Error(`File does not exists: ${path}`);
11
+ }
12
+ } catch (error) {
13
+ throw new Error(
14
+ "Error occurred while converting file path to Readable stream:",
15
+ error
16
+ );
17
+ }
18
+ }
19
+ function bufferToStream(buffer) {
20
+ // Create a custom readable stream
21
+ const readableStream = new Readable();
22
+ // Push the buffer data into the stream
23
+ readableStream.push(buffer);
24
+ // Signal the end of the stream
25
+ readableStream.push(null);
26
+ return readableStream;
27
+ }
28
+ function convertToReadStream(input) {
29
+ if (input instanceof Readable) {
30
+ return input;
31
+ } // else if (Buffer.isBuffer(input)) {
32
+ // return bufferToStream(input);
33
+ // }
34
+ else if (typeof input === "string") {
35
+ return convertStringToReadStream(input);
36
+ } else {
37
+ throw new Error("File input must be either a Readable stream, or a file path.");
38
+ }
39
+ }
40
+
41
+ module.exports = { convertToReadStream };
@@ -5,20 +5,22 @@ module.exports = function loadConnectionData(
5
5
  ) {
6
6
  const errors = [];
7
7
 
8
- return new Promise(function getData(resolve, reject) {
9
- httpClient.request({ method: "GET", url }, (err, results) => {
10
- if (err) {
11
- errors.push(err);
8
+ return new Promise(async function getData(resolve, reject) {
9
+ try {
10
+ const results = await httpClient.request({ url });
11
+ resolve(results);
12
+ } catch (error) {
13
+ errors.push(error);
12
14
 
13
- if (errors.length < limit)
14
- setTimeout(() => getData(resolve, reject), errors.length * wait);
15
- else {
16
- console.error(
17
- `[SystemLynx][Client]: Failed to load Service @${url} after ${errors.length} attempts.\n`
18
- );
19
- reject(err);
20
- }
21
- } else resolve(results);
22
- });
15
+ if (errors.length < limit)
16
+ setTimeout(() => getData(resolve, reject), errors.length * wait);
17
+ else {
18
+ console.error(
19
+ `[SystemLynx][Client]: Failed to load Service @${url} after ${errors.length} attempts.\n`
20
+ );
21
+ console.error(errors);
22
+ reject(errors);
23
+ }
24
+ }
23
25
  });
24
26
  };
@@ -1,3 +1,4 @@
1
+ const fs = require("fs");
1
2
  const { expect } = require("chai");
2
3
  const createClient = require("../Client");
3
4
  const createService = require("../../Service/Service");
@@ -5,6 +6,7 @@ const Service = createService();
5
6
  const port = 6757;
6
7
  const route = "service-test";
7
8
  const url = `http://localhost:${port}/${route}`;
9
+ const TEST_FILE = process.cwd() + "/test.file.json";
8
10
 
9
11
  describe("createClient()", () => {
10
12
  it("should return a SystemLynx Client", () => {
@@ -175,7 +177,7 @@ describe("Service", () => {
175
177
  const useREST = true;
176
178
  Service.module("restTester", function () {
177
179
  this.get = (data) => ({ REST_TEST_PASSED: true, getResponse: true, ...data });
178
- this.put = () => ({ REST_TEST_PASSED: true, putResponse: true });
180
+ this.put = (data) => ({ REST_TEST_PASSED: true, putResponse: true, ...data });
179
181
  this.post = () => ({ REST_TEST_PASSED: true, postResponse: true });
180
182
  this.delete = () => ({ REST_TEST_PASSED: true, deleteResponse: true });
181
183
  });
@@ -183,7 +185,7 @@ describe("Service", () => {
183
185
  await Service.startService({ route, port, useREST });
184
186
  const buAPI = await Client.loadService(url);
185
187
  const getResponse = await buAPI.restTester.get({ name: "GET TEST", id: 12 });
186
- const putResponse = await buAPI.restTester.put();
188
+ const putResponse = await buAPI.restTester.put({ name: "PUT TEST", id: 13 });
187
189
  const postResponse = await buAPI.restTester.post();
188
190
  const deleteResponse = await buAPI.restTester.delete();
189
191
 
@@ -193,7 +195,12 @@ describe("Service", () => {
193
195
  name: "GET TEST",
194
196
  id: 12,
195
197
  });
196
- expect(putResponse).to.deep.equal({ REST_TEST_PASSED: true, putResponse: true });
198
+ expect(putResponse).to.deep.equal({
199
+ REST_TEST_PASSED: true,
200
+ putResponse: true,
201
+ name: "PUT TEST",
202
+ id: 13,
203
+ });
197
204
  expect(postResponse).to.deep.equal({ REST_TEST_PASSED: true, postResponse: true });
198
205
  expect(deleteResponse).to.deep.equal({
199
206
  REST_TEST_PASSED: true,
@@ -201,17 +208,13 @@ describe("Service", () => {
201
208
  });
202
209
  });
203
210
 
204
- it("should be able to use 'useReturnValue' configuration option to enable synchronous return values from Module methods", async () => {
211
+ it("should be able to asynchronously return values from Module methods", async () => {
205
212
  const service = createService();
206
213
  const route = "sync/test";
207
214
  const port = 4920;
208
215
  const host = "localhost";
209
216
  const url = `http://localhost:${port}/${route}`;
210
- service.module("AsyncMath", function () {
211
- this.max = Math.max;
212
- this.min = Math.min;
213
- this.round = Math.round;
214
- });
217
+ service.module("AsyncMath", Math);
215
218
 
216
219
  await service.startService({
217
220
  route,
@@ -227,6 +230,104 @@ describe("Service", () => {
227
230
  const results3 = await AsyncMath.round(10.2);
228
231
  expect(results3).to.equal(10);
229
232
  });
233
+
234
+ it("should send proper error responses", async () => {
235
+ const service = createService();
236
+ const route = "sync/test";
237
+ const port = 7860;
238
+ const host = "localhost";
239
+ const url = `http://localhost:${port}/${route}`;
240
+ service.module("ErrorTest", function () {
241
+ this.sendError = () => {
242
+ return { status: 404, message: "test error" };
243
+ };
244
+ this.throwError = () => {
245
+ throw Error("This is my error!");
246
+ };
247
+ });
248
+
249
+ await service.startService({
250
+ route,
251
+ port,
252
+ host,
253
+ });
254
+ const Client = createClient();
255
+ const { ErrorTest } = await Client.loadService(url);
256
+ try {
257
+ await ErrorTest.sendError();
258
+ throw Error("this test should throw before this point");
259
+ } catch (error) {
260
+ expect(error).to.deep.equal({
261
+ SystemLynxService: true,
262
+ fn: "sendError",
263
+ message: "test error",
264
+ module_name: "ErrorTest",
265
+ serviceUrl: "http://localhost:7860/sync/test",
266
+ status: 404,
267
+ });
268
+ }
269
+ try {
270
+ await ErrorTest.throwError();
271
+ throw Error("this test should throw before this point");
272
+ } catch (error) {
273
+ expect(error).to.deep.equal({
274
+ SystemLynxService: true,
275
+ fn: "throwError",
276
+ message: "This is my error!",
277
+ module_name: "ErrorTest",
278
+ serviceUrl: "http://localhost:7860/sync/test",
279
+ status: 500,
280
+ });
281
+ }
282
+ });
283
+
284
+ it("should be able pass a ReadStream or file path for upload the via property names file or files on an object in the first parameter", async () => {
285
+ const service = createService();
286
+ const route = "file-upload/test";
287
+ const port = 4568;
288
+ const host = "localhost";
289
+ const url = `http://localhost:${port}/${route}`;
290
+
291
+ service.module("storage", function () {
292
+ this.save = ({ file, files, message }) => {
293
+ return { file, files, message };
294
+ };
295
+ });
296
+ await service.startService({
297
+ route,
298
+ port,
299
+ host,
300
+ });
301
+
302
+ const Client = createClient();
303
+ const { storage } = await Client.loadService(url);
304
+
305
+ const singleFileResponse = await storage.save({
306
+ file: fs.createReadStream(TEST_FILE),
307
+ message: "single file upload test confirmation",
308
+ });
309
+ const multiFileResponse = await storage.save({
310
+ files: [TEST_FILE, fs.createReadStream(TEST_FILE)],
311
+ message: "multi file upload test confirmation",
312
+ });
313
+
314
+ expect(singleFileResponse).to.be.an("object").that.has.all.keys("file", "message");
315
+ expect(singleFileResponse.message).to.be.an("string");
316
+ expect(singleFileResponse.message).to.equal("single file upload test confirmation");
317
+ expect(singleFileResponse.file).to.be.an("object");
318
+ expect(singleFileResponse.file.originalname).to.equal("test.file.json");
319
+ expect(singleFileResponse.file.mimetype).to.equal("application/json");
320
+
321
+ expect(multiFileResponse).to.be.an("object").that.has.all.keys("files", "message");
322
+ expect(multiFileResponse.message).to.be.an("string");
323
+ expect(multiFileResponse.message).to.equal("multi file upload test confirmation");
324
+ expect(multiFileResponse.files).to.be.an("array");
325
+ expect(multiFileResponse.files[0]).to.be.an("object");
326
+ expect(multiFileResponse.files[1]).to.be.an("object");
327
+ expect(multiFileResponse.files[0].originalname).to.equal("test.file.json");
328
+ expect(multiFileResponse.files[1].mimetype).to.equal("application/json");
329
+ });
330
+
230
331
  it("should maintain service and module level headers on a Client instance", async () => {
231
332
  const service = createService();
232
333
  const route = "setHeaders/test";
@@ -0,0 +1,33 @@
1
+ const { expect } = require("chai");
2
+ const fs = require("fs");
3
+ const { convertToReadStream } = require("../components/convertToReadStream"); // Update the path accordingly
4
+ // const { Readable } = require("stream");
5
+ const TEST_FILE = process.cwd() + "/test.file.json";
6
+
7
+ describe("convertToReadStream", () => {
8
+ it("should convert file path to Readable stream", () => {
9
+ const expected = fs.createReadStream(TEST_FILE);
10
+ const result = convertToReadStream(TEST_FILE);
11
+ expect(result).to.be.an.instanceOf(fs.ReadStream);
12
+ expect(result.path).to.equal(expected.path);
13
+ });
14
+
15
+ it("should handle existing Readable streams", () => {
16
+ const existingReadStream = fs.createReadStream(TEST_FILE);
17
+ const result = convertToReadStream(existingReadStream);
18
+ expect(result).to.equal(existingReadStream);
19
+ });
20
+
21
+ // it("should convert Buffer to Readable stream", () => {
22
+ // const fileContentsBuffer = fs.readFileSync(TEST_FILE);
23
+ // const result = convertToReadStream(fileContentsBuffer);
24
+ // expect(result).to.be.an.instanceOf(Readable);
25
+ // });
26
+
27
+ it("should throw error for invalid input", () => {
28
+ const invalidInput = 123;
29
+ expect(() => convertToReadStream(invalidInput)).to.throw(
30
+ "File input must be either a Readable stream, or a file path."
31
+ );
32
+ });
33
+ });
File without changes
@@ -30,11 +30,15 @@ module.exports = function createDispatcher(events = {}, systemContext) {
30
30
  if (!fn) {
31
31
  // Clear all listeners for the given event
32
32
  delete events[eventName];
33
- } else {
33
+ } else if (typeof fn === "function") {
34
34
  // Remove the listener function with the specified name from the event's listener array
35
35
  events[eventName] = events[eventName].filter((callback) => {
36
- return callback.name !== fn;
36
+ return callback.name !== fn.name;
37
37
  });
38
+ } else {
39
+ console.error(
40
+ "SystemLynxError: the second parameter of the Dispatcher.$clearEvent takes the original function to the event"
41
+ );
38
42
  }
39
43
 
40
44
  return Dispatcher;
@@ -1,44 +1,35 @@
1
- "use strict";
2
- const request = require("request");
3
- const json = true;
1
+ const { isNode } = require("../../utils/ProcessChecker");
2
+ const axios = require("axios").default;
3
+ const FormData = require("form-data");
4
+ const path = require("path");
4
5
 
5
6
  module.exports = function createHttpClient() {
6
- const Client = this || {};
7
-
8
- Client.request = ({ method, url, body, headers }, cb) => {
9
- const tryRequest = (callback) => {
10
- request({ method, url, body, json, headers }, (err, res, body) => {
11
- if (err) callback(err);
12
- else if (res.statusCode >= 400) callback(body);
13
- else callback(null, body, res);
14
- });
15
- };
16
- if (typeof cb === "function") tryRequest(cb);
17
- else
18
- return new Promise((resolve, reject) =>
19
- tryRequest((err, results) => {
20
- if (err) reject(err);
21
- else resolve(results);
22
- })
23
- );
7
+ const Client = {};
8
+ Client.request = async ({ method = "get", url, body: data, headers }) => {
9
+ method = method.toLowerCase();
10
+ const res = await axios({ url, method, headers, data });
11
+ if (res.status >= 400) {
12
+ throw res.data;
13
+ } else return res.data;
24
14
  };
25
15
 
26
- Client.upload = ({ url, formData, headers }, cb) => {
27
- const tryRequest = (callback) => {
28
- request.post({ url, formData, json, headers }, (err, res, body) => {
29
- if (err) callback(err);
30
- else if (res.statusCode >= 400) callback(body);
31
- else callback(null, body, res);
16
+ Client.upload = async ({ url, formData, headers }) => {
17
+ const { file, files, __arguments } = formData;
18
+ const form = new FormData();
19
+ if (file) form.append("file", file, isNode ? path.basename(file.path) : file.name);
20
+ if (files) {
21
+ files.forEach((file) => {
22
+ form.append("files", file, isNode ? path.basename(file.path) : file.name);
32
23
  });
33
- };
34
- if (typeof cb === "function") tryRequest(cb);
35
- else
36
- return new Promise((resolve, reject) =>
37
- tryRequest((err, results) => {
38
- if (err) reject(err);
39
- else resolve(results);
40
- })
41
- );
24
+ }
25
+ if (__arguments) form.append("__arguments", JSON.stringify(__arguments));
26
+
27
+ const res = await axios.post(url, form, {
28
+ headers: { ...headers, "Content-Type": "multipart/form-data" },
29
+ });
30
+ if (res.status >= 400) {
31
+ throw res.data;
32
+ } else return res.data;
42
33
  };
43
34
 
44
35
  return Client;
@@ -3,7 +3,7 @@ const fs = require("fs");
3
3
  const HttpSystemLynxClient = require("./HttpClient");
4
4
  const port = 4789;
5
5
  const testServerSetup = require("./test.server");
6
- //test server setup
6
+ const TEST_FILE = process.cwd() + "/test.file.json";
7
7
 
8
8
  beforeAll(() => new Promise((resolve) => testServerSetup(port, resolve)));
9
9
  describe("HttpSystemLynxClient Test", () => {
@@ -20,37 +20,13 @@ describe("HttpSystemLynxClient Test", () => {
20
20
  .that.respondsTo("upload");
21
21
  });
22
22
 
23
- it("should be able to make http requests using a callback", async () => {
24
- const results = await new Promise((resolve, reject) => {
25
- HttpClient.request(
26
- {
27
- method: "GET",
28
- url: "http://localhost:4789/test",
29
- body: { getWithCallback: true },
30
- },
31
- (err, results) => {
32
- if (err) reject(err);
33
- else resolve(results);
34
- }
35
- );
36
- });
37
-
38
- expect(results).to.be.an("Object").that.deep.equal({
39
- getWithCallback: true,
40
- testPassed: true,
41
- method: "GET",
42
- });
43
- });
44
-
45
23
  it("should be able to make http requests using a promise", async () => {
46
24
  const results = await HttpClient.request({
47
25
  method: "GET",
48
26
  url,
49
- body: { getWithPromise: true },
50
27
  });
51
28
 
52
29
  expect(results).to.be.an("Object").that.deep.equal({
53
- getWithPromise: true,
54
30
  testPassed: true,
55
31
  method: "GET",
56
32
  });
@@ -60,11 +36,9 @@ describe("HttpSystemLynxClient Test", () => {
60
36
  const results = await HttpClient.request({
61
37
  method: "GET",
62
38
  url,
63
- body: { getWithPromise: true },
64
39
  });
65
40
 
66
41
  expect(results).to.be.an("Object").that.deep.equal({
67
- getWithPromise: true,
68
42
  testPassed: true,
69
43
  method: "GET",
70
44
  });
@@ -87,21 +61,19 @@ describe("HttpSystemLynxClient Test", () => {
87
61
  const results = await HttpClient.request({
88
62
  method: "DELETE",
89
63
  url,
90
- body: { test: true },
91
64
  });
92
65
 
93
66
  expect(results).to.be.an("Object").that.deep.equal({
94
- test: true,
95
67
  testPassed: true,
96
68
  method: "DELETE",
97
69
  });
98
70
  });
99
71
 
100
72
  it("should be able to upload a file", async () => {
101
- const file = fs.createReadStream(__dirname + "/test.file.json");
73
+ const file = fs.createReadStream(TEST_FILE);
102
74
  const results = await HttpClient.upload({
103
75
  url: singleFileUrl,
104
- formData: { file },
76
+ formData: { file, __arguments: [{ uploadArguments: true }] },
105
77
  });
106
78
 
107
79
  expect(results).to.be.an("Object").that.has.property("testPassed", true);
@@ -109,19 +81,19 @@ describe("HttpSystemLynxClient Test", () => {
109
81
  .to.have.property("file")
110
82
  .that.is.an("Object")
111
83
  .that.has.property("originalname", "test.file.json");
84
+ expect(results.__arguments).to.deep.equal([{ uploadArguments: true }]);
112
85
  });
113
86
 
114
87
  it("should be able to upload multiple files", async () => {
115
- const files = [
116
- fs.createReadStream(__dirname + "/test.file.json"),
117
- fs.createReadStream(__dirname + "/test.file.json"),
118
- ];
119
- const multiUploadResponse = await HttpClient.upload({
88
+ const files = [fs.createReadStream(TEST_FILE), fs.createReadStream(TEST_FILE)];
89
+ const results = await HttpClient.upload({
120
90
  url: multiFileUrl,
121
- formData: { files },
91
+ formData: { files, __arguments: [{ multiUploadArguments: true }] },
122
92
  });
123
- expect(multiUploadResponse).to.be.an("Object").that.has.property("testPassed", true);
124
- expect(multiUploadResponse).to.have.property("files").that.is.an("Array");
125
- expect(multiUploadResponse).to.have.property("fileUploadTest", true);
93
+
94
+ expect(results).to.be.an("Object").that.has.property("testPassed", true);
95
+ expect(results).to.have.property("files").that.is.an("Array");
96
+ expect(results).to.have.property("fileUploadTest", true);
97
+ expect(results.__arguments).to.deep.equal([{ multiUploadArguments: true }]);
126
98
  });
127
99
  });
@@ -12,14 +12,14 @@ module.exports = TestServerSetup = (port, done) => {
12
12
  const { file } = req;
13
13
  const json = JSON.parse(fs.readFileSync(file.path));
14
14
 
15
- res.json({ file, ...json });
15
+ res.json({ file, ...json, ...req.body });
16
16
  };
17
17
 
18
18
  const multiUploadResponse = (req, res) => {
19
19
  const { files } = req;
20
20
  const json = JSON.parse(fs.readFileSync(files[0].path));
21
21
 
22
- res.json({ files, ...json });
22
+ res.json({ files, ...json, ...req.body });
23
23
  };
24
24
 
25
25
  server.get("/test", response);
@@ -11,15 +11,18 @@ module.exports = function Router(server) {
11
11
  if (locations.length === 0)
12
12
  return res.status(404).json({
13
13
  message: `No services found on requested route: ${route}`,
14
- locations
14
+ locations,
15
15
  });
16
16
 
17
17
  location_index++;
18
18
  location_index = location_index < locations.length ? location_index : 0;
19
19
  const url = locations[location_index];
20
-
21
- HttpClient.request({ url }, (err, connData) => {
22
- if (err) {
20
+ HttpClient.request({ url })
21
+ .then((connData) => {
22
+ res.json(connData);
23
+ })
24
+ .catch((err) => {
25
+ console.log(err);
23
26
  for (i = 0; i < locations.length; i++) {
24
27
  if (locations[i] === url) {
25
28
  locations.splice(i, 1);
@@ -28,8 +31,7 @@ module.exports = function Router(server) {
28
31
  }
29
32
  }
30
33
  recursiveGetService(req, res);
31
- } else res.json(connData);
32
- });
34
+ });
33
35
  }
34
36
  };
35
37
 
@@ -5,7 +5,7 @@ const SocketEmitter = require("./components/SocketEmitter");
5
5
  const createWebSocket = require("./components/WebSocketServer");
6
6
  const parseMethods = require("./components/parseMethods");
7
7
  const shortId = require("shortid");
8
- const randomPort = () => parseInt(Math.random() * parseInt(Math.random() * 10000)) + 1023;
8
+ const randomPort = () => Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152;
9
9
  const createSSLServer = (app, options) => {
10
10
  const https = require("https");
11
11
  return https.createServer(options, app);
@@ -135,7 +135,7 @@ module.exports = function createServerManager(customServer, customWebSocketServe
135
135
  }
136
136
  });
137
137
  };
138
- ServerManager.addRouteHandler = (...args) => {
138
+ ServerManager.addMiddleware = (...args) => {
139
139
  const name = typeof args[0] === "string" ? args.shift() : "$all";
140
140
  args.forEach(async (middleware) => {
141
141
  if (Array.isArray(middleware)) {
@@ -76,7 +76,7 @@ module.exports = function createRouter(server, config) {
76
76
  const getArguments = () => {
77
77
  const args = body.__arguments || [];
78
78
  if (!isEmpty(query) && !args.length) args.push(query);
79
- if (isObject(args[0]) && method === "PUT")
79
+ if (isObject(args[0]) && method === "POST")
80
80
  args[0] = { ...args[0], ...(file && { file }), ...(files && { files }) };
81
81
  return args;
82
82
  };
@@ -1,6 +1,8 @@
1
- //express server, socket.io server and middleware needed for SystemLynx basic functionality
2
- const clearFolder = require("./clearFolder");
3
- const clearTempFolder = clearFolder.bind({}, "./temp");
1
+ const fs = require("fs");
2
+ const TEMP_FOLDER = `${__dirname}/temp`;
3
+ const { ensureDir, clearFolder } = require("./utils");
4
+ ensureDir(TEMP_FOLDER);
5
+
4
6
  module.exports = function createServer(customServer) {
5
7
  //express server
6
8
  const express = require("express");
@@ -8,13 +10,14 @@ module.exports = function createServer(customServer) {
8
10
  //express middleware
9
11
  const multer = require("multer");
10
12
  //express file upload middleware setup
11
- const TEMP_LOCATION = "./temp";
13
+
12
14
  const mime = require("mime");
13
15
  const shortId = require("shortid");
14
16
  const storage = multer.diskStorage({
15
- destination: (req, file, cb) => cb(null, TEMP_LOCATION),
16
- filename: (req, file, cb) =>
17
- cb(null, `${shortId()}.${mime.getExtension(file.mimetype)}`),
17
+ destination: (req, file, cb) => cb(null, TEMP_FOLDER),
18
+ filename: (req, file, cb) => {
19
+ return cb(null, `${shortId()}.${mime.getExtension(file.mimetype)}`);
20
+ },
18
21
  });
19
22
  //multi-file and single-file upload middleware functions
20
23
  const sf = multer({ storage }).single("file");
@@ -24,18 +27,22 @@ module.exports = function createServer(customServer) {
24
27
  const singleFileUpload = (req, res, next) =>
25
28
  sf(req, res, (err) => {
26
29
  if (err) return res.json(err);
27
- res.on("finish", clearTempFolder);
30
+ res.on("finish", () => fs.unlink(req.file.path, () => {}));
28
31
  next();
29
32
  });
30
33
  const multiFileUpload = (req, res, next) =>
31
34
  mf(req, res, (err) => {
32
35
  if (err) return res.json(err);
33
- res.on("finish", clearTempFolder);
36
+ res.on("finish", () => clearFolder(TEMP_FOLDER));
34
37
  next();
35
38
  });
36
-
37
- server.use("/sf", singleFileUpload);
38
- server.use("/mf", multiFileUpload);
39
+ const parseArguments = (req, res, next) => {
40
+ const { __arguments } = req.body;
41
+ if (__arguments) req.body.__arguments = JSON.parse(__arguments);
42
+ next();
43
+ };
44
+ server.use("/sf", singleFileUpload, parseArguments);
45
+ server.use("/mf", multiFileUpload, parseArguments);
39
46
  server.use(express.json({ limit: "5mb" }));
40
47
 
41
48
  !customServer &&
@@ -8,7 +8,7 @@ const parseMethods = (obj, reserved_methods = [], useREST) => {
8
8
  const method =
9
9
  useREST && REST_methods.indexOf(fn.toLocaleLowerCase()) > -1
10
10
  ? fn.toLocaleLowerCase()
11
- : "put";
11
+ : "post";
12
12
  methods.push({ method, fn });
13
13
  }
14
14
  });
@@ -0,0 +1,21 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ function clearFolder(folder) {
4
+ fs.readdir(folder, (err, files) => {
5
+ if (err) return console.error(err);
6
+ files.forEach((file) => {
7
+ fs.unlink(path.join(folder, file), (err) => err && console.error(err));
8
+ });
9
+ });
10
+ }
11
+ async function ensureDir(dir) {
12
+ if (!fs.existsSync(dir)) {
13
+ return new Promise((resolve, reject) => {
14
+ fs.mkdir(dir, { recursive: true }, (err) => {
15
+ if (err) reject(err);
16
+ else resolve();
17
+ });
18
+ });
19
+ }
20
+ }
21
+ module.exports = { clearFolder, ensureDir };
@@ -11,13 +11,13 @@ describe("createServerManager function", () => {
11
11
  .that.has.all.keys([
12
12
  "startService",
13
13
  "addModule",
14
- "addRouteHandler",
14
+ "addMiddleware",
15
15
  "server",
16
16
  "WebSocket",
17
17
  ])
18
18
  .that.respondsTo("startService")
19
19
  .that.respondsTo("addModule")
20
- .that.respondsTo("addRouteHandler");
20
+ .that.respondsTo("addMiddleware");
21
21
  });
22
22
  });
23
23
  describe("ServerManager", () => {
@@ -200,7 +200,7 @@ describe("ServerManager.startService(ServerConfiguration)", () => {
200
200
  });
201
201
  });
202
202
 
203
- it("should be able to use the ServerManager.addRouteHandler method to add additional route handling", async () => {
203
+ it("should be able to use the ServerManager.addMiddleware method to add additional route handling", async () => {
204
204
  const ServerManager = createServerManager();
205
205
  const route = "/testAPI";
206
206
  const port = 5454;
@@ -228,15 +228,15 @@ describe("ServerManager.startService(ServerConfiguration)", () => {
228
228
  },
229
229
  };
230
230
 
231
- ServerManager.addRouteHandler((req, res, next) => {
231
+ ServerManager.addMiddleware((req, res, next) => {
232
232
  req.$allHandlerAdded = true;
233
233
  next();
234
234
  });
235
- ServerManager.addRouteHandler(`${name}.put`, (req, res, next) => {
235
+ ServerManager.addMiddleware(`${name}.put`, (req, res, next) => {
236
236
  req.putHandlerAdded = true;
237
237
  next();
238
238
  });
239
- ServerManager.addRouteHandler(`${name}.test`, (req, res, next) => {
239
+ ServerManager.addMiddleware(`${name}.test`, (req, res, next) => {
240
240
  res.sendError({ status: 400, message: "tested passed" });
241
241
  });
242
242
  ServerManager.addModule(name, object);
@@ -289,7 +289,7 @@ describe("ServerManager.startService(ServerConfiguration)", () => {
289
289
 
290
290
  const result3 = await new Promise((resolve) => {
291
291
  request(
292
- { url: `${url}/${name}/test`, json: true, method: "PUT" },
292
+ { url: `${url}/${name}/test`, json: true, method: "post" },
293
293
  (err, res, body) => {
294
294
  resolve(body);
295
295
  }
@@ -8,8 +8,8 @@ module.exports = function createService(
8
8
  systemContext = {}
9
9
  ) {
10
10
  const ServerManager = createServerManager(customServer, customWebSocketServer);
11
- const { startService, addRouteHandler, server, WebSocket } = ServerManager;
12
- const Service = { startService, server, WebSocket, before: addRouteHandler };
11
+ const { startService, addMiddleware, server, WebSocket } = ServerManager;
12
+ const Service = { startService, server, WebSocket, before: addMiddleware };
13
13
 
14
14
  Service.module = function (name, constructor, reserved_methods = []) {
15
15
  const exclude_methods = reserved_methods.concat(
@@ -19,9 +19,9 @@ module.exports = function createService(
19
19
  if (typeof args[0] === "string") {
20
20
  const arg1 = args.shift();
21
21
  const fn = arg1 === "$all" ? "" : `.${arg1}`;
22
- addRouteHandler(`${name}${fn}`, ...args);
22
+ addMiddleware(`${name}${fn}`, ...args);
23
23
  } else {
24
- addRouteHandler(`${name}`, ...args);
24
+ addMiddleware(`${name}`, ...args);
25
25
  }
26
26
  };
27
27
  if (typeof constructor === "object" && constructor instanceof Object) {
@@ -1,10 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- module.exports = function clearFolder(folder) {
4
- fs.readdir(folder, (err, files) => {
5
- if (err) return console.error(err);
6
- files.forEach((file) => {
7
- fs.unlink(path.join(folder, file), (err) => err && console.error(err));
8
- });
9
- });
10
- };