data-primals-engine 1.0.8 → 1.0.10

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.
@@ -0,0 +1,59 @@
1
+ # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2
+ # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3
+
4
+ name: Node.js CI
5
+
6
+ on:
7
+ push:
8
+ branches: [ "develop", "main" ]
9
+ pull_request:
10
+ branches: [ "develop", "main" ]
11
+
12
+ jobs:
13
+ build:
14
+
15
+ runs-on: ubuntu-latest
16
+ container:
17
+ image: node:18 # Specify the Docker version you needx
18
+ env:
19
+ NODE_OPTIONS: "--max_old_space_size=4096"
20
+ NODE_ENV: development
21
+ ports:
22
+ - 7635
23
+ - 27017
24
+ strategy:
25
+ matrix:
26
+ node-version: [18.x]
27
+ # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
28
+ services:
29
+ mongodb:
30
+ image: mongo:5.0
31
+ ports:
32
+ - 27017:27017
33
+ options: >-
34
+ --health-cmd "mongo --eval 'db.serverStatus()'"
35
+ --health-interval 10s
36
+ --health-timeout 5s
37
+ --health-retries 5
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+
41
+ - name: Install MongoDB Tools
42
+ run: |
43
+ wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | apt-key add -
44
+ echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-5.0.list
45
+ apt-get update
46
+ apt-get install -y mongodb-org-tools
47
+
48
+ - name: Set up Node.js
49
+ uses: actions/setup-node@v2
50
+ with:
51
+ node-version: '18.18.2'
52
+
53
+ - name: Install dependencies
54
+ run: npm install
55
+
56
+ - name: Run tests
57
+ run: npm test
58
+
59
+
@@ -0,0 +1,23 @@
1
+ name: Publish package to GitHub Packages
2
+ on:
3
+ release:
4
+ types: [published]
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ permissions:
9
+ contents: read
10
+ packages: write
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ # Setup .npmrc file to publish to GitHub Packages
14
+ - uses: actions/setup-node@v4
15
+ with:
16
+ node-version: '20.x'
17
+ registry-url: 'https://npm.pkg.github.com'
18
+ # Defaults to the user or organization that owns the workflow file
19
+ scope: '@octocat'
20
+ - run: npm ci
21
+ - run: npm publish
22
+ env:
23
+ NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
package/Dockerfile ADDED
@@ -0,0 +1,15 @@
1
+ # Base image
2
+ FROM node:18.18.2
3
+
4
+ # Set working directory
5
+ WORKDIR /
6
+
7
+ # Copy package files and install dependencies
8
+ COPY package.json ./
9
+ RUN npm install
10
+
11
+ # Copy application code
12
+ COPY . .
13
+
14
+ # Expose the application port
15
+ EXPOSE 7633
package/DockerfileTest ADDED
@@ -0,0 +1,15 @@
1
+ # Base image
2
+ FROM node:18.18.2
3
+
4
+ # Set working directory
5
+ WORKDIR /
6
+
7
+ # Copy package files and install dependencies
8
+ COPY package.json ./
9
+ RUN npm install
10
+
11
+ # Copy application code
12
+ COPY . .
13
+
14
+ # Expose the application port
15
+ EXPOSE 7635
package/README.md CHANGED
@@ -194,6 +194,195 @@ curl -X DELETE http://localhost:7633/api/data?_user=demo \
194
194
  }'
