From 676c902731a11ee8aa1beaab7447324df38d67f5 Mon Sep 17 00:00:00 2001 From: Weiko Date: Thu, 1 Aug 2024 18:41:28 +0200 Subject: [PATCH] Fix timelineActivity updated fields (#6494) ## Context We recently introduced the new twenty ORM and used it in the update methods in the query runner. Initially we were using pg_graphql to fetch the record before updating it allowing us to compare the before and the after and create a diff. This diff is then used for the timeline activity creation. Now, twentyORM is doing the fetch and pg_graphql is still doing the update and their responses are not exactly the same, which means the diff is not working as intended (e.g date types were always in the diff due to one being in Date format and the other as a string) This PR introduces a updatedFields property to the update event which comes from the input. This is not ideal as this won't work for API users that send the whole payload but will be sufficient enough for our FE that only sends modified fields. We then compare only those fields in the diff. --- .../listeners/entity-events-to-db.listener.ts | 1 + .../workspace-query-runner.service.ts | 2 ++ .../types/object-record-update.event.ts | 1 + .../object-record-changed-values.spec.ts | 4 +++ .../utils/object-record-changed-values.ts | 27 ++++++++++--------- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/entity-events-to-db.listener.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/entity-events-to-db.listener.ts index d9a018f86..0d5909471 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/entity-events-to-db.listener.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/entity-events-to-db.listener.ts @@ -28,6 +28,7 @@ export class EntityEventsToDbListener { payload.properties.diff = objectRecordChangedValues( payload.properties.before, payload.properties.after, + payload.properties.updatedFields, payload.objectMetadata, ); diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service.ts index 1a5de2956..bef145c1e 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service.ts @@ -439,6 +439,7 @@ export class WorkspaceQueryRunnerService { recordId: existingRecord.id, objectMetadata: objectMetadataItem, properties: { + updatedFields: Object.keys(args.data), before: this.removeNestedProperties(existingRecord as Record), after: this.removeNestedProperties(parsedResults?.[0]), }, @@ -518,6 +519,7 @@ export class WorkspaceQueryRunnerService { recordId: existingRecord.id, objectMetadata: objectMetadataItem, properties: { + updatedFields: Object.keys(args.data), before: this.removeNestedProperties(existingRecord as Record), after: this.removeNestedProperties(record), }, diff --git a/packages/twenty-server/src/engine/integrations/event-emitter/types/object-record-update.event.ts b/packages/twenty-server/src/engine/integrations/event-emitter/types/object-record-update.event.ts index 037b38178..aa4b59e72 100644 --- a/packages/twenty-server/src/engine/integrations/event-emitter/types/object-record-update.event.ts +++ b/packages/twenty-server/src/engine/integrations/event-emitter/types/object-record-update.event.ts @@ -2,6 +2,7 @@ import { ObjectRecordBaseEvent } from 'src/engine/integrations/event-emitter/typ export class ObjectRecordUpdateEvent extends ObjectRecordBaseEvent { properties: { + updatedFields: string[]; before: T; after: T; diff?: Partial; diff --git a/packages/twenty-server/src/engine/integrations/event-emitter/utils/__tests__/object-record-changed-values.spec.ts b/packages/twenty-server/src/engine/integrations/event-emitter/utils/__tests__/object-record-changed-values.spec.ts index 9e4e51791..6772e1f87 100644 --- a/packages/twenty-server/src/engine/integrations/event-emitter/utils/__tests__/object-record-changed-values.spec.ts +++ b/packages/twenty-server/src/engine/integrations/event-emitter/utils/__tests__/object-record-changed-values.spec.ts @@ -36,6 +36,7 @@ describe('objectRecordChangedValues', () => { const result = objectRecordChangedValues( oldRecord, newRecord, + ['name'], mockObjectMetadata, ); @@ -58,6 +59,7 @@ it('ignores changes to the updatedAt field', () => { const result = objectRecordChangedValues( oldRecord, newRecord, + [], mockObjectMetadata, ); @@ -79,6 +81,7 @@ it('returns an empty object when there are no changes', () => { const result = objectRecordChangedValues( oldRecord, newRecord, + ['name', 'value'], mockObjectMetadata, ); @@ -108,6 +111,7 @@ it('correctly handles a mix of changed, unchanged, and special case values', () const result = objectRecordChangedValues( oldRecord, newRecord, + ['name', 'config', 'status'], mockObjectMetadata, ); diff --git a/packages/twenty-server/src/engine/integrations/event-emitter/utils/object-record-changed-values.ts b/packages/twenty-server/src/engine/integrations/event-emitter/utils/object-record-changed-values.ts index 062693cbd..eaf3c4ebf 100644 --- a/packages/twenty-server/src/engine/integrations/event-emitter/utils/object-record-changed-values.ts +++ b/packages/twenty-server/src/engine/integrations/event-emitter/utils/object-record-changed-values.ts @@ -1,33 +1,36 @@ import deepEqual from 'deep-equal'; -import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; import { Record as IRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface'; +import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; export const objectRecordChangedValues = ( oldRecord: Partial, newRecord: Partial, + updatedKeys: string[], objectMetadata: ObjectMetadataInterface, ) => { + const fieldsByKey = new Map( + objectMetadata.fields.map((field) => [field.name, field]), + ); + const changedValues = Object.keys(newRecord).reduce( (acc, key) => { + const field = fieldsByKey.get(key); + const oldRecordValue = oldRecord[key]; + const newRecordValue = newRecord[key]; + if ( - objectMetadata.fields.find( - (field) => - field.type === FieldMetadataType.RELATION && field.name === key, - ) + key === 'updatedAt' || + !updatedKeys.includes(key) || + field?.type === FieldMetadataType.RELATION || + deepEqual(oldRecordValue, newRecordValue) ) { return acc; } - if (objectMetadata.nameSingular === 'activity' && key === 'body') { - return acc; - } - - if (!deepEqual(oldRecord[key], newRecord[key]) && key !== 'updatedAt') { - acc[key] = { before: oldRecord[key], after: newRecord[key] }; - } + acc[key] = { before: oldRecordValue, after: newRecordValue }; return acc; },