Vai al contenuto principale

Panoramica

Le funzioni logiche sono funzioni TypeScript lato server che vengono eseguite sulla piattaforma Twenty. Possono essere attivate da richieste HTTP, pianificazioni cron o eventi del database — e possono anche essere esposte come strumenti per agenti di IA.
Ogni file di funzione usa defineLogicFunction() per esportare una configurazione con un handler e trigger opzionali.
src/logic-functions/createPostCard.logic-function.ts
Tipi di trigger disponibili:
  • httpRoute: Espone la tua funzione su un percorso e metodo HTTP. Nel codice dell’app, anteponi al percorso della route /s/ quando utilizzi RestApiClient; l’URL distribuito utilizza la base TWENTY_FUNCTIONS_URL iniettata (o \<server-url>/s quando non è impostata).
Per richiamare, da un componente front-end (headless), una funzione logica attivata da una rotta, vedi Chiamare una funzione logica.
  • cron: Esegue la tua funzione secondo una pianificazione utilizzando un’espressione CRON.
  • databaseEvent: Viene eseguito sugli eventi del ciclo di vita degli oggetti dello spazio di lavoro. Quando l’operazione dell’evento è updated, è possibile specificare campi specifici da monitorare nell’array updatedFields. Se lasciato non definito o vuoto, qualsiasi aggiornamento attiverà la funzione.
ad es. person.updated, *.created, company.*
  • serverRoute: espone una singola route HTTP con ambito di registrazione. Una funzione di resolver (dichiarata con serverRouteTriggerSettings) viene eseguita nel workspace proprietario e restituisce una Response sincrona, oppure il workspace di destinazione e la funzione logica di destinazione da mettere in coda; nel percorso di messa in coda la piattaforma conferma con 202 ed esegue tale destinazione nella coda dei worker. Vedi Trigger route del server.
Puoi anche eseguire manualmente una funzione utilizzando la CLI:
Puoi osservare i log con:
Nel cloud, ogni esecuzione consuma crediti dello spazio di lavoro: un costo fisso per invocazione più un costo per millisecondo per il tempo di esecuzione della funzione, quindi mantenere timeoutSeconds limitato e il codice veloce riduce i costi. Vedi Crediti per le tariffe.

Payload del trigger di route

Quando un trigger di tipo route invoca la tua funzione logica, questa riceve un oggetto RoutePayload che segue il formato AWS HTTP API v2. Importa il tipo RoutePayload da twenty-sdk/logic-function:
Il tipo RoutePayload ha la seguente struttura:

forwardedRequestHeaders

Per impostazione predefinita, le intestazioni HTTP delle richieste in ingresso non vengono passate alla tua funzione logica per motivi di sicurezza. Per accedere a intestazioni specifiche, elencale nell’array forwardedRequestHeaders:
Nel tuo handler, accedi alle intestazioni inoltrate in questo modo:
I nomi delle intestazioni vengono normalizzati in minuscolo. Accedile usando chiavi in minuscolo (ad es., event.headers['content-type']).

Risposta HTTP personalizzata

Per impostazione predefinita, restituire un valore semplice dal tuo handler lo invia come risposta 200 (JSON per gli oggetti, text/plain per le stringhe). Per controllare il codice di stato e le intestazioni della risposta, restituisci un oggetto Response da twenty-sdk/logic-function:
Per motivi di sicurezza, le intestazioni di risposta sono limitate a un elenco consentito. Qualsiasi intestazione che non è presente nell’elenco (ad esempio Set-Cookie, intestazioni CORS come Access-Control-Allow-Origin o intestazioni personalizzate X-*) viene ignorata senza segnalazione prima che la risposta venga inviata. Le intestazioni di risposta consentite sono:
  • content-type
  • content-language
  • content-disposition
  • cache-control
  • retry-after
Il codice di stato deve essere un codice di stato HTTP valido (compreso tra 100 e 599). I nomi delle intestazioni di risposta vengono confrontati senza distinzione tra maiuscole e minuscole.

Risposte di errore della piattaforma

