Real Time Power Apps, con Azure SignalR y React

Uniendo tu backend en Azure con tu aplicación en tiempo real

Vamos a partir de la base de que Power Apps o en concreto Power Platform se está abriendo paso a Azure a pasos agigantados, y que cada vez será más frecuente incorporar backends en Azure que den servicios a nuestras aplicaciones, y crear procesos alrededor de nuestras Apps basados en servicios de integración como Logic Apps o backend serverless en Azure Functions.

Imagen 1.- Power Platform y Azure.

En muchos casos podremos crear conectores personalizados desde Azure y hacer llamadas asíncronas desde nuestras apps, pero ¿Cómo conectamos nuestros procesos pesados de backend con nuestras aplicaciones sin recurrir a los temidos timers?.

Contruyendo nuestra API Web Socket, un backend con Azure Signal R y Azure Functions

En artículos anteriores hemos hablado de Azure SignalR , que por recordarlo es un servicio que proporciona Azure para incorporar funcionalidades web en tiempo real mediante peticiones Http y haciendo uso de protocolo Websocket para conectar nuestros clientes con nuestro servicio de mensajería.

Además, este servicio nos evita tener que montar un backend propio que haga las conexiones y conecte con nuestros clientes, sino que podemos tener múltiples clientes escuchando a nuestro servicio de SignalR y es este el que se encarga de distribuir los mensajes en función de los clientes conectados a un Hub en concreto.

Imagen 2.- Azure SignalR.

Sabiendo un poco por encima que es Azure SignalR, vamos a empezar a utilizarlo en nuestra solución Real Time con Power Apps. Lo primero es crear nuestra API WebSocket sobre Azure functions, que va a encargarse de generar esa conexión segura con Azure SignalR, y de negociarla por nosotros.

Imagen 3.- Negociar conexión.

Como vemos nuestra solución es simple, crear una API de entrada sobre Azure Functions, que negocie las conexiones para nuestra futura aplicación de Power Apps, para que esta puede recibir todo mensaje que enviemos a nuestro servicio de mensajería en tiempo real.

Nuestra functions solo necesita un método Negotiate del siguiente tipo:

image4

El trigger es del tipo HTTP, y además recibirá una petición de conexión del tipo SignalR a un Hub concreto que nosotros hemos denominado Chat. Si todo va bien, se devuelve al cliente que invoque esta función la conexión segura al SignalR, y desde la cual empezará a recibir mensajes.

Para que todo esto funcione, necesitamos tener vinculado a nuestro proyecto de Azure Functions un servicio de SignalR, vía Azure Key Vault, o en las propias settings del servicio de Functions tenemos que referenciar la conexión segura a nuestro servicio de SignalR en Azure.

1 "ConnectionStrings ": {
2
3 "AzureSignalRConnectionString ":
4 "Endpoint=https://eventdrivensignalrservice.service.signal ... "
5
6}
7

La cadena de conexión la podemos encontrar en el portal de Azure en las propiedades del propio servicio de SignalR. Una vez tenemos esta mínima función programada, podemos desplegarla en Azure para poder hacer el componente React que va a conectarse con ella desde Power Apps.

Construir nuestro componente de Notificaciones con PCF y React

El segundo paso es dar forma a nuestro componente de Power App con React. Para ellos vamos a programar con Power App Component framework, un pequeño componente que se va a conectar con nuestra API Websocket, y desde la cual va a recibir mensajes en tiempo real.

Imagen 5.- Arquitectura global.

Para empezar, necesitamos tener instalado en nuestra máquina de desarrollo, Power App CLI y que podéis encontrar en CLI de Microsoft Power Platform - Power Apps | Microsoft Docs.

Para comprobar que todo va bien abrir vuestro Visual Studio Code y en el terminal escribir PAC para ver que tenéis la última versión instalada.

image6

Ahora crearos una carpeta en vuestro entorno de desarrollo, y ejecutar desde el terminal la instrucción pac pcf init --namespace notificationComponent --name TSNotificationComponent --template field

Si todo va bien, nos va a crear una estructura de proyecto en React de Power App Component, que nos va a permitir diseñar nuestro componente de Power Apps.

El primer paso es instalar el paquete de SignalR en nuestra solución para poder usarlo, ejecutando en el terminal npm install @aspnet/signalR .

Sobre la solución creada nos creamos una carpeta Model y dentro un fichero Recieved.ts

