CARL Source
WebHooks [Général]
Système > Échanges de données > WebHooks > WebHooks : Formulaires > WebHooks [Général]

Depuis ce formulaire, vous pouvez créer ou modifier un WebHook.

Informations générales 

 

Si le nombre d'erreurs dépasse le seuil configuré pour le délai donné, le WebHook sera automatiquement désactivé et un mail d'avertissement sera envoyé au responsable désigné.

Tous les échecs seront tracés et visibles dans l'onglet Échecs du WebHook.

Le paramètre WEBHOOK_MAIL_TMPL du module "Échange de données" permet de définir le modèle de message utilisé pour l'envoi du mail.

 

Le paramètre WEBHOOK_ERROR_HORIZON du module "Échange de données" permet de définir la durée de conservation des échecs.

Actuellement la suppression des échecs trop ancien n'est pas faite via un traitement automatique.
Elle ne se déclenche qu'au moment de l'ajout de nouveaux échecs.

Il est donc possible que des échecs restent présent sur une période plus grande que celle prévue dans l'horizon.
Le but du mécanisme étant avant tout d'éviter une accumulation indéfinie des traces d'échecs.
Il est néanmoins possible de les supprimer manuellement dans l'onglet Échecs.

 

Il est possible d'ajouter des documents liés au WebHook ; ceux-ci permettant par exemple de détailler le WebHook en question.

 

WebHooks de type "Entité"

Ce type de WebHook permet d'envoyer un message au format JSON à un système externe lors d'un évènement CRUD (Création, Modification, Suppression) sur des entités.

Définition

La définition du WebHook est une description au format JSON qui permet d'indiquer :

Un WebHook pouvant concerner plusieurs types d'entités, la définition est une liste de descriptions contenant ces informations : quelles entités, pour quels évènements, émettent un message avec quels attributs.

La définition a la format :

{
    "entities": [
        { <entité / évènements / attributs> },
        { <entité / évènements / attributs> },
        { <entité / évènements / attributs> }
    ]
}

 

Chaque descripteur d'entité a lui le format :

{
    "entity": [
        <critères>, ...
    ],
    "events": [ <évènements>, ... ],
    "attributes": {
        "attribut1": "${expression1}",
        ...
    }
}

Entity

La première partie "entity" permet de sélectionner les entités pour lesquelles on souhaite être notifié.
Il s'agit d'une liste de critères ("matchers") qui sont évalués dans l'ordre et qui doivent tous être validés pour que la notification soit envoyée.

Actuellement il y a 3 types de critères :

Type Exemple Description
typeHierarchy { "match": "typeHierarchy", "value": "box" } Permet de sélectionner tous les types d'entités d'une hiérarchie de types dont la racine est donnée.
Par exemple pour "box" : les points de structure (box), et les types FM : espaces (space), étages (floor), salles (room), etc.
exactType { "match": "exactType", "value": "box" } Permet de sélectionner précisément et uniquement les entités du type donné.
Par exemple, "box" ne sélectionnera que les points de structure, mais pas les espaces (space), étages (floor), salles (room), etc.
attributeValue { "match": "attributeValue", "attribute": "use", "value": "TECHNICAL" }

Permet de sélectionner les entités dont l'attribut indiqué a la valeur précisée.
Un attribut de ce nom doit exister sur au moins l'un des types d'entité précisé en amont dans la chaîne. Mais il n'est pas nécessaire qu'il existe sur tous. Cela permet de filtrer sur un attribut qui n'est présent que sur certains types d'entité d'une hiérarchie.
Comme l'attribut "use" de l'exemple qui est présent sur les salles (room) et les bâtiments (building) mais pas sur les points de structures de façon générale (box).

Le nom d'attribut doit être un nom d'attribut simple porté par l'entité.
Il n'est pas possible d'utiliser un nom composé séparé par un "." comme "status.code".

La valeur doit être une chaîne de caractères au format interne utilisé par CARL Source pour représenter des valeurs de différents types (ex. dans les fichiers de chargement XML).
Par exemple : "0" pour l'entier 0, "true" pour le booléen vrai, ...

Pour l'instant le seul opérateur disponible est l'égalité.
Il n'est pas encore possible de faire des comparaisons de type ">", ">=", "<", "<=".

