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
+306
@@ -26,6 +26,7 @@ export function buildOpenApiDocument() {
|
||||
{ name: 'Dashboard', description: 'Data klientskeho portalu' },
|
||||
{ name: 'Tickety', description: 'Pozadavky, jejich resitele a log prubehu' },
|
||||
{ name: 'Automatizace', description: 'Sprava automatizaci a stromu akci' },
|
||||
{ name: 'Skripty', description: 'Vykonna cast konektoru: manifest, kod a zkusebni beh' },
|
||||
{ name: 'Simulace', description: 'Vyvolani provoznich udalosti pro nahled' },
|
||||
{ name: 'Webhook', description: 'Verejny prijem dat do automatizace' },
|
||||
{ name: 'Kontakt', description: 'Poptavkovy formular z webu' },
|
||||
@@ -98,6 +99,145 @@ export function buildOpenApiDocument() {
|
||||
personId: { type: 'string', nullable: true },
|
||||
},
|
||||
},
|
||||
ScriptField: {
|
||||
type: 'object',
|
||||
description:
|
||||
'Parametr skriptu. Stejny tvar pro vstup i vystup - kontrola je pak ' +
|
||||
'jedna funkce, ne dve skoro stejne.',
|
||||
required: ['id', 'label', 'type', 'required'],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
example: 'invoiceId',
|
||||
description: 'Pouziva se v sablonach jako {{invoiceId}}.',
|
||||
},
|
||||
label: { type: 'string', example: 'ID faktury v iDokladu' },
|
||||
type: { type: 'string', enum: ['string', 'number', 'boolean', 'date'] },
|
||||
required: { type: 'boolean' },
|
||||
hint: { type: 'string' },
|
||||
options: {
|
||||
type: 'array',
|
||||
description: 'Vyber z hodnot. Jina hodnota neprojde kontrolou.',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: { value: { type: 'string' }, label: { type: 'string' } },
|
||||
},
|
||||
},
|
||||
pattern: { type: 'string', description: 'Jen u typu string.' },
|
||||
multiline: { type: 'boolean', description: 'Jen u typu string.' },
|
||||
default: { description: 'Dosadi se, kdyz hodnota chybi a parametr neni povinny.' },
|
||||
},
|
||||
},
|
||||
ScriptManifest: {
|
||||
type: 'object',
|
||||
description: 'Co skript umi. Podle nej s nim umi pracovat strom automatizace.',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
example: 'idoklad.get-issued-invoice',
|
||||
description: 'Tvar konektor.operace. Nazev souboru musi byt <id>.js.',
|
||||
},
|
||||
connectorId: { type: 'string', example: 'idoklad' },
|
||||
operationId: { type: 'string', example: 'get-issued-invoice' },
|
||||
name: { type: 'string', example: 'Získat vydanou fakturu' },
|
||||
description: { type: 'string' },
|
||||
inputs: { type: 'array', items: { $ref: '#/components/schemas/ScriptField' } },
|
||||
outputs: { type: 'array', items: { $ref: '#/components/schemas/ScriptField' } },
|
||||
timeoutMs: { type: 'integer', example: 15000 },
|
||||
},
|
||||
},
|
||||
ScriptProblem: {
|
||||
type: 'object',
|
||||
description:
|
||||
'Rozbity skript. Nesmi shodit ostatni ani tise zmizet, proto se vraci sem.',
|
||||
properties: {
|
||||
file: { type: 'string', example: 'idoklad.get-issued-invoice.js' },
|
||||
scriptId: { type: 'string', nullable: true },
|
||||
message: { type: 'string' },
|
||||
issues: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: { field: { type: 'string' }, message: { type: 'string' } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ConnectionStatus: {
|
||||
type: 'object',
|
||||
description:
|
||||
'Stav napojeni konektoru. Hodnoty pristupovych udaju se nevraci nikdy, ' +
|
||||
'jen jmena promennych, ktere chybi.',
|
||||
properties: {
|
||||
connectorId: { type: 'string', example: 'idoklad' },
|
||||
baseUrl: { type: 'string', example: 'https://services.csbot.cz/apps/idoklad' },
|
||||
ready: { type: 'boolean' },
|
||||
missing: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
example: ['IDOKLAD_CLIENT_SECRET'],
|
||||
},
|
||||
headers: { type: 'array', items: { type: 'string' }, example: ['X-ClientId'] },
|
||||
},
|
||||
},
|
||||
ScriptRunResult: {
|
||||
type: 'object',
|
||||
description:
|
||||
'Vysledek behu skriptu. `retryable` rika, jestli ma smysl zkusit to znovu - ' +
|
||||
'timeout ano, spatny vstup ne.',
|
||||
properties: {
|
||||
ok: { type: 'boolean' },
|
||||
scriptId: { type: 'string' },
|
||||
outputs: {
|
||||
type: 'object',
|
||||
additionalProperties: true,
|
||||
description: 'Prazdne, kdyz beh selhal.',
|
||||
},
|
||||
logs: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
at: { type: 'string', format: 'date-time' },
|
||||
message: { type: 'string' },
|
||||
detail: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
durationMs: { type: 'integer' },
|
||||
httpCalls: { type: 'integer' },
|
||||
error: {
|
||||
type: 'object',
|
||||
nullable: true,
|
||||
properties: {
|
||||
kind: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'not_found',
|
||||
'config',
|
||||
'validation',
|
||||
'output',
|
||||
'retryable',
|
||||
'terminal',
|
||||
'timeout',
|
||||
'internal',
|
||||
],
|
||||
},
|
||||
message: { type: 'string' },
|
||||
retryable: { type: 'boolean' },
|
||||
status: { type: 'integer' },
|
||||
detail: { type: 'string' },
|
||||
issues: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: { field: { type: 'string' }, message: { type: 'string' } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
LoginRequest: {
|
||||
type: 'object',
|
||||
required: ['email', 'password'],
|
||||
@@ -811,10 +951,176 @@ export function buildOpenApiDocument() {
|
||||
get: {
|
||||
tags: ['Automatizace'],
|
||||
summary: 'Katalog konektoru',
|
||||
description:
|
||||
'Operace, ktere obsluhuje skript, nesou `implementation: script` a `scriptId`, ' +
|
||||
'a maji skutecne `inputs` a `outputFields` z manifestu toho skriptu.',
|
||||
security: [{ bearerAuth: [] }],
|
||||
responses: { '200': { description: 'Konektory, kategorie a operatory podminek' } },
|
||||
},
|
||||
},
|
||||
'/api/dashboard/scripts': {
|
||||
get: {
|
||||
tags: ['Skripty'],
|
||||
summary: 'Seznam skriptu konektoru',
|
||||
description:
|
||||
'Manifesty vsech nactenych skriptu, rozbite skripty v `problems` ' +
|
||||
'a stav napojeni v `connections`. Pristupove udaje se nikdy nevraci, ' +
|
||||
'jen jmena chybejicich environment variables.',
|
||||
security: [{ bearerAuth: [] }],
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'Skripty, problemy a stav napojeni',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
items: {
|
||||
type: 'array',
|
||||
items: { $ref: '#/components/schemas/ScriptManifest' },
|
||||
},
|
||||
problems: {
|
||||
type: 'array',
|
||||
items: { $ref: '#/components/schemas/ScriptProblem' },
|
||||
},
|
||||
connections: {
|
||||
type: 'array',
|
||||
items: { $ref: '#/components/schemas/ConnectionStatus' },
|
||||
},
|
||||
directory: { type: 'string', example: '/app/scripts' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'/api/dashboard/scripts/reload': {
|
||||
post: {
|
||||
tags: ['Skripty'],
|
||||
summary: 'Znovu nacist skripty ze slozky',
|
||||
description:
|
||||
'Skripty se nacitaji samy podle casu zmeny souboru. Tenhle endpoint ' +
|
||||
'to jen vynuti hned, bez cekani.',
|
||||
security: [{ bearerAuth: [] }],
|
||||
responses: {
|
||||
'200': { description: 'Skripty po nacteni' },
|
||||
'403': { description: 'Jen spravce platformy' },
|
||||
},
|
||||
},
|
||||
},
|
||||
'/api/dashboard/scripts/{id}': {
|
||||
get: {
|
||||
tags: ['Skripty'],
|
||||
summary: 'Manifest a kod skriptu',
|
||||
security: [{ bearerAuth: [] }],
|
||||
parameters: [
|
||||
{
|
||||
name: 'id',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
example: 'idoklad.get-issued-invoice',
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'Kod se vraci vzdy. `manifest` je null, kdyz je skript rozbity.',
|
||||
},
|
||||
'400': { description: 'Neplatne ID skriptu' },
|
||||
'404': { description: 'Skript neexistuje' },
|
||||
},
|
||||
},
|
||||
put: {
|
||||
tags: ['Skripty'],
|
||||
summary: 'Ulozit kod skriptu',
|
||||
description:
|
||||
'Nejdriv se kod nacte a overi, az pak prepise soubor. Rozbita uprava ' +
|
||||
'se neulozi a puvodni skript dal funguje.',
|
||||
security: [{ bearerAuth: [] }],
|
||||
parameters: [
|
||||
{
|
||||
name: 'id',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
example: 'idoklad.get-issued-invoice',
|
||||
},
|
||||
],
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['code'],
|
||||
properties: {
|
||||
code: {
|
||||
type: 'string',
|
||||
description: 'Cely obsah souboru vcetne exportu manifest a run.',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
'200': { description: 'Ulozeno, vraci se overeny manifest' },
|
||||
'400': {
|
||||
description: 'Kod nebo manifest neprosel, v `issues` je co opravit',
|
||||
content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } },
|
||||
},
|
||||
'403': { description: 'Jen spravce platformy' },
|
||||
},
|
||||
},
|
||||
},
|
||||
'/api/dashboard/scripts/{id}/test': {
|
||||
post: {
|
||||
tags: ['Skripty'],
|
||||
summary: 'Zkusebni spusteni skriptu',
|
||||
description:
|
||||
'POZOR: vola opravdovou sluzbu. Vystavena faktura opravdu vznikne. ' +
|
||||
'Chyba skriptu neni chyba API, vraci se 200 s popisem v `error`.',
|
||||
security: [{ bearerAuth: [] }],
|
||||
parameters: [
|
||||
{
|
||||
name: 'id',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
example: 'idoklad.get-issued-invoice',
|
||||
},
|
||||
],
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
inputs: {
|
||||
type: 'object',
|
||||
additionalProperties: true,
|
||||
example: { invoiceId: 12345 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'Vysledek behu',
|
||||
content: {
|
||||
'application/json': { schema: { $ref: '#/components/schemas/ScriptRunResult' } },
|
||||
},
|
||||
},
|
||||
'400': { description: 'Neplatne ID nebo vstupy' },
|
||||
'403': { description: 'Jen spravce platformy' },
|
||||
},
|
||||
},
|
||||
},
|
||||
'/api/dashboard/automations': {
|
||||
get: {
|
||||
tags: ['Automatizace'],
|
||||
|
||||
Reference in New Issue
Block a user