https://d226lax1qjow5r.cloudfront.net/blog/blogposts/using-vonage-apis-with-mongodb-atlas-part-5/mongodb_vonage_p5.png

Uso de las API de Vonage con MongoDB Atlas - Parte 5

Publicado el May 24, 2023

Tiempo de lectura: 7 minutos

En esta serie:

Terminamos nuestra serie sobre Atlas MongoDB y las API de Vonage con un vistazo a nuestra API de In-App Messaging. A través de las otras partes de la serie, recorrimos la configuración de MongoDB Atlas y Vonage en la Parte 1, cómo agregar Vonage Verify a una aplicación en la Parte 2, cómo permitir la interacción del cliente a través de nuestra Messages API y Meetings API, y finalmente mostraremos cómo podemos utilizar nuestra In-App Messaging API para enviar notificaciones a los usuarios que iniciaron sesión en la sección de administración de nuestro sitio.

In-App Messaging

In-App Messaging forma parte de un amplio conjunto de API de llamadas de conversación, cuya otra mitad es nuestro producto In-App Voice. Como su nombre indica, In-App Messaging se encarga del envío de mensajes de texto de un dispositivo cliente a otro, mientras que In-App Voice gestionará el tráfico de voz. Estas API tienen SDK para iOS, Android y navegadores web modernos. Nosotros mostraremos el Web Client SDK para nuestra demostración, pero puedes ampliarlo para permitir la comunicación entre plataformas desde cualquier combinación de Web, iOS y Android.

In-App Messaging y In-App Voice se apoyan en otras dos API que componen nuestro producto Conversations. Estas son la Conversation API, que son eventos entre uno o más Miembros, y la Users API, que maneja la información de contexto del usuario. El flujo de trabajo general es que un usuario se autentique a través de su aplicación y se asocie con un Usuario en nuestra API de Usuarios. Este Usuario se convertirá en Miembro de varias Conversaciones, o almacenes de eventos compuestos por diferentes eventos desencadenados como text, message:submitted, member:joinedetc.

Cada Client SDK hablará con estos servidores backend para facilitar la comunicación en tiempo real a través de eventos de voz o texto. Esto le permite a usted, como desarrollador, disponer de una interfaz para interactuar con nuestras API, y los SDK se encargan de todo el trabajo de conectar y trabajar con nuestras API. Esto te libera de tener que idear sistemas de mensajería dependientes de la plataforma o formas de derivar mensajes entre diferentes plataformas.

Utilizaremos la In-App Messaging API para nuestra demostración de restaurante para enviar un mensaje a cualquier usuario administrador que haya iniciado sesión. Esto hará que aparezca una ventana emergente en su pantalla con un enlace a la reunión que se ha solicitado. Aunque nosotros lo utilizamos para una única notificación, puedes ampliarlo a todo un sistema de chat. Utilizaremos el botón nexmo-client para manejar todo el trabajo en el navegador, y utilizaremos un cliente personalizado en nuestro código de servidor back-end para hablar con las Conversations APIs.

Notificaciones administrativas

Empecemos por ver cómo funciona la sección de administración. Queremos que aparezca una notificación cuando un usuario solicite una reunión. Para las notificaciones emergentes, podemos utilizar @meforma/vue-toasterque es un plugin de VueJS que genera ventanas emergentes al estilo de las tostadas (las tostadas son pequeñas notificaciones que aparecen en la parte inferior de la pantalla y luego desaparecen).

Pero, ¿cómo recibimos la notificación? Debemos usar el nexmo-client SDK web para conectarnos a nuestra aplicación de Vonage y luego suscribirnos a la conversación o serie de mensajes correctos. Creamos un componente de notificación que se conectará a la conversación y mostrará las nuevas notificaciones que lleguen.

import ConversationClient from 'nexmo-client'
import { createToaster } from '@meforma/vue-toaster'
import { authenticationStore } from '../stores/authenticationStore';

const authStore = authenticationStore();
const toaster = createToaster({ dismissable: true });