195
195
  ```
196
196
 
197
+ ## Other operations
198
+
199
+ Make sure you use the code below to initialize the user :
200
+ ```javascript
201
+ import { Engine } from 'data-primals-engine/engine';
202
+ import { insertData, searchData } from 'data-primals-engine/modules/data';
203
+
204
+ // Ensure the engine is initialized
205
+ const engine = await Engine.Create();
206
+ const currentUser = await engine.userProvider.findUserByUsername('demo');
207
+ if (!currentUser) {
208
+ throw new Error("Could not retrieve the user. Please check credentials or user provider.");
209
+ }
210
+ console.log(`Successfully authenticated as ${currentUser.username}`);
211
+ ```
212
+
213
+ ### insertData(modelName, data, files, user)
214
+
215
+ > Inserts one or more documents, intelligently handling nested relationships.
216
+
217
+ ```javascript
218
+ // Uses the `currentUser` object defined above
219
+ const newProduct = { name: 'Super Widget', price: 99.99, status: 'available' };
220
+ const result = await insertData('product', newProduct, {}, currentUser);
221
+
222
+ if (result.success) {
223
+ console.log(`Successfully inserted document with ID: ${result.insertedIds[0]}`);
224
+ }
225
+ ```
226
+
227
+ ### editData(modelName, filter, data, files, user)
228
+
229
+ > Updates existing data matching the filter.
230
+
231
+ Example:
232
+
233
+ ```javascript
234
+ await editData(
235
+ "userProfile",
236
+ { _id: "507f1f77bcf86cd799439011" },
237
+ { bio: "Updated bio text" },
238
+ null, // No files
239
+ currentUser
240
+ );
241
+ ```
242
+
243
+ ### patchData(modelName, filter, data, files, user)
244
+
245
+ > Partially updates data (only modifies specified fields).
246
+
247
+ Example:
248
+
249
+ ```javascript
250
+ await patchData(
251
+ "settings",
252
+ { userId: "507f1f77bcf86cd799439011" },
253
+ { theme: "dark" },
254
+ null,
255
+ currentUser
256
+ );
257
+ ```
258
+
259
+ ### deleteData(modelName, ids, filter, user)
260
+
261
+ >Deletes data with cascading relation cleanup.
262
+
263
+ Examples:
264
+
265
+ ```javascript
266
+ // Delete by IDs
267
+ await deleteData("comments", ["61d1f1a9e3f1a9e3f1a9e3f1"], null, user);
268
+
269
+ // Delete by filter
270
+ await deleteData("logs", null, { createdAt: { $lt: "2023-01-01" } }, user);
271
+ ```
272
+
273
+ ### searchData({user, query})
274
+
275
+ Powerful search with relation expansion and filtering.
276
+
277
+ Query Options:
278
+
279
+ - model: Model name to search
280
+ - filter: MongoDB-style filter
281
+ - depth: Relation expansion depth (default: 1)
282
+ - limit/page: Pagination
283
+ - sort: Sorting criteria
284
+
285
+ Example:
286
+ ```javascript
287
+ const results = await searchData({
288
+ user: currentUser,
289
+ query: {
290
+ model: "blogPost",
291
+ filter: { status: "published" },
292
+ depth: 2, // Expand author and comments
293
+ limit: 10,
294
+ sort: "createdAt:DESC"
295
+ }
296
+ });
297
+ ```
298
+
299
+ ## Import/Export
300
+ ### importData(options, files, user)
301
+ > Imports data from JSON/CSV files.
302
+
303
+ Supported Formats:
304
+
305
+ - JSON arrays
306
+ - JSON with model-keyed objects
307
+ - CSV with headers or field mapping
308
+
309
+ Example:
310
+
311
+ ```javascript
312
+ const result = await importData(
313
+ {
314
+ model: "products",
315
+ hasHeaders: true
316
+ },
317
+ {
318
+ file: req.files.myFileField // from multipart body
319
+ },
320
+ currentUser
321
+ );
322
+ ```
323
+
324
+ ### exportData(options, user)
325
+ > Exports data to a structured format.
326
+
327
+ Example:
328
+
329
+ ```javascript
330
+ await exportData(
331
+ {
332
+ models: ["products", "categories"],
333
+ depth: 1, // Include relations
334
+ lang: "fr" // Localized data
335
+ },
336
+ currentUser
337
+ );
338
+ // Returns: { success: true, data: { products: [...], categories: [...] } }
339
+ ```
340
+
341
+ ## Backup & Restore
342
+ ### dumpUserData(user)
343
+ > Creates an encrypted backup of user data.
344
+
345
+ Features:
346
+
347
+ - Automatic encryption
348
+ - S3 or local storage
349
+ - Retention policies by plan (daily/weekly/monthly)
350
+
351
+ Example:
352
+
353
+ ```javascript
354
+ await dumpUserData(currentUser);
355
+ // Backup saved to S3 or ./backups/
356
+ ```
357
+
358
+ ### loadFromDump(user, options)
359
+
360
+ > Restores user data from backup.
361
+
362
+ Options:
363
+ - modelsOnly: Restore only model definitions
364
+
365
+ Example:
366
+
367
+ ```javascript
368
+ await loadFromDump(currentUser, { modelsOnly: false });
369
+ // Full restore including data
370
+ ```
371
+
372
+ ## Pack Management
373
+
374
+ ### installPack(logger, packId, user, lang)
375
+
376
+ > Installs a predefined data pack.
377
+
378
+ Example:
379
+
380
+ ```javascript
381
+ const result = await installPack(logger, "61d1f1a9e3f1a9e3f1a9e3f1", user, "en");
382
+ // Returns installation summary
383
+ ```
384
+
385
+
197
386
  ---
198
387
 
199
388
  ## 📁 Project Structure
@@ -212,6 +401,34 @@ data-primals-engine/
212
401
  └── server.js
213
402
  ```
214
403
 
