Boostez votre workflow de développement avec Gemini Code Assist

1. Introduction

e5b98fd4e417c877.png

Dans cet atelier de programmation, vous allez découvrir comment Gemini Code Assist peut vous aider à chaque étape clé du cycle de vie du développement logiciel (SDLC, Software Development Life Cycle), comme la conception, la création, les tests et le déploiement. Nous allons concevoir et développer une application complète, puis la déployer sur Google Cloud.

Nous allons créer une API et une application pour rechercher des sessions lors d'un événement technique. Chaque session aura un titre, un résumé, une durée, des catégories et un ou plusieurs intervenants.

Objectifs de l'atelier

  • Concevoir, créer, 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 Gemini Code Assist pour développer une application Python Flask pour la spécification OpenAPI
  • Utiliser Gemini Code Assist pour générer un frontend Web pour l'application Python Flask
  • Utiliser Gemini Code Assist pour obtenir de l'aide sur le déploiement de l'application sur Google Cloud Run
  • Utiliser les fonctionnalités de Gemini Code Assist, comme l'explication de code et la génération de cas de test, lors de la création et du test de 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. L'objectif est 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 Cloud Shell IDE

Pour le reste de l'atelier de programmation, nous allons utiliser Cloud Shell IDE, un environnement de développement entièrement géré basé sur Code OSS. Pour cela, nous devons activer et configurer Code Assist dans Cloud Shell IDE comme suit :

  1. Accédez à ide.cloud.google.com. L'IDE peut mettre un certain temps à s'afficher. Veuillez donc patienter et accepter les choix de configuration par défaut. Si des instructions de configuration de l'IDE s'affichent, suivez-les en utilisant 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 Assistance au codage en bas à droite, comme indiqué, puis sélectionnez une dernière fois le bon projet Google Cloud. Si vous êtes invité à activer l'API Cloud AI Companion, veuillez le faire et continuer.
  2. Une fois que vous avez sélectionné votre projet Google Cloud, vérifiez que le message d'état Cloud Code s'affiche dans la barre d'état et que l'assistance au codage est également activée à droite, dans la barre d'état, comme illustré ci-dessous :

709e6c8248ac7d88.png

Gemini Code Assist est prêt à être utilisé.

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 les données de notre 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. Il contiendra une liste d'exemples de documents de session. Chaque document possède 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. Vous pourrez ensuite importer la collection à l'aide de la commande gcloud firestore import.

Initialisation de la base de données Firestore

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

Si vous n'avez jamais 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 choisissez la région adaptée à votre application. Notez cet emplacement, car vous en aurez besoin à l'étape suivante pour l'emplacement du bucket.
  • Créez la base de données.

504cabdb99a222a5.png