async function boot() {
    const data = await fetch(import.meta.env.VITE_API_URL + '/jwt', {
        method: 'POST',
        body: JSON.stringify({username: authStore.username}),
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + authStore.token
        }
    })
        .then(resp => resp.json())
        .catch(err => console.log(err));
    
    const jwt = data.token
    const conversationID = data.conversation

    const client = new ConversationClient({ debug: true })
    let app = await client.createSession(jwt)
    const conversation = await app.getConversation(conversationID)

    conversation.on("support_request", async (sender: any, event: any) => {
        toaster.show(`${event.body.email} has request support: <a target="_blank" href="${event.body.host_url}">Join Meeting</a>`);
    });
}

boot()

Necesitamos un token de autorización para conectar el SDK web a Vonage. Este token es un JWT firmado que utiliza la clave privada que generamos en la Parte 1 cuando creamos una aplicación de Vonage. Este JWT permitirá que el SDK web se comunique con las API de Vonage y se autentique. Esto es muy similar a la forma en que MongoDB Realms autentica a nuestro usuario administrador.

Para obtener información completa sobre la creación de JWT de cliente para la Conversation API, consulte nuestra documentación. Revisaremos cómo hacemos el JWT en un segundo para que puedas ver un ejemplo, pero por ahora, continuemos con el código VueJS. Ahora mismo, sólo tenemos que preocuparnos de pedir a nuestro servidor back-end que genere un JWT para nuestros usuarios.

El servidor back-end nos devolverá dos bits de información: el JWT firmado de Vonage y el ID de la conversación para que nos conectemos. Necesitaremos esto para decirle al SDK web qué conversación escuchar.

A continuación, creamos un nuevo ConversationClient()un objeto del paquete nexmo-client paquete. Llamamos createSession(jwt) a ese objeto y le pasamos el JWT que nuestro backend creó para nosotros. Esto autentica nuestra aplicación web en Vonage. Luego le decimos que obtenga una conversación específica, el ID que nuestro servidor backend nos devolvió.

Nuestra aplicación está ahora escuchando eventos pero no maneja nada. Debemos utilizar el método conversation.on() para manejar los mensajes entrantes. Utilizaremos un mensaje personalizado llamado support_request para pasar ese nombre como primer parámetro. El segundo parámetro es una función manejadora. Nuestra función manejadora extraerá la información de la solicitud de soporte y mostrará una notificación.

Eso es. Si entra en la sección de administración y va a Ver pedidospodrá ver esto en acción. Si abre una nueva ventana, inicia sesión como cliente y envía un pedido, inicie una videollamada y aparecerá una nueva notificación en la ventana de administración.

¿Cómo generamos un token?

Antes de ver el código del backend, veamos cómo creamos el JWT. Debemos añadir dos piezas cruciales de información al JWT para crear un token válido del lado del cliente. Estos son subque es el usuario para el que se está generando el token. La otra es pathuna lista de rutas a las que el JWT puede acceder.

app.post('/jwt', async (req, res) => {
    const { username } = req.body;
    const conversation = await conversations.fetchByName('pos-notifications');
    const conversationPath = `/*/conversations/${conversation.id}/**`;
    let acl = {
        "paths": {
            "/*/sessions/**": {},
          }
    }
    acl.paths[conversationPath] = { methods: ['GET'] }

    const key = readFileSync(process.env.VONAGE_PRIVATE_KEY);
    const token = tokenGenerate(process.env.VONAGE_APPLICATION_ID, key, {
        sub: username,
        acl: acl,
    });
    res.json({ token, conversation: conversation.id })
})

