
Partager:
Chris est le Developer Relations Tooling Manager et dirige l'équipe qui construit vos outils préférés. Il programme depuis plus de 15 ans dans différents langages et pour différents types de projets, depuis le travail avec les clients jusqu'aux systèmes à grande échelle et aux données volumineuses. Il vit dans l'Ohio, où il passe son temps avec sa famille et joue à des jeux vidéo et TTRPG.
Parlez à votre base de données avec Directus
Temps de lecture : 11 minutes
Parlez à votre base de données avec Directus
"Jouer sur ses points forts" est un élément important du développement de logiciels. De nombreux problèmes et bogues sont dus au fait que quelqu'un construit quelque chose qui n'est pas dans ses cordes. L'authentification et les bases de données sont deux choses qui peuvent être incroyablement difficiles à comprendre, et si vous construisez une application, pourquoi devriez-vous passer un tas de temps sur des choses qui ne sont pas directement liées à votre application ?
Directus nous permet de décharger la gestion de la base de données vers un système externe et de faciliter le travail avec nos données. Non seulement il peut être utilisé pour stocker des données, mais il peut être utilisé pour exposer nos données de multiples façons autres que notre application. Vous pouvez consulter leur Plus de 100 choses à construire pour avoir une idée de tout ce qu'ils font.
Jetons un coup d'œil à leur offre de base de données. Nous allons voir un exemple d'application qui permet aux utilisateurs d'acheter et d'annuler des billets de cinéma, et nous utiliserons Directus pour gérer le stockage des données. Nous pouvons ensuite utiliser l'API Voice de Vonage pour permettre aux utilisateurs d'appeler, de gérer leurs billets et de laisser Directus ou d'autres services s'occuper de l'affichage sur le Web.
Conditions préalables
Un Account de développeur Vonage. Cliquez sur Sign Up pour en créer un si vous n'en avez pas déjà un.
A Directus Account.
Un système de tunnel comme ngrok.
Mise en place de Directus
Si vous ne l'avez pas encore fait, consultez le Guide de démarrage rapide de Directus et suivez les instructions pour configurer un compte Directus Cloud (que nous utilisons pour cette démo), ou pour configurer Directus localement. Vous aurez besoin d'un Account GitHub pour vous inscrire à un compte Cloud.
Nous devons configurer les sources de données auxquelles nous accéderons à partir de Directus. Nous allons mettre en place une collection "Movies", qui est une liste de films actuellement à l'affiche. Nous aurons également une collection "Showtimes" qui contient toutes les heures de projection individuelles d'un film. Nous aurons également une collection "Tickets", qui contient les billets individuels qui ont été achetés.
Les films seront liés aux heures d'ouverture, et les billets seront liés aux heures d'ouverture.
Pour créer une nouvelle collection dans Directus, cliquez sur l'icône de l'engrenage dans votre barre latérale et sélectionnez "Modèle de données".
Directus Sidebar - Settings
Vous obtiendrez ainsi une liste des modèles que vous possédez actuellement. Cliquez sur le grand bouton "+" (Plus) dans le coin supérieur droit de l'écran pour créer un nouveau modèle.
Creating a new collection in Directus
Donnez un nom au modèle, dans ce cas, "films". C'est le nom par lequel nous référencerons la collection lorsque nous l'appellerons via le SDK de Directus. Cliquez sur la flèche droite pour continuer.
Vous pouvez ajouter des champs supplémentaires, mais nous ne les sélectionnerons pas pour l'instant. Cliquez sur l'icône de la coche dans le coin supérieur droit pour générer le modèle.
Vous accédez ainsi à l'écran d'édition du modèle.
Movie Collection Edit Screen
Ici, nous pouvons créer de nouveaux champs et les ajouter au modèle. La collection "Movies" est assez simple, alors cliquez sur "Create Field" et nous ajouterons les champs suivants :
titre - Sélectionnez "Input", le type est "String", et sélectionnez "Required".
description - Sélectionnez "Datetime", le type est "Datetime", et sélectionnez "Required".
heure_début - Sélectionnez "Input", le type est "String", et sélectionnez "Required".
Nous devrons également créer deux autres modèles et leurs champs associés :
horaires
movie_id - Sélectionnez "Many to One" sous Relational, et sous "Related Collection" entrez "movies".
heure_début - Sélectionnez "Datetime", le type est "Datetime", et sélectionnez "Required".
billets
showtime_id - Sélectionnez "Many to One" sous Relational, et sous "Related Collection" entrez "showtimes"
utilisateur - Sélectionnez "Many to One" sous Relational, et sous "Related Collection" entrez "directus_users".
Une fois ces collections créées, vous pouvez entrer quelques films dans la collection Films et quelques horaires associés.
Pour notre démo, nous voudrons également créer un utilisateur qui interagira avec nos collections au nom de l'utilisateur. Nous faisons cela parce que quelqu'un qui appelle ne sera pas en mesure d'entrer un nom d'utilisateur et un mot de passe, donc nous allons tirer parti de la fonction setToken() composable sur le client Directus.
Cliquez sur l'icône Utilisateurs dans la barre latérale pour accéder à la page Utilisateurs. Cliquez sur l'icône "+" (Plus) dans le coin supérieur droit pour créer un nouvel utilisateur. Veillez à ne pas cliquer sur le bouton "Inviter" situé juste à côté.
Remplissez le formulaire de l'utilisateur, puis vers le bas sous "Admin Options", donnez à l'utilisateur le rôle de "Application User", puis cliquez sur "+" dans la case "Token". Conservez ce jeton, car nous en aurons besoin pour configurer notre application.
Cliquez sur le bouton "Checkmark" dans le coin supérieur droit pour enregistrer l'utilisateur.
Démarrage de ngrok
Nous aurons besoin d'un moyen pour que les serveurs de Vonage contactent notre application, et la façon la plus simple de le faire est d'utiliser un système de tunneling comme ngrok. Rendez-vous sur le site de ngrokpour ouvrir un Account et suivre leur guide de démarrage rapide pour l'installer sur votre PC.
Une fois qu'il est installé, nous pouvons le démarrer avec :
ngrok http 3000Cela ouvrira un tunnel vers le monde extérieur à travers les serveurs de ngrok, y compris une URL dont le SSL est activé. Lorsque vous exécutez ngrok, vous obtenez une jolie petite sortie d'état qui vous permet de vérifier les événements qui circulent dans le système. Nous devrons récupérer l'adresse "Forwarding" sur le côté gauche de l'adresse ->qui est notre URL publique. Nous aurons besoin de cette URL dans un instant.
Configuration de Vonage
Nous utiliserons l Voice API de Vonage de Vonage pour permettre à un utilisateur d'appeler notre "système téléphonique de cinéma", nous devrons donc mettre en place une application Vonage. Les Applications Vonage sont essentiellement des groupes de configurations qui interagissent avec les applications. Pour la plupart de nos services, il s'agira de "rappels", ou d'URL que nous utiliserons pour envoyer des informations à votre application. Pour Voice, ces rappels sont utilisés pour vous informer d'un événement d'appel ou d'une URL pour gérer le démarrage d'un appel.
Connectez-vous à votre tableau de bord du développeurpuis rendez-vous dans la section "Applications"Nous allons créer une nouvelle application, donc cliquez sur " + Créer une nouvelle application ".+ Créer une nouvelle application."
Donnons à notre application un nom comme "Directus IVR" afin de pouvoir la retrouver plus tard. Puisque nous utilisons une application Vonage, nous devons générer une clé privée pour signer notre JWT. Pour plus d'informations sur le fonctionnement de l'authentification JWT de Vonage, consultez notre documentation sur l'authentification. Enregistrez ce fichier de clé privée car nous en aurons besoin dans un instant.
Descendez un peu et basculez le bouton à côté de "Voice" pour ouvrir la configuration vocale. En utilisant l'URL ngrok que nous avons copiée il y a quelques instants,
URL de la réponse - Réglé sur "HTTP POST", et la valeur sur
<ngrok URL>/URL de l'événement - Réglé sur "HTTP POST", et la valeur sur
<ngrok URL>/eventsURL de repli - Laisser "HTTP GET" et la valeur vide
Ensuite, allez-y, faites défiler vers le bas et cliquez sur "Générer une nouvelle demande".
Une fois l'application créée, vous accédez à la page d'information de la nouvelle application. En haut de la page se trouve l'identifiant de l'application dont nous aurons besoin pour configurer notre code, alors copiez-le. Nous aurons également besoin d'un numéro de téléphone. Dans la section "Numbers liés", liez l'un de vos numéros en cliquant sur le bouton "Lié" pour un numéro que vous possédez. Si vous n'avez pas encore de numéro, rendez-vous sur la page " Acheter des Numbers ".Acheter des Numbers"Notez que certains pays peuvent avoir des restrictions sur l'achat de numéros.
Notre code
Si vous souhaitez suivre le mouvement, vous pouvez saisir le code.
Configurer notre Applications
Nous devons intégrer dans notre application certaines informations qui vous sont propres. Copiez le fichier .env.dist dans un nouveau fichier nommé .envet ouvrez-le pour le modifier. Il y a quatre variables que nous devons assigner dans ce fichier et dont notre application aura besoin :
DIRECTUS_INSTANCE - Il s'agit de l'URL de votre instance Directus. Elle ressemblera à "https://my-instance.directus.app/"
DIRECTUS_TOKEN - Le jeton statique que nous avons généré pour notre utilisateur Directus plus tôt.
API_APPLICATION_ID - L'ID de l'application Vonage que nous avons généré.
CLÉ PRIVÉE - Une version encodée en base64 de la clé privée que nous avons téléchargée pour notre application. Consultez notre article sur les clés privées dans les variables d'environnement pour savoir comment transformer le fichier
private.keyen une chaîne de caractères base64
Enregistrer ce fichier.
Le client Directus
Directus propose un SDK JavaScript que nous utiliserons pour accéder à notre instance de base de données Directus. Des informations complètes peuvent être trouvées sur la documentation du SDK JavaScript de Directusmais nous allons créer une instance du client. Puisque nous utiliserons un utilisateur système pour accéder à notre base de données Directus, nous pouvons contourner la question de l'authentification par utilisateur, et nous créons donc un client Directus réutilisable qui sera utilisé dans toute l'application.
# src/Directus.js
export function getDirectusClient() {
return createDirectus(process.env.DIRECTUS_INSTANCE)
.with(staticToken(process.env.DIRECTUS_TOKEN))
.with(rest());
}Le Client SDK de Directus est composable, ce qui signifie que vous commencez avec un client qui doit être configuré pour votre utilisation. Dans notre cas, nous allons utiliser un client staticToken() pour l'authentification et utiliser la méthode rest() pour configurer l'accès via l'API REST au lieu de l'API GraphQL.
Tout au long de notre application, nous appellerons cette méthode getDirectusClient() pour utiliser ce client composé dans notre code.
Puisque nous utilisons l'API REST, notre client exposera une méthode request() qui nous permet d'interagir avec son API. Cela se fait par le biais d'une combinaison de méthodes que nous passons dans la méthode request() comme readItems() ou deleteItem().
const directus = getDirectusClient();
const tickets = await directus.request(readItems(
// Collection we are accessing
'tickets',
// Options for what we return and search for
{
'fields': [
'id',
'showtime_id.*.*'
],
'sort': [
'showtime_id.start_time'
],
'filter': {
'user': {
'_eq': user.id
}
}
}
)); Comment fonctionne l'Applications
Lorsque l'utilisateur compose le numéro de Vonage attribué à notre application, les serveurs de Vonage récupèrent l'URL de réponse de notre application et effectuent un appel vers notre serveur. Pour notre application, il s'agit du fichier src/routes/CallStart.js fichier. L'URL de réponse renvoie un NCCO ou un objet de contrôle d'appel. Un NCCO est simplement un blob JSON avec des directions à prendre pour les serveurs Vonage. Lors du lancement d'un appel initial, nous renvoyons un NCCO qui a une action Talk et une action Input action. Nous demandons à l'utilisateur ce qu'il veut faire, puis nous attendons qu'il nous le dise.
Nous consultons également l'API de Directus pour vérifier si les personnes ont déjà un billet. Si c'est le cas, nous leur rappelons le film à venir.
// src/routes/CallStart.js
router.post('/', async (req, res) => {
const builder = new NCCOBuilder();
const directus = getDirectusClient();
const user = await getUserFromPhone(req.body.from);
const tickets = await directus.request(readItems(
'tickets',
{
'fields': [
'showtime_id.*.*'
],
'sort': [
'showtime_id.start_time'
],
'filter': {
'user': {
'_eq': user.id
}
}
}
));
let openingMessage = "Welcome! What can we help you with?";
if (tickets.length > 0) {
const startTime = new Date(tickets[0].showtime_id.start_time);
const timeString = `${startTime.getFullYear()} ${startTime.getMonth() + 1} ${startTime.getDay()} at ${startTime.getHours()} ${startTime.getMinutes()}`;
openingMessage = `Welcome back! We look forward to seeing you on ${timeString} for ${tickets[0].showtime_id.movie_id.title}. What can we help you with?`;
}
const domain = getDomain(req);
return res.send(
builder
.addAction(new Talk(openingMessage))
.addAction(new Input(
null,
{
'context': ['cancel ticket']
},
`${domain}/determine_action`))
.build()
);
});En plus d'appeler l'API Directus, nous utilisons l'application NCCOBuilder qui est fourni avec le SDK Vonage Node pour nous aider à construire le JSON dont nous avons besoin pour le NCCO. Nous ajoutons une Talk pour afficher le message de bienvenue, puis une action Input qui écoute la voix de l'utilisateur et la transforme en une chaîne de caractères. Nous pourrions également utiliser la fonction DTMF ou la saisie via le clavier téléphonique, mais pour l'instant, nous utiliserons la conversion de la voix en texte.
Lorsque l'utilisateur nous dit ce qu'il veut faire (et comme il s'agit d'une démo, nous ne prenons en charge que l'annulation d'un billet), les serveurs de Voice prennent cette entrée vocale et la transforment en une requête JSON qui l'envoie à l'URL déterminée par notre Input qui est <ngrok URL>/determine_action. La requête entrante ressemblera à ceci :
{
"speech": {
"timeout_reason": "end_on_silence_timeout",
"results": [
{
"confidence": "0.7276162",
"text": "cancel ticket"
}
]
},
"dtmf": {
"digits": null,
"timed_out": false
},
"from": "15556661234",
"to": "18005556666",
"uuid": "ee9d7f5daa81673b5461c6f5XXXXXXXX",
"conversation_uuid": "CON-baa6dcee-6750-4cde-82d2-XXXXXXXXXXXX",
"timestamp": "2023-12-22T03:47:55.378Z"
}Ce blob JSON est posté sur notre serveur et traité par src/routes/DetermineAction.js.
// src/routes/DetermineAction.js
router.post('/determine_action', async (req, res) => {
const builder = new NCCOBuilder();
if (req.body.speech?.results[0].text === 'cancel ticket') {
const directus = getDirectusClient();
const user = await getUserFromPhone(req.body.from);
const tickets = await directus.request(readItems(
'tickets',
{
'fields': [
'id',
'showtime_id.*.*'
],
'sort': [
'showtime_id.start_time'
],
'filter': {
'user': {
'_eq': user.id
}
}
}
));
if (tickets.length === 0) {
builder
.addAction(new Talk('We do not see any tickets for you at the moment. Would you like to hear a list of available movies to purchase a ticket?'))
.addAction(new Input(
null,
{
'context': ['yes', 'no']
},
`${getDomain(req)}/list_movies`)
)
} else if (tickets.length === 1) {
builder
.addAction(new Talk(`Would you like to cancel your ticket for ${tickets[0].showtime_id.movie_id.title}?`))
.addAction(new Input(
null,
{
'context': ['yes', 'no']
},
`${getDomain(req)}/confirm_cancel?id=${tickets[0].id}`)
)
} else {
// Ask which upcoming tickets to cancel
}
} else if (req.body.speech.results[0].text === 'buy ticket') {
builder
.addAction(new Talk('Would you like to hear a list of available movies to purchase a ticket?'))
.addAction(new Input(
null,
{
'context': ['yes', 'no']
},
`${getDomain(req)}/list_movies`)
)
} else {
builder.addAction(new Talk('Sorry, I do not understand what you would like to do.'));
builder.addAction(new Notify(
{
action: 'restart',
from: req.body.from,
},
`${getDomain(req)}/notify`
));
}
return res.send(builder.build());
});Cette route est un peu plus compliquée car nous essayons de comprendre ce que l'utilisateur a fait. Le code de démonstration permet d'annuler un billet et d'en acheter un nouveau.
Lors de l'annulation d'un billet, nous affichons les billets que l'utilisateur a achetés. S'il n'en a pas acheté, nous lui demandons s'il souhaite en acheter un. Si c'est le cas, nous le poussons vers le flux de travail d'achat.
S'ils n'ont qu'un seul billet, nous leur demandons s'ils souhaitent l'annuler. C'est le flux de travail que nous détaillons dans ce billet, nous renvoyons donc un NCCO avec un autre Talk et Input confirmant simplement l'annulation, et si l'utilisateur confirme l'annulation, nous appelons à nouveau l'API Directus pour supprimer le billet.
Nous transmettons également l'identifiant du film dans l'URL en tant que paramètre de requête. HTTP lui-même est sans état, et normalement, nous devrions utiliser une session pour suivre les flux. Comme l'API Voice ne fonctionne pas avec des sessions, nous conservons l'état à l'ancienne en passant des paramètres de requête. Nous transmettons l'ID du ticket à la route de confirmation via un paramètre de requête.
Si l'utilisateur avait plus d'un billet, nous lui demanderions de sélectionner le billet qu'il souhaite annuler, mais pour l'instant, nous laisserons cette logique de côté, car elle rend la démo plus compliquée.
// src/routes/ConfirmCancel.js
router.post('/confirm_cancel', async (req, res) => {
const builder = new NCCOBuilder();
if (req.body.speech.results[0].text === 'yes') {
const directus = getDirectusClient();
directus.request(deleteItem('tickets', req.query.id));
builder
.addAction(new Talk('We have removed your ticket'))
.build();
}
builder.addAction(new Notify(
{
action: 'restart',
from: req.body.from,
},
`${getDomain(req)}/notify`
));
return res.send(builder.build());Lorsque nous cliquons sur l'itinéraire d'annulation, nous prenons l'identifiant du ticket dans les paramètres de la requête et renvoyons une demande à Directus. deleteItem() à Directus. Cela supprime le billet de la base de données de Directus et nous informons le client que nous avons annulé son billet.
Nous pourrions mettre fin à l'appel à ce moment-là, mais pour être utile, nous renvoyons l'utilisateur au début du flux de travail. Pour ce faire, nous envoyons une action Notify en tant que NCCO, ce qui nous permet d'injecter un NCCO dans le flux.
// src/routes/Notify
router.all('/notify', async (req, res) => {
const builder = new NCCOBuilder();
const domain = getDomain(req);
return res.send(
builder
.addAction(new Talk('What else can we help you with?'))
.addAction(new Input(
null,
{
'context': ['cancel ticket', 'buy ticket']
},
`${domain}/determine_action`))
.build()
);
});Ce BCN de Notify demande simplement à l'utilisateur s'il y a autre chose que nous pouvons faire pour l'aider et transmet la réponse à l'itinéraire pour déterminer l'action qu'il souhaite entreprendre, comme si l'utilisateur venait d'appeler. L'utilisateur peut raccrocher à tout moment pour mettre fin à l'appel.
Conclusion
Lorsque vous créez une application, jouez sur vos points forts. Occupez-vous de développer la logique de votre application et laissez d'autres services s'occuper des autres parties de votre application qui ne sont pas vos points forts. Directus est un excellent moyen de traiter les données, et les API Voice de Vonage vous évitent d'avoir à vous occuper de la téléphonie. Rejoignez la conversation sur notre Communauté des développeurs de Vonage Slack ou envoyez-nous un message sur X, anciennement connu sous le nom de Twitter.
Partager:
Chris est le Developer Relations Tooling Manager et dirige l'équipe qui construit vos outils préférés. Il programme depuis plus de 15 ans dans différents langages et pour différents types de projets, depuis le travail avec les clients jusqu'aux systèmes à grande échelle et aux données volumineuses. Il vit dans l'Ohio, où il passe son temps avec sa famille et joue à des jeux vidéo et TTRPG.