Le premier critère de la liste doit obligatoirement porter sur un type d'entité.
Il n'est pas possible d'utiliser uniquement un critère de type "attributeValue".
Il faut en premier lieu déterminer un ensemble d'entité sur lesquelles portent les critères suivants.

 

Events

Cette partie permet d'indiquer le type d'évènements sur les entités sélectionnées pour lesquels on veut être notifié : création d'entité (created), modification d'entité (updated), suppression d'entité (deleted).

Si cette partie n'est pas précisée ("events" absent ou null), alors tous les types d'évènements seront notifiés.

Si la liste est vide, un message d'alerte sera présent dans les logs de l'application indiquant que cette partie ne capturera aucun évènement mais aucune erreur ne bloquera l'activation de l'ensemble du WebHook.
Cela peut être utile pour désactiver temporairement une partie d'un WebHook pour certaines entités sans supprimer cette partie de la définition et pouvoir la réactiver plus tard

 

Attributes

Cette partie permet de lister les attributs de l'entité qui seront inclus dans le message de notification sous la forme :

"<nom d'attribut dans le message JSON>": "${<expression utilisée pour produire la valeur mise dans le message>}" 

Le nom d'attribut est une chaîne quelconque.

L'expression doit être de la forme "${entity.attr}".
Il est possible de chaîner l'expression sur plusieurs niveaux : "${entity.attrA.attrB}".

Le chaînage peut entrainer des impacts sur les performances de l'application.
En effet, il peut impliquer la récupération, en base de données, d'informations qui ne sont pas disponibles sur l'entité au moment où l'évènement se produit. Notamment lorsqu'il est utilisé pour suivre des relations vers d'autres entités liées.

Pour avoir l'information sur le type d'évènement qui produit la notification, il est possible d'utiliser un attribut dédié avec l'expression : "${event}", qui aura les valeurs : "created", "updated", ou "deleted".

Il est aussi possible de ne pas mettre d'expression et d'indiquer directement la valeur qui sera incluse dans le message JSON. Par exemple :

 

Sécurité

Sur les WebHooks de type "Entité", il est possible de demander la génération d'un jeton d'authentification unique.
Ce jeton sert de secret partagé avec le système externe destinataire de la requête issue du WebHook.

Si un jeton est défini sur un WebHook, une entête http X-CS-HMAC-SHA256 est ajoutée à la requête.
Cette entête contient le hash (élément crypté) du message JSON envoyé.

À la réception, le système externe est libre de produire un hash du JSON reçu en utilisant le jeton et les mêmes algorithmes (HMAC + SHA256) et de comparer le résultat avec celui stocké dans l'entête.
Si les résultats sont identiques, l'origine du JSON reçu est alors authentifié, sinon il peut être rejeté.

 

Entêtes HTTP

La configuration des entêtes HTTP n'est disponible qu'à partir de la version 7.3.3 de CARL Source mais également à partir du patch P23 de la version 7.2.0.

 Quelques précisions sur la définition des entêtes HTTP :

 

Exemple de définition

{
    "headers": [
        { "name": "some_header", "value": "something" },
        { "name": "my_header", "value": "value_1" },
        { "name": "my_header", "value": "value_2" }
    ],
    "entities": [
        {
            "entity": [
                { "match": "typeHierarchy", "value": "mr" }
            ],
            "events": ["created", "updated", "deleted"],
            "attributes": {
                "event": "${event}",
                "code": "${entity.code}",
                "description": "${entity.description}",
                "lastModTime": "${entity.modifyDate}",
                "eqptBroken": "${entity.eqptBroken}",
                "expectedAmount": "${entity.amount}",
                "delay": "${entity.delay}"
            }
        }, {
            "entity": [
                { "match": "exactType", "value": "room" },
                { "match": "attributeValue", "attribute": "use", "value": "TECHNICAL" }
            ],
            "events": [ "deleted" ],
            "attributes": {
                "code": "${entity.code}",
                "description": "${entity.description}",
                "lastModTime": "${entity.modifyDate}",
                "projectGuid": "${entity.bimGuid}",
                "area": "${entity.area}"
            }
        }
    ]
}

Exemple de message envoyé

