Bases en réseau : Firebase et CloudDB utilisation et création

Durée estimée: 135 minutes

Notions abordées :

  • la persistance et le partage des données en réseau,
  • l'utilisation de firebase ou cloud DB,
  • les processus asynchrones,
  • la question des mises à jour simultanées ou de concurrence d'accès,
  • la création d'une base de données par projet,
  • les fonctions d'administration, d'édition et d'import/export depuis un navigateur,
  • l'analyse et le traitement des problèmes de mises à jour simultanées ou de concurrence d'accès.
Pour aborder cette leçon, il est recommandé de maitriser la notion de donnée permanente et d'avoir déjà utilisé des composants comme tinyDB pour des enregistrements locaux.

Présentation :

Nous allons commencer par réaliser une application simple qui consiste à

  • enregistrer des données sur Internet,
  • et les consulter ou les mettre à jour depuis plusieurs mobiles.
Ensuite nous verrons les problèmes qui peuvent se poser, pour ne pas tomber dedans, puis les résoudre.

  • Dans la 1° partie, nous allons utiliser Firebase (ou CloudDB) dans la configuration par défaut proposée par App Inventor. Vous devez maîtriser cette configuration avant d'aborder la suite.
  • Dans la 2° partie, nous allons aborder la question du contrôle des données et identifier le problème des mises à jour simultanées par plusieurs intervenants. Nous ne résoudrons pas immédiatement ce problème, mais saurons qu'il est présent pour ne pas tomber dans le piège.
  • Dans la 3° partie nous mettrons en place une version du serveur de données qui sera dédiée à notre projet. Elle nous donnera accès à des fonctions d'administration et d'import/export depuis un navigateur. Elle nous permettra aussi de gérer les droits d'accès, d'éditer les données de façon plus simple, et d'importer ou exporter les données, en particulier au format JSON.
  • Dans la 4° partie nous reviendrons - pour le résoudre - au problème des mises à jour simultanées ou de la concurrence d'accès. Nous examinerons aussi des aspects liés à l'organisation des données (en restant sur des cas simples, et sans aborder les modèles de données relationnels).

Pour commencer, vous pouvez vous limiter à la première partie. Elle gère le partage des données en lecture. Elle ne traite pas correctement les mises à jour simultanées du même jeu de données, par plusieurs utilisateurs. Donc attention!

Ce que vous allez faire :

  • Dans la première partie, vous allez
    1. Créer une application qui :
      • enregistre des données dans une base de données sur internet,
      • lit et modifie les données enregistrées.
    2. Tester cette application depuis deux appareils mobiles
  • Dans la deuxième partie, vous allez
    1. Ajouter une fonction de contrôle qualité des données échangées,
    2. Modifier l'application pour compter des observations,
    3. puis tester cette nouvelle version
      • qui marche très bien quand on est seul à l'utiliser,
      • qui marche "presque tout le temps" quand on est peu nombreux,
      • et qui devient clairement défaillante avec la montée en charge.
    4. puis analyser le problème et voir - dans un prmeier temps - comment l'éviter
      • Nota : Bien évidemment, nous serons tous d'accord pour considérer qu'une application qui marche "presque tout le temps" est une application qui est défaillante c'est à dire qui NE marche PAS. Sauf si le but est de trafiquer les élections ...
  • Dans la troisième partie, vous allez :
    1. Créer une une base de données personnelle sous firebase,
    2. utiliser cette base de données depuis l'application créée dans la 1°partie,
    3. tester son bon fonctionnement,
    4. éditer des données depuis la console d'administration,
    5. examiner la notion de bucket.
  • Dans la quatrième partie, vous allez
    • examiner comment optimiser l'organisation des données pour que leur manipulation soit simple et agréable.

Ressources en entrée :

Compte Google.

Applications de départ : à compléter

Réalisation :

1° partie : Création d'une application avec base de données partagées :

Dans cette première partie, vous pouvez utiliser indifféremment firebaseDB ou cloudDB qui sont dans la catégorie "expérimental" des composants

  • Créez une application, par exemple monProjetFirebase, dans lequel vous allez construire l'interface suivante :

