ovictoraurelio

notifications

Mar 24th, 2022
1,126
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import axios from 'axios'
  2. import firebase from 'firebase-admin'
  3. import usersServices from '../users/services'
  4.  
  5. import logger from '@/logger'
  6. import {
  7.   chunk,
  8.   uniqBy
  9. } from 'lodash'
  10. import Bluebird from 'bluebird'
  11.  
  12. const FIRESTORE_WRITES_LIMIT_PER_REQUEST = 499
  13. export default new class {
  14.   /**
  15.    *
  16.    * @typedef {Object} Notification
  17.    * @property {String} notification.title Notification title
  18.    * @property {String} notification.body Notification body
  19.    * @property {Number} notification.post_id Post id
  20.    * @property {Date} notification.publication_time Notification publication time
  21.    */
  22.  
  23.   /**
  24.    * @description creates a notification and send to users in userIds array
  25.    * @param {import('express').Request} req
  26.    * @param {Notification} notification
  27.    * @param {Array<Integer>} usersIds
  28.    */
  29.   async createNotification (db, orgSlug, firestore, notification, usersIds) {
  30.     try {
  31.       if (!usersIds || usersIds.length === 0) {
  32.         return null
  33.       }
  34.  
  35.       const devices = await usersServices.getDevices(db, usersIds)
  36.  
  37.       await this.queueNotification(orgSlug, firestore, devices, notification)
  38.     } catch (err) {
  39.       console.error(err)
  40.     }
  41.   }
  42.  
  43.   async _addToQueue (devices, notifyData, firestore, organizationSlug) {
  44.     if (devices && devices.length) {
  45.       logger.debug(`Recipients users to notification ${devices.length}`)
  46.       await Bluebird.map(chunk(devices, FIRESTORE_WRITES_LIMIT_PER_REQUEST), async chunkDevices => {
  47.         const batch = firestore.batch()
  48.         chunkDevices.forEach(device => {
  49.           // Onde notificações push são enviadas
  50.           const data = {
  51.             ...notifyData,
  52.             token: device.push_token,
  53.             userUUID: device.uuid || null,
  54.             userUid: `${organizationSlug}-${device.user_id}`,
  55.             status: 'queued'
  56.           }
  57.  
  58.           const doc = firestore.collection('notification_queue').doc()
  59.           batch.create(doc, data)
  60.         })
  61.         await batch.commit()
  62.       }, {
  63.         concurrency: 2
  64.       })
  65.     }
  66.   }
  67.   async _addToUsers (recipients, devices, notifyData, firestore, organizationSlug) {
  68.     if (recipients === null) {
  69.       recipients = devices
  70.     }
  71.     logger.debug(`Recipients users to notification ${recipients.length}`)
  72.     await Bluebird.map(chunk(recipients, FIRESTORE_WRITES_LIMIT_PER_REQUEST), async chunkRecipients => {
  73.       const batch = firestore.batch()
  74.       // Onde notificações são listadas no frontend
  75.       uniqBy(chunkRecipients, 'id').forEach(user => {
  76.         const data = {
  77.           ...notifyData,
  78.           userUUID: user.uuid || null,
  79.           userUid: `${organizationSlug}-${user.id}`,
  80.           status: 'unread'
  81.         }
  82.  
  83.         const doc = firestore.collection('notification_users').doc()
  84.         batch.create(doc, data)
  85.       })
  86.       await batch.commit()
  87.     }, {
  88.       concurrency: 2
  89.     })
  90.   }
  91.  
  92.   /**
  93.    *
  94.    * @param {import('@/middlewares/organization').default} db
  95.    * @param {*} usersIds
  96.    * @param {{
  97.    * channelType: String,
  98.    * eventType: String
  99.    * }} event {{channelType: 'web', eventType: 'post_created'}}
  100.    */
  101.   async getUsersToNotify (db, usersIds, { channelType, eventType }) {
  102.     const allConfigs = await db.UsersNotificationsSettings.findAll({
  103.       where: {
  104.         userId: usersIds
  105.       }
  106.     })
  107.     const recipientsFilteredByConfigs = usersIds.filter(userId => {
  108.       const foundConfigs = allConfigs.find(config => config.userId === userId)
  109.       if (!foundConfigs) {
  110.         return true
  111.       }
  112.       if (foundConfigs.notifyOnNewPost) {
  113.         // .. lógica pra checar se baseado nas configs deve receber notificação
  114.       }
  115.     })
  116.     return recipientsFilteredByConfigs
  117.   }
  118.  
  119.   /**
  120.    * @description Put a notification on queue
  121.    * @param {string} organizationSlug
  122.    * @param {import('firebase-admin').firestore.Firestore} firestore
  123.    * @param {Array} tokens Users devices tokens
  124.    * @param {Notification} notification
  125.    * @param {{
  126.    * channelType: String,
  127.    * eventType: String
  128.    * }} event {{channelType: 'web', eventType: 'post_created'}}
  129.    */
  130.   async queueNotification (organizationSlug, firestore, devices, notification, allUsers = null, { channelType = ['email', 'push_notification'], eventType = null } = {}) {
  131.     const isDevelopmentEnviroment = process.env.NODE_ENV !== 'production' || process.env.DATABASE_PORT
  132.     if (isDevelopmentEnviroment) {
  133.       return null
  134.     }
  135.  
  136.     const { recipientsByMail, devicesByPushNotification } = this.getUsersToNotify(db, allUsers, devices, { })
  137.  
  138.     logger.profile('notifications/services', 'queueNotification')
  139.  
  140.     // serverTimestamp count as write in cost also in request rating limit (500 writes per request), so whe are in server side is secure to use new Date
  141.     // const now = firebase.firestore.FieldValue.serverTimestamp()
  142.     const now = new Date()
  143.     let publicationTime = now
  144.  
  145.     if (notification.publication_time && notification.publication_time instanceof Date) {
  146.       logger.debug('notifications/services queueNotification Publication time:', notification.publication_time)
  147.       publicationTime = notification.publication_time
  148.     } else {
  149.       logger.error('notifications/services', `queueNotification', 'Invalid publication time got: ${notification.publication_time}, setting to now`)
  150.     }
  151.  
  152.     const notifyData = {
  153.       created_at: now,
  154.       updated_at: now,
  155.       deleted_at: null,
  156.       publication_time: publicationTime,
  157.       env: process.env.NODE_ENV,
  158.       organization_slug: organizationSlug,
  159.       title: notification.title || null,
  160.       body: notification.body || null,
  161.       post_id: notification.post_id || null,
  162.       meta: notification.meta || null,
  163.       task_name: null
  164.     }
  165.  
  166.     await Promise.all([
  167.       // Enviar notificações de email (Enviada por email de usuário)
  168.       this._addToQueueMail(recipientsByMail, notifyData, firestore, organizationSlug),
  169.       // Envia notificações push notifications (Enviada por dispositivos)
  170.       this._addToQueue(devicesByPushNotification, notifyData, firestore, organizationSlug),
  171.       // Notificação do sistema Quoti (Aquelas que aparecem no sininho de notificações)
  172.       this._addToUsers(allUsers, devices, notifyData, firestore, organizationSlug)
  173.     ])
  174.  
  175.     logger.profile('notifications/services', 'queueNotification')
  176.   }
  177.  
  178.   /**
  179.    *
  180.    * @param {string} orgSlug
  181.    * @param {firebase.firestore} firestore
  182.    * @param {string} postId
  183.    */
  184.   async deleteNotifications (orgSlug, firestore, postId) {
  185.     // Get notifications on firestore
  186.  
  187.     const notificationQueueQuery = firestore.collection('notification_queue')
  188.       .where('env', '==', process.env.NODE_ENV)
  189.       .where('organization_slug', '==', orgSlug)
  190.       .where('post_id', '==', postId)
  191.       .where('status', '==', 'scheduled')
  192.       .where('deleted_at', '==', null)
  193.  
  194.     logger.debug('posts', 'delete', 'Getting notification queue on firestore')
  195.  
  196.     const notificationQueue = await notificationQueueQuery.get()
  197.  
  198.     logger.debug('posts', 'delete', 'Getting tasks names')
  199.  
  200.     // Get notifications tasks names
  201.     const tasks = []
  202.  
  203.     notificationQueue.forEach(snap => {
  204.       const data = snap.data()
  205.  
  206.       tasks.push({
  207.         name: data.task_name
  208.       })
  209.     })
  210.  
  211.     // logger.debug('posts', 'delete', 'Tasks names got', tasks.map(t => t.name))
  212.  
  213.     logger.debug('posts', 'delete', 'Deleting notifications tasks')
  214.  
  215.     // Delete notifications tasks
  216.     try {
  217.       const deleteTaskRes = await axios.post('https://us-central1-beyond-quoti.cloudfunctions.net/deleteTask', {
  218.         tasks
  219.       })
  220.  
  221.       logger.debug('posts', 'delete', 'Delete tasks function response got', {
  222.         status: deleteTaskRes.status,
  223.         statusText: deleteTaskRes.statusText
  224.       })
  225.     } catch (err) {
  226.       logger.error('posts', 'deleteTasks', 'Error at Deleting notifications tasks')
  227.     }
  228.  
  229.     // Delete (set deleted_at) notifications on firestore and get tokens
  230.     const deletedDevices = []
  231.  
  232.     // NotificationQueue só tem notificações caso elas estejam com status scheduled
  233.     // Somente se vieram notificações que estavam agendadas é que se deve criar notificações novamente para o post.
  234.     if (notificationQueue.docs && notificationQueue.docs.length > 0) {
  235.       for (const snap of notificationQueue.docs) {
  236.         const data = snap.data()
  237.  
  238.         deletedDevices.push({
  239.           push_token: data.token,
  240.           user_id: data.userUid ? (data.userUid.split('-')[1] || null) : null
  241.         })
  242.  
  243.         logger.info('posts', 'delete', 'Deleting notification on firestore:', snap.id)
  244.  
  245.         await snap.ref
  246.           .update({
  247.             deleted_at: firebase.firestore.FieldValue.serverTimestamp()
  248.           })
  249.       }
  250.     }
  251.  
  252.     return {
  253.       deletedDevices
  254.     }
  255.   }
  256. }()
  257.  
Advertisement
Add Comment
Please, Sign In to add comment