From 2bef00a3c0e559300332274d03c924c4bb1bbb2d Mon Sep 17 00:00:00 2001 From: David Arranz Date: Thu, 20 Apr 2023 14:20:09 +0200 Subject: [PATCH] Notificaciones push a dispositivos --- modules/events/event.controller.js | 2 +- modules/events/event.model.js | 2 +- .../notification/notification.controller.js | 82 +++++---- modules/notification/notification.service.js | 169 +++++++++++------- .../notification/notification.validations.js | 1 + modules/notification/user_device.service.js | 11 ++ 6 files changed, 163 insertions(+), 104 deletions(-) diff --git a/modules/events/event.controller.js b/modules/events/event.controller.js index 7f6a5b5..429133f 100644 --- a/modules/events/event.controller.js +++ b/modules/events/event.controller.js @@ -303,7 +303,7 @@ const extraControllers = { try { let notification = notificationHelper.createNotificationValidatedInscription(inscription); console.log(notification); - let result = notificationService.sendNotification(notification, [inscription.user.id]); + let result = notificationService.sendNotificationToUsers(notification, [inscription.user.id]); console.log(result); } catch (error) { diff --git a/modules/events/event.model.js b/modules/events/event.model.js index 1c065d5..3eb4a68 100644 --- a/modules/events/event.model.js +++ b/modules/events/event.model.js @@ -322,7 +322,7 @@ module.exports = function (sequelize, DataTypes) { model: sequelize.models.MultimediaFile, as: "multimediaFile" }], - order: [["type", "asc"]], + order: [["type", "ASC"]], // <- esto no funciona por ahora en Sequelize }, ] } diff --git a/modules/notification/notification.controller.js b/modules/notification/notification.controller.js index 3b7b256..c261bd8 100644 --- a/modules/notification/notification.controller.js +++ b/modules/notification/notification.controller.js @@ -21,7 +21,7 @@ const controllerOptions = { MODULE_NAME }; async function _sendNotificationAllActiveUsers(notification) { - let limit = 4; + let limit = 33; let page = 1; let totalRows = 0; @@ -38,7 +38,7 @@ async function _sendNotificationAllActiveUsers(notification) { page = page + 1; let ids = result.rows.map(function (item) { return item.id }); - notificationService.sendNotification(notification, ids); + notificationService.sendNotificationToUsers(notification, ids); } while (page <= totalPages); @@ -46,6 +46,32 @@ async function _sendNotificationAllActiveUsers(notification) { return 'OK'; } + +async function _sendNotificationAllActiveDevices(notification) { + let limit = 33; + let page = 1; + + let totalRows = 0; + let totalPages = -1; + + do { + let offset = (page - 1) * limit; + let result = await userDeviceService._getActiveDeviceIds(offset, limit); + + console.log(result); + + if (totalPages == -1) { + totalPages = Math.ceil(result.count / limit); + } + + page = page + 1; + + notificationService.sendNotificationToDevices(notification, result.rows); + } while (page <= totalPages); + + return 'OK'; +} + async function _getUserIdsForEventId(eventId, segment) { let userIds = []; @@ -80,8 +106,8 @@ const extraControllers = { /** * notificationSample = { - * "tittle": "título de la notificación", - * "message": "cuerpo de la notificación", + * "title": "título de la notificación", + * "body": "cuerpo de la notificación", * "recipients": { * OPCION 1- Unos usuarios determinados * "userIds": ["*" | "f428a317-6d1f-4eda-aa3e-22baff3f48d7", ...] @@ -92,6 +118,8 @@ const extraControllers = { * "segment": "ALL" | "ALL_VALIDATED" | "ALL_NOT_VALIDATED" | * "PARTNERS_ALL" | "PARTNERS_VALIDATED" | "PARTNERS_NOT_VALIDATED" | * "COLLEGE_ALL" | "COLLEGE_VALIDATED" | "COLLEGE_NOT_VALIDATED" + * OPCION 3 - A todos los dispositivos activos + * "deviceIds": ["*" | "ExponentPushToken[YbOS1AIZjQbchZbxNaVRqC]", ...] * }, * "data": { * "type": "message", @@ -117,6 +145,7 @@ const extraControllers = { let receipt = undefined; let userIds = undefined; + let deviceIds = undefined; let eventId = undefined; let segment = undefined; const { body } = req; @@ -129,6 +158,7 @@ const extraControllers = { return handleErrorResponse(controllerOptions.MODULE_NAME, 'sendNotification', new Error('Missing body content'), res) } + // Evento? if (body.recipients.eventId) { eventId = body.recipients.eventId; @@ -136,13 +166,14 @@ const extraControllers = { } else if (body.recipients.userIds) { userIds = body.recipients.userIds; segment = body.recipients.segment; + } else if (body.recipients.deviceIds) { + deviceIds = body.recipients.deviceIds; } else { - return handleErrorResponse(controllerOptions.MODULE_NAME, 'sendNotification', new Error('Missing user Ids or event Ids'), res) + return handleErrorResponse(controllerOptions.MODULE_NAME, 'sendNotification', new Error('Missing user Ids or event Ids or devices Ids'), res) } try { - let notification = notificationHelper.createNotification({ ...body, userId: context.user.id @@ -150,6 +181,8 @@ const extraControllers = { if ((userIds) && (userIds.length == 1) && (userIds[0] == "*")) { receipt = _sendNotificationAllActiveUsers(notification); + } else if ((deviceIds) && (deviceIds.length == 1) && (deviceIds[0] == "*")) { + receipt = _sendNotificationAllActiveDevices(notification); } else { let _userIds = null; @@ -165,45 +198,10 @@ const extraControllers = { } if (_userIds) { - receipt = notificationService.sendNotification(notification, _userIds); + receipt = notificationService.sendNotificationToUsers(notification, _userIds); } } - - /*let getUserIds = async () => { - if (userIds) { - return new Promise(function(resolve) { resolve(userIds) } ); - } else if (eventId) { - switch (segment) { - //Todos los inscritos tanto invitados como libres - case 'ALL_VALIDATED': - userIds = await eventInscriptionService._getInscriptionByEventAndValidated(eventId, true); - break; - //Todos los de lista de espera tanto invitados como libres (Actualmente en invitados no hay lista de espera) - case 'ALL_NOT_VALIDATED': - userIds = await eventInscriptionService._getInscriptionByEventAndValidated(eventId, false); - break; - - //Solo invitados como actualmente no se usa codigo de reserva para los coles, vale con filtrar por aquellos que tengan codigo de reserva - case 'PARTNERS_ALL': - userIds = await eventInscriptionService._getInscriptionByEventFromPartner(eventId); - break; - - //Todos los inscritos al evento, tanto validados como en lista de espera - default: //ALL - userIds = await eventInscriptionService._getInscriptionByEvent(eventId); - break; - } - - return new Promise(function (resolve) { resolve(usersIdsComposer(userIds)); }); - - } else { - return handleErrorResponse(controllerOptions.MODULE_NAME, 'sendNotification', new Error('Missing event and segment'), res) - } - } */ - - //receipt = notificationService.sendNotification(notification, await getUserIds()); - return handleResultResponse(receipt, null, null, res, httpStatus.OK); } catch (error) { console.error(error); diff --git a/modules/notification/notification.service.js b/modules/notification/notification.service.js index 8a0397b..2525b72 100644 --- a/modules/notification/notification.service.js +++ b/modules/notification/notification.service.js @@ -6,9 +6,67 @@ const notificationDetailService = require('./notification_detail.service'); const pushHelper = require('../../helpers/push.helper'); const models = require('../../core/models'); +const saveNotification = (notification) => extraMethods.saveNotification(notification); + +const getUserDevices = async (userId) => { + const result = await userDeviceService.fetchAll({ params: { userId: userId } }, {}); + return result.rows; +}; + +const disableUserDevice = (token) => userDeviceService.update({ + params: { + token: token, + } +}, { valid: 0, invalidated: moment() }, {}); + + +const disableInvalidUserDevices = async (devices) => { + console.log(devices); + const okDevices = []; + devices.forEach(async (device) => { + if (!userDeviceService.isValidPushToken(device.token)) { + await disableUserDevice(device.token); + } else { + okDevices.push(device); + } + }); + + return okDevices; +}; + +const buildPushMessages = (notification, devices) => { + let messages = []; + + devices.forEach(async function (device) { + messages.push(pushHelper.createPushMessage({ + ...notification, + userId: device.userId, + to: device.token, + })); + }); + + return messages; +}; + +const disableUserDevicesWithErrorStatus = (messages, tickets) => { + tickets.forEach(async function (ticket, index) { + if ((ticket.status === 'error') && (ticket.details.error === 'DeviceNotRegistered')) { + disableUserDevice(messages[index].to) + } + }); +} + +let saveResponseStatusPromise = (messages, tickets) => notificationDetailService.saveNotificationDetails(messages, tickets); + + + + + + + const extraMethods = { - createNotification: (data) => { + /*createNotification: (data) => { return { date: data.date, title: data.title, @@ -19,7 +77,7 @@ const extraMethods = { data: data.data, userId: data.userId, } - }, + },*/ saveNotification: ({ date, title, body, ttl, priority, recipients, data, userId }) => { return models.Notification.create({ @@ -35,67 +93,11 @@ const extraMethods = { }, - sendNotification: (notification, userIds) => { + /*sendNotificationToUsers: (notification, userIds) => { - console.log('sendNofitication -----------------------------------------------'); + console.log('sendNotificationToUsers -----------------------------------------------'); console.log(notification, userIds); - let getUserDevicesPromise = (userId) => userDeviceService.fetchAll({ params: { userId: userId } }, {}).then(function (result) { - return new Promise(function (resolve) { resolve(result.rows) }); - }); - let saveNotificationPromise = (notification) => extraMethods.saveNotification(notification); - let sendNotificationsPromise = (messages) => pushHelper.sendPushMessage(messages); - let disableUserDevicePromise = (token) => userDeviceService.update({ - params: { - token: token, - } - }, { valid: 0, invalidated: moment() }, {}); - - let disableInvalidUserDevicesPromise = (userDevices) => { - return new Promise(function (resolve) { - let _userDevices = []; - userDevices.forEach(async function (userDevice) { - if (!userDeviceService.isValidPushToken(userDevice.token)) { - await disableUserDevicePromise(userDevice.token); - } else { - _userDevices.push(userDevice); - } - }); - - resolve(_userDevices) - }); - }; - - let disableUserDevicesWithErrorStatus = (messages, tickets) => { - return new Promise(function (resolve) { - tickets.forEach(async function (ticket, index) { - if ((ticket.status === 'error') && (ticket.details.error === 'DeviceNotRegistered')) { - await disableUserDevicePromise(messages[index].to) - } - }); - resolve(true); - }); - } - - let saveResponseStatusPromise = (messages, tickets) => notificationDetailService.saveNotificationDetails(messages, tickets); - - - let buildMessagesPromise = (userDevices) => { - return new Promise(function (resolve) { - let messages = []; - - userDevices.forEach(async function (userDevice) { - messages.push(pushHelper.createPushMessage({ - ...notification, - userId: userDevice.userId, - to: userDevice.token, - })); - }); - resolve(messages) - }); - }; - - let getUserDevicesList = []; saveNotificationPromise(notification) @@ -128,9 +130,56 @@ const extraMethods = { console.log(details); return details; }); + },*/ + + + sendNotificationToUsers: async (notification, userIds) => { + + console.log('sendNotificationToUsers -----------------------------------------------'); + console.log(notification); + + const savedNotification = (await saveNotification(notification)).toJSON(); + + const userDevicesList = await Promise.all(userIds.map((userId) => getUserDevices(userId))); + + + const allDevices = userDevicesList.flat(); + const validDevices = await disableInvalidUserDevices(allDevices); + + const pushMessages = buildPushMessages(savedNotification, validDevices); + + + const { messages, tickets } = await pushHelper.sendPushMessage(pushMessages); + saveResponseStatusPromise(messages, tickets); + disableUserDevicesWithErrorStatus(messages, tickets); + + return; }, + + sendNotificationToDevices: async (notification, devicesIds) => { + + console.log('sendNotificationToDevices -----------------------------------------------'); + console.log(notification); + + const savedNotification = (await saveNotification(notification)).toJSON(); + + const validDevices = await disableInvalidUserDevices(devicesIds); + const pushMessages = buildPushMessages(savedNotification, validDevices); + + + const { messages, tickets } = await pushHelper.sendPushMessage(pushMessages); + saveResponseStatusPromise(messages, tickets); + disableUserDevicesWithErrorStatus(messages, tickets); + + return; + }, + + + + + getNotificationsWithoutReceipt: async () => { diff --git a/modules/notification/notification.validations.js b/modules/notification/notification.validations.js index 5f051b1..1e5a6d6 100644 --- a/modules/notification/notification.validations.js +++ b/modules/notification/notification.validations.js @@ -17,6 +17,7 @@ const pushSendEvent = Joi.object().keys({ body: Joi.string().required(), recipients: Joi.object().keys({ userIds: Joi.array().optional(), + deviceIds: Joi.array().optional(), eventId: Joi.string().optional(), segment: Joi.string().optional(), }), diff --git a/modules/notification/user_device.service.js b/modules/notification/user_device.service.js index 3f5efd1..182b1be 100644 --- a/modules/notification/user_device.service.js +++ b/modules/notification/user_device.service.js @@ -28,6 +28,17 @@ const extraMethods = { where: params, }); }, + + _getActiveDeviceIds: async (offset = 0, limit = 10) => { + return models.UserDevice.findAndCountAll({ + where: { + valid: 1, + }, + raw: true, + limit: limit, + offset: offset, + }); + }, }; module.exports = generateService(models.UserDevice, extraMethods);