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