{
    "data": [     
        {
            "type": "mr",
            "id": "183368af405-c1",
            "attributes": {
                "event": "updated",
                "code": "000003",
                "description": "test",
                "modifyDate": "2022-09-13T15:01:28.851+02:00",
                "eqptBroken": false,
                "amount": 0.0,
                "delay": null,
                "symptom": "ALERTE",
            }
        },
        {
            "type": "mr",
            "id": "18014fc38127-5a",
            "attributes": {
                "event": "created",
                "code": "000007",
                "description": "test 2",
                "modifyDate": "2022-09-13T15:01:29.583+02:00",
                "eqptBroken": true,
                "amount": 5864.0,
                "delay": null,
                "symptom": "ALERTE",
            }
        }
     ]
}

WebHooks de type "ArcGIS"

Ce type de WebHook permet d'aller plus loin qu'une simple notification et permet de synchroniser les données des entités CARL Source avec celles d'une Géodatabase d'un serveur ArcGIS en passant par un FeatureService et son API REST applyEdits.

Comme pour un WebHook sur les entités, CARL Source enverra des notifications (synchronisation) au fil de l'eau lors de la modification des entités mais il y a un certain nombre de différences.

Pour ces WebHooks :

 

Définition

La définition au format JSON est très similaire à celle d'un WebHook de type entité et décrit un ensemble de "layers" du FeatureService et pour chacun, quelles entités seront synchronisées avec ces layers.

{
    "layers": [
        { <entité / layer / attributs> },
        { <entité / layer / attributs> },
        { <entité / layer / attributs> }
    ]
}

 

Chaque descripteur d'entité a lui le format :

{
    "entity": [
        <critères>, ...
    ],
    "layer": { "id": <id numérique>, "name": "<nom du layer>", "geometryType": "POINT|LINESTRING|POLYGON" },
    "attributes": {
        "attribut1": "${expression1}",
        ...
    }
}

Les parties concernant la sélection des entités et la liste des attributs exportés est identique à celle des WebHooks classiques sur les entités.

La partie "layer" permet d'indiquer les identifiants du layer avec lequel synchroniser les entités sélectionnées.

Ces identifiants sont importants pour permettre à CARL Source d'assurer la synchronisation. En effet CARL Source conserve, pour chaque entité synchronisée, un état de synchronisation lui permettant de déterminer si l'entité a déjà été synchronisée, et avec quel layer. Cela est nécessaire de façon à traduire de façon appropriée les évènements sur les entités (création de l'entité, ajout d'une géométrie, suppression de la géométrie, etc.) en opérations adéquates pour l'API REST d'ArcGIS.

Si le FeatureService sur lequel s'appuie un WebHook est republié, les numéros de layer.id peuvent changer. Il est alors impératif de les mettre à jour dans le définition du WebHook pour éviter les erreurs. Par contre il est impératif de toujours conserver les layer.name à l'identique puisque c'est ce nom qui est conservé par CARL Source pour gérer l'état de synchronisation des entités. Si les noms changent, il faut alors forcer une resynchronisation complète pour le WebHook via un traitement automatique.

 

Lors de la republication d'un FeatureService il est préférable de désactiver le WebHook avant, de le mettre à jour après la republication, de le réactiver, et enfin d'utiliser le traitement automatique pour synchroniser les entités modifiées dans l'intervalle de temps où il était inactif.

A noter que les layers ne peuvent contenir qu'un seul type de géométrie, par conséquent le type de géométrie est aussi utilisé pour filtrer les entités synchronisées avec le layer. Un matériel ayant une géométrie de type "polygone" ne sera pas synchronisé avec un layer ciblant les matériels si ce layer a un type de géométrie "ligne". Si aucun layer ne correspond au filtre et au type de géométrie, l'entité ne sera pas synchronisée même si elle a une géométrie. Si un type d'entité peut avoir plusieurs types de géométries il est nécessaire d'avoir plusieurs déclarations pour plusieurs layers cibles.

 

Sécurité

Sur les WebHooks de type "ArcGIS", il est possible d'indiquer le couple login / mot de passe.
Ces informations sont utilisées pour demander la génération d'un jeton au serveur ArcGIS.
Ce jeton est ensuite adjoint aux requêtes à destination du serveur de cartographie qui peut vérifier sa validité et s'assurer de la légitimité du flux en provenance de CARL Source.