Nous allons maintenant créer la collection sessions en suivant les étapes ci-dessous :

  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 le nom d'une 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 ou 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 pouvoir 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 avons les données à importer, nous pouvons passer à la dernière étape, qui consiste à importer les données 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 terminée, 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 utiliserons dans notre application est 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 pour le reste de l'atelier de programmation. Cette application recherche des 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 Cloud Run application (Application Cloud Run) (ce sera le temps d'exécution de notre application).
  2. Sélectionnez le modèle d'application Python (Flask): Cloud Run.
  3. Nommez 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 cet atelier, nous allons utiliser le chat Gemini Code Assist disponible dans Cloud Shell IDE dans le cadre de l'extension Cloud Code de VS Code. Vous pouvez l'afficher en cliquant sur le bouton "Assistance au codage" situé dans la barre de navigation de gauche. Repérez l'icône Code Assist a489f98a34898727.png dans la barre de navigation de gauche, puis cliquez dessus.

Le volet de chat Code Assist s'affiche dans Cloud Shell IDE. Vous pouvez discuter avec Code Assist.

14ad103efaa0ddaa.png

Notez l'icône de corbeille en haut de l'écran. Elle vous permet de réinitialiser le contexte de l'historique du chat Code Assist. Notez également que cette discussion est contextuelle aux fichiers sur lesquels vous travaillez dans l'IDE.

6. Conception d'API

Notre première étape consistera à faire appel à Gemini Code Assist lors de la phase de conception. Dans cette étape, nous allons générer une spécification OpenAPI pour les entités (sessions techniques d'un événement) que nous souhaitons rechercher.

Fournissez le prompt suivant :

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 de rechercher des sessions à l'aide de différents paramètres de requête. Vous trouverez ci-dessous un exemple de spécification :

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 constater que la spécification comporte les éléments suivants :

  • Schéma défini pour le type de session.
  • Plusieurs chemins d'API définis :
  • /sessions
  • /sessions/{id}
  • /sessions/categories/{category}

Créez un fichier nommé sessionsapi.yaml dans le dossier racine, puis copiez-y le contenu de la fenêtre de chat Code Assist à l'aide de l'option "Insérer dans le fichier actuel" (bouton +). Laissez le fichier ouvert dans l'IDE Cloud Shell.

À ce stade, vous pouvez noter une fonctionnalité intéressante de Gemini Code Assist : citation. Ces informations sont fournies au développeur lorsque le code généré cite directement et longuement une autre source, comme du code Open Source existant. Il fournit la source et la licence au développeur pour qu'il puisse décider de ce qu'il souhaite en faire.

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

7. Générer l'application

Nous allons maintenant demander à Code Assist de générer l'application. Fournissez le prompt suivant avec le fichier sessionsapi.yaml ouvert.

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 spécifiés dans le fichier de spécification OpenAPI.

Le code de l'application Python Flask fourni doit ressembler à ce qui suit :

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é lors de 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 souhaitons modifier la ligne app.run() pour utiliser le port 8080, l'adresse hôte 0.0.0.0 et également exécuter en mode Débogage lors de l'exécution locale.Voici comment procéder. Commençons par mettre en surbrillance/sélectionner la ligne :

app.run()

Ensuite, dans l'interface de chat Code Assist, saisissez le prompt suivant : Explain this.

Une explication détaillée de cette ligne devrait s'afficher, comme dans l'exemple ci-dessous :

58ec896a32a4fb68.png

Utilisez maintenant le prompt suivant :

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

Le code suggéré généré devrait être le suivant :

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

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

Exécuter l'application en local

Exécutons maintenant l'application en local pour valider les exigences de l'application, conformément à ce que nous avions prévu au départ.

La première étape consiste à créer un environnement Python virtuel dans lequel les dépendances du package Python dans requirements.txt seront installées. Pour ce faire, accédez à la palette de commandes (Ctrl+Maj+P) dans l'IDE Cloud Shell et saisissez Create Python environment (Créer un environnement Python). Suivez les prochaines étapes 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 maintenant 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, veuillez le remplacer 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 de pouvoir récupérer les données JSON contenues dans le fichier app.py à l'aide de ces URL :

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 d'inclure les exemples de données JSON codées en dur dans app.py, nous préférons probablement les séparer/extraire dans un autre module afin de maintenir une séparation claire entre le code et les données. C'est parti !

Laissez le fichier app.py ouvert et saisissez le prompt suivant :

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

Vous devriez ainsi obtenir des suggestions sur la façon de procéder. Vous trouverez ci-dessous un exemple de suggestion que nous avons reçue. Vous devriez obtenir un résultat semblable :

9b9c56cb527dac4c.png

Suivons cette suggestion et séparons nos données dans un fichier sessions.py comme suggéré par l'assistance au codage.

Créez un fichier intitulé sessions.py.

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

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 désormais beaucoup plus simple et s'affiche ci-dessous :

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 de pouvoir toujours 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. Vous n'avez donc qu'à rappeler les commandes curl de l'étape précédente.

9. Intégrer à une collection Firestore

L'étape suivante consiste à abandonner la liste JSON en mémoire locale que nous avons 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.

Laissez le fichier sessions.py ouvert et saisissez le prompt suivant :

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)

Intégrez le code dans sessions.py.

Si le serveur de développement Flask est en cours d'exécution en local, il est possible que votre application se soit fermée en indiquant que le module Python n'a pas été trouvé.

Vous pouvez demander à Code Assist, par exemple, 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 ainsi le nom du module Python (par exemple, google-cloud-firestore). Ajoutez-le au fichier requirements.txt.

Vous devrez recréer l'environnement Python avec le module nouvellement ajouté (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 idéal d'utiliser la fonctionnalité "Explain this" de Gemini Code Assist pour mieux comprendre le code. N'hésitez pas à parcourir les fichiers ou à sélectionner des extraits de code spécifiques, puis à poser la requête suivante à Code Assist : Explain this.

Pour vous entraîner, accédez au fichier sessions.py, mettez en surbrillance le code spécifique à Firestore et obtenez une explication de ce code. Essayez également d'utiliser cette fonctionnalité sur d'autres fichiers de votre projet, et pas seulement sur 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, générons une interface Web pour l'application. Notre interface Web maintiendra actuellement ses fonctionnalités au minimum, c'est-à-dire qu'elle pourra rechercher des sessions appartenant à une catégorie spécifique. N'oubliez pas que nous avons un chemin d'API pour cela, c'est-à-dire /sessions/categories/{category}. Notre application Web doit donc l'appeler et récupérer les résultats.

Entrons dans le vif du sujet. Fournissez le prompt suivant à 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 sera généré avec le code JavaScript et CSS intégré. Il vous demandera également d'ajouter une route au fichier app.py afin que tout utilisateur qui accède à l'URL racine ou de base soit redirigé vers la page d'accueil. Si ces informations ne sont pas mentionnées, posez 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 vous demandez peut-être où l'enregistrer (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?

Il devrait vous indiquer clairement qu'il utilise le framework render_template et que le fichier index.html devra donc être placé dans le dossier templates. Vous trouverez ce dossier, car nous avons généré une application basée sur le modèle Flask au début de cet atelier de programmation. Par conséquent, un fichier index.html existe déjà. Vous devez simplement remplacer son contenu par le nouveau contenu généré ici. Code Assist vous invite également à 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 placer ce fichier dans le dossier templates.

Exécuter l'application en local

Exécutons maintenant l'application en local pour valider les exigences de l'application, conformément à ce que nous avions prévu au départ.

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

Une fois l'application opérationnelle, accédez à l'URL de la page d'accueil. Le fichier index.html diffusé doit s'afficher comme suit :

8ca586acc4536879.png

Fournissez l'une des catégories de données en entrée (par exemple, AI), puis cliquez sur le bouton Search. Les sessions taguées avec la catégorie AI devraient 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 Sessions. Il est maintenant temps d'utiliser Gemini Code Assist pour générer des cas de tests unitaires pour les différents chemins d'API.

Laissez le fichier app.py ouvert et saisissez le prompt suivant :

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

Nous avons reçu 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 que vous voyiez une liste de code différente de celle ci-dessus, ce qui peut entraîner des problèmes lors de l'exécution des scénarios de test. Par exemple, nous avons constaté que lors de certaines de nos exécutions, les éléments de code clés suivants étaient manquants :

from app import app

Le code ci-dessus est nécessaire pour importer l'application Flask existante sur 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 cas de test, de vérifier le assertEqual et les 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'il n'y ait pas accès et qu'il utilise des données fictives, ce qui peut entraîner l'échec des tests. Modifiez donc vos scénarios de test en conséquence ou mettez en commentaire certains scénarios de test dont vous n'avez pas besoin immédiatement.

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

python tests.py

Nous avons obtenu le résultat récapitulatif suivant :

Ran 4 tests in 0.274s

FAILED (failures=2)

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

.

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

13. Développement piloté par les tests

Nous allons maintenant ajouter une méthode de recherche à notre API de sessions en suivant la méthodologie de développement piloté par les tests (TDD). Cette méthodologie consiste à écrire d'abord des cas 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 ayez corrigé le fichier tests.py pour que tous les tests réussissent). Posez la requête suivante à Code Assist :

Generate a new test case to search for sessions by speaker

Nous avons ainsi obtenu l'implémentation du cas de test suivant, que nous avons inséré 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, l'erreur suivante devrait 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 d'accès suivant : (/sessions/speakers/), qui n'est pas implémenté dans app.py.

Demandons à Code Assist de nous fournir une implémentation. Accédez au fichier app.py et saisissez la requête suivante dans Code Assist :

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

Nous avons obtenu l'implémentation suivante suggérée par Code Assist, 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)

Revenons au fichier tests.py et modifions notre cas de test comme suit pour une vérification 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 bien déroulé. Nous vous laissons le soin d'examiner les cas de test générés, de les modifier légèrement en fonction des données que vous pourriez avoir dans Firestore et d'utiliser les méthodes assert* appropriées dans les cas de test unitaires Python.

14. Déployer sur Google Cloud Run

Maintenant que nous sommes satisfaits de la qualité de notre développement, notre dernière étape consiste à déployer cette application sur Google Cloud Run. Mais, par précaution, demandons à Code Assist si nous avons oublié quelque chose. Ouvrez app.py et envoyez le prompt suivant :

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

Heureusement que vous avez posé la question, car nous avons oublié de désactiver le flag de débogage :

2f87ed3a811fb218.png

Comme indiqué, désactivez le débogage et demandez à Gemini Code Assist de vous aider avec la commande gcloud qui peut être utilisée pour déployer l'application sur Cloud Run directement à partir de la source (sans avoir à créer un conteneur au préalable).

Fournissez le prompt suivant :

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

Essayez quelques variantes du prompt ci-dessus. Voici un autre exemple que nous avons testé :

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 .

Vous pouvez également obtenir :

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é à sélectionner region, sélectionnez us-central1. Lorsque vous êtes invité à autoriser unauthenticated invocations, sélectionnez Y. Il est également possible que vous soyez invité à activer les API Google Cloud telles qu'Artifact Registry, Cloud Build et Cloud Run, et à autoriser la création d'un dépôt Artifact Registry. Veuillez accorder cette autorisation.

Le processus de déploiement prend environ deux minutes. Merci de patienter.

Une fois le déploiement réussi, l'URL du service Cloud Run s'affiche. Accédez à cette URL publique. L'application Web devrait s'afficher et s'exécuter correctement.

c5322d0fd3e0f616.png

Félicitations, bravo !

15. (Facultatif) Utiliser Cloud Logging

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

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

Essayons de poser cette première question à Code Assist. Essayez le prompt suivant :

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

Vous devriez obtenir une réponse contenant des informations sur le sujet, comme ci-dessous :

2472e1ccaf8a217d.png

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

Commencez par ajouter le package Python google-cloud-logging au fichier requirements.txt.

Voici un extrait de code qui 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 en utilisant la même commande que dans la section précédente. Une fois le service déployé, exécutez quelques appels au point de terminaison /sessions/categories/<category>.

Accédez à Cloud Console → Logs Explorer.

59e297577570695.png

… et vous devriez pouvoir filtrer ces instructions de journalisation, comme indiqué ci-dessous :

914f1fb6cac30a89.png

Vous pouvez cliquer sur l'une des instructions de journalisation, la développer, puis cliquer sur Explain this log entry pour que Gemini explique 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 suivre les instructions pour le faire.

Voici un exemple de réponse :

7fc9783910fa92cc.png

16. Félicitations

Félicitations, vous avez réussi à créer une application de A à Z et à utiliser Gemini Code Assist pour plusieurs aspects du SDLC, y compris la conception, la compilation, les tests et le déploiement.

Et ensuite ?

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

Documents de référence