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) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
72da07debe
commit
e8bb4d6e54
@@ -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<TriggerField>) {
|
||||
|
||||
@@ -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() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{flow.trigger?.serviceId === 'webhook' && (
|
||||
<WebhookCalls calls={automation.data?.recentCalls ?? []} />
|
||||
)}
|
||||
|
||||
<p className="flex gap-2 rounded-xl border border-ink-600/60 px-4 py-3 text-xs leading-relaxed text-white/40">
|
||||
<Info className="mt-0.5 size-3.5 shrink-0" />
|
||||
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 (
|
||||
<div className="glass rounded-card p-5">
|
||||
<h2 className="font-semibold text-white">Poslední volání</h2>
|
||||
|
||||
{calls.length === 0 ? (
|
||||
<p className="mt-3 text-sm text-white/40">
|
||||
Zatím nic nepřišlo. Objeví se tu deset posledních volání včetně těch,
|
||||
která jsme odmítli.
|
||||
</p>
|
||||
) : (
|
||||
<ul className="mt-3 space-y-2 text-sm">
|
||||
{calls.map((call) => (
|
||||
<li key={`${call.at}-${call.runId ?? 'x'}`} className="flex gap-2">
|
||||
{call.ok ? (
|
||||
<Check className="mt-0.5 size-3.5 shrink-0 text-emerald-400" />
|
||||
) : (
|
||||
<AlertCircle className="mt-0.5 size-3.5 shrink-0 text-rose-400" />
|
||||
)}
|
||||
<div className="min-w-0">
|
||||
<span className="text-white/70">{formatDateTime(call.at)}</span>
|
||||
{call.problems.length > 0 && (
|
||||
<p className="text-xs leading-relaxed text-rose-300/80">
|
||||
{call.problems.join(' ')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
<p className="mt-3 text-xs text-white/35">
|
||||
Drží se v paměti, restart je zahodí. Na historii jsou běhy.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Row({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<div className="flex justify-between gap-3">
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user