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: Exécutez de la logique avant ou après l'installation — initiali
|
||||
icon: wrench
|
||||
---
|
||||
|
||||
Les hooks d'installation sont des fonctions logiques spéciales qui s'exécutent pendant le cycle de vie d'installation ou de mise à niveau. Ils partagent le même environnement d'exécution que les [fonctions logiques](/l/fr/developers/extend/apps/logic/logic-functions) classiques et reçoivent un `InstallPayload`, mais ils sont déclarés avec leurs propres fonctions de définition — `definePostInstallLogicFunction()` et `definePreInstallLogicFunction()` — et ne relèvent pas du modèle de déclencheur habituel (HTTP, cron, événements de base de données).
|
||||
Les hooks d'installation sont des fonctions logiques spéciales qui s'exécutent pendant le cycle de vie d'installation ou de mise à niveau. Ils partagent le même environnement d'exécution de gestionnaire que les [fonctions logiques](/l/fr/developers/extend/apps/logic/logic-functions) classiques et reçoivent un `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` est `undefined` lors d'une nouvelle installation), mais ils sont déclarés avec leurs propres fonctions de définition et vivent en dehors du modèle de déclencheur normal (HTTP, cron, événements de base de données).
|
||||
|
||||
Chaque application peut définir **au maximum une pré-installation** et **au maximum une post-installation**. La génération du manifeste renverra une erreur si plus d'une fonction de l'un ou l'autre type est détectée.
|
||||
Chaque application peut définir **au maximum une pré-installation** et **au maximum une post-installation**. La génération du manifeste renvoie une erreur si plus d'une fonction de l'un ou l'autre type est détectée.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
@@ -19,111 +19,59 @@ Chaque application peut définir **au maximum une pré-installation** et **au ma
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="definePostInstallLogicFunction" description="S'exécute après l'application de la migration des métadonnées de l'espace de travail">
|
||||
## En un coup d'œil
|
||||
|
||||
Une fonction post-installation s'exécute automatiquement une fois l'installation de votre application sur un espace de travail terminée. Le serveur l'exécute **après** que les métadonnées de l'application ont été synchronisées et que le client du SDK a été généré, afin que l'espace de travail soit entièrement prêt à l'emploi et que le nouveau schéma soit en place. Les cas d'utilisation typiques incluent le préremplissage de données par défaut, la création d'enregistrements initiaux, la configuration des paramètres de l'espace de travail ou le provisionnement de ressources sur des services tiers.
|
||||
| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Exécutions | Avant la migration des métadonnées — le schéma et les données **précédents** sont toujours intacts | Après la migration et la génération du SDK — le **nouveau** schéma est en place |
|
||||
| Exécution | Toujours synchrone ; bloque l'installation | Asynchrone par défaut (mis en file d'attente, 3 nouvelles tentatives) ; mode synchrone en option via `shouldRunSynchronously: true` |
|
||||
| En cas d'échec | L'installation est **abandonnée** avant toute modification du schéma | Asynchrone : nouvelle tentative jusqu'à 3 fois. Synchrone : l'appelant reçoit `POST_INSTALL_ERROR` (les modifications de schéma **ne** sont pas annulées) |
|
||||
| Utilisation typique | Sauvegarder ou corriger les données qu'une migration ferait perdre ; refuser une mise à niveau risquée en levant une exception | Initialiser des données par défaut, configurer l'espace de travail, enregistrer des ressources externes |
|
||||
|
||||
```ts src/logic-functions/post-install.ts
|
||||
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
**Règle empirique :** privilégiez post-install par défaut. Ne recourez à la pré-installation que lorsque la migration elle-même est destructive et que vous devez intercepter l'état précédent avant qu'il ne disparaisse.
|
||||
|
||||
const handler = async (payload: InstallPayload): Promise<void> => {
|
||||
console.log('Post install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
| Vous souhaitez... | Utiliser |
|
||||
| -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| Initialiser des données, configurer l'espace de travail, enregistrer des ressources externes | `post-install` |
|
||||
| Travail de longue durée qui ne doit pas bloquer la réponse d'installation | `post-install` (mode asynchrone par défaut, avec nouvelles tentatives du worker) |
|
||||
| Configuration rapide dont l'appelant dépend immédiatement après le retour de l'installation | `post-install` avec `shouldRunSynchronously: true` |
|
||||
| Lire ou sauvegarder des données que la migration à venir ferait perdre | `pre-install` |
|
||||
| Rejeter une mise à niveau qui corromprait des données existantes | `pre-install` (lancer une exception depuis le gestionnaire) |
|
||||
| Réconciliation à chaque mise à niveau | N'importe quel hook avec `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,
|
||||
});
|
||||
```
|
||||
## Comportement partagé par les deux hooks
|
||||
|
||||
Vous pouvez également exécuter manuellement la fonction de post-installation à tout moment à l'aide de la CLI :
|
||||
* La configuration est une configuration `defineLogicFunction` moins les paramètres de déclencheur, plus `shouldRunOnVersionUpgrade`.
|
||||
* **Quand il s'exécute** : uniquement lors des nouvelles installations, par défaut. Définissez `shouldRunOnVersionUpgrade: true` pour qu'il s'exécute également lors des mises à niveau. Utilisez `previousVersion` / `newVersion` pour bifurquer selon le chemin de mise à niveau.
|
||||
* **L'idempotence est importante** : le post-install asynchrone peut être relancé, et chaque hook est réexécuté lors des mises à niveau lorsque `shouldRunOnVersionUpgrade` est activé.
|
||||
* L'environnement habituel des fonctions logiques (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) est injecté, ce qui vous permet d'appeler l'API Twenty avec le jeton de votre application.
|
||||
* Le hook est rattaché automatiquement au manifeste de l'application au moment de la compilation (`preInstallLogicFunction` / `postInstallLogicFunction`) — rien à référencer dans [`defineApplication()`](/l/fr/developers/extend/apps/config/application).
|
||||
* La valeur par défaut de `timeoutSeconds` est 300 pour permettre des tâches de configuration plus longues comme l'initialisation des données.
|
||||
* **Non exécuté en mode dev** : `yarn twenty dev` ignore le flux d'installation et synchronise directement les fichiers, donc les hooks ne s'exécutent jamais dans ce cas. Déclenchez-les manuellement à la place :
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:function:exec --postInstall
|
||||
```
|
||||
|
||||
Points clés :
|
||||
* Les fonctions de post-installation utilisent `definePostInstallLogicFunction()` — une variante spécialisée qui omet les paramètres de déclencheur (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`).
|
||||
* Le gestionnaire reçoit un `InstallPayload` avec `{ previousVersion?: string; newVersion: string }` — `newVersion` est la version en cours d'installation, et `previousVersion` est la version précédemment installée (ou `undefined` lors d'une nouvelle installation). Utilisez ces valeurs pour distinguer les nouvelles installations des mises à niveau et pour exécuter une logique de migration spécifique à la version.
|
||||
* **Quand le hook s'exécute** : uniquement lors des nouvelles installations, par défaut. Passez `shouldRunOnVersionUpgrade: true` si vous souhaitez également qu'il s'exécute lorsque l'application est mise à niveau depuis une version précédente. S'il est omis, l'indicateur vaut `false` par défaut et les mises à niveau ignorent le hook.
|
||||
* **Modèle d'exécution — asynchrone par défaut, synchrone sur opt-in** : l'indicateur `shouldRunSynchronously` contrôle *comment* post-install est exécuté.
|
||||
* `shouldRunSynchronously: false` *(par défaut)* — le hook est **placé dans la file de messages** avec `retryLimit: 3` et s'exécute de manière asynchrone dans un worker. La réponse d'installation est renvoyée dès que la tâche est mise en file d'attente, de sorte qu'un gestionnaire lent ou défaillant ne bloque pas l'appelant. Le worker réessaiera jusqu'à trois fois. **Utilisez ceci pour les tâches de longue durée** — initialisation de grands jeux de données, appel d'API tierces lentes, provisionnement de ressources externes, tout ce qui pourrait dépasser une fenêtre de réponse HTTP raisonnable.
|
||||
* `shouldRunSynchronously: true` — le hook est exécuté **en ligne pendant le flux d'installation** (même exécuteur que pre-install). La requête d'installation est bloquée jusqu'à la fin du gestionnaire et, s'il lève une exception, l'appelant de l'installation reçoit un `POST_INSTALL_ERROR`. Aucun réessai automatique. **Utilisez ceci pour un travail rapide devant être terminé avant la réponse** — par exemple, émettre une erreur de validation à l'utilisateur, ou une configuration rapide dont le client dépendra immédiatement après le retour de l'appel d'installation. Gardez à l'esprit que la migration des métadonnées a déjà été appliquée au moment où post-install s'exécute, donc un échec en mode synchrone ne **rétablit pas** les modifications du schéma — il ne fait qu'exposer l'erreur.
|
||||
* Assurez-vous que votre gestionnaire est idempotent. En mode asynchrone, la file peut réessayer jusqu'à trois fois ; dans les deux modes, le hook peut s'exécuter à nouveau lors des mises à niveau lorsque `shouldRunOnVersionUpgrade: true`.
|
||||
* Les variables d'environnement `APPLICATION_ID`, `APP_ACCESS_TOKEN` et `API_URL` sont disponibles dans le gestionnaire (comme pour toute autre fonction logique), vous pouvez donc appeler l'API Twenty avec un jeton d'accès d'application limité à votre application.
|
||||
* Une seule fonction de post-installation est autorisée par application. La génération du manifeste renverra une erreur si plusieurs sont détectées.
|
||||
* Les propriétés `universalIdentifier`, `shouldRunOnVersionUpgrade` et `shouldRunSynchronously` de la fonction sont automatiquement attachées au manifeste de l'application sous le champ `postInstallLogicFunction` pendant le build — vous n'avez pas besoin de les référencer dans [`defineApplication()`](/l/fr/developers/extend/apps/config/application).
|
||||
* Le délai d'expiration par défaut est défini à 300 secondes (5 minutes) pour permettre des tâches de configuration plus longues comme l'initialisation des données.
|
||||
* **Non exécuté en mode dev** : lorsqu'une application est enregistrée localement (via `yarn twenty dev`), le serveur saute complètement le flux d'installation et synchronise les fichiers directement via le watcher de la CLI — ainsi, post-install ne s'exécute jamais en mode dev, quel que soit `shouldRunSynchronously`. Utilisez `yarn twenty dev:function:exec --postInstall` pour le déclencher manuellement sur un espace de travail en cours d'exécution.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="definePreInstallLogicFunction" description="S'exécute avant l'application de la migration des métadonnées de l'espace de travail">
|
||||
|
||||
Une fonction de pré-installation s'exécute automatiquement pendant l'installation, **avant que la migration des métadonnées de l'espace de travail soit appliquée**. Elle partage la même forme de payload que post-install (`InstallPayload`), mais elle est positionnée plus tôt dans le flux d'installation afin de pouvoir préparer l'état dont dépend la migration à venir — les usages typiques incluent la sauvegarde de données, la validation de la compatibilité avec le nouveau schéma, ou l'archivage d'enregistrements sur le point d'être restructurés ou supprimés.
|
||||
|
||||
```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,
|
||||
});
|
||||
```
|
||||
|
||||
Vous pouvez également exécuter manuellement la fonction de pré-installation à tout moment à l'aide de la CLI :
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:function:exec --preInstall
|
||||
```
|
||||
|
||||
Points clés :
|
||||
* Les fonctions de pré-installation utilisent `definePreInstallLogicFunction()` — même configuration spécialisée que post-install, simplement attachée à un autre emplacement du cycle de vie.
|
||||
* Les gestionnaires de pré- et post-install reçoivent le même type `InstallPayload` : `{ previousVersion?: string; newVersion: string }`. Importez-le une fois et réutilisez-le pour les deux hooks.
|
||||
* **Quand le hook s'exécute** : positionné juste avant la migration des métadonnées de l'espace de travail (`synchronizeFromManifest`). Avant l'exécution, le serveur lance une « synchronisation réduite » purement additive qui enregistre la fonction de pré-installation de la **nouvelle** version dans les métadonnées de l'espace de travail — rien d'autre n'est modifié — puis l'exécute. Comme cette synchronisation est uniquement additive, les objets, champs et données de la version précédente sont toujours intacts lorsque votre gestionnaire s'exécute : vous pouvez lire et sauvegarder en toute sécurité l'état pré-migration.
|
||||
* **Modèle d'exécution** : la pré-installation est exécutée **de façon synchrone** et **bloque l'installation**. Si le gestionnaire lève une exception, l'installation est abandonnée avant que des modifications du schéma ne soient appliquées — l'espace de travail reste sur la version précédente dans un état cohérent. C'est intentionnel : la pré-installation est votre dernière chance de refuser une mise à niveau risquée.
|
||||
* Comme pour post-install, une seule fonction de pré-installation est autorisée par application. Elle est automatiquement attachée au manifeste de l'application sous `preInstallLogicFunction` pendant le build.
|
||||
* **Non exécuté en mode dev** : comme pour post-install — le flux d'installation est entièrement ignoré pour les applications enregistrées localement, donc la pré-installation ne s'exécute jamais sous `yarn twenty dev`. Utilisez `yarn twenty dev:function:exec --preInstall` pour la déclencher manuellement.
|
||||
<AccordionGroup>
|
||||
<Accordion title="definePostInstallLogicFunction" description="S'exécute après l'application de la migration des métadonnées de l'espace de travail">
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Pré-installation vs post-installation : quand utiliser quoi" description="Choisir le bon hook d'installation">
|
||||
|
||||
Les deux hooks font partie du même flux d'installation et reçoivent le même `InstallPayload`. La différence tient au **moment** où ils s'exécutent par rapport à la migration des métadonnées de l'espace de travail, et cela change les données qu'ils peuvent manipuler en toute sécurité.
|
||||
|
||||
La pré-installation est toujours **synchrone** (elle bloque l'installation et peut l'interrompre). Post-install est **asynchrone par défaut** — mis en file d'attente sur un worker avec des réessais automatiques — mais peut opter pour une exécution synchrone avec `shouldRunSynchronously: true`. Voir l'accordéon `definePostInstallLogicFunction` ci-dessus pour savoir quand utiliser chaque mode.
|
||||
|
||||
**Utilisez `post-install` pour tout ce qui nécessite l'existence du nouveau schéma.** C'est le cas le plus courant :
|
||||
|
||||
* Initialiser des données par défaut (création d'enregistrements initiaux, de vues par défaut, de contenu de démonstration) sur des objets et champs nouvellement ajoutés.
|
||||
* Enregistrer des webhooks auprès de services tiers maintenant que l'application dispose de ses identifiants.
|
||||
* Appeler votre propre API pour finaliser une configuration qui dépend des métadonnées synchronisées.
|
||||
* Logique idempotente « assurer l'existence de cet élément » qui doit réconcilier l'état à chaque mise à niveau — à combiner avec `shouldRunOnVersionUpgrade: true`.
|
||||
|
||||
Exemple — initialiser un enregistrement `PostCard` par défaut après l'installation :
|
||||
S'exécute une fois que votre application a terminé son installation : métadonnées synchronisées, client SDK généré, nouveau schéma interrogeable. Exemple — initialiser un enregistrement par défaut lors des nouvelles installations :
|
||||
|
||||
```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,
|
||||
});
|
||||
```
|
||||
|
||||
**Utilisez `pre-install` lorsqu'une migration détruirait ou corromprait autrement des données existantes.** Comme la pré-installation s'exécute sur le schéma *précédent* et qu'un échec annule la mise à niveau, c'est l'endroit approprié pour tout ce qui est risqué :
|
||||
Le drapeau `shouldRunSynchronously` contrôle le modèle d'exécution :
|
||||
|
||||
* **Sauvegarder des données sur le point d'être supprimées ou restructurées** — par exemple, vous supprimez un champ en v2 et devez copier ses valeurs dans un autre champ ou les exporter vers un stockage avant l'exécution de la migration.
|
||||
* **Archiver des enregistrements qu'une nouvelle contrainte invaliderait** — par exemple, un champ devient `NOT NULL` et vous devez d'abord supprimer ou corriger les lignes avec des valeurs nulles.
|
||||
* **Valider la compatibilité et refuser la mise à niveau si les données actuelles ne peuvent pas être migrées proprement** — lancez une exception depuis le gestionnaire et l'installation s'interrompt sans appliquer de modifications. C'est plus sûr que de découvrir l'incompatibilité en cours de migration.
|
||||
* **Renommer ou réassigner les clés des données** avant une modification du schéma qui ferait perdre l'association.
|
||||
* `false` *(par défaut)* — mis en file d'attente dans la file de messages (`retryLimit: 3`) et exécuté par un worker. La réponse d'installation est renvoyée dès que la tâche est mise en file d'attente. **À utiliser pour les travaux de longue durée** — initialisation de grands ensembles de données, API tierces lentes.
|
||||
* `true` — exécuté en ligne pendant le flux d'installation. La requête d'installation est bloquée jusqu'à ce que le gestionnaire ait terminé ; une erreur levée apparaît comme `POST_INSTALL_ERROR` pour l'appelant (aucune nouvelle tentative). **À utiliser pour les travaux rapides qui doivent être terminés avant la réponse.** La migration a déjà été appliquée à ce stade, donc un échec n'annule pas les modifications du schéma — il ne fait que remonter l'erreur.
|
||||
|
||||
Exemple — archiver des enregistrements avant une migration destructive :
|
||||
</Accordion>
|
||||
<Accordion title="definePreInstallLogicFunction" description="S'exécute avant l'application de la migration des métadonnées de l'espace de travail">
|
||||
|
||||
S'exécute avant la migration des métadonnées, sur le schéma **précédent** — l'endroit idéal pour sauvegarder des données qu'une migration ferait perdre, ou pour refuser une mise à niveau risquée. Avant l'exécution, le serveur effectue une « synchronisation réduite » purement additive qui enregistre uniquement la fonction de pré-installation de la nouvelle version ; tout le reste — les objets, champs et données de la version précédente — reste intact lorsque votre gestionnaire s'exécute.
|
||||
|
||||
La pré-installation est toujours **synchrone** et bloque l'installation. Si le gestionnaire lève une exception, l'installation est abandonnée avant toute modification du schéma — l'espace de travail reste sur la version précédente dans un état cohérent. C'est intentionnel : la pré-installation est votre dernière chance de refuser une mise à niveau risquée.
|
||||
|
||||
Exemple — copier les valeurs d'un champ hérité avant que la migration ne le supprime :
|
||||
|
||||
```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({
|
||||
});
|
||||
```
|
||||
|
||||
**Règle générale :**
|
||||
|
||||
| Vous souhaitez... | Utiliser |
|
||||
| ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
|
||||
| Initialiser des données par défaut, configurer l'espace de travail, enregistrer des ressources externes | `post-install` |
|
||||
| Exécuter une initialisation longue ou des appels tiers qui ne doivent pas bloquer la réponse d'installation | `post-install` (par défaut — `shouldRunSynchronously: false`, avec des réessais du worker) |
|
||||
| Exécuter une configuration rapide dont l'appelant dépendra immédiatement après le retour de l'appel d'installation | `post-install` avec `shouldRunSynchronously: true` |
|
||||
| Lire ou sauvegarder des données que la migration à venir ferait perdre | `pre-install` |
|
||||
| Rejeter une mise à niveau qui corromprait des données existantes | `pre-install` (lancer une exception depuis le gestionnaire) |
|
||||
| Exécuter une réconciliation à chaque mise à niveau | `post-install` avec `shouldRunOnVersionUpgrade: true` |
|
||||
| Effectuer une configuration ponctuelle uniquement lors de la première installation | `post-install` avec `shouldRunOnVersionUpgrade: false` (par défaut) |
|
||||
|
||||
<Note>
|
||||
En cas de doute, privilégiez **post-install**. Ne recourez à la pré-installation que lorsque la migration elle-même est destructive et que vous devez intercepter l'état précédent avant qu'il ne disparaisse.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -86,6 +86,22 @@ export default defineObject({
|
||||
**Les champs de base sont ajoutés automatiquement.** Lorsque vous définissez un objet personnalisé, Twenty crée pour vous des champs standard comme `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` et `deletedAt`. Vous n’avez pas besoin de les déclarer dans votre tableau `fields` — uniquement vos champs personnalisés. Vous pouvez remplacer un champ par défaut en en déclarant un avec le même nom, mais c’est rarement une bonne idée.
|
||||
</Note>
|
||||
|
||||
## Types de champ
|
||||
|
||||
L’ensemble complet des valeurs de `FieldType`, exportées depuis `twenty-sdk/define` :
|
||||
|
||||
| Catégorie | Types |
|
||||
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Texte | `TEXT`, `RICH_TEXT`, `ARRAY` (de chaînes), `RAW_JSON` |
|
||||
| Numérique | `NUMBER` (`universalSettings.dataType` : `'float'` / `'int'` / `'bigint'`), `NUMERIC` (précision arbitraire), `RATING`, `POSITION` |
|
||||
| Dates | `DATE`, `DATE_TIME` |
|
||||
| Choix | `BOOLEAN`, `SELECT`, `MULTI_SELECT` |
|
||||
| Composés | `FULL_NAME`, `ADDRESS`, `EMAILS`, `PHONES`, `LINKS`, `CURRENCY`, `ACTOR`, `FILES` |
|
||||
| Identifiants et relations | `UUID`, `RELATION`, `MORPH_RELATION` (voir [Relations](/l/fr/developers/extend/apps/data/relations)) |
|
||||
| Système | `TS_VECTOR` (vecteur de recherche en texte intégral, géré par le serveur) |
|
||||
|
||||
Les types composés stockent plusieurs sous-champs (par exemple `FULL_NAME` = prénom + nom de famille ; `CURRENCY` = `amountMicros` + `currencyCode`). `SELECT` et `MULTI_SELECT` nécessitent un tableau `options` comme dans l’exemple ci-dessus.
|
||||
|
||||
## Valeurs par défaut
|
||||
|
||||
Les valeurs par défaut de type chaîne littérale doivent être entourées de guillemets simples **à l’intérieur** de la chaîne — `defaultValue: "'Draft'"`, et non `defaultValue: "Draft"`. C’est pourquoi le champ `status` ci-dessus utilise `` `'${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
|
||||
```
|
||||
|
||||
## Fichiers clés
|
||||
|
||||
| Fichier / Dossier | Objectif |
|
||||
| ---------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| `src/application-config.ts` | **Requis.** Le fichier de configuration principal de votre application. |
|
||||
| `src/default-role.ts` | Rôle par défaut qui contrôle ce à quoi vos fonctions de logique peuvent accéder. |
|
||||
| `src/constants/universal-identifiers.ts` | UUID générés automatiquement et métadonnées de l’application (nom d’affichage, description). |
|
||||
| `src/__tests__/` | Tests d’intégration (configuration + test d’exemple). |
|
||||
| `public/` | Ressources statiques (images, polices) servies avec votre application. |
|
||||
| Fichier / Dossier | Objectif |
|
||||
| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `src/application-config.ts` | **Requis.** Le fichier de configuration principal de votre application. |
|
||||
| `src/default-role.ts` | Rôle par défaut qui contrôle ce à quoi vos fonctions de logique peuvent accéder. |
|
||||
| `src/constants/universal-identifiers.ts` | UUID générés automatiquement et métadonnées de l’application (nom d’affichage, description). |
|
||||
| `src/front-components/`, `src/navigation-menu-items/`, `src/page-layouts/` | Une page d’accueil de démarrage : un composant frontal rendu par une mise en page de page autonome, accessible depuis la barre latérale. |
|
||||
| `src/__tests__/` | Un test unitaire plus un test d’intégration (avec sa configuration globale) qui synchronise l’application avec un serveur réel. |
|
||||
| `public/` | Ressources statiques (images, polices) servies avec votre application. |
|
||||
| `AGENTS.md` / `CLAUDE.md` | Consignes pour les agents d’écriture de code IA qui travaillent sur l’application. |
|
||||
|
||||
<Note>
|
||||
**L’organisation des fichiers vous revient.** Les dossiers ci-dessus sont des conventions — le SDK détecte les entités via une analyse AST sur les appels à `export default defineEntity(...)` quel que soit l’emplacement du fichier.
|
||||
@@ -47,15 +60,18 @@ Les deux packages du SDK Twenty doivent être placés dans `devDependencies`, et
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Le générateur de projet fige `twenty-sdk` et `twenty-client-sdk` sur sa propre version — gardez les deux synchronisés lors de la mise à niveau.
|
||||
|
||||
* **`twenty-sdk`** fournit le CLI `twenty` ainsi que les outils de build et de scaffolding. Il ne s’exécute qu’au moment du développement et du build et n’est jamais importé par le runtime de l’application que vous publiez.
|
||||
* **`twenty-client-sdk`** *est* importé par le code de votre application (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`), mais Twenty le fournit au moment de l’exécution : les fonctions de logique l’obtiennent à partir d’une couche SDK générée, et les composants front le résolvent à partir de modules servis par le serveur. La copie que vous avez installée est uniquement utilisée pour la vérification de type et le build au moment du déploiement, elle n’a donc jamais besoin d’être incluse dans le bundle déployé.
|
||||
|
||||
Conserver l’un ou l’autre package dans `dependencies` l’intègre dans le bundle runtime de l’application installée, où il ne fait que l’alourdir inutilement. `twenty build` émet un avertissement lorsque l’un ou l’autre est encore répertorié dans `dependencies`.
|
||||
Conserver l’un ou l’autre package dans `dependencies` l’intègre dans le bundle runtime de l’application installée, où il ne fait que l’alourdir inutilement. `twenty dev:build` émet un avertissement lorsque l’un ou l’autre est encore répertorié dans `dependencies`.
|
||||
|
||||
Ajoutez les dépendances runtime propres à votre application (les bibliothèques que vos fonctions logiques importent réellement à l’exécution) dans `dependencies` comme d’habitude.
|
||||
|
||||
@@ -6,17 +6,17 @@ description: Créez votre première application Twenty en quelques minutes.
|
||||
|
||||
## Prérequis
|
||||
|
||||
* **Node.js 24+** — [Télécharger](https://nodejs.org/)
|
||||
* **Node.js 24.5+** — [Télécharger](https://nodejs.org/)
|
||||
* **Yarn 4** — fourni avec Node.js via Corepack. Activez-le : `corepack enable`
|
||||
* **Docker** — [Télécharger](https://www.docker.com/products/docker-desktop/). Nécessaire pour exécuter un serveur Twenty local. Ignorez si vous avez déjà Twenty en cours d’exécution ailleurs.
|
||||
|
||||
La création d’une application Twenty comporte trois phases. Le générateur les regroupe en une seule commande pour le parcours idéal, mais chaque phase est un concept distinct — en cas d’échec, savoir dans quelle phase vous vous trouvez indique ce qu’il faut corriger.
|
||||
|
||||
| Phase | Ce que vous faites | Outil | Résultat |
|
||||
| -------------------------- | ------------------------------------------------------ | ----------------------------- | ----------------------------------------------------------- |
|
||||
| **1. Génération** | Générer le code source de l’application | `npx create-twenty-app` | Un projet TypeScript sur le disque |
|
||||
| **2. Exécuter un serveur** | Démarrer un serveur Twenty vers lequel se synchroniser | Docker + `yarn twenty server` | Une instance Twenty en cours d’exécution |
|
||||
| **3. Synchroniser** | Synchroniser en direct votre code avec le serveur | `yarn twenty dev` | Vos modifications apparaissent dans l’interface utilisateur |
|
||||
| Phase | Ce que vous faites | Outil | Résultat |
|
||||
| -------------------------- | ------------------------------------------------------ | ----------------------------------- | ----------------------------------------------------------- |
|
||||
| **1. Génération** | Générer le code source de l’application | `npx create-twenty-app` | Un projet TypeScript sur le disque |
|
||||
| **2. Exécuter un serveur** | Démarrer un serveur Twenty vers lequel se synchroniser | Docker + `yarn twenty docker:start` | Une instance Twenty en cours d’exécution |
|
||||
| **3. Synchroniser** | Synchroniser en direct votre code avec le serveur | `yarn twenty dev` | Vos modifications apparaissent dans l’interface utilisateur |
|
||||
|
||||
---
|
||||
|
||||
@@ -28,7 +28,7 @@ Créez une nouvelle application à partir du modèle :
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
```
|
||||
|
||||
On vous demandera un nom et une description — appuyez sur **Entrée** pour utiliser les valeurs par défaut. Cela génère un projet TypeScript dans `my-twenty-app/` avec un fichier de démarrage `application-config.ts`, un rôle par défaut, un workflow CI et un test d’intégration.
|
||||
Le générateur est non interactif : le nom du répertoire devient le nom de l’application. Passez `--display-name` et `--description` pour personnaliser les métadonnées générées (vous pouvez également les modifier plus tard dans `src/constants/universal-identifiers.ts`). Cela génère un projet TypeScript dans `my-twenty-app/` avec un fichier de démarrage `application-config.ts`, un rôle par défaut, des workflows CI/CD et un test d’intégration.
|
||||
|
||||
**Après cette phase :** vous disposez du code source d’une application sur votre machine. Elle ne s’exécute pas encore — c’est la phase 2.
|
||||
|
||||
@@ -38,28 +38,14 @@ On vous demandera un nom et une description — appuyez sur **Entrée** pour uti
|
||||
|
||||
Votre application a besoin d’un serveur Twenty vers lequel se synchroniser. Le serveur est une instance Twenty complète — interface utilisateur, API GraphQL, PostgreSQL — exécutée localement dans Docker. Votre code local envoie ses définitions à ce serveur, qui les fait apparaître dans l’interface utilisateur.
|
||||
|
||||
Le générateur propose d’en démarrer un pour vous :
|
||||
Le générateur de projet en crée un pour vous : avec Docker en cours d’exécution, il récupère l’image `twentycrm/twenty-app-dev`, la démarre sur le port `2020`, et authentifie le CLI auprès de l’espace de travail de démonstration prérempli (`tim@apple.dev`) — aucune connexion requise.
|
||||
|
||||
> **Souhaitez-vous configurer une instance locale de Twenty ?**
|
||||
|
||||
* **Oui (recommandé)** — récupère l’image Docker `twentycrm/twenty-app-dev` et la démarre sur le port `2020`. Assurez-vous d’abord que Docker est en cours d’exécution.
|
||||
* **Non** — choisissez cette option si vous avez déjà un serveur Twenty auquel vous souhaitez vous connecter. Vous pourrez le connecter plus tard avec `yarn twenty remote:add`.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Faut-il démarrer l’instance locale ?" />
|
||||
</div>
|
||||
|
||||
Une fois le serveur démarré, un navigateur s’ouvre pour la connexion. Utilisez le compte de démonstration prérempli :
|
||||
|
||||
* **E-mail :** `tim@apple.dev`
|
||||
* **Mot de passe :** `tim@apple.dev`
|
||||
Pour vous connecter à un serveur Twenty existant à la place, passez `--url \<your-server-url>`. Les serveurs distants s’authentifient avec OAuth : un navigateur s’ouvre pour que vous puissiez vous connecter et cliquer sur **Authorize**, ce qui donne au CLI l’accès à votre espace de travail. (Vous pouvez aussi choisir OAuth en local avec `--authentication-method oauth` — connectez-vous avec `tim@apple.dev` / `tim@apple.dev`.)
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/login.png" alt="Écran de connexion Twenty" />
|
||||
</div>
|
||||
|
||||
Cliquez sur **Authorize** sur l’écran suivant — cela donne à la CLI l’accès à votre espace de travail.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/authorize.png" alt="Écran d’autorisation de la CLI Twenty" />
|
||||
</div>
|
||||
@@ -117,27 +103,31 @@ Cliquez sur **View installed app** pour voir l’installation dans l’espace de
|
||||
|
||||
### Synchronisation ponctuelle pour la CI et les scripts
|
||||
|
||||
Passez `--once` pour exécuter une seule opération de build + synchronisation puis quitter — même pipeline, pas de watcher :
|
||||
Utilisez `plan` et `apply` pour exécuter une fois le même pipeline, sans surveillance :
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
| Commande | Comportement | Quand l'utiliser : |
|
||||
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
||||
| `yarn twenty dev` | Surveille et resynchronise à chaque modification. Reste en cours d’exécution jusqu’à ce que vous l’arrêtiez. | Développement local interactif. |
|
||||
| `yarn twenty dev --once` | Une seule opération de build + synchronisation, se termine avec le code `0` en cas de réussite et `1` en cas d’échec. | CI, hooks de pré-commit, agents IA et flux de travail scriptés. |
|
||||
| `yarn twenty dev --once --dry-run` | Construit et affiche les modifications de métadonnées **sans les appliquer**. | Inspection de ce qu’une synchronisation changerait avant de s’y engager. |
|
||||
| Commande | Comportement | Quand l'utiliser : |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
||||
| `yarn twenty dev` | Surveille et resynchronise à chaque modification. Reste en cours d’exécution jusqu’à ce que vous l’arrêtiez. | Développement local interactif. |
|
||||
| `yarn twenty apply` | Une seule opération de build + synchronisation, se termine avec le code `0` en cas de réussite et `1` en cas d’échec. Demande une confirmation pour les modifications destructrices (passez `--force` pour l’ignorer). | CI, hooks de pré-commit, agents IA et flux de travail scriptés. |
|
||||
| `yarn twenty plan` | Construit et affiche les modifications de métadonnées **sans les appliquer**. | Inspection de ce qu’une synchronisation changerait avant de s’y engager. |
|
||||
|
||||
Les deux modes nécessitent un serveur distant authentifié. Voir [synchronisation et récupération](/l/fr/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) pour plus d’informations sur `--dry-run`.
|
||||
Tous les modes nécessitent un serveur distant authentifié. Voir [synchronisation et récupération](/l/fr/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) pour plus d’informations sur `plan`.
|
||||
|
||||
<Note>
|
||||
`yarn twenty dev --once` et `yarn twenty dev --once --dry-run` sont des alias obsolètes pour `yarn twenty apply` et `yarn twenty plan`.
|
||||
</Note>
|
||||
|
||||
### Options du mode de développement
|
||||
|
||||
| Option | Description |
|
||||
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| `--once` | Construire et synchroniser une fois, puis quitter. |
|
||||
| `--dry-run` | Avec `--once`, prévisualisez les modifications de métadonnées sans les appliquer. N’écrit rien. |
|
||||
| `--debounceMs \<ms>` | Définir le délai de temporisation des modifications de fichiers en millisecondes (valeur par défaut : `2000`). |
|
||||
| `--force` | Applique les modifications destructrices (suppressions) sans confirmation. |
|
||||
| `--debounceMs \<ms>` | Définir le délai de temporisation des modifications de fichiers en millisecondes (valeur par défaut : `1000`). |
|
||||
| `--verbose` / `--debug` | Afficher des journaux de build détaillés, les requêtes de synchronisation et les traces d’erreur. |
|
||||
|
||||
## Ce que vous pouvez créer
|
||||
|
||||
@@ -34,6 +34,10 @@ yarn twenty dev:add frontComponent
|
||||
| Vue | `yarn twenty dev:add view` | `src/views/\<name>.ts` |
|
||||
| Élément de menu de navigation | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
|
||||
| Mise en page | `yarn twenty dev:add pageLayout` | `src/page-layouts/\<name>.ts` |
|
||||
| Onglet Mise en page | `yarn twenty dev:add pageLayoutTab` | `src/page-layout-tabs/\<name>.ts` |
|
||||
| Élément du menu de commande | `yarn twenty dev:add commandMenuItem` | `src/command-menu-items/\<name>.ts` |
|
||||
| Champ de vue | `yarn twenty dev:add viewField` | `src/view-fields/\<name>.ts` |
|
||||
| Fournisseur de connexion | `yarn twenty dev:add connectionProvider` | `src/connection-providers/\<name>.ts` |
|
||||
|
||||
## Ce que génère l'outil de génération
|
||||
|
||||
|
||||
+2
-2
@@ -5,10 +5,10 @@ icon: wrench
|
||||
---
|
||||
|
||||
* **Erreurs Docker** — Assurez-vous que Docker Desktop (ou le démon) est en cours d’exécution avant `yarn twenty docker:start`. Le message d’erreur indiquera la bonne commande de démarrage pour votre système d’exploitation.
|
||||
* **Mauvaise version de Node** — la version 24+ est requise. Vérifiez avec `node -v`.
|
||||
* **Mauvaise version de Node** — Version 24.5+ requise (`engines.node: ^24.5.0`). Vérifiez avec `node -v`.
|
||||
* **Yarn 4 manquant** — Exécutez `corepack enable`.
|
||||
* **Dépendances cassées** — `rm -rf node_modules && yarn install`.
|
||||
* **Erreurs de `twenty-sdk` après la mise à niveau vers la v2.8.0** — il est passé de `dependencies` à `devDependencies` dans la v2.8.0. Voir [Structure du projet → Dépendances](/l/fr/developers/extend/apps/getting-started/project-structure#dependencies).
|
||||
* **`twenty build` avertit au sujet de `twenty-client-sdk` dans `dependencies`** — il est fourni au moment de l’exécution par Twenty, donc il devrait être déplacé vers `devDependencies` avec `twenty-sdk`. Voir [Structure du projet → Dépendances](/l/fr/developers/extend/apps/getting-started/project-structure#dependencies).
|
||||
* **`twenty dev:build` avertit au sujet de `twenty-client-sdk` dans `dependencies`** — il est fourni au moment de l'exécution par Twenty, donc il devrait être déplacé vers `devDependencies` avec `twenty-sdk`. Voir [Structure du projet → Dépendances](/l/fr/developers/extend/apps/getting-started/project-structure#dependencies).
|
||||
|
||||
Bloqué ? Demandez de l’aide sur le [Discord de 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({
|
||||
|
||||
## Champs de configuration
|
||||
|
||||
| Champ | Obligatoire | Description |
|
||||
| --------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `universalIdentifier` | Oui | ID unique et stable pour la commande |
|
||||
| `label` | Oui | Libellé complet affiché dans le menu de commande (Cmd+K) |
|
||||
| `frontComponentUniversalIdentifier` | Oui | L'`universalIdentifier` du composant frontal que cette commande ouvre |
|
||||
| `shortLabel` | Non | Libellé plus court affiché sur le bouton d'action rapide épinglé |
|
||||
| `icon` | Non | Nom de l'icône affiché à côté du libellé (p. ex. `'IconBolt'`, `'IconSend'`) |
|
||||
| `isPinned` | Non | Lorsque `true`, affiche la commande comme un bouton d'action rapide dans le coin supérieur droit de la page |
|
||||
| `availabilityType` | Non | Contrôle l'emplacement d'apparition de la commande : `'GLOBAL'` (toujours disponible), `'RECORD_SELECTION'` (uniquement lorsque des enregistrements sont sélectionnés) ou `'FALLBACK'` (affichée lorsqu'aucune autre commande ne correspond) |
|
||||
| `availabilityObjectUniversalIdentifier` | Non | Restreint la commande aux pages d’un type d’objet spécifique (p. ex., uniquement sur les enregistrements « Company ») |
|
||||
| `conditionalAvailabilityExpression` | Non | Une expression booléenne qui contrôle dynamiquement la visibilité (voir ci-dessous) |
|
||||
| Champ | Obligatoire | Description |
|
||||
| --------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `universalIdentifier` | Oui | ID unique et stable pour la commande |
|
||||
| `label` | Oui | Libellé complet affiché dans le menu de commande (Cmd+K) |
|
||||
| `frontComponentUniversalIdentifier` | Oui | L'`universalIdentifier` du composant frontal que cette commande ouvre |
|
||||
| `shortLabel` | Non | Libellé plus court affiché sur le bouton d'action rapide épinglé |
|
||||
| `icon` | Non | **Obsolète** — ignoré au profit de l’icône de l’application ; la build émet un avertissement si elle est définie |
|
||||
| `isPinned` | Non | Lorsque `true`, affiche la commande comme un bouton d'action rapide dans le coin supérieur droit de la page |
|
||||
| `availabilityType` | Non | Contrôle l’emplacement d’apparition de la commande : `'GLOBAL'` (toujours disponible), `'GLOBAL_OBJECT_CONTEXT'` (uniquement sur les pages avec un contexte d’objet — pages d’index et d’enregistrement), `'RECORD_SELECTION'` (uniquement lorsque des enregistrements sont sélectionnés) ou `'FALLBACK'` (affichée lorsqu’aucune autre commande ne correspond) |
|
||||
| `availabilityObjectUniversalIdentifier` | Non | Restreint la commande aux pages d’un type d’objet spécifique (p. ex., uniquement sur les enregistrements « Company ») |
|
||||
| `conditionalAvailabilityExpression` | Non | Une expression booléenne qui contrôle dynamiquement la visibilité (voir ci-dessous) |
|
||||
|
||||
## Commandes sans interface
|
||||
|
||||
Un élément de menu de commande associé à un [headless front component](/l/fr/developers/extend/apps/layout/front-components#headless-vs-non-headless) est la manière idiomatique de proposer une action en un clic — exécuter du code, naviguer, ou confirmer puis exécuter. La page Front Components couvre les [SDK Command components](/l/fr/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) qui gèrent le modèle action-et-démontage.
|
||||
|
||||
Un flux typique :
|
||||
|
||||
```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 flux typique : un composant headless affiche `<Command execute={...} />` (voir [l’exemple complet](/l/fr/developers/extend/apps/layout/front-components#sdk-command-components)), et l’élément de menu de commande y pointe :
|
||||
|
||||
```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',
|
||||
});
|
||||
```
|
||||
|
||||
Après la synchronisation avec `yarn twenty dev` (ou en exécutant une seule fois `yarn twenty dev --once`), l'action rapide apparaît dans le coin supérieur droit de la page :
|
||||
Après la synchronisation avec `yarn twenty dev` (ou en exécutant une seule fois `yarn twenty apply`), l'action rapide apparaît dans le coin supérieur droit de la page :
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/quick-action.png" alt="Bouton d'action rapide dans le coin supérieur droit" />
|
||||
@@ -88,11 +87,11 @@ Les composants frontaux existent en deux modes de rendu contrôlés par l’opti
|
||||
|
||||
```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 @@ Comme le composant retourne `null`, Twenty n'affiche pas de conteneur pour celui
|
||||
|
||||
Le package `twenty-sdk` fournit quatre composants utilitaires Command conçus pour les composants frontaux headless. Chaque composant exécute une action au montage, gère les erreurs en affichant une notification snackbar et démonte automatiquement le composant frontal une fois terminé.
|
||||
|
||||
Importez-les depuis `twenty-sdk/command` :
|
||||
Importez-les depuis `twenty-sdk/front-component` :
|
||||
|
||||
* **`Command`** — Exécute un callback asynchrone via la prop `execute`.
|
||||
* **`CommandLink`** — Navigue vers un chemin d'application. Props : `to`, `params`, `queryParams`, `options`.
|
||||
@@ -127,8 +126,8 @@ Voici un exemple complet d'un composant frontal headless utilisant `Command` pou
|
||||
|
||||
```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 @@ Et un exemple utilisant `CommandModal` pour demander une confirmation avant l'ex
|
||||
|
||||
```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 @@ Les composants front s’exécutent côté navigateur dans un Web Worker isolé
|
||||
|
||||
Une fonction logique déclarée avec `httpRouteTriggerSettings` est accessible via HTTP à son chemin de route. Twenty injecte dans le worker l’URL de base à partir de laquelle vos fonctions sont servies sous la forme de `TWENTY_FUNCTIONS_URL`, ainsi que le `TWENTY_APP_ACCESS_TOKEN` qui authentifie l’appel. Il n’existe pas encore de client SDK dédié pour invoquer vos propres fonctions, donc appelez-les avec un simple `fetch` :
|
||||
|
||||
> **Sur Twenty Cloud, les fonctions logiques déclenchées par HTTP sont servies sur un domaine dédié par espace de travail** à l’adresse `https://\<your-workspace-subdomain>.twenty.com\<path>` — c’est exactement ce à quoi `TWENTY_FUNCTIONS_URL` correspond. Pour les appelants externes, copiez l’URL exacte à partir des paramètres **HTTP trigger** de la fonction ou de l’onglet **Settings** de l’application.
|
||||
> **Sur Twenty Cloud, les fonctions logiques déclenchées par HTTP sont servies sur un domaine dédié par espace de travail** à l’adresse `https://\<your-workspace-subdomain>.withtwenty.com\<path>` — c’est exactement ce à quoi `TWENTY_FUNCTIONS_URL` correspond. Pour les appelants externes, copiez l’URL exacte à partir des paramètres **HTTP trigger** de la fonction ou de l’onglet **Settings** de l’application.
|
||||
|
||||
<Warning>
|
||||
L’ancienne route de fonction `/s/` est **obsolète** et sera **désactivée le 2026-07-24**. Utilisez plutôt `TWENTY_FUNCTIONS_URL` (ci-dessus), et migrez toutes les URL `/s/` en dur avant cette date. La route `/s/` reste disponible pour l’auto-hébergement.
|
||||
@@ -212,7 +210,7 @@ Un composant front sans interface (headless) peut effectuer l’appel au montage
|
||||
|
||||
```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 @@ Dans votre composant, utilisez les hooks du SDK pour accéder à l'utilisateur a
|
||||
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 @@ Voici un exemple qui utilise l'API hôte pour afficher une snackbar et fermer le
|
||||
|
||||
```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({
|
||||
Utilisez `useSelectedRecordIds()` pour gérer plusieurs enregistrements sélectionnés. C'est utile pour les opérations groupées :
|
||||
|
||||
```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,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Affichez-la avec un [élément de menu de commande](/l/fr/developers/extend/apps/layout/command-menu-items) limité aux sélections d'enregistrements :
|
||||
|
||||
```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` contrôle l’ordre dans la barre latérale.
|
||||
|
||||
* L’énumération contient également `NavigationMenuItemType.RECORD`, utilisé en interne pour les favoris d’enregistrements créés par l’utilisateur — il n’est pas utilisable depuis un manifeste d’application (il n’existe aucun champ pour référencer un enregistrement).
|
||||
|
||||
* `icon` et `color` sont facultatifs et personnalisent l’apparence de l’entrée.
|
||||
|
||||
* `folderUniversalIdentifier` est également disponible sur n’importe quel élément pour l’imbriquer dans un parent de type `FOLDER`.
|
||||
|
||||
@@ -33,17 +33,32 @@ export default defineView({
|
||||
## Points clés
|
||||
|
||||
* `objectUniversalIdentifier` spécifie à quel objet cette vue s'applique. Il peut s’agir d’un objet personnalisé que vous avez défini ou d’un objet Twenty standard.
|
||||
* `key` détermine le type de vue — `ViewKey.INDEX` est la vue de liste principale pour l’objet.
|
||||
* "`key: ViewKey.INDEX`" marque la vue comme la vue de liste principale de l’objet (celle qu’un élément de navigation "`OBJECT`" ouvre).
|
||||
* `fields` contrôle les colonnes affichées et leur ordre. Chaque champ référence un `fieldMetadataUniversalIdentifier`.
|
||||
* Vous pouvez également définir `filters`, `filterGroups`, `groups` et `fieldGroups` pour des configurations plus avancées.
|
||||
* Vous pouvez également déclarer `filters`, `filterGroups`, `sorts`, `groups` et `fieldGroups` pour des configurations plus avancées.
|
||||
* `position` contrôle l’ordre lorsqu’il existe plusieurs vues pour le même objet.
|
||||
|
||||
## Propriétés optionnelles
|
||||
|
||||
| Propriété | Valeurs | Description |
|
||||
| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `type` | `ViewType.TABLE` (par défaut), `ViewType.KANBAN`, `ViewType.CALENDAR` | Comment les enregistrements sont disposés. (`FIELDS_WIDGET` / `TABLE_WIDGET` existent également mais sont utilisés en interne par les widgets de mise en page de page.) |
|
||||
| `visibility` | `ViewVisibility.WORKSPACE` (par défaut), `ViewVisibility.UNLISTED` | Indique si la vue est listée pour l’ensemble de l’espace de travail ou masquée dans les sélecteurs. |
|
||||
| `openRecordIn` | `ViewOpenRecordIn.SIDE_PANEL` (par défaut), `ViewOpenRecordIn.RECORD_PAGE` | Endroit où un clic sur un enregistrement l’ouvre. |
|
||||
| `tris` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | Ordre de tri par défaut. |
|
||||
| `isCompact` | `boolean` | Affichage compact des lignes. |
|
||||
| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | Regrouper les enregistrements (par exemple, les colonnes kanban) par un champ. |
|
||||
| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | Agrégats et dimensionnement des colonnes Kanban. |
|
||||
| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | Vues de calendrier : disposition et champ de date qui positionne les enregistrements. |
|
||||
|
||||
Tous les enums ci-dessus sont exportés depuis `twenty-sdk/define`.
|
||||
|
||||
## Filtres
|
||||
|
||||
Une vue peut être livrée avec des filtres préappliqués. Chaque filtre possède trois coordonnées : le **champ** faisant l’objet du filtrage, l’**opérateur** (comment comparer) et la **valeur** (par rapport à quoi comparer). Les trois doivent être alignées : l’utilisation d’un opérateur qui ne s’applique pas à un type de champ sera rejetée au moment de la synchronisation.
|
||||
|
||||
```ts
|
||||
import { ViewFilterOperand } from 'twenty-shared/types';
|
||||
import { ViewFilterOperand } from 'twenty-sdk/define';
|
||||
|
||||
filters: [
|
||||
{
|
||||
|
||||
@@ -51,8 +51,12 @@ export default defineLogicFunction({
|
||||
```
|
||||
|
||||
Types de déclencheurs disponibles :
|
||||
* **httpRoute** : Expose votre fonction sur un chemin et une méthode HTTP **sous l'endpoint `/s/`** :
|
||||
> p. ex. `path: '/post-card/create'` est appelable à `https://your-twenty-server.com/s/post-card/create`
|
||||
* **httpRoute** : Expose votre fonction sur un chemin HTTP et une méthode dans l'URL de **fonctions de base** de votre espace de travail — la valeur de 20 injects en tant que `TWENTY_FUNCTIONS_URL` (sur Twenty Cloud, un domaine dédié par espace de travail):
|
||||
> p. ex. `path: '/post-card/create'` est appelable à `https://your-workspace.withtwenty.com/post-card/create`
|
||||
|
||||
<Warning>
|
||||
L'ancienne route de préfixe `/s/` (`https://your-twenty-server.com/s/post-card/create`) est **obsolète sur Twenty Cloud** et sera désactivée le **2026-07-24**. Il reste disponible pour les instances auto-hébergées et locales qui ne configurent pas un domaine de fonctions isolées — utilisez `TWENTY_FUNCTIONS_URL` quand il est défini, et revenez à `\<server-url>/s/\<path>` autrement.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
Pour appeler une fonction logique déclenchée par une route depuis un composant frontal (sans interface), consultez [Appeler une fonction logique](/l/fr/developers/extend/apps/layout/front-components#calling-a-logic-function).
|
||||
|
||||
@@ -40,13 +40,13 @@ La **couche logique** d’une application Twenty est le code qui *s’exécute*
|
||||
|
||||
Une fonction logique choisit un ou plusieurs déclencheurs — chaque entrée ci-dessous est un champ distinct sur `defineLogicFunction()`:
|
||||
|
||||
| Déclencheur | Moment d’exécution | Paramètre |
|
||||
| -------------------------------- | ---------------------------------------------------------------------------- | ------------------------------- |
|
||||
| **Route HTTP** | Une requête atteint votre point de terminaison `/s/\<path>` | `httpRouteTriggerSettings` |
|
||||
| **Cron** | Une expression CRON correspond | `cronTriggerSettings` |
|
||||
| **Événement de base de données** | Un enregistrement de l’espace de travail est créé, mis à jour ou supprimé | `databaseEventTriggerSettings` |
|
||||
| **Outil IA** | Une fonctionnalité IA de Twenty décide d’appeler votre fonction | `toolTriggerSettings` |
|
||||
| **Action de flux de travail** | Une étape de flux de travail invoque votre fonction | `workflowActionTriggerSettings` |
|
||||
| Déclencheur | Moment d’exécution | Paramètre |
|
||||
| -------------------------------- | ------------------------------------------------------------------------- | ------------------------------- |
|
||||
| **Route HTTP** | Une requête atteint l'URL publique de votre fonction | `httpRouteTriggerSettings` |
|
||||
| **Cron** | Une expression CRON correspond | `cronTriggerSettings` |
|
||||
| **Événement de base de données** | Un enregistrement de l’espace de travail est créé, mis à jour ou supprimé | `databaseEventTriggerSettings` |
|
||||
| **Outil IA** | Une fonctionnalité IA de Twenty décide d’appeler votre fonction | `toolTriggerSettings` |
|
||||
| **Action de flux de travail** | Une étape de flux de travail invoque votre fonction | `workflowActionTriggerSettings` |
|
||||
|
||||
Les fonctions s’exécutent dans un environnement isolé dans des processus Node.js sandboxés et accèdent à l’espace de travail via un client API typé, limité au rôle déclaré sur [`defineApplication()`](/l/fr/developers/extend/apps/config/application).
|
||||
|
||||
|
||||
@@ -4,7 +4,25 @@ description: Les commandes `yarn twenty` pour exécuter des fonctions, diffuser
|
||||
icon: terminal
|
||||
---
|
||||
|
||||
Au-delà de `dev`, `dev:build`, `dev:add` et `dev:typecheck`, la CLI `yarn twenty` fournit des commandes pour exécuter des fonctions, consulter les journaux et gérer les installations d'applications.
|
||||
Le CLI `yarn twenty` est votre interface pour tout ce qui concerne les applications. Liste complète des commandes :
|
||||
|
||||
| Commande | Ce que cela fait | Documenté dans |
|
||||
| ----------------------------------------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `dev` | Surveille les fichiers sources et synchronise en direct les modifications | [Prise en main rapide](/l/fr/developers/extend/apps/getting-started/quick-start) |
|
||||
| `plan` | Prévisualiser les modifications de métadonnées sans les appliquer | [Synchronisation et récupération](/l/fr/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) |
|
||||
| `appliquer` | Appliquer les modifications de métadonnées après avoir affiché le plan | [Synchronisation et récupération](/l/fr/developers/extend/apps/operations/sync-and-recovery) |
|
||||
| `dev:build` | Compiler l’application et générer le client d’API (`--tarball` pour empaqueter un `.tgz`) | [Publication](/l/fr/developers/extend/apps/operations/publishing) |
|
||||
| `dev:typecheck` | Exécuter la vérification des types TypeScript | [Tests](/l/fr/developers/extend/apps/operations/testing) |
|
||||
| `dev:add` | Générer la structure d’une nouvelle entité | [Génération de structure](/l/fr/developers/extend/apps/getting-started/scaffolding) |
|
||||
| `dev:generate-client` | Régénérer le client d’API typé | cette page |
|
||||
| `dev:function:exec` / `dev:function:logs` | Exécuter des fonctions et diffuser leurs journaux | cette page |
|
||||
| `dev:translations-extract` | Extraire les chaînes traduisibles dans les catalogues `locales/` | [Traductions](/l/fr/developers/extend/apps/translations/overview) |
|
||||
| `dev:catalog-sync` | Déclencher la synchronisation du catalogue de la place de marché | [Publication](/l/fr/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) |
|
||||
| `app:publish` / `app:install` / `app:uninstall` | Cycle de vie de la mise en production | [Publication](/l/fr/developers/extend/apps/operations/publishing) et cette page |
|
||||
| `docker:*` | Gérer le conteneur du serveur Twenty local | [Serveur local](/l/fr/developers/extend/apps/getting-started/local-server) |
|
||||
| `remote:*` | Gérer les connexions serveur | cette page |
|
||||
|
||||
Chaque commande accepte `-r, --remote \<name>` pour cibler un serveur distant spécifique au lieu de celui par défaut.
|
||||
|
||||
## Exécuter des fonctions (`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
|
||||
```
|
||||
|
||||
## Afficher les journaux des fonctions (`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>
|
||||
```
|
||||
|
||||
Vos identifiants sont stockés dans `~/.twenty/config.json`.
|
||||
|
||||
@@ -229,7 +229,7 @@ yarn twenty dev:catalog-sync
|
||||
# yarn twenty dev:catalog-sync --remote production
|
||||
```
|
||||
|
||||
Les métadonnées affichées dans la place de marché proviennent de votre configuration `defineApplication()` — des champs comme `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` et `termsUrl`.
|
||||
Les métadonnées affichées dans la marketplace proviennent de votre configuration `defineApplication()` — voir [Métadonnées de la marketplace](#marketplace-metadata) ci-dessus.
|
||||
|
||||
<Note>
|
||||
Si votre application ne définit pas de `aboutDescription` dans `defineApplication()`, la place de marché utilisera automatiquement le `README.md` de votre package depuis npm comme contenu de la page À propos. Cela signifie que vous pouvez maintenir un seul README à la fois pour npm et pour la place de marché Twenty. Si vous souhaitez une description différente dans la place de marché, définissez explicitement `aboutDescription`.
|
||||
|
||||
@@ -15,33 +15,44 @@ Pour l'itération locale au quotidien, vous voudrez presque toujours `yarn twent
|
||||
| Vous souhaitez… | Commande | Notes |
|
||||
| ------------------------------------------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Itérer localement avec la synchronisation en direct | `yarn twenty dev` | Surveille vos fichiers et synchronise à chaque modification. |
|
||||
| Synchroniser une fois puis quitter (CI, scripts, hooks) | `yarn twenty dev --once` | Effectue une compilation + synchronisation, puis quitte. |
|
||||
| Prévisualiser les changements **sans les appliquer** | `yarn twenty dev --once --dry-run` | Calcule et affiche le diff ; n'écrit rien. |
|
||||
| Synchroniser une fois puis quitter (CI, scripts, hooks) | `yarn twenty apply` | Effectue une compilation + synchronisation, puis quitte. Ajoutez `--force` pour ignorer la confirmation des changements destructifs. |
|
||||
| Prévisualiser les changements **sans les appliquer** | `yarn twenty plan` | Calcule et affiche le diff ; n'écrit rien. |
|
||||
| Retirer l'application de l'espace de travail | `yarn twenty app:uninstall` | Ajoutez `--yes` pour ignorer la confirmation. |
|
||||
| Envoyer une archive tarball vers un serveur | `yarn twenty app:publish --private` | Nécessite une version de `package.json` **strictement supérieure** — voir [Publication](/l/fr/developers/extend/apps/operations/publishing). |
|
||||
| Publier sur la place de marché (npm) | `yarn twenty app:publish` | — |
|
||||
| Installer / mettre à niveau une version déployée | `yarn twenty app:install` | Installe la version actuellement déployée. |
|
||||
| Effacer le serveur local et repartir de zéro | `yarn twenty docker:reset` | Supprime **toutes** les données locales — en dernier recours. |
|
||||
|
||||
<Note>
|
||||
`yarn twenty dev --once` et `yarn twenty dev --once --dry-run` fonctionnent toujours comme alias obsolètes de `yarn twenty apply` et `yarn twenty plan`.
|
||||
</Note>
|
||||
|
||||
### La synchronisation locale n'a pas besoin d'un incrément de version
|
||||
|
||||
La règle de `version` strictement croissante (`VERSION_ALREADY_EXISTS` lors du déploiement, `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` lors de l'installation) s'applique à **`app:publish` / `app:install`** — le chemin de mise en production. `yarn twenty dev` synchronise votre manifeste sur place et ne nécessite jamais de changement de version, vous n'avez donc pas besoin de toucher à `package.json` pour itérer. Si vous vous surprenez à incrémenter la version pour tester un changement local, c'est que vous utilisez le chemin de mise en production alors que vous voulez la boucle de développement.
|
||||
|
||||
## Lire la sortie de synchronisation
|
||||
|
||||
Chaque synchronisation affiche les changements de métadonnées qu'elle a appliqués (ou appliquerait, avec `--dry-run`) :
|
||||
Chaque synchronisation affiche les changements de métadonnées qu'elle a appliqués (ou qu'elle appliquerait, avec `plan`), à la manière de Terraform — un bloc par entité avec ses attributs, puis une ligne récapitulative :
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
C'est votre premier diagnostic : il vous indique exactement quels objets, champs et mises en page ont changé, afin que vous puissiez confirmer qu'une synchronisation a fait ce que vous attendiez avant de vérifier l'interface utilisateur.
|
||||
|
||||
Les changements destructifs (`to destroy`) sont listés avec ce qu'ils suppriment (par ex. `objectMetadata "auditNote" — drops the table and all its rows`) et nécessitent une confirmation interactive, ou `--force` dans les scripts.
|
||||
|
||||
Lorsqu'une synchronisation échoue sur une seule entité, l'erreur nomme l'entité en cause et son `universalIdentifier`, par exemple :
|
||||
|
||||
```text
|
||||
@@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337)
|
||||
|
||||
Utilisez cet identifiant pour trouver l'entité dans votre manifeste (et, si nécessaire, dans l'espace de travail) plutôt que de deviner laquelle est en conflit.
|
||||
|
||||
## Prévisualiser les changements (dry run)
|
||||
## Prévisualiser les changements (plan)
|
||||
|
||||
`yarn twenty dev --once --dry-run` construit votre manifeste, demande au serveur le plan de migration et l'affiche — **sans rien appliquer**. C'est le moyen sûr de répondre « que changerait cette synchronisation ? » avant de s'y engager.
|
||||
`yarn twenty plan` construit votre manifeste, demande au serveur le plan de migration et l'affiche — **sans rien appliquer**. C'est le moyen sûr de répondre « que changerait cette synchronisation ? » avant de s'y engager.
|
||||
|
||||
```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 plan :
|
||||
|
||||
* **N'écrit rien** — aucune migration de métadonnées, aucune mise à jour de l'enregistrement d'application, aucun changement de rôle/onglet par défaut, et aucune génération de client d'API.
|
||||
* Renvoie le **même diff** qu'une synchronisation réelle appliquerait, afin que vous puissiez examiner à l'avance les entités créées/mises à jour/supprimées.
|
||||
* Est utile avant un changement risqué, lors de la révision d'un changement généré par une IA, ou dans un script qui doit échouer si un changement inattendu est sur le point d'être appliqué.
|
||||
|
||||
<Note>
|
||||
Un dry run ne prévisualise que les changements de **métadonnées**, et il nécessite que l'application ait été synchronisée au moins une fois (pour que l'espace de travail la connaisse). Si vous l'exécutez sur une application qui n'a jamais été synchronisée, le serveur indique que l'application n'est pas installée — exécutez d'abord une fois `yarn twenty dev`.
|
||||
Un plan ne prévisualise que les changements de **métadonnées**, et il nécessite que l'application ait été synchronisée au moins une fois (pour que l'espace de travail la connaisse). Si vous l'exécutez sur une application qui n'a jamais été synchronisée, le serveur indique que l'application n'est pas installée — exécutez d'abord une fois `yarn twenty dev`.
|
||||
</Note>
|
||||
|
||||
## Échelle de récupération
|
||||
|
||||
Lorsque les métadonnées locales semblent incorrectes, augmentez le niveau de manière progressive dans cet ordre et arrêtez-vous dès que vous êtes débloqué. Chaque étape est plus perturbatrice que la précédente.
|
||||
|
||||
1. **Resynchroniser.** Exécutez à nouveau `yarn twenty dev --once`. Les synchronisations sont idempotentes — réexécuter un manifeste propre est sûr et résout souvent un incident passager.
|
||||
2. **Prévisualiser le plan.** Exécutez `yarn twenty dev --once --dry-run` pour voir exactement ce que la prochaine synchronisation compte changer, sans l'appliquer.
|
||||
1. **Resynchroniser.** Exécutez à nouveau `yarn twenty apply`. Les synchronisations sont idempotentes — réexécuter un manifeste propre est sûr et résout souvent un incident passager.
|
||||
2. **Prévisualiser le plan.** Exécutez `yarn twenty plan` pour voir exactement ce que la prochaine synchronisation compte changer, sans l'appliquer.
|
||||
3. **Lire l'erreur nommée.** Si une synchronisation échoue, relevez le type de métadonnées et l'`universalIdentifier` dans le message (voir ci-dessus) et localisez cette entité dans votre manifeste. Un conflit pointe généralement vers un identifiant dupliqué ou réutilisé.
|
||||
4. **Désinstaller et réinstaller.** `yarn twenty app:uninstall`, puis synchronisez à nouveau (`yarn twenty dev`). Cette opération reconstruit les métadonnées de l'application à partir d'une base saine tout en gardant le reste de votre espace de travail intact.
|
||||
5. **Réinitialisation complète (en dernier recours).** `yarn twenty docker:reset`, puis réinjectez des données et resynchronisez.
|
||||
|
||||
@@ -78,6 +78,13 @@ Créez un `vitest.config.ts` à la racine de votre application :
|
||||
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,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Créez un fichier de configuration qui vérifie que le serveur est joignable avant l'exécution des tests :
|
||||
Créez un fichier de configuration globale qui vérifie que le serveur est joignable, écrit une configuration de test pour le SDK (`~/.twenty/config.test.json`) et synchronise l’application avant l’exécution des tests :
|
||||
|
||||
```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 });
|
||||
}
|
||||
```
|
||||
|
||||
## APIs programmatiques du SDK
|
||||
|
||||
Le sous-chemin `twenty-sdk/cli` exporte des fonctions que vous pouvez appeler directement depuis le code de test :
|
||||
|
||||
| Fonction | Description |
|
||||
| -------------- | -------------------------------------------------------------------- |
|
||||
| `appBuild` | Construire l'application et éventuellement créer une archive tarball |
|
||||
| `appDeploy` | Téléverser une archive tarball vers le serveur |
|
||||
| `appInstall` | Installer l'application sur l'espace de travail actif |
|
||||
| `appUninstall` | Désinstaller l'application de l'espace de travail actif |
|
||||
| Fonction | Description |
|
||||
| -------------- | ----------------------------------------------------------------------------------- |
|
||||
| `appBuild` | Construire l'application et éventuellement créer une archive tarball |
|
||||
| `appDeploy` | Téléverser une archive tarball vers le serveur |
|
||||
| `appDevOnce` | Construire et synchroniser l’application une fois (identique à `yarn twenty apply`) |
|
||||
| `appInstall` | Installer l'application sur l'espace de travail actif |
|
||||
| `appUninstall` | Désinstaller l'application de l'espace de travail actif |
|
||||
|
||||
Chaque fonction retourne un objet résultat avec `success: boolean` et soit `data` soit `error`.
|
||||
|
||||
@@ -238,64 +253,10 @@ Vous pouvez également exécuter une vérification des types sur votre applicati
|
||||
yarn twenty dev:typecheck
|
||||
```
|
||||
|
||||
Cela exécute `tsc --noEmit` et signale toute erreur de type.
|
||||
Cela exécute `tsc --noEmit` sur le `tsconfig.json` de votre application et signale toute erreur de type. Les applications générées contiennent également un script `yarn typecheck` qui couvre aussi les fichiers de test (`tsconfig.spec.json`).
|
||||
|
||||
## CI avec GitHub Actions
|
||||
|
||||
Le générateur crée un workflow GitHub Actions prêt à l’emploi dans `.github/workflows/ci.yml`. Il exécute automatiquement vos tests d’intégration à chaque push sur `main` et sur les pull requests.
|
||||
Le générateur crée un workflow prêt à l’emploi dans `.github/workflows/ci.yml`. À chaque push sur `main` et à chaque pull request, il lance un serveur Twenty éphémère dans le runner (via l’action `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`), puis exécute `yarn lint`, `yarn typecheck`, `yarn test:unit` et `yarn test` avec `TWENTY_API_URL` / `TWENTY_API_KEY` pointant vers ce serveur. Aucun secret n’est requis, et vous pouvez fixer la version du serveur via la variable d’environnement `TWENTY_VERSION` en haut du workflow.
|
||||
|
||||
Le workflow :
|
||||
|
||||
1. Récupère votre code
|
||||
2. Lance un serveur Twenty temporaire en utilisant l’action `twentyhq/twenty/.github/actions/spawn-twenty-docker-image`
|
||||
3. Installe les dépendances avec `yarn install --immutable`
|
||||
4. Exécute `yarn test` avec `TWENTY_API_URL` et `TWENTY_API_KEY` injectés à partir des sorties de l’action
|
||||
|
||||
```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 }}
|
||||
```
|
||||
|
||||
Vous n’avez pas besoin de configurer de secrets — l’action `spawn-twenty-docker-image` démarre un serveur Twenty éphémère directement dans le runner et fournit les détails de connexion. Le secret `GITHUB_TOKEN` est fourni automatiquement par GitHub.
|
||||
|
||||
Pour épingler une version spécifique de Twenty au lieu de `latest`, modifiez la variable d’environnement `TWENTY_VERSION` en haut du workflow.
|
||||
Voir [Publication → CI/CD automatisé](/l/fr/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) pour un guide complet des deux workflows générés (`ci.yml` et le pipeline de déploiement `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 }),
|
||||
@@ -185,7 +187,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 @@ Le même gestionnaire peut également répondre aux requêtes HTTP. Nous allons
|
||||
* un point de terminaison **POST** que l'interface utilisateur appelle pour générer un document, et
|
||||
* un point de terminaison public **GET** qui rend un document en tant que page web imprimable.
|
||||
|
||||
Les deux utilisent `httpRouteTriggerSettings`. Les routes des applis sont servies dans `/s` sur votre
|
||||
Serveur Vingt (par exemple `http://localhost:2020/s/documents/generate`).
|
||||
Les deux utilisent `httpRouteTriggerSettings`. Sur le serveur de développement local, les routes des applications sont
|
||||
servies sous le préfixe `/s` (par exemple `http://localhost:2020/s/documents/generate`).
|
||||
|
||||
<Note>
|
||||
Sur Twenty Cloud, les routes sont servies sur le domaine de fonctions dédiées à l'espace de travail
|
||||
— l'URL 20 injecte en tant que `TWENTY_FUNCTIONS_URL`, sans préfixe `/s`. Le préfixe `/s`
|
||||
est déprécié là-bas et ne reste que pour les instances auto-hébergées et locales.
|
||||
Voir [Appel à une fonction logique] (/developers/extend/apps/layout/front-components#calling-a-logic-function).
|
||||
</Note>
|
||||
|
||||
## Itinéraire POST — générer à la demande
|
||||
|
||||
|
||||
+3
-3
@@ -77,11 +77,11 @@ Exécuter les mêmes portes CI :
|
||||
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
|
||||
```
|
||||
|
||||
La course à sec imprime exactement ce qui pourrait changer sur le serveur sans l'appliquer —
|
||||
une bonne vérification de l'état d'esprit. Voir
|
||||
Le plan affiche exactement ce qui changerait sur le serveur sans les appliquer —
|
||||
un bon dernier contrôle de cohérence. Voir
|
||||
[Testing](/l/fr/developers/extend/apps/operations/testing) et
|
||||
[Synchronisation et récupération](/l/fr/developers/extend/apps/operations/sync-and-recovery).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user