@bluealba/platform-cli 1.0.1 → 1.1.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 (52) hide show
  1. package/dist/index.js +278 -15
  2. package/docs/404.mdx +5 -0
  3. package/docs/architecture/api-explorer.mdx +478 -0
  4. package/docs/architecture/architecture-diagrams.mdx +12 -0
  5. package/docs/architecture/authentication-system.mdx +903 -0
  6. package/docs/architecture/authorization-system.mdx +886 -0
  7. package/docs/architecture/bootstrap.mdx +1442 -0
  8. package/docs/architecture/gateway-architecture.mdx +845 -0
  9. package/docs/architecture/multi-tenancy.mdx +1150 -0
  10. package/docs/architecture/overview.mdx +776 -0
  11. package/docs/architecture/scheduler.mdx +818 -0
  12. package/docs/architecture/shell.mdx +885 -0
  13. package/docs/architecture/ui-extension-points.mdx +781 -0
  14. package/docs/architecture/user-states.mdx +794 -0
  15. package/docs/development/overview.mdx +21 -0
  16. package/docs/development/workflow.mdx +914 -0
  17. package/docs/getting-started/core-concepts.mdx +892 -0
  18. package/docs/getting-started/installation.mdx +780 -0
  19. package/docs/getting-started/overview.mdx +83 -0
  20. package/docs/getting-started/quick-start.mdx +940 -0
  21. package/docs/guides/adding-documentation-sites.mdx +1367 -0
  22. package/docs/guides/creating-services.mdx +1736 -0
  23. package/docs/guides/creating-ui-modules.mdx +1860 -0
  24. package/docs/guides/identity-providers.mdx +1007 -0
  25. package/docs/guides/mermaid-diagrams.mdx +212 -0
  26. package/docs/guides/using-feature-flags.mdx +1059 -0
  27. package/docs/guides/working-with-rooms.mdx +566 -0
  28. package/docs/index.mdx +57 -0
  29. package/docs/platform-cli/commands.mdx +604 -0
  30. package/docs/platform-cli/overview.mdx +195 -0
  31. package/package.json +5 -2
  32. package/skills/ba-platform/platform-cli.skill.md +26 -0
  33. package/skills/ba-platform/platform.skill.md +35 -0
  34. package/templates/application-monorepo-template/gitignore +95 -0
  35. package/templates/bootstrap-service-template/Dockerfile.development +1 -1
  36. package/templates/bootstrap-service-template/gitignore +57 -0
  37. package/templates/bootstrap-service-template/package.json +1 -1
  38. package/templates/bootstrap-service-template/src/main.ts +6 -16
  39. package/templates/customization-ui-module-template/Dockerfile.development +1 -1
  40. package/templates/customization-ui-module-template/gitignore +73 -0
  41. package/templates/nestjs-service-module-template/Dockerfile.development +1 -1
  42. package/templates/nestjs-service-module-template/gitignore +56 -0
  43. package/templates/platform-init-template/{{platformName}}-core/gitignore +97 -0
  44. package/templates/platform-init-template/{{platformName}}-core/local/.env.example +1 -1
  45. package/templates/platform-init-template/{{platformName}}-core/local/platform-docker-compose.yml +1 -1
  46. package/templates/platform-init-template/{{platformName}}-core/local/{{platformName}}-core-docker-compose.yml +0 -1
  47. package/templates/react-ui-module-template/Dockerfile +1 -1
  48. package/templates/react-ui-module-template/Dockerfile.development +1 -3
  49. package/templates/react-ui-module-template/caddy/Caddyfile +1 -1
  50. package/templates/react-ui-module-template/gitignore +72 -0
  51. package/templates/react-ui-module-template/Dockerfile_nginx +0 -11
  52. package/templates/react-ui-module-template/nginx/default.conf +0 -23
