-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor
triggerUpdateRelationsOptimisticEffect
to compute relation…
…ship from Metadatas (#9815) # Introduction At the moment the relationships are inferred from the record data structure instead of its metadatas We should refactor the code that computes or not the necessity to detach a relation on a mutation We've refactored the `isObjectRecordConnection` method to be consuming a `relationDefintion` instead of "typeChecking" at the runtime the data structure using zod validation schema Related to #9580
- Loading branch information
Showing
3 changed files
with
99 additions
and
110 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 31 additions & 20 deletions
51
...ty-front/src/modules/object-record/cache/utils/__tests__/isObjectRecordConnection.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,38 @@ | ||
import { peopleQueryResult } from '~/testing/mock-data/people'; | ||
|
||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; | ||
import { isObjectRecordConnection } from '@/object-record/cache/utils/isObjectRecordConnection'; | ||
|
||
import { RelationDefinitionType } from '~/generated-metadata/graphql'; | ||
describe('isObjectRecordConnection', () => { | ||
it('should work with query result', () => { | ||
const validQueryResult = peopleQueryResult.people; | ||
|
||
const isValidQueryResult = isObjectRecordConnection( | ||
'person', | ||
validQueryResult, | ||
); | ||
|
||
expect(isValidQueryResult).toEqual(true); | ||
}); | ||
const relationDefinitionMap: { [K in RelationDefinitionType]: boolean } = { | ||
[RelationDefinitionType.MANY_TO_MANY]: true, | ||
[RelationDefinitionType.ONE_TO_MANY]: true, | ||
[RelationDefinitionType.MANY_TO_ONE]: false, | ||
[RelationDefinitionType.ONE_TO_ONE]: false, | ||
}; | ||
|
||
it('should fail with invalid result', () => { | ||
const invalidResult = { test: 123 }; | ||
it.each(Object.entries(relationDefinitionMap))( | ||
'.$relation', | ||
(relation, expected) => { | ||
const emptyRecord = {}; | ||
const result = isObjectRecordConnection( | ||
{ | ||
direction: relation, | ||
} as NonNullable<FieldMetadataItem['relationDefinition']>, | ||
emptyRecord, | ||
); | ||
|
||
const isValidQueryResult = isObjectRecordConnection( | ||
'person', | ||
invalidResult, | ||
); | ||
expect(result).toEqual(expected); | ||
}, | ||
); | ||
|
||
expect(isValidQueryResult).toEqual(false); | ||
it('should throw on unknown relation direction', () => { | ||
const emptyRecord = {}; | ||
expect(() => | ||
isObjectRecordConnection( | ||
{ | ||
direction: 'UNKNOWN_TYPE', | ||
} as any, | ||
emptyRecord, | ||
), | ||
).toThrowError(); | ||
}); | ||
}); |
41 changes: 17 additions & 24 deletions
41
packages/twenty-front/src/modules/object-record/cache/utils/isObjectRecordConnection.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,23 @@ | ||
import { z } from 'zod'; | ||
|
||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; | ||
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection'; | ||
import { capitalize } from 'twenty-shared'; | ||
import { assertUnreachable } from '@/workflow/utils/assertUnreachable'; | ||
import { RelationDefinitionType } from '~/generated-metadata/graphql'; | ||
|
||
export const isObjectRecordConnection = ( | ||
objectNameSingular: string, | ||
relationDefinition: NonNullable<FieldMetadataItem['relationDefinition']>, | ||
value: unknown, | ||
): value is RecordGqlConnection => { | ||
const objectConnectionTypeName = `${capitalize( | ||
objectNameSingular, | ||
)}Connection`; | ||
const objectEdgeTypeName = `${capitalize(objectNameSingular)}Edge`; | ||
|
||
const objectConnectionSchema = z.object({ | ||
__typename: z.literal(objectConnectionTypeName).optional(), | ||
edges: z.array( | ||
z.object({ | ||
__typename: z.literal(objectEdgeTypeName).optional(), | ||
node: z.object({ | ||
id: z.string().uuid(), | ||
}), | ||
}), | ||
), | ||
}); | ||
|
||
const connectionValidation = objectConnectionSchema.safeParse(value); | ||
|
||
return connectionValidation.success; | ||
switch (relationDefinition.direction) { | ||
case RelationDefinitionType.MANY_TO_MANY: | ||
case RelationDefinitionType.ONE_TO_MANY: { | ||
return true; | ||
} | ||
case RelationDefinitionType.MANY_TO_ONE: | ||
case RelationDefinitionType.ONE_TO_ONE: { | ||
return false; | ||
} | ||
default: { | ||
return assertUnreachable(relationDefinition.direction); | ||
} | ||
} | ||
}; |