Para restringir nuestro token a lo que necesitamos, establecemos nuestro ACL en /*/sessions/** y /*/conversations/<conversation-id>/**. Esto significa que nuestro JWT sólo es válido para la conversación específica a la que se envían nuestras notificaciones, llamada pos-notifications. Si alguien secuestrara el token, sólo serviría para acceder a una conversación.

Aseguramos aún más el JWT diciéndole que sólo permitiremos GET a la conversación. Esto significa que nuestro front-end o cualquiera que intente usar el JWT sólo podrá leer la conversación y no modificarla. En tu aplicación, deberías restringir las rutas y métodos para ser lo más estricto posible para el JWT.

Crear una notificación

Sabemos cómo leer una conversación, pero ¿cómo introducir un evento en una conversación? Si entramos en nuestro archivo server.ts y vamos a nuestra ruta /api/website/video-call generamos allí un nuevo evento.

const orderRecord = await client.db('restaurant_pos_demo')
    .collection('orders')
    .updateOne(
        { _id: new ObjectId(orderNumber) },
        { $set: { meetingUrl: data._links.host_url.href}}
    )
    .then(async (document) => {
        const userRecord = await client.db('restaurant_pos_demo')
            .collection('users')
            .findOne({ _id: new ObjectId(decodedToken.user_id) });

        await conversations.addEventToConversation(
            'pos-notifications',
            userRecord.username, 
            {
                type: 'custom:support_request',
                body: { 
                    host_url: data._links.host_url.href,
                    email: userRecord.username
                }
            });

        res.json({
            guest_url: data._links.guest_url.href
        })
    });

Actualizamos la orden con las nuevas URLs de las reuniones. Una vez hecho esto, buscamos el usuario en MongoDB usando la colección users colección y findOne(). Esto nos da el registro de usuario para el usuario autenticado, y entonces podemos usar conversations.addEventToConversation() para enviar un nuevo evento a nuestra conversación. Para todas las notificaciones, utilizamos una conversación llamada pos-notifications.

addEventToConversation() es una envoltura que maneja un montón de trabajo subyacente que se necesita para añadir un evento a una conversación y es parte de una colección de métodos en conversation.ts que hará un montón de búsquedas y configuración para nosotros.

Como he mencionado antes, la Conversations API es una combinación de Conversations y Users API. Cuando llamamos a addEventToConversation()el método intentará buscar una conversación con un nombre especificado. Esa búsqueda de conversación generará una conversación con un nombre (en nuestro caso, pos-notification) si no existe ninguna.

A continuación necesitamos un miembro al que adjuntar el evento. Los miembros son usuarios que forman parte de una conversación específica. Un miembro sólo forma parte de una conversación. Sin embargo, un usuario puede ser miembro de varias conversaciones. addEventToConversation() intentará buscar un miembro por la dirección de correo electrónico especificada. Si ese miembro no forma parte de la conversación. Buscará un usuario con ese correo electrónico. Si ese usuario no existe, lo creará. Ese usuario se añade entonces a la conversación como miembro, y se devuelve la información del miembro.

A continuación, podemos enviar el nuevo evento a la conversación. El evento se propaga a cualquier cliente conectado que escuche la conversación. Cuando enviamos un evento al servidor, cualquier usuario conectado a la página "Pedidos" recibirá el evento y la notificación emergente.

Conclusión

Esto es una pequeña muestra de lo que puede hacer la Conversations API. Puedes crear interfaces de chat con todas las funciones que esperan los usuarios, como notificaciones de escritura, uso compartido de imágenes y almacenamiento de mensajes. Además, todo ello en tiempo real y multiplataforma. Un usuario web puede enviar una notificación a un cliente iOS del mismo modo que a un cliente Android o a otro cliente web.

También puede combinarlo con In-App Voice para proporcionar voz en tiempo real entre varios clientes o combinarlo con nuestra Voice API para permitir que el usuario de una aplicación llame a un teléfono o viceversa. El cliente podría, en cambio, llamar al restaurante, ¡y el restaurante podría responder en su navegador!

Aquí termina nuestro recorrido por MongoDB Atlas y algunas API complementarias de Vonage. Esperamos que esta serie te haya inspirado a ver qué ofrece cada una de nuestras plataformas y cómo podrían serte útiles. Como siempre, comunícate con nuestros defensores de desarrolladores si tienes alguna pregunta.

¡Feliz codificación!

Compartir:

https://a.storyblok.com/f/270183/384x384/3bc39cbd62/christankersley.png
Chris TankersleyGestor de herramientas de relaciones con los desarrolladores

Chris es el director de herramientas de relaciones con los desarrolladores y dirige el equipo que crea sus herramientas favoritas. Lleva más de 15 años programando en varios lenguajes y tipos de proyectos, desde trabajos para clientes hasta sistemas de big data a gran escala. Vive en Ohio, donde pasa el tiempo con su familia y jugando a Video y TTRPG.