fix: reflect on #10521 changes to order-by-input-factory.ts file (#10662)

# This PR

Fixes an error on the FindMany REST API
I was getting the following error:
```
{
  "statusCode": 400,
  "error": "TypeError",
  "messages": [
    "Cannot read properties of undefined (reading 'fields')"
  ]
}
```
Now, it's working as expected
Related to #10521

cc: @Weiko @ijreilly

---------

Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
P A C · 先生
2025-04-17 16:06:53 +02:00
committed by GitHub
parent 42e060ac74
commit f40aafb89f
4 changed files with 79 additions and 9 deletions

View File

@ -2,6 +2,7 @@ import { FieldMetadataType } from 'twenty-shared/types';
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
export const FIELD_LINKS_MOCK_NAME = 'fieldLinks'; export const FIELD_LINKS_MOCK_NAME = 'fieldLinks';
export const FIELD_CURRENCY_MOCK_NAME = 'fieldCurrency'; export const FIELD_CURRENCY_MOCK_NAME = 'fieldCurrency';
@ -11,6 +12,7 @@ export const FIELD_FULL_NAME_MOCK_NAME = 'fieldFullName';
export const FIELD_PHONES_MOCK_NAME = 'fieldPhones'; export const FIELD_PHONES_MOCK_NAME = 'fieldPhones';
export const fieldNumberMock = { export const fieldNumberMock = {
id: 'fieldNumberId',
name: 'fieldNumber', name: 'fieldNumber',
type: FieldMetadataType.NUMBER, type: FieldMetadataType.NUMBER,
isNullable: false, isNullable: false,
@ -18,6 +20,7 @@ export const fieldNumberMock = {
}; };
export const fieldTextMock = { export const fieldTextMock = {
id: 'fieldTextId',
name: 'fieldText', name: 'fieldText',
type: FieldMetadataType.TEXT, type: FieldMetadataType.TEXT,
isNullable: true, isNullable: true,
@ -25,6 +28,7 @@ export const fieldTextMock = {
}; };
export const fieldCurrencyMock = { export const fieldCurrencyMock = {
id: 'fieldCurrencyId',
name: FIELD_CURRENCY_MOCK_NAME, name: FIELD_CURRENCY_MOCK_NAME,
type: FieldMetadataType.CURRENCY, type: FieldMetadataType.CURRENCY,
isNullable: true, isNullable: true,
@ -32,6 +36,7 @@ export const fieldCurrencyMock = {
}; };
export const fieldSelectMock = { export const fieldSelectMock = {
id: 'fieldSelectId',
name: 'fieldSelect', name: 'fieldSelect',
type: FieldMetadataType.SELECT, type: FieldMetadataType.SELECT,
isNullable: true, isNullable: true,
@ -55,6 +60,7 @@ export const fieldSelectMock = {
}; };
const fieldMultiSelectMock = { const fieldMultiSelectMock = {
id: 'fieldMultiSelectId',
name: 'fieldMultiSelect', name: 'fieldMultiSelect',
type: FieldMetadataType.MULTI_SELECT, type: FieldMetadataType.MULTI_SELECT,
isNullable: true, isNullable: true,
@ -78,6 +84,7 @@ const fieldMultiSelectMock = {
}; };
export const fieldRelationMock = { export const fieldRelationMock = {
id: 'fieldRelationId',
name: 'fieldRelation', name: 'fieldRelation',
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
fromRelationMetadata: { fromRelationMetadata: {
@ -90,6 +97,7 @@ export const fieldRelationMock = {
}; };
const fieldLinksMock = { const fieldLinksMock = {
id: 'fieldLinksId',
name: FIELD_LINKS_MOCK_NAME, name: FIELD_LINKS_MOCK_NAME,
type: FieldMetadataType.LINKS, type: FieldMetadataType.LINKS,
isNullable: false, isNullable: false,
@ -99,6 +107,7 @@ const fieldLinksMock = {
}; };
const fieldUuidMock = { const fieldUuidMock = {
id: 'fieldUuidId',
name: 'fieldUuid', name: 'fieldUuid',
type: FieldMetadataType.UUID, type: FieldMetadataType.UUID,
isNullable: true, isNullable: true,
@ -106,6 +115,7 @@ const fieldUuidMock = {
}; };
const fieldDateTimeMock = { const fieldDateTimeMock = {
id: 'fieldDateTimeId',
name: 'fieldDateTime', name: 'fieldDateTime',
type: FieldMetadataType.DATE_TIME, type: FieldMetadataType.DATE_TIME,
isNullable: true, isNullable: true,
@ -113,6 +123,7 @@ const fieldDateTimeMock = {
}; };
const fieldDateMock = { const fieldDateMock = {
id: 'fieldDateId',
name: 'fieldDate', name: 'fieldDate',
type: FieldMetadataType.DATE, type: FieldMetadataType.DATE,
isNullable: true, isNullable: true,
@ -120,6 +131,7 @@ const fieldDateMock = {
}; };
const fieldBooleanMock = { const fieldBooleanMock = {
id: 'fieldBooleanId',
name: 'fieldBoolean', name: 'fieldBoolean',
type: FieldMetadataType.BOOLEAN, type: FieldMetadataType.BOOLEAN,
isNullable: true, isNullable: true,
@ -127,6 +139,7 @@ const fieldBooleanMock = {
}; };
const fieldNumericMock = { const fieldNumericMock = {
id: 'fieldNumericId',
name: 'fieldNumeric', name: 'fieldNumeric',
type: FieldMetadataType.NUMERIC, type: FieldMetadataType.NUMERIC,
isNullable: true, isNullable: true,
@ -134,6 +147,7 @@ const fieldNumericMock = {
}; };
const fieldFullNameMock = { const fieldFullNameMock = {
id: 'fieldFullNameId',
name: FIELD_FULL_NAME_MOCK_NAME, name: FIELD_FULL_NAME_MOCK_NAME,
type: FieldMetadataType.FULL_NAME, type: FieldMetadataType.FULL_NAME,
isNullable: true, isNullable: true,
@ -141,6 +155,7 @@ const fieldFullNameMock = {
}; };
const fieldRatingMock = { const fieldRatingMock = {
id: 'fieldRatingId',
name: 'fieldRating', name: 'fieldRating',
type: FieldMetadataType.RATING, type: FieldMetadataType.RATING,
isNullable: true, isNullable: true,
@ -164,6 +179,7 @@ const fieldRatingMock = {
}; };
const fieldPositionMock = { const fieldPositionMock = {
id: 'fieldPositionId',
name: 'fieldPosition', name: 'fieldPosition',
type: FieldMetadataType.POSITION, type: FieldMetadataType.POSITION,
isNullable: true, isNullable: true,
@ -171,6 +187,7 @@ const fieldPositionMock = {
}; };
const fieldAddressMock = { const fieldAddressMock = {
id: 'fieldAddressId',
name: FIELD_ADDRESS_MOCK_NAME, name: FIELD_ADDRESS_MOCK_NAME,
type: FieldMetadataType.ADDRESS, type: FieldMetadataType.ADDRESS,
isNullable: true, isNullable: true,
@ -187,6 +204,7 @@ const fieldAddressMock = {
}; };
const fieldRawJsonMock = { const fieldRawJsonMock = {
id: 'fieldRawJsonId',
name: 'fieldRawJson', name: 'fieldRawJson',
type: FieldMetadataType.RAW_JSON, type: FieldMetadataType.RAW_JSON,
isNullable: true, isNullable: true,
@ -194,6 +212,7 @@ const fieldRawJsonMock = {
}; };
const fieldRichTextMock = { const fieldRichTextMock = {
id: 'fieldRichTextId',
name: 'fieldRichText', name: 'fieldRichText',
type: FieldMetadataType.RICH_TEXT, type: FieldMetadataType.RICH_TEXT,
isNullable: true, isNullable: true,
@ -201,6 +220,7 @@ const fieldRichTextMock = {
}; };
const fieldActorMock = { const fieldActorMock = {
id: 'fieldActorId',
name: FIELD_ACTOR_MOCK_NAME, name: FIELD_ACTOR_MOCK_NAME,
type: FieldMetadataType.ACTOR, type: FieldMetadataType.ACTOR,
isNullable: true, isNullable: true,
@ -211,6 +231,7 @@ const fieldActorMock = {
}; };
const fieldEmailsMock = { const fieldEmailsMock = {
id: 'fieldEmailsId',
name: 'fieldEmails', name: 'fieldEmails',
type: FieldMetadataType.EMAILS, type: FieldMetadataType.EMAILS,
isNullable: false, isNullable: false,
@ -218,6 +239,7 @@ const fieldEmailsMock = {
}; };
const fieldArrayMock = { const fieldArrayMock = {
id: 'fieldArrayId',
name: 'fieldArray', name: 'fieldArray',
type: FieldMetadataType.ARRAY, type: FieldMetadataType.ARRAY,
isNullable: true, isNullable: true,
@ -225,6 +247,7 @@ const fieldArrayMock = {
}; };
const fieldPhonesMock = { const fieldPhonesMock = {
id: 'fieldPhonesId',
name: FIELD_PHONES_MOCK_NAME, name: FIELD_PHONES_MOCK_NAME,
type: FieldMetadataType.PHONES, type: FieldMetadataType.PHONES,
isNullable: false, isNullable: false,
@ -265,7 +288,35 @@ export const fields = [
export const objectMetadataItemMock = { export const objectMetadataItemMock = {
targetTableName: 'testingObject', targetTableName: 'testingObject',
id: 'mockObjectId',
nameSingular: 'objectName', nameSingular: 'objectName',
namePlural: 'objectsName', namePlural: 'objectsName',
fields, fields,
} as ObjectMetadataEntity; } as ObjectMetadataEntity;
export const objectMetadataMapItemMock = {
id: 'mockObjectId',
nameSingular: 'objectName',
namePlural: 'objectsName',
fields,
fieldsById: fields.reduce((acc, field) => {
acc[field.id] = field;
return acc;
}, {}),
fieldsByName: fields.reduce((acc, field) => {
acc[field.name] = field;
return acc;
}, {}),
} as ObjectMetadataItemWithFieldMaps;
export const objectMetadataMapsMock = {
byId: {
[objectMetadataMapItemMock.id || 'mock-id']: objectMetadataMapItemMock,
},
idByNameSingular: {
[objectMetadataMapItemMock.nameSingular]:
objectMetadataMapItemMock.id || 'mock-id',
},
};

View File

@ -6,7 +6,7 @@ import {
fieldCurrencyMock, fieldCurrencyMock,
fieldNumberMock, fieldNumberMock,
fieldTextMock, fieldTextMock,
objectMetadataItemMock, objectMetadataMapItemMock,
} from 'src/engine/api/__mocks__/object-metadata-item.mock'; } from 'src/engine/api/__mocks__/object-metadata-item.mock';
import { FilterInputFactory } from 'src/engine/api/rest/input-factories/filter-input.factory'; import { FilterInputFactory } from 'src/engine/api/rest/input-factories/filter-input.factory';
import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map'; import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map';
@ -55,18 +55,18 @@ describe('FilterInputFactory', () => {
}; };
const objectMetadataMapItem = { const objectMetadataMapItem = {
...objectMetadataItemMock, ...objectMetadataMapItemMock,
fieldsById, fieldsById,
fieldsByName, fieldsByName,
}; };
const objectMetadataMaps = { const objectMetadataMaps = {
byId: { byId: {
[objectMetadataItemMock.id || 'mock-id']: objectMetadataMapItem, [objectMetadataMapItemMock.id || 'mock-id']: objectMetadataMapItem,
}, },
idByNameSingular: { idByNameSingular: {
[objectMetadataItemMock.nameSingular]: [objectMetadataMapItemMock.nameSingular]:
objectMetadataItemMock.id || 'mock-id', objectMetadataMapItemMock.id || 'mock-id',
}, },
}; };

View File

@ -2,11 +2,22 @@ import { Test, TestingModule } from '@nestjs/testing';
import { OrderByDirection } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; import { OrderByDirection } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
import { objectMetadataItemMock } from 'src/engine/api/__mocks__/object-metadata-item.mock'; import {
objectMetadataMapItemMock,
objectMetadataMapsMock,
} from 'src/engine/api/__mocks__/object-metadata-item.mock';
import { OrderByInputFactory } from 'src/engine/api/rest/input-factories/order-by-input.factory'; import { OrderByInputFactory } from 'src/engine/api/rest/input-factories/order-by-input.factory';
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
describe('OrderByInputFactory', () => { describe('OrderByInputFactory', () => {
const objectMetadata = { objectMetadataItem: objectMetadataItemMock }; const objectMetadata: {
objectMetadataMaps: ObjectMetadataMaps;
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
} = {
objectMetadataMaps: objectMetadataMapsMock,
objectMetadataMapItem: objectMetadataMapItemMock,
};
let service: OrderByInputFactory; let service: OrderByInputFactory;

View File

@ -8,12 +8,20 @@ import {
} from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
import { checkArrayFields } from 'src/engine/api/rest/core/query-builder/utils/check-order-by.utils'; import { checkArrayFields } from 'src/engine/api/rest/core/query-builder/utils/check-order-by.utils';
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
export const DEFAULT_ORDER_DIRECTION = OrderByDirection.AscNullsFirst; export const DEFAULT_ORDER_DIRECTION = OrderByDirection.AscNullsFirst;
@Injectable() @Injectable()
export class OrderByInputFactory { export class OrderByInputFactory {
create(request: Request, objectMetadata): ObjectRecordOrderBy { create(
request: Request,
objectMetadata: {
objectMetadataMaps: ObjectMetadataMaps;
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
},
): ObjectRecordOrderBy {
const orderByQuery = request.query.order_by; const orderByQuery = request.query.order_by;
if (typeof orderByQuery !== 'string') { if (typeof orderByQuery !== 'string') {
@ -72,7 +80,7 @@ export class OrderByInputFactory {
result = [...result, ...resultFields]; result = [...result, ...resultFields];
} }
checkArrayFields(objectMetadata.objectMetadataItem, result); checkArrayFields(objectMetadata.objectMetadataMapItem, result);
return result; return result;
} }