Boostez votre workflow de développement avec Gemini Code Assist

1. Introduction

e5b98fd4e417c877.png

Dans cet atelier de programmation, vous découvrirez comment Gemini Code Assist peut vous aider à travers les étapes clés du cycle de développement logiciel (SDLC), comme la conception, le développement et à tester et à déployer. Nous allons concevoir et développer une application complète, puis la déployer sur Google Cloud.

Lors d'un événement technique, nous développerons une API et une application permettant d'effectuer des recherches dans toutes les sessions. Chaque session est associée à un titre, un résumé, une durée, des catégories et un ou plusieurs intervenants.

Objectifs de l'atelier

  • Concevoir, compiler, tester et déployer une application Web basée sur une spécification OpenAPI à partir de zéro

Points abordés

  • Utiliser Gemini Code Assist pour générer une spécification OpenAPI
  • Utiliser les fonctionnalités de génération de code de Gemini Code Assist pour développer une application Python Flask pour la spécification OpenAPI
  • Utiliser Gemini Code Assist afin de générer un front-end Web pour l'application Python Flask
  • Utiliser Gemini Code Assist pour obtenir de l'aide afin de déployer l'application sur Google Cloud Run
  • Utilisez les fonctionnalités de Gemini Code Assist telles que l'explication de code et la génération de scénarios de test, lorsque vous créez et testez l'application

Prérequis

  • Navigateur Web Chrome
  • Un compte Gmail
  • Un projet Cloud pour lequel la facturation est activée
  • Gemini Code Assist activé pour votre projet Cloud

Cet atelier s'adresse aux développeurs de tous niveaux, y compris aux débutants. Bien que l'exemple d'application soit en langage Python, vous n'avez pas besoin de maîtriser la programmation Python pour comprendre le processus. Notre objectif sera de vous familiariser avec les fonctionnalités de Gemini Code Assist.

2. Configurer Gemini Code Assist

Cette section décrit tout ce que vous devez faire pour commencer cet atelier.

Activer Gemini Code Assist dans l'IDE Cloud Shell

Pour le reste de l'atelier de programmation, nous utiliserons l'IDE Cloud Shell, un environnement de développement entièrement géré basé sur Code OSS. Nous devons activer et configurer Code Assist dans l'IDE Cloud Shell en suivant les étapes ci-dessous:

  1. Accédez à ide.cloud.google.com. L'affichage de l'IDE peut prendre un certain temps. Veuillez donc faire preuve de patience et accepter les choix de configuration par défaut. Si vous voyez des instructions sur la configuration de l'IDE, veuillez les compléter avec les paramètres par défaut.
  2. Cliquez sur le bouton Cloud Code – Se connecter dans la barre d'état inférieure (voir ci-dessous). Autorisez le plug-in comme indiqué. Si Cloud Code – Aucun projet est affiché dans la barre d'état, cliquez dessus. Dans la liste des projets, sélectionnez le projet Google Cloud que vous comptez utiliser.

6f5ce865fc7a3ef5.png

  1. Cliquez sur le bouton Code Assist en bas à droite, comme illustré, et sélectionnez une dernière fois le projet Google Cloud approprié. Si vous êtes invité à activer l'API Cloud AI Companion, veuillez le faire et continuer.
  2. Après avoir sélectionné votre projet Google Cloud, vérifiez qu'il s'affiche dans le message d'état Cloud Code de la barre d'état et que Code Assist est également activé à droite, dans la barre d'état, comme illustré ci-dessous:

709e6c8248ac7d88.png

Gemini Code Assist est prêt à l'emploi !

3. Configurer Firestore

Cloud Firestore est une base de données de documents sans serveur entièrement gérée que nous utiliserons comme backend pour nos données d'application. Dans Cloud Firestore, les données sont structurées en collections de documents.

Nous devons créer une collection nommée sessions dans notre base de données Firestore par défaut. Cette collection contiendra des exemples de données (documents) que nous utiliserons ensuite dans notre application.

Ouvrez le terminal depuis votre IDE Cloud Shell via le menu principal, comme indiqué ci-dessous:

f1535e14c9beeec6.png

Nous devons créer une collection nommée sessions. Elle contiendra une liste d'exemples de documents de session. Chaque document comporte les attributs suivants:

  1. title: chaîne
  2. categories: tableau de chaînes
  3. speakers: tableau de chaînes
  4. duration: chaîne
  5. summary: chaîne