Imagen 6.- Modelo de datos.

Abrirmos el fichero y definimos la siguiente clase:

1class ReceivedModel
2{
3 sender: string;
4 text: string;
5 type:string;
6}
7

Ahora vamos a modificar el manifiesto de la aplicación para que podamos recibir los datos que necesitamos. Para ello, en el fichero ControlManifest.input.xml añadimos las siguientes líneas:

1 <property name= "Message " display-name-key= "Property_Display_Key "
2description-key= "Property_Desc_Key " of-type= "SingleLine.Text "
3
4usage= "bound " required= "false " />
5
6 <property name= "SignalRUrl " display-name-key= "signalR_Key "
7description-key= "signalR_Key " of-type= "SingleLine.Text "
8usage= "bound " required= "true " />
9
10 <property name= "hubName " display-name-key= "hub_key "
11description-key= "hub_key " of-type= "SingleLine.Text " usage= "bound "
12required= "false " />
13

Con esto estamos definiendo las tres propiedades que queremos manejar en nuestro componente:

  • SignalRUrl: Aquí debemos introducir la URL del API Webscoket que hemos creado anterior mente en este artículo.

  • HubName: Vamos a usar esta propiedad para identificar el "nombre del evento ", del que queremos recibir mensajes.

  • Message: Que es cada uno de los mensajes que vamos a recibir en tiempo real.

Una vez configurado el manifiesto, nos queda configurar el Index.ts que es la clase que maneja todo el componente. Lo primero es importar la referencia a SignalR con la siguiente línea:

1import * as signalR from " @aspnet/signalr ";
2

Dentro de la definición del componente "Notification ", debemos añadir las siguiente propiedades privadas:

1private _receivedMessage: string;
2
3private _notifyOutputChanged: () => void;
4
5private _context: ComponentFramework.Context <IInputs>;
6
7private connection: signalR.HubConnection;
8
9private _signalRApi: string;
10
11private _hub: string;
12

Dentro del método Init que es el primer método que se ejecuta al cargar nuestro componente en la APP, debemos añadir el siguiente código:

1public init(context: ComponentFramework.Context <IInputs>,
2 notifyOutputChanged: () => void, state: ComponentFramework.Dictionary,
3 container:HTMLDivElement): void
4 {
5 this. _context = context;
6 this. _notifyOutputChanged = notifyOutputChanged;
7 this. _signalRApi=context.parameters.SignalRUrl.raw?
8 context.parameters.SignalRUrl.raw: " ";
9 this. _hub=context.parameters.hubName.raw?
10 context.parameters.hubName.raw: " ";
11 //Create the connection to SignalR Hub
12 this.connection = new signalR.HubConnectionBuilder()
13 .withUrl(this. _signalRApi)
14 .configureLogging(signalR.LogLevel.Information) // for debug
15 .build();
16 //configure the event when a new message arrives
17 this.connection.on(this. _hub, (message:string) => {
18 this. _receivedMessage=message;
19 this. _notifyOutputChanged();
20 });
21 //connect
22 this.connection.start()
23 .catch(err => console.log(err));
24 }
25

Si analizamos el código anterior, lo primero que hacemos es mapear a nuestras propiedades privadas los datos que hemos definido en el manifiesto (hubName, SignalRUrl), y con estas vamos a crear nuestra conexión SignalR y a definir nuestro evento para recibir los mensajes:

1this.connection = new signalR.HubConnectionBuilder()
2 .withUrl(this. _signalRApi)
3 .configureLogging(signalR.LogLevel.Information) // for debug
4 .build();
5 //configure the event when a new message arrives
6 this.connection.on(this. _hub, (message:string) => {
7 this. _receivedMessage=message;
8 this. _notifyOutputChanged();
9 });
10

Por último, el código inicializa la conexión contra el SignalR y abre la conexión por WebSockets con el servicio en Azure, recordemos que pasa por nuestra API WebSocket, y es esta la que establece la conexión de forma correcta con Azure SignalR.

Por último, tenemos que poder refrescar la propiedad Message cada vez que se reciba un mensaje, ya que el código de Init solo refresca la propiedad privada, necesitamos que el componente exponga a nuestra APP el mensaje recibido.

Para ello sobre el método getOutputs añadimos el siguiente código:

