diff --git a/README.md b/README.md index dbc4665..31fdbee 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,9 @@ A trigger action (`save_schema_trigger`, `save_files_trigger`, `initiatorId` (who the send is attributed to) on Email, Push and SMS actions. Two related calls: +- `trigger.order` (whole number, lower runs first) and `trigger.breakOnError` + (stop the later triggers of the same event when this one fails) set the queue + of one event's triggers — see [docs/hub/triggers.md](./docs/hub/triggers.md). - `norbix.hub.triggers.get_triggers_needing_attention(triggerType="Schema")` — triggers that need a fix (for example, their provider is gone); `{"items": [...]}`. - `norbix.hub.account.check_project_languages(project_id, languages=["en", "de"])` — diff --git a/docs/hub/triggers.md b/docs/hub/triggers.md index 29aa832..daa8f98 100644 --- a/docs/hub/triggers.md +++ b/docs/hub/triggers.md @@ -41,3 +41,31 @@ norbix.hub.database.save_schema_trigger( ``` Everything here is asserted in `tests/hub/test_triggers.py`. + +## Order and "break on failure" + +When several triggers fire for the same event, they run one after the other. +Two optional fields on `trigger` decide that queue (all four save methods): + +- `order` — a whole number, 0 or more. Lower runs earlier; a trigger without + an order runs after every numbered one; equal places run by name. +- `breakOnError` — `True` stops the triggers after this one when this + trigger's action fails. Default `False`: the others still run. + +```python +norbix.hub.membership.save_membership_trigger( + trigger={ + "type": "Membership", + "name": "welcome first", + "when": "OnRegistered", + "isEnabled": True, + "order": 1, + "breakOnError": True, + "action": {"type": "Email", "integrationId": "int_email", "templateId": "tpl_123"}, + } +) +``` + +Both come back on `get_*_trigger` and on every row of `get_*_triggers`. A +negative `order` is refused with `CM-ERRORS-TRIGGERS-008`. + diff --git a/tests/hub/test_trigger_order.py b/tests/hub/test_trigger_order.py new file mode 100644 index 0000000..2bb0605 --- /dev/null +++ b/tests/hub/test_trigger_order.py @@ -0,0 +1,45 @@ +"""Trigger "order" and "break on failure" reach the gateway on every save. + +The four save methods send the caller's map as the body, so `trigger.order` +and `trigger.breakOnError` must arrive exactly as given (gateway task +trigger-order-break: the fields were added to every trigger kind). +""" + +import json + +import pytest + +from tests.helpers import make_client + +TRIGGER = { + "name": "welcome first", + "isEnabled": True, + "order": 0, + "breakOnError": True, + "action": {"type": "WebhookCall"}, +} + + +@pytest.mark.parametrize( + ("module", "method", "kind", "path"), + [ + ("membership", "save_membership_trigger", "Membership", "/membership/triggers"), + ("database", "save_schema_trigger", "Schema", "/database/schemas/triggers"), + ("files", "save_files_trigger", "Files", "/files/triggers"), + ("payments", "save_payments_trigger", "Payments", "/payments/triggers"), + ], +) +def test_save_trigger_sends_order_and_break_on_error(module: str, method: str, kind: str, path: str) -> None: + client, transport = make_client(account_id=None) + + getattr(getattr(client.hub, module), method)(trigger={"type": kind, **TRIGGER}) + + assert transport.last_request is not None + assert transport.last_request["method"] == "POST" + assert transport.last_request["url"].endswith(path) + sent = json.loads(transport.last_request["body"])["trigger"] + assert {k: sent[k] for k in ("type", "order", "breakOnError")} == { + "type": kind, + "order": 0, + "breakOnError": True, + }