Remplissons cette collection avec des exemples de données en copiant un fichier contenant les exemples de données dans un bucket de votre propre projet, à partir d'où vous pouvez ensuite importer la collection via la commande gcloud firestore import.

Initialisation de la base de données Firestore

Accédez à la page Firestore dans la console Cloud.

Si vous n'avez pas encore initialisé de base de données Firestore dans le projet, créez la base de données default. Lors de la création de la base de données, utilisez les valeurs suivantes:

  • Mode Firestore: Native
  • Emplacement: sélectionnez Region comme type d'emplacement, puis la région appropriée pour votre application. Notez cet emplacement, car vous en aurez besoin à l'étape suivante pour déterminer l'emplacement du bucket.
  • Créez la base de données.

504cabdb99a222a5.png

Nous allons maintenant créer la collection sessions en procédant comme suit:

  1. Créez un bucket dans votre projet à l'aide de la commande gsutil ci-dessous. Remplacez la variable <PROJECT_ID> dans la commande ci-dessous par l'ID de votre projet Google Cloud. Remplacez <BUCKET_LOCATION> par un nom de région correspondant à la zone géographique de votre base de données Firestore par défaut (comme indiqué à l'étape précédente). Il peut s'agir de US-WEST1, EUROPE-WEST1, ASIA-EAST1 :
gsutil mb -l <BUCKET-LOCATION> gs://<PROJECT_ID>-my-bucket
  1. Maintenant que le bucket est créé, nous devons y copier l'exportation de la base de données que nous avons préparée, avant de l'importer dans la base de données Firebase. Utilisez la commande ci-dessous:
gsutil cp -r gs://sessions-master-database-bucket/2024-03-26T09:28:15_95256  gs://<PROJECT_ID>-my-bucket

Maintenant que nous disposons des données à importer, nous pouvons passer à la dernière étape, qui consiste à les importer dans la base de données Firebase (default) que nous avons créée.

  1. Utilisez la commande gcloud ci-dessous:
gcloud firestore import gs://<PROJECT_ID>-my-bucket/2024-03-26T09:28:15_95256

L'importation prendra quelques secondes. Une fois qu'elle sera prête, vous pourrez valider votre base de données Firestore et la collection en accédant à https://console.cloud.google.com/firestore/databases, puis en sélectionnant la base de données default et la collection sessions comme indiqué ci-dessous:

d3e294d46ba29cd5.png

La création de la collection Firestore que nous allons utiliser dans notre application est maintenant terminée.

4. Créer le modèle d'application

Nous allons créer un exemple d'application (une application Python Flask) que nous utiliserons tout au long de cet atelier de programmation. Cette application effectuera une recherche parmi les sessions proposées lors d'une conférence technique.

Procédez comme suit :

  1. Cliquez sur le nom du projet Google Cloud dans la barre d'état ci-dessous.

f151759c156c124e.png

  1. Une liste d'options s'affiche alors. Cliquez sur Nouvelle application dans la liste ci-dessous.

91ea9836f38b7f74.png

  1. Sélectionnez Application Cloud Run (il s'agira de l'environnement d'exécution de notre application).
  2. Sélectionnez le modèle d'application Python (Flask): Cloud Run.
  3. Attribuez un nom à l'application et enregistrez-la à l'emplacement de votre choix.
  4. Une notification confirme que votre application a été créée. Une nouvelle fenêtre s'ouvre et l'application est chargée, comme illustré ci-dessous. Un fichier README.md est ouvert. Vous pouvez fermer cette vue pour le moment.

aaa3725b17ce27cf.png

5. Interagir avec Gemini Code Assist

Pour les besoins de cet atelier, nous allons utiliser Gemini Code Assist Chat, disponible dans l'IDE Cloud Shell, avec l'extension Cloud Code de VS Code. Pour l'afficher, cliquez sur le bouton "Code Assist" dans la barre de navigation de gauche. Recherchez l'icône Code Assist a489f98a34898727.png dans la barre de navigation de gauche et cliquez dessus.

Le volet de chat Code Assist s'affiche dans l'IDE Cloud Shell, et vous pouvez discuter avec Code Assist.

14ad103efaa0ddaa.png

Notez l'icône de la corbeille située en haut. Elle vous permet de réinitialiser le contexte de l'historique des discussions de Code Assist. Notez également que cette interaction de chat dépend du ou des fichiers sur lesquels vous travaillez dans l'IDE.

6. Conception d'API

La première étape consistera à bénéficier de l'aide de Gemini Code Assist pendant la phase de conception. Au cours de cette étape, nous allons générer une spécification OpenAPI pour les entités (sessions techniques en cas d'événement) que nous voulons parcourir.

Saisissez la requête suivante:

Generate an Open API spec that will allow me to retrieve all sessions, sessions by category, session by id. Each session has the following attributes: id, title, list of speakers, list of categories, summary and duration.

Cela devrait générer une spécification OpenAPI permettant d'effectuer des recherches dans plusieurs sessions à l'aide de divers paramètres de requête. L'exemple de spécification est présenté ci-dessous:

openapi: 3.0.0
info:
 title: Sessions API
 description: This API allows you to retrieve all sessions, sessions by category, and session by id.
 version: 1.0.0
servers:
 - url: https://sessions.example.com
paths:
 /sessions:
   get:
     summary: Get all sessions
     operationId: getSessions
     responses:
       '200':
         description: OK
         content:
           application/json:
             schema:
               type: array
               items:
                 $ref: '#/components/schemas/Session'
 /sessions/{id}:
   get:
     summary: Get session by id
     operationId: getSessionById
     parameters:
       - name: id
         in: path
         required: true
         description: The id of the session
         schema:
           type: string
     responses:
       '200':
         description: OK
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/Session'
 /sessions/categories/{category}:
   get:
     summary: Get sessions by category
     operationId: getSessionsByCategory
     parameters:
       - name: category
         in: path
         required: true
         description: The category of the sessions
         schema:
           type: string
     responses:
       '200':
         description: OK
         content:
           application/json:
             schema:
               type: array
               items:
                 $ref: '#/components/schemas/Session'
components:
 schemas:
   Session:
     type: object
     properties:
       id:
         type: string
         description: The id of the session
       title:
         type: string
         description: The title of the session
       speakers:
         type: array
         items:
           type: string
         description: The list of speakers for the session
       categories:
         type: array
         items:
           type: string
         description: The list of categories for the session
       summary:
         type: string
         description: The summary of the session
       duration:
         type: string
         description: The duration of the session

Vous pouvez noter que la spécification comporte les éléments suivants:

  • Schéma défini pour le type de session.
  • Plusieurs chemins d'accès à l'API ont été définis:
  • /sessions
  • /sessions/{id}
  • /sessions/categories/{category}

Créez un fichier intitulé sessionsapi.yaml dans le dossier supérieur et copiez le contenu depuis la fenêtre de chat de Code Assist à l'aide de l'option "Insérer dans le fichier actuel". (bouton +) et laissez le fichier ouvert dans l'IDE Cloud Shell.

À ce stade, voici une fonctionnalité intéressante de Gemini Code Assist: citation. Ces informations sont présentées au développeur lorsque le code généré cite directement et largement une autre source, comme le code Open Source existant. Elle fournit au développeur la source et la licence lui permettant de décider de ce qu'il doit en faire.

En supposant que le contenu généré nous convient, nous pouvons maintenant utiliser ce document de spécification pour générer une application Python Flask pour ce contenu.

7. Générer l'application

Nous allons maintenant demander à Code Assist de générer l'application. Affichez l'invite suivante après avoir ouvert le fichier sessionsapi.yaml.

Generate a Python Application using the Flask framework, based on the sessionsapi.yaml file. This application uses a local in memory list of sessions. Do not use any Flask extensions.

Vous devriez ainsi obtenir un squelette pour l'application Python Flask, basé sur les fonctionnalités et les chemins d'accès spécifiés dans le fichier de spécification OpenAPI.

Le code d'application Python Flask fourni doit être semblable à celui-ci:

from flask import Flask, jsonify, request

app = Flask(__name__)

sessions = [
    {
        "id": "1",
        "title": "Session 1",
        "speakers": ["Speaker 1", "Speaker 2"],
        "categories": ["Category 1", "Category 2"],
        "summary": "This is a summary of session 1.",
        "duration": "1 hour",
    },
    {
        "id": "2",
        "title": "Session 2",
        "speakers": ["Speaker 3", "Speaker 4"],
        "categories": ["Category 3", "Category 4"],
        "summary": "This is a summary of session 2.",
        "duration": "1 hour 30 minutes",
    },
]

@app.route('/sessions', methods=['GET'])
def get_sessions():
    return jsonify(sessions)

@app.route('/sessions/<id>', methods=['GET'])
def get_session_by_id(id):
    session = next((session for session in sessions if session['id'] == id), None)
    if session is None:
        return jsonify({}), 404
    return jsonify(session)

@app.route('/sessions/categories/<category>', methods=['GET'])
def get_sessions_by_category(category):
    sessions_by_category = [session for session in sessions if category in session['categories']]
    return jsonify(sessions_by_category)

if __name__ == '__main__':
    app.run()

Un fichier app.py existant a été généré à l'étape précédente. Il vous suffit de remplacer son contenu par le code généré par Code Assist et d'enregistrer le fichier.

Nous aimerions modifier la ligne app.run() pour qu'elle utilise le port 8080, l'adresse de l'hôte 0.0.0.0, et l'exécute en mode débogage lors de l'exécution locale.Voici un moyen de procéder. Tout d'abord, mettons en surbrillance/sélectionnons la ligne:

app.run()

Ensuite, dans l'interface Chat de Code Assist, saisissez la requête suivante: Explain this.

Vous devriez voir une explication détaillée de la ligne concernée, comme illustré ci-dessous:

58ec896a32a4fb68.png

À présent, utilisez l'invite suivante:

update the code to run the application on port 8080, host address 0.0.0.0, and in debug mode

Le code suggéré doit se présenter comme suit :

app.run(host='0.0.0.0', port=8080, debug=True)

N'oubliez pas de mettre à jour le fichier app.py avec cet extrait.

Exécuter l'application en local

Exécutons maintenant l'application localement pour valider ses exigences par rapport à nos premiers pas.

La première étape consiste à créer un environnement Python virtuel avec les dépendances de packages Python figurant dans le fichier requirements.txt, et à installer dans cet environnement virtuel. Pour ce faire, accédez à la palette de commandes (Ctrl+Maj+P) dans l'IDE Cloud Shell et saisissez Créer un environnement Python. Suivez les étapes ci-dessous pour sélectionner un environnement virtuel (venv), un interpréteur Python 3.x et le fichier requirements.txt.

Une fois l'environnement créé, lancez une nouvelle fenêtre de terminal (Ctrl+Maj+`) et exécutez la commande suivante:

python app.py

Voici un exemple d'exécution:

(.venv) romin@cloudshell: $ python app.py 
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:8080
 * Running on http://10.88.0.3:8080
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 132-247-368

Vous pouvez désormais prévisualiser l'API aux URL suivantes. Nous partons du principe que votre serveur de développement s'exécute sur le port 8080. Si ce n'est pas le cas, remplacez-le par le numéro de port approprié.

  • https://<host-name>:8080/sessions
  • https://<host-name>:8080/sessions/{id}
  • https://<host-name>:8080/sessions/categories/{category}

Suivez les étapes ci-dessous pour vous assurer que vous pouvez récupérer à l'aide de ces URL, les données JSON contenues dans le fichier app.py:

Ouvrez une nouvelle fenêtre de terminal et essayez l'une des commandes suivantes:

curl -X GET http://127.0.0.1:8080/sessions
curl -X GET http://127.0.0.1:8080/sessions/<ID>
curl -X GET http://127.0.0.1:8080/sessions/categories/<CATEGORY_NAME> 

8. Refactorisation de code

Plutôt que de faire en sorte que app.py contienne les exemples de données JSON codés en dur, nous souhaitons probablement les séparer ou les extraire dans un autre module, afin de maintenir une séparation claire entre le code et les données. C'est parti !

Gardez le fichier app.py ouvert et exécutez l'invite suivante:

Can I improve this code and separate out the sessions data from this app.py file?

Cela devrait vous donner quelques suggestions sur la façon de procéder. Voici un exemple de suggestion que nous avons reçue. Vous devriez obtenir un résultat semblable à celui-ci:

9b9c56cb527dac4c.png

Suivons cela en séparant nos données dans un fichier sessions.py, comme suggéré par Code Assist.

Créez un fichier nommé sessions.py.

,,dont le contenu est une liste JSON, comme indiqué dans les données générées ci-dessous:

sessions = [
   {
       "id": "1",
       "title": "Session 1",
       "speakers": ["Speaker 1", "Speaker 2"],
       "categories": ["Category 1", "Category 2"],
       "summary": "This is a summary of session 1.",
       "duration": "1 hour",
   },
   {
       "id": "2",
       "title": "Session 2",
       "speakers": ["Speaker 3", "Speaker 4"],
       "categories": ["Category 3", "Category 4"],
       "summary": "This is a summary of session 2.",
       "duration": "1 hour 30 minutes",
   },
]

Le fichier app.py est maintenant très simplifié et se présente comme suit:

from flask import Flask, jsonify, request
from sessions import sessions

app = Flask(__name__)

@app.route('/sessions', methods=['GET'])
def get_sessions():
   return jsonify(sessions.sessions)

@app.route('/sessions/<id>', methods=['GET'])
def get_session_by_id(id):
   session = next((session for session in sessions.sessions if session['id'] == id), None)
   if session is None:
       return jsonify({}), 404
   return jsonify(session)

@app.route('/sessions/categories/<category>', methods=['GET'])
def get_sessions_by_category(category):
   sessions_by_category = [session for session in sessions.sessions if category in session['categories']]
   return jsonify(sessions_by_category)

if __name__ == '__main__':
   app.run(host='0.0.0.0', port=8080, debug=True)

Assurez-vous que vous êtes toujours en mesure d'exécuter l'application localement et d'utiliser l'API avec ces modifications refactorisées. Le serveur de développement Python est probablement toujours en cours d'exécution. Il vous suffit donc de rappeler les commandes curl de l'étape précédente.

9. Intégrer à la collection Firestore

L'étape suivante consiste à abandonner la liste JSON en mémoire locale dont nous disposons pour nos sessions et à connecter notre application à la collection sessions de la base de données Firestore, que nous avons créée au début de cet atelier de programmation.

Gardez le fichier sessions.py ouvert et exécutez l'invite suivante:

Can you further refactor the sessions.py code to read from a Firestore database that has a collection named sessions. The collection has the same attributes as the session object that we have defined. Use the Python module google-cloud-firestore. 

Nous avons reçu la suggestion suivante pour lire toutes les sessions de la collection Firestore:

import google.cloud.firestore

# Create a Firestore client
db = google.cloud.firestore.Client()

# Get the sessions collection
sessions_collection = db.collection("sessions")

# Create a list of sessions
sessions = []

# Iterate over the documents and add them to the list
for doc in sessions_collection.stream():
   session = doc.to_dict()
   session["id"] = doc.id
   sessions.append(session)

Vous pouvez maintenant intégrer le code dans sessions.py.

Si le serveur de développement Flask s'exécute localement, il se peut que votre application s'est fermée en signalant que le module Python était introuvable.

Par exemple, vous pouvez demander à Code Assist quel module Python doit être ajouté au fichier requirements.txt, comme suit:

Which Python package needs to be installed to make the firestore code work?

Vous obtiendrez le nom du module Python (par exemple, google-cloud-firestore). Ajoutez-le au fichier requirements.txt.

Vous devez recréer l'environnement Python avec le module que vous venez d'ajouter (google-cloud-firestore). Pour ce faire, exécutez la commande suivante dans la fenêtre de terminal existante:

pip install -r requirements.txt

Exécutez à nouveau l'application (redémarrez-la avec python app.py) et accédez à l'URL /sessions. Vous devriez maintenant obtenir les exemples de documents que nous avons ajoutés à la collection sessions.

975d05e6518f1a6a.png

N'hésitez pas à interroger d'autres URI pour récupérer des sessions spécifiques ou toutes les sessions d'une catégorie donnée, comme décrit dans les étapes précédentes.

10. Explication du code

C'est le moment d'utiliser la fonctionnalité "Explain this" de Gemini Code Assist pour mieux comprendre le code. N'hésitez pas à accéder à l'un des fichiers ou à sélectionner des extraits de code spécifiques, puis à demander à Code Assist de s'adresser à l'invite suivante: Explain this.

À titre d'exercice, consultez le fichier sessions.py et mettez en surbrillance le code propre à Firestore afin d'obtenir des explications sur le code. Essayez également d'utiliser cette fonctionnalité sur d'autres fichiers de votre projet, en plus du code Python.

11. Générer l'application Web

Maintenant que nous avons généré l'API et l'avons intégrée à une collection Firestore en direct, créons un front-end Web pour l'application. Pour le moment, les fonctionnalités de notre interface Web seront réduites au minimum, c'est-à-dire qu'elle pourra rechercher des sessions appartenant à une catégorie spécifique. N'oubliez pas que nous disposons d'un chemin d'API pour cela, à savoir /sessions/categories/{category}. Notre application Web doit donc l'appeler et récupérer les résultats.

Entrons dans le vif du sujet. Envoyez la requête suivante à Code Assist:

Generate a web application that allows me to search for sessions by category and uses the Flask application that we created. Please use basic HTML, CSS and JS. Embed all the Javascript and CSS code into a single HTML file only.

Le code HTML de l'application Web est alors généré, avec le code JavaScript et le CSS intégré. Vous serez également invité à ajouter un nouvel itinéraire au fichier app.py, afin que tout utilisateur accédant à l'URL racine ou de base voie la page d'accueil. S'il ne mentionne pas cette information, posez-lui la question ou utilisez l'extrait ci-dessous:

@app.route('/')
def index():
   return render_template('index.html')

Vous pouvez l'enregistrer sous le nom index.html, mais vous avez peut-être des questions sur l'emplacement d'enregistrement de ce fichier (c'est-à-dire dans quel dossier). Nous pouvons poser une question complémentaire à Code Assist.