404
+ ## Workflows: Automate Your Business Logic
405
+
406
+ > Workflows are the automation engine of your application.
407
+
408
+ They allow you to define complex business processes that run in response to specific events, without writing custom code.
409
+
410
+ This is perfect for tasks like **sending welcome emails**, managing **order fulfillment**, or triggering data synchronization.
411
+
412
+ A workflow is composed of two main parts: **Triggers** and **Actions**.
413
+
414
+ > A 'workflowTrigger' is the event that initiates a workflow run.
415
+ - **DataAdded**: Fires when a new document is created (e.g., a new user signs up).
416
+ - **DataEdited**: Fires when a document is updated (e.g., an order status changes to "shipped").
417
+ - **DataDeleted**: Fires when a document is removed.
418
+ - **Scheduled**: Runs at a specific time or interval using a Cron expression (e.g., 0 0 * * * for a nightly data cleanup job).
419
+ - **Manual**: Triggered on-demand via an API call, allowing you to integrate workflows into any part of your application.
420
+
421
+ > A 'workflowAction' is the individual steps a workflow executes. You can chain them together to create sophisticated logic.
422
+ - **CreateData**: Create a new document in any model.
423
+ - **UpdateData**: Modify one or more existing documents that match a filter.
424
+ - **SendEmail**: Send a transactional email using dynamic data from the trigger.
425
+ - **CallWebhook**: Make an HTTP request (GET, POST, etc.) to an external service or API.
426
+ - **ExecuteScript**: Run a custom JavaScript snippet for complex logic, data transformation, or conditional branching.
427
+ - **GenerateAIContent**: Use an integrated AI provider (like OpenAI or Gemini) to generate text, summarize content, or make decisions.
428
+ - **Wait**: Pause the workflow for a specific duration before continuing to the next step
429
+
430
+ See the details of the workflow models for more details.
431
+
215
432
  ---
216
433
 
217
434
  ## 🤝 Contributing
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "data-primals-engine",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "data-primals-engine is the package responsible from handling large amount of data in a practical and performant way. It can handle large amount of data in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation.",
5
5
  "main": "src/engine.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
+ "test": "cross-env MONGO_DB_URL=\"mongodb://localhost:27017\" PORT=7635 vitest --no-file-parallelism",
8
9
  "preinstall": "npx force-resolutions",
9
10
  "devserver": "cross-env NODE_ENV=development node server.js",
10
11
  "server": "cross-env NODE_ENV=production node server.js",
@@ -39,11 +40,9 @@
39
40
  "chalk": "^5.4.1",
40
41
  "check-disk-space": "^3.4.0",
41
42
  "compression": "^1.8.0",
42
- "connect-mongo": "^5.1.0",
43
43
  "cookie-parser": "^1.4.7",
44
44
  "cronstrue": "^3.1.0",
45
45
  "csv-parse": "^5.6.0",
46
- "csv-parser": "^3.2.0",
47
46
  "date-fns": "^4.1.0",
48
47
  "express": "^4.21.2",
49
48
  "express-csrf-double-submit-cookie": "^1.2.1",
@@ -61,7 +60,6 @@
61
60
  "nodemailer": "^6.10.0",
62
61
  "openai": "^5.8.2",
63
62
  "randomcolor": "^0.6.2",
64
- "rate-limiter-flexible": "^5.0.5",
65
63
  "react-helmet": "^6.1.0",
66
64
  "react-i18next": "^15.4.1",
67
65
  "react-markdown": "^10.1.0",
@@ -70,10 +68,9 @@
70
68
  "sirv": "^3.0.1",
71
69
  "swagger-ui-express": "^5.0.1",
72
70
  "tar": "^7.4.3",
73
- "tar-fs": "^3.0.8",
74
71
  "uniqid": "^5.4.0",
75
- "yaml": "^2.8.0",
76
- "zlib": "^1.0.5"
72
+ "vitest": "^3.2.3",
73
+ "yaml": "^2.8.0"
77
74
  },
78
75
  "devDependencies": {
79
76
  "@eslint/js": "^9.17.0",
package/src/engine.js CHANGED
@@ -17,7 +17,7 @@ import formidableMiddleware from 'express-formidable';
17
17
  const isProduction = process.env.NODE_ENV === 'production'
18
18
 
19
19
  // Connection URL
20
- export const dbUrl = process.env.MONGO_DB_URL || 'mongodb://127.0.0.1:27017';
20
+ export const dbUrl = process.env.CI ? 'mongodb://mongodb:27017' : (process.env.MONGO_DB_URL || 'mongodb://127.0.0.1:27017');
21
21
  export const MongoClient = new InternalMongoClient(dbUrl, { maxPoolSize: 20 });
22
22
 
23
23
  // Database Name
@@ -87,7 +87,7 @@ export const Engine = {
87
87
  }
88
88
  };
89
89
 
90
- await Promise.all(Config.Get('modules').map(async module => {
90
+ await Promise.all(Config.Get('modules', []).map(async module => {
91
91
  try {
92
92
  if( fs.existsSync(module)){
93
93
  return await importModule(module);