Oltre alle risposte specifiche del tuo handler, la piattaforma risponde direttamente alle chiamate di route in alcune situazioni: 404 quando la route o la funzione non esiste, 403 quando l’applicazione è arrestata, 402 quando il workspace ha esaurito il proprio credito o non ha un abbonamento attivo, 409 quando viene raggiunto un limite di archiviazione, 429 quando viene raggiunto un rate limit di esecuzione o un limite di utilizzo e 422 quando le dependencies dell’applicazione in produzione sono troppo grandi per essere installate — vedi limiti di dimensione delle dipendenze.

Trigger route del server

httpRouteTriggerSettings espone una funzione sotto /s/ e risolve lo spazio di lavoro dall’host della richiesta — il che funziona quando ogni spazio di lavoro ha il proprio dominio. I provider di terze parti, tuttavia, inviano gli eventi di ogni tenant a un URL. Per questo caso, usa serverRouteTriggerSettings.Il trigger ha due parti:
  1. Una funzione logica di resolver — dichiarata con serverRouteTriggerSettings — viene eseguita nel tuo workspace proprietario (il workspace che possiede la registrazione dell’applicazione). Analizza la richiesta in ingresso e restituisce:
    • { workspaceId, targetLogicFunctionUniversalIdentifier, payload? } — la piattaforma mette in coda tale destinazione nel workspace risolto e conferma con 202 { queued: true }, oppure
    • una Response da twenty-sdk/logic-function — la piattaforma restituisce tale risposta HTTP sincronicamente e non mette in coda alcuna destinazione (usa questo per challenge handshake come Slack url_verification).
    Il resolver è l’unico punto di autorizzazione: l’URL contiene solo l’identificatore del resolver. Questo è il punto preferenziale per verificare le firme delle richieste: il resolver viene eseguito prima di qualsiasi effetto collaterale, ha accesso al rawBody originale e agli header inoltrati, e può rifiutare senza toccare la destinazione.
  2. Una funzione logica di destinazione — una normale funzione logica per-workspace — viene quindi eseguita nel workspace risolto con il payload restituito dal resolver (o il payload originale della richiesta se il resolver non lo ha trasformato). Il suo valore di ritorno non viene osservato dal chiamante HTTP quando il resolver ha scelto il percorso di messa in coda.