Given that I am using the flask framework, where should I put the index.html file?

Vous devriez être clairement informé qu'il utilise le framework render_template. Par conséquent, le fichier index.html doit être placé dans le dossier templates. Ce dossier est disponible, car nous avons généré un modèle Application basé sur Flask au début de cet atelier de programmation. Par conséquent, il existe un fichier index.html. Vous devez simplement remplacer son contenu par le nouveau généré ici. Code Assist mentionne également qu'il faut importer render_template dans votre fichier app.py.

Enregistrez le code de l'application Web dans le fichier index.html et n'oubliez pas de le placer dans le dossier templates.

Exécuter l'application en local

Exécutons maintenant l'application localement pour valider ses exigences par rapport à nos premiers pas.

Assurez-vous que le serveur Flask local est toujours en cours d'exécution et n'affiche aucune erreur. En cas d'erreur, corrigez-les et démarrez le serveur.

Une fois l'application opérationnelle, accédez à l'URL d'accueil. Vous devriez voir le fichier index.html diffusé, comme illustré ci-dessous:

8ca586acc4536879.png

Indiquez l'une des catégories de données d'entrée (par exemple, AI), puis cliquez sur le bouton Search. Les sessions taguées avec la catégorie AI doivent s'afficher.

165faded790a6c.png

