diff --git a/core/passport.js b/core/passport.js
index a80c5c5..8a247e8 100644
--- a/core/passport.js
+++ b/core/passport.js
@@ -116,12 +116,9 @@ passport.use('jwt', new CustomStrategy(async (req, done) => {
let user = await authService.extraMethods.findUser({ id: result.id });
if (user) {
user = user.toJSON();
- if (appVersion) {
- if (user.app_version != appVersion){
- const result = userService._updateLastLoginAndVersionUser(user.id, appVersion);
- user.app_version = appVersion;
- }
- }
+ const result = userService._updateLastLoginAndVersionUser(user.id, appVersion);
+ user.app_version = appVersion;
+
delete user.password;
console.log('Logged in Successfully');
return done(null, user, { message: 'Logged in Successfully' });
diff --git a/helpers/composes.helper.js b/helpers/composes.helper.js
index e8fcff4..9c04b46 100644
--- a/helpers/composes.helper.js
+++ b/helpers/composes.helper.js
@@ -258,9 +258,7 @@ const usersIdsComposer = (inscriptions) => {
let usersId = []
if (inscriptions) {
inscriptions.map((inscription) => {
- usersId.push({
- userId: inscription.userId,
- });
+ usersId.push(inscription.userId);
});
};
return usersId;
diff --git a/helpers/message.helper.js b/helpers/message.helper.js
index d8a718a..4fb0a84 100644
--- a/helpers/message.helper.js
+++ b/helpers/message.helper.js
@@ -1,5 +1,7 @@
'use strict';
+const tinytim = require('tinytim').tim;
+
////////////////////////////////////////////////////////////////////////////////
// CONSTANTS
////////////////////////////////////////////////////////////////////////////////
@@ -29,7 +31,16 @@ function buildMessage(text) {
return buildGenericMessage(TITLE_MESSAGE, text)
}
+function tinytom(literal, data) {
+ if (typeof literal === 'object' && literal !== null) {
+ return JSON.parse(tinytim(JSON.stringify(literal), data));
+ } else {
+ return tinytim(literal, data);
+ }
+}
+
module.exports = {
+ tinytom,
buildErrorMessage,
buildMessage,
//For testing
diff --git a/helpers/messages.json b/helpers/messages.json
index 7f7b020..e7482c1 100644
--- a/helpers/messages.json
+++ b/helpers/messages.json
@@ -21,5 +21,21 @@
"html": "¡Hola!,
Solicitaste cambiar la contraseña, hemos creado una nueva.
Entra en la app con tu dirección de email y esta clave: {{password}}
Fundación Lo Que De Verdad Importa."
}
}
+ },
+ "push": {
+ "confirmInvitation": {
+ "title": "{{ congress }}",
+ "body": "Tu inscripción al congreso de {{ congress }} está confirmada.",
+ "data": {
+ "type": "message",
+ "title": "{{ congress }}",
+ "message": "Tu inscripción al congreso de {{ congress }} está confirmada.\nPor favor, si no puedes asistir al congreso, elimina tu inscripción para que otra persona pueda asistir.\n",
+ "button": {
+ "caption": "Ver mi entrada",
+ "screen": "Ticket",
+ "paramId": "{{ ticketId }}"
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/helpers/notification.helpers.js b/helpers/notification.helpers.js
new file mode 100644
index 0000000..c61abc7
--- /dev/null
+++ b/helpers/notification.helpers.js
@@ -0,0 +1,44 @@
+const moment = require('moment');
+const { tinytom } = require('./message.helper');
+const messages = require('./messages.json');
+
+
+const createNotification = (data) => {
+ return {
+ date: data.date,
+ title: data.title,
+ body: data.body,
+ ttl: data.ttl,
+ priority: data.priority ? data.priority : 'high',
+ recipients: data.recipients,
+ data: data.data,
+ userId: data.userId,
+ }
+}
+
+createNotificationValidatedInscription = (inscription) => {
+ let jsonMessage = messages.push.confirmInvitation;
+
+ const jsonNotification = tinytom(jsonMessage, {
+ congress: inscription.event.name,
+ ticketId: inscription.id,
+ });
+
+ return {
+ ...jsonNotification,
+ date: moment(),
+ priority: "high",
+ recipients: {
+ "userIds": [
+ inscription.user.id,
+ ]
+ },
+ }
+}
+
+
+module.exports = {
+ createNotificationValidatedInscription,
+ createNotification,
+ //createMessageNotification
+}
diff --git a/helpers/push.helper.js b/helpers/push.helper.js
new file mode 100644
index 0000000..f992ac1
--- /dev/null
+++ b/helpers/push.helper.js
@@ -0,0 +1,133 @@
+const { Expo } = require('expo-server-sdk');
+
+// Create a new Expo SDK client
+const expo = new Expo();
+
+const createPushMessage = (data) => {
+ const pushMessage = {
+ title: data.title,
+ body: data.body,
+ ttl: data.ttl,
+ priority: data.priority,
+ userId: data.userId,
+ to: data.to,
+ notificationId: data.id,
+ data: data.data,
+ _displayInForeground: true,
+ sound: 'default',
+ channelId: 'lqdvi-messages'
+ };
+ console.log(pushMessage);
+ return pushMessage;
+};
+
+
+
+
+
+const sendPushMessage = async (messages) => {
+
+ // The Expo push notification service accepts batches of notifications so
+ // that you don't need to send 1000 requests to send 1000 notifications. We
+ // recommend you batch your notifications to reduce the number of requests
+ // and to compress them (notifications with similar content will get
+ // compressed).
+
+ /**
+ * There is a limit on the number of push notifications (100) you can send at once.Use
+ * `chunkPushNotifications` to divide an array of push notification messages into appropriately
+ * sized chunks
+ */
+
+ // Later, after the Expo push notification service has delivered the
+ // notifications to Apple or Google (usually quickly, but allow the the service
+ // up to 30 minutes when under load), a "receipt" for each notification is
+ // created. The receipts will be available for at least a day; stale receipts
+ // are deleted.
+ //
+ // The ID of each receipt is sent back in the response "ticket" for each
+ // notification. In summary, sending a notification produces a ticket, which
+ // contains a receipt ID you later use to get the receipt.
+ //
+ // The receipts may contain error codes to which you must respond. In
+ // particular, Apple or Google may block apps that continue to send
+ // notifications to devices that have blocked notifications or have uninstalled
+ // your app. Expo does not control this policy and sends back the feedback from
+ // Apple and Google so you can handle it appropriately.
+
+ let chunks = expo.chunkPushNotifications(messages);
+ return {
+ messages,
+ tickets: await _sendPushNotificationsAsync(chunks)
+ };
+};
+
+module.exports = {
+ sendPushMessage,
+ createPushMessage,
+}
+
+
+const _sendPushNotificationsAsync = async function (chunks) {
+ return new Promise(async function (resolve) {
+ let tickets = [];
+ // Send the chunks to the Expo push notification service. There are
+ // different strategies you could use. A simple one is to send one chunk at a
+ // time, which nicely spreads the load out over time:
+ for (let chunk of chunks) {
+ try {
+ let ticketChunk = await expo.sendPushNotificationsAsync(chunk);
+ tickets.push(...ticketChunk);
+
+ // NOTE: If a ticket contains an error code in ticket.details.error, you
+ // must handle it appropriately. The error codes are listed in the Expo
+ // documentation:
+ // https://docs.expo.io/versions/latest/guides/push-notifications#response-format
+
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ resolve(tickets);
+ });
+};
+
+const _getPushNotificationsResultAsync = async function (receiptIdChunks) {
+ return new Promise(async function (resolve) {
+ // Like sending notifications, there are different strategies you could use
+ // to retrieve batches of receipts from the Expo service.
+
+ let result = [];
+
+ console.log(receiptIdChunks);
+
+ for (let chunk of receiptIdChunks) {
+ try {
+ let receipts = await expo.getPushNotificationReceiptsAsync(chunk);
+ console.log('hola', receipts);
+
+ // The receipts specify whether Apple or Google successfully received the
+ // notification and information about an error, if one occurred.
+ for (let key in receipts) {
+ if (receipts[key].status === 'ok') {
+ result.push[receipts[key]];
+ continue;
+ } else if (receipts[key].status === 'error') {
+ console.error(`There was an error sending a notification: ${receipts[key].message}`);
+ if (receipts[key].details && receipts[key].details.error) {
+ // The error codes are listed in the Expo documentation:
+ // https://docs.expo.io/versions/latest/guides/push-notifications#response-format
+ // You must handle the errors appropriately.
+ console.error(`The error code is ${receipts[key].details.error}`);
+ }
+ }
+ }
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ resolve(result);
+ });
+}
diff --git a/modules/auth/user.model.js b/modules/auth/user.model.js
index 1da9242..0af072e 100644
--- a/modules/auth/user.model.js
+++ b/modules/auth/user.model.js
@@ -77,7 +77,7 @@ module.exports = function (sequelize, DataTypes) {
User.Comments = User.hasMany(models.Comment, { foreignKey: 'userId' });
User.EventsReservations = User.hasMany(models.EventReservation, { foreignKey: 'userId' });
User.EventsInscriptions = User.hasMany(models.EventInscription, { foreignKey: 'userId' });
- User.Questions = User.hasMany(models.EventQuestion, { foreignKey: 'userId', as: "questions", required: false, });
+ User.Questions = User.hasMany(models.EventQuestion, { foreignKey: 'userId', as: "questions", required: false });
// User.InscriptionsValidate = User.hasMany(models.EventIncription, { foreignkey: 'validateUserId'})
//User.Reactions = User.hasMany(models.UserReaction, { foreignKey: 'UserId' });
diff --git a/modules/auth/user.service.js b/modules/auth/user.service.js
index ea39c22..59823a5 100644
--- a/modules/auth/user.service.js
+++ b/modules/auth/user.service.js
@@ -26,14 +26,13 @@ const extraMethods = {
})
},
- _updateLastLoginAndVersionUser: async (Id, appVersion) => {
- return models.User.update (
- { app_version : appVersion,
- lastlogin: moment().utc() },
- {
- where: { id: Id}
- },
- );
+ _updateLastLoginAndVersionUser: async (id, appVersion) => {
+ return models.User.update ({
+ app_version : appVersion,
+ lastlogin: moment().utc()
+ }, {
+ where: { id: id }
+ });
},
_getOrCreateUser: async (dataUser) => {
diff --git a/modules/events/event.controller.js b/modules/events/event.controller.js
index cc11e0e..ebf5cf1 100644
--- a/modules/events/event.controller.js
+++ b/modules/events/event.controller.js
@@ -4,11 +4,14 @@ const httpStatus = require('http-status');
const generateControllers = require('../../core/controllers');
const QRHelper = require('../../helpers/qr.helper');
const emailHelper = require('../../helpers/mail.helper');
+const notificationHelper = require('../../helpers/notification.helpers');
const path = require("path");
const messages = require('../../helpers/messages.json');
const eventService = require('./event.service');
const eventReservationService = require('./events_reservations.service');
const eventInscriptionService = require('./events_inscriptions.service');
+const notificationService = require('../notification/notification.service');
+
const { extractParamsFromRequest, handleErrorResponse, handleResultResponse } = require('../../helpers/controller.helper');
//PRUEBA
@@ -26,25 +29,25 @@ function generateMemberInscription (user, inscription, reservation) {
let memberInscription = null;
if (user && inscription) {
memberInscription = {
- marketing_memberId: null,
- email: user.email,
- name: user.name,
- surname: user.surname,
- source: inscription.source,
- event_name: (inscription.event) ? inscription.event.name : 'N/A',
- event_date: (inscription.event) ? inscription.event.init_date : 'N/A',
- reservation_code: (reservation) ? reservation.reservation_code : null,
- date_inscription: inscription.date,
- code_ticket: inscription.code_ticket,
- validated: inscription.validated,
- color: (reservation) ? reservation.color : null,
- description: ((reservation) ? reservation.description : 'Entrada').toUpperCase(),
- entity: (reservation) ? reservation.Entity.name : user.entityId,
- userId: user.id,
- qrConfig: null,
- qrCode: null,
+ marketing_memberId: null,
+ email: user.email,
+ name: user.name,
+ surname: user.surname,
+ source: inscription.source,
+ event_name: (inscription.event) ? inscription.event.name : 'N/A',
+ event_date: (inscription.event) ? inscription.event.init_date : 'N/A',
+ reservation_code: (reservation) ? reservation.reservation_code : null,
+ date_inscription: inscription.date,
+ code_ticket: inscription.code_ticket,
+ validated: inscription.validated,
+ color: (reservation) ? reservation.color : null,
+ description: ((reservation) ? reservation.description : 'Entrada').toUpperCase(),
+ entity: (reservation) ? reservation.Entity.name : user.entityId,
+ userId: user.id,
+ qrConfig: null,
+ qrCode: null,
- }
+ }
}
return memberInscription;
@@ -264,19 +267,32 @@ console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ', member);
- //Mandar correo de confirmacion de desinscripcion
+
+ //Mandar correo de confirmacion de inscripcion
try {
+
+ var member = generateMemberInscription(inscription.user, inscription, inscription.reservation);
+ member.marketing_memberId = await eventInscriptionService._addMember(marketingListIdEvent, member);
+ eventInscriptionService._updateMarketingMemberOfInscription(inscription.id, member.marketing_memberId);
+ member.qrConfig = generateQRConfig(member);
+ member.qrCode = await QRHelper.getInscriptionQRCode(member.qrConfig);
+ console.log('mandar correo>>>>>>>>>>>>>>>>>>>>> ', member);
emailHelper.sendTicket(generateHeaderMail(member), generateBodyMail(member))
} catch (error) {
console.log('No se ha podido mandar email con entrada');
};
+
+
+ try {
+ let notification = notificationHelper.createNotificationValidatedInscription(inscription);
+ console.log(notification);
+ let result = notificationService.sendNotification(notification, [inscription.user.id]);
+
+ console.log(result);
+ } catch (error) {
+ console.log('No se ha podido mandar push');
+ };
};
return handleResultResponse("Inscripción validada", null, params, res, httpStatus.OK);
@@ -285,6 +301,7 @@ console.log('mandar correo>>>>>>>>>>>>>>>>>>>>> ', member);
return handleResultResponse("No se pudo validar inscripción", null, params, res, httpStatus.NOT_FOUND);
} catch (error) {
+ console.log(error);
return handleResultResponse("Error al validar inscripción", null, params, res, httpStatus.NOT_FOUND);
}
diff --git a/modules/notification/notification.controller.js b/modules/notification/notification.controller.js
index cf84b86..23cbc93 100644
--- a/modules/notification/notification.controller.js
+++ b/modules/notification/notification.controller.js
@@ -8,6 +8,8 @@ const notificationDetailService = require('./notification_detail.service');
const userDeviceService = require('./user_device.service');
const eventInscriptionService = require('../events/events_inscriptions.service');
const { usersIdsComposer } = require('../../helpers/composes.helper');
+const pushHelper = require('../../helpers/push.helper');
+const notificationHelper = require('../../helpers/notification.helpers')
const { extractParamsFromRequest, handleErrorResponse, handleResultResponse } = require('../../helpers/controller.helper');
@@ -17,77 +19,32 @@ const controllerOptions = { MODULE_NAME };
const extraControllers = {
- sendNotificationEvent: async (req, res, next) => {
-
- /**
- * notificationSample = {
- * "tittle": "título de la notificación",
- * "message": "cuerpo de la notificación",
- * "recipients": {
- * "eventId": "xxx-xxx-xxx-xxx",
- * "segment": "ALL" | "ALL_VALIDATED" | "ALL_NOT_VALIDATED" |
- * "PARTNERS_ALL" | "PARTNERS_VALIDATED" | "PARTNERS_NOT_VALIDATED" |
- * "COLLEGE_ALL" | "COLLEGE_VALIDATED" | "COLLEGE_NOT_VALIDATED"
- * },
- * "data": {
- * "type": "message",
- * "title": "Título del mensaje",
- * "message": "Cuerpo del mensaje",
- * "button": {
- * "caption": "Etiqueta del boton",
- * "url": "https://www.site.es",
- * "screen": "",
- * "paramId": "23",
- * }
- * }
- *}
- */
- let usersIds = null;
- const params = req.body;
- const eventId = params.recipients.eventId;
- const segment = params.recipients.segment;
- console.log('prueba de llamada>>>>> ', params);
-
- try {
- notificationService.createNotification(params.date, params.title, params.message, undefined, 'default', params.data, req.user.id);
- switch (segment) {
- //Todos los inscritos al evento tanto en validos como en lista de espera
- case 'ALL':
- usersIds = await eventInscriptionService._getInscriptionByEvent(eventId);
- break;
- //Todos los inscritos tanto invitados como libres
- case 'ALL_VALIDATED':
- usersIds = 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':
- usersIds = 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':
- usersIds = await eventInscriptionService._getInscriptionByEventFromPartner(eventId);
-
- break;
-
- //Todos los inscritos al evento, tanto validados como en lista de espera
- default: //ALL
- break;
- }
-
- usersIds = usersIdsComposer(usersIds);
- console.log('usuarios inscritos>>>>> ', usersIds);
-
- } catch(error) {
- return handleErrorResponse(MODULE_NAME, 'sendNotificationEvent', error, res);
- }
-
-
-
- return handleResultResponse("sendNotificationEvent", null, params, res, httpStatus.OK);
- },
-
sendNotification: (config) => {
+
+ /**
+ * notificationSample = {
+ * "tittle": "título de la notificación",
+ * "message": "cuerpo de la notificación",
+ * "recipients": {
+ * "eventId": "xxx-xxx-xxx-xxx",
+ * "segment": "ALL" | "ALL_VALIDATED" | "ALL_NOT_VALIDATED" |
+ * "PARTNERS_ALL" | "PARTNERS_VALIDATED" | "PARTNERS_NOT_VALIDATED" |
+ * "COLLEGE_ALL" | "COLLEGE_VALIDATED" | "COLLEGE_NOT_VALIDATED"
+ * },
+ * "data": {
+ * "type": "message",
+ * "title": "Título del mensaje",
+ * "message": "Cuerpo del mensaje",
+ * "button": {
+ * "caption": "Etiqueta del boton",
+ * "url": "https://www.site.es",
+ * "screen": "",
+ * "paramId": "23",
+ * }
+ * }
+ *}
+ */
+
return async (req, res, next) => {
config = config || {
scopes: [],
@@ -97,115 +54,70 @@ const extraControllers = {
const context = buildContext(req, config);
let params = extractParamsFromRequest(req, res);
- let userIds = req.body.userIds;
- if (!userIds) {
- return handleErrorResponse(controllerOptions.MODULE_NAME, 'sendNotification', new Error('Missing user Ids'), res)
- }
+ let userIds = undefined;
+ let eventId = undefined;
+ let segment = undefined;
+ const { body } = req;
- if (!req.body.title) {
+ if (!body.title) {
return handleErrorResponse(controllerOptions.MODULE_NAME, 'sendNotification', new Error('Missing message title'), res)
}
- if (!req.body.message) {
- return handleErrorResponse(controllerOptions.MODULE_NAME, 'sendNotification', new Error('Missing message content'), res)
+ if (!body.body) {
+ return handleErrorResponse(controllerOptions.MODULE_NAME, 'sendNotification', new Error('Missing body content'), res)
}
- try {
- let getUserDevicesPromise = (userId) => userDeviceService.fetchAll({ params: { userId: userId }}, context).then(function(result) {
- return new Promise(function(resolve) { resolve(result.rows) });
- });
- let saveNotificationPromise = (notification) => notificationService.create(notification, context);
- let sendNotificationsPromise = (messages) => notificationService.sendNotification(messages);
- let disableUserDevicePromise = (token) => userDeviceService.update({ params: {
- token: token,
- }}, { valid: 0, invalidated: moment() }, context);
+ // Evento?
+ if (body.recipients.eventId) {
+ eventId = body.recipients.eventId;
+ segment = body.recipients.segment;
+ } else if (body.recipients.userIds) {
+ userIds = body.recipients.userIds;
+ } else {
+ return handleErrorResponse(controllerOptions.MODULE_NAME, 'sendNotification', new Error('Missing user Ids or event Ids'), res)
+ }
- 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);
-
- const notificationRecord = {
- date: moment(),
- title: req.body.title,
- body: req.body.message,
- ttl: req.body.ttl || undefined,
- priority: req.body.priority || 'default',
- data: req.body.data || { userIds: req.body.userIds },
- userId: context.user.id,
- };
-
- let buildMessagesPromise = (userDevices) => {
- return new Promise(function (resolve) {
- let messages = [];
-
- userDevices.forEach(async function (userDevice) {
- messages.push({
- title: notificationRecord.title,
- body: notificationRecord.body,
- ttl: notificationRecord.ttl,
- priority: notificationRecord.priority,
- userId: userDevice.userId,
- to: userDevice.token,
- notificationId: notificationRecord.id,
- data: notificationRecord.data,
- _displayInForeground: true,
- sound: 'default',
- });
- });
-
- resolve(messages)
- });
- };
-
- let getUserDevicesList = [];
-
- userIds.forEach(function (userId) {
- getUserDevicesList.push(getUserDevicesPromise(userId));
+ try {
+ let notification = notificationHelper.createNotification ({
+ ...body,
+ userId: context.user.id
});
- saveNotificationPromise(notificationRecord)
- .then(function(notification) {
- notificationRecord.id = notification.id;
- return Promise.all(getUserDevicesList)
- }).then(function (userDeviceList) {
- return new Promise(function(resolve) { resolve(userDeviceList[0]); });
- })
- .then(disableInvalidUserDevicesPromise)
- .then(buildMessagesPromise)
- .then(sendNotificationsPromise)
- .then(function({ messages, tickets }) {
- let _saveStatus = saveResponseStatusPromise(messages, tickets);
- let _disableDevices = disableUserDevicesWithErrorStatus(messages, tickets);
+ let getUserIds = async () => {
+ if (userIds) {
+ return new Promise(function(resolve) { resolve(userIds) } );
+ } else if (eventId && segment) {
+ 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 Promise.all([_saveStatus, _disableDevices]);
- })
- .then(function (details) {
- console.log(details);
- });
} catch (error) {
console.error(error);
return handleErrorResponse(controllerOptions.MODULE_NAME, 'sendNotification', error, res)
diff --git a/modules/notification/notification.model.js b/modules/notification/notification.model.js
index 469ec2f..76fdbd5 100644
--- a/modules/notification/notification.model.js
+++ b/modules/notification/notification.model.js
@@ -26,6 +26,10 @@ module.exports = function (sequelize, DataTypes) {
allowNull: false,
default: 'default',
},
+ recipients: {
+ type: DataTypes.JSON,
+ allowNull: true,
+ },
data: {
type: DataTypes.JSON,
allowNull: true,
diff --git a/modules/notification/notification.routes.js b/modules/notification/notification.routes.js
index 41eb88a..c22f692 100644
--- a/modules/notification/notification.routes.js
+++ b/modules/notification/notification.routes.js
@@ -6,7 +6,7 @@ const PaginateMiddleware = require('../../middlewares/paginate');
const FieldMiddleware = require('../../middlewares/fields');
const SortMiddleware = require('../../middlewares/sort');
const notificationController = require('./notification.controller');
-const { pushSendEvent, deviceTokenInputType, notificationSendType } = require('./notification.validations');
+const { pushSendEvent, deviceTokenInputType } = require('./notification.validations');
const generalInvalidFields = [
'createdAt', 'updatedAt',
@@ -36,17 +36,17 @@ routes.get('/admin/notifications/:id',
routes.post('/admin/notifications',
isAdministratorUser,
- SchemaValidator(notificationSendType, true),
+ SchemaValidator(pushSendEvent, true),
notificationController.sendNotification({
scopes: ['defaultScope']
})
);
-routes.post('/admin/notifications/event',
+/*routes.post('/admin/notifications/event',
isAdministratorUser,
SchemaValidator(pushSendEvent, true),
notificationController.sendNotificationEvent
-);
+);*/
/* Borrar cuando ya no aparezca la versión 1.0.10 */
routes.post('/notifications/register',
diff --git a/modules/notification/notification.service.js b/modules/notification/notification.service.js
index ef93031..bdd6106 100644
--- a/modules/notification/notification.service.js
+++ b/modules/notification/notification.service.js
@@ -1,74 +1,137 @@
-const { Expo } = require('expo-server-sdk');
-const { generateService, parseParamsToFindOptions } = require('../../helpers/service.helper');
-const models = require('../../core/models');
+const moment = require('moment');
-// Create a new Expo SDK client
-const expo = new Expo();
+const { generateService, parseParamsToFindOptions } = require('../../helpers/service.helper');
+const userDeviceService = require('./user_device.service');
+const notificationDetailService = require('./notification_detail.service');
+const pushHelper = require('../../helpers/push.helper');
+const models = require('../../core/models');
const extraMethods = {
- createNotification: (date, title, body, ttl, priority, data, userId) => {
- console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>< {
+ return {
+ date: data.date,
+ title: data.title,
+ body: data.body,
+ ttl: data.ttl,
+ priority: data.priority,
+ recipients: data.recipients,
+ data: data.data,
+ userId: data.userId,
+ }
+ },
- return new Promise(function (resolve, reject) {
- models.Notification.create({
- date: date,
- title: title,
- body: body,
- userId: userId,
- ttl: ttl,
- priority: priority,
- data: data,
- })
- .then(function (result) {
- resolve(result);
- })
- .catch(function (error) {
- reject(error)
- });
+ saveNotification: ({ date, title, body, ttl, priority, recipients, data, userId }) => {
+ return models.Notification.create({
+ date: moment(date),
+ title: title,
+ body: body,
+ userId: userId,
+ ttl: ttl ? ttl : undefined,
+ priority: priority ? priority : 'default',
+ recipients: recipients,
+ data: data,
});
},
+
-
-
- sendNotification: async (messages) => {
+ sendNotification: (notification, userIds) => {
- // The Expo push notification service accepts batches of notifications so
- // that you don't need to send 1000 requests to send 1000 notifications. We
- // recommend you batch your notifications to reduce the number of requests
- // and to compress them (notifications with similar content will get
- // compressed).
+ console.log('sendNofitication -----------------------------------------------');
+ console.log(notification, userIds);
- /**
- * There is a limit on the number of push notifications (100) you can send at once.Use
- * `chunkPushNotifications` to divide an array of push notification messages into appropriately
- * sized chunks
- */
+ 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() }, {});
- // Later, after the Expo push notification service has delivered the
- // notifications to Apple or Google (usually quickly, but allow the the service
- // up to 30 minutes when under load), a "receipt" for each notification is
- // created. The receipts will be available for at least a day; stale receipts
- // are deleted.
- //
- // The ID of each receipt is sent back in the response "ticket" for each
- // notification. In summary, sending a notification produces a ticket, which
- // contains a receipt ID you later use to get the receipt.
- //
- // The receipts may contain error codes to which you must respond. In
- // particular, Apple or Google may block apps that continue to send
- // notifications to devices that have blocked notifications or have uninstalled
- // your app. Expo does not control this policy and sends back the feedback from
- // Apple and Google so you can handle it appropriately.
+ 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);
+ }
+ });
- let chunks = expo.chunkPushNotifications(messages);
- return {
- messages,
- tickets: await _sendPushNotificationsAsync(chunks)
+ 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)
+ .then(function (notificationRecord) {
+ notification = notificationRecord.toJSON();
+ userIds.forEach(function (userId) {
+ getUserDevicesList.push(getUserDevicesPromise(userId));
+ });
+
+ return Promise.all(getUserDevicesList)
+ }).then(function (userDeviceList) {
+ let result = [];
+ userDeviceList.forEach(function (elements) {
+ elements.forEach(function (item) {
+ result.push(item)
+ })
+ });
+ return new Promise(function (resolve) { resolve(result); });
+ })
+ .then(disableInvalidUserDevicesPromise)
+ .then(buildMessagesPromise)
+ .then(sendNotificationsPromise)
+ .then(function ({ messages, tickets }) {
+ let _saveStatus = saveResponseStatusPromise(messages, tickets);
+ let _disableDevices = disableUserDevicesWithErrorStatus(messages, tickets);
+
+ return Promise.all([_saveStatus, _disableDevices]);
+ })
+ .then(function (details) {
+ console.log(details);
+ return details;
+ });
},
+
+
getNotificationsWithoutReceipt: async() => {
},
@@ -84,67 +147,4 @@ const extraMethods = {
module.exports = generateService(models.Notification, extraMethods);
-const _sendPushNotificationsAsync = async function (chunks) {
- return new Promise(async function (resolve) {
- let tickets = [];
- // Send the chunks to the Expo push notification service. There are
- // different strategies you could use. A simple one is to send one chunk at a
- // time, which nicely spreads the load out over time:
- for (let chunk of chunks) {
- try {
- let ticketChunk = await expo.sendPushNotificationsAsync(chunk);
- tickets.push(...ticketChunk);
-
- // NOTE: If a ticket contains an error code in ticket.details.error, you
- // must handle it appropriately. The error codes are listed in the Expo
- // documentation:
- // https://docs.expo.io/versions/latest/guides/push-notifications#response-format
-
- } catch (error) {
- console.error(error);
- }
- }
-
- resolve(tickets);
- });
-};
-
-const _getPushNotificationsResultAsync = async function (receiptIdChunks) {
- return new Promise(async function (resolve) {
- // Like sending notifications, there are different strategies you could use
- // to retrieve batches of receipts from the Expo service.
-
- let result = [];
-
- console.log(receiptIdChunks);
-
- for (let chunk of receiptIdChunks) {
- try {
- let receipts = await expo.getPushNotificationReceiptsAsync(chunk);
- console.log('hola', receipts);
-
- // The receipts specify whether Apple or Google successfully received the
- // notification and information about an error, if one occurred.
- for (let key in receipts) {
- if (receipts[key].status === 'ok') {
- result.push[receipts[key]];
- continue;
- } else if (receipts[key].status === 'error') {
- console.error(`There was an error sending a notification: ${receipts[key].message}`);
- if (receipts[key].details && receipts[key].details.error) {
- // The error codes are listed in the Expo documentation:
- // https://docs.expo.io/versions/latest/guides/push-notifications#response-format
- // You must handle the errors appropriately.
- console.error(`The error code is ${receipts[key].details.error}`);
- }
- }
- }
- } catch (error) {
- console.error(error);
- }
- }
-
- resolve(result);
- });
-}
diff --git a/modules/notification/notification.validations.js b/modules/notification/notification.validations.js
index 43a38a8..5f051b1 100644
--- a/modules/notification/notification.validations.js
+++ b/modules/notification/notification.validations.js
@@ -4,21 +4,24 @@ const deviceTokenInputType = Joi.object().keys({
token: Joi.string().required(),
});
-const pushSendType = Joi.object().keys({
+/*const pushSendType = Joi.object().keys({
userIds: Joi.array().required(),
title: Joi.string().required(),
message: Joi.string().required(),
//token: Joi.string().required(),
-});
+});*/
const pushSendEvent = Joi.object().keys({
- date: Joi.date().required(),
+ date: Joi.date().optional(),
title: Joi.string().required(),
- message: Joi.string().required(),
+ body: Joi.string().required(),
recipients: Joi.object().keys({
- eventId: Joi.string().required(),
- segment: Joi.string().required(),
+ userIds: Joi.array().optional(),
+ eventId: Joi.string().optional(),
+ segment: Joi.string().optional(),
}),
+ priority: Joi.string().optional(),
+ ttl: Joi.string().optional(),
data: Joi.object().keys({
type: Joi.string().required(),
title: Joi.string().required(),
@@ -33,5 +36,5 @@ const pushSendEvent = Joi.object().keys({
});
module.exports = {
- deviceTokenInputType, pushSendType, pushSendEvent
+ deviceTokenInputType, pushSendEvent
};
diff --git a/package.json b/package.json
index 07a4980..20b61fe 100644
--- a/package.json
+++ b/package.json
@@ -66,6 +66,7 @@
"sanitize-filename": "^1.6.2",
"sequelize": "^5.16.0",
"sharp": "^0.23.0",
+ "tinytim": "^0.1.1",
"unique-file-name": "^1.0.0",
"vimeo": "^2.1.1",
"vm": "^0.1.0",