src/logic-functions/resolve-server-route.logic-function.ts
src/logic-functions/handle-invoice-paid.logic-function.ts
L’endpoint è raggiungibile all’indirizzo:
Il primo segmento è l’id della registrazione della tua app, il secondo è il universalIdentifier del resolver dal tuo manifest. Una route viene risolta solo a un resolver di quella registrazione, nel relativo workspace proprietario. Quando la tua app registra il webhook stesso, crea l’URL dall’interno di una funzione logica con getServerRouteUrl; Twenty inserisce l’URL di base delle route del server della tua app come TWENTY_SERVER_ROUTES_URL:
Gli URL che contengono solo l’id o l’universalIdentifier del resolver (/webhooks/server/:identifier) continuano per ora a essere risolti, ed entrambi restituiscono un’intestazione Deprecation. Il formato universalIdentifier restituisce 409 quando due registrazioni dichiarano lo stesso identificatore. Sposta gli URL registrati presso i tuoi provider nel modulo di registrazione.
Risposta a una verifica GET. Alcuni provider verificano un endpoint prima di inviarvi dati, inviando un GET contenente una verifica allo stesso URL a cui in seguito invieranno eventi tramite POST — WhatsApp Cloud API di Meta è uno di questi. Una route del server risponde solo a POST, a meno che non venga specificato diversamente, quindi dichiara entrambi i metodi:
La verifica arriva in event.queryStringParameters e la restituzione di un Response la rimanda al provider nella stessa richiesta. Un corpo stringa viene inviato come text/plain, che è ciò che questi provider si aspettano:
httpMethods sostituisce l’impostazione predefinita anziché aggiungersi a essa, quindi ['GET'] da solo fa sì che la route rifiuti POST. Sono supportati solo GET e POST. Lascialo non impostato a meno che il provider non richieda il secondo verbo: una route che dichiara GET avrà il proprio resolver eseguito da qualsiasi chiamante non autenticato, inclusi i crawler e gli strumenti di anteprima dei link che inviano GET senza richiesta. A tutto ciò per cui la piattaforma non dispone di un metodo viene restituito 405 senza che il resolver venga mai eseguito.
L’applicazione deve essere rivendicata e installata nel suo spazio di lavoro proprietario. Segui Rivendicare la tua app per assegnare un proprietario, quindi installa l’app in quello spazio di lavoro. Finché entrambe le condizioni non sono soddisfatte, il resolver non ha dove essere eseguito e la route del server restituisce 404 (LOGIC_FUNCTION_NOT_FOUND), anche se l’app è installata altrove. La ricerca nel Marketplace nasconde le app con route del server finché questa configurazione e tutte le variabili del server richieste non sono complete. Vedi costi e disponibilità dello spazio di lavoro proprietario per i dettagli di fatturazione e operativi.
Contratto del resolver. Il tipo LogicFunctionConfig dell’SDK impone questo a tempo di compilazione: non appena imposti serverRouteTriggerSettings, il tuo handler è vincolato a restituire una Response, oppure { workspaceId: string; targetLogicFunctionUniversalIdentifier: string; payload?: object } (o una Promise di uno dei due). Nel percorso di dispatch, il workspaceId deve essere un workspace in cui la funzione di destinazione è installata, altrimenti la richiesta viene rifiutata con 404. Un risultato che non corrisponde a nessuna delle due forme — incluso uno i cui identificatori non sono UUID — viene rifiutato con 502.
La verifica della firma è una tua responsabilità — eseguila nel resolver. La piattaforma non verifica le firme delle richieste. Il resolver è il punto consigliato in cui farlo: viene eseguito per primo, con accesso a event.rawBody e agli header che hai elencato in forwardedRequestHeaders, e un errore generato (o qualsiasi workspaceId non corrispondente) interrompe l’inoltro prima che la destinazione venga invocata. Se invece sposti la verifica verso la destinazione, la destinazione deve fare attenzione a non perdere rawBody e gli header — cioè il resolver non deve restituire un payload. Verifica sempre prima di qualsiasi effetto collaterale e usa un confronto a tempo costante.
Token bearer. Alcuni provider, come Microsoft Bot Framework, firmano ogni richiesta con un JWT nell’intestazione Authorization anziché con un’intestazione di firma. Una route del server può elencare authorization in forwardedRequestHeaders per riceverla e il resolver la verifica come qualsiasi altra firma. La piattaforma rimuove l’intestazione se contiene un token emesso da Twenty, quindi una route del server non riceve mai una sessione Twenty. cookie e proxy-authorization non vengono mai inoltrati e le route HTTP non possono inoltrare nessuna di queste tre intestazioni.Per le firme delle richieste, la maggior parte dei provider firma con HMAC-SHA256; le parti che differiscono sono il nome dell’header, la codifica del digest e la stringa del payload firmato. Alcuni esempi:L’esempio di resolver sopra mostra già il flusso HMAC-SHA256 di GitHub — adatta il nome dell’header, la codifica del digest e la stringa del payload firmato in base al provider con cui ti stai integrando.
Quando il resolver restituisce un oggetto di dispatch, la route risponde con 202 { queued: true } e la funzione di destinazione viene eseguita nella coda dei worker — il chiamante non osserva mai la latenza, il risultato o gli errori della funzione di destinazione (che vengono registrati nei log di esecuzione). Questo impedisce che le nuove consegne da parte del mittente amplifichino i rallentamenti nell’elaborazione, che è esattamente ciò che si desidera per l’acquisizione dei webhook.Il resolver viene eseguito nel workspace del proprietario e utilizza i relativi crediti, pertanto la route restituisce 402 quando tale workspace ha esaurito il proprio credito o non ha un abbonamento attivo, e 429 quando viene raggiunto un limite di utilizzo. I mittenti che ritentano in caso di 5xx non ritenteranno in questi casi, ed è ciò che vuoi: il rifiuto non si risolverà al tentativo successivo.Quando il chiamante deve leggere il corpo della risposta sulla stessa richiesta (challenge handshake, acknowledgement interattivi), restituisci invece una Response dal resolver. La piattaforma lo riecheggia in modo sincrono e salta la coda; i suoi header passano attraverso la stessa allow-list delle risposte delle route HTTP. Mantieni il resolver veloce — alcuni provider (ad es. Slack) vanno in timeout in pochi secondi. Poiché il resolver è raggiungibile come endpoint pubblico, proteggilo con rate limiting al tuo edge.