Cette fonctionnalité est optionnelle.

 

Layers du FeatureService ArcGIS

Pour pouvoir initialiser automatiquement un WebHook à partir de la définition d'un FeatureService il faut, comme pour l'initialisation automatique des cartes, nommer les layers avec le préfixe : CARL_<type d'entité>_... . Ces layers seront automatiquement importés, et les autres ignorés.

Les layers doivent également avoir les champs suivants :

 

Nom du champ Type
ENTITYID Text (33)
ENTITYCLASS Text (255)
GlobalID GlobalID / avec un index unique.

Enfin, les layers et le FeatureService doivent avoir la capacité (visible dans la description du FeatureService) : "Supports ApplyEdits With Global Ids: true". Cette capacité est souvent dépendante de la présence du champ GlobalID avec un index unique mais d'autres restrictions peuvent s'appliquer pour que cette capacité soit active. Il faut pour cela consulter la documentation ArcGIS.

 

Exemple de définition

{"layers": [
    {
        "entity": [ {"match": "typeHierarchy", "value": "mr"} ],
        "layer": { "id": 0, "name": "CARL_MR_POINT", "geometryType": "POINT" },
        "attributes": { "xtraTxt01": "${entity.description}" }
    },
    {
        "entity": [ {"match": "typeHierarchy", "value": "wo"} ],
        "layer": { "id": 1, "name": "CARL_WO_POINT", "geometryType": "POINT" },
        "attributes": { "xtraTxt01": "${entity.description}" }
    },
    {
        "entity": [ {"match": "typeHierarchy", "value": "wo"} ],
        "layer": { "id": 2, "name": "CARL_WO_LINE", "geometryType": "LINESTRING" },
        "attributes": { "xtraTxt01": "${entity.description}" }
    },
    {
        "entity": [ {"match": "typeHierarchy", "value": "wo"} ],
        "layer": { "id": 3, "name": "CARL_WO_POLYGON", "geometryType": "POLYGON" },
        "attributes": { "xtraTxt01": "${entity.description}" }
    },
    {
        "entity": [ 
            {"match": "typeHierarchy", "value": "material"},
            {"match": "attributeValue", "attribute": "eqptType", "value": "VHL"}
        ],
        "layer": { "id": 4, "name": "CARL_MATERIAL_POINT", "geometryType": "POINT" },
        "attributes": { 
            "xtraTxt01": "${entity.description}"
            "xtraNum01": "${entity.replacementAge}",
            "xtraDate01": "${entity.lastInventory}"            
        }
    },
    {
        "entity": [ {"match": "typeHierarchy", "value": "material"} ],
        "layer": { "id": 5, "name": "CARL_MATERIAL_LINE", "geometryType": "LINESTRING" },
        "attributes": { "xtraTxt01": "${entity.description}" }
    },
    {
        "entity": [ {"match": "typeHierarchy", "value": "material"} ],
        "layer": { "id": 6, "name": "CARL_MATERIAL_POLYGON", "geometryType": "POLYGON" },
        "attributes": { "xtraTxt01": "${entity.description}" }
    },
    {
        "entity": [ {"match": "typeHierarchy", "value": "box"} ],
        "layer": { "id": 7, "name": "CARL_BOX_POINT", "geometryType": "POINT" },
        "attributes": { "xtraTxt01": "${entity.description}" }
    },
    {
        "entity": [ {"match": "typeHierarchy", "value": "box"} ],
        "layer": { "id": 8, "name": "CARL_BOX_LINE", "geometryType": "LINESTRING" },
        "attributes": { "xtraTxt01": "${entity.description}" }
    },
    {
        "entity": [ {"match": "typeHierarchy", "value": "box"} ],
        "layer": { "id": 9, "name": "CARL_BOX_POLYGON", "geometryType": "POLYGON" },
        "attributes": { "xtraTxt01": "${entity.description}" }
    }
]}

 

Traitement automatique de synchronisation avec un serveur ArcGIS