@@ -0,0 +1,818 @@
1
+ ---
2
+ title: Scheduler Module
3
+ description: The PAE Scheduler Module for managing and executing recurring tasks and scheduled jobs within the Gateway Service
4
+ ---
5
+
6
+ import { Card, CardGrid, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
7
+
8
+ The **PAE Scheduler Module** provides a robust mechanism for scheduling and executing recurring tasks within the Blue Alba Platform. It is an integrated module of the Gateway Service, inheriting its authentication, authorization, and multi-tenancy capabilities.
9
+
10
+ ## Overview
11
+
12
+ The Scheduler Module allows you to:
13
+
14
+ <CardGrid stagger>
15
+ <Card title="Schedule Recurring Tasks" icon="seti:clock">
16
+ Define jobs that execute periodically using standard cron expressions
17
+ </Card>
18
+
19
+ <Card title="HTTP Job Execution" icon="random">
20
+ Make HTTP calls to any service endpoint on a schedule
21
+ </Card>
22
+
23
+ <Card title="Full REST API" icon="seti:config">
24
+ Complete CRUD operations, enable/disable, manual trigger, and execution history
25
+ </Card>
26
+
27
+ <Card title="Persistent Storage" icon="seti:db">
28
+ Job configurations and execution history are persisted in the platform database
29
+ </Card>
30
+
31
+ <Card title="Dynamic Parameters" icon="seti:text">
32
+ Use dynamic date/time parameters that resolve at execution time
33
+ </Card>
34
+
35
+ <Card title="Execution Tracking" icon="approve-check">
36
+ Track job execution history with detailed status, results, and error information
37
+ </Card>
38
+ </CardGrid>
39
+
40
+ ---
41
+
42
+ ## Authentication
43
+
44
+ <Aside type="caution" title="Authentication Required">
45
+ All Scheduler API endpoints require authentication. The module inherits the Gateway's authentication mechanisms.
46
+ </Aside>
47
+
48
+ The Scheduler Module uses the Gateway's authentication system:
49
+
50
+ - **JWT Token**: Include in the `Authorization` header as `Bearer <token>`
51
+ - **Session Cookie**: Use the platform's session cookie for browser-based access
52
+
53
+ ```bash
54
+ # Using JWT token
55
+ curl -H "Authorization: Bearer <your-jwt-token>" \
56
+ http://localhost:3000/_/scheduler/jobs
57
+
58
+ # Using cookie (from browser session)
59
+ curl -b "session=<session-cookie>" \
60
+ http://localhost:3000/_/scheduler/jobs
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Supported Job Types
66
+
67
+ <Aside type="note" title="Current Limitation">
68
+ Although the Job model is designed to be generic and extensible, the only currently supported job type is **HttpInvokerTask**. Future versions may support additional job types such as database operations, message queue publishing, or custom function execution.
69
+ </Aside>
70
+
71
+ ### HTTP Invoker Task
72
+
73
+ HTTP jobs make web requests to specified endpoints on a schedule:
74
+
75
+ **Capabilities**:
76
+ - GET, POST, HEAD, PUT, DELETE, PATCH methods
77
+ - Custom request headers
78
+ - Request body for POST/PUT/PATCH (objects are automatically converted to JSON)
79
+ - Dynamic parameter resolution at execution time
80
+ - Scheduling via cron pattern expressions
81
+
82
+ **Use Cases**:
83
+ - Data synchronization between services
84
+ - Periodic data cleanup or archival
85
+ - Report generation and email delivery
86
+ - Health checks and monitoring
87
+ - Cache invalidation
88
+ - Batch processing triggers
89
+
90
+ ---
91
+
92
+ ## Job Configuration
93
+
94
+ ### Job Structure
95
+
96
+ ```typescript
97
+ interface Job {
98
+ id: number; // Auto-generated on creation
99
+ application: { // Associated application info
100
+ id: number;
101
+ name: string;
102
+ displayName: string;
103
+ };
104
+ name: string; // Unique job name
105
+ description?: string; // Optional description
106
+ enabled: boolean; // Whether the job is active
107
+ pattern: string; // Cron expression (e.g., "0 2 * * *")
108
+ taskType: 'HttpInvokerTask'; // Type of task to execute
109
+ taskConfig: {
110
+ endpoint: string; // Base URL of the service
111
+ method: string; // HTTP method: GET, POST, PUT, DELETE, etc.
112
+ path: string; // URL path to invoke
113
+ body?: object; // Request body (converted to JSON)
114
+ headers?: object; // Custom HTTP headers
115
+ };
116
+ createdAt: string; // ISO timestamp
117
+ createdBy?: string; // User who created the job
118
+ updatedAt: string; // ISO timestamp
119
+ updatedBy?: string; // User who last updated the job
120
+ }
121
+ ```
122
+
123
+ ### Example Job
124
+
125
+ ```json
126
+ {
127
+ "applicationId": 5,
128
+ "name": "daily-data-sync",
129
+ "description": "Synchronizes data from external API every day at 2 AM",
130
+ "enabled": true,
131
+ "pattern": "0 2 * * *",
132
+ "taskType": "HttpInvokerTask",
133
+ "taskConfig": {
134
+ "endpoint": "http://integration-service:4005",
135
+ "method": "POST",
136
+ "path": "/api/sync",
137
+ "body": {
138
+ "fromDate": "${T-1:YYYY-MM-DD}",
139
+ "toDate": "${T:YYYY-MM-DD}"
140
+ },
141
+ "headers": {
142
+ "X-Custom-Header": "value"
143
+ }
144
+ }
145
+ }
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Bootstrap Configuration
151
+
152
+ Jobs can be defined declaratively as part of the platform bootstrap process, alongside other application resources such as roles, operations, and modules. This approach is recommended for jobs that should exist consistently across all environments.
153
+
154
+ ### `jobs.json` Format
155
+
156
+ Create a `jobs.json` file inside the application's bootstrap folder. The structure uses a simplified `url` field rather than the separate `endpoint` + `path` fields used by the REST API:
157
+
158
+ ```json
159
+ [
160
+ {
161
+ "name": "crm-health-check",
162
+ "description": "Periodic health check of the CRM service",
163
+ "enabled": true,
164
+ "pattern": "*/5 * * * *",
165
+ "taskType": "HttpInvokerTask",
166
+ "taskConfig": {
167
+ "url": "http://crm-service/health",
168
+ "method": "GET",
169
+ "timeout": 5000
170
+ }
171
+ },
172
+ {
173
+ "name": "crm-daily-sync",
174
+ "description": "Synchronizes CRM data with the external source every day at 2 AM",
175
+ "enabled": true,
176
+ "pattern": "0 2 * * *",
177
+ "taskType": "HttpInvokerTask",
178
+ "taskConfig": {
179
+ "url": "http://crm-service/api/sync",
180
+ "method": "POST",
181
+ "body": { "type": "full" },
182
+ "headers": { "Content-Type": "application/json" },
183
+ "auth": { "authMethod": "bearer", "token": "example-token" },
184
+ "timeout": 30000
185
+ }
186
+ }
187
+ ]
188
+ ```
189
+
190
+ ### Sync Lifecycle
191
+
192
+ The bootstrap engine manages the complete lifecycle of jobs defined in `jobs.json`:
193
+
194
+ | Scenario | Behavior |
195
+ |----------|----------|
196
+ | Job in `jobs.json`, not in DB | Created automatically |
197
+ | Job in `jobs.json`, in DB, owned by bootstrap | Updated to match latest definition |
198
+ | Job in `jobs.json`, in DB, created by bootstrap but manually modified (`updatedBy` ≠ bootstrap) | Skipped — **WARNING** logged ("manually modified") |
199
+ | Job in `jobs.json`, in DB, created by a different source entirely (`createdBy` ≠ bootstrap) | Skipped silently (debug log) |
200
+ | Job not in `jobs.json`, in DB, owned by bootstrap | Deleted automatically |
201
+ | Job not in `jobs.json`, in DB, owned by another source | Left untouched |
202
+
203
+ ### Ownership Model
204
+
205
+ Bootstrap tracks ownership using the `updatedBy` field. When bootstrap creates or updates a job it sets both `createdBy` and `updatedBy` to its `scopedTo` value. On each subsequent sync, bootstrap checks whether `updatedBy` still matches its own `scopedTo` value:
206
+
207
+ - If `updatedBy` matches — the job is considered bootstrap-owned and is updated or deleted as needed.
208
+ - If `updatedBy` does not match — the job was manually edited after bootstrap created it (for example, through the Admin UI, which changes `updatedBy` to the user's username). Bootstrap skips the job and logs a **WARNING** indicating the record was manually modified.
209
+ - If `createdBy` does not match bootstrap's `scopedTo` at all — the job was created by a completely different source and is skipped silently.
210
+
211
+ This distinction means that editing a job in the Admin UI effectively transfers ownership away from bootstrap. Bootstrap will no longer overwrite those manual changes on subsequent runs.
212
+
213
+ <Aside type="tip">
214
+ Use bootstrap configuration (`jobs.json`) for jobs that must exist consistently across development, staging, and production. For one-off or environment-specific jobs, use the Admin UI or REST API instead.
215
+ </Aside>
216
+
217
+ See the [Bootstrap documentation](/_/docs/architecture/bootstrap/) for the full bootstrap service structure and how `jobs.json` fits alongside other definition files.
218
+
219
+ ---
220
+
221
+ ## Dynamic Parameters
222
+
223
+ The Scheduler supports dynamic date/time parameters that are resolved at execution time. Use these in `task_config` fields (path, body, headers).
224
+
225
+ ### Parameter Syntax
226
+
227
+ ```
228
+ ${T:FORMAT} → Current date/time
229
+ ${T-N:FORMAT} → N days ago
230
+ ${T+N:FORMAT} → N days from now
231
+ ```
232
+
233
+ ### Supported Formats
234
+
235
+ | Format | Example Output | Description |
236
+ |--------|----------------|-------------|
237
+ | `YYYY-MM-DD` | `2026-02-04` | ISO date |
238
+ | `YYYY-MM-DDTHH:mm:ss` | `2026-02-04T14:30:00` | ISO datetime |
239
+ | `HH:mm:ss` | `14:30:00` | Time only |
240
+ | `YYYY` | `2026` | Year only |
241
+ | `MM` | `02` | Month (zero-padded) |
242
+ | `DD` | `04` | Day (zero-padded) |
243
+
244
+ ### Examples
245
+
246
+ ```json
247
+ {
248
+ "taskConfig": {
249
+ "endpoint": "http://reports-service:4004",
250
+ "method": "POST",
251
+ "path": "/api/reports/generate",
252
+ "body": {
253
+ "date": "${T:YYYY-MM-DD}",
254
+ "startDate": "${T-7:YYYY-MM-DD}",
255
+ "endDate": "${T:YYYY-MM-DD}",
256
+ "timestamp": "${T:YYYY-MM-DDTHH:mm:ss}"
257
+ }
258
+ }
259
+ }
260
+ ```
261
+
262
+ At execution on February 4, 2026 at 14:30:00, this resolves to:
263
+
264
+ ```json
265
+ {
266
+ "date": "2026-02-04",
267
+ "startDate": "2026-01-28",
268
+ "endDate": "2026-02-04",
269
+ "timestamp": "2026-02-04T14:30:00"
270
+ }
271
+ ```
272
+
273
+ ---
274
+
275
+ ## Security
276
+
277
+ ### URL Validation
278
+
279
+ All HTTP jobs have their `taskConfig.url` validated to prevent Server-Side Request Forgery (SSRF) attacks. For jobs with static URLs, validation runs at creation and update time. For jobs that use dynamic parameters (e.g., `https://${HOST}/api`), validation runs at execution time after parameters are resolved.
280
+
281
+ **Blocked targets**:
282
+
283
+ - Private IP ranges: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`
284
+ - Loopback addresses: `127.0.0.1`, `::1`
285
+ - Cloud metadata endpoints: `169.254.169.254`, `metadata.google.internal`, and equivalent provider-specific addresses
286
+ - Internal catalog services (these must be called through the gateway instead)
287
+ - Hostnames that resolve via DNS to any of the above private ranges (DNS rebinding protection)
288
+ - Non-HTTP/HTTPS protocols (`file://`, `ftp://`, etc.)
289
+
290
+ **Allowed targets**:
291
+
292
+ - Public HTTPS and HTTP URLs that resolve to public IP addresses
293
+ - The gateway's own host — requests sent to the gateway pass through its full authentication and authorization pipeline
294
+
295
+ <Aside type="caution" title="Internal services must go through the gateway">
296
+ To invoke an internal microservice on a schedule, target the gateway's public URL (e.g., `https://platform.example.com/api/my-service/endpoint`) rather than the service's internal hostname directly. This ensures the request is subject to the platform's auth and authz controls.
297
+ </Aside>
298
+
299
+ ---
300
+
301
+ ## Scheduling Syntax
302
+
303
+ ### Cron Expressions
304
+
305
+ Use standard cron syntax for precise scheduling:
306
+
307
+ ```
308
+ ┌───────────── minute (0 - 59)
309
+ │ ┌───────────── hour (0 - 23)
310
+ │ │ ┌───────────── day of month (1 - 31)
311
+ │ │ │ ┌───────────── month (1 - 12)
312
+ │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday)
313
+ │ │ │ │ │
314
+ │ │ │ │ │
315
+ * * * * *
316
+ ```
317
+
318
+ **Common Examples**:
319
+
320
+ <Tabs>
321
+ <TabItem label="Every Minute">
322
+ ```
323
+ * * * * *
324
+ ```
325
+ Runs every minute
326
+ </TabItem>
327
+
328
+ <TabItem label="Every Hour">
329
+ ```
330
+ 0 * * * *
331
+ ```
332
+ Runs at the start of every hour
333
+ </TabItem>
334
+
335
+ <TabItem label="Daily at Midnight">
336
+ ```
337
+ 0 0 * * *
338
+ ```
339
+ Runs daily at 12:00 AM
340
+ </TabItem>
341
+
342
+ <TabItem label="Weekdays at 9 AM">
343
+ ```
344
+ 0 9 * * 1-5
345
+ ```
346
+ Runs Monday through Friday at 9:00 AM
347
+ </TabItem>
348
+
349
+ <TabItem label="First of Month">
350
+ ```
351
+ 0 0 1 * *
352
+ ```
353
+ Runs at midnight on the first day of every month
354
+ </TabItem>
355
+
356
+ <TabItem label="Every 15 Minutes">
357
+ ```
358
+ */15 * * * *
359
+ ```
360
+ Runs every 15 minutes
361
+ </TabItem>
362
+ </Tabs>
363
+
364
+ ---
365
+
366
+ ## API Endpoints
367
+
368
+ All endpoints are prefixed with `/_/scheduler` and require authentication.
369
+
370
+ ### Jobs Management
371
+
372
+ | Method | Endpoint | Description |
373
+ |--------|----------|-------------|
374
+ | `GET` | `/_/scheduler/jobs` | List all jobs (paginated) |
375
+ | `GET` | `/_/scheduler/jobs/:id` | Get job by ID |
376
+ | `POST` | `/_/scheduler/jobs` | Create a new job |
377
+ | `PUT` | `/_/scheduler/jobs/:id` | Update an existing job |
378
+ | `DELETE` | `/_/scheduler/jobs/:id` | Delete a job |
379
+ | `POST` | `/_/scheduler/jobs/:id/enable` | Enable a job |
380
+ | `POST` | `/_/scheduler/jobs/:id/disable` | Disable a job |
381
+ | `POST` | `/_/scheduler/jobs/:id/trigger` | Manually trigger a job execution |
382
+
383
+ ### Executions
384
+
385
+ | Method | Endpoint | Description |
386
+ |--------|----------|-------------|
387
+ | `GET` | `/_/scheduler/jobs/:id/executions` | Get execution history for a job |
388
+ | `GET` | `/_/scheduler/executions/:id` | Get execution details by ID |
389
+ | `GET` | `/_/scheduler/executions` | List recent executions across all jobs |
390
+
391
+ ### Statistics
392
+
393
+ | Method | Endpoint | Description |
394
+ |--------|----------|-------------|
395
+ | `GET` | `/_/scheduler/stats` | Get scheduler engine statistics |
396
+
397
+ ---
398
+
399
+ ## API Examples
400
+
401
+ ### Create a Job
402
+
403
+ ```bash
404
+ curl -X POST http://localhost:3000/_/scheduler/jobs \
405
+ -H "Authorization: Bearer <token>" \
406
+ -H "Content-Type: application/json" \
407
+ -d '{
408
+ "applicationId": 1,
409
+ "name": "health-check",
410
+ "description": "Check external service health every 5 minutes",
411
+ "enabled": true,
412
+ "pattern": "*/5 * * * *",
413
+ "taskType": "HttpInvokerTask",
414
+ "taskConfig": {
415
+ "endpoint": "https://external-service.com",
416
+ "method": "GET",
417
+ "path": "/health"
418
+ }
419
+ }'
420
+ ```
421
+
422
+ ### List All Jobs
423
+
424
+ ```bash
425
+ curl http://localhost:3000/_/scheduler/jobs \
426
+ -H "Authorization: Bearer <token>"
427
+ ```
428
+
429
+ **Response**:
430
+ ```json
431
+ [
432
+ {
433
+ "id": 1,
434
+ "application": {
435
+ "id": 1,
436
+ "name": "my-app",
437
+ "displayName": "My Application"
438
+ },
439
+ "name": "health-check",
440
+ "description": "Check external service health every 5 minutes",
441
+ "enabled": true,
442
+ "pattern": "*/5 * * * *",
443
+ "taskType": "HttpInvokerTask",
444
+ "taskConfig": {
445
+ "endpoint": "https://external-service.com",
446
+ "method": "GET",
447
+ "path": "/health"
448
+ },
449
+ "createdAt": "2026-02-04T10:00:00.000Z",
450
+ "createdBy": "admin",
451
+ "updatedAt": "2026-02-04T10:00:00.000Z",
452
+ "updatedBy": "admin"
453
+ }
454
+ ]
455
+ ```
456
+
457
+ ### Update a Job
458
+
459
+ ```bash
460
+ curl -X PUT http://localhost:3000/_/scheduler/jobs/1 \
461
+ -H "Authorization: Bearer <token>" \
462
+ -H "Content-Type: application/json" \
463
+ -d '{
464
+ "pattern": "*/10 * * * *",
465
+ "description": "Check every 10 minutes instead"
466
+ }'
467
+ ```
468
+
469
+ ### Enable/Disable a Job
470
+
471
+ ```bash
472
+ # Disable
473
+ curl -X POST http://localhost:3000/_/scheduler/jobs/1/disable \
474
+ -H "Authorization: Bearer <token>"
475
+
476
+ # Enable
477
+ curl -X POST http://localhost:3000/_/scheduler/jobs/1/enable \
478
+ -H "Authorization: Bearer <token>"
479
+ ```
480
+
481
+ ### Manually Trigger a Job
482
+
483
+ ```bash
484
+ curl -X POST http://localhost:3000/_/scheduler/jobs/1/trigger \
485
+ -H "Authorization: Bearer <token>"
486
+ ```
487
+
488
+ ### Get Job Execution History
489
+
490
+ ```bash
491
+ curl http://localhost:3000/_/scheduler/jobs/1/executions \
492
+ -H "Authorization: Bearer <token>"
493
+ ```
494
+
495
+ **Response**:
496
+ ```json
497
+ [
498
+ {
499
+ "id": 42,
500
+ "jobId": 1,
501
+ "status": "succeeded",
502
+ "startedAt": "2026-02-04T14:30:00.000Z",
503
+ "finishedAt": "2026-02-04T14:30:01.234Z",
504
+ "result": {
505
+ "status": 200,
506
+ "data": {"healthy": true}
507
+ }
508
+ },
509
+ {
510
+ "id": 41,
511
+ "jobId": 1,
512
+ "status": "failed",
513
+ "startedAt": "2026-02-04T14:25:00.000Z",
514
+ "finishedAt": "2026-02-04T14:25:05.000Z",
515
+ "error": {
516
+ "message": "Connection timeout",
517
+ "code": "ETIMEDOUT"
518
+ }
519
+ }
520
+ ]
521
+ ```
522
+
523
+ ---
524
+
525
+ ## Job Executions
526
+
527
+ ### Execution Status
528
+
529
+ Each job execution has one of the following statuses:
530
+
531
+ | Status | Description |
532
+ |--------|-------------|
533
+ | `pending` | Execution has been queued but not yet started |
534
+ | `running` | Execution is currently in progress |
535
+ | `succeeded` | Execution completed successfully |
536
+ | `failed` | Execution failed with an error |
537
+
538
+ ### Execution Record
539
+
540
+ ```typescript
541
+ interface JobExecution {
542
+ id: number;
543
+ jobId: number;
544
+ status: 'pending' | 'running' | 'succeeded' | 'failed';
545
+ startedAt: string; // ISO timestamp
546
+ finishedAt?: string; // ISO timestamp (null if still running)
547
+ jobSnapshot: object; // Job configuration at execution time (sensitive data obfuscated)
548
+ result?: object; // Success response data
549
+ error?: {
550
+ message: string;
551
+ stack?: string;
552
+ code?: string;
553
+ };
554
+ createdAt: string;
555
+ createdBy?: string;
556
+ }
557
+ ```
558
+
559
+ ### Job Snapshot
560
+
561
+ The `jobSnapshot` field captures the job configuration at the moment of execution, with sensitive data (like headers and body) obfuscated. This allows auditing what configuration was used even if the job has been modified since.
562
+
563
+ ---
564
+
565
+ ## Job Execution Flow
566
+
567
+ ```
568
+ ┌─────────────────────────────────────────────────────────┐
569
+ │ 1. Cron Trigger │
570
+ │ • SchedulerEngineService monitors cron patterns │
571
+ │ • Triggers when pattern matches current time │
572
+ └────────────────────┬────────────────────────────────────┘
573
+
574
+
575
+ ┌─────────────────────────────────────────────────────────┐
576
+ │ 2. Create Execution Record │
577
+ │ • JobExecutionService creates pending execution │
578
+ │ • Captures job snapshot │
579
+ │ • Emits JOB_EXECUTION_STARTED event │
580
+ └────────────────────┬────────────────────────────────────┘
581
+
582
+
583
+ ┌─────────────────────────────────────────────────────────┐
584
+ │ 3. Resolve Dynamic Parameters │
585
+ │ • ParametersResolver processes taskConfig │
586
+ │ • Replaces ${T...} patterns with actual values │
587
+ └────────────────────┬────────────────────────────────────┘
588
+
589
+
590
+ ┌─────────────────────────────────────────────────────────┐
591
+ │ 4. Execute Task │
592
+ │ • TaskFactory creates HttpInvokerTask instance │
593
+ │ • Makes HTTP request to configured endpoint │
594
+ │ • Captures response or error │
595
+ └────────────────────┬────────────────────────────────────┘
596
+
597
+
598
+ ┌─────────────────────────────────────────────────────────┐
599
+ │ 5. Update Execution Record │
600
+ │ • Sets status to succeeded or failed │
601
+ │ • Records result or error details │
602
+ │ • Emits JOB_EXECUTION_SUCCEEDED or _FAILED event │
603
+ └─────────────────────────────────────────────────────────┘
604
+ ```
605
+
606
+ ---
607
+
608
+ ## Use Cases
609
+
610
+ ### Data Synchronization
611
+
612
+ Synchronize data between services or external systems:
613
+
614
+ ```json
615
+ {
616
+ "applicationId": 3,
617
+ "name": "sync-orders-from-external-api",
618
+ "description": "Sync orders every 15 minutes",
619
+ "enabled": true,
620
+ "pattern": "*/15 * * * *",
621
+ "taskType": "HttpInvokerTask",
622
+ "taskConfig": {
623
+ "endpoint": "http://integration-service:4005",
624
+ "method": "POST",
625
+ "path": "/api/sync-orders",
626
+ "body": {
627
+ "since": "${T-1:YYYY-MM-DDTHH:mm:ss}"
628
+ }
629
+ }
630
+ }
631
+ ```
632
+
633
+ ### Data Cleanup
634
+
635
+ Periodically clean up old or temporary data:
636
+
637
+ ```json
638
+ {
639
+ "applicationId": 2,
640
+ "name": "delete-old-audit-logs",
641
+ "description": "Clean up audit logs older than 90 days every Sunday at 1 AM",
642
+ "enabled": true,
643
+ "pattern": "0 1 * * 0",
644
+ "taskType": "HttpInvokerTask",
645
+ "taskConfig": {
646
+ "endpoint": "http://audit-service:4003",
647
+ "method": "POST",
648
+ "path": "/api/cleanup",
649
+ "body": {
650
+ "retentionDays": 90,
651
+ "beforeDate": "${T-90:YYYY-MM-DD}"
652
+ }
653
+ }
654
+ }
655
+ ```
656
+
657
+ ### Report Generation
658
+
659
+ Generate and send reports on a schedule:
660
+
661
+ ```json
662
+ {
663
+ "applicationId": 4,
664
+ "name": "weekly-sales-report",
665
+ "description": "Generate weekly sales report every Monday at 9 AM",
666
+ "enabled": true,
667
+ "pattern": "0 9 * * 1",
668
+ "taskType": "HttpInvokerTask",
669
+ "taskConfig": {
670
+ "endpoint": "http://reports-service:4004",
671
+ "method": "POST",
672
+ "path": "/api/generate",
673
+ "body": {
674
+ "reportType": "weekly-sales",
675
+ "startDate": "${T-7:YYYY-MM-DD}",
676
+ "endDate": "${T-1:YYYY-MM-DD}",
677
+ "emailTo": ["sales@company.com"]
678
+ }
679
+ }
680
+ }
681
+ ```
682
+
683
+ ### Health Checks
684
+
685
+ Monitor service health:
686
+
687
+ ```json
688
+ {
689
+ "applicationId": 1,
690
+ "name": "monitor-external-service",
691
+ "description": "Check external service health every 5 minutes",
692
+ "enabled": true,
693
+ "pattern": "*/5 * * * *",
694
+ "taskType": "HttpInvokerTask",
695
+ "taskConfig": {
696
+ "endpoint": "https://external-service.com",
697
+ "method": "GET",
698
+ "path": "/health"
699
+ }
700
+ }
701
+ ```
702
+
703
+ ---
704
+
705
+ ## Common Issues
706
+
707
+ <Tabs>
708
+ <TabItem label="Job Not Running">
709
+ **Possible Causes**:
710
+ - `enabled` is set to `false`
711
+ - Gateway service is down
712
+ - Invalid cron pattern
713
+
714
+ **Solution**:
715
+ ```bash
716
+ # Check job configuration
717
+ curl http://localhost:3000/_/scheduler/jobs \
718
+ -H "Authorization: Bearer <token>"
719
+
720
+ # Verify gateway service is running
721
+ docker ps | grep gateway
722
+
723
+ # Check gateway logs for scheduler errors
724
+ docker logs pae-nestjs-gateway-service | grep -i scheduler
725
+ ```
726
+ </TabItem>
727
+
728
+ <TabItem label="Job Failing">
729
+ **Possible Causes**:
730
+ - Target service is down
731
+ - Invalid endpoint URL or path
732
+ - Network issues
733
+ - Authentication issues on target service
734
+
735
+ **Solution**:
736
+ ```bash
737
+ # Check execution history for error details
738
+ curl http://localhost:3000/_/scheduler/jobs/1/executions \
739
+ -H "Authorization: Bearer <token>"
740
+
741
+ # Test the HTTP endpoint manually
742
+ curl -X POST http://target-service:4005/api/endpoint
743
+
744
+ # Check gateway logs
745
+ docker logs pae-nestjs-gateway-service | grep -i "job execution"
746
+ ```
747
+ </TabItem>
748
+
749
+ <TabItem label="Pattern Not Working">
750
+ **Possible Causes**:
751
+ - Invalid cron pattern syntax
752
+ - Job is disabled
753
+
754
+ **Solution**:
755
+ - Validate cron expression at [crontab.guru](https://crontab.guru/)
756
+ - Verify `enabled: true` in job configuration
757
+ - Check the pattern field matches standard cron format (5 fields)
758
+ </TabItem>
759
+
760
+ <TabItem label="Dynamic Parameters Not Resolving">
761
+ **Possible Causes**:
762
+ - Incorrect parameter syntax
763
+ - Invalid date format
764
+
765
+ **Solution**:
766
+ - Use correct syntax: `${T:FORMAT}`, `${T-N:FORMAT}`, `${T+N:FORMAT}`
767
+ - Check supported formats: `YYYY-MM-DD`, `HH:mm:ss`, etc.
768
+ - Review execution's `jobSnapshot` to see resolved values
769
+ </TabItem>
770
+ </Tabs>
771
+
772
+ ---
773
+
774
+ ## Best Practices
775
+
776
+ <CardGrid>
777
+ <Card title="Use Descriptive Names" icon="pencil">
778
+ Give jobs clear, descriptive names that indicate what they do (e.g., `daily-order-sync`, `weekly-report-generation`)
779
+ </Card>
780
+
781
+ <Card title="Add Descriptions" icon="document">
782
+ Include detailed descriptions explaining the job's purpose and any important notes
783
+ </Card>
784
+
785
+ <Card title="Implement Idempotency" icon="approve-check">
786
+ Ensure job endpoints can handle duplicate executions safely in case of retries
787
+ </Card>
788
+
789
+ <Card title="Monitor Execution History" icon="seti:graphql">
790
+ Regularly review execution history to detect patterns, failures, or issues
791
+ </Card>
792
+
793
+ <Card title="Use Dynamic Parameters" icon="seti:clock">
794
+ Leverage `${T...}` parameters for date-based operations instead of hardcoding values
795
+ </Card>
796
+
797
+ <Card title="Test Before Enabling" icon="seti:config">
798
+ Create jobs with `enabled: false`, test with manual trigger, then enable for production
799
+ </Card>
800
+ </CardGrid>
801
+
802
+ ---
803
+
804
+ ## Events
805
+
806
+ The Scheduler Module emits events through NestJS EventEmitter for integration with other modules:
807
+
808
+ | Event | Description |
809
+ |-------|-------------|
810
+ | `scheduler.job.created` | A new job was created |
811
+ | `scheduler.job.updated` | A job was updated |
812
+ | `scheduler.job.deleted` | A job was deleted |
813
+ | `scheduler.job.execution.started` | A job execution started |
814
+ | `scheduler.job.execution.succeeded` | A job execution completed successfully |
815
+ | `scheduler.job.execution.failed` | A job execution failed |
816
+
817
+ ---
818
+