=================================== Manejo de infomación con WebSockets =================================== Sails usa `socket.io `_ como el motor subyacente para la comunicación, por lo que la especificación de la comunicación se ejemplifica con fragmentos de código JavaScript. El sistema debe notificar a los asistentes y a los temporizadores de las estaciones cuando se lleven a cabo acciones de los usuarios que requieran atención. Esta comunicación será llevada a cabo utilizando la tecnología WebSocket. Descripción general =================== Establecimiento de conexión --------------------------- .. image:: /images/websockets/client_socket_connection_sequence.svg :align: center 1. El cliente intenta establecer una conexión con el servidor Onsite en la ruta ``{local_ip}:1337/notifications``, donde *local_ip* corresponde a la IP local del servidor Onsite; y *notifications* es el nombre del espacio de nombres que se usará para el manejo de conexiones WebSocket. 2. El servidor recibe la conexión y crea un socket para comunicarse con el cliente. 3. Inmediatamente después de establecer la conexión, el cliente debe autenticarse ante Onsite como el tipo de cliente que anunció. 4. El servidor responde con el resultado de la autenticación. Si el proceso es exitoso, el servidor incluirá al cliente en el *room* correspondiente. Si la autenticación resulta en error, el servidor cierra el socket. Flujo ***** La conexión entre el cliente y el servidor se establece sin autenticación. Por lo tanto, inmediatamente después de establecer conexión, el cliente debe identificarse ante Onsite con una petición firmada con un JWT de la siguiente manera: Servidor ^^^^^^^^ .. code-block:: javascript connect: function (req, res) { var socket = req.socket; var jwtAuth = req.body.params['jwt-api-key']; // JWT validation goes here } Cliente ^^^^^^^ Conexión hacia Onsite .. code-block:: javascript var notifications = io.connect('https://[local].onsite.gamersarena.com.mx/'); notifications.emit('get', { url: "/notifications", params: { "jwt-api-key": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" } }, function(data) { // data is server response }); Conexión hacia Tournament Handler .. code-block:: javascript var notifications = io.connect('http://tournament.gamersarena.com.mx/'); notifications.emit('get', { url: "/notifications", params: { "jwt-api-key": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ", "tournamentId": 2 // identificador del torneo para recibir notificaciones } }, function(data) { // data is server response }); Si el proceso de autenticación es exitoso, el servidor debe agregar al cliente al *room* correspondiente dependiendo de la entidad a la que pertenezca su JWT. Adicionalmente, se establece en la BD el identificador del socket asociado al cliente, el cual se obtiene invocando a la variable ``id`` del socket. Si la autenticación falla, el servidor cierra el socket asociado al cliente cuyas credenciales no pudieron ser verificadas. Esto se logra invocando a la función ``disconnect()`` del socket. Enlace a *room* --------------- Un *room* es un conjunto de sockets identificados por un nombre, el cual es usado para enviar mensajes a un subconjunto de los clientes conectados. Por defecto, cada socket se une automáticamente a un *room* cuyo nombre es el *id* del socket. Con el objetivo de propagar mensajes a cierto tipo de clientes en el local, es conveniente incorporarlos a un *room* dependiendo de su rol. Los rooms gestionados por el sistema Onsite son: - assistant - timer Por otro lado, en Tournament Handler se crean rooms con la estructura: - ``assistant_[id]``, donde ``id`` corresponde al identificador del torneo en Tournament Handler al cual está suscrito el asistente. Agregar a un cliente a un *room* se logra de la siguiente manera .. code-block:: javascript socket.join('room name'); Y después enviar un mensaje a los sockets en el room como sigue .. code-block:: javascript broadcast('room name', "eventName", message_as_object); donde - *room names* es el nombre o nombres (arreglo de cadenas) de los rooms a los cuales enviar el mensaje - *eventName* es el identificador de la operación - *message_as_object* es el objeto que se envía como mensaje Desconexión ----------- En caso de ocurrir un desconexión de parte de el cliente, su id de socket debe ser limpiado de la base de datos, lo cual se logra estableciendo como nulo el campo ``socket_id`` de la entidad correspondiente. Estuctura de los mensajes ========================= Servidor -------- Si la comunicación es iniciada por el servidor, los mensajes enviados tienen una estructura de objeto como la siguiente: .. code-block:: javascript { check_in_id: { id: 4, nick: "aguila_calva" } } donde el cuerpo es variable, por lo que la estructura del mensaje esperado por el cliente será descrita en la especificación de cada evento. Si el mensaje corresponde a una **respuesta** de una petición efectuada por el cliente, la estructura será la siguiente: .. code-block:: javascript { body: { error: { code: '409-13', message: 'There was a problem with the checkin.' } }, statusCode: 409 } Si la respuesta fue exitosa, el campo ``error`` no es incluido. Cliente ------- Los mensajes del cliente siempre presentan dos campos: *header* y *body*. En el *header* se incluyen encabezados de la operación, como el token de autenticación. Por otro lado, el *body* alberga los valores relativos a la transacción. .. code-block:: javascript { "header": { "jwt-api-key": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" }, "body": { "check_in_id": { "id": 4, "nick": "aguila_calva" } } }