# Análisis de eventos
## Diagramas
### Sincronizar con Google
```plantuml
user -> browser: Escribe la url de su equipco
browser -> equipco: busca su subdominio respectivo
equipco -> browser: devuelve la aplicación
browser -> browser: Se ejecuta
browser -> user: Muestra al usuario
user -> browser: Click en Sincronizar con google calendar
browser -> auth: llamada https a /google-auth con host y userId
auth -> auth: Genera el link para autenticación en google
auth -> browser: redirección al link generado para google
browser -> google: visita el link seleccionado ingresa su contraseña y confirma permiso
google -> browser: redirecciona a /choose-calendar/google?GOOGLECODE
browser -> auth: /choose-calendar/google?GOOGLECODE
auth -> browser: Entrega archivos del app para que seleccione calendario (auth_app)
browser->auth: post /google-login con code y userId
auth->auth: Obten refresh token y guardalo, obten los calendarios
auth->equipco: Post api/google-login-success con userId
equipco->equipco: Guarda logged y mail
equipco->auth: ok
auth-> browser: envío de calendarios
browser-> user: Muestra la página cargada
user-> browser: selecciona un calendario y hace click en guardar
browser -> auth: set-google-calendar
auth -> auth: Crea la subscripción al calendario y obtiene lista de eventos de 2 meses
auth -> equipco: POST service-calendar-success con los nuevos eventos
equipco -> equipco: Compara crea edita Eventos
equipco -> auth: ok
auth -> browser: ok
browser -> equipco: hostname.equipco
equipco -> browser: App
browser -> browser: render
browser -> user: Muestra página equip.
```
### Desincronizar con Google
```plantuml
user -> browser: Click en Desconectar con google
browser -> equipco: Envía GOOGLE_LOGOUT
equipco -> auth: POST google-logout con userId
auth -> auth: logout
auth -> equipco: ok
equipco -> equipco: remove events from agenda, update projects, update events
equipco -> browser: Envía C_GOOGLE_LOGOUT
browser -> browser: Ejecuta los cambios
browser -> user: Actualiza la vista
```
### Events Endpoint
**No se priorizó terminarlo debido a que va a ser modificado**
```plantuml
start
:Validación del userId;
:Validación del body;
if (bodyError) then (yes)
#yellow:warn: Received a bad request body. Send 422;
stop
else (no)
if (UserIdError) then (yes)
#yellow:warn: Received a bad userId param. Send 422;
stop
else (no)
:eventSyncQ();
partition eventSyncQ()
:**await** getUser(agenda, outlook, google);
note right
Query to the DB
end note
if (user is logged in service?) then (yes)
else (no)
#AAAAAA:res.json ok: false, error: not synced with service;
end
endif
:**await** GetPriorities();
note right
Query to the DB
end note
:getTime();
:Filter events of the service;
:getAttendeeEmailsFromCalendarEvents();
:**await** getUsersByServiceAccount();
note right
Query to the DB
end note
:userAsDictByEventService();
:**await** getEventPrioritiesByServiceEventIds();
note right
Query to the DB
end note
:getEventsUpdates();
if (newEvents) then (yes)
:continue();
else (no)
:res ok true, failedEvents;
end
endif
:define eventReducer;
:Filter newEvents action update & has snapshot;
if(events with snapshot > 0) then(yes)
:groupEventByItsProjectIndex();
:**await** projectsWithSnapshotEvents();
note right
Query to the DB
end note
:**foreach** projectsWithSnapshotEvents
updateProject();
note right
Query to the DB, not awaiting
end note
endif
:brandNewEvents;
if(brandNewEvents > 0) then (yes)
:groupEventByItsProjectIndex;
:**await** projectsWithNewEvents;
:**foreach** projectsWithNewEvents
updtateProject();
note right
Query to the DB, not awaiting
end note
else (no)
endif
:filter removed Events;
:eventsToBeRemovedFromProjects();
if(eventsToBeRemovedFromProjects > 0)
:projectsIdsWithDeletedEvents;
:**await** projectsWithDeletedEvents;
note right
Query to the DB
end note
:**foreach** projectsWithDeletedEvents
getItemsAndOrderOfProjectByItemsBatch()
updateProject();
note right
Query to the DB, not awaiting
end note
endif
:processedNewEvents;
:**foreach** processedNewEvents
eventReducer(e);
:**await** updateUser(agenda);
:**foreach** processedNewEvents;
:**foreach** userAttendee
push to derivatedActions;
:save derivatedActions for userAttendees;
:**await** allPromises not awaited;
:Sockets.sendUpdate(derivatedActions);
:res.json ok: true failedEvents;
end
```
- getEventsUpdates
```plantuml
start
:map dbEvents;
partition "**foreach** calendarEvents" {
:find calendarEvent in dbEvents;
:find calendarEvent in nonScheduleDBEvents;
:dbEvent = scheduleDBEvent || nonScheduleDBEvent;
if(formatEvent(dbEvent)) then(no)
:formatEvent(dbEvent);
else (yes)
:failedEvents.push(event,error);
kill
endif
if(newEvent && datelimit > now) then(yes)
if(dbEvent) then(yes)
if(compare attributes) then(yes)
if(recurring) then(yes)
:updateEventsNewRecurrents = newEvent;
endif
:newEvents.push(newEvent, update);
endif
if(db.status !== canceled && eventsPrioritiesIds exists) then(yes)
:splice dbEvent;
endif
else(no)
:newEvents.push(newEvent);
endif
else(no)
endif
}
:newEvents.map
update newEvent if is recurring;
if(eventsPrioritiesIds > 0) then (yes)
:push event as deleted to processedNewEvents;
endif
:return processedNewEvents, failedEvents;
end
```
## Problemas
- Web
- **Acciones derivadas**:
- Las acciones derivadas enviadas a los dispositivos de los usuarios invitados no agrega nueva información, y solo manda la info que ya tenía la BD. Solo en la información del owner envía el nuevo evento.
- Api
- **Problemas con el Formato de evento**
- completedDate, confirmedAt están en la estructura principal, cuando el evento se cumple por cada atendee.
- hadDurationAt930: Solo hay uno en la estructura principal, pero si tuvo o no duración depende de cada attendee.
- isOrganizer: Hay uno en la estructura principal, pero fuera de los attendee no tiene sentido.
- organizerUserId, responsibleId son redundantes
- status y state se confunden.
- timeInvested y timesInvested No tiene sentido que esté en la estructura principal
- Hay un desafío claro en el feature de recurrencia y de interacción entre "acciones" por lo cual sería útil estructurar el objeto, con atributos que son modificados por una "entidad", por ejemplo: google_data, outlook_data y equip_data, para aislar las modificaciones "compatibles"
```javascript
{
"attendees": [
{
"completedDate": null ,
"confirmedAt": 1620819550208 ,
"duration": 30 ,
"email": anibal.cerda@3s.cl, »
"isOrganizer": false ,
"state": "confirmed" ,
"timeInvested": null ,
"timesInvested": [ ],
"updatedDate": "2021-05-12" ,
"userId": "635b5c6a-4bd1-42b8-a9ef-0e2a49a0e043"
} ,
{
"completedDate": null ,
"confirmedAt": 1620777414166 ,
"duration": 30 ,
"email": ignacio.bolomey@3s.cl, »
"isOrganizer": true ,
"state": "confirmed" ,
"timeInvested": null ,
"timesInvested": [ ],
"updatedDate": "2021-05-12" ,
"userId": "e75c820a-9900-400f-8524-10b0dda35954"
} ,
{
"completedDate": null ,
"confirmedAt": null ,
"duration": 30 ,
"email": nacho.bolomey@equipco.app, »
"isOrganizer": false ,
"state": "notConfirmed" ,
"timeInvested": null ,
"timesInvested": [ ],
"updatedDate": "2021-05-12" ,
"userId": null
}
] ,
"completedDate": null ,
"confirmedAt": 1620777414166 ,
"createdAt": 1615553494429 ,
"createdSnapshotId": null ,
"dateLimit": "2021-05-12" ,
"duration": 30 ,
"eventId": "6gqjgpb268sjcbb6cph3ib9k6cs3ab9ocgqjgb9gcpijcohi64o34pb26k_20210512T120000Z" ,
"hadDurationAt930": true ,
"iCalUId": 6gqjgpb268sjcbb6cph3ib9k6cs3ab9ocgqjgb9gcpijcohi64o34pb26k_R20210316T110000@google.com, »
"id": "b19bf6b9-1b6f-4dae-a9b3-5818f0a9bf22" ,
"isOrganizer": true ,
"isPrivate": false ,
"link": https://www.google.com/calendar/event?eid=NmdxamdwYjI2OHNqY2JiNmNwaDNpYjlrNmNzM2FiOW9jZ3FqZ2I5Z2NwaWpjb2hpNjRvMzRwYjI2a18yMDIxMDUxMlQxMjAwMDBaIGlnbmFjaW8uYm9sb21leUAzcy5jbA, »
"organizerUserId": "e75c820a-9900-400f-8524-10b0dda35954" ,
"originalCreatedAt": "2021-01-20T18:43" ,
"originalDateLimit": "2021-05-12T08:00" ,
"project": null ,
"recurringId": "6gqjgpb268sjcbb6cph3ib9k6cs3ab9ocgqjgb9gcpijcohi64o34pb26k_R20210316T110000" ,
"responsibleId": "e75c820a-9900-400f-8524-10b0dda35954" ,
"service": "google" ,
"snapshots": [ ],
"state": "confirmed" ,
"status": "confirmed" ,
"timeInvested": null ,
"timeType": "td" ,
"timesInvested": [ ],
"title": "Check in Nacho y Anibal" ,
"type": "Event" ,
"wasInHoyAt930": true ,
"wasInHoyPast930": true ,
"wasInTd": [ ]
}
```
- **Esructura del código**
- El código fue escrito sin ninguna estructura más que los endpoints que reciben los eventos. Esto es grave en terminos de mantención y resolución de bugs.
- La lógica de eventos debiese estar ordenada al menos como una líbrería para interactuar con los eventos.
- routehandlers /events, **se define la función eventReducer dentro de la lógica**, por lo que por cada llamada es necesario crear esta función.
- routehandlers, La lógica de Eventos no se reutiliza, por lo que tenemos dos endpoint `events`, `serviceCalendarSuccess` que definen distintos métodos para incorporar los eventos.
- **Desempeño**:
- Cada vez que un usuario se sincroniza con google genera una subscripción a un calendario, y por ende cada vez que google reconozca un cambio en algún evento de ese calendario enviará la lista de 2 semanas desde hoy a equip.
- Events:
- Cada vez que le llega una llamada de algún usuario, recibe todos los eventos de dos semanas
- Se llama al usuario, su agenda y su mail de google y outlook
- Si el cliente está loggeado a ese servicio, se llaman a todas las prioridades de la agenda del usuario.
- Se recorren todos los eventos que llegaron, guardando la lista de mails de los asistentes.
- Basado en la lista de asistentes, se obtiene de esos usuarios: id, createdAt, google, outlook y la agenda.
- Se crea un diccionario de attendees. (mail -> uid)
- nonScheduleDBEvents: Se obtienen todas las prioridades con el id del evento.
- Se ejecuta getEventsUpdates:
- Se crea eventsPrioritiesIds array con ids de eventos de la bd.
- Recorre los eventos traidos por servicio:
- Find de los eventos de la BD el id del evento traido por el servicio
- Find del evento traido por el servicio en nonScheduleDBEvents
- Si encontró el evento en la BD o en nonScheduleDBEvents, asume que tiene un evento en la BD.
- Se formatea el evento y se guarda en newEvent
- Si el evento se pudo formatear y es para hoy o mayor, se compara el evento de la BD con el evento nuevo.
- **problema** La comparación del evento de la BD con el de google, asigna updates a eventos que no han cambiado. La comparación está realizando incorrectamente, debido a la condición `newEvent.attendees.length`
## Relaciones con equip
- web
- Desde Agenda o leaderTable
- PlanificationHistory
- PlanificationList
- SimpleList
- SimpleCardWithSubtaskList
- simpleCard
- IndicatorsInfo
-VariableInfo
- Lists
- PlanificationList
- SimpleList
- SimpleCardWithSubtaskList
- SimpleCard
- Schedule
- AgendaList
- sortableList
- sortableElement
- CardTaskWrapper
- EventCard
- Projects
- projectList
- sortableProjectList
- ProjectElementsList
- sortableList
- sortableElement
- ElementWrapper
- sortableHandler
- SimpleCardWithSubtaskList
- simpleCard
- handlers
- events: Recibe los eventos de auth y los procesa generando acciones
- outlookLoginSuccess: Confirmación de que el usuario se conectó a outlook. Guarda el email utilizado en outlook.
- googleLoginSuccess: Confirmación de que el usuario se conectó a google. Guarda el email utilizado en gcalendar.
- serviceCalendarSuccess: Luego de una sincronización satisfactoria, se utiliza este endpoint para incorporar los eventos del usuario.
- calendarServiceLogout: Confirmación de que el usuario se desconectó correctamente y la eliminación de sus eventos
- Acciones
- Delete_calendar_event
- D_DELETE_CALENDAR_EVENT
- complete_event
- D_COMPLETE_EVENT
- set_project_event
- D_SET_PROJECT_EVENT
- confirm_attendance
- D_CONFIRM_ATTENDANCE
- outlook_logout
- D_OUTLOOK_LOGOUT
- google_logout
- D_GOOGLE_LOGOUT
- Otras derivadas
- del routehandler de events y serviceCalendarSuccess: D_SYNC_CALENDAR_EVENTS
- Consecuencias del cron
- Los eventos se incluyen dentro del modelo de prioridades.
- dateLimitMovement: Se modifica el timetype
- longitudinalChanges (Tiene errores, los valores longitudinales son por cada persona, pero actualmente es una por evento calculada para el organizador)
- resetCompletedEvents
- Hay un evento que fue cumplido ayer pero en el que state sea distinto a completed (significa que ese evento fue primero cumplido y luego movido a otra fecha), si tiene snapshots y el confirmedAt tiene la misa fecha que el snapshot, define el completedDate como null y confirmedAt como merezca.
- delete events form agenda
- Nueva consecuencia que elimina los eventos que ya sucedieron. Los elimina de agendas, priority y se actualiza en oldEvents
- Indicadores
- Productividad variable c
- Está erroneo el filtro de c1 de la variable `completedEvents`, ya que revisa si el evento fue cumplido usando el modelo anterior, no el de attendees.
- Está erroneo el calculo de la variable c2 , ya que usa el modelo antiguo (confirmedAt de primer nivel), en vez de usar el nuevo modelo de attendees.
- Librerías utilizadas
- actionReducer/utils
- Lib3S
- completeAttendeeDataForEvent
## Relación con feature de reuniones de clarificación
- Aclaraciones
- Los usuarios ingresan a su agenda una nueva prioridad que tiene como atributo el id de un evento, pero se genera una prioridad por usuario.
- ¿Cómo se envian los eventos de meeting en el estado?
- Web
- Acciones
- CANCEL_CLARIFICATION_MEETING_EVENT
- CONFIRM_CLARIFICATION_MEETING_PROPOSAL
- GET_CLARIFICATION_MEETING_DETAILS
- SEND_CLARIFICATION_MEETING_PROPOSAL
- SEND_DRAFT_REQUEST
- SEND_REQUEST
- UPDATE_MEETING_LINK
- UPDATE_REQUEST
- UPDATE_REQUEST_DATE_LIMIT
- UPDATE_CREATED_REQUEST_BY_RECEIVER
- queries
- getMeeting
- getMeetingsWithIds
- getUserMeetingProposals
- insertMeeting
- updateMeeting
- Consecuencias del cron
- updateRequestNotificationsFromClarificationMeeting
- longitudinalChanges // actualmente no lo afecta
- dateLimitMovement
- resetCompletedEvents // actualmente no lo afecta
-
- Indicadores
- Librerías utilizadas
----
## Requisitos
- No se eliminará información de los eventos.
- Crear una tabla historial para los eventos
- Cuando hago un cambio e un evento, lo actualizo en priorities y en oldEvents.
- Cuando saco al ultimo attendee se elimina el attendee, se elimina de priority y se guarda en oldEvents.
- Por cada llamada de auth (serviceEvents, serviceName, userId)
- Priority
- newEvents: Agregar los serviceEvents que no están en priorities, ingresando solo los attendees que están sincronizados.
- updatedEvents: Actualizar los eventos que estaban en priority, pero que tienen diferencias con los serviceEvents
- changedDataEvents: Eventos que modificaron su hora, fecha, título
- deletedAttendeeEvents: eventos que eliminaron a algún sus attendees
- addedAttendeeEvents: eventos que agregaron a attendees
- cancelledEvents: Los eventos que están en priority pero no en serviceEvents, deben eliminar al userId como attendee de los eventos, Y si es el último attendee, eliminar el registro de priority. A la vez, actualizar el evento en oldEvents, agregando una nueva versión, si queda sin attendees, el estado del evento es cancelled.
- OldEvents
- newEvents: Agregar los serviceEvents que no están en priorities, ingresando solo los attendees que están sincronizados.
- updatedEvents
- Actualizar los eventos que estaban en priority en una nueva versión.
- cancelledEvents
- Actualizar los eventos en oldEvents, la última versión debe no tener attendees y debe ser marcado como cancelado, si tiene más de un attendee solo hay que eliminar el userId de attendees.
- Agenda
- Agregar newEvents en las agendas de cada attendees del evento.
- Actualizar changedDataEvents en la agenda de cada attendee. // @TODO Revisar si se duplica el orden de la agenda en la api y en schedule de la agenda. [incertidumbre con respecto a cómo el componente schedule ordena sus elementos.] Si es que no necesito modificar el orden de la agenda, quizás es más eficiente el crear un índice para obtener todos los eventos a los cuales están invitados.
- Eliminar los deletedAttendeeEvents en la agenda de los attendees eliminados
- Agregar addedAttendeeEvents en la agenda de los attendees agregados
- Eliminar los cancelledEvents de la agenda del usuario.
- Project
- Si los cancelledEvents quedan sin integrantes, eliminarlos de projects
- Actualizar los changedDataEvents (aquí está el proceso de los snapshots)
- Acciones derivadas
- Usuarios con newEvents (evento y nuevo orden)
- Usuarios con changedDataEvents (evento y nuevo orden)
- Usuarios dentro de deletedAttendeeEvents (nuevo orden)
- Usuarios dentro de addedAttendeeEvents (evento y nuevo orden)
- Usuarios con cancelledEvents (nuevo orden)
- // Cambios en proyectos no se notifican debido a que la próxima vez que los consulten se actualizarán.
## Pseudocodigo de /events y ServiceCalendarSuccess
- joiValidation()
- userIdError
- bodyError
- Q // Toda la ejecución posterior se ingresa dentro de este wrapper
- const serviceEmails = await queries.getServiceEmail()
- const userIds2ServiceEmail = getUserIds2ServiceEmail(serviceEmails)
userIds2ServiceEmail= {
userId: 'serviceEmail'
}
- if (userIds2ServiceEmail[userId]) // De estó depende la ejecución posterior
- serviceEmail2UserIds = transformEmail2userIds(userIds2ServiceEmail)
serviceEmail2UserIds = {
serviceEmail = [userId1, userId2...]
}
- const formattedEvents = formatServiceEvents(serviceName, serviceEvents, serviceEmail2UserIds)
- const dbEvents = await queries.getEventsById(serviceEvents.map(e=>e.eventId))
- const trackUserChanges = {
userId: [
'agenda': [] // last agenda (whit all modifications)
'added': [event],
'deleted': [event],
'updatedEvent': [event] // event was updated
]
}
- const trackProjectChanges = {
projectId: [
'updated': {event}
'order': [],
'items': []
]
}
- const trackUserChanges[userId] = await queries.getAgendaEvents(userId)
- const eventChanges = getEventChanges(formattedEvents, dbEvents, userAgendaEvents) => {
newEvents: [{...event},{...event2},...]
updatedEvents: [{...event, addedAttendees, deletedAttendees}],
cancelledEvents: [{...event, deletedAttendees},...] // from dbEvents from the difference between userAgendaEvents and serviceEvents. The user will be removed.
}
- for each newEvent in eventChanges.newEvents
- await queries.saveNewPriority(newEvent)
- await queries.saveOldEvent(newEvent)
- const newAttendeesUserId = getAttendees(newEvent)
- for each newAttendee in newAttendeesUserId
- const trackUserChanges[newAttendee.id] = trackUserChanges[newAttendee.id] || await queries.getAgenda(newAttendee.id)
- trackUserChanges[newAttendee.id] = {
agenda: addEventToAgenda(newEvent, trackUserChanges[newAttendee.id]['agenda']),
added: [...trackUserChanges[newAttendee.id]['added'], newEvent],
deleted: trackUserChanges[newAttendee.id]['deleted']
}
- for each updatedEvent in eventChanges.updatedEvents
- queries.saveUpdatedEventInPriorities
- const trackProjectChanges[projectId] = queries.getProject(eventChanges.updatedEvents)
- trackProjectChanges[projectId] = updateProjects(trackProjectChanges[projectId], eventChanges.updatedEvents) // updatedEvents
- const affectedAttendeesIds = getAffectedAttendees(updatedEvent) // We need ids of attendees and deleted attendees of each updatedEvents
- foreach user id affectedAttendeesIds
- trackUserChanges[affectedAttendeeId] = trackUserChanges[affectedAttendeesId] || queries.getAgenda(affectedAttendeesId) // we need agendas from attendees and deletedAttendees of updated events, we may have some of them, we only need to call the rest.
- foreach deletedAttendees
- trackUserChanges[attendeeId] = deleteFromAgendas(attendeeId, trackUserChanges[attendeeId]) //deletedAttendees
- foreach attendee
- if (addedAttendees.has(attendee))
- trackUserChanges[attendeeId] = AddToAgenda(attendeeId, trackUserChanges[attendeeId]) // addedAttendees
- trackUserChanges[attendeeId] = changeAgendasOrder(attendeeId, trackUserChanges[attendeeId]) // updatedEvents
- updateCanceledEvents(eventChanges.cancelledEvents)
- const affectedAttendeesIds = getAffectedAttendees(eventChanges.cancelledEvents)
- foreach(affectedAttendeesIds)
- trackUserChanges[affectedAttendeesId] = trackUserChanges[affectedAttendeesId] || queries.getMissingAttendeesAgendas(affectedAttendeesId)
- foreach cancelledEvents
- queries.saveCancelledEventsPriority
- trackProjectChanges[projectId] = updateProject(trackProjectChanges[projectId], eventChanges.cancelledEvent)
- foreach attendee
- trackUserChanges[affectedAttendeesId] =deleteFromAgenda(trackUserChanges[affectedAttendeesId], cancelledEvent) // cancelledEvents attendees.
- derivedActions = getDerivedActions(trackUserChanges, trackProjectChanges)
- confirmAction = getConfirmAction(trackUserChanges[userId])
## Formato de los eventos en BD
Reglas:
- Atributos que están en otras prioridades tb van en primer nivel
- Campos que dependen de la información de google o outlook van en service
- Frecuencia de uso amerita acercarlo al primer nivel
-
const event = {
id,
projectId: 'projectId',
snapshots, // revisar como cambia con oldEvents
type: 'Event',
createdAt: // date
title
link,
duration: 30 ,
dateLimit, // shortdate // revisar qué ocurre con los eventos de más de un día.
service: { //
organizerUserId, // Es un arreglo! ya que muchos usuarios pueden estar conectados. @todo ojo en la web.
serviceName
eventId,
iCalUId, // Es redundante con eventID? cual es la diferencia
originalCreatedAt,
originalDateLimit, // para qué sirve
recurringId,
status: 'notConfirmed'
},
attendees: {
'userId1' : {
completedDate: null ,
confirmedAt: null ,
hadDurationAt930, // no solo se utiliza en prioritize_agenda ya que no se puede cambiar de día todavía
isOrganizer: true ,
state: "notConfirmed", // defined by us. ('confirmed', 'completed')
timeInvested: null ,
timesInvested: [ ],
timeType: 'cs',
updatedAt: timestamp // used in confirm_attendance action.
updatedDate: "2019-02-27",
userId: null
wasInHoyAt930, // no solo se utiliza en prioritize_agenda ya que no se puede cambiar de día todavía
wasInHoyPast930, // no solo se utiliza en prioritize_agenda ya que no se puede cambiar de día todavía
wasInTd: [] // necesario para el calculo de productividad c2 según el formato, es un array.
}
}
}
## assign_duration_ priority is used on events?
## Estructura del código
/serviceEvents
readme.md
index.js
getUserIds2ServiceEmail
transformEmail2userIds
formatServiceEvents
getEventChanges
utils.js
## ¿Cómo funcionan los snapshots?
- Al crear el formato, en formatEvent, se utiliza la función llamada getDbEventChanges que devuelve un atributo llamado snapshot y createdSnapshotId.
- En getDbEventChanges,
- Se crea una array que si que contenga los dbEvent.snapshots se pasa a snapshots
- Si existe un evento en la bd y se detecta un cambio en datelimit (el evento trae un datelimit distinto a el que se encontraba en la bd) y el evento tenía estado de completed, se hace push a snapshots con el evento de la base de dato, con un array vacío snapshots y el snapshotId (uuid). En los comentarios de ese código dice: the snapshotId is only used to keep track an order inside the project
- Luego vuelve al routehandler y si existen eventos con crceatedSnapshotId calcula la constante projectsIdsWithSnapshotEvents al ejecutar la función groupEventByItsProjectIndex donde acumula estos eventos según su projectId
- Luego llama a todos los proyectos que están en esos projectId y los define como projectsWithSnapshotEvents
- Luego los recorre, para reducirlos, toma como valor inicial items y order de project y como función reductora entrega la función getProjectItemsAndOrderForNewSnapshotEvent con el id del evento, el createdSanpshotId el order y items acumumulado
- La función getProjectItemsAndOrderForNewSnapshotEvent obtiene el eventId, snapshotId el order y items, y reemplaza el evento y su snapshot y devuelve items y order.
- Luego se actualiza el proyecto.
## Decisiones pendientes
- Queremos mantener los eventos aunque sean eliminados desde google o después de ser realizado?