From e8bb4d6e54f69909de5b2986b5e922252f97f143 Mon Sep 17 00:00:00 2001 From: JiriUhlir <149317995+JiriUhlir@users.noreply.github.com> Date: Wed, 2 Sep 2026 10:14:48 +0200 Subject: [PATCH] Kontrakt webhooku: objekt jde vybrat a odmitnuti je videt Parametr spoustece `data` byl deklarovany jako type string a povinny, zatimco odesilatel ho posila jako objekt a v prvni zprave hovoru ho jeste nema. Kazde volani proto skoncilo na 400 a automatizace hodinu nedelala nic. Za tim byly tri veci, kazda sama o sobe malicherna: - rucne pridany parametr byl vychozi povinny, zatimco parametr odvozeny z ukazkoveho tela nepovinny. Dve ruzna vychozi nastaveni pro tutez vec v jednom formulari. Nove je nepovinny i rucne pridany: povinny znamena "odmitni volani" a do toho nema nikdo spadnout omylem - objekt a seznam neslo vybrat. declarableFieldTypes nabizel jen string, number, boolean a date, a TriggerConfig.tsx mel jeste treti kopii toho seznamu. Deklarovat data jako objekt tedy neslo, i kdyz matchesType objekt umi a operatorsByType pro nej ma operatory - odmitnuti nebylo nikde videt. Skoncilo jako console.warn v logu kontejneru: zadna udalost, zadny beh, nic na detailu automatizace Ten treti bod je ten podstatny. Chybu v kontraktu udela ten, kdo ho psal, ale 400 dostane odesilatel - a ten s tim nic nenadela, casto je to cizi sluzba, ktera volani neopakuje. Majitel automatizace se nedozvi nic a v portalu vypada vsechno v poradku. Detail automatizace proto ukazuje poslednich deset volani: cas, jestli proslo nebo ne, a u odmitnutych duvod. Telo se schvalne neuklada, duvod uz rika, co je spatne, a drzet payloady by znamenalo mit v pameti kopie zakaznickych dat. Seznam je v pameti, restart ho zahodi. Incident se z toho nezaklada a upozorneni se neposila: staci radek, implementator se ozve sam. Vzorova automatizace ma data opravene na object a nepovinne. Do navrhu 25 jsou zapsana rozhodnuti z diskuze: "moje tickety" jsou tickety prirazene mne, v helpdesku ty, ktere jsem zalozil ja, a helpdeskove pozadavky vidi lide podle teze hierarchie jako tickety. Sekce 6 popisuje tuhle zmenu. Overeno na bezici instanci s vlastnim DATA_DIR: telo s data jako objektem projde, prvni zprava hovoru s data null projde, telo bez callSid se dal odmita, a vsechna tri jsou videt v seznamu poslednich volani. Co-Authored-By: Claude Opus 5 (1M context) --- documentation/25-navrh-pristupny-portal.md | 105 +++++++++++++----- documentation/99-zmeny.md | 38 +++++++ src/data/automationStore.ts | 51 ++++++++- src/data/conditions.ts | 19 +++- src/routes/webhook.ts | 26 ++++- .../dashboard/flow/TriggerConfig.tsx | 26 +++-- web/src/pages/dashboard/AutomationDetail.tsx | 54 +++++++++ web/src/types/dashboard.ts | 24 +++- 8 files changed, 302 insertions(+), 41 deletions(-) diff --git a/documentation/25-navrh-pristupny-portal.md b/documentation/25-navrh-pristupny-portal.md index 554db34..0e455c6 100644 --- a/documentation/25-navrh-pristupny-portal.md +++ b/documentation/25-navrh-pristupny-portal.md @@ -1,8 +1,9 @@ # 25 - Navrh: pristupny portal a viditelnost -**Navrh, ne popis stavu. Nic z toho zatim neni naprogramovane.** Az se cast -udela, prepise se do prislusneho souboru dokumentace a odsud zmizi. Stejne -pravidlo jako u [09-navrh-rozsireni.md](09-navrh-rozsireni.md). +**Navrh, ne popis stavu.** Jedina hotova cast je sekce 6, ostatni zatim +naprogramovane neni. Az se cast udela, prepise se do prislusneho souboru +dokumentace a odsud zmizi. Stejne pravidlo jako +u [09-navrh-rozsireni.md](09-navrh-rozsireni.md). Popis soucasneho stavu je v [01-prehled-a-stav.md](01-prehled-a-stav.md), prava a pohledy v [07-firmy-a-prava.md](07-firmy-a-prava.md), widgety @@ -390,6 +391,19 @@ jinak -> moje tickety Posledni radka je rozhodnuti, ne odvozeni: bez ni nema vedouci co rozdelovat, protoze neprirazeny ticket nepatri nikomu. +**"Moje" znamena prirazene mne.** Nic vic: ne zalozene mnou, ne komentovane +mnou. Ticket, ktery clovek zapsal po telefonu a predal dal, uz neni jeho - +prave o tom predani slo. + +V **helpdesku** je to naopak: tam jsou "moje" ty pozadavky, ktere jsem **zalozil +ja**. Neni to vyjimka z pravidla, je to tentyz pohled z druhe strany. Zadavatel +pozadavek nikdy nema prirazeny, protoze ho resi nekdo jiny, a helpdeskove +pozadavky se automaticky vazou na hlavni firmu. + +Kdo pozadavky v helpdesku vidi, se ridi **toutez hierarchii** jako u ticketu: +firemni priznak, pak skupiny, jinak svoje. Helpdesk tedy nema vlastni pravidlo +viditelnosti, jen jinou definici toho, co je "moje". + ### Past: dve identity Projekt ma **uzivatele** (kdo se prihlasi) a **resitele** (na koho jde ticket), @@ -474,6 +488,58 @@ nem neda nic overit do hloubky. --- +## 6 - Kdyz kontrakt nesedi na data (hotovo) + +Jedina cast, ktera uz naprogramovana **je**. Je tady proto, ze patri ke stejne +tride problemu jako zbytek: konfigurace je formalne v poradku a pritom nesedi na +skutecna data. + +### Jak to vypadalo + +Parametr spoustece `data` byl deklarovany jako `type: string, required: true`, +zatimco odesilatel ho posila jako objekt a v prvni zprave hovoru ho jeste nema. +Kazde volani proto skoncilo na 400 a automatizace hodinu nedelala nic. + +Za tim byly tri veci a kazda z nich sama o sobe malicherna: + +1. **Rucne pridany parametr byl vychozi povinny**, zatimco parametr odvozeny + z ukazkoveho tela nepovinny. Dve ruzna vychozi nastaveni pro tutez vec + v jednom formulari. +2. **Objekt a seznam neslo vybrat.** `declarableFieldTypes` je nabizel jen + `string`, `number`, `boolean` a `date`, a klient mel jeste svoji treti kopii + toho seznamu. Deklarovat `data` jako objekt tedy neslo, i kdyz `matchesType` + objekt umi a `operatorsByType` pro nej ma operatory. +3. **Odmitnuti nebylo nikde videt.** Skoncilo jako `console.warn` v logu + kontejneru: zadna udalost, zadny beh, nic na detailu automatizace. + +Ten treti bod je ten podstatny. Chybu v kontraktu udela ten, kdo ho psal, ale +**400 dostane odesilatel** - a ten s tim nic nenadela, casto je to cizi sluzba, +ktera volani neopakuje. Majitel automatizace se nedozvi nic a v portalu vypada +vsechno v poradku. + +### Co se zmenilo + +- **Novy parametr je nepovinny.** Povinny znamena "odmitni volani" a do toho + nema nikdo spadnout omylem. Zaskrtnout to jde vzdycky. +- **Objekt a seznam jdou vybrat.** `declarableFieldTypes` je ma a klient si uz + nedrzi vlastni kopii, bere sdileny seznam. +- **Poslednich deset volani je videt na detailu automatizace.** Cas, jestli + proslo nebo ne, a u odmitnutych duvod. + +Telo se **schvalne neuklada**. Duvod odmitnuti uz rika, co je spatne ("Parametr +data ma mit typ string"), a drzet payloady by znamenalo mit v pameti kopie +zakaznickych dat, aniz by o tom kdokoliv vedel. Seznam volani se drzi v pameti, +restart ho zahodi - je to diagnostika posledni hodiny, ne historie. + +### Co se tim nedela + +Nezaklada se incident, neposila se upozorneni. Staci radek: kdyz se +implementator ozve, ze mu neco nesedi, je tady videt co. Rozsirit se to da +pozdeji, ale kazda dalsi vrstva stoji za to az ve chvili, kdy tenhle seznam +prestane stacit. + +--- + ## Poradi praci 1. **Formularove primitivy** a rozdeleni dvou formularu (ticket, helpdesk). Nic @@ -500,34 +566,19 @@ Postgres kdykoliv mezi tim, nezavisle na ostatnim. - **Strankovani seznamu ticketu** zmeni chovani dnesniho klientskeho hledani. Musi jit ruku v ruce se serverovym hledanim, ne pred nim. +## Rozhodnuto + +- **"Moje tickety" jsou tickety prirazene mne.** V helpdesku ty, ktere jsem + zalozil ja. Podrobnosti v sekci 4, cast "Vypocet stropu". +- **Helpdeskove pozadavky vidi lide podle teze hierarchie** jako tickety, tedy + firemni priznak, pak skupiny, jinak svoje. +- **Odmitnuta volani webhooku maji byt videt na automatizaci**, staci radek + s casem a duvodem. Uz je to hotove, viz sekce 6. + ## Otevrene otazky U kazde jde o rozhodnuti, ktere se z modelu neda odvodit. -### A - Co znamena "moje tickety" pro strop - -Tri odpovedi, lisi se v tom, kdy clovek o ticket prijde z dohledu: - -| Varianta | Dusledek | -| ---------------------------------- | --------------------------------------------------------------- | -| jen prirazene mne | pracovnik zalozi ticket po telefonu, preda ho a hned o nem nevi | -| prirazene plus zalozene mnou | vidi i to, co poslal dal | -| prirazene, zalozene i komentovane | vidi vse, ceho se dotkl | - -Ticket dnes nenese, kdo ho zalozil rucne (`automationId` je jen u automatickych), -takze druha a treti varianta znamenaji nove pole na ticketu. - -### B - Helpdesk a strop - -Firma A posle pozadavek firme B. Zadavatel u firmy A ho vidi pres -`helpdeskSourceId`, i kdyz ticket vlastni firma B. Zadavatel neni resitel a neni -v zadne skupine, takze strop na nej nesedi. - -Nabizi se, ze helpdeskovy pohled ma vlastni pravidlo ("vidim, co moje firma -poslala") a strop se na nej nevztahuje. Otazka je, **kdo z firmy A to vidi**: -kazdy clen firmy, nebo jen ten, kdo pozadavek poslal? U firmy o peti lidech je -odpoved jina nez u firmy o padesati. - ### C - Hledani v udalostech Udalosti nesou cele prijate telo webhooku. Hledat v nem znamena hledat v datech, diff --git a/documentation/99-zmeny.md b/documentation/99-zmeny.md index 39f494b..c163602 100644 --- a/documentation/99-zmeny.md +++ b/documentation/99-zmeny.md @@ -2,6 +2,44 @@ Nejnovejsi nahore. +## 2026-09-02 - Kontrakt webhooku: objekt jde vybrat a odmitnuti je videt + +Parametr spoustece `data` byl deklarovany jako `type: string, required: true`, +zatimco odesilatel ho posila jako objekt a v prvni zprave hovoru ho jeste nema. +Kazde volani proto skoncilo na 400 a automatizace hodinu nedelala nic. + +Za tim byly tri veci, kazda sama o sobe malicherna: + +- **Rucne pridany parametr byl vychozi povinny**, zatimco parametr odvozeny + z ukazkoveho tela nepovinny. Dve ruzna vychozi nastaveni pro tutez vec + v jednom formulari. Nove je nepovinny i rucne pridany: povinny znamena + "odmitni volani" a do toho nema nikdo spadnout omylem. +- **Objekt a seznam neslo vybrat.** `declarableFieldTypes` nabizel jen `string`, + `number`, `boolean` a `date`, a `TriggerConfig.tsx` mel jeste treti kopii toho + seznamu. Deklarovat `data` jako objekt tedy neslo, i kdyz `matchesType` objekt + umi a `operatorsByType` pro nej ma operatory. Ted jsou v nabidce oba a klient + si vlastni kopii nedrzi. +- **Odmitnuti nebylo nikde videt.** Skoncilo jako `console.warn` v logu + kontejneru: zadna udalost, zadny beh, nic na detailu automatizace. + +Ten treti bod je ten podstatny. Chybu v kontraktu udela ten, kdo ho psal, ale +**400 dostane odesilatel** - a ten s tim nic nenadela, casto je to cizi sluzba, +ktera volani neopakuje. Majitel automatizace se nedozvi nic a v portalu vypada +vsechno v poradku. + +Detail automatizace proto ukazuje **poslednich deset volani**: cas, jestli +proslo nebo ne, a u odmitnutych duvod. Telo se schvalne neuklada, duvod uz rika, +co je spatne, a drzet payloady by znamenalo mit v pameti kopie zakaznickych dat. +Seznam je v pameti, restart ho zahodi - je to diagnostika posledni hodiny, ne +historie. Incident se z toho nezaklada a upozorneni se neposila: staci radek, +protoze implementator se ozve sam a tady je videt co. + +Vzorova automatizace ma `data` opravene na `type: 'object', required: false`. + +Overeno na bezici instanci s vlastnim DATA_DIR: telo s `data` jako objektem +projde, prvni zprava hovoru s `data: null` projde, telo bez `callSid` se dal +odmita - a vsechna tri jsou videt v seznamu poslednich volani. + ## 2026-09-02 - Navrh: pristupny portal a viditelnost Novy [25-navrh-pristupny-portal.md](25-navrh-pristupny-portal.md). Je to navrh, diff --git a/src/data/automationStore.ts b/src/data/automationStore.ts index 855d662..5a47ba8 100644 --- a/src/data/automationStore.ts +++ b/src/data/automationStore.ts @@ -170,8 +170,54 @@ export interface Automation { issues: string[]; } +/** + * Jedno volani webhooku, jak dopadlo. + * + * Odmitnute volani do ted skoncilo jako radek v logu kontejneru, kam se nikdo + * nedostane. Odesilatel dostal 400 a vedel o tom, ale ten, kdo kontrakt napsal, + * se nedozvedel nic - automatizace svitila zelene a jen do ni nic nechodilo. + * + * Telo se **schvalne neuklada**. Duvod odmitnuti uz rika, co je spatne + * ("Parametr data ma mit typ string"), a drzet payloady by znamenalo mit + * v pameti kopie zakaznickych dat bez toho, aby o tom kdokoliv vedel. + */ +export interface WebhookCall { + at: string; + /** true = prevzato do fronty, false = odmitnuto pro neplatna data. */ + ok: boolean; + /** Duvody odmitnuti. Prazdne u prevzatych volani. */ + problems: string[]; + /** Beh, ktery z volani vznikl. null u odmitnutych a u duplicit. */ + runId: string | null; +} + +/** Kolik poslednich volani se u automatizace drzi. */ +const MAX_CALLS = 10; + +const calls = new Map(); + +/** Zapise, jak dopadlo jedno volani webhooku. Nejnovejsi je prvni. */ +export function recordWebhookCall(automationId: string, call: WebhookCall): void { + const list = calls.get(automationId) ?? []; + list.unshift(call); + if (list.length > MAX_CALLS) list.length = MAX_CALLS; + calls.set(automationId, list); +} + +/** Poslednich par volani. Cte se jen pres detail automatizace, ktery hlida firmu. */ +export function recentWebhookCalls(automationId: string): WebhookCall[] { + return calls.get(automationId) ?? []; +} + export interface AutomationDetail extends Automation { flow: AutomationFlow; + /** + * Poslednich `MAX_CALLS` volani webhooku, nejnovejsi prvni. + * + * Drzi se v pameti, restart je zahodi. Je to diagnostika posledni hodiny, + * ne historie - na tu jsou behy. + */ + recentCalls: WebhookCall[]; /** * Model prichozich dat odvozeny z ukazky u spoustece. * @@ -1001,7 +1047,9 @@ function seedRealAutomations(): void { { id: 'f_voicebot', name: 'voicebotId', type: 'string', required: true }, { id: 'f_mtjqv4qj_1', name: 'result', type: 'string', required: false, path: 'data.result' }, { id: 'f_mtjqv4zn_2', name: 'rating', type: 'string', required: false, path: 'data.rating' }, - { id: 'f_mtjqvws7_3', name: 'data', type: 'string', required: true, path: 'data' }, + // Objekt a nepovinne: telo ho posila jako strukturu a prvni zprava + // hovoru ho jeste nema. Deklarace `string` a povinny odmitala oboji. + { id: 'f_mtjqvws7_3', name: 'data', type: 'object', required: false, path: 'data' }, ], webhookToken: config.seedWebhookToken || generateWebhookToken(), }, @@ -1101,6 +1149,7 @@ function toDetail(stored: StoredAutomation): AutomationDetail { model: stored.flow.trigger?.sample === undefined ? [] : describeModel(stored.flow.trigger.sample), + recentCalls: recentWebhookCalls(stored.id), createdAt: stored.createdAt, updatedAt: stored.updatedAt, }; diff --git a/src/data/conditions.ts b/src/data/conditions.ts index c21eb01..a87d379 100644 --- a/src/data/conditions.ts +++ b/src/data/conditions.ts @@ -35,8 +35,23 @@ export const fieldTypes: FieldType[] = [ 'list', ]; -/** Typy, ktere si uzivatel muze zvolit u vlastniho parametru spoustece. */ -export const declarableFieldTypes: FieldType[] = ['string', 'number', 'boolean', 'date']; +/** + * Typy, ktere si uzivatel muze zvolit u vlastniho parametru spoustece. + * + * Objekt a seznam tady driv nebyly, protoze podminka se nad nimi zeptat skoro + * nema na co. Jenze parametr neni jen podklad pro podminku: kdyz odesilatel + * posle `data` jako objekt, musi jit deklarovat objekt, jinak ho kontrakt + * odmitne. Bez toho zbyval jediny "spravny" postup - nedeklarovat ho vubec + * a sahat na nej cestou, coz nikoho nenapadne. + */ +export const declarableFieldTypes: FieldType[] = [ + 'string', + 'number', + 'boolean', + 'date', + 'object', + 'list', +]; /** Ktere operatory maji smysl pro ktery typ. */ export const operatorsByType: Record = { diff --git a/src/routes/webhook.ts b/src/routes/webhook.ts index 415380b..84f1d6a 100644 --- a/src/routes/webhook.ts +++ b/src/routes/webhook.ts @@ -1,6 +1,10 @@ import { Router } from 'express'; import { z } from 'zod'; -import { findByWebhookToken, type TriggerField } from '../data/automationStore.js'; +import { + findByWebhookToken, + recordWebhookCall, + type TriggerField, +} from '../data/automationStore.js'; import type { FieldType } from '../data/conditions.js'; import { findByIntakeToken } from '../data/tenants.js'; import { intakeEvent } from '../data/ticketStore.js'; @@ -191,6 +195,18 @@ webhookRouter.post('/:token', (req, res) => { if (problems.length > 0) { console.warn(`[webhook] ${automation.id}: neplatna data - ${problems.join(' ')}`); + /* + * Odmitnuti musi byt videt v portalu, ne jen v logu kontejneru. Chybu + * v kontraktu udela ten, kdo ho psal, ale 400 dostane odesilatel - a ten + * s tim nic nenadela. Bez tohohle zaznamu se majitel automatizace nedozvi, + * ze uz hodinu nic nechodi, protoze v portalu vypada vsechno v poradku. + */ + recordWebhookCall(automation.id, { + at: new Date().toISOString(), + ok: false, + problems, + runId: null, + }); return res.status(400).json({ error: 'validation_error', message: problems[0], @@ -224,6 +240,14 @@ webhookRouter.post('/:token', (req, res) => { console.info( `[webhook] ${automation.id}: prijato, zarazeno jako ${item?.id ?? '(duplicita)'}`, ); + // I prevzata volani, aby v prehledu bylo videt "tohle proslo, tohle ne" + // a ne jen seznam chyb bez meritka. + recordWebhookCall(automation.id, { + at: new Date().toISOString(), + ok: true, + problems: [], + runId: item?.id ?? null, + }); // 202: prevzato, zpracuje se. Ne 200, ktera by rikala "hotovo". return res.status(202).json({ accepted: true, diff --git a/web/src/components/dashboard/flow/TriggerConfig.tsx b/web/src/components/dashboard/flow/TriggerConfig.tsx index 435ca96..3fe17fe 100644 --- a/web/src/components/dashboard/flow/TriggerConfig.tsx +++ b/web/src/components/dashboard/flow/TriggerConfig.tsx @@ -12,15 +12,19 @@ import { useState } from 'react'; import { Badge } from '@/components/ui/Badge'; import { cn } from '@/lib/cn'; import { fieldTypeLabels, newFieldId } from '@/lib/flow'; -import type { - Service, - FieldType, - FlowTrigger, - ModelNode, - TriggerField, +import { + declarableFieldTypes, + type Service, + type FieldType, + type FlowTrigger, + type ModelNode, + type TriggerField, } from '@/types/dashboard'; -const fieldTypes: FieldType[] = ['string', 'number', 'boolean', 'date']; +// Nabidka typu se bere ze sdileneho seznamu. Driv tu byla vlastni kopie a ta +// se rozesla: objekt a seznam v ni chybely, takze `data` sla deklarovat jen +// jako text a kontrakt pak odmital kazde volani. +const fieldTypes: readonly FieldType[] = declarableFieldTypes; /** * Nastaveni spoustece: registrovana adresa webhooku a deklarace parametru, @@ -101,8 +105,14 @@ function CustomFields({ editable: boolean; onChange: (fields: TriggerField[]) => void; }) { + /* + * Novy parametr je **nepovinny**. Povinny znamena "odmitni volani", a do toho + * nema nikdo spadnout omylem - zaskrtnout to jde vzdycky. Odvozeni parametru + * z ukazkoveho tela to tak delalo uz driv, rucni pridani ne, a ta nesrovnalost + * stala jedno odpoledne hledani, proc webhook prestal chodit. + */ function addField() { - onChange([...fields, { id: newFieldId(), name: '', type: 'string', required: true }]); + onChange([...fields, { id: newFieldId(), name: '', type: 'string', required: false }]); } function updateField(id: string, patch: Partial) { diff --git a/web/src/pages/dashboard/AutomationDetail.tsx b/web/src/pages/dashboard/AutomationDetail.tsx index b7d11ae..e43669a 100644 --- a/web/src/pages/dashboard/AutomationDetail.tsx +++ b/web/src/pages/dashboard/AutomationDetail.tsx @@ -28,6 +28,7 @@ import type { AutomationFlow, ServiceCatalog, TriggerField, + WebhookCall, } from '@/types/dashboard'; /** Kam se ma vlozit dalsi krok - null znamena, ze vyber neni otevreny. */ @@ -442,6 +443,10 @@ export default function AutomationDetail() { )} + {flow.trigger?.serviceId === 'webhook' && ( + + )} +

