Schéma grafu
Workflow je jeden JSON dokument. Balík ho zverejňuje ako JSON Schema (podmnožina draft 2020-12) cez Workflows::schema()->graph(); obálku šablóny cez Workflows::schema()->template() a spoločné $defs (value, field, party, parties, duration, condition) cez Workflows::schema()->definitions() — schéma grafu k nim pridáva retry a escalation. Konfigurácie uzlov sú typované podľa typu kroku: pozri Workflows::catalogue()->export().
Workflows::schema()->graph(); // the roundly.workflows.graph/v1 JSON Schema
Workflows::schema()->template(); // the roundly.workflows.template/v1 envelope
Workflows::schema()->definitions(); // the shared $defs: value, field, party, parties, duration, condition
Workflows::catalogue()->export(); // every node type's config schema, outcomes and availabilityDokument
{
"schema": "roundly.workflows.graph/v1", // required; anything else → invalid_schema_version
"settings": { // optional; each key has a default
"actor": "system" | "on_behalf_of", // default "system"
"timezone": "Europe/Bratislava", // IANA; default workflows.timezone → app.timezone
"calendar": "weekdays", // default business calendar
"limits": { "max_steps": 200 }, // may only LOWER the configured limit
"retry": { "max_attempts": 3, "backoff": [30, 120, 600] }
},
"nodes": [ {
"id": "approve_cfo", // ^[a-z][a-z0-9_]{0,63}$, unique
"type": "approval", // a catalogue key
"config": { }, // validated against the type's config schema
"retry": { }, // optional per-node override
"escalations": [ ], // only on types that support them
"ui": { "x": 120, "y": 80, "label": "CFO sign-off" } // free-form; ignored by the engine
} ],
"edges": [ {
"from": "approve_cfo", "to": "notify_rejected",
"outcome": "rejected" // required unless the source's only outcome is `next`
} ]
}Pravidlá
- Presne jeden uzol trigger, aspoň jeden uzol end.
- Stropy veľkosti: uzly ≤ graph.max_nodes (500), hrany ≤ graph.max_edges (2000), zakódovaný graf ≤ graph.max_bytes (512 KiB) → graph_too_large.
- Hranu určuje dvojica (from, outcome). Dve hrany s rovnakou dvojicou → duplicate_outcome. Grafom prechádza jeden token: paralelné vetvy neexistujú.
- Hrana bez outcome znamená next a nič iné, preto je povolená len z uzla, ktorého jediný výsledok je next (set_field, delay, akčný krok, …). Uzol s akýmkoľvek iným výsledkom — aj jediným, ako start_workflow bez wait (started), wait (received), webhook (queued) — ho musí uviesť na každej hrane, inak pri publikovaní outcome_label_required.
- Cyklus musí prechádzať prerušovačom: hranou continue uzla loop alebo oneskorením delay s kladným for. Hrana exhausted uzla loop a oneskorenie s nulovou dĺžkou, until či until_field sa nerátajú → cycle_without_breaker.
- ui sa ukladá doslovne a nikdy sa neinterpretuje (porovnanie hlási zmeny len v ui ako presun).
- Uloženie konceptu kontroluje len štruktúru, takže editor môže uložiť aj nedokončený graf. Publikovanie spustí všetky kontroly.
Odkazy na hodnoty
Všade, kde schéma povoľuje #/$defs/value, je hodnota literál, {"ref": "<path>"} alebo {"relative": <duration>} — „teraz“ behu plus (pri zápornej hodnote mínus) trvanie, v časovom pásme verzie:
"value": "50000" // a literal
"value": { "ref": "subject.amount" } // a registered field of the subject
"value": { "ref": "steps.approve.output.request_id" } // the output of an ancestor step
"value": { "relative": "P7D" } // now + 7 days, in the version timezone
"value": { "relative": { "amount": -3, "unit": "days" } } // now − 3 days| Cesta | Číta |
|---|---|
subject.<field> | Registrované pole subjektu (nikdy neregistrovaný atribút). |
owner.<field> | Registrované pole typu vlastníka. |
input.<name> | Deklarovaný vstup ručného spúšťača. |
trigger.<key> | Payload spúšťača (payload udalosti, telo webhooku). |
steps.<node_id>.output.<key> | Výstup predchádzajúceho kroku. |
now | Aktuálny okamih. |
payload.<path> | Telo webhooku — len v subject.value webhook spúšťača (subjekt sa hľadá skôr, než inštancia vznikne). |
Trvania
Reťazec ISO 8601 (kalendárny čas v časovom pásme verzie) alebo objekt s amount a unit — minutes, hours, days, weeks, months, business_minutes, business_hours, business_days. calendar prepíše settings.calendar:
"P2D" // ISO 8601: calendar time in the version timezone
"PT4H"
{ "amount": 2, "unit": "business_days", "calendar": "opening_hours:office" }Odkazy na strany
{"kind": "user", "id": 12}
{"kind": "field", "field": "subject.requester"}
{"kind": "role", "role": "legal"}
{"kind": "triggered_by"}Verzovanie schémy
Verziou je reťazec schema. Budúca nekompatibilná zmena dostane nový reťazec; dokumenty roundly.workflows.graph/v1 sa budú naďalej validovať aj spúšťať.
JSON Schema grafu
Vygenerovaná cez Workflows::schema()->graph() — limity zodpovedajú predvolenej konfigurácii:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://roundly-consulting.com/schemas/roundly.workflows.graph/v1.json",
"title": "Workflow graph",
"type": "object",
"required": [
"schema",
"nodes",
"edges"
],
"properties": {
"schema": {
"const": "roundly.workflows.graph/v1"
},
"settings": {
"type": "object",
"properties": {
"actor": {
"enum": [
"system",
"on_behalf_of"
],
"default": "system"
},
"timezone": {
"type": "string",
"description": "IANA timezone; default workflows.timezone, else app.timezone."
},
"calendar": {
"type": "string",
"description": "Default business calendar (weekdays, opening_hours:<name> or a host calendar)."
},
"limits": {
"type": "object",
"properties": {
"max_steps": {
"type": "integer",
"minimum": 2,
"maximum": 200
}
},
"additionalProperties": false
},
"retry": {
"$ref": "#/$defs/retry"
}
},
"additionalProperties": false
},
"nodes": {
"type": "array",
"maxItems": 500,
"items": {
"type": "object",
"required": [
"id",
"type"
],
"properties": {
"id": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,63}$"
},
"type": {
"type": "string",
"description": "A step catalogue key."
},
"config": {
"type": "object",
"description": "Validated against the type's config schema."
},
"retry": {
"$ref": "#/$defs/retry"
},
"escalations": {
"type": "array",
"maxItems": 5,
"items": {
"$ref": "#/$defs/escalation"
}
},
"ui": {
"type": "object",
"description": "Free-form (position, label); ignored by the engine."
}
},
"additionalProperties": false
}
},
"edges": {
"type": "array",
"maxItems": 2000,
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"from": {
"type": "string"
},
"to": {
"type": "string"
},
"outcome": {
"type": "string",
"description": "Required unless the source type's only outcome is next."
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false,
"$defs": {
"value": {
"description": "A literal, {\"ref\": \"<path>\"} or {\"relative\": <duration>}."
},
"field": {
"type": "string",
"format": "field-path",
"maxLength": 200
},
"party": {
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"user",
"field",
"role",
"triggered_by"
]
},
"id": {
"type": [
"integer",
"string"
],
"x-workflows-owner-specific": true
},
"field": {
"$ref": "#/$defs/field"
},
"role": {
"type": "string",
"minLength": 1,
"maxLength": 100
}
},
"additionalProperties": false
},
"parties": {
"type": "array",
"items": {
"$ref": "#/$defs/party"
},
"minItems": 1,
"maxItems": 50
},
"duration": {
"anyOf": [
{
"type": "string",
"format": "duration"
},
{
"type": "object",
"required": [
"amount",
"unit"
],
"properties": {
"amount": {
"type": "integer",
"minimum": -100000,
"maximum": 100000
},
"unit": {
"enum": [
"minutes",
"hours",
"days",
"weeks",
"months",
"business_minutes",
"business_hours",
"business_days"
]
},
"calendar": {
"type": "string",
"minLength": 1,
"maxLength": 100
}
},
"additionalProperties": false
}
]
},
"condition": {
"type": "object",
"description": "A declarative condition: {\"all\"|\"any\": [...]}, {\"not\": {...}} or {\"field\", \"op\", \"value\"}."
},
"retry": {
"type": "object",
"properties": {
"max_attempts": {
"type": "integer",
"minimum": 1,
"maximum": 10
},
"backoff": {
"type": "array",
"items": {
"type": "integer",
"minimum": 0
}
}
},
"additionalProperties": false
},
"escalation": {
"type": "object",
"required": [
"after",
"actions"
],
"properties": {
"after": {
"$ref": "#/$defs/duration"
},
"actions": {
"type": "array",
"minItems": 1,
"maxItems": 5,
"items": {
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"enum": [
"notify",
"reassign",
"start_workflow"
]
}
}
}
}
},
"additionalProperties": false
}
}
}JSON Schema šablóny
Vygenerovaná cez Workflows::schema()->template() — obálka exportovanej šablóny aj súboru definície:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://roundly-consulting.com/schemas/roundly.workflows.template/v1.json",
"title": "Workflow template",
"type": "object",
"required": [
"schema",
"definition",
"graph"
],
"properties": {
"schema": {
"const": "roundly.workflows.template/v1"
},
"definition": {
"type": "object",
"required": [
"key",
"name"
],
"properties": {
"key": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9._-]{0,99}$"
},
"name": {
"type": "string"
},
"subject_type": {
"type": [
"string",
"null"
]
},
"description": {
"type": [
"string",
"null"
]
}
}
},
"graph": {
"$ref": "roundly.workflows.graph/v1"
},
"review": {
"type": "array"
},
"exported_at": {
"type": "string",
"format": "date-time"
},
"source": {
"type": "object",
"properties": {
"checksum": {
"type": "string"
}
}
}
}
}Spoločné definície
Vygenerované cez Workflows::schema()->definitions() — $defs, na ktoré sa môže odvolávať schéma konfigurácie každého kroku:
{
"value": {
"description": "A literal, {\"ref\": \"<path>\"} or {\"relative\": <duration>}."
},
"field": {
"type": "string",
"format": "field-path",
"maxLength": 200
},
"party": {
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"user",
"field",
"role",
"triggered_by"
]
},
"id": {
"type": [
"integer",
"string"
],
"x-workflows-owner-specific": true
},
"field": {
"$ref": "#/$defs/field"
},
"role": {
"type": "string",
"minLength": 1,
"maxLength": 100
}
},
"additionalProperties": false
},
"parties": {
"type": "array",
"items": {
"$ref": "#/$defs/party"
},
"minItems": 1,
"maxItems": 50
},
"duration": {
"anyOf": [
{
"type": "string",
"format": "duration"
},
{
"type": "object",
"required": [
"amount",
"unit"
],
"properties": {
"amount": {
"type": "integer",
"minimum": -100000,
"maximum": 100000
},
"unit": {
"enum": [
"minutes",
"hours",
"days",
"weeks",
"months",
"business_minutes",
"business_hours",
"business_days"
]
},
"calendar": {
"type": "string",
"minLength": 1,
"maxLength": 100
}
},
"additionalProperties": false
}
]
},
"condition": {
"type": "object",
"description": "A declarative condition: {\"all\"|\"any\": [...]}, {\"not\": {...}} or {\"field\", \"op\", \"value\"}."
}
}Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.