Aller au contenu principal

Connecteurs

Un connecteur définit comment Liberty Next accède à une source de données. Deux types coexistent :

  • Le connecteur SQL regroupe un ensemble de requêtes nommées exécutées sur un pool de base de données. Le schéma de chaque résultat est obtenu à l'exécution depuis le curseur de la requête ; les libellés et les règles d'affichage sont définis dans le dictionnaire, les options d'affichage côté requête (filtres, visibilité, format) sont portées par la définition de la requête elle-même.
  • Le connecteur API regroupe un ensemble d'endpoints HTTP nommés. L'authentification, l'URL de base et la substitution de variables sont configurées sur le connecteur.

Les connecteurs sont déclarés dans config/connectors.toml. Le fichier est rechargeable à chaud : POST /admin/reload reconstruit le registre, et les requêtes en cours continuent de tourner sur la version qu'elles utilisent.


Vue d'ensemble

📄 connectors.toml[pools.myapp]url = "postgresql+asyncpg://…"password = "ENC:…"max_rows = 5000[connectors.myapp]type = "sql"pool = "myapp"licensed = falsequeries = [...][[connectors.myapp.queries]]name = "users_get"label = "Users"auto_load = truesql = "SELECT id, name, status …"columns = [...]⚙ Connecteur SQLexecute(nom, params, langue)retourne les lignes + le schéma typélibellés et règles résolus à la voléecontrôle d'écritureUPDATE / INSERT / DELETE → writable=true requisliaison :name?paramètres jamais inlinés dans le SQLrésolution #SCHEMA.X#depuis la table schemas du poolSQL par dialectesql = default / oracle / postgresqlRESTPOST/api/query/{c}/{q}POST/api/http/{c}/{e}GET/api/lookup/…Permissionssql:{c}:{q}api:{c}:{e}UtilisateursGrilles de tablePanneaux de dashboardOutils de l'assistant IASélecteurs de lookup

Connecteur SQL

Pool

Un connecteur SQL s'appuie sur un pool : un moteur SQLAlchemy asynchrone.

[pools.myapp]
url = "postgresql+asyncpg://myapp@db:5432/myapp"
password = "ENC:…" # déchiffré via [crypto] master_key — ou ${ENV} / texte clair
pool_size = 10
max_rows = 5000 # plafond SELECT par défaut sur ce pool
dialect = "postgresql" # surcharge optionnelle ; sinon déduit de l'URL

[pools.myapp.schemas]
PROD = "myapp_prod" # `#SCHEMA.PROD#` dans une requête → `myapp_prod` à l'exécution

Le mot de passe peut être indiqué séparément de l'URL (plus propre, hors des logs) ou intégré dans l'URL sous la forme ENC:…. Dans les deux cas, le moteur est construit avec un échappement correct des caractères spéciaux. Si la clé maîtresse est absente ou incorrecte pour une valeur ENC:, celle-ci est conservée telle quelle et un avertissement est journalisé.

Les moteurs sont créés à la demande : une base inaccessible ne bloque jamais le démarrage, et les tests peuvent injecter leur propre moteur.

Connecteur

[connectors.myapp]
type = "sql"
pool = "myapp"
licensed = false # mettre à true pour gérer derrière la clé de licence
max_rows = 5000 # surcharge le défaut du pool

Requêtes

Un connecteur porte une liste ordonnée de requêtes nommées.

