@5minds/processcube_engine_client 6.3.0-develop.1 → 6.3.0-develop.2

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 (2) hide show
  1. package/README.md +611 -100
  2. package/package.json +6 -6
package/README.md CHANGED
@@ -1,201 +1,712 @@
1
- # Engine Client.ts
2
-
3
- Ein NodeJS basierter Client zur Kommunikation mit der ProcessCube Engine.
1
+ # ProcessCube Engine Client
2
+
3
+ TypeScript-Client zur Kommunikation mit der ProcessCube Engine via REST und Socket.IO.
4
+
5
+ - **Paketname:** `@5minds/processcube_engine_client`
6
+ - **Lizenz:** MIT
7
+ - **Plattformen:** Node.js, Browser
8
+
9
+ ## Inhaltsverzeichnis
10
+
11
+ - [Installation](#installation)
12
+ - [Schnelleinstieg](#schnelleinstieg)
13
+ - [Architektur](#architektur)
14
+ - [EngineClient](#engineclient)
15
+ - [ClientFactory](#clientfactory)
16
+ - [Authentifizierung](#authentifizierung)
17
+ - [Prozesse starten](#prozesse-starten)
18
+ - [UserTasks](#usertasks)
19
+ - [External Tasks](#external-tasks)
20
+ - [Konzept](#konzept)
21
+ - [Lebenszyklus eines ExternalTasks](#lebenszyklus-eines-externaltasks)
22
+ - [ExternalTaskWorker](#externaltaskworker)
23
+ - [Worker-Konfiguration](#worker-konfiguration)
24
+ - [Processing Function](#processing-function)
25
+ - [Fehlerbehandlung](#fehlerbehandlung)
26
+ - [Lock-Verlängerung](#lock-verlängerung)
27
+ - [Health Monitoring](#health-monitoring)
28
+ - [Heartbeat Monitoring](#heartbeat-monitoring)
29
+ - [Graceful Shutdown](#graceful-shutdown)
30
+ - [Multi-Topic Worker](#multi-topic-worker)
31
+ - [Payload-Filter](#payload-filter)
32
+ - [ExternalTask REST API](#externaltask-rest-api)
33
+ - [Echtzeit-Events via Socket.IO](#echtzeit-events-via-socketio)
34
+ - [Alle verfügbaren Clients](#alle-verfügbaren-clients)
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ npm install @5minds/processcube_engine_client
40
+ ```
4
41
 
5
42
  ## Schnelleinstieg
6
43
 
7
44
  ```ts
8
45
  import { EngineClient } from '@5minds/processcube_engine_client';
9
46
 
10
- const engineUri = 'http://localhost:10560';
47
+ const client = new EngineClient('http://localhost:10560');
48
+
49
+ const processInstances = await client.processInstances.query({
50
+ correlationId: 'my-correlation-id',
51
+ });
52
+ ```
53
+
54
+ ## Architektur
55
+
56
+ Der Client ist modular aufgebaut. Der `EngineClient` bündelt spezialisierte Sub-Clients, die jeweils einen fachlichen Bereich der Engine abdecken. Die Kommunikation erfolgt über REST (HTTP) und Socket.IO (WebSocket).
57
+
58
+ ```mermaid
59
+ graph TB
60
+ App[Anwendung]
61
+ EC[EngineClient]
62
+
63
+ subgraph "Sub-Clients"
64
+ PM[ProcessModels]
65
+ PI[ProcessInstances]
66
+ UT[UserTasks]
67
+ ET[ExternalTasks]
68
+ MT[ManualTasks]
69
+ CO[Correlations]
70
+ EV[Events]
71
+ NO[Notifications]
72
+ PD[ProcessDefinitions]
73
+ CR[Cronjobs]
74
+ FN[FlowNodeInstances]
75
+ DO[DataObjectInstances]
76
+ AI[ApplicationInfo]
77
+ UNT[UntypedTasks]
78
+ end
79
+
80
+ subgraph "Kommunikation"
81
+ HTTP[HttpClient<br/>REST / fetch]
82
+ SIO[SocketIoManager<br/>Socket.IO]
83
+ end
84
+
85
+ Engine[ProcessCube Engine]
86
+
87
+ App --> EC
88
+ EC --> PM & PI & UT & ET & MT & CO & EV & NO & PD & CR & FN & DO & AI & UNT
89
+ PM & PI & UT & ET & MT & CO & CR & FN & DO & AI & UNT & PD --> HTTP
90
+ EV & NO --> SIO
91
+ HTTP --> Engine
92
+ SIO --> Engine
93
+ ```
94
+
95
+ ## EngineClient
96
+
97
+ Der `EngineClient` ist der zentrale Einstiegspunkt. Er erstellt alle Sub-Clients und verwaltet die Socket.IO-Verbindung.
98
+
99
+ ```ts
100
+ import { EngineClient } from '@5minds/processcube_engine_client';
101
+
102
+ const client = new EngineClient('http://localhost:10560');
103
+
104
+ // Sub-Clients als Properties
105
+ client.processModels;
106
+ client.processInstances;
107
+ client.userTasks;
108
+ client.externalTasks;
109
+ client.manualTasks;
110
+ client.correlations;
111
+ client.events;
112
+ client.notification;
113
+ client.processDefinitions;
114
+ client.cronjobs;
115
+ client.flowNodeInstances;
116
+ client.dataObjectInstances;
117
+ client.applicationInfo;
118
+ client.untypedTasks;
119
+
120
+ // Aufräumen
121
+ client.dispose();
122
+ ```
123
+
124
+ ## ClientFactory
125
+
126
+ Alternativ lassen sich einzelne Clients gezielt erstellen, wenn nur ein bestimmter fachlicher Bereich benötigt wird:
127
+
128
+ ```ts
129
+ import { ClientFactory } from '@5minds/processcube_engine_client';
130
+
131
+ const engineUrl = 'http://localhost:10560';
11
132
 
12
- const client = new EngineClient(engineUri);
133
+ const processInstanceClient = ClientFactory.createProcessInstanceClient(engineUrl);
13
134
 
14
- const processInstancesInCorrelation = await client.processInstances.query({
15
- correlationid: 'my-correlation-id',
135
+ const processInstances = await processInstanceClient.query({
136
+ correlationId: 'my-correlation-id',
16
137
  });
17
138
  ```
18
139
 
19
- ## Wie kann ich den Client verwenden?
140
+ Verfügbare Factory-Methoden:
141
+
142
+ | Methode | Beschreibung |
143
+ | ---------------------------------- | ------------------------------------------ |
144
+ | `createProcessModelClient()` | Prozessmodelle deployen, starten, abfragen |
145
+ | `createProcessInstanceClient()` | Prozessinstanzen abfragen und verwalten |
146
+ | `createProcessDefinitionClient()` | Prozessdefinitionen verwalten |
147
+ | `createUserTaskClient()` | UserTasks abfragen und abschließen |
148
+ | `createExternalTaskClient()` | ExternalTasks abonnieren und verarbeiten |
149
+ | `createManualTaskClient()` | ManualTasks abfragen und abschließen |
150
+ | `createUntypedTaskClient()` | Untypisierte Tasks verwalten |
151
+ | `createCorrelationClient()` | Correlations abfragen |
152
+ | `createEventClient()` | BPMN-Events senden und empfangen |
153
+ | `createNotificationClient()` | Echtzeit-Benachrichtigungen |
154
+ | `createCronJobClient()` | Cronjobs verwalten |
155
+ | `createFlowNodeInstanceClient()` | FlowNode-Instanzen abfragen |
156
+ | `createDataObjectInstanceClient()` | DataObjects abfragen |
157
+ | `createApplicationInfoClient()` | Engine-Metadaten abfragen |
158
+
159
+ ## Authentifizierung
20
160
 
21
- Man benötigt lediglich die URL der anzusteuernden Engine.
22
- Mit dieser lässt sich eine Instanz des Clients anlegen, die direkt verwendbar ist.
161
+ Der Client unterstützt verschiedene Authentifizierungsarten:
23
162
 
24
- Es gibt verschiedene Wege sich Clients anzulegen.
163
+ ### Statischer Token
164
+
165
+ ```ts
166
+ import { EngineClient, Identity } from '@5minds/processcube_engine_client';
25
167
 
26
- ### EngineClient
168
+ const identity: Identity = { token: 'Bearer my-jwt-token', userId: 'user123' };
169
+ const client = new EngineClient('http://localhost:10560', identity);
170
+ ```
27
171
 
28
- Man kann eine Instanz des `EngineClients` direkt anlegen, welche die API der ProcessCube Engine vollständig abdeckt.
172
+ ### Dynamischer Token (Callback)
29
173
 
30
174
  ```ts
31
175
  import { EngineClient } from '@5minds/processcube_engine_client';
32
176
 
33
- const engineUri = 'http://localhost:10560';
177
+ const client = new EngineClient('http://localhost:10560', async () => {
178
+ const token = await fetchTokenFromAuthProvider();
179
+ return { token, userId: 'user123' };
180
+ });
181
+ ```
34
182
 
35
- const client = new EngineClient(engineUri);
183
+ ### Client Credentials (OAuth2)
184
+
185
+ ```ts
186
+ import { EngineClient, ClientCredentialsConfig } from '@5minds/processcube_engine_client';
36
187
 
37
- const processInstancesInCorrelation = await client.processInstances.query({
38
- correlationid: 'my-correlation-id',
188
+ const credentials: ClientCredentialsConfig = {
189
+ clientId: 'my-client-id',
190
+ clientSecret: 'my-client-secret',
191
+ authority: 'https://auth.example.com',
192
+ scope: 'engine',
193
+ };
194
+
195
+ const client = new EngineClient('http://localhost:10560', credentials);
196
+ ```
197
+
198
+ Bei dynamischen Tokens und Client Credentials wird der Token automatisch vor Ablauf erneuert.
199
+
200
+ ## Prozesse starten
201
+
202
+ ### Einfacher Prozessstart
203
+
204
+ ```ts
205
+ await client.processModels.startProcessInstance({
206
+ processModelId: 'myProcessModelId',
39
207
  });
40
208
  ```
41
209
 
42
- ### Client Factory
210
+ ### Konfigurierter Prozessstart
43
211
 
44
- Oder man kann sich über die Client Factory einen feature-spezifischen Client anlegen, der nur einen besimmten fachlichen Aspekt der ProcessCube Engine abdeckt.
212
+ ```ts
213
+ await client.processModels.startProcessInstance({
214
+ processModelId: 'myProcessModelId',
215
+ startEventId: 'StartEvent_1',
216
+ correlationId: 'my-correlation-id',
217
+ initialToken: { hello: 'world' },
218
+ });
219
+ ```
45
220
 
46
- Beispiel:
221
+ ### Prozess starten und auf Ende warten
47
222
 
48
223
  ```ts
49
- import { ClientFactory } from '@5minds/processcube_engine_client';
224
+ const result = await client.processModels.startProcessInstanceAndAwaitEndEvent({
225
+ processModelId: 'myProcessModelId',
226
+ });
227
+ ```
50
228
 
51
- const engineUri = 'http://localhost:10560';
229
+ ## UserTasks
52
230
 
53
- const processInstanceClient = ClientFactory.createProcessInstanceClient(engineUri);
231
+ ```ts
232
+ import { EngineClient, DataModels } from '@5minds/processcube_engine_client';
54
233
 
55
- const processInstances = await processInstanceClient.query({
56
- correlationid: 'my-correlation-id',
234
+ const client = new EngineClient('http://localhost:10560');
235
+
236
+ const userTasks = await client.userTasks.query({
237
+ processModelId: 'myProcessModelId',
238
+ state: DataModels.FlowNodeInstances.FlowNodeInstanceState.suspended,
57
239
  });
58
240
  ```
59
241
 
60
- Hier wird eine Client Instanz angelegt, welche lediglich die Interaktion mit Prozessinstanzen abdeckt.
242
+ ---
61
243
 
62
- ## Anwendungsbeispiele
244
+ ## External Tasks
63
245
 
64
- Nachfolgend werden ein paar der am häufigsten verwendeten Use Cases demonstriert.
246
+ ### Konzept
65
247
 
66
- ### Prozesse starten
248
+ External Tasks sind ein zentrales Muster in BPMN 2.0 zur Auslagerung von Arbeitsschritten an externe Worker. Wenn die ProcessCube Engine einen **External Service Task** in einem Prozess erreicht, erstellt sie einen ExternalTask-Eintrag und wartet, bis ein externer Worker diesen abholt, verarbeitet und das Ergebnis zurückmeldet.
67
249
 
68
- Das Starten von Prozessinstanzen kann über die `EngineClient.processModels` Fachlichkeit realisiert werden.
250
+ ```mermaid
251
+ sequenceDiagram
252
+ participant P as BPMN-Prozess
253
+ participant E as ProcessCube Engine
254
+ participant W as ExternalTask Worker
69
255
 
70
- Prozesse lassen sich auf mehrere Arten starten.
256
+ P->>E: Erreicht External Service Task
257
+ E->>E: ExternalTask erstellen (state: pending)
258
+ Note over E: Task wartet auf Worker
71
259
 
72
- #### Einfacher Prozessstart
260
+ loop Polling-Schleife
261
+ W->>E: Fetch & Lock (Topic, maxTasks, lockDuration)
262
+ E-->>W: Gesperrte ExternalTasks
263
+ end
73
264
 
74
- ```ts
75
- import { EngineClient } from '@5minds/processcube_engine_client';
265
+ W->>W: Payload verarbeiten
76
266
 
77
- const engineUri = 'http://localhost:10560';
267
+ alt Erfolg
268
+ W->>E: Finish (Result)
269
+ E->>P: Prozess fortsetzen mit Result
270
+ else Fehler
271
+ W->>E: HandleError (ErrorCode, Message)
272
+ E->>P: Fehler im Prozess auslösen
273
+ end
274
+ ```
78
275
 
79
- const client = new EngineClient(engineUri);
276
+ Dieses Muster entkoppelt die Prozessausführung von der eigentlichen Geschäftslogik und ermöglicht:
80
277
 
81
- await client.processModels.startProcessInstance({ processModelId: 'myProcessModelId' });
278
+ - **Skalierung:** Beliebig viele Worker können dieselben Topics verarbeiten
279
+ - **Sprachunabhängigkeit:** Worker können in jeder Sprache implementiert werden
280
+ - **Fehlertoleranz:** Locks verfallen automatisch, sodass fehlgeschlagene Tasks erneut verarbeitet werden
281
+ - **Langläufige Aufgaben:** Locks werden automatisch verlängert
282
+
283
+ ### Lebenszyklus eines ExternalTasks
284
+
285
+ ```mermaid
286
+ stateDiagram-v2
287
+ [*] --> pending: Engine erstellt Task
288
+ pending --> pending: Lock abgelaufen<br/>(Multi-Try)
289
+ pending --> expired: Lock abgelaufen<br/>(Single-Try)
290
+ pending --> finished: Worker meldet Ergebnis<br/>oder Fehler
291
+ finished --> [*]
292
+ expired --> [*]
82
293
  ```
83
294
 
84
- Die `processModelId` ist der einzige erforderliche Parameter für diesen Request.
295
+ | State | Beschreibung |
296
+ | ---------- | ------------------------------------------------------------ |
297
+ | `pending` | Task wartet auf Verarbeitung oder wird gerade verarbeitet |
298
+ | `finished` | Task wurde erfolgreich abgeschlossen oder mit Fehler beendet |
299
+ | `expired` | Lock ist abgelaufen (nur bei Single-Try Tasks) |
300
+
301
+ **Single-Try vs. Multi-Try:**
85
302
 
86
- Der Client wartet in diesem Szenario nur, bis die Engine den _Start_ des Prozesses bestätigt hat.
303
+ - **Multi-Try** (Standard): Wenn der Lock abläuft, geht der Task zurück in `pending` und kann von einem anderen Worker abgeholt werden.
304
+ - **Single-Try**: Wenn der Lock abläuft, geht der Task in `expired` und wird nicht erneut angeboten.
87
305
 
88
- #### Konfigurierter Prozessstart
306
+ ### ExternalTaskWorker
89
307
 
90
- Neben der `processModelId` können folgende Paramter mitgegeben werden:
308
+ Der `ExternalTaskWorker` ist die zentrale Klasse zur Verarbeitung von External Tasks. Er implementiert eine Polling-Schleife mit Long-Polling, automatischer Lock-Verlängerung und robuster Fehlerbehandlung.
91
309
 
92
- - `startEventId`: Die ID des Start Events von welchem aus der Prozess losgehen soll
93
- - **Hinweis:** Bei Prozessen mit mehreren Start Events ist dieser Parameter ebenfalls erforderlich!
94
- - `correlationId`: Gibt an, in welcher Correlation die Prozessinstanz laufen soll
95
- - `initialToken`: Der JSON-formatierte Prozess-Token, den die Prozessinstanz zu beginn besitzen soll
310
+ #### Minimalbeispiel
96
311
 
97
312
  ```ts
98
- import { EngineClient } from '@5minds/processcube_engine_client';
313
+ import { ExternalTaskWorker } from '@5minds/processcube_engine_client';
99
314
 
100
- const engineUri = 'http://localhost:10560';
315
+ const worker = new ExternalTaskWorker<MyPayload, MyResult>('http://localhost:10560', 'my_topic', async (payload) => {
316
+ return { result: payload.value * 2 };
317
+ });
101
318
 
102
- const client = new EngineClient(engineUri);
319
+ worker.start();
320
+ ```
103
321
 
104
- await client.processModels.startProcessInstance({
105
- processModelId: 'myProcessModelId',
106
- startEventId: 'StartEvent_1',
107
- correlationId: 'MyCorrelatioNid',
108
- initialToken: {
109
- hello: 'world',
322
+ #### Vollständiges Beispiel mit Typen
323
+
324
+ ```ts
325
+ import { ExternalTaskWorker, DataModels } from '@5minds/processcube_engine_client';
326
+
327
+ interface OrderPayload {
328
+ orderId: string;
329
+ items: Array<{ productId: string; quantity: number }>;
330
+ }
331
+
332
+ interface OrderResult {
333
+ confirmationNumber: string;
334
+ totalPrice: number;
335
+ }
336
+
337
+ const worker = new ExternalTaskWorker<OrderPayload, OrderResult>(
338
+ 'http://localhost:10560',
339
+ 'process_order',
340
+ async (payload, externalTask, abortSignal) => {
341
+ const order = await processOrder(payload.orderId, payload.items);
342
+
343
+ if (abortSignal?.aborted) {
344
+ throw new Error('Task wurde abgebrochen');
345
+ }
346
+
347
+ return {
348
+ confirmationNumber: order.confirmation,
349
+ totalPrice: order.total,
350
+ };
110
351
  },
352
+ {
353
+ maxTasks: 5,
354
+ lockDuration: 60000,
355
+ longpollingTimeout: 15000,
356
+ },
357
+ );
358
+
359
+ worker.start();
360
+ ```
361
+
362
+ #### Interner Ablauf des Workers
363
+
364
+ ```mermaid
365
+ flowchart TD
366
+ Start([worker.start]) --> CheckAuth{Identity<br/>gültig?}
367
+ CheckAuth -->|Ja| FetchLock[Fetch & Lock<br/>Long-Polling]
368
+ CheckAuth -->|Nein| RefreshToken[Token erneuern]
369
+ RefreshToken -->|Erfolg| FetchLock
370
+ RefreshToken -->|Fehlgeschlagen| CritErr[Kritischer Fehler<br/>Zähler erhöhen]
371
+ CritErr --> MaxErr{> 5 kritische<br/>Fehler?}
372
+ MaxErr -->|Ja| StopWorker([Worker stoppt])
373
+ MaxErr -->|Nein| Backoff[Exponentielles<br/>Backoff]
374
+ Backoff --> CheckAuth
375
+
376
+ FetchLock -->|Tasks erhalten| Process[Tasks parallel<br/>verarbeiten]
377
+ FetchLock -->|Keine Tasks| Idle[Warten<br/>idleTimeout]
378
+ FetchLock -->|Fehler| ErrHandle[Fehlerbehandlung]
379
+ ErrHandle -->|401/403| PermErr[Permanenter<br/>Auth-Fehler]
380
+ PermErr --> StopWorker
381
+ ErrHandle -->|Transient| Backoff
382
+ Idle --> CheckAuth
383
+ Process --> CheckAuth
384
+
385
+ subgraph "Pro Task (parallel)"
386
+ Process --> Exec[ExternalTaskExecution]
387
+ Exec --> LockExt[Lock-Verlängerung<br/>planen]
388
+ Exec --> RunFn[Processing Function<br/>ausführen]
389
+ RunFn -->|Result| Finish[finishExternalTask]
390
+ RunFn -->|Error| HandleErr[handleError]
391
+ RunFn -->|Aborted| Cleanup[Aufräumen]
392
+ end
393
+ ```
394
+
395
+ ### Worker-Konfiguration
396
+
397
+ ```ts
398
+ import { ExternalTaskWorker, IExternalTaskWorkerConfig } from '@5minds/processcube_engine_client';
399
+
400
+ const config: IExternalTaskWorkerConfig = {
401
+ workerId: 'order-worker-1',
402
+ lockDuration: 30000,
403
+ maxTasks: 10,
404
+ idleTimeout: 500,
405
+ longpollingTimeout: 10000,
406
+ identity: { token: 'Bearer ...', userId: 'worker-user' },
407
+ payloadFilter: /urgent/,
408
+ };
409
+ ```
410
+
411
+ | Option | Typ | Standard | Beschreibung |
412
+ | -------------------- | -------------- | -------- | ------------------------------------------- |
413
+ | `workerId` | `string` | UUID | Eindeutige Worker-ID |
414
+ | `lockDuration` | `number` | `30000` | Dauer des Locks in ms |
415
+ | `maxTasks` | `number` | `10` | Max. Tasks pro Fetch-and-Lock |
416
+ | `idleTimeout` | `number` | `500` | Wartezeit in ms, wenn keine Tasks vorhanden |
417
+ | `longpollingTimeout` | `number` | `10000` | Long-Polling-Timeout in ms |
418
+ | `identity` | `IdentityLike` | - | Authentifizierung |
419
+ | `payloadFilter` | `RegExp` | - | Regex-Filter auf Task-Payload |
420
+ | `logger` | `ILogger` | - | Eigener Logger |
421
+
422
+ ### Processing Function
423
+
424
+ Die Processing Function erhält drei Parameter:
425
+
426
+ ```ts
427
+ type HandleExternalTaskAction<TPayload, TResult> = (payload: TPayload, externalTask?: ExternalTask<TPayload>, signal?: AbortSignal) => TResult | Promise<TResult>;
428
+ ```
429
+
430
+ | Parameter | Beschreibung |
431
+ | -------------- | -------------------------------------------------- |
432
+ | `payload` | Die Nutzdaten des Tasks (generisch typisiert) |
433
+ | `externalTask` | Das vollständige ExternalTask-Objekt mit Metadaten |
434
+ | `signal` | AbortSignal zum Reagieren auf Abbruch |
435
+
436
+ **Rückgabewerte:**
437
+
438
+ | Rückgabe | Verhalten |
439
+ | ------------------- | ----------------------------------------------------------- |
440
+ | Objekt | Task wird mit diesem Ergebnis als erfolgreich abgeschlossen |
441
+ | `ExternalTaskError` | Task wird als fehlgeschlagen gemeldet |
442
+ | `throw Error` | Task wird als fehlgeschlagen gemeldet |
443
+
444
+ #### Fehler gezielt melden
445
+
446
+ ```ts
447
+ import { ExternalTaskWorker, DataModels } from '@5minds/processcube_engine_client';
448
+
449
+ const worker = new ExternalTaskWorker<MyPayload, MyResult>('http://localhost:10560', 'validate_data', async (payload) => {
450
+ if (!payload.email) {
451
+ return new DataModels.ExternalTasks.ExternalTaskError('VALIDATION_ERROR', 'E-Mail-Adresse fehlt', { field: 'email' });
452
+ }
453
+
454
+ return { valid: true };
111
455
  });
112
456
  ```
113
457
 
114
- #### Prozess starten und auf dessen Ende warten
458
+ ### Fehlerbehandlung
459
+
460
+ Der Worker unterscheidet zwischen verschiedenen Fehlertypen und reagiert entsprechend:
461
+
462
+ ```mermaid
463
+ flowchart LR
464
+ Error[Fehler tritt auf]
465
+ Error --> Type{Fehlertyp}
466
+
467
+ Type -->|Netzwerk / Timeout| Retry[Retry mit<br/>exponentiellem Backoff]
468
+ Type -->|Token abgelaufen| Refresh[Token erneuern<br/>und Retry]
469
+ Type -->|401 / 403| Stop[Worker stoppt<br/>nach 5 Versuchen]
470
+ Type -->|409 Conflict| Abort[Task bereits<br/>abgeschlossen - Skip]
471
+ Type -->|Unbekannt| Report[Fehler melden<br/>und weiter]
472
+ ```
473
+
474
+ #### Error-Handler registrieren
115
475
 
116
476
  ```ts
117
- import { EngineClient } from '@5minds/processcube_engine_client';
477
+ worker.onWorkerError((errorType, error, externalTask) => {
478
+ console.error(`[${errorType}]`, error.message);
479
+ });
480
+ ```
481
+
482
+ | ErrorType | Beschreibung |
483
+ | ----------------------------- | ------------------------------------------- |
484
+ | `fetchAndLock` | Fehler beim Abholen von Tasks |
485
+ | `extendLock` | Fehler bei der Lock-Verlängerung |
486
+ | `processExternalTask` | Fehler in der Processing Function |
487
+ | `finishExternalTask` | Fehler beim Abschließen des Tasks |
488
+ | `unprocessableExternalTask` | Task konnte nicht verarbeitet werden |
489
+ | `identityRefresh` | Fehler bei der Token-Erneuerung |
490
+ | `criticalIdentityFailure` | Token-Erneuerung endgültig fehlgeschlagen |
491
+ | `permanentAuthorizationError` | Dauerhafter 401/403-Fehler |
492
+ | `unexpectedLoopError` | Unerwarteter Fehler in der Polling-Schleife |
493
+
494
+ ### Lock-Verlängerung
495
+
496
+ Wenn eine Task-Verarbeitung länger dauert als die `lockDuration`, verlängert der Worker den Lock automatisch im Hintergrund.
497
+
498
+ ```mermaid
499
+ sequenceDiagram
500
+ participant W as Worker
501
+ participant E as Engine
502
+
503
+ W->>E: Fetch & Lock (lockDuration: 30s)
504
+ E-->>W: Task (Lock bis T+30s)
505
+
506
+ Note over W: Verarbeitung startet...
507
+
508
+ W->>E: Extend Lock (bei T+25s)
509
+ E-->>W: OK (Lock bis T+55s)
510
+
511
+ Note over W: Verarbeitung läuft weiter...
118
512
 
119
- const engineUri = 'http://localhost:10560';
513
+ W->>E: Extend Lock (bei T+50s)
514
+ E-->>W: OK (Lock bis T+80s)
120
515
 
121
- const client = new EngineClient(engineUri);
516
+ Note over W: Verarbeitung abgeschlossen
122
517
 
123
- await client.processModels.startProcessInstanceAndAwaitEndEvent({ processModelId: 'myProcessModelId' });
518
+ W->>E: Finish Task (Result)
124
519
  ```
125
520
 
126
- Hier wartet der Client, bis die Engine den Prozess bis zum Ende ausgeführt hat.
521
+ Die Lock-Verlängerung wird 5 Sekunden vor Ablauf des aktuellen Locks ausgelöst und verlängert jeweils um die konfigurierte `lockDuration`. Dies geschieht automatisch und transparent.
127
522
 
128
- Die Methode akzeptiert dieselben Parameter wie `processModels.startProcessInstance`.
523
+ ### Health Monitoring
129
524
 
130
- ## Query UserTasks
525
+ ```ts
526
+ const health = worker.getHealth();
527
+ ```
528
+
529
+ Das Health-Objekt enthält:
131
530
 
132
- Abfragen aller User Tasks eines bestimmten Prozesses, die sich in einem "suspended" State befinden:
531
+ | Eigenschaft | Typ | Beschreibung |
532
+ | --------------------------------- | --------- | --------------------------------------------- |
533
+ | `isPollingActive` | `boolean` | Ob die Polling-Schleife aktiv ist |
534
+ | `consecutiveCriticalErrors` | `number` | Anzahl aufeinanderfolgender kritischer Fehler |
535
+ | `maxConsecutiveCriticalErrors` | `number` | Maximum (5), danach stoppt der Worker |
536
+ | `activeTaskCount` | `number` | Anzahl aktuell verarbeiteter Tasks |
537
+ | `identityRefreshFailedCritically` | `boolean` | Ob die Token-Erneuerung fehlgeschlagen ist |
133
538
 
134
539
  ```ts
135
- import { EngineClient, DataModels } from '@5minds/processcube_engine_client';
540
+ if (!worker.isHealthy()) {
541
+ // Worker neu starten oder Alarm auslösen
542
+ }
543
+ ```
136
544
 
137
- const engineUri = 'http://localhost:10560';
545
+ ### Heartbeat Monitoring
138
546
 
139
- const client = new EngineClient(engineUri);
547
+ Für detailliertes Monitoring kann ein Heartbeat-Callback registriert werden:
140
548
 
141
- const userTasks = await client.userTasks.query({
142
- processModelId: 'myProcessModelId',
143
- state: DataModels.FlowNodeInstances.FlowNodeInstanceState.suspended,
549
+ ```ts
550
+ worker.onHeartbeat((operation, externalTaskId) => {
551
+ metrics.recordHeartbeat(operation, externalTaskId);
144
552
  });
553
+ ```
554
+
555
+ Gemeldete Operationen: `fetchAndLock`, `processExternalTask`, `extendLock`, `finishExternalTask`, `handleError`
556
+
557
+ ### Graceful Shutdown
145
558
 
146
- console.log(userTasks);
559
+ ```ts
560
+ process.on('SIGTERM', () => {
561
+ worker.stop();
562
+ });
147
563
  ```
148
564
 
149
- ## External Task Worker
565
+ `stop()` bewirkt:
150
566
 
151
- External Task Worker werden dazu benutzt, um die an einer Prozessinstanz anfallenden `External Service Tasks` zu verarbeiten.
567
+ 1. Polling-Schleife wird beendet
568
+ 2. Alle laufenden Tasks erhalten ein Abort-Signal
569
+ 3. Lock-Verlängerungen werden gestoppt
152
570
 
153
- Ein Worker benötigt minimal die folgenden 3 Einstellungen
571
+ Für vollständiges Aufräumen:
154
572
 
155
- - Die URL der Ziel-Engine
156
- - Das Topic der abzuarbeitenden External Tasks
157
- - Eine Handler Funktion zum Verarbeiten der External Tasks
573
+ ```ts
574
+ worker.dispose();
575
+ ```
158
576
 
159
- Beispiel:
577
+ ### Multi-Topic Worker
578
+
579
+ Über die `ExternalTaskApiClient`-Klasse lassen sich Tasks aus mehreren Topics gleichzeitig abholen:
160
580
 
161
581
  ```ts
162
- import { ExternalTaskWorker } from '@5minds/processcube_engine_client';
582
+ import { ClientFactory } from '@5minds/processcube_engine_client';
163
583
 
164
- interface AddPayload {
165
- number1: number;
166
- number2: number;
167
- }
584
+ const externalTaskApiClient = ClientFactory.createExternalTaskClient('http://localhost:10560');
168
585
 
169
- interface AddResult {
170
- sum: number;
171
- }
586
+ const tasks = await externalTaskApiClient.fetchAndLockExternalTasks('my-worker-id', ['topic_a', 'topic_b', 'topic_c'], 10, 10000, 30000);
587
+ ```
588
+
589
+ ### Payload-Filter
172
590
 
173
- const engineUri = 'http://localhost:10560';
174
- const topic = 'sum_numbers';
591
+ Mit einem Payload-Filter können nur Tasks abgeholt werden, deren Payload einem bestimmten Muster entspricht:
175
592
 
176
- const externalTaskWorker = new ExternalTaskWorker<AddPayload, AddResult>(url, topic, doAdd, config);
593
+ ```ts
594
+ const worker = new ExternalTaskWorker<MyPayload, MyResult>('http://localhost:10560', 'notifications', handleNotification, {
595
+ payloadFilter: /priority.*high/,
596
+ });
597
+ ```
177
598
 
178
- externalTaskWorker.start();
599
+ ### ExternalTask REST API
179
600
 
180
- async function doAdd(payload: AddPayload, externalTask: DataModels.ExternalTasks.ExternalTask<AddPayload>): Promise<AddResult> {
181
- const result: AddResult = {
182
- sum: payload.number1 + payload.number2,
183
- };
601
+ Der Client kommuniziert mit folgenden Engine-Endpunkten:
184
602
 
185
- return result;
186
- }
603
+ | Methode | Endpunkt | Beschreibung |
604
+ | ------- | ------------------------------------------------------ | --------------------------- |
605
+ | `GET` | `/atlas_engine/api/v1/external_tasks/deployed_topics` | Alle aktiven Topics abrufen |
606
+ | `POST` | `/atlas_engine/api/v1/external_tasks/fetch_and_lock` | Tasks abholen und sperren |
607
+ | `PUT` | `/atlas_engine/api/v1/external_tasks/{id}/extend_lock` | Lock verlängern |
608
+ | `PUT` | `/atlas_engine/api/v1/external_tasks/{id}/finish` | Task abschließen |
609
+ | `PUT` | `/atlas_engine/api/v1/external_tasks/{id}/error` | Fehler melden |
610
+
611
+ ### ExternalTask-Datenmodell
612
+
613
+ ```ts
614
+ type ExternalTask<TPayload> = {
615
+ id: string;
616
+ workerId: string;
617
+ topic: string;
618
+ isSingleTry: boolean;
619
+ flowNodeInstanceId: string;
620
+ correlationId: string;
621
+ processDefinitionId: string;
622
+ processModelId?: string;
623
+ embeddedProcessModelId?: string;
624
+ processInstanceId: string;
625
+ ownerId: string;
626
+ payload: TPayload;
627
+ lockExpirationTime: Date;
628
+ state: ExternalTaskState; // 'pending' | 'finished' | 'expired'
629
+ finishedAt?: Date;
630
+ result?: any;
631
+ error?: any;
632
+ createdAt?: Date;
633
+ };
187
634
  ```
188
635
 
189
- Der Worker lässt sich mit folgendem Befehl starten:
636
+ ### ExternalTaskError
190
637
 
191
638
  ```ts
192
- externalTaskWorker.start();
639
+ class ExternalTaskError {
640
+ errorCode: string;
641
+ errorMessage?: string;
642
+ errorDetails?: any;
643
+
644
+ constructor(errorCode: string, errorMessage?: string, errorDetails?: any);
645
+ }
193
646
  ```
194
647
 
195
- und mit folgendem Befehl stoppen:
648
+ ---
649
+
650
+ ## Echtzeit-Events via Socket.IO
651
+
652
+ Der Client kann sich über Socket.IO für Echtzeit-Benachrichtigungen der Engine registrieren.
653
+
654
+ ### Notification-Events
196
655
 
197
656
  ```ts
198
- externalTaskWorker.stop();
657
+ const subscription = await client.notification.onProcessStarted((event) => {
658
+ // Prozess wurde gestartet
659
+ });
660
+
661
+ // Später abmelden
662
+ client.notification.removeSubscription(subscription);
199
663
  ```
200
664
 
201
- Ein ausführbares Code-Beispiel [findet sich hier](./samples/external_task_worker/).
665
+ ### ExternalTask-Events
666
+
667
+ | Event | Beschreibung |
668
+ | ------------------------ | ------------------------------------------------ |
669
+ | `external_task_created` | Ein neuer ExternalTask wurde erstellt |
670
+ | `external_task_locked` | Ein ExternalTask wurde von einem Worker gesperrt |
671
+ | `external_task_unlocked` | Ein Lock ist abgelaufen (Multi-Try) |
672
+ | `external_task_expired` | Ein Lock ist abgelaufen (Single-Try) |
673
+
674
+ ### Prozess-Events
675
+
676
+ | Event | Beschreibung |
677
+ | --------------------- | ---------------------------------- |
678
+ | `OnProcessStarting` | Prozessinstanz wird gestartet |
679
+ | `OnProcessStarted` | Prozessinstanz wurde gestartet |
680
+ | `OnProcessFinished` | Prozessinstanz wurde abgeschlossen |
681
+ | `OnProcessError` | Fehler in einer Prozessinstanz |
682
+ | `OnProcessTerminated` | Prozessinstanz wurde abgebrochen |
683
+
684
+ ### Task-Events
685
+
686
+ | Event | Beschreibung |
687
+ | ---------------------- | --------------------------------- |
688
+ | `user_task_waiting` | UserTask wartet auf Bearbeitung |
689
+ | `user_task_finished` | UserTask wurde abgeschlossen |
690
+ | `manual_task_waiting` | ManualTask wartet auf Bearbeitung |
691
+ | `manual_task_finished` | ManualTask wurde abgeschlossen |
692
+
693
+ ---
694
+
695
+ ## Alle verfügbaren Clients
696
+
697
+ | Client | Property | Beschreibung |
698
+ | -------------------------- | ---------------------------- | -------------------------------------------- |
699
+ | `ProcessModelClient` | `client.processModels` | Prozessmodelle deployen, starten, abfragen |
700
+ | `ProcessInstanceClient` | `client.processInstances` | Laufende und abgeschlossene Prozessinstanzen |
701
+ | `ProcessDefinitionClient` | `client.processDefinitions` | Prozessdefinitionen (BPMN-XML) verwalten |
702
+ | `UserTaskClient` | `client.userTasks` | UserTasks abfragen und abschließen |
703
+ | `ExternalTaskClient` | `client.externalTasks` | ExternalTasks abonnieren und verarbeiten |
704
+ | `ManualTaskClient` | `client.manualTasks` | ManualTasks abfragen und abschließen |
705
+ | `UntypedTaskClient` | `client.untypedTasks` | Untypisierte Tasks verwalten |
706
+ | `CorrelationClient` | `client.correlations` | Correlations abfragen |
707
+ | `EventClient` | `client.events` | BPMN-Message/Signal-Events senden |
708
+ | `NotificationClient` | `client.notification` | Echtzeit-Events über Socket.IO |
709
+ | `CronjobClient` | `client.cronjobs` | Zeitgesteuerte Prozesse verwalten |
710
+ | `FlowNodeInstanceClient` | `client.flowNodeInstances` | FlowNode-Instanzen abfragen |
711
+ | `DataObjectInstanceClient` | `client.dataObjectInstances` | Prozess-Datenobjekte abfragen |
712
+ | `ApplicationInfoClient` | `client.applicationInfo` | Engine-Version und Metadaten |
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "utf-8-validate": "Not used by the client directly, but a requirement of the 'ws' sub-dependency. Since it is not available in the browser, the client would be unusable for the APP SDK, if we did not install it explicitly here"
5
5
  },
6
6
  "name": "@5minds/processcube_engine_client",
7
- "version": "6.3.0-develop.1",
7
+ "version": "6.3.0-develop.2",
8
8
  "description": "Contains a typescript based client for accessing the Engine.",
9
9
  "main": "dist/commonjs/index.js",
10
10
  "types": "dist/index.d.ts",
@@ -32,14 +32,14 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "async-lock": "^1.4.1",
35
- "bufferutil": "4.0.9",
35
+ "bufferutil": "4.1.0",
36
36
  "cross-fetch": "4.1.0",
37
- "dayjs": "^1.11.18",
38
- "openid-client": "^6.8.1",
39
- "socket.io-client": "4.8.1",
37
+ "dayjs": "^1.11.20",
38
+ "openid-client": "^6.8.2",
39
+ "socket.io-client": "4.8.3",
40
40
  "utf-8-validate": "5.0.10",
41
41
  "uuid": "^13.0.0",
42
- "@5minds/processcube_engine_sdk": "7.3.0-develop.1"
42
+ "@5minds/processcube_engine_sdk": "7.3.0-develop.2"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^24.7.2",