Interface utilisateur


  1. Dans un premier arrangement horizontal, ajoutez :
    • un curseur animé (spinner) avec dans la propriété des éléments de chaîne, une liste de noms séparés par des virgules : par exemple des pokemons
      Pikachu, Scarabrute, Magicarpe, Aquali, Herbizarre, Tentacruel
    • BoutonStore pour déclencher l'enregistrement d'un jeu de données dans la base, avec une étiquette (ou tag) dont le nom correspond à celui affiché par le curseur animé,
    • BoutonGetTag pour demander les données correspondant au tag (étiquette) affiché dans le curseur animé,
    • BoutonClearTag pour supprimer le tag dont le nom est affiché dans le curseur animé,
  2. Dans un deuxième arrangement horizontal, ajoutez :
    • BoutonTagList pour demander la liste des tags (ou étiquettes) enregistrés dans la base,
    • BoutonGetAll pour lire les valeurs de tous les tags de la liste courante,
    • BoutonClearAll pour supprimer tous les tags de la liste courante,
  3. Dans un arrangement vertical défilant (vertical scroll arrangement) de largeur et hauteur " remplir parent",
    • créez un label labelFB de largeur " remplir parent" et de hauteur "automatique",
  4. depuis la catégorie "expérimental" ajoutez
    • un composant FirebaseDB, en laissant les paramètres par défaut.

      Notez que pour firebase, le "projectBucket" correspond au nom du projet (nous aurons l'occasion d'y revenir plus tard avec les bases de données par projet).

  5. et ajoutez enfin
    • un composant Horloge depuis la catégorie des capteurs (qui nous servira seulement pour modifier en continu les données enregistrées),
    • et un composant "notificateur" depuis la catégaorie interface utilisateur (qui nous servira à afficher des messages ou des erreurs en particulier dans la phase de debug/mise au point).

  6. Dans Screen 1, nous suggérons les réglages suivants :
    • AppName : monProjetFirebase,
    • orientation écran : portrait,
    • sizing : fixed,
    • tutorial URL : https://onvaessayer.org/appinventor/basics?app=firebase
    • un icone nuage de petite taille
Nota : Vous pourrez faire le même exercice avec un composant CloudDB(les blocs de programmation sont les mêmes mais à prendre dans CloudDB au lieu de firebaseDB).

Passez à la programmation des blocs :

Dans Firebase (ou CloudDB) chaque jeu de données est enregistré sous une étiquette (ou tag). Chaque étiquette est unique dans la base et permet de récupérer le jeu de données.
Nous allons examiner successivement comment :
  • enregistrer (créer ou mettre à jour) un jeu de données, sous une étiquette (tag) dans la base,
  • lire (ou obtenir) le contenu ou la valeur des données enregistrées sous une étiquette (ou tag),
  • supprimer une étiquette (tag) et le jeu de données associé,
  • obtenir la liste des étiquettes (tags) de données enregistrées dans la base,
  • supprimer toutes les données de la base (all),
  • lire toutes les données de la base (all),
  • être au courant de la modification d'un jeu de données - par nous ou quelqu'un d'autre,
et nous testerons ces fonctions (au fur et à mesure) avec un exemple.

Celui choisi pour démarrer, consiste à partager sur le réseau une liste de Pokemons. Toute personne qui voit un Pokemon enregistre l'heure à laquelle il le voit (avec un nombre qui nous servira plus tard à compter le nombre d'observations).

Programmation :