[[connectors.myapp.queries]]
name = "users_get"
label = "Users" # titre du panneau dans l'interface React
description = "Application users"
auto_load = true # exécute un SELECT à l'ouverture de l'écran
sql = "SELECT id, name, status FROM users ORDER BY name"
columns = [
{ name = "id", filter = true },
{ name = "name" },
{ name = "status", dd = "USER_STATUS" },
]
ChampDescription
nameNom de la requête au sein du connecteur. Les permissions y font référence sous la forme sql:<connecteur>:<name>.
label / descriptionTextes affichés. L'interface utilise description comme titre du panneau, sinon label, sinon le libellé du menu.
sqlLe texte SQL — soit une chaîne, soit une table par dialecte : sql = { default = "…", oracle = "…" } indexée par nom de backend SQLAlchemy. La clé default est obligatoire.
writabletrue pour toute instruction non-SELECT. Combiné à la permission sql:<c>:<name> de l'appelant.
auto_loadExécute le SELECT à l'ouverture de l'écran, sans attendre un clic Exécuter.
max_rowsPlafond SELECT propre à la requête. Surcharge le défaut du connecteur, puis du pool, puis le défaut global (1000 lignes).
key_columnsColonnes du résultat qui identifient une ligne. Exposées dans describe() pour la correspondance import-Excel de la grille.
columnsOptions d'affichage optionnelles — voir Options de colonne ci-dessous.
paramsListe optionnelle de ParamDef — déclare chaque paramètre :name attendu par la requête, avec valeur par défaut, dd pour le widget d'entrée et label.

Options de colonne

Une entrée columns complète le schéma découvert : elle ne le remplace pas. Tout ce qui n'est pas indiqué provient directement du curseur de la requête.

columns = [
{ name = "id", hidden = true, filter = true },
{ name = "status", dd = "USER_STATUS", width = 120 },
{ name = "amount", format = "amount", align = "right" },
{ name = "score", visible_when = { field = "view_mode", value = "advanced" } },
]
OptionEffet
ddClé d'entrée du dictionnaire — récupère label, format et la règle d'affichage (BOOLEAN / ENUM / LOOKUP). dd = "" désactive l'entrée.
label, formatSurcharge ponctuelle quand l'entrée du dictionnaire ne suffit pas.
hiddenMasque la colonne dans la grille (reste disponible pour les filtres et le formulaire).
filterAjoute la colonne à la ligne de filtre au-dessus de la grille.
filter_fromListe de { source, column } — dépendances de filtre en cascade. Quand source a une valeur, les options LOOKUP de cette colonne sont restreintes aux lignes du lookup dont column correspond.
visible_whenUne règle { field, value } (ou une liste de règles, toutes en ET logique). La colonne disparaît si une règle n'est pas vérifiée.
width, alignOptions de mise en page de la grille.

Filtres d'instruction

Chaque instruction est analysée puis classée avant la liaison :

  • Instructions autorisées : SELECT, INSERT, UPDATE, DELETE, MERGE. Un WITH … SELECT est traité comme un SELECT ; un WITH … DELETE comme un DELETE. Un WITH non analysable est rejeté.
  • Contrôle d'écriture : INSERT / UPDATE / DELETE / MERGE nécessitent writable = true et la permission sql:<c>:<name> de l'appelant. L'absence de l'un ou l'autre déclenche un 403.

Liaison :name?

Chaque jeton :name dans le SQL est réécrit en ? positionnel puis lié via PreparedStatement. Les valeurs ne sont jamais inlinées dans le SQL. L'analyseur respecte :

  • les littéraux entre guillemets simples ('O''Brien'),
  • les identifiants entre guillemets doubles ("customer.name"),
  • les commentaires de ligne et de bloc (-- … / /* … */),
  • l'opérateur de cast PostgreSQL ::type ('foo'::text).

Un paramètre :name non fourni est lié à NULL SQL — la même requête reste donc utilisable pour les chemins create et update.

Placeholders #SCHEMA.X#

Une requête peut référencer #SCHEMA.PROD# (ou toute autre clé) dans son SQL. À l'exécution, le placeholder est remplacé par la valeur correspondante de la table schemas du pool — PROD = "myapp_prod" devient myapp_prod. Un #SCHEMA.X# sans correspondance, ou avec une valeur qui n'est pas un identifiant simple, lève une erreur ConnectorError. Adapté aux bascules dev / prod et aux configurations multi-schémas sous un même utilisateur.