Un exercice facultatif consiste à afficher des métadonnées supplémentaires telles que la durée, les catégories, les intervenants et le résumé de chacune des sessions.

12. Génération de scénarios de test

Nous avons généré l'API de sessions, et il est temps d'utiliser Gemini Code Assist pour générer des cas de tests unitaires pour les différents chemins d'API.

Gardez le fichier app.py ouvert et exécutez l'invite suivante:

Generate unit test cases for app routes. Use unittest module.

Nous avons obtenu la réponse suivante:

import unittest
from app import app

class FlaskAppTestCase(unittest.TestCase):

    def test_index(self):
        tester = app.test_client()
        response = tester.get('/')
        self.assertEqual(response.status_code, 200)

    def test_get_sessions(self):
        tester = app.test_client()
        response = tester.get('/sessions')
        self.assertEqual(response.status_code, 200)

    def test_get_session_by_id(self):
        tester = app.test_client()
        response = tester.get('/sessions/1')
        self.assertEqual(response.status_code, 200)

    def test_get_sessions_by_category(self):
        tester = app.test_client()
        response = tester.get('/sessions/categories/category1')
        self.assertEqual(response.status_code, 200)

if __name__ == '__main__':
    unittest.main()

Créez un fichier nommé tests.py avec le code ci-dessus.