Prototyp: nastavení jednotlivých polí kroku (mapování dat, filtry) zatím @@ -467,6 +472,55 @@ export default function AutomationDetail() { ); } +/** + * Poslednich deset volani webhooku. + * + * Odmitnute volani do ted skoncilo jako radek v logu kontejneru. Odesilatel + * dostal 400 a vedel o tom, ale ten, kdo kontrakt psal, se nedozvedel nic - + * automatizace svitila zelene a jen do ni prestalo chodit. Tohle je to nejmensi, + * co staci: kdyz se implementator ozve, ze mu neco nesedi, je tady videt co. + * + * Telo se neuklada, jen duvod odmitnuti. Ten uz rika, co je spatne. + */ +function WebhookCalls({ calls }: { calls: WebhookCall[] }) { + return ( +

+

Poslední volání

+ + {calls.length === 0 ? ( +

+ Zatím nic nepřišlo. Objeví se tu deset posledních volání včetně těch, + která jsme odmítli. +

+ ) : ( +
    + {calls.map((call) => ( +
  • + {call.ok ? ( + + ) : ( + + )} +
    + {formatDateTime(call.at)} + {call.problems.length > 0 && ( +

    + {call.problems.join(' ')} +

    + )} +
    +
  • + ))} +
+ )} + +

+ Drží se v paměti, restart je zahodí. Na historii jsou běhy. +

+
+ ); +} + function Row({ label, value }: { label: string; value: string }) { return (
diff --git a/web/src/types/dashboard.ts b/web/src/types/dashboard.ts index 61556da..8c823f1 100644 --- a/web/src/types/dashboard.ts +++ b/web/src/types/dashboard.ts @@ -506,8 +506,18 @@ export interface ServiceCatalog { */ export type FieldType = 'string' | 'number' | 'boolean' | 'date' | 'object' | 'list'; -/** Typy, ktere si uzivatel muze zvolit u vlastniho parametru spoustece. */ -export const declarableFieldTypes = ['string', 'number', 'boolean', 'date'] as const; +/** + * Typy, ktere si uzivatel muze zvolit u vlastniho parametru spoustece. + * Objekt a seznam jsou mezi nimi kvuli telu, kde `data` prijde jako objekt. + */ +export const declarableFieldTypes = [ + 'string', + 'number', + 'boolean', + 'date', + 'object', + 'list', +] as const; export type ConditionOperator = | 'eq' @@ -599,10 +609,20 @@ export interface AutomationFlow { steps: FlowStep[]; } +/** Jedno volani webhooku, jak dopadlo. Telo se neuklada, jen duvod odmitnuti. */ +export interface WebhookCall { + at: string; + ok: boolean; + problems: string[]; + runId: string | null; +} + export interface AutomationDetail extends Automation { flow: AutomationFlow; /** Model prichozich dat z ukazky u spoustece. Pocita server pri cteni. */ model: ModelNode[]; + /** Poslednich deset volani webhooku, nejnovejsi prvni. Drzi se v pameti. */ + recentCalls: WebhookCall[]; createdAt: string; updatedAt: string; }