1public getOutputs(): IOutputs
2{
3 //This code will run when we call notifyOutputChanged when we receive a new message
4 //here is where the message gets exposed to outside
5 let result: IOutputs = {
6 Message: this. _receivedMessage
7 };
8
9 return result;
10}
11

Con getOutputs podemos indicar a una propiedad de salida como Message, que se resetee con la propiedad recievedMessage con cada cambio de valor de esta.

Depurando el componente en local con Postman

Una vez tenemos el componente terminado, podemos probar el componente. Para ello vamos a añadir un método que reciba mensajes en nuestra API de Websocket, con el siguiente código, con el objetivo de que el componente que se conecte al evento puede recibir los mensajes:

image8

Esta functions recibe mensajes con un modelo tipado, pero que podéis elegir vosotros como mejor os venga para definir el mensaje. Ese mensaje lo enviamos a SignalRMessage, mediante la cadena de conexión de nuestro API Websocket, y cada cliente que se conecte a esta conexión irá recibiendo en tiempo real los mensajes.

Una vez terminado de actualizar la functions, para probar el componente de React, tenemos que ejecutar estos tres comandos en orden:

  • npm install

  • npm run build

  • npm run start

Con estos tres comandos instalamos todos los paquetes necesarios para el componente de React de Power Apps, lo compilamos y sobre todo nos lo ejecuta en un navegador local.

Imagen 7.- Simulador PCF.

Como vemos en la figura actual se levanta un navegador con el componente, simulando a Power Apps Studio, y podemos configurar nuestro componente:

  • HubName: Compartimoss

  • SignalRUrl: url_websocket_api/api, recordar no poner el método negotiate en la url, lo coge por convenio.

Por otro lado, debemos lanzar desde Postman peticiones al método SendMessage que hemos configurado y desplegado en nuestra Function.

Imagen 8.- Postman testing.

Mandamos un json con los datos del mensaje, recordando que cruzamos el nombre de la propiedad HubName con el target que enviamos por Postman. Podríamos cambiar el nombre de hubname por cualquier otro, aunque recordar que debemos asegurarnos que por código enviemos a SignalR, el nombre del "Target " de forma correcta:

1return signalRMessages.AddAsync(
2 new SignalRMessage
3 {
4 Target = message.Target,
5 Arguments = new [ ] { message.Text }
6 });
7

Con esto ya tenemos en tiempo real el componente en el simulador, y podemos ver que gracias al código del getOuputs de mi componente, la propiedad Message se actualiza en tiempo real

Imagen 9.- Propiedades componente en local.

Desde aquí ya tenemos conectado un componente PFC con nuestro backend en Azure mediante Azure SignalR. Este componente lo podemos empaquetar, y desplegar en nuestro tenant de Power Apps, pudiendo así usarlo en nuestras aplicaciones.

¿Cómo le podemos sacar partido?, pues fácil si a la propiedad mensaje le conectamos funcionalidad de Power Apps como un Notify podemos tener notificaciones en nuestra App en tiempo real.

Imagen 10.- Notify real time.

Como vemos en la siguiente imagen, ya tengo una página en blanco con mi componente Notification agregado, y sobre la propiedad "On change ", añado un Notify(Notification.Message).

Notification es el nombre que le he dado a nuestro componente en la página de Power Apps, y Message es la propiedad que por código me escribía los mensajes obtenidos desde Azure SignalR.

Imagen 11.- Binding notify con el componente.

Con esto tenemos una notificación en tiempo real en Power Apps, con solo añadir el componente y recordando configurar los campos de signalR_key y hub_key tal cual se ve en la siguiente figura.

Imagen 12.- Configurando el componente.

Conclusiones

Pues la mejor de las reflexiones, es que en todo el artículo estamos hablando de Power Apps, y no hemos parado de hablar de Azure y de React, nada más que añadir.

Bueno sí, ¿Quién decía que Power Platform es solo Low Code?, los tiempos cambian, y debemos poder sacar el máximo partidos a cada uno de los servicios que nos proporciona Microsoft, sean de Office 365, de Power Platform o de Azure. Vivimos la época dorada del desarrollo de Aplicaciones empresariales, no nos cerremos a una sola tecnología.

¡¡¡¡Feliz navidad a todos!!!!

Sergio Hernandez Mancebo
Azure MVP

Siguemos en LinkedInSiguemos en Twitter
Powered by  ENCAMINA