Payload del trigger di evento del database

Quando un trigger di evento del database richiama la funzione logica, questa riceve un DatabaseEventPayload per ogni record modificato (oppure un DatabaseEventBatchPayload che copre più record quando batchMode è abilitato). Il payload combina i metadati sull’area di lavoro e sull’oggetto di origine con l’evento a livello di record.
Il payload include:Per le eliminazioni logiche, .deleted segue la struttura in stile aggiornamento perché il campo deletedAt del record cambia. Per le eliminazioni permanenti, usa .destroyed.
databaseEventTriggerSettings.updatedFields filtra quali eventi di aggiornamento attivano la funzione. event.properties.updatedFields indica quali campi sono effettivamente cambiati nell’evento corrente.
databaseEventTriggerSettings accetta:Esempio di evento “created”:
Esempio di evento “updated”:
Attiva solo sugli aggiornamenti dell’email:
Esempio di evento “destroyed”:

Raggruppamento degli eventi del database

Una scrittura in blocco genera molti eventi contemporaneamente: l’importazione di una casella di posta, ad esempio, può aggiornare migliaia di record in una singola operazione e una chiamata per record sovraccarica la coda. Imposta batchMode per riceverli insieme:
L’handler riceve quindi un DatabaseEventBatchPayload, che contiene gli stessi metadati di DatabaseEventPayload ma sostituisce i campi a livello di record con un array events:
La dimensione del batch non è qualcosa su cui un handler può fare affidamento. Il server decide quanti eventi contiene una chiamata, con un massimo di 200, e un batch contiene da un singolo evento fino a tale limite, a seconda di quanti ne ha prodotto la scrittura di origine. Scrivi l’handler in modo che esegua il ciclo su events, indipendentemente dalla sua lunghezza.
Gli eventi vengono raggruppati in base all’utente che li ha causati prima di essere inseriti in batch, quindi ogni chiamata riguarda un singolo attore e context.workspaceMemberId mantiene il proprio significato. Il filtro updatedFields viene applicato prima del raggruppamento in batch, quindi un batch contiene solo i record richiesti.Senza batchMode, l’handler continua a ricevere un DatabaseEventPayload per record.

Contesto di esecuzione

I gestori di funzioni logiche generiche ricevono un secondo argomento che descrive l’esecuzione stessa, indipendentemente da ciò che l’ha attivata. Mentre il primo argomento cambia struttura in base all’attivatore, questo no:
userWorkspaceId e workspaceMemberId sono null quando nessuno ha attivato l’esecuzione: le pianificazioni cron, gli hook di installazione e i webhook non autenticati non hanno una persona alle spalle. Sono inoltre null quando la persona non ha un record del membro dello spazio di lavoro o quando il suo è stato eliminato.
Il contesto indica chi ha attivato l’esecuzione. Ciò che l’esecuzione può fare è una dichiarazione separata, riportata di seguito.

Quale accesso utilizza una chiamata

Ogni client — CoreApiClient, MetadataApiClient e RestApiClient — agisce come la persona che ha attivato l’esecuzione: il suo ruolo intersecato con quello della tua applicazione, quindi la chiamata non può mai fare più di quanto possa fare ciascuno dei due. Questo è il comportamento predefinito e significa che una persona non può mai usare la tua app per superare le proprie autorizzazioni.Quando nessuno ha attivato l’esecuzione, non c’è nessuna persona per conto della quale agire, quindi lo stesso client usa come fallback l’accesso della tua applicazione: le pianificazioni cron, gli hook di installazione e i webhook non autenticati non richiedono una gestione speciale.Alcune chiamate necessitano legittimamente dell’accesso dell’applicazione stessa anche quando una persona è dietro l’esecuzione — per leggere i record di configurazione della tua app o per svolgere operazioni che quella persona non potrebbe eseguire autonomamente. Crea un secondo client per questi casi:
Crea entrambi una sola volta, nell’ambito del modulo, e ogni punto di chiamata indica quindi quale accesso utilizza in base al client che chiama. RestApiClient accetta la stessa opzione:
Un’esecuzione che nessuno ha attivato agisce come la tua applicazione. Le pianificazioni cron, gli hook di installazione e i webhook non autenticati non hanno una persona alle spalle, quindi il client predefinito usa come fallback l’accesso della tua applicazione e continua a funzionare. runAs: 'application' è necessario solo quando vuoi tale accesso in un’esecuzione che una persona ha attivato.Controlla context.workspaceMemberId quando una funzione si comporta diversamente a seconda che ci sia qualcuno alle sue spalle, ad esempio per attribuire un record.
Gli helper dell’SDK che accedono alle risorse della tua applicazione usano sempre il relativo accesso e ignorano runAs: l’archivio chiave-valore, le connessioni, i canali di messaggistica, runAgent, getPublicAssetUrl e l’addebito dei crediti. ai.generate segue invece i client predefiniti: agisce come la persona che ha avviato l’esecuzione, nell’ambito del ruolo della tua app, oppure come la tua app quando nessuno l’ha avviata.

