Skripty konektoru: vykonna cast s manifestem a kontrolou parametru
Konektory dostaly vykonnou cast. Jeden skript je jeden soubor, ktery nese manifest (vstupni a vystupni parametry) i kod. Diky manifestu s nim umi pracovat strom automatizace, aniz by o kodu cokoliv vedel. Soubory jsou zamerne obycejny JavaScript, ne TypeScript. TypeScript by se musel prelozit a to je presne to otaceni, ktere tady nema byt. Registr sleduje cas zmeny souboru, takze uprava v portalu, rucni uprava souboru i novy soubor ve slozce funguji stejne a bez restartu. Pridano: - scripts/ se skripty konektoru, nazev souboru je zaroven ID operace - kontrola vstupu i vystupu proti manifestu, jedna funkce pro obe strany. Chybejici povinny vystup je chyba skriptu, ne uzivatele - jinak by strom veril parametru, ktery nikdy nedosel - ctx predavany skriptu: http nad adresou napojeni, util, log, config, idempotencyKey, fail a retry. Skript nedostane pristupove udaje - rozliseni opakovatelne a koncove chyby. Runner nikdy nevyhodi vyjimku, vzdy vraci vysledek vcetne retryable - redakce tajnych hodnot pred zapisem do logu. Cizi API rado vraci prijaty token v chybove zprave a log ticketu vidi klient - napojeni z environment variables vcetne iDokladu - sest ukazkovych skriptu pro iDoklad proti skutecnemu API sluzby services.csbot.cz/apps/idoklad, kazdy na jiny vzor - stranka /dashboard/skripty: seznam, manifest, editor, zkusebni spusteni. Formular testu se sklada z manifestu, nepise se pro kazdy skript - endpointy /api/dashboard/scripts vcetne Swaggeru Zmeneno: - katalog konektoru uz neni jen staticky seznam. Akce ze skriptu se domeruji prekryvem v src/data/connectors.ts, takze se naraz objevi ve validaci stromu, ve vypoctu scope i v sablonach. Pri stejnem ID vyhrava skript - ConnectorOperation ma implementation a scriptId - ApiError na klientovi nese cele telo odpovedi a umi z nej vytahnout issues - Dockerfile kopiruje scripts/ do vysledneho image Ukladani nemuze rozbit fungujici skript: kod se nejdriv zapise do docasneho souboru, ten se nacte a overi, a az pak prepise puvodni. K tomu tri dokumenty navrhu dalsich kroku: 09 datove modely a prava, 10 runtime a rozpocet na 150 klientu, 11 popis skriptu konektoru. Overeno: npm run typecheck prochazi na serveru i webu. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
bbc2236c0d
commit
6f6b287d7e
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* iDoklad: dohledani kontaktu podle ICO nebo e-mailu.
|
||||
*
|
||||
* Sluzba: https://services.csbot.cz/apps/idoklad
|
||||
* Endpoint: GET /contacts?filter=(IdentificationNumber~eq~12345678)
|
||||
*
|
||||
* Vzor **vlastni kontroly vstupu**. Manifest umi rict "tohle pole je povinne",
|
||||
* ale ne "aspon jedno z dvojice". Takova pravidla patri do kodu, protoze jen
|
||||
* tam jde napsat citelny duvod.
|
||||
*/
|
||||
|
||||
export const manifest = {
|
||||
id: 'idoklad.find-contact',
|
||||
name: 'Najít kontakt',
|
||||
description:
|
||||
'Dohledá odběratele v iDokladu podle IČO nebo e-mailu. Nic nezakládá. ' +
|
||||
'Výsledek se použije jako ID odběratele při vystavení faktury.',
|
||||
|
||||
inputs: [
|
||||
{
|
||||
id: 'identificationNumber',
|
||||
label: 'IČO',
|
||||
type: 'string',
|
||||
required: false,
|
||||
pattern: '^[0-9]{6,12}$',
|
||||
hint: 'Jen číslice. Přesnější než e-mail, hledá se podle něj první.',
|
||||
},
|
||||
{
|
||||
id: 'email',
|
||||
label: 'E-mail',
|
||||
type: 'string',
|
||||
required: false,
|
||||
hint: 'Použije se, když IČO není k dispozici.',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: [
|
||||
{ id: 'found', label: 'Kontakt nalezen', type: 'boolean', required: true },
|
||||
{ id: 'contactId', label: 'ID kontaktu', type: 'number', required: false },
|
||||
{ id: 'companyName', label: 'Název firmy', type: 'string', required: false },
|
||||
{ id: 'identificationNumber', label: 'IČO', type: 'string', required: false },
|
||||
{ id: 'email', label: 'E-mail', type: 'string', required: false },
|
||||
{ id: 'matchedBy', label: 'Podle čeho se našel', type: 'string', required: true },
|
||||
],
|
||||
};
|
||||
|
||||
const notFound = {
|
||||
found: false,
|
||||
contactId: null,
|
||||
companyName: null,
|
||||
identificationNumber: null,
|
||||
email: null,
|
||||
matchedBy: 'nenalezeno',
|
||||
};
|
||||
|
||||
export async function run(inputs, ctx) {
|
||||
const { unwrap, pick, text, num } = ctx.util;
|
||||
|
||||
if (!inputs.identificationNumber && !inputs.email) {
|
||||
ctx.fail('Vyplňte IČO nebo e-mail, jinak není podle čeho hledat.');
|
||||
}
|
||||
|
||||
/** Jedno hledani podle jednoho pole. Vraci kontakt, nebo null. */
|
||||
async function search(field, value) {
|
||||
const { body } = await ctx.http.get('/contacts', {
|
||||
query: { filter: `(${field}~eq~${value})`, filtertype: 'and', pageSize: 1 },
|
||||
});
|
||||
const items = unwrap(body);
|
||||
return Array.isArray(items) && items.length > 0 ? items[0] : null;
|
||||
}
|
||||
|
||||
// Poradi je zamer: ICO je jednoznacne, e-mail muze mit vic firem stejny.
|
||||
const attempts = [];
|
||||
if (inputs.identificationNumber) {
|
||||
attempts.push(['IdentificationNumber', inputs.identificationNumber, 'IČO']);
|
||||
}
|
||||
if (inputs.email) attempts.push(['Email', inputs.email, 'e-mail']);
|
||||
|
||||
for (const [field, value, label] of attempts) {
|
||||
const contact = await search(field, value);
|
||||
if (!contact) {
|
||||
ctx.log(`Podle ${label} ${value} se nic nenašlo.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
return {
|
||||
found: true,
|
||||
contactId: num(pick(contact, 'id')),
|
||||
companyName: text(pick(contact, 'companyName', 'name')),
|
||||
identificationNumber: text(pick(contact, 'identificationNumber')),
|
||||
email: text(pick(contact, 'email')),
|
||||
matchedBy: label,
|
||||
};
|
||||
}
|
||||
|
||||
return notFound;
|
||||
}
|
||||
Reference in New Issue
Block a user