Dans ce document, nous décrivons l'ensemble du code puis nous passons aux essais. Quand vous suivez le cours, codez au fur et à mesure et faites des tests à chaque étape.
  1. Enregistrer des données dans la base : Dans notre cas, chaque Pokemon correspond à une étiquette (tag), le jeu de données est une liste avec :
    • l'heure à laquelle il a été vu en index 1,
    • le nombre de fois où il a été vu - en index 2 (pour l'instant on laisse ce nombre à 1).
    Quand un utilisateur voit un pokemon, il le sélectionne avec le curseur animé et clique sur le bouton "store". Le script de l'évènement BoutonStore.Clic enregistre le jeu de données en appelant la fonction appeler FirebaseDB.StockerValeur avec
    • en premier paramètre : le tag correspondant au curseur animé (spinner)
    • en second paramètre : le jeu de données qui est une liste avec l'heure issue du composant horloge et 1 pour le nombre d'observations:

      Notez que la procédure Horloge.maintenant, renvoie la date et l'heure dans un format exploitable par le programme, et que la procédure Horloge.format date temps( ... ) convertit cette date dans un format affichable et lisible par nous.

    Ce script enregistre les données dans la base pour ce tag. Si un jeu de données avec le même tag existait déjà dans la base, il sera remplacé, sinon, le tag et ses données seront créés.
    Nous verrons plus bas que l'enregistrement déclenche l'évènement Firebase.datachanged.

  2. Lire les données correspondant à un tag

    Nous avons vu comment enregistrer des données pour un tag. Voyons maintenant comment lire les données associées à un tag.

    • La procédure appeler FirebaseDB.Obtenir valeur demande la lecture du jeu de données correspondant au tag transmis en premier paramètre.
      Le second paramètre est utilisé pour définir la valeur renvoyée si le tag n'est pas trouvé dans la base (on utilise souvent une valeur nulle, i.e. une chaine vide).
      Dans notre exemple, la demande est faite dans le script de l'évènement quand BoutonGetTag.clic .

      Vous noterez que l'on n'attend pas la réponse, car elle peut prendre du temps selon les conditions d'accès à internet.

    • L'arrivée de la réponse correspond à l'évènement quand FirebaseDB.Valeur reçue.
      Dans notre exemple, le tag et value la valeur lue sont affichés en tête de la zone de texte.

  3. Supprimer un tag et le jeu de données associé
    Nous avons vu comment enregistrer des données dans la base, puis comment les lire. Voyons maintenant comment les effacer ou supprimer.
    • La procédure appeler FirebaseDB.Supprimer tag demande la suppression du tag en paramètre et du jeu de données associé.
      Dans notre exemple, la demande est faite dans une procédure appelée dans le script de l'évènement quand BoutonClearTag.clic .
    • Il n'y a pas de réponse.
      (Nous verrons plus bas que la suppression de données ne déclenche pas datachanged).

  4. Obtenir la liste des étiquettes (ou tags) de données de la base

    Nous savons maintenant enregistrer, lire et supprimer des données, mais nous ne savons pas ce que contient la base de données! C'est une des premières informations dont on a besoin quand on se connecte à une base. On veut récupérer la liste des tags déjà dans la base, en l'occurence, la liste des pokemons.

    Firebase permet à l'utilisateur de récupérer cette liste de tags en deux temps (c'est un processus asynchrone) :

    1. on demande d'abord la liste des tags en appelant la procédure FirebaseDB.GetTagList.
      Dans notre exemple, cette demande est déclenchée par l'évènement quand BoutonTaglist.clic .

      Le résultat peut prendre du temps selon les conditions d'accès à internet. Ce script se termine sans rester bloqué à l'attendre.

    2. la réception de la liste des tags correspond à l'évènement quand FirebaseDB.TagList

      Dans notre exemple, le script affiche la liste des tags au début de la zone de texte, puis copie la liste de tags dans la variable DBtagList qui nous servira tout à l'heure.

      Attention : la liste de tags enregistrée dans la variable TagList est pratique pour l'application, mais ne correspond pas toujours à la liste des tags de la base de données qui peut changer en permanence.

      Nota : vous noterez - pendant les essais - que si la base est vide, la procédure appeler FirebaseDB.GetTagList ne déclenche pas l'évènement quand FirebaseDB.TagList(il faut prévoir ce cas).


  5. Supprimer toutes les données de la base
    • Pour supprimer toutes les données de tous les tags on définit la procédure clearAllTags qui itère sur la liste des tags DBtagList et appelle pour chaque tag la procédure clearTag.
    • Pour que la liste des tags DBtagList soit renseignée, il faut avoir cliqué auparavant sur le bouton "tag list" qui demande cette liste.

  6. Lire toutes les données de la base
    • Pour lire toutes les données, on définit la procédure getAllTags qui itère sur la liste des tags DBtagList et appelle pour chaque tag la procédure FirebaseDB.ObtenirValeur.
    • Pour que la liste des tags DBtagList soit renseignée, il faut avoir cliqué auparavant sur le bouton "tag list" qui demande cette liste. Chacun des appels FirebaseDB.ObtenirValeur donnera lieu à une réponse et au déclenchement de l'évènement quand FirebaseDB.Valeur reçue vu plus haut.
  7. Etre au courant des mises à jour et les prendre en compte :

    Les données partagées peuvent être mises à jour par plusieurs personnes à n'importe quel moment. Donc à chaque fois qu'on veut utiliser une donnée partagée, il faut normalement la relire dans la base par un appel à FirebaseDB.Obtenir valeur suivi de l'évènement quand FirebaseDB.Valeur reçue pour la récupérer. Ca marche, ... mais c'est pénible et ça prend du temps (avec tinyWebDB c'est la seule solution).

    Firebase et cloudDB proposent une solution pratique à ce problème en se chargeant de faire savoir à TOUS les utilisateurs connectés, les changements apportés à la base : chaque création ou mise à jour d'un jeu de données déclenche l'évènement Firebase.datachanged avec en paramètres le nom du tag et la valeur enregistrée.

    On peut, comme ci-dessous se limiter à afficher le tag et la valeur modifiés au début de la zone de texte, avant les messages précédents. Selon votre application, vous pourrez modifier ce script pour prendre en compte la mise à jour de tous les tags qui vous intéressent. C'est souvent le plus simple, mais nous verrons dans la 2° partie une réponse beaucoup plus générale qui consiste à entretenir - à l'intérieur de l'application - une variable qui contient, sous forme de liste une copie des donénes de la base.


A ce stade, vous arrivez aux blocs suivants que vous devez tester.

Liste des tests à effectuer avec 2 smartphones, tablettes (ou émulateurs) :

  • Test de l'enregistrement et du partage de données avec 2 téléphones:
    • sur le premier smartphone sélectionnez un pokemon et cliquez sur le bouton "store",
    • sur le même téléphone cliquez sur le bouton "get tag".
    L'heure affichée correspond-elle au moment où vous avez cliqué sur "store"?
    • notez l'heure affichée et recommencez ces deux opérations.
    L'heure affichée a t-elle changé ?
    • sur le 2° smartphone, sélectionnez le même Pokemon et appuyez sur le bouton "get tag".
  • Les données sont-elles identiques aux dernières lues sur le premier téléphone ?
    • Sur le 2° téléphone cliquez sur le bouton "store" et vérifiez la valeur enregistrée avec "get tag".
    • puis - sur le premier smartphone, pour le même pokemon, cliquez sur le bouton "get tag"
    La valeur lue sur le 1° smartphone correspond-elle à celle enregistrée depuis le 2° smartphone?

  • Test de la suppression d'un tag et des données associées
    • Sur le 1° smartphone, pour le même pokemon, cliquez sur le bouton "clear" puis cliquez sur le bouton "get tag".
    La valeur lue correspond-elle à la valeur non trouvée (c'est à dire une valeur vide)?
    • Sur le 2° smartphone, pour le même pokemon, cliquez sur le bouton "get tag".
    Obtenez vous le même résultat?
    • Sur un smartphones sélectionnez un second pokemon et cliquez sur le bouton "store"
    • puis cliquez sur le bouton "tag list"
    Le 2°pokemon est il présent dans la liste et le premier absent(car on l'a supprimé)?
Voilà! vous savez créer, mettre à jour, supprimer des données entre plusieurs smartphones depuis n'importe où dans le monde!


2° partie : suivi continu des mises à jour et contrôle des données

Dans cette 2° partie, nous allons voir comment notre application peut disposer d'une vision simple et à jour de la base de données. Nous verrons ensuite comment controler les données échangées pour éviter les problèmes.

Pour le suivi des données on va principalement se servir de l'évènement Firebase.datachanged pour entretenir une copie des données de la base dans une variable "listOfTagValues". La liste des données qu'elle contient sera mise à jour à chaque évènement quand Firebase.datachanged.

Voici le principe ou le pseudo-code de l'algorithme :
  • initialisation d'une variable listOfTagValues qui contient la liste des données.
  • au démarrage de l'application (quand Screen.initialize), appel à FirebaseDB.GetTagList pour récupérer la liste des tags,
  • Quand la liste des tags revient avec l'évènement quand FirebaseDB.TagList
    • itération sur la liste de tags pour identifier les données qui ne sont pas encore dans la liste listOfTagValues. On demande alors leur lecture avec FirebaseDB.Obtenir valeur,
    • itération sur la liste listOfTagValues pour supprimer les données qui n'existent plus dans la base. Ce sont celles dont le tag n'est pas dans la liste DBtagList reçue de la base : Elles ont été supprimées de la base, donc il faut également les supprimer de listOfTagValues.
  • quand une donnée est reçue, avec l'évènement quand FirebaseDB.Valeur reçue (qui fait suite à une demande FirebaseDB.Obtenir valeur)
    • si le jeu de données pour le tag reçu est déjà dans la liste listOfTagValues on le met à jour,
    • sinon, on l'ajoute à la liste listOfTagValues


Rédaction en cours

Finalement l'ensemble des blocs donne le résultat suivant :


  • Fonctions de contrôle qualité
    • Toute application doit s'asssurer que les données qu'elle reçoit sont conformes à ce que le logiciel sait traiter. C'est vrai en général, mais c'est particulièrement important quand vous partagez des données en réseau.
      Les données peuvent être mises à jour par des intervenants multiples. Ils peuvent se tromper (et vous aussi d'ailleurs ...). Le problème n'est pas de se tromper, c'est de ne pas s'apercevoir des erreurs ou ne pas les anticiper ...
      Donc, par principe, vous devez considérer que les données de la base peuvent être différentes de ce que vous attendez. Vous devez vérifier les données et rejeter celles qui ne correspondent pas au modèle prévu par votre logiciel.

      Par sécurité, vous devez donc définir un modèle de données et la liste des règles à vérifier pour qu'elles soient conformes à ce modèle.

      Dans notre exemple, on peut proposer comme modèle : Toutes les valeurs ou jeux de données devant vérifier les règles suivantes :
      • ce doit être une liste,
      • de taille égale à 2,
      • l'index 1 doit contenir être une chaine de caractères,
      • non vide
      • correspondant à une date,
      • l'index 2 doit être un nombre.
    • Ces contrôles peuvent être codés dans une procédure de contrôle qui renvoie vrai si les règles sont respectés et faux sinon. C'est à vous de définir ces règles en fonction de votre application, pour être certain qu'il n'y aura pas d'erreur.

      Ici on a utilisé des combinaisons logiques, car les tests tiennent sur une seule ligne. Pour des cas plus compliqués, on peut utiliser le canevas suivant est plus général ou permet des vérifications plus compliquées (à coder)

      Ensuite on peut appeler cette procédure de contrôle avant de laisser rentrer ou de alaisser sortir des données.

3° partie : Création d'une base de données pour un projet

  • Connectez-vous (login) à votre compte Google et allez à l'adresse https://console.firebase.google.com/ qui affichera une page du type suivant :
  • Cliquez sur le bouton "ajouter un projet", puis dans le menu de création de projet :
    • renseignez le nom de projet,
    • sélectionnez la région d'hébergement des données,
    • lisez et vérififez les conditions d'utilisation
    • et cochez la reconnaisance d'utilisation,
    • puis cliquez sur le bouton "Créer un projet".

  • Ceci va déclencher la création (après quelques secondes) d'une base de données Firebase avec un abonnement "Spark" qui est gratuit et qui va nous suffire.
    Dans les services associés, sélectionner Database, puis cliquer sur le bouton "Créer une base de données"

    et sélectionner l'option "commencer en mode test".
    Dans cette configuration, en cliquant sur l'onglet des règles, vous verrez que les données seront accessibles en lecture et en écriture par n'importe quelle personne disposant du lien (visible sur internet). Ne mettez aucune donnée personnelle dans cette base avant d'avoir sécurisé les accès. Si vous changez des paramètres, vous devez "publier" les modifications pour les rendre actives.
  • Cliquez sur le bouton à droite de Database et sélectionnez "Real time database"
    Puis récupérez et conservez l'URL de la base de données firebase (désignée par la flèche). Cet URL est le lien utilisé par App Inventor pour accéder à la base de données. Vous noterez dans la ligne du dessous "null", qu'il n'y a aucune donnée pour l'instant.
    Nous pourrions ajouter des données, mais nous allons le faire depuis App Inventor. Si nous le faisions maintenant, l'organisation des données ne correspondrait probablement pas au modèle de données de notre application. Nous tout à l'heure à cet interface.

Création d'un projet App Inventor qui utilise de cette base de données :

Fonctions d'administration et d'import/export :