Esporre una funzione come strumento di IA o come azione del flusso di lavoro

Le funzioni logiche possono essere esposte su due superfici, ciascuna con il proprio trigger:
  • toolTriggerSettings — rende la funzione individuabile dalle funzionalità di IA di Twenty (chat, MCP, function calling). Usa lo standard JSON Schema, il formato che gli LLM comprendono nativamente.
  • workflowActionTriggerSettings — fa apparire la funzione come un passaggio nel builder visivo dei flussi di lavoro. Usa il ricco InputSchema di Twenty affinché il builder possa visualizzare correttamente editor di campi, selettori di variabili ed etichette.
Una funzione può optare per uno, l’altro o entrambi. Si affiancano a cronTriggerSettings, databaseEventTriggerSettings e httpRouteTriggerSettings — stesso schema, stessa struttura.
Relazione con l’azione Code del workflow. L’azione Code incorporata nel generatore di workflow è essa stessa una funzione logica — Twenty ne crea una per ogni passaggio Code e ne espone l’editor inline. workflowActionTriggerSettings è il modo in cui trasformi quel codice inline una tantum in un’azione riutilizzabile: definisci la funzione una volta nella tua app e diventa selezionabile in qualsiasi workflow, invece di essere copiata e incollata in ogni passaggio Code. Consulta l’azione Code nella guida utente per la vista dell’utente finale.
src/logic-functions/enrich-company.logic-function.ts
Punti chiave:
  • Una funzione può combinare le superfici — dichiara sia toolTriggerSettings sia workflowActionTriggerSettings per esporla in chat E nel builder dei flussi di lavoro.
  • toolTriggerSettings.inputSchema e workflowActionTriggerSettings.inputSchema sono entrambi opzionali. Se omessi, il builder del manifest li deduce dal codice sorgente dell’handler (JSON Schema per lo strumento di IA, InputSchema di Twenty per l’azione del flusso di lavoro). Forniscine uno esplicitamente quando desideri una tipizzazione più ricca — ad esempio, con campi compatibili con FieldMetadataType come CURRENCY o RELATION per il builder dei flussi di lavoro, oppure con campi description che l’agente di IA può leggere:
Per dichiarare i parametri una sola volta e servire entrambi i contesti, definisci un unico JSON Schema (InputJsonSchema) e convertilo per l’azione del flusso di lavoro con jsonSchemaToInputSchema da twenty-sdk/logic-function. toolTriggerSettings.inputSchema accetta direttamente il JSON Schema, mentre workflowActionTriggerSettings.inputSchema si aspetta l’InputSchema di Twenty:
Rendering di una chiamata dello strumento con un componente front
Per impostazione predefinita, una chiamata dello strumento viene visualizzata nella chat AI come riga del passaggio: il nome dello strumento e un indicatore di caricamento finché non restituisce un risultato. Imposta toolTriggerSettings.frontComponentUniversalIdentifier su uno dei tuoi componenti front e la chat eseguirà invece il rendering di quel componente in linea, per ogni chiamata a questo strumento.
Il componente legge la chiamata di cui esegue il rendering con useToolCall():
src/front-components/invoice-preview.tsx
Il componente viene montato non appena l’agente ha terminato lo streaming dei suoi argomenti, quindi gestisce l’intera chiamata, non solo il risultato. toolCall.status passa attraverso input-available, gli stati di approvazione quando lo strumento ne richiede una, e quindi output-available, output-denied o output-error; input è ciò che l’agente ha proposto, output ciò che il tuo gestore ha restituito. Esegui il rendering di ogni stato che ti interessa, poiché la riga del passaggio non è più presente per gestirli.Sia input sia output sono tipizzati come Record\<string, unknown>, quindi restringi ogni campo prima di utilizzarlo anziché eseguire un cast. Ciò è particolarmente importante per tutto ciò che passi al DOM: un valore che inserisci in un href senza verificare che sia una stringa è un link controllato dall’output dello strumento.
Se l’identificatore non corrisponde a nessun componente front-end nell’app, la chat torna alla riga del passaggio predefinita. I componenti front-end nella chat IA vengono eseguiti con lo stesso ruolo dichiarato dalla tua app, pertanto un widget può solo leggere e scrivere ciò che l’app stessa è autorizzata a fare.
Un esempio completo di azione di workflow
workflowActionTriggerSettings accetta quattro campi:Mettendo tutto insieme: una funzione esposta come azione di workflow, con un output dichiarato così che i passaggi successivi possano fare riferimento a taskId:
src/logic-functions/enrich-company.logic-function.ts
Una volta che l’app è installata, Enrich Company appare nel selettore di azioni del generatore di workflow. Il generatore visualizza companyName e domain come campi di input (ognuno in grado di prelevare valori dai passaggi precedenti), e i passaggi a valle possono fare riferimento ai risultati taskId ed enriched del passaggio.
Scrivi una buona description. Gli agenti IA fanno affidamento sul campo description della funzione per decidere quando usare lo strumento. Sii specifico su cosa fa lo strumento e quando dovrebbe essere invocato.
Helper di runtime. twenty-sdk/utils riesporta piccoli helper di runtime in modo che gli handler non importino mai direttamente da twenty-shared. Per esempio, isDefined(value) restituisce false sia per null che per undefined — usalo per restringere in modo sicuro gli input opzionali degli handler, che possono arrivare come null a runtime anche quando sono tipizzati come T | undefined:
Hook di installazione — i gestori di pre-installazione, post-installazione e disinstallazione — condividono questo runtime, ma sono dichiarati con le proprie funzioni di definizione e non accettano impostazioni dei trigger. Consulta Hook di installazione per definePreInstallLogicFunction, definePostInstallLogicFunction e defineUninstallLogicFunction.

Controllo di integrità.

Dichiara un controllo di integrità con defineHealthCheck per segnalare se la tua app è effettivamente in grado di funzionare. Riguarda ciò che solo l’app può sapere: una chiave presente ma revocata, un account con il piano sbagliato, un webhook che non è mai stato registrato. Ciò che Twenty può rilevare autonomamente — una variabile obbligatoria che nessuno ha compilato — è gestito da isRequired e non richiede codice. Viene eseguito lato server, quindi legge le variabili segrete come qualsiasi altra funzione logica. Un controllo dello spazio di lavoro non ha nessuno alle spalle, quindi il gestore non richiede argomenti.
src/logic-functions/health-check.ts
Il risultato è un’unione discriminata, non un booleano: OK non mostra alcun banner; ogni altro stato visualizza il titolo in un banner colorato in base allo stato, quindi scegli quello che corrisponde al grado di azione richiesto alla persona. Gli stati sono gli ApplicationHealthStatus di Twenty, quindi la stringa semplice e il membro enum sono intercambiabili:
Twenty esegue il controllo all’apertura della pagina delle impostazioni dell’app e ne mostra il risultato in un banner. Non viene memorizzato nulla, quindi ciò che segnali corrisponde sempre a quanto appena restituito dal controllo. Un controllo che genera un’eccezione, va in timeout o restituisce una struttura che Twenty non riesce a leggere viene trattato come sconosciuto, mai come errore, e non viene mostrato alcun banner. Un’interruzione di servizio di terze parti non dovrebbe colorare di rosso ogni app installata — altrimenti le persone imparerebbero a ignorare il segnale. La posizione deve rimanere all’interno di Twenty, quindi inizia con / o #: una posizione esterna, relativa al protocollo o relativa perde il proprio pulsante anziché diventare un reindirizzamento altrove. Il pulsante viene inoltre rimosso quando l’app non segnala alcuna posizione e non dispone di una scheda di configurazione a cui ricorrere, quindi un controllo su un’app senza variabili e senza voci del menu delle impostazioni mostra il relativo titolo senza un pulsante non funzionante.