Remarque sur la génération de scénarios de test

Il est possible qu'une liste de code différente de celle ci-dessus s'affiche, ce qui peut entraîner des problèmes lors de l'exécution des scénarios de test. Par exemple, nous avons constaté que certaines de nos exécutions manquaient les éléments clés suivants:

from app import app

Le code ci-dessus est nécessaire pour importer l'application Flask existante à laquelle nous allons appeler les scénarios de test.

if __name__ == '__main__':

`unittest.main()`

Le code ci-dessus est nécessaire pour exécuter les scénarios de test.

Nous vous recommandons d'examiner chacun des scénarios de test, de vérifier le assertEqual et d'autres conditions dans le code généré pour vous assurer qu'il fonctionnera. Étant donné que les données sont externes dans la collection Firestore, il est possible qu'elles n'y aient pas accès et qu'elles utilisent des données factices, ce qui peut entraîner l'échec des tests. Modifiez vos scénarios de test en conséquence ou mettez en commentaire certains dont vous n'avez peut-être pas besoin immédiatement.

À titre de démonstration, nous avons exécuté les scénarios de test à l'aide de la commande suivante (veillez à exécuter le serveur de développement local, car des appels seront effectués vers les points de terminaison locaux de l'API):

python tests.py

Nous avons le résultat récapitulatif suivant:

Ran 4 tests in 0.274s

FAILED (failures=2)

C'est exact, car l'ID de session n'était pas correct lors du 3e test et il n'existe aucune catégorie nommée category1.

.

Ajustez les scénarios de test en conséquence et testez-les.

13. Développement piloté par les tests

Voyons maintenant comment ajouter une nouvelle méthode de recherche dans l'API Sessions, basée sur la méthodologie de développement piloté par les tests (TDD, Test Driven Development). Elle consiste à écrire d'abord des scénarios de test, à les faire échouer en raison d'un manque d'implémentation et à utiliser Gemini Code Assist pour générer l'implémentation manquante afin que le test réussisse.

Accédez au fichier tests.py (en supposant que vous avez corrigé tous les tests dans le fichier tests.py). Demandez à Code Assist la requête suivante:

Generate a new test case to search for sessions by speaker

Nous avons ainsi obtenu l'implémentation du scénario de test suivante, que nous avons correctement insérée dans le fichier tests.py.

  def test_get_sessions_by_speaker(self):
        tester = app.test_client()
        response = tester.get('/sessions/speakers/speaker1')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json, [sessions.sessions[0], sessions.sessions[1]])

Si vous exécutez les tests, le message d'erreur suivant doit s'afficher:

$ python tests.py 
.F.
======================================================================
FAIL: test_get_sessions_by_speaker (__main__.FlaskAppTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/romin/hello-world-5/tests.py", line 21, in test_get_sessions_by_speaker
    self.assertEqual(response.status_code, 200)
AssertionError: 404 != 200

----------------------------------------------------------------------
Ran 3 tests in 0.010s

FAILED (failures=1)

En effet, le scénario de test a appelé le chemin suivant (/sessions/speakers/) et il n'y a aucune implémentation de cela dans app.py.

Laissez-nous demander à Code Assist de nous fournir une implémentation. Accédez au fichier app.py et envoyez la requête suivante à Code Assist:

Add a new route to search for sessions by a specific speaker

Code Assist nous a suggéré l'implémentation suivante, que nous avons ajoutée au fichier app.py:

@app.route('/sessions/speakers/<speaker>', methods=['GET'])
def get_sessions_by_speaker(speaker):
    sessions_by_speaker = [session for session in sessions.sessions if speaker in session['speakers']]
    return jsonify(sessions_by_speaker)

Consultez à nouveau le fichier tests.py. Nous avons modifié notre scénario de test comme suit pour effectuer un contrôle rapide:

   def test_get_sessions_by_speaker(self):
       tester = app.test_client()
       response = tester.get('/sessions/speakers/Romin Irani')
       self.assertEqual(response.status_code, 200)
       self.assertEqual(len(response.json), 1)

Le test s'est déroulé correctement. Nous laissons cela comme un exercice pour vous permettre d'examiner les scénarios de test générés, de les modifier légèrement en fonction des données dont vous disposez dans Firestore et de disposer des méthodes assert* appropriées dans les scénarios de test unitaire Python.

14. Déployer une application sur Google Cloud Run

Maintenant que nous sommes satisfaits de la qualité de notre développement, la dernière étape consiste à déployer cette application sur Google Cloud Run. Mais peut-être, pour de bonnes mesures, nous devrions demander à Code Assist si nous avons oublié quelque chose. Après avoir ouvert app.py, envoyez la requête suivante :

Is there something here I should change before I deploy to production?

Bonne nouvelle, puisque nous avons oublié de désactiver l'indicateur de débogage :

2f87ed3a811fb218.png

Comme indiqué, désactivez le débogage et demandez de l'aide à Gemini Code Assist avec la commande gcloud qui permet de déployer l'application sur Cloud Run directement depuis la source (sans avoir à créer de conteneur au préalable).

Saisissez la requête suivante:

I would like to deploy the application to Cloud Run directly from source. What is the gcloud command to do that?

Essayez quelques variantes de la requête ci-dessus. Nous avons également essayé:

I would like to deploy this application to Cloud Run. I don't want to build a container image locally but deploy directly from source to Cloud Run. What is the gcloud command for that?

Dans l'idéal, vous devriez obtenir la commande gcloud suivante:

gcloud run deploy sessions --source .

Autres avantages:

gcloud run deploy <service-name> --source . \
—-platform managed \
—-allow-unauthenticated

Exécutez la commande ci-dessus à partir du dossier racine de l'application. Lorsque vous êtes invité à indiquer region, sélectionnez us-central1. Lorsque vous êtes invité à autoriser unauthenticated invocations, sélectionnez Y. Vous devrez peut-être également activer des API Google Cloud comme Artifact Registry, Cloud Build et Cloud Run, ainsi que l'autorisation de créer un dépôt Artifact Registry. Veuillez accorder cette autorisation.

Le processus de déploiement prendra environ 2 minutes. Nous vous prions de patienter.

Une fois le service déployé, l'URL du service Cloud Run s'affiche. Accédez à cette URL publique. Vous devriez voir la même application Web déployée et exécutée correctement.

c5322d0fd3e0f616.png

Félicitations, bravo !

15. (Facultatif) Utiliser Cloud Logging

Nous pouvons intégrer la journalisation dans notre application de sorte que les journaux de l'application soient centralisés dans l'un des services Google Cloud (Cloud Logging). Nous pouvons ensuite utiliser la fonctionnalité Observability Gemini pour mieux comprendre les entrées de journal.

Pour ce faire, nous devons d'abord utiliser une bibliothèque Cloud Logging Python existante de Google Cloud et l'utiliser pour consigner des informations, des messages d'avertissement ou d'erreur (en fonction du journal / niveau de gravité).

Essayons de poser la question d'abord à Code Assist. Essayez l'invite suivante:

How do I use the google-cloud-logging package in Python?

Vous devriez obtenir une réponse contenant des informations à ce sujet, comme indiqué ci-dessous:

2472e1ccaf8a217d.png

Ajoutons des instructions de journalisation à la fonction qui recherche des sessions par catégorie.

Tout d'abord, ajoutez le package Python google-cloud-logging au fichier requirements.txt.

L'extrait de code suivant montre comment nous avons intégré le code pour implémenter la journalisation:

...
from google.cloud import logging
...
app = Flask(__name__)

# Create a logger
logger = logging.Client().logger('my-log')

@app.route('/sessions/categories/<category>', methods=['GET'])
def get_sessions_by_category(category):
   logger.log_text(f"Fetching sessions with category {category}")
   sessions_by_category = [session for session in sessions.sessions if category in session['categories']]
   logger.log_text(f'Found {len(sessions_by_category)} sessions with category {category}')
   return jsonify(sessions_by_category)

# # Other App Routes

Déployez à nouveau le service sur Cloud Run à l'aide de la même commande que dans la section précédente. Une fois le service déployé, exécutez quelques appels vers le point de terminaison /sessions/categories/<category>.

Accédez au Cloud Console → Logs Explorer.

59e297577570695.png

...et vous devriez être en mesure de filtrer ces instructions de journalisation comme indiqué ci-dessous:

914f1fb6cac30a89.png

Vous pouvez cliquer sur l'une des entrées de journal, la développer, puis cliquer sur Explain this log entry. Gemini vous expliquera alors l'entrée de journal. Notez que si vous n'avez pas activé Gemini pour Google Cloud, vous serez invité à activer l'API Cloud AI Companion. Veuillez procéder comme indiqué.

Voici un exemple de réponse:

7fc9783910fa92cc.png

16. Félicitations

Félicitations ! Vous avez entièrement créé une application et utilisé Gemini Code Assist pour différents aspects du SDLC, y compris la conception, le build, les tests et le déploiement.

Et ensuite ?

Découvrez quelques-uns des ateliers de programmation...

Documents de référence