i18n - docs translations (#22715)
Created by Github action <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22715?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
committed by
GitHub
parent
a0cf4cc9e1
commit
ebee7d71b9
@@ -4,9 +4,9 @@ description: Esegui logica prima o dopo l'installazione — popola i dati, esegu
|
||||
icon: wrench
|
||||
---
|
||||
|
||||
Gli hook di installazione sono funzioni logiche speciali che vengono eseguite durante il ciclo di vita di installazione o aggiornamento. Condividono lo stesso runtime del gestore delle [logic functions](/l/it/developers/extend/apps/logic/logic-functions) normali e ricevono un `InstallPayload`, ma sono dichiarati con le proprie funzioni di definizione — `definePostInstallLogicFunction()` e `definePreInstallLogicFunction()` — e vivono al di fuori del normale modello di trigger (eventi HTTP, cron, database).
|
||||
Gli hook di installazione sono funzioni logiche speciali che vengono eseguite durante il ciclo di vita di installazione o aggiornamento. Condividono lo stesso runtime del gestore delle [logic functions](/l/it/developers/extend/apps/logic/logic-functions) normali e ricevono un `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` è `undefined` in una nuova installazione), ma sono dichiarati con le proprie funzioni di definizione e vivono al di fuori del normale modello di trigger (HTTP, eventi cron, eventi del database).
|
||||
|
||||
Ogni app può definire **al massimo una funzione di pre-installazione** e **al massimo una funzione di post-installazione**. La build del manifesto genererà un errore se ne viene rilevata più di una per ciascun tipo.
|
||||
Ogni app può definire **al massimo una funzione di pre-installazione** e **al massimo una funzione di post-installazione**. La build del manifesto genera un errore se ne viene rilevata più di una per ciascun tipo.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
@@ -19,111 +19,59 @@ Ogni app può definire **al massimo una funzione di pre-installazione** e **al m
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="definePostInstallLogicFunction" description="Viene eseguita dopo che la migrazione dei metadati dello spazio di lavoro è stata applicata">
|
||||
## A colpo d'occhio
|
||||
|
||||
Una funzione di post-installazione viene eseguita automaticamente una volta che la tua app ha terminato l'installazione in uno spazio di lavoro. Il server la esegue **dopo** che i metadati dell'app sono stati sincronizzati e il client SDK è stato generato, così lo spazio di lavoro è completamente pronto per l'uso e il nuovo schema è attivo. I casi d'uso tipici includono il popolamento di dati predefiniti, la creazione di record iniziali, la configurazione delle impostazioni dello spazio di lavoro o il provisioning di risorse su servizi di terze parti.
|
||||
| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Esecuzioni | Prima della migrazione dei metadati — lo schema e i dati **precedenti** sono ancora intatti | Dopo la migrazione e la generazione dell'SDK — il **nuovo** schema è in vigore |
|
||||
| Esecuzione | Sempre sincrona; blocca l'installazione | Async per impostazione predefinita (in coda, 3 tentativi); modalità sync tramite opt-in con `shouldRunSynchronously: true` |
|
||||
| In caso di errore | L'installazione viene **annullata** prima di qualsiasi modifica allo schema | Async: ritentato fino a 3 volte. Sync: il chiamante riceve `POST_INSTALL_ERROR` (le modifiche allo schema **non** vengono annullate) |
|
||||
| Uso tipico | Eseguire il backup o correggere dati che una migrazione perderebbe; rifiutare un aggiornamento rischioso lanciando un'eccezione | Popolare dati predefiniti, configurare il workspace, registrare risorse esterne |
|
||||
|
||||
```ts src/logic-functions/post-install.ts
|
||||
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
**Regola generale:** usa post-install come impostazione predefinita. Ricorri al pre-install solo quando la migrazione stessa è distruttiva e devi intercettare lo stato precedente prima che vada perso.
|
||||
|
||||
const handler = async (payload: InstallPayload): Promise<void> => {
|
||||
console.log('Post install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
| Vuoi... | Usa |
|
||||
| ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- |
|
||||
| Popolare i dati, configurare il workspace, registrare risorse esterne | `post-install` |
|
||||
| Lavoro di lunga durata che non dovrebbe bloccare la risposta dell'installazione | `post-install` (modalità async predefinita, con retry del worker) |
|
||||
| Eseguire un setup rapido da cui il chiamante dipende immediatamente dopo il completamento dell'installazione | `post-install` con `shouldRunSynchronously: true` |
|
||||
| Leggere o eseguire il backup dei dati che la prossima migrazione perderebbe | `pre-install` |
|
||||
| Rifiutare un aggiornamento che corromperebbe i dati esistenti | `pre-install` (genera un'eccezione dall'handler) |
|
||||
| Riconciliazione a ogni aggiornamento | Uno qualsiasi dei due hook con `shouldRunOnVersionUpgrade: true` |
|
||||
|
||||
export default definePostInstallLogicFunction({
|
||||
universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
|
||||
name: 'post-install',
|
||||
description: 'Runs after installation to set up the application.',
|
||||
timeoutSeconds: 300,
|
||||
shouldRunOnVersionUpgrade: false,
|
||||
shouldRunSynchronously: false,
|
||||
handler,
|
||||
});
|
||||
```
|
||||
## Comportamento condiviso da entrambi gli hook
|
||||
|
||||
Puoi anche eseguire manualmente la funzione di post-installazione in qualsiasi momento utilizzando la CLI:
|
||||
* La config è una config di `defineLogicFunction` meno le impostazioni di trigger, più `shouldRunOnVersionUpgrade`.
|
||||
* **Quando viene eseguito**: solo sulle nuove installazioni, per impostazione predefinita. Imposta `shouldRunOnVersionUpgrade: true` per eseguirlo anche sugli upgrade. Usa `previousVersion` / `newVersion` per ramificare in base al percorso di upgrade.
|
||||
* **L'idempotenza è importante**: il post-install async può essere ritentato e qualsiasi hook viene rieseguito sugli upgrade quando `shouldRunOnVersionUpgrade` è attivo.
|
||||
* Il consueto ambiente delle logic-function (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) viene iniettato, così puoi chiamare le API di Twenty con il token della tua app.
|
||||
* L'hook viene collegato automaticamente al manifesto dell'applicazione in fase di build (`preInstallLogicFunction` / `postInstallLogicFunction`) — non c'è nulla da referenziare in [`defineApplication()`](/l/it/developers/extend/apps/config/application).
|
||||
* Il `timeoutSeconds` predefinito è 300 per consentire attività di setup più lunghe, come il seeding dei dati.
|
||||
* **Non eseguito in modalità dev**: `yarn twenty dev` salta il flusso di installazione e sincronizza direttamente i file, quindi gli hook non vengono mai eseguiti in quell'ambiente. Attivali invece manualmente:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:function:exec --postInstall
|
||||
```
|
||||
|
||||
Punti chiave:
|
||||
* Le funzioni di post-installazione utilizzano `definePostInstallLogicFunction()` — una variante specializzata che omette le impostazioni dei trigger (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`).
|
||||
* L'handler riceve un `InstallPayload` con `{ previousVersion?: string; newVersion: string }` — `newVersion` è la versione in fase di installazione e `previousVersion` è la versione installata in precedenza (oppure `undefined` in caso di nuova installazione). Usa questi valori per distinguere le nuove installazioni dagli aggiornamenti e per eseguire logiche di migrazione specifiche per versione.
|
||||
* **Quando viene eseguito l'hook**: solo sulle nuove installazioni, per impostazione predefinita. Passa `shouldRunOnVersionUpgrade: true` se vuoi che venga eseguito anche quando l'app viene aggiornata da una versione precedente. Se omesso, il flag è `false` per impostazione predefinita e gli aggiornamenti saltano l'hook.
|
||||
* **Modello di esecuzione — asincrono per impostazione predefinita, sincrono su richiesta**: il flag `shouldRunSynchronously` controlla *come* viene eseguito il post-install.
|
||||
* `shouldRunSynchronously: false` *(default)* — l'hook viene **messo in coda nella coda dei messaggi** con `retryLimit: 3` ed eseguito in modo asincrono in un worker. La risposta di installazione viene restituita non appena il job è messo in coda, quindi un handler lento o in errore non blocca il chiamante. Il worker riproverà fino a tre volte. **Usalo per job di lunga durata** — popolamento di dataset di grandi dimensioni, chiamate a API di terze parti lente, provisioning di risorse esterne, qualsiasi cosa che possa superare una finestra di risposta HTTP ragionevole.
|
||||
* `shouldRunSynchronously: true` — l'hook viene eseguito **inline durante il flusso di installazione** (stesso executor del pre-install). La richiesta di installazione rimane bloccata finché l'handler non termina e, se genera un'eccezione, il chiamante dell'installazione riceve un `POST_INSTALL_ERROR`. Nessun tentativo automatico. **Usalo per attività rapide che devono completarsi prima della risposta** — ad esempio, emettere un errore di validazione all'utente, oppure un setup rapido di cui il client avrà bisogno immediatamente dopo il ritorno della chiamata di installazione. Tieni presente che la migrazione dei metadati è già stata applicata quando viene eseguito il post-install, quindi un errore in modalità sincrona **non** annulla le modifiche allo schema — si limita a far emergere l'errore.
|
||||
* Assicurati che il tuo handler sia idempotente. In modalità asincrona la coda può riprovare fino a tre volte; in entrambe le modalità l'hook può essere eseguito di nuovo durante gli aggiornamenti quando `shouldRunOnVersionUpgrade: true`.
|
||||
* Le variabili d'ambiente `APPLICATION_ID`, `APP_ACCESS_TOKEN` e `API_URL` sono disponibili all'interno dell'handler (come in qualsiasi altra funzione logica), quindi puoi chiamare le API di Twenty con un token di accesso applicativo con ambito sulla tua app.
|
||||
* È consentita una sola funzione di post-installazione per applicazione. La build del manifesto genererà un errore se ne viene rilevata più di una.
|
||||
* I campi `universalIdentifier`, `shouldRunOnVersionUpgrade` e `shouldRunSynchronously` della funzione vengono associati automaticamente al manifest dell'applicazione nel campo `postInstallLogicFunction` durante la build — non è necessario referenziarli in [`defineApplication()`](/l/it/developers/extend/apps/config/application).
|
||||
* Il timeout predefinito è impostato a 300 secondi (5 minuti) per consentire attività di configurazione più lunghe, come il popolamento dei dati.
|
||||
* **Non eseguito in modalità dev**: quando un'app è registrata in locale (tramite `yarn twenty dev`), il server salta completamente il flusso di installazione e sincronizza i file direttamente tramite il watcher della CLI — quindi il post-install non viene mai eseguito in modalità dev, indipendentemente da `shouldRunSynchronously`. Usa `yarn twenty dev:function:exec --postInstall` per attivarlo manualmente su un workspace in esecuzione.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="definePreInstallLogicFunction" description="Viene eseguita prima che la migrazione dei metadati dello spazio di lavoro sia applicata">
|
||||
|
||||
Una funzione di pre-installazione viene eseguita automaticamente durante l'installazione, **prima che venga applicata la migrazione dei metadati dello spazio di lavoro**. Condivide la stessa struttura di payload del post-install (`InstallPayload`), ma è posizionata prima nel flusso di installazione così da poter preparare lo stato da cui dipenderà la migrazione imminente — usi tipici includono il backup dei dati, la validazione della compatibilità con il nuovo schema o l'archiviazione di record che stanno per essere ristrutturati o eliminati.
|
||||
|
||||
```ts src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
|
||||
const handler = async (payload: InstallPayload): Promise<void> => {
|
||||
console.log('Pre install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
|
||||
export default definePreInstallLogicFunction({
|
||||
universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
|
||||
name: 'pre-install',
|
||||
description: 'Runs before installation to prepare the application.',
|
||||
timeoutSeconds: 300,
|
||||
shouldRunOnVersionUpgrade: true,
|
||||
handler,
|
||||
});
|
||||
```
|
||||
|
||||
Puoi anche eseguire manualmente la funzione di pre-installazione in qualsiasi momento utilizzando la CLI:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:function:exec --preInstall
|
||||
```
|
||||
|
||||
Punti chiave:
|
||||
* Le funzioni di pre-install usano `definePreInstallLogicFunction()` — stessa configurazione specialistica del post-install, solo agganciata a uno slot di ciclo di vita diverso.
|
||||
* Sia gli handler di pre- sia quelli di post-install ricevono lo stesso tipo `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Importalo una volta e riutilizzalo per entrambi gli hook.
|
||||
* **Quando viene eseguito l'hook**: posizionato appena prima della migrazione dei metadati del workspace (`synchronizeFromManifest`). Prima dell'esecuzione, il server esegue una "sincronizzazione ridotta" puramente additiva che registra nei metadati del workspace la funzione di pre-install della versione **nuova** — nient'altro viene toccato — e poi la esegue. Poiché questa sincronizzazione è solo additiva, gli oggetti, i campi e i dati della versione precedente restano intatti quando il tuo handler viene eseguito: puoi leggere ed eseguire in sicurezza il backup dello stato pre-migrazione.
|
||||
* **Modello di esecuzione**: il pre-install è eseguito **in modo sincrono** e **blocca l'installazione**. Se l'handler genera un'eccezione, l'installazione viene interrotta prima che vengano applicate modifiche allo schema — il workspace rimane sulla versione precedente in uno stato coerente. Questo è intenzionale: il pre-install è la tua ultima possibilità per rifiutare un aggiornamento rischioso.
|
||||
* Come per il post-install, è consentita una sola funzione di pre-installazione per applicazione. Viene collegata automaticamente al manifest dell'applicazione nel campo `preInstallLogicFunction` durante la build.
|
||||
* **Non eseguito in modalità dev**: come per il post-install — il flusso di installazione viene completamente saltato per le app registrate localmente, quindi il pre-install non viene mai eseguito con `yarn twenty dev`. Usa `yarn twenty dev:function:exec --preInstall` per attivarlo manualmente.
|
||||
<AccordionGroup>
|
||||
<Accordion title="definePostInstallLogicFunction" description="Viene eseguita dopo che la migrazione dei metadati dello spazio di lavoro è stata applicata">
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Pre-install vs post-install: quando usare l'uno o l'altro" description="Scegliere l'hook di installazione giusto">
|
||||
|
||||
Entrambi gli hook fanno parte dello stesso flusso di installazione e ricevono lo stesso `InstallPayload`. La differenza è **quando** vengono eseguiti rispetto alla migrazione dei metadati del workspace, e questo modifica quali dati possono gestire in sicurezza.
|
||||
|
||||
Il pre-install è sempre **sincrono** (blocca l'installazione e può interromperla). Il post-install è **asincrono per impostazione predefinita** — messo in coda su un worker con retry automatici — ma può optare per l'esecuzione sincrona con `shouldRunSynchronously: true`. Vedi l'accordion `definePostInstallLogicFunction` sopra per quando usare ciascuna modalità.
|
||||
|
||||
**Usa `post-install` per tutto ciò che richiede l'esistenza del nuovo schema.** Questo è il caso più comune:
|
||||
|
||||
* Popolamento di dati predefiniti (creazione di record iniziali, viste predefinite, contenuti demo) su oggetti e campi appena aggiunti.
|
||||
* Registrazione di webhook con servizi di terze parti ora che l'app ha le proprie credenziali.
|
||||
* Chiamare la tua API per completare il setup che dipende dai metadati sincronizzati.
|
||||
* Logica idempotente di "ensure this exists" che dovrebbe riconciliare lo stato a ogni aggiornamento — da combinare con `shouldRunOnVersionUpgrade: true`.
|
||||
|
||||
Esempio — eseguire il seeding di un record `PostCard` predefinito dopo l'installazione:
|
||||
Viene eseguito una volta che l'installazione della tua app è terminata: metadati sincronizzati, client SDK generato, nuovo schema interrogabile. Esempio — eseguire il seeding di un record predefinito nelle nuove installazioni:
|
||||
|
||||
```ts src/logic-functions/post-install.ts
|
||||
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
import { createClient } from './generated/client';
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
|
||||
const handler = async ({ previousVersion }: InstallPayload): Promise<void> => {
|
||||
if (previousVersion) return; // fresh installs only
|
||||
|
||||
const client = createClient();
|
||||
await client.postCard.create({
|
||||
data: { title: 'Welcome to Postcard', content: 'Your first card!' },
|
||||
const client = new CoreApiClient();
|
||||
await client.mutation({
|
||||
createPostCard: {
|
||||
__args: { data: { name: 'Welcome to Postcard', content: 'Your first card!' } },
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -133,22 +81,28 @@ export default definePostInstallLogicFunction({
|
||||
description: 'Seeds a welcome post card after install.',
|
||||
timeoutSeconds: 300,
|
||||
shouldRunOnVersionUpgrade: false,
|
||||
shouldRunSynchronously: false,
|
||||
handler,
|
||||
});
|
||||
```
|
||||
|
||||
**Usa `pre-install` quando una migrazione altrimenti distruggerebbe o corromperebbe i dati esistenti.** Poiché il pre-install viene eseguito contro lo schema *precedente* e un suo fallimento annulla l'aggiornamento, è il posto giusto per qualsiasi operazione rischiosa:
|
||||
Il flag `shouldRunSynchronously` controlla il modello di esecuzione:
|
||||
|
||||
* **Eseguire il backup dei dati che stanno per essere eliminati o ristrutturati** — ad esempio, stai rimuovendo un campo nella v2 e devi copiarne i valori in un altro campo o esportarli su uno storage prima che venga eseguita la migrazione.
|
||||
* **Archiviare i record che un nuovo vincolo renderebbe non validi** — ad esempio, un campo sta diventando `NOT NULL` e devi prima eliminare o correggere le righe con valori nulli.
|
||||
* **Validare la compatibilità e rifiutare l'aggiornamento se i dati attuali non possono essere migrati correttamente** — genera un'eccezione dall'handler e l'installazione si interrompe senza applicare modifiche. Questo è più sicuro che scoprire l'incompatibilità a migrazione in corso.
|
||||
* **Rinominare o rigenerare le chiavi dei dati** prima di una modifica dello schema che farebbe perdere l'associazione.
|
||||
* `false` *(predefinito)* — messo in coda nella message queue (`retryLimit: 3`) ed eseguito da un worker. La risposta dell'installazione ritorna non appena il job viene messo in coda. **Da usare per lavoro di lunga durata** — seeding di grandi dataset, API di terze parti lente.
|
||||
* `true` — eseguito inline durante il flusso di installazione. La richiesta di installazione rimane bloccata finché l'handler non termina; un errore lanciato viene esposto al chiamante come `POST_INSTALL_ERROR` (nessun retry). **Da usare per lavoro rapido che deve completarsi prima della risposta.** La migrazione è già stata applicata a questo punto, quindi un errore non annulla le modifiche allo schema — si limita a esporre l'errore.
|
||||
|
||||
Esempio — archiviare i record prima di una migrazione distruttiva:
|
||||
</Accordion>
|
||||
<Accordion title="definePreInstallLogicFunction" description="Viene eseguita prima che la migrazione dei metadati dello spazio di lavoro sia applicata">
|
||||
|
||||
Viene eseguito prima della migrazione dei metadati, contro lo schema **precedente** — il posto giusto per eseguire il backup di dati che una migrazione perderebbe o per rifiutare un upgrade rischioso. Prima dell'esecuzione, il server esegue una "sincronizzazione ridotta" puramente additiva che registra solo la funzione di pre-install della versione nuova; tutto il resto — oggetti, campi e dati della versione precedente — rimane intatto quando il tuo handler viene eseguito.
|
||||
|
||||
Il pre-install è sempre **sincrono** e blocca l'installazione. Se l'handler genera un'eccezione, l'installazione viene interrotta prima che venga applicata qualsiasi modifica allo schema — il workspace rimane sulla versione precedente in uno stato coerente. Questo è intenzionale: il pre-install è la tua ultima possibilità per rifiutare un aggiornamento rischioso.
|
||||
|
||||
Esempio — copiare i valori di un campo legacy prima che la migrazione lo elimini:
|
||||
|
||||
```ts src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
import { createClient } from './generated/client';
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
|
||||
const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise<void> => {
|
||||
// Only the 1.x → 2.x upgrade drops the legacy `notes` field.
|
||||
@@ -156,24 +110,24 @@ const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise
|
||||
return;
|
||||
}
|
||||
|
||||
const client = createClient();
|
||||
const legacyRecords = await client.postCard.findMany({
|
||||
where: { notes: { isNotNull: true } },
|
||||
const client = new CoreApiClient();
|
||||
const { postCards } = await client.query({
|
||||
postCards: {
|
||||
__args: { filter: { notes: { isNot: null } } },
|
||||
edges: { node: { id: true, notes: true } },
|
||||
},
|
||||
});
|
||||
|
||||
if (legacyRecords.length === 0) return;
|
||||
|
||||
// Copy legacy `notes` into the new `description` field before the migration
|
||||
// drops the `notes` column. If this fails, the upgrade is aborted and the
|
||||
// workspace stays on v1 with all data intact.
|
||||
await Promise.all(
|
||||
legacyRecords.map((record) =>
|
||||
client.postCard.update({
|
||||
where: { id: record.id },
|
||||
data: { description: record.notes },
|
||||
}),
|
||||
),
|
||||
);
|
||||
// Copy legacy `notes` into `description` before the migration drops the
|
||||
// column. If this fails, the upgrade aborts and the workspace stays on v1.
|
||||
for (const { node } of postCards.edges) {
|
||||
await client.mutation({
|
||||
updatePostCard: {
|
||||
__args: { id: node.id, data: { description: node.notes } },
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default definePreInstallLogicFunction({
|
||||
@@ -186,21 +140,5 @@ export default definePreInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
**Regola generale:**
|
||||
|
||||
| Vuoi... | Usa |
|
||||
| ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
||||
| Popolare dati predefiniti, configurare il workspace, registrare risorse esterne | `post-install` |
|
||||
| Eseguire seeding di lunga durata o chiamate a terze parti che non dovrebbero bloccare la risposta dell'installazione | `post-install` (predefinito — `shouldRunSynchronously: false`, con retry del worker) |
|
||||
| Eseguire un setup rapido di cui il chiamante farà affidamento immediatamente dopo il ritorno della chiamata di installazione | `post-install` con `shouldRunSynchronously: true` |
|
||||
| Leggere o eseguire il backup dei dati che la prossima migrazione perderebbe | `pre-install` |
|
||||
| Rifiutare un aggiornamento che corromperebbe i dati esistenti | `pre-install` (genera un'eccezione dall'handler) |
|
||||
| Eseguire la riconciliazione a ogni aggiornamento | `post-install` con `shouldRunOnVersionUpgrade: true` |
|
||||
| Eseguire un setup una tantum solo alla prima installazione | `post-install` con `shouldRunOnVersionUpgrade: false` (predefinito) |
|
||||
|
||||
<Note>
|
||||
In caso di dubbio, usa **post-install**. Ricorri al pre-install solo quando la migrazione stessa è distruttiva e devi intercettare lo stato precedente prima che vada perso.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -86,6 +86,22 @@ export default defineObject({
|
||||
**I campi base vengono aggiunti automaticamente.** Quando definisci un oggetto personalizzato, Twenty crea per te campi standard come `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` e `deletedAt`. Non è necessario dichiararli nel tuo array `fields` — solo i tuoi campi personalizzati. Puoi sovrascrivere un campo predefinito dichiarandone uno con lo stesso nome, ma è raramente una buona idea.
|
||||
</Note>
|
||||
|
||||
## Tipi di campo
|
||||
|
||||
L’insieme completo dei valori di `FieldType`, esportati da `twenty-sdk/define`:
|
||||
|
||||
| Categoria | Tipi |
|
||||
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Testo | `TEXT`, `RICH_TEXT`, `ARRAY` (di stringhe), `RAW_JSON` |
|
||||
| Numerico | `NUMBER` (`universalSettings.dataType`: `'float'` / `'int'` / `'bigint'`), `NUMERIC` (precisione arbitraria), `RATING`, `POSITION` |
|
||||
| Date | `DATE`, `DATE_TIME` |
|
||||
| Scelta | `BOOLEAN`, `SELECT`, `MULTI_SELECT` |
|
||||
| Composito | `FULL_NAME`, `ADDRESS`, `EMAILS`, `PHONES`, `LINKS`, `CURRENCY`, `ACTOR`, `FILES` |
|
||||
| Identificatori e relazioni | `UUID`, `RELATION`, `MORPH_RELATION` (vedi [Relazioni](/l/it/developers/extend/apps/data/relations)) |
|
||||
| Sistema | `TS_VECTOR` (vettore per la ricerca full-text, gestito dal server) |
|
||||
|
||||
I tipi compositi memorizzano più sotto-campi (ad es. `FULL_NAME` = nome + cognome; `CURRENCY` = `amountMicros` + `currencyCode`). `SELECT` e `MULTI_SELECT` richiedono un array `options` come nell’esempio sopra.
|
||||
|
||||
## Valori predefiniti
|
||||
|
||||
I valori predefiniti letterali devono essere racchiusi tra apici singoli **all'interno** della stringa — `defaultValue: "'Draft'"`, non `defaultValue: "Draft"`. Ecco perché il campo `status` sopra utilizza `` `'${PostCardStatus.DRAFT}'` ``.
|
||||
|
||||
+32
-16
@@ -14,26 +14,39 @@ my-twenty-app/
|
||||
default-role.ts # Permissions for logic functions
|
||||
constants/
|
||||
universal-identifiers.ts # Auto-generated UUIDs and metadata
|
||||
front-components/
|
||||
main-page.tsx # Welcome page component
|
||||
navigation-menu-items/
|
||||
main-page.navigation-menu-item.ts # Sidebar entry for the welcome page
|
||||
page-layouts/
|
||||
main-page.page-layout.ts # Standalone page hosting the component
|
||||
__tests__/
|
||||
setup-test.ts
|
||||
app-install.integration-test.ts
|
||||
.github/workflows/ci.yml # GitHub Actions
|
||||
public/ # Static assets
|
||||
vitest.config.ts # Test runner config
|
||||
application-config.test.ts # Unit test
|
||||
global-setup.ts # Integration test setup (sync + uninstall)
|
||||
schema.integration-test.ts # Integration test against a live server
|
||||
.github/workflows/
|
||||
ci.yml # Lint, typecheck, unit + integration tests
|
||||
cd.yml # Deploy + install on push to main
|
||||
public/
|
||||
logo.svg # Static assets
|
||||
vitest.config.ts # Integration test runner config
|
||||
vitest.unit.config.ts # Unit test runner config
|
||||
tsconfig.json, tsconfig.spec.json
|
||||
.nvmrc, .yarnrc.yml, .oxlintrc.json
|
||||
README.md, LLMS.md
|
||||
README.md, AGENTS.md, CLAUDE.md
|
||||
```
|
||||
|
||||
## File principali
|
||||
|
||||
| File / Cartella | Scopo |
|
||||
| ---------------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| `src/application-config.ts` | **Obbligatorio.** Il file di configurazione principale della tua app. |
|
||||
| `src/default-role.ts` | Ruolo predefinito che controlla a cosa possono accedere le tue funzioni logiche. |
|
||||
| `src/constants/universal-identifiers.ts` | UUID generati automaticamente e metadati (nome visualizzato, descrizione). |
|
||||
| `src/__tests__/` | Test di integrazione (setup + test di esempio). |
|
||||
| `public/` | Asset statici (immagini, font) serviti insieme alla tua app. |
|
||||
| File / Cartella | Scopo |
|
||||
| -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| `src/application-config.ts` | **Obbligatorio.** Il file di configurazione principale della tua app. |
|
||||
| `src/default-role.ts` | Ruolo predefinito che controlla a cosa possono accedere le tue funzioni logiche. |
|
||||
| `src/constants/universal-identifiers.ts` | UUID generati automaticamente e metadati (nome visualizzato, descrizione). |
|
||||
| `src/front-components/`, `src/navigation-menu-items/`, `src/page-layouts/` | Una pagina di benvenuto iniziale: un front component eseguito da un page layout autonomo, raggiungibile dalla sidebar. |
|
||||
| `src/__tests__/` | Un test unitario più un test di integrazione (con il relativo setup globale) che sincronizza l'app con un server reale. |
|
||||
| `public/` | Asset statici (immagini, font) serviti insieme alla tua app. |
|
||||
| `AGENTS.md` / `CLAUDE.md` | Linee guida per gli agenti di codice AI che lavorano sull'app. |
|
||||
|
||||
<Note>
|
||||
**L'organizzazione dei file dipende da te.** Le cartelle sopra sono convenzioni — l'SDK rileva le entità tramite analisi AST sulle chiamate a `export default defineEntity(...)` indipendentemente da dove si trova il file.
|
||||
@@ -47,15 +60,18 @@ Entrambi i pacchetti Twenty SDK devono essere inseriti sotto `devDependencies`,
|
||||
{
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"twenty-client-sdk": "^2.13.0",
|
||||
"twenty-sdk": "^2.13.0"
|
||||
"twenty-client-sdk": "2.20.0",
|
||||
"twenty-sdk": "2.20.0",
|
||||
"twenty-ui": "1.0.0-alpha.1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Lo scaffolder blocca `twenty-sdk` e `twenty-client-sdk` alla propria versione — mantieni i due allineati durante l'aggiornamento.
|
||||
|
||||
* **`twenty-sdk`** fornisce la CLI `twenty` e gli strumenti di build/scaffolding. Viene eseguito solo in fase di sviluppo e di build e non viene mai importato dal runtime dell'app pubblicata.
|
||||
* **`twenty-client-sdk`** *viene* importato dal codice della tua app (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`), ma Twenty lo fornisce a runtime: le funzioni di logica lo ricevono da un layer SDK generato e i componenti di front-end lo risolvono da moduli forniti dal server. La copia installata viene utilizzata solo per il type checking e per la build al momento del deploy, quindi non è mai necessario includerla nel bundle distribuito.
|
||||
|
||||
Mantenere uno qualsiasi dei pacchetti sotto `dependencies` lo inserisce nel bundle di runtime dell'app installata, dove rappresenta solo zavorra. `twenty build` emette un avviso quando uno dei due è ancora elencato sotto `dependencies`.
|
||||
Mantenere uno qualsiasi dei pacchetti sotto `dependencies` lo inserisce nel bundle di runtime dell'app installata, dove rappresenta solo zavorra. `twenty dev:build` emette un avviso quando uno dei due è ancora elencato sotto `dependencies`.
|
||||
|
||||
Aggiungi come di consueto le dipendenze di runtime proprie della tua app (librerie che le tue funzioni di logica importano effettivamente a runtime) sotto `dependencies`.
|
||||
|
||||
@@ -6,17 +6,17 @@ description: Crea la tua prima app Twenty in pochi minuti.
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
* **Node.js 24+** — [Scarica](https://nodejs.org/)
|
||||
* **Node.js 24.5+** — [Scarica](https://nodejs.org/)
|
||||
* **Yarn 4** — incluso con Node.js tramite Corepack. Abilitalo: `corepack enable`
|
||||
* **Docker** — [Scarica](https://www.docker.com/products/docker-desktop/). Necessario per eseguire un server Twenty locale. Salta se hai già Twenty in esecuzione altrove.
|
||||
|
||||
La creazione di un'app Twenty ha tre fasi. Lo strumento di scaffolding le combina in un unico comando per il percorso ottimale, ma ogni fase è un concetto distinto — quando qualcosa fallisce, sapere in quale fase ti trovi indica cosa correggere.
|
||||
|
||||
| Fase | Cosa fai | Strumento | Risultato |
|
||||
| ----------------------- | ------------------------------------------------------ | ----------------------------- | ---------------------------------- |
|
||||
| **1. Crea struttura** | Genera il codice sorgente dell'app | `npx create-twenty-app` | Un progetto TypeScript sul disco |
|
||||
| **2. Esegui un server** | Avvia un server Twenty con cui sincronizzare | Docker + `yarn twenty server` | Un'istanza Twenty in esecuzione |
|
||||
| **3. Sincronizza** | Sincronizza in tempo reale il tuo codice con il server | `yarn twenty dev` | Le tue modifiche compaiono nell'UI |
|
||||
| Fase | Cosa fai | Strumento | Risultato |
|
||||
| ----------------------- | ------------------------------------------------------ | ----------------------------------- | ---------------------------------- |
|
||||
| **1. Crea struttura** | Genera il codice sorgente dell'app | `npx create-twenty-app` | Un progetto TypeScript sul disco |
|
||||
| **2. Esegui un server** | Avvia un server Twenty con cui sincronizzare | Docker + `yarn twenty docker:start` | Un'istanza Twenty in esecuzione |
|
||||
| **3. Sincronizza** | Sincronizza in tempo reale il tuo codice con il server | `yarn twenty dev` | Le tue modifiche compaiono nell'UI |
|
||||
|
||||
---
|
||||
|
||||
@@ -28,7 +28,7 @@ Crea una nuova app dal modello:
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
```
|
||||
|
||||
Ti verrà chiesto un nome e una descrizione — premi **Invio** per usare i valori predefiniti. Questo genera un progetto TypeScript in `my-twenty-app/` con un `application-config.ts` iniziale, un ruolo predefinito, un workflow CI e un test di integrazione.
|
||||
Lo scaffolder è non interattivo: il nome della directory diventa il nome dell'app. Passa `--display-name` e `--description` per personalizzare i metadati generati (puoi anche modificarli in seguito in `src/constants/universal-identifiers.ts`). Questo genera un progetto TypeScript in `my-twenty-app/` con un `application-config.ts` iniziale, un ruolo predefinito, workflow CI/CD e un test di integrazione.
|
||||
|
||||
**Dopo questa fase:** hai il codice sorgente dell'app sulla tua macchina. Non è ancora in esecuzione — questa è la Fase 2.
|
||||
|
||||
@@ -38,28 +38,14 @@ Ti verrà chiesto un nome e una descrizione — premi **Invio** per usare i valo
|
||||
|
||||
La tua app ha bisogno di un server Twenty con cui sincronizzarsi. Il server è un'istanza Twenty completa — UI, API GraphQL, PostgreSQL — in esecuzione in locale su Docker. Il tuo codice locale carica le sue definizioni su quel server, che le rende visibili nell'UI.
|
||||
|
||||
Lo strumento di scaffolding ti propone di avviarne uno per te:
|
||||
Lo scaffolder avvia un'istanza per te: con Docker in esecuzione, scarica l'immagine `twentycrm/twenty-app-dev`, la avvia sulla porta `2020` e autentica la CLI sullo spazio di lavoro demo prepopolato (`tim@apple.dev`) — non è necessario effettuare l'accesso.
|
||||
|
||||
> **Vuoi configurare un'istanza locale di Twenty?**
|
||||
|
||||
* **Sì (consigliato)** — scarica l'immagine Docker `twentycrm/twenty-app-dev` e la avvia sulla porta `2020`. Assicurati prima che Docker sia in esecuzione.
|
||||
* **No** — scegli questa opzione se hai già un server Twenty a cui vuoi connetterti. Puoi collegarlo in seguito con `yarn twenty remote:add`.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Avviare l'istanza locale?" />
|
||||
</div>
|
||||
|
||||
Quando il server è attivo, si apre il browser per l'accesso. Usa l'account demo preconfigurato:
|
||||
|
||||
* **Email:** `tim@apple.dev`
|
||||
* **Password:** `tim@apple.dev`
|
||||
Per connetterti invece a un server Twenty esistente, passa `--url \<your-server-url>`. I server remoti eseguono l'autenticazione con OAuth: si apre un browser così puoi effettuare l'accesso e fare clic su **Authorize**, concedendo alla CLI l'accesso al tuo spazio di lavoro. (Puoi anche scegliere di utilizzare OAuth in locale con `--authentication-method oauth` — accedi con `tim@apple.dev` / `tim@apple.dev`.)
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/login.png" alt="Schermata di accesso di Twenty" />
|
||||
</div>
|
||||
|
||||
Fai clic su **Authorize** nella schermata successiva — questo concede alla CLI l'accesso al tuo spazio di lavoro.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/authorize.png" alt="Schermata di autorizzazione della CLI di Twenty" />
|
||||
</div>
|
||||
@@ -117,28 +103,32 @@ Fai clic su **View installed app** per vedere l'installazione nello spazio di la
|
||||
|
||||
### Sincronizzazione una tantum per CI e script
|
||||
|
||||
Passa `--once` per eseguire una singola build + sincronizzazione ed uscire — stessa pipeline, nessun watcher:
|
||||
Usa `plan` e `apply` per eseguire la stessa pipeline una volta, senza watcher:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev --once
|
||||
yarn twenty plan # preview the metadata changes without applying them
|
||||
yarn twenty apply # show the plan, then apply it
|
||||
```
|
||||
|
||||
| Comando | Comportamento | Quando usarlo |
|
||||
| ---------------------------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| `yarn twenty dev` | Monitora e risincronizza a ogni modifica. Rimane in esecuzione finché non lo interrompi. | Sviluppo locale interattivo. |
|
||||
| `yarn twenty dev --once` | Singola build + sincronizzazione, termina con codice `0` in caso di successo, `1` in caso di errore. | CI, hook pre-commit, agenti IA, flussi di lavoro scriptati. |
|
||||
| `yarn twenty dev --once --dry-run` | Crea e stampa le modifiche ai metadati **senza applicarle**. | Ispezionare quali modifiche verrebbero apportate da una sincronizzazione prima di confermarla. |
|
||||
| Comando | Comportamento | Quando usarlo |
|
||||
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| `yarn twenty dev` | Monitora e risincronizza a ogni modifica. Rimane in esecuzione finché non lo interrompi. | Sviluppo locale interattivo. |
|
||||
| `yarn twenty apply` | Singola build + sincronizzazione, termina con codice `0` in caso di successo, `1` in caso di errore. Richiede conferma per le modifiche distruttive (passa `--force` per saltarla). | CI, hook pre-commit, agenti IA, flussi di lavoro scriptati. |
|
||||
| `yarn twenty plan` | Crea e stampa le modifiche ai metadati **senza applicarle**. | Ispezionare quali modifiche verrebbero apportate da una sincronizzazione prima di confermarla. |
|
||||
|
||||
Entrambe le modalità richiedono un remoto autenticato. Vedi [Sincronizzazione e ripristino](/l/it/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) per maggiori informazioni su `--dry-run`.
|
||||
Tutte le modalità richiedono un remoto autenticato. Vedi [Sincronizzazione e ripristino](/l/it/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) per maggiori informazioni su `plan`.
|
||||
|
||||
<Note>
|
||||
`yarn twenty dev --once` e `yarn twenty dev --once --dry-run` sono alias deprecati di `yarn twenty apply` e `yarn twenty plan`.
|
||||
</Note>
|
||||
|
||||
### Opzioni della modalità di sviluppo
|
||||
|
||||
| Opzione | Descrizione |
|
||||
| ------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| `--once` | Esegui una build e una sincronizzazione una sola volta, quindi esci. |
|
||||
| `--dry-run` | Con `--once`, visualizza in anteprima le modifiche ai metadati senza applicarle. Non scrive nulla. |
|
||||
| `--debounceMs \<ms>` | Imposta il ritardo di debounce delle modifiche ai file in millisecondi (predefinito: `2000`). |
|
||||
| `--verbose` / `--debug` | Mostra log di build dettagliati, richieste di sincronizzazione e tracce di errore. |
|
||||
| Opzione | Descrizione |
|
||||
| ------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| `--force` | Applica modifiche distruttive (eliminazioni) senza conferma. |
|
||||
| `--debounceMs \<ms>` | Imposta il ritardo di debounce delle modifiche ai file in millisecondi (predefinito: `1000`). |
|
||||
| `--verbose` / `--debug` | Mostra log di build dettagliati, richieste di sincronizzazione e tracce di errore. |
|
||||
|
||||
## Cosa puoi creare
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ yarn twenty dev:add frontComponent
|
||||
| Vista | `yarn twenty dev:add view` | `src/views/\<name>.ts` |
|
||||
| Voce del menu di navigazione | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
|
||||
| Layout di pagina | `yarn twenty dev:add pageLayout` | `src/page-layouts/\<name>.ts` |
|
||||
| Scheda layout di pagina | `yarn twenty dev:add pageLayoutTab` | `src/page-layout-tabs/\<name>.ts` |
|
||||
| Voce del menu comandi | `yarn twenty dev:add commandMenuItem` | `src/command-menu-items/\<name>.ts` |
|
||||
| Campo vista | `yarn twenty dev:add viewField` | `src/view-fields/\<name>.ts` |
|
||||
| Provider di connessione | `yarn twenty dev:add connectionProvider` | `src/connection-providers/\<name>.ts` |
|
||||
|
||||
## Cosa genera lo scaffolder
|
||||
|
||||
|
||||
+2
-2
@@ -5,10 +5,10 @@ icon: wrench
|
||||
---
|
||||
|
||||
* **Errori di Docker** — Assicurati che Docker Desktop (o il demone) sia in esecuzione prima di `yarn twenty docker:start`. Il messaggio di errore mostrerà il comando di avvio corretto per il tuo sistema operativo.
|
||||
* **Versione di Node errata** — È necessaria la versione 24 o superiore. Verifica con `node -v`.
|
||||
* **Versione di Node errata** — Serve la 24.5+ (`engines.node: ^24.5.0`). Verifica con `node -v`.
|
||||
* **Manca Yarn 4** — Esegui `corepack enable`.
|
||||
* **Dipendenze danneggiate** — `rm -rf node_modules && yarn install`.
|
||||
* **Errori di `twenty-sdk` dopo l'aggiornamento alla v2.8.0** — è stato spostato da `dependencies` a `devDependencies` nella v2.8.0. Vedi [Struttura del progetto → Dipendenze](/l/it/developers/extend/apps/getting-started/project-structure#dependencies).
|
||||
* **`twenty build` mostra un avviso su `twenty-client-sdk` sotto `dependencies`** — viene fornito in fase di esecuzione da Twenty, quindi dovrebbe essere spostato in `devDependencies` insieme a `twenty-sdk`. Vedi [Struttura del progetto → Dipendenze](/l/it/developers/extend/apps/getting-started/project-structure#dependencies).
|
||||
* **`twenty dev:build` mostra un avviso su `twenty-client-sdk` sotto `dependencies`** — viene fornito in fase di esecuzione da Twenty, quindi dovrebbe essere spostato in `devDependencies` insieme a `twenty-sdk`. Vedi [Struttura del progetto → Dipendenze](/l/it/developers/extend/apps/getting-started/project-structure#dependencies).
|
||||
|
||||
Bloccato? Chiedi aiuto su [Discord di Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322).
|
||||
|
||||
@@ -13,7 +13,6 @@ export default defineCommandMenuItem({
|
||||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
label: 'Open Dashboard',
|
||||
shortLabel: 'Dashboard',
|
||||
icon: 'IconLayoutDashboard',
|
||||
isPinned: true,
|
||||
availabilityType: 'GLOBAL',
|
||||
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
|
||||
@@ -22,51 +21,23 @@ export default defineCommandMenuItem({
|
||||
|
||||
## Campi di configurazione
|
||||
|
||||
| Campo | Obbligatorio | Descrizione |
|
||||
| --------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `universalIdentifier` | Sì | ID univoco stabile per il comando |
|
||||
| `label` | Sì | Etichetta completa mostrata nel menu comandi (Cmd+K) |
|
||||
| `frontComponentUniversalIdentifier` | Sì | L'`universalIdentifier` del componente front-end che questo comando apre |
|
||||
| `shortLabel` | No | Etichetta breve visualizzata sul pulsante di azione rapida fissato |
|
||||
| `icon` | No | Nome dell'icona visualizzato accanto all'etichetta (ad es. `'IconBolt'`, `'IconSend'`) |
|
||||
| `isPinned` | No | Quando `true`, mostra il comando come pulsante di azione rapida nell'angolo in alto a destra della pagina |
|
||||
| `availabilityType` | No | Controlla dove compare il comando: `'GLOBAL'` (sempre disponibile), `'RECORD_SELECTION'` (solo quando sono selezionati dei record) o `'FALLBACK'` (mostrato quando nessun altro comando corrisponde) |
|
||||
| `availabilityObjectUniversalIdentifier` | No | Limita il comando alle pagine di uno specifico tipo di oggetto (ad es. solo sui record Company) |
|
||||
| `conditionalAvailabilityExpression` | No | Un'espressione booleana che controlla dinamicamente la visibilità (vedi sotto) |
|
||||
| Campo | Obbligatorio | Descrizione |
|
||||
| --------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `universalIdentifier` | Sì | ID univoco stabile per il comando |
|
||||
| `label` | Sì | Etichetta completa mostrata nel menu comandi (Cmd+K) |
|
||||
| `frontComponentUniversalIdentifier` | Sì | L'`universalIdentifier` del componente front-end che questo comando apre |
|
||||
| `shortLabel` | No | Etichetta breve visualizzata sul pulsante di azione rapida fissato |
|
||||
| `icon` | No | **Deprecato** — ignorato a favore dell'icona dell'applicazione; la build emette un avviso se impostato |
|
||||
| `isPinned` | No | Quando `true`, mostra il comando come pulsante di azione rapida nell'angolo in alto a destra della pagina |
|
||||
| `availabilityType` | No | Controlla dove compare il comando: `'GLOBAL'` (sempre disponibile), `'GLOBAL_OBJECT_CONTEXT'` (solo sulle pagine con un contesto oggetto — pagine indice e di record), `'RECORD_SELECTION'` (solo quando sono selezionati dei record) o `'FALLBACK'` (mostrato quando nessun altro comando corrisponde) |
|
||||
| `availabilityObjectUniversalIdentifier` | No | Limita il comando alle pagine di uno specifico tipo di oggetto (ad es. solo sui record Company) |
|
||||
| `conditionalAvailabilityExpression` | No | Un'espressione booleana che controlla dinamicamente la visibilità (vedi sotto) |
|
||||
|
||||
## Comandi headless
|
||||
|
||||
Un elemento del menu comandi abbinato a un [headless front component](/l/it/developers/extend/apps/layout/front-components#headless-vs-non-headless) è il modo idiomatico per distribuire un'azione con un clic: eseguire codice, navigare oppure confermare ed eseguire. La pagina Front Components tratta i [SDK Command components](/l/it/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) che gestiscono il pattern di action-and-unmount.
|
||||
|
||||
Un flusso tipico:
|
||||
|
||||
```tsx src/front-components/run-action.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { Command } from 'twenty-sdk/command';
|
||||
import { CoreApiClient } from 'twenty-sdk/clients';
|
||||
|
||||
const RunAction = () => {
|
||||
const execute = async () => {
|
||||
const client = new CoreApiClient();
|
||||
await client.mutation({
|
||||
createTask: {
|
||||
__args: { data: { title: 'Created by my app' } },
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return <Command execute={execute} />;
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
|
||||
name: 'run-action',
|
||||
description: 'Creates a task from the command menu',
|
||||
component: RunAction,
|
||||
isHeadless: true,
|
||||
});
|
||||
```
|
||||
Un flusso tipico: un componente headless renderizza `<Command execute={...} />` (vedi l'[esempio completo](/l/it/developers/extend/apps/layout/front-components#sdk-command-components)), e la voce di menu del comando lo punta:
|
||||
|
||||
```ts src/command-menu-items/run-action.command-menu-item.ts
|
||||
import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
@@ -74,7 +45,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
export default defineCommandMenuItem({
|
||||
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
|
||||
label: 'Run my action',
|
||||
icon: 'IconPlayerPlay',
|
||||
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
|
||||
});
|
||||
```
|
||||
|
||||
@@ -49,14 +49,13 @@ export default defineCommandMenuItem({
|
||||
universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345',
|
||||
shortLabel: 'Hello',
|
||||
label: 'Hello World',
|
||||
icon: 'IconBolt',
|
||||
isPinned: true,
|
||||
availabilityType: 'GLOBAL',
|
||||
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
|
||||
});
|
||||
```
|
||||
|
||||
Dopo la sincronizzazione con `yarn twenty dev` (o eseguendo una volta sola `yarn twenty dev --once`), l'azione rapida appare nell'angolo in alto a destra della pagina:
|
||||
Dopo la sincronizzazione con `yarn twenty dev` (o eseguendo una volta sola `yarn twenty apply`), l'azione rapida appare nell'angolo in alto a destra della pagina:
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/quick-action.png" alt="Pulsante di azione rapida nell'angolo in alto a destra" />
|
||||
@@ -88,11 +87,11 @@ I componenti front-end prevedono due modalità di rendering controllate dall'opz
|
||||
|
||||
```tsx src/front-components/sync-tracker.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { useRecordId, enqueueSnackbar } from 'twenty-sdk/front-component';
|
||||
import { useSelectedRecordIds, enqueueSnackbar } from 'twenty-sdk/front-component';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const SyncTracker = () => {
|
||||
const recordId = useRecordId();
|
||||
const [recordId] = useSelectedRecordIds();
|
||||
|
||||
useEffect(() => {
|
||||
enqueueSnackbar({ message: `Tracking record ${recordId}`, variant: 'info' });
|
||||
@@ -116,7 +115,7 @@ Poiché il componente restituisce `null`, Twenty evita di renderizzare un conten
|
||||
|
||||
Il pacchetto `twenty-sdk` fornisce quattro componenti di supporto Command progettati per i componenti front-end headless. Ogni componente esegue un'azione al montaggio, gestisce gli errori mostrando una notifica snackbar e smonta automaticamente il componente front-end al termine.
|
||||
|
||||
Importali da `twenty-sdk/command`:
|
||||
Importali da `twenty-sdk/front-component`:
|
||||
|
||||
* **`Command`** — Esegue una callback asincrona tramite la prop `execute`.
|
||||
* **`CommandLink`** — Naviga verso un percorso dell'app. Props: `to`, `params`, `queryParams`, `options`.
|
||||
@@ -127,8 +126,8 @@ Ecco un esempio completo di componente front-end headless che usa `Command` per
|
||||
|
||||
```tsx src/front-components/run-action.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { Command } from 'twenty-sdk/command';
|
||||
import { CoreApiClient } from 'twenty-sdk/clients';
|
||||
import { Command } from 'twenty-sdk/front-component';
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
|
||||
const RunAction = () => {
|
||||
const execute = async () => {
|
||||
@@ -160,7 +159,6 @@ import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
export default defineCommandMenuItem({
|
||||
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
|
||||
label: 'Run my action',
|
||||
icon: 'IconPlayerPlay',
|
||||
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
|
||||
});
|
||||
```
|
||||
@@ -169,7 +167,7 @@ E un esempio che usa `CommandModal` per chiedere conferma prima di eseguire:
|
||||
|
||||
```tsx src/front-components/delete-draft.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { CommandModal } from 'twenty-sdk/command';
|
||||
import { CommandModal } from 'twenty-sdk/front-component';
|
||||
|
||||
const DeleteDraft = () => {
|
||||
const execute = async () => {
|
||||
@@ -202,7 +200,7 @@ I componenti front vengono eseguiti lato browser in un Web Worker in sandbox, me
|
||||
|
||||
Una funzione logica dichiarata con `httpRouteTriggerSettings` è raggiungibile tramite HTTP al relativo percorso della route. Twenty inietta nel worker l'URL di base da cui vengono servite le tue funzioni come `TWENTY_FUNCTIONS_URL`, insieme al `TWENTY_APP_ACCESS_TOKEN` che autentica la chiamata. Non esiste ancora un client SDK dedicato per invocare le proprie funzioni, quindi chiamale con un semplice `fetch`:
|
||||
|
||||
> **Su Twenty Cloud, le funzioni logiche attivate tramite HTTP sono servite su un dominio dedicato per ogni workspace** in `https://\<your-workspace-subdomain>.twenty.com\<path>` — questo è esattamente ciò in cui viene risolto `TWENTY_FUNCTIONS_URL`. Per i chiamanti esterni, copia l’URL esatto dalle impostazioni del **trigger HTTP** della funzione o dalla scheda **Settings** dell’applicazione.
|
||||
> **Su Twenty Cloud, le funzioni logiche attivate tramite HTTP sono servite su un dominio dedicato per ogni workspace** in `https://\<your-workspace-subdomain>.withtwenty.com\<path>` — questo è esattamente ciò in cui viene risolto `TWENTY_FUNCTIONS_URL`. Per i chiamanti esterni, copia l’URL esatto dalle impostazioni del **trigger HTTP** della funzione o dalla scheda **Settings** dell’applicazione.
|
||||
|
||||
<Warning>
|
||||
La route legacy della funzione `/s/` è **deprecata** e sarà **disattivata il 2026-07-24**. Usa invece `TWENTY_FUNCTIONS_URL` (sopra) e migra tutti gli URL `/s/` hard-coded prima di quella data. La route `/s/` rimane disponibile per il self-hosting.
|
||||
@@ -212,7 +210,7 @@ Un front component headless può eseguire la chiamata al mount tramite il compon
|
||||
|
||||
```tsx src/front-components/sync-prs.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { Command } from 'twenty-sdk/command';
|
||||
import { Command } from 'twenty-sdk/front-component';
|
||||
|
||||
const SyncPrs = () => {
|
||||
const execute = async () => {
|
||||
@@ -316,13 +314,13 @@ All'interno del tuo componente, usa gli hook dell'SDK per accedere all'utente co
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import {
|
||||
useUserId,
|
||||
useRecordId,
|
||||
useSelectedRecordIds,
|
||||
useFrontComponentId,
|
||||
} from 'twenty-sdk/front-component';
|
||||
|
||||
const RecordInfo = () => {
|
||||
const userId = useUserId();
|
||||
const recordId = useRecordId();
|
||||
const [recordId] = useSelectedRecordIds();
|
||||
const componentId = useFrontComponentId();
|
||||
|
||||
return (
|
||||
@@ -405,12 +403,11 @@ Ecco un esempio che usa l'API host per mostrare una snackbar e chiudere il panne
|
||||
|
||||
```tsx src/front-components/archive-record.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { useRecordId } from 'twenty-sdk/front-component';
|
||||
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
|
||||
import { CoreApiClient } from 'twenty-sdk/clients';
|
||||
import { enqueueSnackbar, closeSidePanel, useSelectedRecordIds } from 'twenty-sdk/front-component';
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
|
||||
const ArchiveRecord = () => {
|
||||
const recordId = useRecordId();
|
||||
const [recordId] = useSelectedRecordIds();
|
||||
|
||||
const handleArchive = async () => {
|
||||
const client = new CoreApiClient();
|
||||
@@ -451,10 +448,10 @@ export default defineFrontComponent({
|
||||
Usa `useSelectedRecordIds()` per gestire più record selezionati. Questo è utile per operazioni in blocco:
|
||||
|
||||
```tsx src/front-components/bulk-export.tsx
|
||||
import { defineFrontComponent, numberOfSelectedRecords } from 'twenty-sdk/define';
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { useSelectedRecordIds } from 'twenty-sdk/front-component';
|
||||
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
|
||||
import { CoreApiClient } from 'twenty-sdk/clients';
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
|
||||
const BulkExport = () => {
|
||||
const selectedRecordIds = useSelectedRecordIds();
|
||||
@@ -492,12 +489,19 @@ export default defineFrontComponent({
|
||||
name: 'bulk-export',
|
||||
description: 'Export selected records',
|
||||
component: BulkExport,
|
||||
command: {
|
||||
universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
|
||||
label: 'Bulk Export',
|
||||
availabilityType: 'RECORD_SELECTION',
|
||||
conditionalAvailabilityExpression: numberOfSelectedRecords > 0,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Mostralo con una [voce del menu dei comandi](/l/it/developers/extend/apps/layout/command-menu-items) limitata alle selezioni di record:
|
||||
|
||||
```ts src/command-menu-items/bulk-export.command-menu-item.ts
|
||||
import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
|
||||
export default defineCommandMenuItem({
|
||||
universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
|
||||
label: 'Bulk Export',
|
||||
availabilityType: 'RECORD_SELECTION',
|
||||
frontComponentUniversalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901',
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ export default defineNavigationMenuItem({
|
||||
|
||||
* `position` controlla l’ordinamento nella barra laterale.
|
||||
|
||||
* L'enum contiene anche `NavigationMenuItemType.RECORD`, utilizzato internamente per i record preferiti creati dall'utente — non è utilizzabile da un app manifest (non esiste alcun campo per fare riferimento a un record).
|
||||
|
||||
* `icon` e `color` sono opzionali e personalizzano l’aspetto della voce.
|
||||
|
||||
* `folderUniversalIdentifier` è inoltre disponibile su qualsiasi elemento per annidarlo all’interno di un genitore di tipo `FOLDER`.
|
||||
|
||||
@@ -33,17 +33,32 @@ export default defineView({
|
||||
## Punti chiave
|
||||
|
||||
* `objectUniversalIdentifier` specifica a quale oggetto si applica questa vista. Può essere un oggetto personalizzato che hai definito o un oggetto Twenty standard.
|
||||
* `key` determina il tipo di vista — `ViewKey.INDEX` è la vista elenco principale per l'oggetto.
|
||||
* `key: ViewKey.INDEX` contrassegna la vista come vista elenco principale dell'oggetto (quella che un elemento di navigazione `OBJECT` apre).
|
||||
* `fields` controlla quali colonne compaiono e in quale ordine. Ogni campo fa riferimento a un `fieldMetadataUniversalIdentifier`.
|
||||
* Puoi anche definire `filters`, `filterGroups`, `groups` e `fieldGroups` per configurazioni più avanzate.
|
||||
* Puoi anche definire `filters`, `filterGroups`, `sorts`, `groups` e `fieldGroups` per configurazioni più avanzate.
|
||||
* `position` controlla l'ordinamento quando esistono più viste per lo stesso oggetto.
|
||||
|
||||
## Proprietà opzionali
|
||||
|
||||
| Proprietà | Valori | Descrizione |
|
||||
| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `type` | `ViewType.TABLE` (predefinito), `ViewType.KANBAN`, `ViewType.CALENDAR` | Come sono disposti i record. (`FIELDS_WIDGET` / `TABLE_WIDGET` esistono anche ma sono usati internamente dai widget di layout di pagina.) |
|
||||
| `visibility` | `ViewVisibility.WORKSPACE` (predefinito), `ViewVisibility.UNLISTED` | Se la vista è elencata per l'intero workspace o nascosta dai selettori. |
|
||||
| `openRecordIn` | `ViewOpenRecordIn.SIDE_PANEL` (predefinito), `ViewOpenRecordIn.RECORD_PAGE` | Dove l'apertura di un record con un clic lo visualizza. |
|
||||
| `sorts` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | Ordine di ordinamento predefinito. |
|
||||
| `isCompact` | `boolean` | Visualizzazione compatta delle righe. |
|
||||
| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | Raggruppa i record (ad es. colonne kanban) per un campo. |
|
||||
| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | Aggregazioni e dimensionamento delle colonne kanban. |
|
||||
| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | Viste calendario: layout e campo data che posiziona i record. |
|
||||
|
||||
Tutti gli enum sopra sono esportati da `twenty-sdk/define`.
|
||||
|
||||
## Filtri
|
||||
|
||||
Una vista può essere fornita con filtri preapplicati. Ogni filtro ha tre coordinate: il **campo** che viene filtrato, l'**operando** (come confrontare) e il **valore** (con cosa confrontare). Tutti e tre devono allinearsi: l'uso di un operando che non si applica a un tipo di campo verrà rifiutato al momento della sincronizzazione.
|
||||
|
||||
```ts
|
||||
import { ViewFilterOperand } from 'twenty-shared/types';
|
||||
import { ViewFilterOperand } from 'twenty-sdk/define';
|
||||
|
||||
filters: [
|
||||
{
|
||||
|
||||
@@ -51,8 +51,12 @@ export default defineLogicFunction({
|
||||
```
|
||||
|
||||
Tipi di trigger disponibili:
|
||||
* **httpRoute**: Espone la tua funzione su un percorso e metodo HTTP **sotto l'endpoint `/s/`**:
|
||||
> ad es. `path: '/post-card/create'` è invocabile su `https://your-twenty-server.com/s/post-card/create`
|
||||
* **httpRoute**: Espone la tua funzione su un percorso HTTP e un metodo al **URL di base delle funzioni del tuo workspace** — il valore Twenty inietta come `TWENTY_FUNCTIONS_URL` (su Twenty Cloud, un dominio dedicato per workspace
|
||||
> ad es. `path: '/post-card/create'` è invocabile su `https://your-workspace.withtwenty.com/post-card/create`
|
||||
|
||||
<Warning>
|
||||
Il prefisso tradizionale `/s/` (`https://your-twenty-server.com/s/post-card/create`) è **deprecato su Twenty Cloud** e sarà disattivato il **2026-07-24**. Rimane disponibile per le istanze locali e self-hosted che non configurano un dominio di funzioni isolate — usa `TWENTY_FUNCTIONS_URL` quando è impostato, e torna a `\<server-url>/s/\<path>` altrimenti.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
Per richiamare, da un componente front-end (headless), una funzione logica attivata da una rotta, vedi [Chiamare una funzione logica](/l/it/developers/extend/apps/layout/front-components#calling-a-logic-function).
|
||||
|
||||
@@ -42,7 +42,7 @@ Una funzione logica sceglie uno o più trigger — ogni voce qui sotto è un cam
|
||||
|
||||
| Scatenante | Quando viene eseguito | Impostazione |
|
||||
| ----------------------- | --------------------------------------------------------------------- | ------------------------------- |
|
||||
| **Route HTTP** | Una richiesta raggiunge il tuo endpoint `/s/\<path>` | `httpRouteTriggerSettings` |
|
||||
| **Route HTTP** | Una richiesta colpisce l'URL pubblico della tua funzione | `httpRouteTriggerSettings` |
|
||||
| **Cron** | Viene soddisfatta un'espressione CRON | `cronTriggerSettings` |
|
||||
| **Evento database** | Un record dello spazio di lavoro viene creato, aggiornato o eliminato | `databaseEventTriggerSettings` |
|
||||
| **Strumento AI** | Una funzionalità AI di Twenty decide di chiamare la tua funzione | `toolTriggerSettings` |
|
||||
|
||||
@@ -4,7 +4,25 @@ description: Comandi di `yarn twenty` per eseguire funzioni, eseguire lo streami
|
||||
icon: terminal
|
||||
---
|
||||
|
||||
Oltre a `dev`, `dev:build`, `dev:add` e `dev:typecheck`, la CLI `yarn twenty` fornisce comandi per eseguire funzioni, visualizzare i log e gestire le installazioni delle app.
|
||||
La CLI `yarn twenty` è la tua interfaccia per tutto ciò che riguarda le app. Elenco completo dei comandi:
|
||||
|
||||
| Comando | Cosa fa | Documentato in |
|
||||
| ----------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| `dev` | Monitora i file sorgente e sincronizza in tempo reale le modifiche | [Guida rapida](/l/it/developers/extend/apps/getting-started/quick-start) |
|
||||
| `piano` | Visualizza in anteprima le modifiche ai metadati senza applicarle | [Sincronizzazione e ripristino](/l/it/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) |
|
||||
| `applica` | Applica le modifiche ai metadati dopo aver mostrato il piano | [Sincronizzazione e ripristino](/l/it/developers/extend/apps/operations/sync-and-recovery) |
|
||||
| `dev:build` | Compila l'app e genera il client API (`--tarball` per creare un `.tgz`) | [Pubblicazione](/l/it/developers/extend/apps/operations/publishing) |
|
||||
| `dev:typecheck` | Esegui il controllo dei tipi TypeScript | [Test](/l/it/developers/extend/apps/operations/testing) |
|
||||
| Crea l'impalcatura per una nuova entità | Crea l'impalcatura per una nuova entità | [Scaffolding](/l/it/developers/extend/apps/getting-started/scaffolding) |
|
||||
| `dev:generate-client` | Rigenera il client API tipizzato | questa pagina |
|
||||
| `dev:function:exec` / `dev:function:logs` | Esegui le funzioni e trasmetti in streaming i relativi log | questa pagina |
|
||||
| `dev:translations-extract` | Estrai le stringhe traducibili nei cataloghi in `locales/` | [Traduzioni](/l/it/developers/extend/apps/translations/overview) |
|
||||
| `dev:catalog-sync` | Attiva la sincronizzazione del catalogo del marketplace | [Pubblicazione](/l/it/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) |
|
||||
| `app:publish` / `app:install` / `app:uninstall` | Ciclo di vita delle release | [Pubblicazione](/l/it/developers/extend/apps/operations/publishing) e questa pagina |
|
||||
| `docker:*` | Gestisci il container del server Twenty locale | [Server locale](/l/it/developers/extend/apps/getting-started/local-server) |
|
||||
| `remote:*` | Gestisci le connessioni al server | questa pagina |
|
||||
|
||||
Ogni comando accetta `-r, --remote \<name>` per indirizzarsi a un remote specifico invece di quello predefinito.
|
||||
|
||||
## Esecuzione delle funzioni (`yarn twenty dev:function:exec`)
|
||||
|
||||
@@ -20,8 +38,9 @@ yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
|
||||
# Pass a JSON payload
|
||||
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
|
||||
|
||||
# Execute the post-install function
|
||||
# Execute the install hooks
|
||||
yarn twenty dev:function:exec --postInstall
|
||||
yarn twenty dev:function:exec --preInstall
|
||||
```
|
||||
|
||||
## Visualizzazione dei log delle funzioni (`yarn twenty dev:function:logs`)
|
||||
@@ -100,6 +119,12 @@ yarn twenty remote:list
|
||||
|
||||
# Set the active remote
|
||||
yarn twenty remote:use <name>
|
||||
|
||||
# Check that the active remote's authentication is still valid
|
||||
yarn twenty remote:status
|
||||
|
||||
# Remove a remote
|
||||
yarn twenty remote:remove <name>
|
||||
```
|
||||
|
||||
Le tue credenziali sono archiviate in `~/.twenty/config.json`.
|
||||
|
||||
@@ -229,7 +229,7 @@ yarn twenty dev:catalog-sync
|
||||
# yarn twenty dev:catalog-sync --remote production
|
||||
```
|
||||
|
||||
I metadati visualizzati nel marketplace provengono dalla configurazione `defineApplication()` — campi come `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` e `termsUrl`.
|
||||
I metadati mostrati nel marketplace provengono dalla configurazione di `defineApplication()` — vedi [Metadati del marketplace](#marketplace-metadata) sopra.
|
||||
|
||||
<Note>
|
||||
Se la tua app non definisce un `aboutDescription` in `defineApplication()`, il marketplace userà automaticamente il `README.md` del tuo pacchetto su npm come contenuto della pagina Informazioni. Questo significa che puoi mantenere un unico README sia per npm sia per il marketplace di Twenty. Se desideri una descrizione diversa nel marketplace, imposta esplicitamente `aboutDescription`.
|
||||
|
||||
@@ -15,33 +15,44 @@ Per l’iterazione locale quotidiana vuoi quasi sempre `yarn twenty dev`. Il dep
|
||||
| Vuoi… | Comando | Note |
|
||||
| ----------------------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Iterare in locale con sincronizzazione in tempo reale | `yarn twenty dev` | Monitora i file e sincronizza a ogni modifica. |
|
||||
| Sincronizzare una volta e uscire (CI, script, hook) | `yarn twenty dev --once` | Esegue una build + sincronizzazione, poi termina. |
|
||||
| Visualizzare in anteprima le modifiche **senza applicarle** | `yarn twenty dev --once --dry-run` | Calcola e stampa il diff; non scrive nulla. |
|
||||
| Sincronizzare una volta e uscire (CI, script, hook) | `yarn twenty apply` | Esegue una build + sincronizzazione, poi termina. Aggiungi `--force` per saltare la conferma delle modifiche distruttive. |
|
||||
| Visualizzare in anteprima le modifiche **senza applicarle** | `yarn twenty plan` | Calcola e stampa il diff; non scrive nulla. |
|
||||
| Rimuovere l'app dallo spazio di lavoro | `yarn twenty app:uninstall` | Aggiungi `--yes` per saltare il prompt. |
|
||||
| Inviare un tarball a un server | `yarn twenty app:publish --private` | Richiede una versione di `package.json` **strettamente superiore** — vedi [Publishing](/l/it/developers/extend/apps/operations/publishing). |
|
||||
| Pubblicare nel marketplace (npm) | `yarn twenty app:publish` | — |
|
||||
| Installare / aggiornare una versione distribuita | `yarn twenty app:install` | Installa la versione attualmente distribuita. |
|
||||
| Pulire il server locale e ripartire da zero | `yarn twenty docker:reset` | Elimina **tutti** i dati locali — ultima risorsa. |
|
||||
|
||||
<Note>
|
||||
`yarn twenty dev --once` e `yarn twenty dev --once --dry-run` funzionano ancora come alias deprecati per `yarn twenty apply` e `yarn twenty plan`.
|
||||
</Note>
|
||||
|
||||
### La sincronizzazione locale non richiede un incremento di versione
|
||||
|
||||
La regola della `version` strettamente crescente (`VERSION_ALREADY_EXISTS` in fase di deploy, `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` in fase di installazione) si applica a **`app:publish` / `app:install`** — il percorso di release. `yarn twenty dev` sincronizza il tuo manifest in-place e non richiede mai una modifica di versione, quindi non devi toccare `package.json` per iterare. Se ti ritrovi ad aumentare la versione per testare una modifica locale, stai usando il percorso di release quando invece vuoi il ciclo di sviluppo.
|
||||
|
||||
## Lettura dell'output di sincronizzazione
|
||||
|
||||
Ogni sincronizzazione stampa le modifiche ai metadati che ha applicato (o che applicherebbe, con `--dry-run`):
|
||||
Ogni sincronizzazione stampa le modifiche ai metadati che ha applicato (o che applicherebbe, con `plan`), in stile Terraform — un blocco per entità con i relativi attributi, quindi una riga di riepilogo:
|
||||
|
||||
```text filename="Terminal"
|
||||
Metadata changes: 2 created, 1 updated, 1 deleted
|
||||
created objectMetadata rocket
|
||||
created fieldMetadata timelineActivities
|
||||
updated fieldMetadata launchedAt
|
||||
deleted pageLayout legacyTab
|
||||
✓ Synced
|
||||
# objectMetadata "rocket" will be created
|
||||
+ icon = "IconRocket"
|
||||
+ labelSingular = "Rocket"
|
||||
+ ...
|
||||
|
||||
# fieldMetadata "launchedAt" will be updated
|
||||
~ isNullable = false -> true
|
||||
|
||||
Plan: 2 to add, 1 to change, 1 to destroy.
|
||||
|
||||
✓ Synced My App (4 files)
|
||||
```
|
||||
|
||||
Questo è il tuo primo strumento diagnostico: ti dice esattamente quali oggetti, campi e layout sono cambiati, così puoi confermare che una sincronizzazione ha fatto ciò che ti aspettavi prima di controllare l'interfaccia utente (UI).
|
||||
|
||||
Le modifiche distruttive (`to destroy`) sono elencate con ciò che eliminano (ad es. `objectMetadata "auditNote" — drops the table and all its rows`) e richiedono una conferma interattiva, oppure `--force` negli script.
|
||||
|
||||
Quando una sincronizzazione fallisce su una singola entità, l'errore indica l'entità in questione e il suo `universalIdentifier`, per esempio:
|
||||
|
||||
```text
|
||||
@@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337)
|
||||
|
||||
Usa quell'identificatore per trovare l'entità nel tuo manifest (e, se necessario, nello spazio di lavoro) invece di indovinare quale sia in conflitto.
|
||||
|
||||
## Anteprima delle modifiche (dry run)
|
||||
## Anteprima delle modifiche (plan)
|
||||
|
||||
`yarn twenty dev --once --dry-run` crea il tuo manifest, chiede al server il piano di migrazione e lo stampa — **senza applicare nulla**. È il modo sicuro per rispondere a "cosa cambierebbe questa sincronizzazione?" prima di impegnarti ad applicarla.
|
||||
`yarn twenty plan` crea il tuo manifest, chiede al server il piano di migrazione e lo stampa — **senza applicare nulla**. È il modo sicuro per rispondere a "cosa cambierebbe questa sincronizzazione?" prima di impegnarti ad applicarla.
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev --once --dry-run
|
||||
yarn twenty plan
|
||||
```
|
||||
|
||||
```text filename="Terminal"
|
||||
Building manifest...
|
||||
Computing metadata diff (dry run, nothing will be applied)...
|
||||
Metadata changes: 1 created, 1 updated
|
||||
created fieldMetadata timelineActivities
|
||||
updated objectMetadata rocket
|
||||
✓ Dry run complete for My App — no changes were applied
|
||||
Computing metadata plan (read-only, nothing will be applied)...
|
||||
|
||||
# fieldMetadata "timelineActivities" will be created
|
||||
+ ...
|
||||
|
||||
Plan: 1 to add, 1 to change, 0 to destroy.
|
||||
|
||||
✓ Plan complete for My App — no changes were applied
|
||||
```
|
||||
|
||||
Un dry run:
|
||||
Un piano:
|
||||
|
||||
* **Non scrive nulla** — nessuna migrazione dei metadati, nessun aggiornamento del record dell'applicazione, nessuna modifica ai ruoli/schede predefiniti e nessuna generazione del client API.
|
||||
* Restituisce lo **stesso diff** che una sincronizzazione reale applicherebbe, così puoi esaminare in anticipo le entità create/aggiornate/eliminate.
|
||||
* È utile prima di una modifica rischiosa, quando si rivede una modifica generata da un'IA o in uno script che deve fallire se sta per essere applicata una modifica imprevista.
|
||||
|
||||
<Note>
|
||||
Un dry run mostra in anteprima solo le modifiche ai **metadati** e richiede che l'app sia stata sincronizzata almeno una volta (così lo spazio di lavoro la conosce). Se lo esegui su un'app che non è mai stata sincronizzata, il server segnala che l'app non è installata — esegui prima `yarn twenty dev` una volta.
|
||||
Un piano mostra in anteprima solo le modifiche ai **metadati** e richiede che l'app sia stata sincronizzata almeno una volta (così lo spazio di lavoro la conosce). Se lo esegui su un'app che non è mai stata sincronizzata, il server segnala che l'app non è installata — esegui prima `yarn twenty dev` una volta.
|
||||
</Note>
|
||||
|
||||
## Scala di ripristino
|
||||
|
||||
Quando i metadati locali sembrano errati, procedi in quest'ordine e fermati non appena ti sblocchi. Ogni passaggio è più invasivo del precedente.
|
||||
|
||||
1. **Nuova sincronizzazione.** Esegui di nuovo `yarn twenty dev --once`. Le sincronizzazioni sono idempotenti — rieseguire un manifest pulito è sicuro e spesso risolve un problema temporaneo.
|
||||
2. **Visualizza in anteprima il piano.** Esegui `yarn twenty dev --once --dry-run` per vedere esattamente cosa intende cambiare la prossima sincronizzazione, senza applicarlo.
|
||||
1. **Nuova sincronizzazione.** Esegui di nuovo `yarn twenty apply`. Le sincronizzazioni sono idempotenti — rieseguire un manifest pulito è sicuro e spesso risolve un problema temporaneo.
|
||||
2. **Visualizza in anteprima il piano.** Esegui `yarn twenty plan` per vedere esattamente cosa intende cambiare la prossima sincronizzazione, senza applicarlo.
|
||||
3. **Leggi l'errore nominale.** Se una sincronizzazione fallisce, annota il tipo di metadato e lo `universalIdentifier` nel messaggio (vedi sopra) e individua quell'entità nel tuo manifest. Un conflitto di solito indica un identificatore duplicato o riutilizzato.
|
||||
4. **Disinstalla e reinstalla.** `yarn twenty app:uninstall`, poi sincronizza di nuovo (`yarn twenty dev`). Questo ricostruisce i metadati dell'app partendo da zero, lasciando intatto il resto del tuo spazio di lavoro.
|
||||
5. **Ripristino completo (ultima risorsa).** `yarn twenty docker:reset`, poi esegui di nuovo il seeding e la sincronizzazione.
|
||||
|
||||
@@ -78,6 +78,13 @@ Crea un `vitest.config.ts` alla radice della tua app:
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
|
||||
const TWENTY_API_KEY = process.env.TWENTY_API_KEY ?? '<the pre-seeded local dev key>';
|
||||
|
||||
// Make env vars available to globalSetup (test.env only applies to workers)
|
||||
process.env.TWENTY_API_URL = TWENTY_API_URL;
|
||||
process.env.TWENTY_API_KEY = TWENTY_API_KEY;
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tsconfigPaths({
|
||||
@@ -88,66 +95,74 @@ export default defineConfig({
|
||||
test: {
|
||||
testTimeout: 120_000,
|
||||
hookTimeout: 120_000,
|
||||
fileParallelism: false,
|
||||
include: ['src/**/*.integration-test.ts'],
|
||||
setupFiles: ['src/__tests__/setup-test.ts'],
|
||||
globalSetup: ['src/__tests__/global-setup.ts'],
|
||||
env: {
|
||||
TWENTY_API_URL: 'http://localhost:2020',
|
||||
TWENTY_API_KEY: 'your-api-key',
|
||||
TWENTY_API_URL,
|
||||
TWENTY_API_KEY,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Crea un file di setup che verifichi che il server sia raggiungibile prima dell'esecuzione dei test:
|
||||
Crea un file di configurazione globale che verifichi che il server sia raggiungibile, scriva una configurazione di test per l'SDK (`~/.twenty/config.test.json`) e sincronizzi l'app prima dell'esecuzione dei test:
|
||||
|
||||
```ts src/__tests__/setup-test.ts
|
||||
```ts src/__tests__/global-setup.ts
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { beforeAll } from 'vitest';
|
||||
|
||||
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
|
||||
const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
|
||||
import { appDevOnce, appUninstall } from 'twenty-sdk/cli';
|
||||
|
||||
const APP_PATH = process.cwd();
|
||||
const CONFIG_DIR = path.join(os.homedir(), '.twenty');
|
||||
|
||||
export async function setup() {
|
||||
const apiUrl = process.env.TWENTY_API_URL!;
|
||||
const apiKey = process.env.TWENTY_API_KEY!;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Verify the server is running
|
||||
const response = await fetch(`${TWENTY_API_URL}/healthz`);
|
||||
|
||||
const response = await fetch(`${apiUrl}/healthz`);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Twenty server is not reachable at ${TWENTY_API_URL}. ` +
|
||||
'Start the server before running integration tests.',
|
||||
);
|
||||
throw new Error(`Twenty server is not reachable at ${apiUrl}.`);
|
||||
}
|
||||
|
||||
// Write a temporary config for the SDK
|
||||
fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
|
||||
|
||||
// Write the SDK's test config (the CLI reads config.test.json when NODE_ENV=test)
|
||||
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(TEST_CONFIG_DIR, 'config.json'),
|
||||
path.join(CONFIG_DIR, 'config.test.json'),
|
||||
JSON.stringify({
|
||||
remotes: {
|
||||
local: {
|
||||
apiUrl: process.env.TWENTY_API_URL,
|
||||
apiKey: process.env.TWENTY_API_KEY,
|
||||
},
|
||||
},
|
||||
remotes: { local: { apiUrl, apiKey } },
|
||||
defaultRemote: 'local',
|
||||
}, null, 2),
|
||||
);
|
||||
});
|
||||
|
||||
// Start from a clean slate, then sync the app
|
||||
await appUninstall({ appPath: APP_PATH }).catch(() => {});
|
||||
|
||||
const result = await appDevOnce({ appPath: APP_PATH });
|
||||
if (!result.success) {
|
||||
throw new Error(`Dev sync failed: ${result.error?.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function teardown() {
|
||||
await appUninstall({ appPath: APP_PATH });
|
||||
}
|
||||
```
|
||||
|
||||
## API programmatiche dell'SDK
|
||||
|
||||
Il sottopercorso `twenty-sdk/cli` esporta funzioni che puoi chiamare direttamente dal codice di test:
|
||||
|
||||
| Funzione | Descrizione |
|
||||
| -------------- | ----------------------------------------------- |
|
||||
| `appBuild` | Compila l'app e, opzionalmente, crea un tarball |
|
||||
| `appDeploy` | Carica un tarball sul server |
|
||||
| `appInstall` | Installa l'app nello spazio di lavoro attivo |
|
||||
| `appUninstall` | Disinstalla l'app dallo spazio di lavoro attivo |
|
||||
| Funzione | Descrizione |
|
||||
| -------------- | ---------------------------------------------------------------- |
|
||||
| `appBuild` | Compila l'app e, opzionalmente, crea un tarball |
|
||||
| `appDeploy` | Carica un tarball sul server |
|
||||
| `appDevOnce` | Compila e sincronizza l'app una volta (come `yarn twenty apply`) |
|
||||
| `appInstall` | Installa l'app nello spazio di lavoro attivo |
|
||||
| `appUninstall` | Disinstalla l'app dallo spazio di lavoro attivo |
|
||||
|
||||
Ogni funzione restituisce un oggetto risultato con `success: boolean` e `data` oppure `error`.
|
||||
|
||||
@@ -238,64 +253,10 @@ Puoi anche eseguire il controllo dei tipi sulla tua app senza eseguire i test:
|
||||
yarn twenty dev:typecheck
|
||||
```
|
||||
|
||||
Questo esegue `tsc --noEmit` e riporta eventuali errori di tipo.
|
||||
Questo esegue `tsc --noEmit` contro il `tsconfig.json` della tua app e riporta eventuali errori di tipo. Le app generate dallo scaffolder includono anche uno script `yarn typecheck` che copre anche i file di test (`tsconfig.spec.json`).
|
||||
|
||||
## CI con GitHub Actions
|
||||
|
||||
Lo strumento di scaffolding genera un workflow GitHub Actions pronto all'uso in `.github/workflows/ci.yml`. Esegue automaticamente i test di integrazione a ogni push su `main` e sulle pull request.
|
||||
Lo strumento di scaffolding genera un workflow pronto all'uso in `.github/workflows/ci.yml`. A ogni push su `main` e a ogni pull request, avvia un server Twenty effimero nel runner (tramite l'azione `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`), quindi esegue `yarn lint`, `yarn typecheck`, `yarn test:unit` e `yarn test` con `TWENTY_API_URL` / `TWENTY_API_KEY` che puntano a quel server. Non sono necessari secret e puoi fissare la versione del server tramite la variabile di ambiente `TWENTY_VERSION` in cima al workflow.
|
||||
|
||||
Il workflow:
|
||||
|
||||
1. Esegue il checkout del tuo codice
|
||||
2. Avvia un server Twenty temporaneo utilizzando l'azione `twentyhq/twenty/.github/actions/spawn-twenty-docker-image`
|
||||
3. Installa le dipendenze con `yarn install --immutable`
|
||||
4. Esegue `yarn test` con `TWENTY_API_URL` e `TWENTY_API_KEY` iniettati dagli output dell'azione
|
||||
|
||||
```yaml .github/workflows/ci.yml
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request: {}
|
||||
|
||||
env:
|
||||
TWENTY_VERSION: latest
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Spawn Twenty instance
|
||||
id: twenty
|
||||
uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
|
||||
with:
|
||||
twenty-version: ${{ env.TWENTY_VERSION }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Enable Corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Run integration tests
|
||||
run: yarn test
|
||||
env:
|
||||
TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
|
||||
TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
|
||||
```
|
||||
|
||||
Non è necessario configurare alcun secret — l'azione `spawn-twenty-docker-image` avvia un server Twenty effimero direttamente nel runner e fornisce i dettagli di connessione. Il secret `GITHUB_TOKEN` è fornito automaticamente da GitHub.
|
||||
|
||||
Per fissare una versione specifica di Twenty invece di `latest`, modifica la variabile d'ambiente `TWENTY_VERSION` all'inizio del workflow.
|
||||
Vedi [Pubblicazione → CI/CD automatizzato](/l/it/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) per una spiegazione completa di entrambi i workflow generati dallo scaffolder (`ci.yml` e la pipeline di deploy `cd.yml`).
|
||||
|
||||
+7
-3
@@ -91,9 +91,11 @@ const GenerateDocumentForm = () => {
|
||||
}, []);
|
||||
|
||||
const generate = async () => {
|
||||
const apiBaseUrl = process.env.TWENTY_API_URL;
|
||||
// Prefer the injected functions URL; fall back to the legacy /s prefix (self-hosted/local)
|
||||
const functionsBaseUrl =
|
||||
process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL}/s`;
|
||||
const token = process.env.TWENTY_APP_ACCESS_TOKEN ?? process.env.TWENTY_API_KEY;
|
||||
const res = await fetch(`${apiBaseUrl}/s/documents/generate`, {
|
||||
const res = await fetch(`${functionsBaseUrl}/documents/generate`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
||||
body: JSON.stringify({ templateId, recordId }),
|
||||
@@ -186,7 +188,9 @@ const DocumentViewer = () => {
|
||||
const recordId = useFrontComponentExecutionContext((c) => c.recordId ?? null);
|
||||
// ...load { content, file } for recordId, then derive the links:
|
||||
const pdfUrl = document.file?.[0]?.url;
|
||||
const webUrl = `${process.env.TWENTY_API_URL ?? ''}/s/documents/view?id=${recordId}`;
|
||||
const functionsBaseUrl =
|
||||
process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL ?? ''}/s`;
|
||||
const webUrl = `${functionsBaseUrl}/documents/view?id=${recordId}`;
|
||||
|
||||
// Render the template body, plus quick links to the web page and the PDF.
|
||||
// Links open in a new tab so they don't navigate the embedded component.
|
||||
|
||||
+9
-2
@@ -9,8 +9,15 @@ Lo stesso gestore può anche rispondere alle richieste HTTP. Aggiungeremo due pe
|
||||
* un endpoint **POST** per generare un documento, e
|
||||
* un endpoint pubblico **GET** che rende un documento come una pagina web stampabile.
|
||||
|
||||
Entrambi usano `httpRouteTriggerSettings`. Gli itinerari delle app sono serviti sotto `/s` sul tuo server
|
||||
Twenty (es. `http://localhost:2020/s/documents/generate`).
|
||||
Entrambi usano `httpRouteTriggerSettings`. Sul server dev locale, gli itinerari delle app sono
|
||||
serviti sotto il prefisso `/s` (ad es. `http://localhost:2020/s/documents/generate`).
|
||||
|
||||
<Note>
|
||||
Su Twenty Cloud, i percorsi sono serviti sul dominio delle funzioni dedicate
|
||||
dello spazio di lavoro — l'URL Twenty inietta come `TWENTY_FUNCTIONS_URL`, senza prefisso `/s`. Il prefisso `/s`
|
||||
è deprecato e rimane solo per le istanze autosostenute e locali.
|
||||
Vedi [Chiamare una funzione logica](/l/it/developers/extend/apps/layout/front-components#calling-a-logic-function).
|
||||
</Note>
|
||||
|
||||
## Percorso POST — generare su richiesta
|
||||
|
||||
|
||||
+3
-3
@@ -76,11 +76,11 @@ Eseguire lo stesso cancelli CI fa:
|
||||
yarn lint # oxlint
|
||||
yarn typecheck # tsgo
|
||||
yarn test:unit # unit tests
|
||||
yarn twenty dev --once --dry-run # preview the metadata diff
|
||||
yarn twenty plan # preview the metadata diff
|
||||
```
|
||||
|
||||
L'esecuzione a secco stampa esattamente quello che cambierebbe sul server senza applicarlo —
|
||||
un buon controllo finale di sanità. Vedi
|
||||
Il piano mostra esattamente cosa verrebbe modificato sul server senza applicare effettivamente le modifiche —
|
||||
un buon controllo finale di coerenza. Vedi
|
||||
[Testing](/l/it/developers/extend/apps/operations/testing) e
|
||||
[Sincronizzazione & recupero](/l/it/developers/extend/apps/operations/sync-and-recovery).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user