Files
workflow-editor/README.md
T

159 lines
6.2 KiB
Markdown

# Workflow Editor
n8n-inspirierter Editor zum Erstellen von Aufgabenlisten (Tasks) für die
stadtbote-App. Funktionsblöcke werden per Drag & Drop auf einem Canvas
angeordnet und verbunden; daraus wird das app-konforme `tasks`-JSON erzeugt.
## Funktionen
- Funktionsblöcke für die stadtbote-Task-Typen (Ankunft, Unterschrift, Fotos,
Quittungsgeber, Stationshinweis, Kommissionsnummer, Abhol-/Lieferstatus)
- Erzeugen des app-konformen Task-JSON
- Eigene Typen mit konfigurierbaren Vorgaben
- Aufruf per Deep-Link mit Übergabe von Tasks, eigenen Typen und Metadaten als JSON-Body
- Rückgabe der bearbeiteten Tasks und des vollständigen eigenen Typkatalogs an den Aufrufer
## Technik
- Java 21, Spring Boot 3, Vaadin 24
- Drawflow als Canvas-/Node-Editor
## Konfiguration
Für den Editor ist keine Datenbank erforderlich. Tasks und eigene Typen werden
beim Aufruf übergeben und bei **Fertig** an den Aufrufer zurückgegeben. Der
Aufrufer ist für deren dauerhafte Speicherung verantwortlich.
Optionale Einstellungen sind in `.env.example` beschrieben.
## Start
```bash
mvn spring-boot:run
```
Die Anwendung läuft anschließend unter http://localhost:8080/.
## Aufruf und Rückgabe mit dem Python-Testserver
Die Webapp muss mit dem aktuellen Code auf Port 8080 laufen. In einem zweiten
Terminal den Testserver starten (Python 3, keine zusätzlichen Pakete):
```bash
python3 tools/editor_test_server.py
```
Der Testserver öffnet seine Aufruferseite unter `http://127.0.0.1:8090`.
Dort das Beispiel bearbeiten oder eigenes JSON einfügen und den Editor öffnen.
Der Editor erscheint eingebettet in der Seite. **Fertig** gibt die validierten
Tasks und den vollständigen Katalog eigener Typen zurück; **Abbrechen** meldet
den Abbruch ohne bearbeitete Tasks oder Typen.
Beide Ergebnisse werden im Python-Terminal als JSON ausgegeben und auf der
Testseite angezeigt. Nach **Fertig** enthält das JSON-Eingabefeld die Rückgabe
für den nächsten Editor-Aufruf. **Neue Liste** leert die Tasks und behält den
aktuellen eigenen Typkatalog. Nach **Abbrechen** bleibt die Eingabe unverändert.
Die Testseite hält diese Daten nur bis zum Neuladen; die Konsolenausgabe bleibt
im Terminal verfügbar.
Eine Datei vorgeben oder andere Ports verwenden:
```bash
python3 tools/editor_test_server.py --json tools/example_tasks.json
python3 tools/editor_test_server.py --editor-url http://127.0.0.1:8080 --port 8090 --no-browser
```
Als Eingabe ist ein Task-Array oder ein Objekt erlaubt:
```json
{
"name": "Testauftrag",
"tasks": [{ "type": 3, "topic": "Ankunft melden", "track": true }],
"customTypes": [],
"metadata": { "auftragId": "TEST-4711", "revision": 2 }
}
```
Für eine neue Liste `tasks` weglassen oder `[]` übergeben. Die Metadaten werden
unverändert zurückgegeben. Zum Abschließen eines neu aufgebauten Workflows
müssen Start, Aufgaben und Abschluss vollständig verbunden sein. Beim Import
vorhandener Tasks ergänzt der Editor die internen Start-/Abschlussmarkierungen;
sie sind nicht Teil des zurückgegebenen Task-Arrays.
### Eigene Typen
`customTypes` enthält den vollständigen Katalog für diesen Editor-Aufruf.
Ohne dieses Feld oder mit `[]` stehen nur die eingebauten Typen zur Verfügung.
Eigene Typen werden vor den Tasks geladen. Ein Task verweist mit `type` auf die
`taskType`-Nummer seines eigenen Typs (ab 1000), beispielsweise:
```json
{
"name": "Zustellnachweis",
"tasks": [{ "type": 1000, "topic": "Zustellung fotografieren", "min": 1, "max": 3 }],
"customTypes": [{
"typeId": "custom-1000",
"taskType": 1000,
"baseTypeId": "fotos-aufnehmen",
"label": "Zustellnachweis",
"description": "Fotos zur Dokumentation einer Zustellung.",
"icon": "vaadin:camera",
"color": "#059669",
"inputs": 1,
"outputs": 1,
"properties": [
{ "id": "min", "label": "Mindestanzahl Fotos", "type": "NUMBER", "defaultValue": 1 },
{ "id": "max", "label": "Maximale Anzahl Fotos", "type": "NUMBER", "defaultValue": 3 },
{ "id": "opt", "label": "Optionale Aufgabe", "type": "BOOLEAN", "defaultValue": false },
{ "id": "reenter", "label": "Erneut bearbeitbar", "type": "BOOLEAN", "defaultValue": true }
]
}],
"metadata": { "auftragId": "TEST-4711" }
}
```
`properties` beschreibt die Eigenschaften samt Vorgabewerten; `type` ist dabei
einer der Werte `TEXT`, `TEXTAREA`, `NUMBER`, `BOOLEAN`, `FIELD_LIST`, `NAME_LIST`
oder `CHOICE_LIST`. Das mitgelieferte Beispiel `tools/example_tasks.json`
enthält einen eigenen Foto-Typ und einen zugehörigen Task.
Bei **Fertig** wird `customTypes` immer vollständig zurückgegeben, einschließlich
unveränderter und neu angelegter Typen. Der Aufrufer ersetzt seinen bisherigen
Katalog durch dieses Array, damit auch gelöschte Typen berücksichtigt werden.
`[]` bedeutet, dass keine eigenen Typen mehr vorhanden sind. Bei **Abbrechen**
übernimmt der Aufrufer keine Änderungen.
### Übergabeprotokoll
Die Python-Seite ruft serverseitig `POST /api/editor/import` auf und ergänzt
`returnOrigin`, z. B. `http://127.0.0.1:8090`. Die Antwort enthält `token` und
`deepLink`. Der Aufrufer öffnet den Deep-Link in einem iframe oder Popup.
Der Import bleibt für 30 Minuten im Speicher der Webapp verfügbar, sodass ein
Neuladen während dieser Zeit möglich ist.
Der Editor sendet das Ergebnis per `postMessage` ausschließlich an diese Origin:
```json
{
"type": "tasklist-editor:result",
"token": "<Token aus der Importantwort>",
"status": "completed",
"name": "Testauftrag",
"metadata": { "auftragId": "TEST-4711", "revision": 2 },
"tasks": [{ "s_id": 1, "type": 3, "topic": "Ankunft melden", "track": true }],
"customTypes": []
}
```
Bei Abbruch ist `status` gleich `cancelled`; die Felder `tasks` und `customTypes`
entfallen.
Die Testseite prüft Absender-Origin, Fenster und Token und leitet das Ergebnis
an `POST /api/result` des Python-Servers weiter. Erst nach dessen Bestätigung
wird der iframe geschlossen. Ohne `returnOrigin` zeigt der Editor einen
schreibgeschützten JSON-Dialog zum Kopieren mit `name`, `metadata`, `tasks`
und `customTypes`.
Der Testserver ist ein lokales Entwicklungswerkzeug und bindet an `127.0.0.1`.
Er übernimmt bekannte Task-Felder des Editors; unbekannte Task-Typen werden vom
bestehenden Importer übersprungen. Beliebige zusätzliche Daten daher in
`metadata` übergeben.