@cocreate/lazy-loader 1.17.0 → 1.19.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/package.json +1 -1
  3. package/src/server.js +222 -29
package/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ # [1.19.0](https://github.com/CoCreate-app/CoCreate-lazy-loader/compare/v1.18.0...v1.19.0) (2024-04-26)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * added processOperators and processOperator to lazyLoader class ([973f07a](https://github.com/CoCreate-app/CoCreate-lazy-loader/commit/973f07ac492dee348f15b6889c15e180cf4d3eb0))
7
+
8
+
9
+ ### Features
10
+
11
+ * dynamically handle webhooks using operations and objects to perform actions ([c1f17b4](https://github.com/CoCreate-app/CoCreate-lazy-loader/commit/c1f17b47304bd9c0b44ba591809dfee3c7186992))
12
+
13
+ # [1.18.0](https://github.com/CoCreate-app/CoCreate-lazy-loader/compare/v1.17.0...v1.18.0) (2024-03-18)
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * handlng wbhook returned as string or object ([daf7438](https://github.com/CoCreate-app/CoCreate-lazy-loader/commit/daf7438bf8d6f049629112d056d9a598499cc23e))
19
+ * imporved authentication handling ([dfc9b1c](https://github.com/CoCreate-app/CoCreate-lazy-loader/commit/dfc9b1c8b9fe547ff16d441e4b31b3f0332bacb8))
20
+ * typo executeect updated to object ([a1b6258](https://github.com/CoCreate-app/CoCreate-lazy-loader/commit/a1b6258a7a88cd4e9847257fb552852342d9c7e3))
21
+
22
+
23
+ ### Features
24
+
25
+ * authentication can be defined at envirnonemt level ([e4bf74c](https://github.com/CoCreate-app/CoCreate-lazy-loader/commit/e4bf74cdd8146bd390aec734d29e4177c3b264a7))
26
+ * executeMethod() to handle dotNotion method without losing context ([8ab4f84](https://github.com/CoCreate-app/CoCreate-lazy-loader/commit/8ab4f8484073891dba4c154938ae597c21962c5f))
27
+ * processOperators() to execute/rturn operator value ([e948ca2](https://github.com/CoCreate-app/CoCreate-lazy-loader/commit/e948ca24c1fb1ba5ae9718aaa466f0e26af78c74))
28
+
1
29
  # [1.17.0](https://github.com/CoCreate-app/CoCreate-lazy-loader/compare/v1.16.0...v1.17.0) (2024-02-05)
2
30
 
3
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/lazy-loader",
3
- "version": "1.17.0",
3
+ "version": "1.19.0",
4
4
  "description": "A simple lazy-loader component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.",
5
5
  "keywords": [
6
6
  "lazy-loader",
package/src/server.js CHANGED
@@ -1,8 +1,10 @@
1
1
  const fs = require('fs').promises;
2
2
  const path = require('path');
3
+ const { URL } = require('url');
3
4
  const vm = require('vm');
4
5
  const Config = require("@cocreate/config");
5
- const { URL } = require('url');
6
+ const { getValueFromObject } = require('@cocreate/utils');
7
+
6
8
 
7
9
  class CoCreateLazyLoader {
8
10
  constructor(server, crud, files) {
@@ -44,6 +46,7 @@ class CoCreateLazyLoader {
44
46
 
45
47
  async request(req, res) {
46
48
  try {
49
+ // TODO: track usage
47
50
  const valideUrl = new URL(`http://${req.headers.host}${req.url}`);
48
51
  const hostname = valideUrl.hostname;
49
52
  let organization
@@ -76,7 +79,7 @@ class CoCreateLazyLoader {
76
79
 
77
80
  async executeScriptWithTimeout(name, data) {
78
81
  try {
79
- if (this.modules[name].initialize) {
82
+ if (this.modules[name].initialize || this.modules[name].initialize === '') {
80
83
  if (data.req)
81
84
  data = await this.webhooks(this.modules[name], data, name)
82
85
  else
@@ -159,18 +162,11 @@ class CoCreateLazyLoader {
159
162
  throw new Error(`Missing ${name} key in organization apis object`);
160
163
 
161
164
  const service = require(config.path);
162
- const instance = new service[config.initialize](key);
163
-
164
- let method = instance
165
- for (let i = 0; i < methodPath.length; i++) {
166
- method = method[methodPath[i]]
167
- if (method === undefined) {
168
- throw new Error(`Method ${methodPath[i]} not found using ${data.method}.`);
169
- }
170
- }
171
-
172
- if (typeof method !== 'function')
173
- throw new Error(`Method ${data.method} is not a function.`);
165
+ let instance
166
+ if (config.initialize)
167
+ instance = new service[config.initialize](key);
168
+ else
169
+ instance = new service(key);
174
170
 
175
171
  let params = [], mainParam = false
176
172
  for (let i = 0; true; i++) {
@@ -185,7 +181,14 @@ class CoCreateLazyLoader {
185
181
  }
186
182
  }
187
183
 
188
- data.postmark = await method.apply(instance, params);
184
+ // TODO: should run processOperators before in order to perform complex opertions and get data
185
+ // data[name] = await processOperators(data, null, data[name]);
186
+
187
+ data[name] = await executeMethod(data.method, methodPath, instance, params)
188
+
189
+ // TODO: should run processOperators after in order to perform complex opertions and get data
190
+ // data[name] = await processOperators(data, data[name]);
191
+
189
192
  return data
190
193
  } catch (error) {
191
194
  data.error = error.message
@@ -204,18 +207,30 @@ class CoCreateLazyLoader {
204
207
  if (!key)
205
208
  throw new Error(`Missing ${name} key in organization apis object`);
206
209
 
207
- let name = data.req.url.split('/');
208
- name = name[3] || name[2] || name[1]
210
+ let webhookName = data.req.url.split('/');
211
+ webhookName = webhookName[webhookName.length - 1]
209
212
 
210
- // TODO: webhook secert could be a key pair
211
- const webhookSecret = data.apis[environment].webhooks[name];
212
- if (webhookSecret !== req.headers[name])
213
- throw new Error(`Webhook secret failed for ${name}. Unauthorized access attempt.`);
213
+ const webhook = apis[environment].webhooks[webhookName];
214
+ if (!webhook)
215
+ throw new Error(`Webhook ${name} ${webhookName} is not defined`);
216
+
217
+ // eventDataKey is used to access the event data
218
+ let eventDataKey = webhook.eventDataKey || apis[environment].eventDataKey
219
+ if (!eventDataKey)
220
+ throw new Error(`Webhook ${name} eventKey is not defined`);
221
+
222
+ // eventNameKey is used to access the event the event name
223
+ let eventNameKey = webhook.eventNameKey || apis[environment].eventNameKey
224
+ if (!eventNameKey)
225
+ throw new Error(`Webhook ${name} eventNameKey is not defined`);
226
+
227
+ if (!webhook.events)
228
+ throw new Error(`Webhook ${name} events are not defined`);
214
229
 
215
- let rawBody = '';
230
+ data.rawBody = '';
216
231
  await new Promise((resolve, reject) => {
217
232
  data.req.on('data', chunk => {
218
- rawBody += chunk.toString();
233
+ data.rawBody += chunk.toString();
219
234
  });
220
235
  data.req.on('end', () => {
221
236
  resolve();
@@ -225,13 +240,58 @@ class CoCreateLazyLoader {
225
240
  });
226
241
  });
227
242
 
228
- // TODO: if decrypt and validation is builtin to service
229
- // const service = require(config.path);
230
- // const instance = new service[config.initialize](key);
243
+ let parameters, method
244
+
245
+
246
+ if (webhook.authenticate && webhook.authenticate.method) {
247
+ method = webhook.authenticate.method
248
+ } else if (apis[environment].authenticate && apis[environment].authenticate.method) {
249
+ method = apis[environment].authenticate.method
250
+ } else
251
+ throw new Error(`Webhook ${name} authenticate method is not defined`);
252
+
253
+ if (webhook.authenticate && webhook.authenticate.parameters) {
254
+ parameters = webhook.authenticate.parameters
255
+ } else if (apis[environment].authenticate && apis[environment].authenticate.parameters) {
256
+ parameters = apis[environment].authenticate.parameters
257
+ } else
258
+ throw new Error(`Webhook ${name} authenticate parameters is not defined`);
259
+
260
+ // TODO: webhook secert could be a key pair
261
+
262
+ let event
263
+ if (!method) {
264
+ if (!parameters[0] !== parameters[1])
265
+ throw new Error(`Webhook secret failed for ${name}. Unauthorized access attempt.`);
266
+
267
+ event = JSON.parse(data.rawBody)
268
+ } else {
269
+ const service = require(config.path);
270
+ let instance
271
+ if (config.initialize)
272
+ instance = new service[config.initialize](key);
273
+ else
274
+ instance = new service(key);
275
+
276
+ const methodPath = method.split('.')
277
+
278
+ await this.processOperators(data, '', parameters);
279
+
280
+ event = await executeMethod(method, methodPath, instance, parameters)
281
+ }
282
+
283
+ let eventName = getValueFromObject(event, eventNameKey)
284
+ if (!eventName)
285
+ throw new Error(`Webhook ${name} eventNameKey: ${eventNameKey} could not be found in the event.`);
231
286
 
232
- // TODO: event may need to be handle by a built in service function
233
- const event = JSON.parse(rawBody)
234
- // TODO: using request.method and event.type get object and send socket.onMessage for proccessing
287
+ let eventData = getValueFromObject(event, eventDataKey)
288
+ if (!eventData)
289
+ throw new Error(`Webhook ${name} eventDataKey: ${eventDataKey} could not be found in the event.`);
290
+
291
+ let execute = webhook.events[eventName];
292
+ if (execute) {
293
+ execute = await this.processOperators(data, event, execute);
294
+ }
235
295
 
236
296
  data.res.writeHead(200, { 'Content-Type': 'application/json' });
237
297
  data.res.end(JSON.stringify({ message: 'Webhook received and processed' }));
@@ -244,6 +304,67 @@ class CoCreateLazyLoader {
244
304
  }
245
305
  }
246
306
 
307
+ async processOperators(data, event, execute, parent = null, parentKey = null) {
308
+ if (Array.isArray(execute)) {
309
+ for (let index = 0; index < execute.length; index++) {
310
+ execute[index] = await this.processOperators(data, event, execute[index], execute, index);
311
+ }
312
+ } else if (typeof execute === 'object' && execute !== null) {
313
+ for (let key of Object.keys(execute)) {
314
+ if (key.startsWith('$')) {
315
+ const operatorResult = await this.processOperator(data, event, key, execute[key]);
316
+ if (parent && operatorResult !== null && parentKey !== null) {
317
+ parent[parentKey] = operatorResult;
318
+ await this.processOperators(data, event, parent[parentKey], parent, parentKey);
319
+ }
320
+ } else {
321
+ execute[key] = await this.processOperators(data, event, execute[key], execute, key);
322
+ }
323
+ }
324
+ } else {
325
+ return await this.processOperator(data, event, execute);
326
+ }
327
+ return execute;
328
+ }
329
+
330
+ async processOperator(data, event, operator, context) {
331
+ let result
332
+ if (operator.startsWith('$data.')) {
333
+ return getValueFromObject(data, operator.substring(6))
334
+ } else if (operator.startsWith('$req')) {
335
+ return getValueFromObject(data, operator.substring(1))
336
+ } else if (operator.startsWith('$header')) {
337
+ return getValueFromObject(data.req, operator.substring(1))
338
+ } else if (operator.startsWith('$rawBody')) {
339
+ return getValueFromObject(data, operator.substring(1))
340
+ } else if (operator.startsWith('$crud')) {
341
+ context = await this.processOperators(data, event, context);
342
+ result = await this.crud.send(context)
343
+ if (operator.startsWith('$crud.'))
344
+ result = getValueFromObject(operator, operator.substring(6))
345
+ return await this.processOperators(data, event, result);
346
+ } else if (operator.startsWith('$socket')) {
347
+ context = await this.processOperators(data, event, context);
348
+ result = await this.socket.send(context)
349
+ if (operator.startsWith('$socket.'))
350
+ result = getValueFromObject(operator, operator.substring(6))
351
+ return await this.processOperators(data, event, result);
352
+ } else if (operator.startsWith('$api')) {
353
+ context = await this.processOperators(data, event, context);
354
+ let name = context.method.split('.')[0]
355
+ result = this.executeScriptWithTimeout(name, context)
356
+ if (operator.startsWith('$api.'))
357
+ result = getValueFromObject(event, operator.substring(5))
358
+ return await this.processOperators(data, event, result);
359
+ } else if (operator.startsWith('$event')) {
360
+ if (operator.startsWith('$event.'))
361
+ result = getValueFromObject(event, operator.substring(7))
362
+ return await this.processOperators(data, event, result);
363
+ }
364
+
365
+ return operator;
366
+ }
367
+
247
368
  async getApiKey(data, name) {
248
369
  let organization = await this.crud.getOrganization(data);
249
370
  if (organization.error)
@@ -257,6 +378,75 @@ class CoCreateLazyLoader {
257
378
 
258
379
  }
259
380
 
381
+
382
+ // async function processOperators(data, event, execute, parent = null, parentKey = null) {
383
+ // if (Array.isArray(execute)) {
384
+ // execute.forEach(async (item, index) => await processOperators(data, event, item, execute, index));
385
+ // } else if (typeof execute === 'object' && execute !== null) {
386
+ // for (let key of Object.keys(execute)) {
387
+ // // Check if key is an operator
388
+ // if (key.startsWith('$')) {
389
+ // const operatorResult = await processOperator(data, event, key, execute[key]);
390
+ // if (parent && operatorResult !== null) {
391
+ // if (parentKey !== null) {
392
+ // parent[parentKey] = operatorResult;
393
+ // await processOperators(data, event, parent[parentKey], parent, parentKey);
394
+ // }
395
+ // // else {
396
+ // // // Scenario 2: Replace the key (more complex, might require re-structuring the executable object)
397
+ // // delete parent[key]; // Remove the original key
398
+ // // parent[operatorResult] = execute[key]; // Assign the value to the new key
399
+ // // // Continue processing the new key if necessary
400
+ // // }
401
+ // }
402
+ // } else {
403
+ // await processOperators(data, event, execute[key], execute, key);
404
+ // }
405
+ // }
406
+ // } else {
407
+ // return await processOperator(data, event, execute);
408
+ // }
409
+ // }
410
+
411
+ async function executeMethod(method, methodPath, instance, params) {
412
+ try {
413
+ switch (methodPath.length) {
414
+ case 1:
415
+ return await instance[methodPath[0]](...params)
416
+ case 2:
417
+ return await instance[methodPath[0]][methodPath[1]](...params);
418
+ case 3:
419
+ return await instance[methodPath[0]][methodPath[1]][methodPath[2]](...params);
420
+ case 4:
421
+ return await instance[methodPath[0]][methodPath[1]][methodPath[2]][methodPath[3]](...params);
422
+ case 5:
423
+ return await instance[methodPath[0]][methodPath[1]][methodPath[2]][methodPath[3]][methodPath[4]](...params);
424
+ case 6:
425
+ return await instance[methodPath[0]][methodPath[1]][methodPath[2]][methodPath[3]][methodPath[4]][methodPath[5]](...params);
426
+ case 7:
427
+ return await instance[methodPath[0]][methodPath[1]][methodPath[2]][methodPath[3]][methodPath[4]][methodPath[5]][methodPath[6]](...params);
428
+ case 8:
429
+ return await instance[methodPath[0]][methodPath[1]][methodPath[2]][methodPath[3]][methodPath[4]][methodPath[5]][methodPath[6]][methodPath[7]](...params);
430
+ default:
431
+ const methodName = methodPath.pop();
432
+ let Method = instance
433
+ for (let i = 0; i < methodPath.length; i++) {
434
+ Method = Method[methodPath[i]]
435
+ if (Method === undefined) {
436
+ throw new Error(`Method ${methodPath[i]} not found using ${method}.`);
437
+ }
438
+ }
439
+
440
+ if (typeof Method[methodName] !== 'function')
441
+ throw new Error(`Method ${method} is not a function.`);
442
+
443
+ return await Method[methodName](...params)
444
+ }
445
+ } catch (error) {
446
+ throw new Error(`Method ${method} not found.`);
447
+ }
448
+ }
449
+
260
450
  function getModuleDependencies(modulePath) {
261
451
  let moduleObj = require.cache[modulePath];
262
452
  if (!moduleObj) {
@@ -343,4 +533,7 @@ async function fetchScriptFromDatabaseAndSave(name, moduleConfig) {
343
533
  return src;
344
534
  }
345
535
 
536
+
537
+
538
+
346
539
  module.exports = CoCreateLazyLoader;