fix: fix Apollo client cache update error for Links field (#5473)
Fixes #5437
This commit is contained in:
@ -0,0 +1,80 @@
|
|||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import {
|
||||||
|
mockedObjectMetadataItems,
|
||||||
|
mockedPersonObjectMetadataItem,
|
||||||
|
} from '~/testing/mock-data/metadata';
|
||||||
|
import { mockedPeopleData } from '~/testing/mock-data/people';
|
||||||
|
|
||||||
|
import { getRecordNodeFromRecord } from '../getRecordNodeFromRecord';
|
||||||
|
|
||||||
|
describe('getRecordNodeFromRecord', () => {
|
||||||
|
it('computes relation records cache references by default', () => {
|
||||||
|
// Given
|
||||||
|
const objectMetadataItems: ObjectMetadataItem[] = mockedObjectMetadataItems;
|
||||||
|
const objectMetadataItem: Pick<
|
||||||
|
ObjectMetadataItem,
|
||||||
|
'fields' | 'namePlural' | 'nameSingular'
|
||||||
|
> = mockedPersonObjectMetadataItem;
|
||||||
|
const recordGqlFields = {
|
||||||
|
name: true,
|
||||||
|
company: true,
|
||||||
|
};
|
||||||
|
const record = mockedPeopleData[0];
|
||||||
|
|
||||||
|
// When
|
||||||
|
const result = getRecordNodeFromRecord({
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
recordGqlFields,
|
||||||
|
record,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
expect(result).toEqual({
|
||||||
|
__typename: 'Person',
|
||||||
|
company: {
|
||||||
|
__ref: 'Company:5c21e19e-e049-4393-8c09-3e3f8fb09ecb',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
__typename: 'FullName',
|
||||||
|
firstName: 'Alexandre',
|
||||||
|
lastName: 'Prot',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not compute relation records cache references when `computeReferences` is false', () => {
|
||||||
|
// Given
|
||||||
|
const objectMetadataItems: ObjectMetadataItem[] = mockedObjectMetadataItems;
|
||||||
|
const objectMetadataItem: Pick<
|
||||||
|
ObjectMetadataItem,
|
||||||
|
'fields' | 'namePlural' | 'nameSingular'
|
||||||
|
> = mockedPersonObjectMetadataItem;
|
||||||
|
const recordGqlFields = {
|
||||||
|
name: true,
|
||||||
|
company: true,
|
||||||
|
};
|
||||||
|
const record = mockedPeopleData[0];
|
||||||
|
const computeReferences = false;
|
||||||
|
|
||||||
|
// When
|
||||||
|
const result = getRecordNodeFromRecord({
|
||||||
|
objectMetadataItems,
|
||||||
|
objectMetadataItem,
|
||||||
|
recordGqlFields,
|
||||||
|
record,
|
||||||
|
computeReferences,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
expect(result).toEqual({
|
||||||
|
__typename: 'Person',
|
||||||
|
company: record.company,
|
||||||
|
name: {
|
||||||
|
__typename: 'FullName',
|
||||||
|
firstName: 'Alexandre',
|
||||||
|
lastName: 'Prot',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -11,7 +11,7 @@ import {
|
|||||||
RelationDefinitionType,
|
RelationDefinitionType,
|
||||||
} from '~/generated-metadata/graphql';
|
} from '~/generated-metadata/graphql';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { lowerAndCapitalize } from '~/utils/string/lowerAndCapitalize';
|
import { pascalCase } from '~/utils/string/pascalCase';
|
||||||
|
|
||||||
export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
@ -129,6 +129,7 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
case FieldMetadataType.Link:
|
case FieldMetadataType.Link:
|
||||||
|
case FieldMetadataType.Links:
|
||||||
case FieldMetadataType.Address:
|
case FieldMetadataType.Address:
|
||||||
case FieldMetadataType.FullName:
|
case FieldMetadataType.FullName:
|
||||||
case FieldMetadataType.Currency: {
|
case FieldMetadataType.Currency: {
|
||||||
@ -136,7 +137,7 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
|||||||
fieldName,
|
fieldName,
|
||||||
{
|
{
|
||||||
...value,
|
...value,
|
||||||
__typename: lowerAndCapitalize(field.type),
|
__typename: pascalCase(field.type),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,11 +49,11 @@ export const responseData = {
|
|||||||
personDuplicates: {
|
personDuplicates: {
|
||||||
edges: [
|
edges: [
|
||||||
{
|
{
|
||||||
node: { __typename: 'Person', ...mockedPeopleData[0], updatedAt: '' },
|
node: { ...mockedPeopleData[0], updatedAt: '' },
|
||||||
cursor: 'cursor1',
|
cursor: 'cursor1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: { __typename: 'Person', ...mockedPeopleData[1], updatedAt: '' },
|
node: { ...mockedPeopleData[1], updatedAt: '' },
|
||||||
cursor: 'cursor2',
|
cursor: 'cursor2',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export const generateEmptyFieldValue = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
case FieldMetadataType.Links: {
|
case FieldMetadataType.Links: {
|
||||||
return { primaryLinkUrl: '', primaryLinkLabel: '' };
|
return { primaryLinkUrl: '', primaryLinkLabel: '', secondaryLinks: null };
|
||||||
}
|
}
|
||||||
case FieldMetadataType.FullName: {
|
case FieldMetadataType.FullName: {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ type RequiredAndNotNull<T> = {
|
|||||||
type MockedPerson = RequiredAndNotNull<
|
type MockedPerson = RequiredAndNotNull<
|
||||||
Pick<
|
Pick<
|
||||||
Person,
|
Person,
|
||||||
|
| '__typename'
|
||||||
| 'id'
|
| 'id'
|
||||||
| 'name'
|
| 'name'
|
||||||
| 'linkedinLink'
|
| 'linkedinLink'
|
||||||
@ -20,12 +21,13 @@ type MockedPerson = RequiredAndNotNull<
|
|||||||
| 'createdAt'
|
| 'createdAt'
|
||||||
| 'companyId'
|
| 'companyId'
|
||||||
> & {
|
> & {
|
||||||
company: Pick<Company, 'id' | 'name' | 'domainName'>;
|
company: Pick<Company, '__typename' | 'id' | 'name' | 'domainName'>;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const mockedPeopleData: MockedPerson[] = [
|
export const mockedPeopleData: MockedPerson[] = [
|
||||||
{
|
{
|
||||||
|
__typename: 'Person',
|
||||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
|
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
|
||||||
name: {
|
name: {
|
||||||
firstName: 'Alexandre',
|
firstName: 'Alexandre',
|
||||||
@ -45,6 +47,7 @@ export const mockedPeopleData: MockedPerson[] = [
|
|||||||
jobTitle: 'CEO',
|
jobTitle: 'CEO',
|
||||||
companyId: '5c21e19e-e049-4393-8c09-3e3f8fb09ecb',
|
companyId: '5c21e19e-e049-4393-8c09-3e3f8fb09ecb',
|
||||||
company: {
|
company: {
|
||||||
|
__typename: 'Company',
|
||||||
id: '5c21e19e-e049-4393-8c09-3e3f8fb09ecb',
|
id: '5c21e19e-e049-4393-8c09-3e3f8fb09ecb',
|
||||||
name: 'Qonto',
|
name: 'Qonto',
|
||||||
domainName: 'qonto.com',
|
domainName: 'qonto.com',
|
||||||
@ -54,6 +57,7 @@ export const mockedPeopleData: MockedPerson[] = [
|
|||||||
city: 'Paris',
|
city: 'Paris',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
__typename: 'Person',
|
||||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6d',
|
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6d',
|
||||||
name: { firstName: 'John', lastName: 'Doe' },
|
name: { firstName: 'John', lastName: 'Doe' },
|
||||||
linkedinLink: {
|
linkedinLink: {
|
||||||
@ -69,6 +73,7 @@ export const mockedPeopleData: MockedPerson[] = [
|
|||||||
email: 'john@linkedin.com',
|
email: 'john@linkedin.com',
|
||||||
companyId: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6e',
|
companyId: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6e',
|
||||||
company: {
|
company: {
|
||||||
|
__typename: 'Company',
|
||||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6e',
|
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6e',
|
||||||
name: 'LinkedIn',
|
name: 'LinkedIn',
|
||||||
domainName: 'linkedin.com',
|
domainName: 'linkedin.com',
|
||||||
@ -78,6 +83,7 @@ export const mockedPeopleData: MockedPerson[] = [
|
|||||||
city: 'Paris',
|
city: 'Paris',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
__typename: 'Person',
|
||||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6f',
|
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6f',
|
||||||
name: {
|
name: {
|
||||||
firstName: 'Jane',
|
firstName: 'Jane',
|
||||||
@ -96,6 +102,7 @@ export const mockedPeopleData: MockedPerson[] = [
|
|||||||
email: 'jane@sequoiacap.com',
|
email: 'jane@sequoiacap.com',
|
||||||
companyId: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6g',
|
companyId: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6g',
|
||||||
company: {
|
company: {
|
||||||
|
__typename: 'Company',
|
||||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6g',
|
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6g',
|
||||||
name: 'Sequoia',
|
name: 'Sequoia',
|
||||||
domainName: 'sequoiacap.com',
|
domainName: 'sequoiacap.com',
|
||||||
@ -105,6 +112,7 @@ export const mockedPeopleData: MockedPerson[] = [
|
|||||||
city: 'Paris',
|
city: 'Paris',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
__typename: 'Person',
|
||||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6h',
|
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6h',
|
||||||
name: {
|
name: {
|
||||||
firstName: 'Janice',
|
firstName: 'Janice',
|
||||||
@ -123,6 +131,7 @@ export const mockedPeopleData: MockedPerson[] = [
|
|||||||
jobTitle: 'CEO',
|
jobTitle: 'CEO',
|
||||||
companyId: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6i',
|
companyId: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6i',
|
||||||
company: {
|
company: {
|
||||||
|
__typename: 'Company',
|
||||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6i',
|
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6i',
|
||||||
name: 'Facebook',
|
name: 'Facebook',
|
||||||
domainName: 'facebook.com',
|
domainName: 'facebook.com',
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
import { pascalCase } from '../pascalCase';
|
||||||
|
|
||||||
|
describe('pascalCase', () => {
|
||||||
|
it('converts a string to pascal case', () => {
|
||||||
|
// Given
|
||||||
|
const input = 'HELLO_WORLD';
|
||||||
|
|
||||||
|
// When
|
||||||
|
const result = pascalCase(input);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
expect(result).toBe('HelloWorld');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles empty strings', () => {
|
||||||
|
// Given
|
||||||
|
const input = '';
|
||||||
|
|
||||||
|
// When
|
||||||
|
const result = pascalCase(input);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
expect(result).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles strings with only one word', () => {
|
||||||
|
// Given
|
||||||
|
const input = 'hello';
|
||||||
|
|
||||||
|
// When
|
||||||
|
const result = pascalCase(input);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
expect(result).toBe('Hello');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles strings with several words, spaces and special characters', () => {
|
||||||
|
// Given
|
||||||
|
const input = '& Hello world! How are you today? #';
|
||||||
|
|
||||||
|
// When
|
||||||
|
const result = pascalCase(input);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
expect(result).toBe('HelloWorldHowAreYouToday');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles strings with leading and trailing spaces', () => {
|
||||||
|
// Given
|
||||||
|
const input = ' hello_world ';
|
||||||
|
|
||||||
|
// When
|
||||||
|
const result = pascalCase(input);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
expect(result).toBe('HelloWorld');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import { isNonEmptyString } from '@sniptt/guards';
|
|
||||||
|
|
||||||
export const lowerAndCapitalize = (stringToCapitalize: string) => {
|
|
||||||
if (!isNonEmptyString(stringToCapitalize)) return '';
|
|
||||||
|
|
||||||
const loweredString = stringToCapitalize.toLowerCase();
|
|
||||||
|
|
||||||
return loweredString[0].toUpperCase() + loweredString.slice(1);
|
|
||||||
};
|
|
||||||
5
packages/twenty-front/src/utils/string/pascalCase.ts
Normal file
5
packages/twenty-front/src/utils/string/pascalCase.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import camelCase from 'lodash.camelcase';
|
||||||
|
|
||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
|
export const pascalCase = (str: string) => capitalize(camelCase(str));
|
||||||
Reference in New Issue
Block a user