Controllo della configurazione di ciascun membro

Un’app dichiara una funzione di controllo dell’integrità. Ometti runOnUserPreferences o impostalo su false per eseguire un controllo dello spazio di lavoro nella pagina delle impostazioni dell’app, con un gestore senza argomenti. Imposta runOnUserPreferences: true per eseguire invece un controllo del membro nella pagina delle preferenze delle app. Un gestore del membro riceve il valore obbligatorio { userWorkspaceId: string } dalla sessione autenticata:
src/logic-functions/health-check.ts
Un controllo del membro viene eseguito quando un membro apre l’app nella propria pagina preferenze delle app, e solo quel membro ne vede il banner. Viene eseguito come quel membro: il gestore riceve { userWorkspaceId } dalla sessione autenticata e le variabili con ambito utente mantengono i propri valori. L’oggetto parametro e l’ID sono obbligatori per questa modalità. Non viene eseguito nelle impostazioni dell’app dello spazio di lavoro. Passa userWorkspaceId a listConnections() affinché restituisca solo le connessioni di quel membro. Una connessione il cui aggiornamento del token non riesce viene esclusa dall’elenco, quindi “nessuna connessione” include anche “riconnetti”. Un pulsante senza location porta alla scheda Generale dell’app in quella pagina.
I controlli del membro richiedono Twenty 2.55.0 o versioni successive. Preferenze delle app non è ancora abilitato in tutti gli spazi di lavoro: dove non lo è, un controllo del membro non viene mai eseguito.
Un’app può dichiarare una sola funzione di controllo dell’integrità, in modalità spazio di lavoro o membro. Le dichiarazioni esistenti mantengono il proprio gestore dello spazio di lavoro senza argomenti. La dichiarazione di una seconda funzione causa l’errore della build.

Crea un’attività della cronologia di eventi

Usa createTimelineActivity() per pubblicare un evento di dominio esplicito da una funzione logica. Definisci prima l’evento come tipo di attività della cronologia, quindi fai riferimento al tipo e agli oggetti tramite i relativi identificatori universali stabili:
Twenty risolve gli identificatori universali negli ID dei metadati specifici dell’installazione, verifica che il tipo di attività della cronologia appartenga all’app chiamante e acquisisce un’istantanea dei relativi metadati di presentazione nella nuova attività. Gli input obbligatori sono timelineActivityTypeUniversalIdentifier, targetObjectUniversalIdentifier e targetRecordId. Puoi anche fornire happensAt, properties e workspaceMemberId. happensAt controlla la posizione e l’ora visualizzata dell’evento nella cronologia; per impostazione predefinita corrisponde all’ora di creazione. Per associare un altro record all’evento, fornisci insieme linkedRecordId e linkedObjectMetadataUniversalIdentifier. Puoi inoltre fornire linkedRecordCachedName come alternativa di visualizzazione storica:
Il ruolo della funzione logica deve disporre dell’autorizzazione di scrittura sull’oggetto standard timelineActivity. Mantieni i tipi di evento espliciti non associati a un’action; un tipo associato a un’azione riceve già eventi di audit automatici e altrimenti produrrebbe righe duplicate.

Client API tipizzati (twenty-client-sdk)

