@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.
- package/dist/index.js +278 -15
- package/docs/404.mdx +5 -0
- package/docs/architecture/api-explorer.mdx +478 -0
- package/docs/architecture/architecture-diagrams.mdx +12 -0
- package/docs/architecture/authentication-system.mdx +903 -0
- package/docs/architecture/authorization-system.mdx +886 -0
- package/docs/architecture/bootstrap.mdx +1442 -0
- package/docs/architecture/gateway-architecture.mdx +845 -0
- package/docs/architecture/multi-tenancy.mdx +1150 -0
- package/docs/architecture/overview.mdx +776 -0
- package/docs/architecture/scheduler.mdx +818 -0
- package/docs/architecture/shell.mdx +885 -0
- package/docs/architecture/ui-extension-points.mdx +781 -0
- package/docs/architecture/user-states.mdx +794 -0
- package/docs/development/overview.mdx +21 -0
- package/docs/development/workflow.mdx +914 -0
- package/docs/getting-started/core-concepts.mdx +892 -0
- package/docs/getting-started/installation.mdx +780 -0
- package/docs/getting-started/overview.mdx +83 -0
- package/docs/getting-started/quick-start.mdx +940 -0
- package/docs/guides/adding-documentation-sites.mdx +1367 -0
- package/docs/guides/creating-services.mdx +1736 -0
- package/docs/guides/creating-ui-modules.mdx +1860 -0
- package/docs/guides/identity-providers.mdx +1007 -0
- package/docs/guides/mermaid-diagrams.mdx +212 -0
- package/docs/guides/using-feature-flags.mdx +1059 -0
- package/docs/guides/working-with-rooms.mdx +566 -0
- package/docs/index.mdx +57 -0
- package/docs/platform-cli/commands.mdx +604 -0
- package/docs/platform-cli/overview.mdx +195 -0
- package/package.json +5 -2
- package/skills/ba-platform/platform-cli.skill.md +26 -0
- package/skills/ba-platform/platform.skill.md +35 -0
- package/templates/application-monorepo-template/gitignore +95 -0
- package/templates/bootstrap-service-template/Dockerfile.development +1 -1
- package/templates/bootstrap-service-template/gitignore +57 -0
- package/templates/bootstrap-service-template/package.json +1 -1
- package/templates/bootstrap-service-template/src/main.ts +6 -16
- package/templates/customization-ui-module-template/Dockerfile.development +1 -1
- package/templates/customization-ui-module-template/gitignore +73 -0
- package/templates/nestjs-service-module-template/Dockerfile.development +1 -1
- package/templates/nestjs-service-module-template/gitignore +56 -0
- package/templates/platform-init-template/{{platformName}}-core/gitignore +97 -0
- package/templates/platform-init-template/{{platformName}}-core/local/.env.example +1 -1
- package/templates/platform-init-template/{{platformName}}-core/local/platform-docker-compose.yml +1 -1
- package/templates/platform-init-template/{{platformName}}-core/local/{{platformName}}-core-docker-compose.yml +0 -1
- package/templates/react-ui-module-template/Dockerfile +1 -1
- package/templates/react-ui-module-template/Dockerfile.development +1 -3
- package/templates/react-ui-module-template/caddy/Caddyfile +1 -1
- package/templates/react-ui-module-template/gitignore +72 -0
- package/templates/react-ui-module-template/Dockerfile_nginx +0 -11
- 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
|
+
|