Lorsqu'un WebHook est actif, il synchronise au fils de l'eau les entités vers le service ArcGIS. Cependant, il peut arriver qu'en cas d'erreur (indisponibilité d'un serveur, coupures réseau, etc.) certaines entités ne soient pas synchronisées correctement. De même, lorsqu'une synchronisation est mise en place sur une base existante, il faut pouvoir synchroniser les entités existantes avant son activation.

Pour ces cas d'usage, CARL Source permet d'ajouter un traitement automatique (classe de traitement : ARCGISWEBHOOKJOB) permettant de déclencher (manuellement ou de façon programmée) une synchronisation de certaines entités. Les entités qui seront (re)synchronisées sont celles qui ont été modifiées entre les deux dates indiquées en paramètre du traitement, ou si aucune n'est précisée, toutes les entités (par exemple pour une synchronisation initiale).

Pour fonctionner, le traitement de synchronisation s'appuie sur la définition d'un WebHook qui doit donc être précisé et être à l'état "Actif" ou "Inactif" (dans cet état la synchronisation au fil de l'eau est désactivée mais le WebHook est valide et utilisable par le traitement).

En plus des dates, il est aussi possible de retreindre les entités synchronisées par le traitement en listant les types d'entités à synchroniser. Il s'agit d'une liste séparée par des virgules. Si elle est vide, tous les types d'entités précisés dans le WebHook seront synchronisées.

Par défaut, le traitement suppose que les états de synchronisation des entités sont valides. Les entités en question n'ont simplement pas été synchronisées (ex. WebHook inactif). Lorsqu'il y a des incohérences, il faut alors "Forcer la resynchronisation". Dans ce cas, les données synchronisées sont supprimées (état de synchronisation dans CARL Source, et données sur l'entité dans le FeatureService ArcGIS), de façon à repartir de zéro pour ces entités, puis ces entités sont de nouveau synchronisées.

 

Durée du traitement

La durée du traitement est évidemment proportionnelle au nombre d'entités à synchroniser. Pour cela il est recommandé de limiter leur nombre en précisant un intervalle de dates, ou en limitant les types d'entités à synchroniser.

Le paramètre "Forcer la resynchronisation" à aussi un impact très fort sur le temps du traitement puisqu'il nécessite au préalable, pour chaque entité, de faire des suppressions.

 

Programmation périodique du traitement

A partir de la version 7.1.0 de CARL Source, un nouveau paramètre intitulé "Entités modifiées depuis la dernière exécution en succès de ce traitement automatique" est disponible sur les traitements automatiques associés à la classe ArcGisWebHookJobBean.
Ce paramètre a pour objectif de faciliter l'ordonnancement périodique du traitement.
En effet, dans le cadre d'une exécution automatique, il n'est, en général, pas souhaitable de fixer une plage temporelle ni de resynchroniser l'ensemble des entités; en revanche, il est intéressant de pouvoir synchroniser uniquement les entités modifiées depuis la dernière exécution réussie du traitement.
Ce paramètre permet donc, s'il est coché, de laisser le système positionner la date de début appropriée correspondant à la date de la dernière exécution du traitement effectuée avec succès. De cette façon, la fenêtre temporelle du traitement glisse au fur et à mesure des exécutions.

Si une date de début est déjà spécifiée sur le traitement, le fait de cocher ce paramètre entrainera le remplacement de cette dernière par la date calculée automatiquement.

À noter que si un traitement automatique était déjà défini avec la classe ArcGisWebHookJobBean avant la mise à jour en version 7.1.0, ce nouveau paramètre ne sera pas disponible.
Pour pouvoir l'utiliser, il sera alors nécessaire de supprimer le traitement actuel et de le recréer.

 

Impact des WebHooks sur les performances

Les WebHooks ont été développés avec l'objectif d'impacter le minimum possible les performances de l'application. Néanmoins, lorsqu'ils sont actifs, ils ajoutent nécessairement des traitements supplémentaires.

Quelques mesures ont été prises pour éviter les impacts négatifs :

Dans tous les cas les WebHooks sont suivis par des indicateurs de performance qui sont disponibles dans le "rapport de performance".

Débogage des WebHooks

Un profil de traces a été ajouté de façon à activer des traces détaillées sur les WebHooks.
Il se nomme WH_DEBUG.

Par ailleurs, pour faciliter les diagnostics, un fichier webhooks.json contenant les définitions des WebHooks a été ajouté dans le Rapport de performance avec les indicateurs suivants :