mirror of
				https://github.com/lingble/twenty.git
				synced 2025-10-30 04:12:28 +00:00 
			
		
		
		
	Fixes on messaging and calendar (#7485)
Fix syncedAt no longer been set on message sync. Fix calendar data model: - Add `syncedAt` to `CalendarChannelWorkspaceEntity` - Move `recurringEventExternalId` from `CalendarEventWorkspaceEntity` to `CalendarChannelEventAssociationWorkspaceEntity` since the id is relative to one channel Fix save queries on calendar sync after regression.
This commit is contained in:
		| @@ -9080,28 +9080,6 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = | ||||
|                     relationDefinition: null, | ||||
|                   }, | ||||
|                 }, | ||||
|                 { | ||||
|                   __typename: 'fieldEdge', | ||||
|                   node: { | ||||
|                     __typename: 'field', | ||||
|                     settings: {}, | ||||
|                     id: '567c7852-6dc5-4c6e-826d-e4b253614e60', | ||||
|                     type: 'TEXT', | ||||
|                     name: 'recurringEventExternalId', | ||||
|                     label: 'Recurring Event ID', | ||||
|                     description: 'Recurring Event ID', | ||||
|                     icon: 'IconHistory', | ||||
|                     isCustom: false, | ||||
|                     isActive: true, | ||||
|                     isSystem: false, | ||||
|                     isNullable: false, | ||||
|                     createdAt: '2024-09-25T13:45:32.757Z', | ||||
|                     updatedAt: '2024-09-25T13:45:32.757Z', | ||||
|                     defaultValue: "''", | ||||
|                     options: null, | ||||
|                     relationDefinition: null, | ||||
|                   }, | ||||
|                 }, | ||||
|                 { | ||||
|                   __typename: 'fieldEdge', | ||||
|                   node: { | ||||
|   | ||||
| @@ -14,6 +14,7 @@ export const seedCalendarChannelEventAssociations = async ( | ||||
|       'calendarChannelId', | ||||
|       'calendarEventId', | ||||
|       'eventExternalId', | ||||
|       'recurringEventExternalId', | ||||
|     ]) | ||||
|     .orIgnore() | ||||
|     .values([ | ||||
| @@ -22,6 +23,7 @@ export const seedCalendarChannelEventAssociations = async ( | ||||
|         calendarChannelId: '59efdefe-a40f-4faf-bb9f-c6f9945b8203', | ||||
|         calendarEventId: '86083141-1c0e-494c-a1b6-85b1c6fefaa5', | ||||
|         eventExternalId: 'exampleExternalId', | ||||
|         recurringEventExternalId: 'exampleRecurringExternalId', | ||||
|       }, | ||||
|     ]) | ||||
|     .execute(); | ||||
|   | ||||
| @@ -24,7 +24,6 @@ export const seedCalendarEvents = async ( | ||||
|       'conferenceSolution', | ||||
|       'conferenceLinkPrimaryLinkLabel', | ||||
|       'conferenceLinkPrimaryLinkUrl', | ||||
|       'recurringEventExternalId', | ||||
|     ]) | ||||
|     .orIgnore() | ||||
|     .values([ | ||||
| @@ -43,7 +42,6 @@ export const seedCalendarEvents = async ( | ||||
|         conferenceSolution: 'Zoom', | ||||
|         conferenceLinkPrimaryLinkLabel: 'https://zoom.us/j/1234567890', | ||||
|         conferenceLinkPrimaryLinkUrl: 'https://zoom.us/j/1234567890', | ||||
|         recurringEventExternalId: 'recurring1', | ||||
|       }, | ||||
|     ]) | ||||
|     .execute(); | ||||
|   | ||||
| @@ -63,6 +63,7 @@ export const CALENDAR_CHANNEL_EVENT_ASSOCIATION_STANDARD_FIELD_IDS = { | ||||
|   calendarChannel: '20202020-93ee-4da4-8d58-0282c4a9cb7d', | ||||
|   calendarEvent: '20202020-5aa5-437e-bb86-f42d457783e3', | ||||
|   eventExternalId: '20202020-9ec8-48bb-b279-21d0734a75a1', | ||||
|   recurringEventExternalId: '20202020-c58f-4c69-9bf8-9518fa31aa50', | ||||
| }; | ||||
|  | ||||
| export const CALENDAR_CHANNEL_STANDARD_FIELD_IDS = { | ||||
| @@ -78,6 +79,7 @@ export const CALENDAR_CHANNEL_STANDARD_FIELD_IDS = { | ||||
|   syncStatus: '20202020-7116-41da-8b4b-035975c4eb6a', | ||||
|   syncStage: '20202020-6246-42e6-b5cd-003bd921782c', | ||||
|   syncStageStartedAt: '20202020-a934-46f1-a8e7-9568b1e3a53e', | ||||
|   syncedAt: '20202020-2ff5-4f70-953a-3d0d36357576', | ||||
| }; | ||||
|  | ||||
| export const CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS = { | ||||
| @@ -103,7 +105,6 @@ export const CALENDAR_EVENT_STANDARD_FIELD_IDS = { | ||||
|   iCalUID: '20202020-f24b-45f4-b6a3-d2f9fcb98714', | ||||
|   conferenceSolution: '20202020-1c3f-4b5a-b526-5411a82179eb', | ||||
|   conferenceLink: '20202020-35da-43ef-9ca0-e936e9dc237b', | ||||
|   recurringEventExternalId: '20202020-4b96-43d0-8156-4c7a9717635c', | ||||
|   calendarChannelEventAssociations: '20202020-bdf8-4572-a2cc-ecbb6bcc3a02', | ||||
|   calendarEventParticipants: '20202020-e07e-4ccb-88f5-6f3d00458eec', | ||||
| }; | ||||
|   | ||||
| @@ -7,12 +7,10 @@ import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queu | ||||
| import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; | ||||
| import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; | ||||
| import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; | ||||
| import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; | ||||
| import { injectIdsInCalendarEvents } from 'src/modules/calendar/calendar-event-import-manager/utils/inject-ids-in-calendar-events.util'; | ||||
| import { CalendarEventParticipantService } from 'src/modules/calendar/calendar-event-participant-manager/services/calendar-event-participant.service'; | ||||
| import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity'; | ||||
| import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity'; | ||||
| import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity'; | ||||
| import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity'; | ||||
| import { CalendarEventWithParticipants } from 'src/modules/calendar/common/types/calendar-event'; | ||||
| import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; | ||||
| @@ -28,7 +26,6 @@ export class CalendarSaveEventsService { | ||||
|     private readonly calendarEventParticipantService: CalendarEventParticipantService, | ||||
|     @InjectMessageQueue(MessageQueue.contactCreationQueue) | ||||
|     private readonly messageQueueService: MessageQueueService, | ||||
|     private readonly workspaceEventEmitter: WorkspaceEventEmitter, | ||||
|   ) {} | ||||
|  | ||||
|   public async saveCalendarEventsAndEnqueueContactCreationJob( | ||||
| @@ -103,6 +100,7 @@ export class CalendarSaveEventsService { | ||||
|         calendarEventId: calendarEvent.id, | ||||
|         eventExternalId: calendarEvent.externalId, | ||||
|         calendarChannelId: calendarChannel.id, | ||||
|         recurringEventExternalId: calendarEvent.recurringEventExternalId, | ||||
|       })); | ||||
|  | ||||
|     const participantsToSave = eventsToSave.flatMap( | ||||
| @@ -113,16 +111,57 @@ export class CalendarSaveEventsService { | ||||
|       (event) => event.participants, | ||||
|     ); | ||||
|  | ||||
|     const savedCalendarEventParticipantsToEmit: CalendarEventParticipantWorkspaceEntity[] = | ||||
|       []; | ||||
|  | ||||
|     const workspaceDataSource = await this.twentyORMManager.getDatasource(); | ||||
|  | ||||
|     await workspaceDataSource?.transaction(async (transactionManager) => { | ||||
|       await calendarEventRepository.save(eventsToSave, {}, transactionManager); | ||||
|       await calendarEventRepository.save( | ||||
|         eventsToSave.map( | ||||
|           (calendarEvent) => | ||||
|             ({ | ||||
|               id: calendarEvent.id, | ||||
|               iCalUID: calendarEvent.iCalUID, | ||||
|               title: calendarEvent.title, | ||||
|               description: calendarEvent.description, | ||||
|               startsAt: calendarEvent.startsAt, | ||||
|               endsAt: calendarEvent.endsAt, | ||||
|               location: calendarEvent.location, | ||||
|               isFullDay: calendarEvent.isFullDay, | ||||
|               isCanceled: calendarEvent.isCanceled, | ||||
|               conferenceSolution: calendarEvent.conferenceSolution, | ||||
|               conferenceLink: { | ||||
|                 primaryLinkLabel: calendarEvent.conferenceLinkLabel, | ||||
|                 primaryLinkUrl: calendarEvent.conferenceLinkUrl, | ||||
|               }, | ||||
|               externalCreatedAt: calendarEvent.externalCreatedAt, | ||||
|               externalUpdatedAt: calendarEvent.externalUpdatedAt, | ||||
|             }) satisfies DeepPartial<CalendarEventWorkspaceEntity>, | ||||
|         ), | ||||
|         {}, | ||||
|         transactionManager, | ||||
|       ); | ||||
|  | ||||
|       await calendarEventRepository.save( | ||||
|         eventsToUpdate, | ||||
|         eventsToUpdate.map( | ||||
|           (calendarEvent) => | ||||
|             ({ | ||||
|               id: calendarEvent.id, | ||||
|               iCalUID: calendarEvent.iCalUID, | ||||
|               title: calendarEvent.title, | ||||
|               description: calendarEvent.description, | ||||
|               startsAt: calendarEvent.startsAt, | ||||
|               endsAt: calendarEvent.endsAt, | ||||
|               location: calendarEvent.location, | ||||
|               isFullDay: calendarEvent.isFullDay, | ||||
|               isCanceled: calendarEvent.isCanceled, | ||||
|               conferenceSolution: calendarEvent.conferenceSolution, | ||||
|               conferenceLink: { | ||||
|                 primaryLinkLabel: calendarEvent.conferenceLinkLabel, | ||||
|                 primaryLinkUrl: calendarEvent.conferenceLinkUrl, | ||||
|               }, | ||||
|               externalCreatedAt: calendarEvent.externalCreatedAt, | ||||
|               externalUpdatedAt: calendarEvent.externalUpdatedAt, | ||||
|             }) satisfies DeepPartial<CalendarEventWorkspaceEntity>, | ||||
|         ), | ||||
|         {}, | ||||
|         transactionManager, | ||||
|       ); | ||||
|   | ||||
| @@ -171,6 +171,7 @@ export class CalendarChannelSyncStatusService { | ||||
|       syncStatus: CalendarChannelSyncStatus.ACTIVE, | ||||
|       throttleFailureCount: 0, | ||||
|       syncStageStartedAt: null, | ||||
|       syncedAt: new Date().toISOString(), | ||||
|     }); | ||||
|  | ||||
|     await this.schedulePartialCalendarEventListFetch(calendarChannelIds); | ||||
|   | ||||
| @@ -1,16 +1,16 @@ | ||||
| import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface'; | ||||
|  | ||||
| import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; | ||||
| import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; | ||||
| import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity'; | ||||
| import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator'; | ||||
| import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator'; | ||||
| import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator'; | ||||
| import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator'; | ||||
| import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator'; | ||||
| import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator'; | ||||
| import { CALENDAR_CHANNEL_EVENT_ASSOCIATION_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; | ||||
| import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; | ||||
| import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator'; | ||||
| import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator'; | ||||
| import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator'; | ||||
| import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator'; | ||||
| import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; | ||||
| import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator'; | ||||
| import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity'; | ||||
| import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator'; | ||||
| import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity'; | ||||
| import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity'; | ||||
|  | ||||
| @@ -35,6 +35,16 @@ export class CalendarChannelEventAssociationWorkspaceEntity extends BaseWorkspac | ||||
|   }) | ||||
|   eventExternalId: string; | ||||
|  | ||||
|   @WorkspaceField({ | ||||
|     standardId: | ||||
|       CALENDAR_CHANNEL_EVENT_ASSOCIATION_STANDARD_FIELD_IDS.recurringEventExternalId, | ||||
|     type: FieldMetadataType.TEXT, | ||||
|     label: 'Recurring Event ID', | ||||
|     description: 'Recurring Event ID', | ||||
|     icon: 'IconHistory', | ||||
|   }) | ||||
|   recurringEventExternalId: string; | ||||
|  | ||||
|   @WorkspaceRelation({ | ||||
|     standardId: | ||||
|       CALENDAR_CHANNEL_EVENT_ASSOCIATION_STANDARD_FIELD_IDS.calendarChannel, | ||||
|   | ||||
| @@ -270,6 +270,16 @@ export class CalendarChannelWorkspaceEntity extends BaseWorkspaceEntity { | ||||
|   }) | ||||
|   syncCursor: string; | ||||
|  | ||||
|   @WorkspaceField({ | ||||
|     standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.syncedAt, | ||||
|     type: FieldMetadataType.DATE_TIME, | ||||
|     label: 'Last sync date', | ||||
|     description: 'Last sync date', | ||||
|     icon: 'IconHistory', | ||||
|   }) | ||||
|   @WorkspaceIsNullable() | ||||
|   syncedAt: string | null; | ||||
|  | ||||
|   @WorkspaceField({ | ||||
|     standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.syncStageStartedAt, | ||||
|     type: FieldMetadataType.DATE_TIME, | ||||
|   | ||||
| @@ -145,15 +145,6 @@ export class CalendarEventWorkspaceEntity extends BaseWorkspaceEntity { | ||||
|   @WorkspaceIsNullable() | ||||
|   conferenceLink: LinksMetadata; | ||||
|  | ||||
|   @WorkspaceField({ | ||||
|     standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.recurringEventExternalId, | ||||
|     type: FieldMetadataType.TEXT, | ||||
|     label: 'Recurring Event ID', | ||||
|     description: 'Recurring Event ID', | ||||
|     icon: 'IconHistory', | ||||
|   }) | ||||
|   recurringEventExternalId: string; | ||||
|  | ||||
|   @WorkspaceRelation({ | ||||
|     standardId: | ||||
|       CALENDAR_EVENT_STANDARD_FIELD_IDS.calendarChannelEventAssociations, | ||||
|   | ||||
| @@ -36,6 +36,7 @@ export type CalendarEventParticipantWithCalendarEventId = | ||||
|  | ||||
| export type CalendarEventWithParticipants = CalendarEvent & { | ||||
|   externalId: string; | ||||
|   recurringEventExternalId?: string; | ||||
|   participants: CalendarEventParticipant[]; | ||||
|   status: string; | ||||
| }; | ||||
| @@ -43,6 +44,7 @@ export type CalendarEventWithParticipants = CalendarEvent & { | ||||
| export type CalendarEventWithParticipantsAndCalendarEventId = CalendarEvent & { | ||||
|   id: string; | ||||
|   externalId: string; | ||||
|   recurringEventExternalId?: string; | ||||
|   participants: CalendarEventParticipantWithCalendarEventId[]; | ||||
|   status: string; | ||||
| }; | ||||
|   | ||||
| @@ -146,6 +146,7 @@ export class MessageChannelSyncStatusService { | ||||
|       syncStage: MessageChannelSyncStage.PARTIAL_MESSAGE_LIST_FETCH_PENDING, | ||||
|       throttleFailureCount: 0, | ||||
|       syncStageStartedAt: null, | ||||
|       syncedAt: new Date().toISOString(), | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Raphaël Bosi
					Raphaël Bosi