Fix REST API not using metadata cache (#10521)
This commit is contained in:
@ -19,9 +19,12 @@ import { parseCoreBatchPath } from 'src/engine/api/rest/core/query-builder/utils
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { Query } from 'src/engine/api/rest/core/types/query.type';
|
||||
import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
||||
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';
|
||||
import { getObjectMetadataMapItemByNamePlural } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-plural.util';
|
||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
@Injectable()
|
||||
export class CoreQueryBuilderFactory {
|
||||
@ -38,25 +41,35 @@ export class CoreQueryBuilderFactory {
|
||||
private readonly updateVariablesFactory: UpdateVariablesFactory,
|
||||
private readonly getVariablesFactory: GetVariablesFactory,
|
||||
private readonly findDuplicatesVariablesFactory: FindDuplicatesVariablesFactory,
|
||||
private readonly objectMetadataService: ObjectMetadataService,
|
||||
private readonly accessTokenService: AccessTokenService,
|
||||
private readonly domainManagerService: DomainManagerService,
|
||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||
) {}
|
||||
|
||||
async getObjectMetadata(
|
||||
request: Request,
|
||||
parsedObject: string,
|
||||
): Promise<{
|
||||
objectMetadataItems: ObjectMetadataEntity[];
|
||||
objectMetadataItem: ObjectMetadataEntity;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
}> {
|
||||
const { workspace } =
|
||||
await this.accessTokenService.validateTokenByRequest(request);
|
||||
|
||||
const objectMetadataItems =
|
||||
await this.objectMetadataService.findManyWithinWorkspace(workspace.id);
|
||||
const currentCacheVersion =
|
||||
await this.workspaceCacheStorageService.getMetadataVersion(workspace.id);
|
||||
|
||||
if (!objectMetadataItems.length) {
|
||||
if (currentCacheVersion === undefined) {
|
||||
throw new BadRequestException('No cacheVersion');
|
||||
}
|
||||
|
||||
const objectMetadataMaps =
|
||||
await this.workspaceCacheStorageService.getObjectMetadataMaps(
|
||||
workspace.id,
|
||||
currentCacheVersion,
|
||||
);
|
||||
|
||||
if (!objectMetadataMaps) {
|
||||
throw new BadRequestException(
|
||||
`No object was found for the workspace associated with this API key. You may generate a new one here ${this.domainManagerService
|
||||
.buildWorkspaceURL({
|
||||
@ -67,19 +80,21 @@ export class CoreQueryBuilderFactory {
|
||||
);
|
||||
}
|
||||
|
||||
const [objectMetadata] = objectMetadataItems.filter(
|
||||
(object) => object.namePlural === parsedObject,
|
||||
const objectMetadataItem = getObjectMetadataMapItemByNamePlural(
|
||||
objectMetadataMaps,
|
||||
parsedObject,
|
||||
);
|
||||
|
||||
if (!objectMetadata) {
|
||||
const [wrongObjectMetadata] = objectMetadataItems.filter(
|
||||
(object) => object.nameSingular === parsedObject,
|
||||
if (!objectMetadataItem) {
|
||||
const wrongObjectMetadataItem = getObjectMetadataMapItemByNameSingular(
|
||||
objectMetadataMaps,
|
||||
parsedObject,
|
||||
);
|
||||
|
||||
let hint = 'eg: companies';
|
||||
|
||||
if (wrongObjectMetadata) {
|
||||
hint = `Did you mean '${wrongObjectMetadata.namePlural}'?`;
|
||||
if (wrongObjectMetadataItem) {
|
||||
hint = `Did you mean '${wrongObjectMetadataItem.namePlural}'?`;
|
||||
}
|
||||
|
||||
throw new BadRequestException(
|
||||
@ -88,8 +103,8 @@ export class CoreQueryBuilderFactory {
|
||||
}
|
||||
|
||||
return {
|
||||
objectMetadataItems,
|
||||
objectMetadataItem: objectMetadata,
|
||||
objectMetadataMaps,
|
||||
objectMetadataMapItem: objectMetadataItem,
|
||||
};
|
||||
}
|
||||
|
||||
@ -101,12 +116,14 @@ export class CoreQueryBuilderFactory {
|
||||
|
||||
if (!id) {
|
||||
throw new BadRequestException(
|
||||
`delete ${objectMetadata.objectMetadataItem.nameSingular} query invalid. Id missing. eg: /rest/${objectMetadata.objectMetadataItem.namePlural}/0d4389ef-ea9c-4ae8-ada1-1cddc440fb56`,
|
||||
`delete ${objectMetadata.objectMetadataMapItem.nameSingular} query invalid. Id missing. eg: /rest/${objectMetadata.objectMetadataMapItem.namePlural}/0d4389ef-ea9c-4ae8-ada1-1cddc440fb56`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
query: this.deleteQueryFactory.create(objectMetadata.objectMetadataItem),
|
||||
query: this.deleteQueryFactory.create(
|
||||
objectMetadata.objectMetadataMapItem,
|
||||
),
|
||||
variables: this.deleteVariablesFactory.create(id),
|
||||
};
|
||||
}
|
||||
@ -144,7 +161,7 @@ export class CoreQueryBuilderFactory {
|
||||
|
||||
if (!id) {
|
||||
throw new BadRequestException(
|
||||
`update ${objectMetadata.objectMetadataItem.nameSingular} query invalid. Id missing. eg: /rest/${objectMetadata.objectMetadataItem.namePlural}/0d4389ef-ea9c-4ae8-ada1-1cddc440fb56`,
|
||||
`update ${objectMetadata.objectMetadataMapItem.nameSingular} query invalid. Id missing. eg: /rest/${objectMetadata.objectMetadataMapItem.namePlural}/0d4389ef-ea9c-4ae8-ada1-1cddc440fb56`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -2,12 +2,12 @@ import { Module } from '@nestjs/common';
|
||||
|
||||
import { CoreQueryBuilderFactory } from 'src/engine/api/rest/core/query-builder/core-query-builder.factory';
|
||||
import { coreQueryBuilderFactories } from 'src/engine/api/rest/core/query-builder/factories/factories';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
|
||||
import { DomainManagerModule } from 'src/engine/core-modules/domain-manager/domain-manager.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
|
||||
@Module({
|
||||
imports: [ObjectMetadataModule, AuthModule, DomainManagerModule],
|
||||
imports: [AuthModule, DomainManagerModule, WorkspaceCacheStorageModule],
|
||||
providers: [...coreQueryBuilderFactories, CoreQueryBuilderFactory],
|
||||
exports: [CoreQueryBuilderFactory],
|
||||
})
|
||||
|
||||
@ -3,25 +3,33 @@ import { Injectable } from '@nestjs/common';
|
||||
import { capitalize } from 'twenty-shared';
|
||||
|
||||
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.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';
|
||||
|
||||
@Injectable()
|
||||
export class CreateManyQueryFactory {
|
||||
create(objectMetadata, depth?: number): string {
|
||||
create(
|
||||
objectMetadata: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
},
|
||||
depth?: number,
|
||||
): string {
|
||||
const objectNamePlural = capitalize(
|
||||
objectMetadata.objectMetadataItem.namePlural,
|
||||
objectMetadata.objectMetadataMapItem.namePlural,
|
||||
);
|
||||
const objectNameSingular = capitalize(
|
||||
objectMetadata.objectMetadataItem.nameSingular,
|
||||
objectMetadata.objectMetadataMapItem.nameSingular,
|
||||
);
|
||||
|
||||
return `
|
||||
mutation Create${objectNamePlural}($data: [${objectNameSingular}CreateInput!]) {
|
||||
create${objectNamePlural}(data: $data) {
|
||||
id
|
||||
${objectMetadata.objectMetadataItem.fields
|
||||
${objectMetadata.objectMetadataMapItem.fields
|
||||
.map((field) =>
|
||||
mapFieldMetadataToGraphqlQuery(
|
||||
objectMetadata.objectMetadataItems,
|
||||
objectMetadata.objectMetadataMaps,
|
||||
field,
|
||||
depth,
|
||||
),
|
||||
|
||||
@ -3,22 +3,30 @@ import { Injectable } from '@nestjs/common';
|
||||
import { capitalize } from 'twenty-shared';
|
||||
|
||||
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.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';
|
||||
|
||||
@Injectable()
|
||||
export class CreateOneQueryFactory {
|
||||
create(objectMetadata, depth?: number): string {
|
||||
create(
|
||||
objectMetadata: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
},
|
||||
depth?: number,
|
||||
): string {
|
||||
const objectNameSingular = capitalize(
|
||||
objectMetadata.objectMetadataItem.nameSingular,
|
||||
objectMetadata.objectMetadataMapItem.nameSingular,
|
||||
);
|
||||
|
||||
return `
|
||||
mutation Create${objectNameSingular}($data: ${objectNameSingular}CreateInput!) {
|
||||
create${objectNameSingular}(data: $data) {
|
||||
id
|
||||
${objectMetadata.objectMetadataItem.fields
|
||||
${objectMetadata.objectMetadataMapItem.fields
|
||||
.map((field) =>
|
||||
mapFieldMetadataToGraphqlQuery(
|
||||
objectMetadata.objectMetadataItems,
|
||||
objectMetadata.objectMetadataMaps,
|
||||
field,
|
||||
depth,
|
||||
),
|
||||
|
||||
@ -2,10 +2,12 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { capitalize } from 'twenty-shared';
|
||||
|
||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteQueryFactory {
|
||||
create(objectMetadataItem): string {
|
||||
const objectNameSingular = capitalize(objectMetadataItem.nameSingular);
|
||||
create(objectMetadataMapItem: ObjectMetadataItemWithFieldMaps): string {
|
||||
const objectNameSingular = capitalize(objectMetadataMapItem.nameSingular);
|
||||
|
||||
return `
|
||||
mutation Delete${objectNameSingular}($id: ID!) {
|
||||
|
||||
@ -3,11 +3,20 @@ import { Injectable } from '@nestjs/common';
|
||||
import { capitalize } from 'twenty-shared';
|
||||
|
||||
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.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';
|
||||
|
||||
@Injectable()
|
||||
export class FindDuplicatesQueryFactory {
|
||||
create(objectMetadata, depth?: number): string {
|
||||
const objectNameSingular = objectMetadata.objectMetadataItem.nameSingular;
|
||||
create(
|
||||
objectMetadata: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
},
|
||||
depth?: number,
|
||||
): string {
|
||||
const objectNameSingular =
|
||||
objectMetadata.objectMetadataMapItem.nameSingular;
|
||||
|
||||
return `
|
||||
query FindDuplicate${capitalize(
|
||||
@ -22,10 +31,10 @@ export class FindDuplicatesQueryFactory {
|
||||
}
|
||||
edges{
|
||||
node {
|
||||
${objectMetadata.objectMetadataItem.fields
|
||||
${objectMetadata.objectMetadataMapItem.fields
|
||||
.map((field) =>
|
||||
mapFieldMetadataToGraphqlQuery(
|
||||
objectMetadata.objectMetadataItems,
|
||||
objectMetadata.objectMetadataMaps,
|
||||
field,
|
||||
depth,
|
||||
),
|
||||
|
||||
@ -3,14 +3,22 @@ import { Injectable } from '@nestjs/common';
|
||||
import { capitalize } from 'twenty-shared';
|
||||
|
||||
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.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';
|
||||
|
||||
@Injectable()
|
||||
export class FindManyQueryFactory {
|
||||
create(objectMetadata, depth?: number): string {
|
||||
create(
|
||||
objectMetadata: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
},
|
||||
depth?: number,
|
||||
): string {
|
||||
const objectNameSingular = capitalize(
|
||||
objectMetadata.objectMetadataItem.nameSingular,
|
||||
objectMetadata.objectMetadataMapItem.nameSingular,
|
||||
);
|
||||
const objectNamePlural = objectMetadata.objectMetadataItem.namePlural;
|
||||
const objectNamePlural = objectMetadata.objectMetadataMapItem.namePlural;
|
||||
|
||||
return `
|
||||
query FindMany${capitalize(objectNamePlural)}(
|
||||
@ -32,10 +40,10 @@ export class FindManyQueryFactory {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
${objectMetadata.objectMetadataItem.fields
|
||||
${objectMetadata.objectMetadataMapItem.fields
|
||||
.map((field) =>
|
||||
mapFieldMetadataToGraphqlQuery(
|
||||
objectMetadata.objectMetadataItems,
|
||||
objectMetadata.objectMetadataMaps,
|
||||
field,
|
||||
depth,
|
||||
),
|
||||
|
||||
@ -3,11 +3,20 @@ import { Injectable } from '@nestjs/common';
|
||||
import { capitalize } from 'twenty-shared';
|
||||
|
||||
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.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';
|
||||
|
||||
@Injectable()
|
||||
export class FindOneQueryFactory {
|
||||
create(objectMetadata, depth?: number): string {
|
||||
const objectNameSingular = objectMetadata.objectMetadataItem.nameSingular;
|
||||
create(
|
||||
objectMetadata: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
},
|
||||
depth?: number,
|
||||
): string {
|
||||
const objectNameSingular =
|
||||
objectMetadata.objectMetadataMapItem.nameSingular;
|
||||
|
||||
return `
|
||||
query FindOne${capitalize(objectNameSingular)}(
|
||||
@ -15,10 +24,10 @@ export class FindOneQueryFactory {
|
||||
) {
|
||||
${objectNameSingular}(filter: $filter) {
|
||||
id
|
||||
${objectMetadata.objectMetadataItem.fields
|
||||
${objectMetadata.objectMetadataMapItem.fields
|
||||
.map((field) =>
|
||||
mapFieldMetadataToGraphqlQuery(
|
||||
objectMetadata.objectMetadataItems,
|
||||
objectMetadata.objectMetadataMaps,
|
||||
field,
|
||||
depth,
|
||||
),
|
||||
|
||||
@ -3,11 +3,20 @@ import { Injectable } from '@nestjs/common';
|
||||
import { capitalize } from 'twenty-shared';
|
||||
|
||||
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.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';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateQueryFactory {
|
||||
create(objectMetadata, depth?: number): string {
|
||||
const objectNameSingular = objectMetadata.objectMetadataItem.nameSingular;
|
||||
create(
|
||||
objectMetadata: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
},
|
||||
depth?: number,
|
||||
): string {
|
||||
const objectNameSingular =
|
||||
objectMetadata.objectMetadataMapItem.nameSingular;
|
||||
|
||||
return `
|
||||
mutation Update${capitalize(
|
||||
@ -15,10 +24,10 @@ export class UpdateQueryFactory {
|
||||
)}($id: ID!, $data: ${capitalize(objectNameSingular)}UpdateInput!) {
|
||||
update${capitalize(objectNameSingular)}(id: $id, data: $data) {
|
||||
id
|
||||
${objectMetadata.objectMetadataItem.fields
|
||||
${Object.values(objectMetadata.objectMetadataMapItem.fieldsById)
|
||||
.map((field) =>
|
||||
mapFieldMetadataToGraphqlQuery(
|
||||
objectMetadata.objectMetadataItems,
|
||||
objectMetadata.objectMetadataMaps,
|
||||
field,
|
||||
depth,
|
||||
),
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { FieldMetadataType } from 'twenty-shared';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import {
|
||||
fieldCurrencyMock,
|
||||
fieldNumberMock,
|
||||
@ -8,19 +10,85 @@ import {
|
||||
} from 'src/engine/api/__mocks__/object-metadata-item.mock';
|
||||
import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map';
|
||||
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('mapFieldMetadataToGraphqlQuery', () => {
|
||||
const typedFieldNumberMock: FieldMetadataInterface = {
|
||||
id: 'field-number-id',
|
||||
name: fieldNumberMock.name,
|
||||
type: fieldNumberMock.type,
|
||||
label: 'Field Number',
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
isNullable: fieldNumberMock.isNullable,
|
||||
defaultValue: fieldNumberMock.defaultValue,
|
||||
};
|
||||
|
||||
const typedFieldTextMock: FieldMetadataInterface = {
|
||||
id: 'field-text-id',
|
||||
name: fieldTextMock.name,
|
||||
type: fieldTextMock.type,
|
||||
label: 'Field Text',
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
isNullable: fieldTextMock.isNullable,
|
||||
defaultValue: fieldTextMock.defaultValue,
|
||||
};
|
||||
|
||||
const typedFieldCurrencyMock: FieldMetadataInterface = {
|
||||
id: 'field-currency-id',
|
||||
name: fieldCurrencyMock.name,
|
||||
type: fieldCurrencyMock.type,
|
||||
label: 'Field Currency',
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
isNullable: fieldCurrencyMock.isNullable,
|
||||
defaultValue: fieldCurrencyMock.defaultValue,
|
||||
};
|
||||
|
||||
const fieldsById: FieldMetadataMap = {
|
||||
'field-number-id': typedFieldNumberMock,
|
||||
'field-text-id': typedFieldTextMock,
|
||||
'field-currency-id': typedFieldCurrencyMock,
|
||||
};
|
||||
|
||||
const fieldsByName: FieldMetadataMap = {
|
||||
[typedFieldNumberMock.name]: typedFieldNumberMock,
|
||||
[typedFieldTextMock.name]: typedFieldTextMock,
|
||||
[typedFieldCurrencyMock.name]: typedFieldCurrencyMock,
|
||||
};
|
||||
|
||||
const typedObjectMetadataItem: ObjectMetadataItemWithFieldMaps = {
|
||||
...objectMetadataItemMock,
|
||||
fieldsById,
|
||||
fieldsByName,
|
||||
};
|
||||
|
||||
const objectMetadataMapsMock: ObjectMetadataMaps = {
|
||||
byId: {
|
||||
[objectMetadataItemMock.id]: typedObjectMetadataItem,
|
||||
},
|
||||
idByNameSingular: {
|
||||
[objectMetadataItemMock.nameSingular]: objectMetadataItemMock.id,
|
||||
},
|
||||
};
|
||||
|
||||
it('should map properly', () => {
|
||||
expect(
|
||||
mapFieldMetadataToGraphqlQuery([objectMetadataItemMock], fieldNumberMock),
|
||||
mapFieldMetadataToGraphqlQuery(
|
||||
objectMetadataMapsMock,
|
||||
typedFieldNumberMock,
|
||||
),
|
||||
).toEqual('fieldNumber');
|
||||
expect(
|
||||
mapFieldMetadataToGraphqlQuery([objectMetadataItemMock], fieldTextMock),
|
||||
mapFieldMetadataToGraphqlQuery(
|
||||
objectMetadataMapsMock,
|
||||
typedFieldTextMock,
|
||||
),
|
||||
).toEqual('fieldText');
|
||||
expect(
|
||||
mapFieldMetadataToGraphqlQuery(
|
||||
[objectMetadataItemMock],
|
||||
fieldCurrencyMock,
|
||||
objectMetadataMapsMock,
|
||||
typedFieldCurrencyMock,
|
||||
),
|
||||
).toEqual(`
|
||||
fieldCurrency
|
||||
@ -30,19 +98,24 @@ describe('mapFieldMetadataToGraphqlQuery', () => {
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
describe('should handle all field metadata types', () => {
|
||||
Object.values(FieldMetadataType).forEach((fieldMetadataType) => {
|
||||
it(`with field type ${fieldMetadataType}`, () => {
|
||||
const field = {
|
||||
const field: FieldMetadataInterface = {
|
||||
id: 'test-field-id',
|
||||
type: fieldMetadataType,
|
||||
name: 'toObjectMetadataName',
|
||||
label: 'Test Field',
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
fromRelationMetadata: {
|
||||
relationType: RelationMetadataType.ONE_TO_MANY,
|
||||
},
|
||||
toObjectMetadataId: objectMetadataItemMock.id,
|
||||
} as any,
|
||||
};
|
||||
|
||||
expect(
|
||||
mapFieldMetadataToGraphqlQuery([objectMetadataItemMock], field),
|
||||
mapFieldMetadataToGraphqlQuery(objectMetadataMapsMock, field),
|
||||
).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
import { FieldMetadataType } from 'twenty-shared';
|
||||
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
const DEFAULT_DEPTH_VALUE = 1;
|
||||
|
||||
// TODO: Should be properly type and based on composite type definitions
|
||||
export const mapFieldMetadataToGraphqlQuery = (
|
||||
objectMetadataItems,
|
||||
field,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
field: FieldMetadataInterface,
|
||||
maxDepthForRelations = DEFAULT_DEPTH_VALUE,
|
||||
): string | undefined => {
|
||||
if (maxDepthForRelations < 0) {
|
||||
@ -41,19 +44,25 @@ export const mapFieldMetadataToGraphqlQuery = (
|
||||
fieldType === FieldMetadataType.RELATION &&
|
||||
field.toRelationMetadata?.relationType === RelationMetadataType.ONE_TO_MANY
|
||||
) {
|
||||
const relationMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.id ===
|
||||
(field.toRelationMetadata as any)?.fromObjectMetadataId,
|
||||
);
|
||||
const fromObjectMetadataId = field.toRelationMetadata?.fromObjectMetadataId;
|
||||
|
||||
if (!fromObjectMetadataId) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const relationMetadataItem = objectMetadataMaps.byId[fromObjectMetadataId];
|
||||
|
||||
if (!relationMetadataItem) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return `${field.name}
|
||||
{
|
||||
id
|
||||
${(relationMetadataItem?.fields ?? [])
|
||||
${Object.values(relationMetadataItem.fieldsById)
|
||||
.map((field) =>
|
||||
mapFieldMetadataToGraphqlQuery(
|
||||
objectMetadataItems,
|
||||
objectMetadataMaps,
|
||||
field,
|
||||
maxDepthForRelations - 1,
|
||||
),
|
||||
@ -66,21 +75,27 @@ export const mapFieldMetadataToGraphqlQuery = (
|
||||
field.fromRelationMetadata?.relationType ===
|
||||
RelationMetadataType.ONE_TO_MANY
|
||||
) {
|
||||
const relationMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.id ===
|
||||
(field.fromRelationMetadata as any)?.toObjectMetadataId,
|
||||
);
|
||||
const toObjectMetadataId = field.fromRelationMetadata?.toObjectMetadataId;
|
||||
|
||||
if (!toObjectMetadataId) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const relationMetadataItem = objectMetadataMaps.byId[toObjectMetadataId];
|
||||
|
||||
if (!relationMetadataItem) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return `${field.name}
|
||||
{
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
${(relationMetadataItem?.fields ?? [])
|
||||
${Object.values(relationMetadataItem.fieldsById)
|
||||
.map((field) =>
|
||||
mapFieldMetadataToGraphqlQuery(
|
||||
objectMetadataItems,
|
||||
objectMetadataMaps,
|
||||
field,
|
||||
maxDepthForRelations - 1,
|
||||
),
|
||||
|
||||
@ -106,7 +106,7 @@ export class RestApiCoreServiceV2 {
|
||||
}
|
||||
|
||||
const objectMetadataNameSingular =
|
||||
objectMetadata.objectMetadataItem.nameSingular;
|
||||
objectMetadata.objectMetadataMapItem.nameSingular;
|
||||
const repository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
workspace.id,
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
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 getObjectMetadataMapItemByNamePlural = (
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
namePlural: string,
|
||||
): ObjectMetadataItemWithFieldMaps | undefined => {
|
||||
const objectMetadataItems = Object.values(objectMetadataMaps.byId);
|
||||
|
||||
return objectMetadataItems.find(
|
||||
(objectMetadata) => objectMetadata.namePlural === namePlural,
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user