params et lookup_param_binds

Déclarés sur la requête pour que le formulaire React sache quoi demander. Une entrée params peut porter un dd afin que son widget soit piloté par le dictionnaire (BOOLEAN → case à cocher, ENUM → liste déroulante recherchable, LOOKUP → liste déroulante recherchable). Les entrées de type LOOKUP peuvent en plus référencer des valeurs antérieures du formulaire via lookup_param_binds — soit une valeur littérale value, soit un source qui lit l'état courant du formulaire. Cela permet à un WHERE de type UDC de se restreindre correctement.


Connecteur API

[connectors.myservice]
type = "api"
base_url = "https://api.example.com"
auth = "bearer" # none / basic / bearer / api_key / oauth2
auth_token = "ENC:…" # déchiffré à l'exécution ; ${ENV} accepté

[[connectors.myservice.endpoints]]
name = "ping"
method = "GET"
path = "/health"

[[connectors.myservice.endpoints]]
name = "lookup_user"
method = "GET"
path = "/users/{{user_id}}"
ChampDescription
authnone / basic (auth_user + auth_pass) / bearer (auth_token) / api_key / oauth2.
OAuth2POST sur l'endpoint de jeton + extraction par chemin pointé + cache TTL + un rafraîchissement après un 401. Le corps est form-urlencoded ou JSON selon auth_token_content_type.
{{placeholder}}Remplacé dans le chemin, la query-string et le corps depuis les paramètres de l'appel.
endpoint.responseTable de chemins pointés sur la réponse — expose des valeurs nommées aux chaînes d'actions via {call.N.fieldName}.
writableUn endpoint qui émet du POST / PUT / DELETE nécessite writable = true.

Les endpoints exposent POST /api/http/{connecteur}/{endpoint} et sont contrôlés par la permission api:<connecteur>:<endpoint>.


Rechargement à chaud

Modifier connectors.toml puis appeler POST /admin/reload (route réservée aux super-utilisateurs). Le framework reconstruit le registre de connecteurs, relit le dictionnaire et les menus, les remplace sur app.state et libère l'ancien registre. Les requêtes en cours conservent la version qu'elles utilisent — aucune course possible sur une requête en cours d'exécution. Les outils du connecteur de l'assistant IA, eux, ne sont rafraîchis qu'au redémarrage de la JVM, pas au rechargement.

La même route est exposée dans l'onglet Paramètres de React : chaque éditeur écrit via PUT /admin/config/<pools|connectors|dictionary|menus|screens> puis propose un Reload.


Conseils & bonnes pratiques

  • Découvrir, pas redéclarer. Laisser le curseur de la requête piloter le schéma. N'utiliser les options columns que pour ce que le curseur ne peut pas exprimer (libellé, format, visibilité, filtre en cascade).
  • Les entrées du dictionnaire vivent dans le dictionnaire, pas sur chaque requête. Déclarer USER_STATUS une seule fois sous [entries.USER_STATUS] ; toutes les requêtes qui retournent cette colonne y font référence via dd = "USER_STATUS".
  • SQL par dialecte uniquement si nécessaire. Une requête qui fonctionne sur tous les backends reste une chaîne unique. La forme en table n'a de sens que pour de la syntaxe spécifique Oracle ou une fonction dont l'écriture diffère selon le backend.
  • Garder le mot de passe du pool hors de l'URL. Un password = "ENC:…" (ou ${ENV}) à côté de l'URL est plus simple à faire tourner et ne ressort jamais dans la chaîne de connexion journalisée.
  • Toujours marquer writable = true sur les requêtes qui modifient des données. Le contrôle s'effectue aussi à l'exécution, mais le flag TOML est l'endroit naturel pour exprimer l'intention.
  • max_rows se cascade en profondeur : surcharge par appel → requête → connecteur → pool → défaut global (1000). Définir une valeur raisonnable par requête quand l'utilisateur veut souvent consulter l'ensemble de la table.