Il pacchetto twenty-client-sdk fornisce due client GraphQL tipizzati per interagire con l’API di Twenty dalle tue funzioni logiche e dai componenti front-end.
CoreApiClient è il client principale per interrogare e modificare i dati dello spazio di lavoro. Viene generato dallo schema del tuo spazio di lavoro con twenty app generate-client, o dopo una sincronizzazione con twenty app apply o twenty app dev, quindi è completamente tipizzato per corrispondere ai tuoi oggetti e campi.
shareWith è rilevante solo su un oggetto i cui record sono privati; altrove l’argomento viene ignorato. Su un oggetto di questo tipo, elenca chi ottiene l’accesso al record creato: ogni voce si rivolge esattamente a uno tra workspaceMemberId, roleId o everyone: true, con un accessLevel pari a READ, READ_WRITE o FULL. Un membro dello spazio di lavoro che crea un record riceve sempre un’autorizzazione proprietario FULL, quindi l’argomento è facoltativo per lui; anche una funzione logica che agisce come relativa applicazione o una chiave API riceve un’autorizzazione FULL per il proprio ruolo e tali chiamanti, come un chiamante di sistema, devono passare shareWith, altrimenti la creazione viene rifiutata. Ogni voce deve indicare un membro o un ruolo dello spazio di lavoro. Una funzione logica o una chiave API condivide tramite il proprio ruolo, quindi ogni titolare di tale ruolo ottiene lo stesso accesso ai record che crea; assegna a una chiave i cui record devono rimanere privati un ruolo proprio.Il client utilizza una sintassi a selection-set: passa true per includere un campo, usa __args per gli argomenti e annida oggetti per le relazioni. Ottieni completamento automatico e controllo dei tipi completi basati sullo schema del tuo spazio di lavoro.
Esegui twenty app generate-client per aggiornare il client dallo schema GraphQL del tuo spazio di lavoro. twenty app apply e twenty app dev lo rigenerano anche dopo la sincronizzazione. twenty app build esegue la build solo localmente e non genera il client.

Utilizzo di CoreSchema per le annotazioni di tipo

CoreSchema fornisce tipi TypeScript corrispondenti agli oggetti del tuo spazio di lavoro — utile per tipizzare lo stato dei componenti o i parametri delle funzioni:
MetadataApiClient è fornito pronto all’uso con l’SDK (nessuna generazione richiesta). Interroga l’endpoint /metadata per la configurazione dello spazio di lavoro, le applicazioni e i caricamenti di file. Accetta la stessa opzione runAs di CoreApiClient — vedi Quale accesso utilizza una chiamata.È anche il modo in cui un’app legge e aggiorna le proprie variabili dell’applicazione, in una funzione logica come in un componente front. Un token dell’applicazione raggiunge solo la propria applicazione, nel proprio spazio di lavoro.Una variabile utente si trova in process.env con il valore del membro che ha attivato l’esecuzione o con il relativo valore predefinito finché non imposta il proprio. Un’esecuzione non attivata da nessuno non riceve variabili utente. Per leggere il valore delle variabili utente della tua app per ogni membro, ad esempio in un processo cron, esegui una query su myUserApplicationVariables con runAs: 'application'.

Caricamento dei file

MetadataApiClient include un metodo uploadFile per allegare file ai campi di tipo file. Il file viene inviato direttamente all’archiviazione file di Twenty: l’API fornisce una destinazione di caricamento, il client invia lì i byte, quindi l’API conferma il caricamento.
Punti chiave:
  • Usa l’universalIdentifier del campo (non il suo ID specifico dello spazio di lavoro), quindi il tuo codice di upload funziona in qualsiasi spazio di lavoro in cui la tua app è installata.
  • Il tipo MIME viene rilevato dal contenuto del file una volta caricato, quindi non c’è nulla da dichiarare.
  • L’url restituito è un URL firmato che puoi usare per accedere al file caricato.
Quando il tuo codice viene eseguito su Twenty (funzioni logiche o componenti front-end), la piattaforma inietta le credenziali come variabili d’ambiente:
  • TWENTY_API_URL — URL di base dell’API di Twenty
  • TWENTY_APP_ACCESS_TOKEN — Chiave a breve durata per l’accesso predefinito: il ruolo di una persona intersecato con quello della tua applicazione quando qualcuno è alle spalle dell’esecuzione, il ruolo della tua applicazione quando non c’è nessuno. La persona è chiunque abbia attivato l’esecuzione in una funzione logica, oppure chiunque stia visualizzando la pagina in un componente front-end.
  • TWENTY_APP_APPLICATION_ACCESS_TOKEN — Chiave a breve durata con ambito esclusivamente al ruolo della tua applicazione. Solo funzioni logiche, sempre inserita lì, ed è ciò che utilizza runAs: 'application'.
Non devi passare questi elementi ai client — vengono letti automaticamente da process.env e Quale accesso utilizza una chiamata spiega come scegliere tra di essi. Le autorizzazioni della tua applicazione sono determinate dal ruolo dichiarato con defineApplicationRole() (o referenziato tramite defaultRoleUniversalIdentifier in application-config.ts); un’esecuzione che agisce come una persona non può mai superare né tale ruolo né il proprio.