Deprecate object metadata maps in favor of flat entities (#16080)
## Context Deprecating the old objectMetadataMap type in favour of split flat entities to match with our new caching. In the long run, trying to achieve: - Better performance through caching - Consistent data access patterns across the codebase - Reduced database queries Now that everything is based on flat entities, which are cached, we can finish the refactoring of workspace context cache which should already improve performances. Then the last step will be to consume that new cache in the new global datasource to get rid of the many workspace datasources stored in the server
This commit is contained in:
@@ -5,7 +5,6 @@ import { DatabaseCommandModule } from 'src/database/commands/database-command.mo
|
||||
import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
import { WorkspaceCleanerModule } from 'src/engine/workspace-manager/workspace-cleaner/workspace-cleaner.module';
|
||||
import { WorkspaceHealthCommandModule } from 'src/engine/workspace-manager/workspace-health/commands/workspace-health-command.module';
|
||||
import { WorkspaceMigrationRunnerCommandsModule } from 'src/engine/workspace-manager/workspace-migration-runner/commands/workspace-migration-runner-commands.module';
|
||||
import { WorkspaceSyncMetadataCommandsModule } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module';
|
||||
import { MessagingMessageCleanerModule } from 'src/modules/messaging/message-cleaner/messaging-message-cleaner.module';
|
||||
@@ -15,7 +14,6 @@ import { MessagingMessageCleanerModule } from 'src/modules/messaging/message-cle
|
||||
AppModule,
|
||||
WorkspaceSyncMetadataCommandsModule,
|
||||
DatabaseCommandModule,
|
||||
WorkspaceHealthCommandModule,
|
||||
MessagingMessageCleanerModule,
|
||||
WorkspaceMigrationRunnerCommandsModule,
|
||||
ObjectMetadataModule,
|
||||
|
||||
@@ -9,8 +9,6 @@ import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfa
|
||||
import { type FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { getMockFieldMetadataEntity } from 'src/utils/__test__/get-field-metadata-entity.mock';
|
||||
|
||||
export const FIELD_LINKS_MOCK_NAME = 'fieldLinks';
|
||||
@@ -463,32 +461,3 @@ export const objectMetadataItemMock: ObjectMetadataEntity = {
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
} as ObjectMetadataEntity;
|
||||
|
||||
export const objectMetadataMapItemMock: ObjectMetadataItemWithFieldMaps = {
|
||||
...objectMetadataItemMock,
|
||||
fieldsById: FIELDS_MOCK.reduce(
|
||||
(acc, field) => ({
|
||||
...acc,
|
||||
[field.id]: field,
|
||||
}),
|
||||
{},
|
||||
),
|
||||
fieldIdByName: FIELDS_MOCK.reduce(
|
||||
(acc, field) => ({
|
||||
...acc,
|
||||
[field.name]: field.id,
|
||||
}),
|
||||
{},
|
||||
),
|
||||
fieldIdByJoinColumnName: {},
|
||||
indexMetadatas: [],
|
||||
};
|
||||
export const objectMetadataMapsMock: ObjectMetadataMaps = {
|
||||
byId: {
|
||||
[objectMetadataMapItemMock.id || 'mock-id']: objectMetadataMapItemMock,
|
||||
},
|
||||
idByNameSingular: {
|
||||
[objectMetadataMapItemMock.nameSingular]:
|
||||
objectMetadataMapItemMock.id || 'mock-id',
|
||||
},
|
||||
};
|
||||
|
||||
+38
-25
@@ -11,39 +11,43 @@ import { CommonSelectedFieldsResult } from 'src/engine/api/common/types/common-s
|
||||
import { getAllSelectableFields } from 'src/engine/api/rest/core/rest-to-common-args-handlers/utils/get-all-selectable-fields.util';
|
||||
import { MAX_DEPTH } from 'src/engine/api/rest/input-request-parsers/constants/max-depth.constant';
|
||||
import { Depth } from 'src/engine/api/rest/input-request-parsers/types/depth.type';
|
||||
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 { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class CommonSelectedFieldsHandler {
|
||||
computeFromDepth = ({
|
||||
objectsPermissions,
|
||||
objectMetadataMaps,
|
||||
objectMetadataMapItem,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
depth,
|
||||
}: {
|
||||
objectsPermissions: ObjectsPermissions;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
depth: Depth | undefined;
|
||||
}): CommonSelectedFieldsResult => {
|
||||
const restrictedFields =
|
||||
objectsPermissions[objectMetadataMapItem.id].restrictedFields;
|
||||
objectsPermissions[flatObjectMetadata.id].restrictedFields;
|
||||
|
||||
const { relations, relationsSelectFields } =
|
||||
this.getRelationsAndRelationsSelectFields({
|
||||
objectMetadataMaps,
|
||||
objectMetadataMapItem,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
objectsPermissions,
|
||||
depth,
|
||||
});
|
||||
|
||||
const selectableFields = getAllSelectableFields({
|
||||
restrictedFields,
|
||||
objectMetadata: {
|
||||
objectMetadataMapItem,
|
||||
},
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -57,13 +61,15 @@ export class CommonSelectedFieldsHandler {
|
||||
};
|
||||
|
||||
private getRelationsAndRelationsSelectFields({
|
||||
objectMetadataMaps,
|
||||
objectMetadataMapItem,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
objectsPermissions,
|
||||
depth,
|
||||
}: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
objectsPermissions: ObjectsPermissions;
|
||||
depth: Depth | undefined;
|
||||
}) {
|
||||
@@ -82,12 +88,19 @@ export class CommonSelectedFieldsHandler {
|
||||
| { [key: string]: boolean | { [key: string]: boolean } };
|
||||
} = {};
|
||||
|
||||
for (const field of Object.values(objectMetadataMapItem.fieldsById)) {
|
||||
if (!isFieldMetadataEntityOfType(field, FieldMetadataType.RELATION))
|
||||
for (const fieldId of flatObjectMetadata.fieldMetadataIds) {
|
||||
const field = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
flatEntityId: fieldId,
|
||||
});
|
||||
|
||||
if (!isFlatFieldMetadataOfType(field, FieldMetadataType.RELATION))
|
||||
continue;
|
||||
|
||||
if (!field.relationTargetObjectMetadataId) continue;
|
||||
|
||||
const relationTargetObjectMetadata =
|
||||
objectMetadataMaps.byId[field.relationTargetObjectMetadataId];
|
||||
flatObjectMetadataMaps.byId[field.relationTargetObjectMetadataId];
|
||||
|
||||
if (!isDefined(relationTargetObjectMetadata)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
@@ -98,9 +111,8 @@ export class CommonSelectedFieldsHandler {
|
||||
const relationFieldSelectFields = getAllSelectableFields({
|
||||
restrictedFields:
|
||||
objectsPermissions[relationTargetObjectMetadata.id].restrictedFields,
|
||||
objectMetadata: {
|
||||
objectMetadataMapItem: relationTargetObjectMetadata,
|
||||
},
|
||||
flatObjectMetadata: relationTargetObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (Object.keys(relationFieldSelectFields).length === 0) continue;
|
||||
@@ -113,8 +125,9 @@ export class CommonSelectedFieldsHandler {
|
||||
relations: depth2Relations,
|
||||
relationsSelectFields: depth2RelationsSelectFields,
|
||||
} = this.getRelationsAndRelationsSelectFields({
|
||||
objectMetadataMaps,
|
||||
objectMetadataMapItem: relationTargetObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: relationTargetObjectMetadata,
|
||||
objectsPermissions,
|
||||
depth: 1,
|
||||
}) as {
|
||||
|
||||
+28
-13
@@ -52,8 +52,10 @@ import { transformLinksValue } from 'src/engine/core-modules/record-transformer/
|
||||
import { transformPhonesValue } from 'src/engine/core-modules/record-transformer/utils/transform-phones-value.util';
|
||||
import { transformRichTextV2Value } from 'src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util';
|
||||
import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class DataArgProcessor {
|
||||
@@ -65,12 +67,14 @@ export class DataArgProcessor {
|
||||
async process({
|
||||
partialRecordInputs,
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
shouldBackfillPositionIfUndefined = true,
|
||||
}: {
|
||||
partialRecordInputs: Partial<ObjectRecord>[] | undefined;
|
||||
authContext: AuthContext;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
shouldBackfillPositionIfUndefined?: boolean;
|
||||
}): Promise<Partial<ObjectRecord>[]> {
|
||||
if (!isDefined(partialRecordInputs)) {
|
||||
@@ -87,6 +91,12 @@ export class DataArgProcessor {
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
const { fieldIdByName, fieldIdByJoinColumnName } =
|
||||
buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
|
||||
const processedRecords: Partial<ObjectRecord>[] = [];
|
||||
|
||||
for (const record of partialRecordInputs) {
|
||||
@@ -94,18 +104,23 @@ export class DataArgProcessor {
|
||||
|
||||
for (const [key, value] of Object.entries(record)) {
|
||||
const fieldMetadataId =
|
||||
objectMetadataItemWithFieldMaps.fieldIdByName[key] ||
|
||||
objectMetadataItemWithFieldMaps.fieldIdByJoinColumnName[key];
|
||||
fieldIdByName[key] || fieldIdByJoinColumnName[key];
|
||||
|
||||
if (!isDefined(fieldMetadataId)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Object ${objectMetadataItemWithFieldMaps.nameSingular} doesn't have any "${key}" field.`,
|
||||
`Object ${flatObjectMetadata.nameSingular} doesn't have any "${key}" field.`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA,
|
||||
);
|
||||
}
|
||||
|
||||
const fieldMetadata =
|
||||
objectMetadataItemWithFieldMaps.fieldsById[fieldMetadataId];
|
||||
const fieldMetadata = flatFieldMetadataMaps.byId[fieldMetadataId];
|
||||
|
||||
if (!fieldMetadata) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Field metadata not found for field ${key}`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!isDefined(fieldMetadata.defaultValue) &&
|
||||
@@ -137,9 +152,9 @@ export class DataArgProcessor {
|
||||
partialRecordInputs: processedRecords,
|
||||
workspaceId: workspace.id,
|
||||
objectMetadata: {
|
||||
isCustom: objectMetadataItemWithFieldMaps.isCustom,
|
||||
nameSingular: objectMetadataItemWithFieldMaps.nameSingular,
|
||||
fieldIdByName: objectMetadataItemWithFieldMaps.fieldIdByName,
|
||||
isCustom: flatObjectMetadata.isCustom,
|
||||
nameSingular: flatObjectMetadata.nameSingular,
|
||||
fieldIdByName,
|
||||
},
|
||||
shouldBackfillPositionIfUndefined,
|
||||
});
|
||||
@@ -148,7 +163,7 @@ export class DataArgProcessor {
|
||||
}
|
||||
|
||||
private async processField(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
key: string,
|
||||
value: unknown,
|
||||
isNullEquivalenceEnabled: boolean,
|
||||
|
||||
+28
-15
@@ -5,15 +5,15 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
|
||||
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
|
||||
import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service';
|
||||
import { type FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class QueryRunnerArgsFactory {
|
||||
constructor(
|
||||
private readonly recordPositionService: RecordPositionService,
|
||||
private readonly recordInputTransformerService: RecordInputTransformerService,
|
||||
) {}
|
||||
|
||||
@@ -21,12 +21,18 @@ export class QueryRunnerArgsFactory {
|
||||
T extends ObjectRecordFilter | undefined,
|
||||
>(
|
||||
filter: T,
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
): T {
|
||||
if (!isDefined(filter)) {
|
||||
return filter;
|
||||
}
|
||||
|
||||
const { fieldIdByName } = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
|
||||
const overrideFilter = (filterObject: ObjectRecordFilter) => {
|
||||
return Object.entries(filterObject).reduce((acc, [key, value]) => {
|
||||
if (key === 'and' || key === 'or') {
|
||||
@@ -42,7 +48,8 @@ export class QueryRunnerArgsFactory {
|
||||
acc[key] = this.transformFilterValueByType(
|
||||
key,
|
||||
value,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
fieldIdByName,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,11 +64,13 @@ export class QueryRunnerArgsFactory {
|
||||
key: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any,
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
fieldIdByName: Record<string, string>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) {
|
||||
const fieldMetadataId = objectMetadataItemWithFieldMaps.fieldIdByName[key];
|
||||
const fieldMetadata =
|
||||
objectMetadataItemWithFieldMaps.fieldsById[fieldMetadataId];
|
||||
const fieldMetadataId = fieldIdByName[key];
|
||||
const fieldMetadata = fieldMetadataId
|
||||
? flatFieldMetadataMaps.byId[fieldMetadataId]
|
||||
: undefined;
|
||||
|
||||
if (!fieldMetadata) {
|
||||
return value;
|
||||
@@ -90,18 +99,22 @@ export class QueryRunnerArgsFactory {
|
||||
key: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any,
|
||||
fieldMetadataMapByName: FieldMetadataMap,
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
fieldIdByName: Record<string, string>,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) {
|
||||
const fieldMetadata = fieldMetadataMapByName[key];
|
||||
const fieldMetadata = flatFieldMetadataMaps.byId[fieldIdByName[key]];
|
||||
|
||||
if (!fieldMetadata) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.recordInputTransformerService.process({
|
||||
const processed = await this.recordInputTransformerService.process({
|
||||
recordInput: { [key]: value },
|
||||
objectMetadataMapItem: objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return processed[key] ?? value;
|
||||
}
|
||||
}
|
||||
|
||||
+36
-26
@@ -36,6 +36,9 @@ import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service'
|
||||
import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type';
|
||||
import { ThrottlerService } from 'src/engine/core-modules/throttler/throttler.service';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
|
||||
import {
|
||||
PermissionsException,
|
||||
@@ -43,8 +46,6 @@ import {
|
||||
PermissionsExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.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 { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||
@@ -93,8 +94,12 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
args: CommonInput<Args>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<Output> {
|
||||
const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } =
|
||||
queryRunnerContext;
|
||||
const {
|
||||
authContext,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
} = queryRunnerContext;
|
||||
|
||||
if (!isWorkspaceAuthContext(authContext)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
@@ -107,7 +112,7 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
|
||||
await this.validate(args, queryRunnerContext);
|
||||
|
||||
if (objectMetadataItemWithFieldMaps.isSystem === true) {
|
||||
if (flatObjectMetadata.isSystem === true) {
|
||||
await this.validateSettingsPermissionsOnObjectOrThrow(
|
||||
authContext,
|
||||
queryRunnerContext,
|
||||
@@ -115,8 +120,9 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
}
|
||||
|
||||
const commonQueryParser = new GraphqlQueryParser(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
const processedArgs = await this.processArgs(
|
||||
@@ -172,8 +178,9 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
|
||||
protected abstract processQueryResult(
|
||||
queryResult: Output,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<Output>;
|
||||
|
||||
@@ -187,14 +194,14 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
args.selectedFields,
|
||||
);
|
||||
|
||||
const { authContext, objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { authContext, flatObjectMetadata } = queryRunnerContext;
|
||||
|
||||
const computedArgs = await this.computeArgs(args, queryRunnerContext);
|
||||
|
||||
const hookedArgs =
|
||||
(await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
operationName,
|
||||
computedArgs as WorkspacePreQueryHookPayload<CommonQueryNames>,
|
||||
)) as CommonInput<Args>;
|
||||
@@ -231,9 +238,9 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
results,
|
||||
operationName: this.operationName,
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps:
|
||||
queryRunnerContext.objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: queryRunnerContext.objectMetadataMaps,
|
||||
flatObjectMetadata: queryRunnerContext.flatObjectMetadata,
|
||||
flatObjectMetadataMaps: queryRunnerContext.flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: queryRunnerContext.flatFieldMetadataMaps,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -241,25 +248,28 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
results,
|
||||
operationName,
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
}: {
|
||||
results: Output;
|
||||
operationName: CommonQueryNames;
|
||||
authContext: WorkspaceAuthContext;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
}): Promise<Output> {
|
||||
const resultWithGetters = await this.processQueryResult(
|
||||
results,
|
||||
objectMetadataItemWithFieldMaps.id,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext,
|
||||
);
|
||||
|
||||
await this.workspaceQueryHookService.executePostQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
operationName,
|
||||
resultWithGetters as QueryResultFieldValue,
|
||||
);
|
||||
@@ -271,18 +281,18 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
authContext: WorkspaceAuthContext,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
) {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata } = queryRunnerContext;
|
||||
|
||||
const workspace = authContext.workspace;
|
||||
|
||||
if (
|
||||
Object.keys(OBJECTS_WITH_SETTINGS_PERMISSIONS_REQUIREMENTS).includes(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
)
|
||||
) {
|
||||
const permissionRequired: PermissionFlagType =
|
||||
OBJECTS_WITH_SETTINGS_PERMISSIONS_REQUIREMENTS[
|
||||
objectMetadataItemWithFieldMaps.nameSingular as keyof typeof OBJECTS_WITH_SETTINGS_PERMISSIONS_REQUIREMENTS
|
||||
flatObjectMetadata.nameSingular as keyof typeof OBJECTS_WITH_SETTINGS_PERMISSIONS_REQUIREMENTS
|
||||
];
|
||||
|
||||
const userHasPermission =
|
||||
@@ -368,7 +378,7 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
const rolePermissionConfig = { unionOf: [roleId] };
|
||||
|
||||
const repository = workspaceDataSource.getRepository(
|
||||
queryRunnerContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
queryRunnerContext.flatObjectMetadata.nameSingular,
|
||||
rolePermissionConfig,
|
||||
authContext,
|
||||
);
|
||||
@@ -397,7 +407,7 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
|
||||
const repository = await this.globalWorkspaceOrmManager.getRepository(
|
||||
workspaceId,
|
||||
queryRunnerContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
queryRunnerContext.flatObjectMetadata.nameSingular,
|
||||
rolePermissionConfig,
|
||||
);
|
||||
|
||||
|
||||
+100
-61
@@ -31,9 +31,11 @@ import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runne
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { getAllSelectableColumnNames } from 'src/engine/api/utils/get-all-selectable-column-names.utils';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
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 { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
@@ -62,22 +64,25 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
repository,
|
||||
authContext,
|
||||
rolePermissionConfig,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceDataSource,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const objectRecords = await this.insertOrUpsertRecords({
|
||||
repository,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
args,
|
||||
});
|
||||
|
||||
const upsertedRecords = await this.fetchUpsertedRecords({
|
||||
objectRecords,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
repository,
|
||||
selectedFieldsResult: args.selectedFieldsResult,
|
||||
});
|
||||
@@ -85,8 +90,9 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
await this.processNestedRelationsIfNeeded({
|
||||
args,
|
||||
records: upsertedRecords,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
@@ -98,16 +104,18 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
private async processNestedRelationsIfNeeded({
|
||||
args,
|
||||
records,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
}: {
|
||||
args: CommonExtendedInput<CreateManyQueryArgs>;
|
||||
records: ObjectRecord[];
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
authContext: AuthContext;
|
||||
workspaceDataSource: WorkspaceDataSource;
|
||||
rolePermissionConfig?: RolePermissionConfig;
|
||||
@@ -117,8 +125,9 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
}
|
||||
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem: flatObjectMetadata,
|
||||
parentObjectRecords: records,
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
@@ -136,14 +145,16 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
args: CommonInput<CreateManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<CreateManyQueryArgs>> {
|
||||
const { authContext, objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { authContext, flatObjectMetadata, flatFieldMetadataMaps } =
|
||||
queryRunnerContext;
|
||||
|
||||
return {
|
||||
...args,
|
||||
data: await this.dataArgProcessor.process({
|
||||
partialRecordInputs: args.data,
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
}),
|
||||
};
|
||||
}
|
||||
@@ -152,9 +163,9 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
args: CommonInput<CreateManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
assertMutationNotOnRemoteObject(flatObjectMetadata);
|
||||
|
||||
args.data.forEach((record) => {
|
||||
if (record?.id) {
|
||||
@@ -165,13 +176,15 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
private async insertOrUpsertRecords({
|
||||
repository,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
args,
|
||||
}: {
|
||||
repository: WorkspaceRepository<ObjectLiteral>;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
args: CommonExtendedInput<CreateManyQueryArgs>;
|
||||
}): Promise<InsertResult> {
|
||||
const { selectedFieldsResult } = args;
|
||||
@@ -180,8 +193,9 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
const selectedColumns = buildColumnsToReturn({
|
||||
select: selectedFieldsResult.select,
|
||||
relations: selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return await repository.insert(args.data, undefined, selectedColumns);
|
||||
@@ -189,8 +203,9 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
return this.performUpsertOperation({
|
||||
repository,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
args,
|
||||
selectedFieldsResult,
|
||||
});
|
||||
@@ -198,23 +213,27 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
private async performUpsertOperation({
|
||||
repository,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
args,
|
||||
selectedFieldsResult,
|
||||
}: {
|
||||
repository: WorkspaceRepository<ObjectLiteral>;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
args: CreateManyQueryArgs;
|
||||
selectedFieldsResult: CommonSelectedFieldsResult;
|
||||
}): Promise<InsertResult> {
|
||||
const conflictingFields = getConflictingFields(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
const existingRecords = await this.findExistingRecords({
|
||||
repository,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
args,
|
||||
conflictingFields,
|
||||
});
|
||||
@@ -234,15 +253,17 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
const columnsToReturn = buildColumnsToReturn({
|
||||
select: selectedFieldsResult.select,
|
||||
relations: selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (recordsToUpdate.length > 0) {
|
||||
await this.processRecordsToUpdate({
|
||||
partialRecordsToUpdate: recordsToUpdate,
|
||||
repository,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
result,
|
||||
columnsToReturn,
|
||||
});
|
||||
@@ -260,12 +281,14 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
private async findExistingRecords({
|
||||
repository,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
args,
|
||||
conflictingFields,
|
||||
}: {
|
||||
repository: WorkspaceRepository<ObjectLiteral>;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
args: CreateManyQueryArgs;
|
||||
conflictingFields: {
|
||||
baseField: string;
|
||||
@@ -274,7 +297,7 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
}[];
|
||||
}): Promise<PartialObjectRecordWithId[]> {
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
const whereConditions = buildWhereConditions(args.data, conflictingFields);
|
||||
@@ -284,13 +307,14 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
});
|
||||
|
||||
const restrictedFields =
|
||||
repository.objectRecordsPermissions?.[objectMetadataItemWithFieldMaps.id]
|
||||
repository.objectRecordsPermissions?.[flatObjectMetadata.id]
|
||||
?.restrictedFields;
|
||||
|
||||
const selectOptions = getAllSelectableColumnNames({
|
||||
restrictedFields: restrictedFields ?? {},
|
||||
objectMetadata: {
|
||||
objectMetadataMapItem: objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMapItem: flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -305,19 +329,25 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
private async processRecordsToUpdate({
|
||||
partialRecordsToUpdate,
|
||||
repository,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
result,
|
||||
columnsToReturn,
|
||||
}: {
|
||||
partialRecordsToUpdate: PartialObjectRecordWithId[];
|
||||
repository: WorkspaceRepository<ObjectLiteral>;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
result: InsertResult;
|
||||
columnsToReturn: string[];
|
||||
}): Promise<void> {
|
||||
const partialRecordsToUpdateWithoutCreatedByUpdate =
|
||||
partialRecordsToUpdate.map((record) =>
|
||||
this.getRecordWithoutCreatedBy(record, objectMetadataItemWithFieldMaps),
|
||||
this.getRecordWithoutCreatedBy(
|
||||
record,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
);
|
||||
|
||||
const savedRecords = await repository.updateMany(
|
||||
@@ -363,26 +393,29 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
private async fetchUpsertedRecords({
|
||||
objectRecords,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
repository,
|
||||
selectedFieldsResult,
|
||||
}: {
|
||||
objectRecords: InsertResult;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
repository: WorkspaceRepository<ObjectLiteral>;
|
||||
selectedFieldsResult: CommonSelectedFieldsResult;
|
||||
}): Promise<ObjectRecord[]> {
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
const columnsToSelect = buildColumnsToSelect({
|
||||
select: selectedFieldsResult.select,
|
||||
relations: selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const upsertedRecords = await queryBuilder
|
||||
@@ -401,32 +434,38 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord[],
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord[]> {
|
||||
return await this.commonResultGettersService.processRecordArray(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
private getRecordWithoutCreatedBy(
|
||||
record: PartialObjectRecordWithId,
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
): Omit<PartialObjectRecordWithId, 'createdBy'> {
|
||||
let recordWithoutCreatedByUpdate = record;
|
||||
|
||||
const createdByFieldMetadataId =
|
||||
objectMetadataItemWithFieldMaps.fieldIdByName['createdBy'];
|
||||
const { fieldIdByName } = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
|
||||
const createdByFieldMetadata =
|
||||
objectMetadataItemWithFieldMaps.fieldsById[createdByFieldMetadataId];
|
||||
flatFieldMetadataMaps.byId[fieldIdByName['createdBy']];
|
||||
|
||||
if (!isDefined(createdByFieldMetadata)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Missing createdBy field metadata for object ${objectMetadataItemWithFieldMaps.nameSingular}`,
|
||||
`Missing createdBy field metadata for object ${flatObjectMetadata.nameSingular}`,
|
||||
CommonQueryRunnerExceptionCode.MISSING_SYSTEM_FIELD,
|
||||
);
|
||||
}
|
||||
|
||||
+99
-52
@@ -1,87 +1,128 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { objectMetadataMapItemMock } from 'src/engine/api/__mocks__/object-metadata-item.mock';
|
||||
import { getConflictingFields } from 'src/engine/api/common/common-query-runners/common-create-many-query-runner/utils/get-conflicting-fields.util';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { getMockFieldMetadataEntity } from 'src/utils/__test__/get-field-metadata-entity.mock';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
describe('getConflictingFields', () => {
|
||||
const workspaceId = 'workspaceId';
|
||||
const objectMetadataId = 'objectMetadataId';
|
||||
|
||||
const idField = getMockFieldMetadataEntity({
|
||||
workspaceId,
|
||||
objectMetadataId,
|
||||
const createMockField = (
|
||||
overrides: Partial<FlatFieldMetadata> & {
|
||||
id: string;
|
||||
name: string;
|
||||
type: FieldMetadataType;
|
||||
},
|
||||
): FlatFieldMetadata =>
|
||||
({
|
||||
workspaceId,
|
||||
objectMetadataId,
|
||||
isNullable: false,
|
||||
isLabelSyncedWithName: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
universalIdentifier: overrides.id,
|
||||
viewFieldIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
applicationId: null,
|
||||
label: overrides.name,
|
||||
...overrides,
|
||||
}) as FlatFieldMetadata;
|
||||
|
||||
const idField = createMockField({
|
||||
id: 'id-field-id',
|
||||
name: 'id',
|
||||
type: FieldMetadataType.UUID,
|
||||
isUnique: true,
|
||||
});
|
||||
|
||||
const uniqueTextField = getMockFieldMetadataEntity({
|
||||
workspaceId,
|
||||
objectMetadataId,
|
||||
const uniqueTextField = createMockField({
|
||||
id: 'unique-text-id',
|
||||
name: 'uniqueText',
|
||||
type: FieldMetadataType.TEXT,
|
||||
isUnique: true,
|
||||
});
|
||||
|
||||
const emailsUniqueField = getMockFieldMetadataEntity({
|
||||
workspaceId,
|
||||
objectMetadataId,
|
||||
const emailsUniqueField = createMockField({
|
||||
id: 'emails-unique-id',
|
||||
name: 'emailsField',
|
||||
type: FieldMetadataType.EMAILS,
|
||||
isUnique: true,
|
||||
});
|
||||
|
||||
const phonesNotUniqueField = getMockFieldMetadataEntity({
|
||||
workspaceId,
|
||||
objectMetadataId,
|
||||
const phonesNotUniqueField = createMockField({
|
||||
id: 'phones-not-unique-id',
|
||||
name: 'phonesField',
|
||||
type: FieldMetadataType.PHONES,
|
||||
isUnique: false,
|
||||
});
|
||||
|
||||
const addressUniqueFieldNoIncludedProp = getMockFieldMetadataEntity({
|
||||
workspaceId,
|
||||
objectMetadataId,
|
||||
const addressUniqueFieldNoIncludedProp = createMockField({
|
||||
id: 'address-unique-id',
|
||||
name: 'addressField',
|
||||
type: FieldMetadataType.ADDRESS,
|
||||
isUnique: true,
|
||||
});
|
||||
|
||||
const buildObjectMetadataWithFields = (
|
||||
fields: (typeof idField)[],
|
||||
): ObjectMetadataItemWithFieldMaps => {
|
||||
const fieldsById = fields.reduce<Record<string, typeof idField>>(
|
||||
const buildFlatObjectMetadata = (
|
||||
fields: FlatFieldMetadata[],
|
||||
): FlatObjectMetadata =>
|
||||
({
|
||||
id: objectMetadataId,
|
||||
workspaceId,
|
||||
nameSingular: 'testObject',
|
||||
namePlural: 'testObjects',
|
||||
labelSingular: 'Test Object',
|
||||
labelPlural: 'Test Objects',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
universalIdentifier: objectMetadataId,
|
||||
fieldMetadataIds: fields.map((f) => f.id),
|
||||
indexMetadataIds: [],
|
||||
viewIds: [],
|
||||
applicationId: null,
|
||||
}) as unknown as FlatObjectMetadata;
|
||||
|
||||
const buildFlatFieldMetadataMaps = (
|
||||
fields: FlatFieldMetadata[],
|
||||
): FlatEntityMaps<FlatFieldMetadata> => ({
|
||||
byId: fields.reduce(
|
||||
(acc, field) => {
|
||||
acc[field.id] = field;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
{} as Record<string, FlatFieldMetadata>,
|
||||
),
|
||||
idByUniversalIdentifier: fields.reduce(
|
||||
(acc, field) => {
|
||||
acc[field.universalIdentifier] = field.id;
|
||||
|
||||
return {
|
||||
...objectMetadataMapItemMock,
|
||||
fieldsById,
|
||||
fieldIdByName: Object.fromEntries(
|
||||
Object.values(fieldsById).map((f) => [f.name, f.id]),
|
||||
),
|
||||
} as ObjectMetadataItemWithFieldMaps;
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
),
|
||||
universalIdentifiersByApplicationId: {},
|
||||
});
|
||||
|
||||
it('returns id and unique non-composite fields as conflicts', () => {
|
||||
const objectMetadata = buildObjectMetadataWithFields([
|
||||
idField,
|
||||
uniqueTextField,
|
||||
]);
|
||||
const fields = [idField, uniqueTextField];
|
||||
const flatObjectMetadata = buildFlatObjectMetadata(fields);
|
||||
const flatFieldMetadataMaps = buildFlatFieldMetadataMaps(fields);
|
||||
|
||||
const result = getConflictingFields(objectMetadata);
|
||||
const result = getConflictingFields(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
@@ -96,12 +137,14 @@ describe('getConflictingFields', () => {
|
||||
});
|
||||
|
||||
it('returns composite field with included unique property using full path and computed column', () => {
|
||||
const objectMetadata = buildObjectMetadataWithFields([
|
||||
idField,
|
||||
emailsUniqueField,
|
||||
]);
|
||||
const fields = [idField, emailsUniqueField];
|
||||
const flatObjectMetadata = buildFlatObjectMetadata(fields);
|
||||
const flatFieldMetadataMaps = buildFlatFieldMetadataMaps(fields);
|
||||
|
||||
const result = getConflictingFields(objectMetadata);
|
||||
const result = getConflictingFields(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
@@ -116,23 +159,27 @@ describe('getConflictingFields', () => {
|
||||
});
|
||||
|
||||
it('does not include composite fields without included unique property', () => {
|
||||
const objectMetadata = buildObjectMetadataWithFields([
|
||||
idField,
|
||||
addressUniqueFieldNoIncludedProp,
|
||||
]);
|
||||
const fields = [idField, addressUniqueFieldNoIncludedProp];
|
||||
const flatObjectMetadata = buildFlatObjectMetadata(fields);
|
||||
const flatFieldMetadataMaps = buildFlatFieldMetadataMaps(fields);
|
||||
|
||||
const result = getConflictingFields(objectMetadata);
|
||||
const result = getConflictingFields(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
expect(result).toEqual([{ baseField: 'id', fullPath: 'id', column: 'id' }]);
|
||||
});
|
||||
|
||||
it('ignores non-unique fields', () => {
|
||||
const objectMetadata = buildObjectMetadataWithFields([
|
||||
idField,
|
||||
phonesNotUniqueField,
|
||||
]);
|
||||
const fields = [idField, phonesNotUniqueField];
|
||||
const flatObjectMetadata = buildFlatObjectMetadata(fields);
|
||||
const flatFieldMetadataMaps = buildFlatFieldMetadataMaps(fields);
|
||||
|
||||
const result = getConflictingFields(objectMetadata);
|
||||
const result = getConflictingFields(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
expect(result).toEqual([{ baseField: 'id', fullPath: 'id', column: 'id' }]);
|
||||
});
|
||||
|
||||
+11
-4
@@ -1,16 +1,23 @@
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { compositeTypeDefinitions } from 'twenty-shared/types';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { getFlatFieldsFromFlatObjectMetadata } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-flat-fields-for-flat-object-metadata.util';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const getConflictingFields = (
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
): {
|
||||
baseField: string;
|
||||
fullPath: string;
|
||||
column: string;
|
||||
}[] => {
|
||||
return Object.values(objectMetadataItemWithFieldMaps.fieldsById)
|
||||
return getFlatFieldsFromFlatObjectMetadata(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
)
|
||||
.filter((field) => field.isUnique || field.name === 'id')
|
||||
.flatMap((field) => {
|
||||
const compositeType = compositeTypeDefinitions.get(field.type);
|
||||
|
||||
+15
-9
@@ -16,8 +16,10 @@ import {
|
||||
CreateOneQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
@Injectable()
|
||||
export class CommonCreateOneQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -51,12 +53,14 @@ export class CommonCreateOneQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
args: CommonInput<CreateOneQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<CreateOneQueryArgs>> {
|
||||
const { authContext, objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { authContext, flatObjectMetadata, flatFieldMetadataMaps } =
|
||||
queryRunnerContext;
|
||||
|
||||
const coercedData = await this.dataArgProcessor.process({
|
||||
partialRecordInputs: [args.data],
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -67,14 +71,16 @@ export class CommonCreateOneQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord> {
|
||||
return this.commonResultGettersService.processRecord(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
@@ -83,9 +89,9 @@ export class CommonCreateOneQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
args: CommonInput<CreateOneQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
assertMutationNotOnRemoteObject(flatObjectMetadata);
|
||||
|
||||
if (args.data?.id) {
|
||||
assertIsValidUuid(args.data.id);
|
||||
|
||||
+30
-22
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'class-validator';
|
||||
import { QUERY_MAX_RECORDS } from 'twenty-shared/constants';
|
||||
import { ObjectRecord } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FindOptionsRelations, ObjectLiteral } from 'typeorm';
|
||||
|
||||
import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
|
||||
@@ -22,8 +22,10 @@ import {
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { buildColumnsToReturn } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-return';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
@Injectable()
|
||||
export class CommonDeleteManyQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -41,26 +43,28 @@ export class CommonDeleteManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
authContext,
|
||||
rolePermissionConfig,
|
||||
workspaceDataSource,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
commonQueryParser,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
commonQueryParser.applyFilterToBuilder(
|
||||
queryBuilder,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
args.filter,
|
||||
);
|
||||
|
||||
const columnsToReturn = buildColumnsToReturn({
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const deletedObjectRecords = await queryBuilder
|
||||
@@ -72,10 +76,10 @@ export class CommonDeleteManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
if (isDefined(args.selectedFieldsResult.relations)) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem: flatObjectMetadata,
|
||||
parentObjectRecords: deletedRecords,
|
||||
//TODO : Refacto-common - Typing to fix when switching processNestedRelationsHelper to Common
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
FindOptionsRelations<ObjectLiteral>
|
||||
@@ -95,39 +99,43 @@ export class CommonDeleteManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
args: CommonInput<DeleteManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<DeleteManyQueryArgs>> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata, flatFieldMetadataMaps } = queryRunnerContext;
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord[],
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord[]> {
|
||||
return this.commonResultGettersService.processRecordArray(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
async validate(
|
||||
args: DeleteManyQueryArgs,
|
||||
args: CommonInput<DeleteManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
) {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
): Promise<void> {
|
||||
const { flatObjectMetadata } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
if (!args.filter) {
|
||||
assertMutationNotOnRemoteObject(flatObjectMetadata);
|
||||
|
||||
if (!isDefined(args.filter)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Filter is required',
|
||||
CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
|
||||
|
||||
+11
-7
@@ -20,8 +20,10 @@ import {
|
||||
DeleteOneQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
@Injectable()
|
||||
export class CommonDeleteOneQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -67,14 +69,16 @@ export class CommonDeleteOneQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord> {
|
||||
return this.commonResultGettersService.processRecord(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
@@ -83,9 +87,9 @@ export class CommonDeleteOneQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
args: CommonInput<DeleteOneQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
assertMutationNotOnRemoteObject(flatObjectMetadata);
|
||||
assertIsValidUuid(args.id);
|
||||
}
|
||||
}
|
||||
|
||||
+37
-21
@@ -21,7 +21,11 @@ import {
|
||||
DestroyManyQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { buildColumnsToReturn } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-return';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
|
||||
@Injectable()
|
||||
export class CommonDestroyManyQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -39,41 +43,44 @@ export class CommonDestroyManyQueryRunnerService extends CommonBaseQueryRunnerSe
|
||||
authContext,
|
||||
rolePermissionConfig,
|
||||
workspaceDataSource,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
commonQueryParser,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
commonQueryParser.applyFilterToBuilder(
|
||||
queryBuilder,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
args.filter,
|
||||
);
|
||||
|
||||
const columnsToReturn = buildColumnsToReturn({
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const deletedObjectRecords = await queryBuilder
|
||||
const destroyedObjectRecords = await queryBuilder
|
||||
.delete()
|
||||
.returning(columnsToReturn)
|
||||
.execute();
|
||||
|
||||
const deletedRecords = deletedObjectRecords.generatedMaps as ObjectRecord[];
|
||||
const destroyedRecords =
|
||||
destroyedObjectRecords.generatedMaps as ObjectRecord[];
|
||||
|
||||
if (isDefined(args.selectedFieldsResult.relations)) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
parentObjectRecords: deletedRecords,
|
||||
//TODO : Refacto-common - Typing to fix when switching processNestedRelationsHelper to Common
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem: flatObjectMetadata,
|
||||
parentObjectRecords: destroyedRecords,
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
FindOptionsRelations<ObjectLiteral>
|
||||
@@ -86,47 +93,56 @@ export class CommonDestroyManyQueryRunnerService extends CommonBaseQueryRunnerSe
|
||||
});
|
||||
}
|
||||
|
||||
return deletedRecords;
|
||||
return destroyedRecords;
|
||||
}
|
||||
|
||||
async computeArgs(
|
||||
args: CommonInput<DestroyManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<DestroyManyQueryArgs>> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata, flatFieldMetadataMaps } = queryRunnerContext;
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord[],
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord[]> {
|
||||
return this.commonResultGettersService.processRecordArray(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
async validate(
|
||||
args: CommonInput<DestroyManyQueryArgs>,
|
||||
_queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
const { flatObjectMetadata } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(flatObjectMetadata);
|
||||
|
||||
if (!isDefined(args.filter)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Filter is required',
|
||||
CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
args.filter.id?.in?.forEach((id: string) => assertIsValidUuid(id));
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -19,7 +19,9 @@ import {
|
||||
CommonQueryNames,
|
||||
DestroyOneQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class CommonDestroyOneQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -65,14 +67,16 @@ export class CommonDestroyOneQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord> {
|
||||
return this.commonResultGettersService.processRecord(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
+37
-19
@@ -25,7 +25,10 @@ import {
|
||||
import { getPageInfo } from 'src/engine/api/common/utils/get-page-info.util';
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { buildDuplicateConditions } from 'src/engine/api/utils/build-duplicate-conditions.utils';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -40,8 +43,9 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
|
||||
): Promise<CommonFindDuplicatesOutputItem[]> {
|
||||
const {
|
||||
repository,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
commonQueryParser,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
@@ -49,7 +53,7 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
|
||||
} = queryRunnerContext;
|
||||
|
||||
const existingRecordsQueryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
let objectRecords: Partial<ObjectRecord>[] = [];
|
||||
@@ -57,8 +61,9 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
|
||||
const columnsToSelect = buildColumnsToSelect({
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (isDefined(args.ids) && args.ids.length > 0) {
|
||||
@@ -76,7 +81,9 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
|
||||
await Promise.all(
|
||||
objectRecords.map(async (record) => {
|
||||
const duplicateConditions = buildDuplicateConditions(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
[record],
|
||||
record.id,
|
||||
);
|
||||
@@ -93,12 +100,12 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
|
||||
}
|
||||
|
||||
const duplicateRecordsQueryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
commonQueryParser.applyFilterToBuilder(
|
||||
duplicateRecordsQueryBuilder,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
duplicateConditions,
|
||||
);
|
||||
|
||||
@@ -132,8 +139,9 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
|
||||
|
||||
if (isDefined(args.selectedFieldsResult.relations)) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem: flatObjectMetadata,
|
||||
parentObjectRecords: findDuplicatesOutput.flatMap(
|
||||
(item) => item.records,
|
||||
),
|
||||
@@ -157,7 +165,13 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
|
||||
args: CommonInput<FindDuplicatesQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<FindDuplicatesQueryArgs>> {
|
||||
const { authContext, objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { authContext, flatObjectMetadata, flatFieldMetadataMaps } =
|
||||
queryRunnerContext;
|
||||
|
||||
const { fieldIdByName } = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
|
||||
return {
|
||||
...args,
|
||||
@@ -166,15 +180,17 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
|
||||
this.queryRunnerArgsFactory.overrideValueByFieldMetadata(
|
||||
'id',
|
||||
id,
|
||||
objectMetadataItemWithFieldMaps.fieldsById,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
fieldIdByName,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
) ?? [],
|
||||
),
|
||||
data: await this.dataArgProcessor.process({
|
||||
partialRecordInputs: args.data,
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
shouldBackfillPositionIfUndefined: false,
|
||||
}),
|
||||
};
|
||||
@@ -182,8 +198,9 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: CommonFindDuplicatesOutputItem[],
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<CommonFindDuplicatesOutputItem[]> {
|
||||
const processedResults = await Promise.all(
|
||||
@@ -192,8 +209,9 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne
|
||||
...result,
|
||||
records: await this.commonResultGettersService.processRecordArray(
|
||||
result.records,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
),
|
||||
};
|
||||
|
||||
+28
-20
@@ -30,7 +30,9 @@ import { ProcessAggregateHelper } from 'src/engine/api/graphql/graphql-query-run
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { getCursor } from 'src/engine/api/graphql/graphql-query-runner/utils/cursors.util';
|
||||
import { computeCursorArgFilter } from 'src/engine/api/utils/compute-cursor-arg-filter.utils';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -47,14 +49,15 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
repository,
|
||||
authContext,
|
||||
rolePermissionConfig,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceDataSource,
|
||||
commonQueryParser,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
const aggregateQueryBuilder = queryBuilder.clone();
|
||||
@@ -63,7 +66,7 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
|
||||
commonQueryParser.applyFilterToBuilder(
|
||||
aggregateQueryBuilder,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
appliedFilters,
|
||||
);
|
||||
|
||||
@@ -85,7 +88,8 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
const cursorArgFilter = computeCursorArgFilter(
|
||||
cursor,
|
||||
orderByWithIdCondition,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
isForwardPagination,
|
||||
);
|
||||
|
||||
@@ -98,14 +102,14 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
|
||||
commonQueryParser.applyFilterToBuilder(
|
||||
queryBuilder,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
appliedFilters,
|
||||
);
|
||||
|
||||
commonQueryParser.applyOrderToBuilder(
|
||||
queryBuilder,
|
||||
orderByWithIdCondition,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
isForwardPagination,
|
||||
);
|
||||
|
||||
@@ -114,7 +118,7 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
ProcessAggregateHelper.addSelectedAggregatedFieldsQueriesToQueryBuilder({
|
||||
selectedAggregatedFields: args.selectedFieldsResult.aggregate,
|
||||
queryBuilder: aggregateQueryBuilder,
|
||||
objectMetadataNameSingular: objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectMetadataNameSingular: flatObjectMetadata.nameSingular,
|
||||
});
|
||||
|
||||
const limit = args.first ?? args.last ?? QUERY_MAX_RECORDS;
|
||||
@@ -122,8 +126,9 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
const columnsToSelect = buildColumnsToSelect({
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (isDefined(args.offset)) {
|
||||
@@ -153,11 +158,11 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
|
||||
if (isDefined(args.selectedFieldsResult.relations)) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem: flatObjectMetadata,
|
||||
parentObjectRecords: objectRecords,
|
||||
parentObjectRecordsAggregatedValues,
|
||||
//TODO : Refacto-common - Typing to fix when switching processNestedRelationsHelper to Common
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
FindOptionsRelations<ObjectLiteral>
|
||||
@@ -184,28 +189,31 @@ export class CommonFindManyQueryRunnerService extends CommonBaseQueryRunnerServi
|
||||
args: CommonInput<FindManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<FindManyQueryArgs>> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata, flatFieldMetadataMaps } = queryRunnerContext;
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: CommonFindManyOutput,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<CommonFindManyOutput> {
|
||||
const processedRecords =
|
||||
await this.commonResultGettersService.processRecordArray(
|
||||
queryResult.records,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
|
||||
|
||||
+23
-16
@@ -22,7 +22,9 @@ import {
|
||||
FindOneQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class CommonFindOneQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -40,18 +42,19 @@ export class CommonFindOneQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
authContext,
|
||||
rolePermissionConfig,
|
||||
workspaceDataSource,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
commonQueryParser,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
commonQueryParser.applyFilterToBuilder(
|
||||
queryBuilder,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
args.filter ?? ({} as ObjectRecordFilter),
|
||||
);
|
||||
|
||||
@@ -63,8 +66,9 @@ export class CommonFindOneQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
const columnsToSelect = buildColumnsToSelect({
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const objectRecord = await queryBuilder
|
||||
@@ -84,10 +88,10 @@ export class CommonFindOneQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
|
||||
if (isDefined(args.selectedFieldsResult.relations)) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem: flatObjectMetadata,
|
||||
parentObjectRecords: objectRecords,
|
||||
//TODO : Refacto-common - To fix when switching processNestedRelationsHelper to Common
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
FindOptionsRelations<ObjectLiteral>
|
||||
@@ -107,27 +111,30 @@ export class CommonFindOneQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
args: CommonInput<FindOneQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<FindOneQueryArgs>> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata, flatFieldMetadataMaps } = queryRunnerContext;
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord> {
|
||||
return this.commonResultGettersService.processRecord(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
+46
-27
@@ -44,9 +44,11 @@ import { parseGroupByArgs } from 'src/engine/api/graphql/graphql-query-runner/gr
|
||||
import { GroupByWithRecordsService } from 'src/engine/api/graphql/graphql-query-runner/group-by/services/group-by-with-records.service';
|
||||
import { getGroupLimit } from 'src/engine/api/graphql/graphql-query-runner/group-by/utils/get-group-limit.util';
|
||||
import { ProcessAggregateHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-aggregate.helper';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.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';
|
||||
import { getFlatFieldsFromFlatObjectMetadata } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-flat-fields-for-flat-object-metadata.util';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { ViewFilterGroupService } from 'src/engine/metadata-modules/view-filter-group/services/view-filter-group.service';
|
||||
import { ViewFilterService } from 'src/engine/metadata-modules/view-filter/services/view-filter.service';
|
||||
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
@@ -77,12 +79,13 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
const {
|
||||
repository,
|
||||
commonQueryParser,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const objectMetadataNameSingular =
|
||||
objectMetadataItemWithFieldMaps.nameSingular;
|
||||
const objectMetadataNameSingular = flatObjectMetadata.nameSingular;
|
||||
|
||||
let queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataNameSingular,
|
||||
@@ -90,11 +93,12 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
|
||||
const groupByFields = parseGroupByArgs(
|
||||
args,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
queryRunnerContext.objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
const objectAlias = getObjectAlias(objectMetadataItemWithFieldMaps);
|
||||
const objectAlias = getObjectAlias(flatObjectMetadata);
|
||||
|
||||
this.addJoinForGroupByOnRelationFields({
|
||||
queryBuilder,
|
||||
@@ -108,7 +112,8 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
args,
|
||||
appliedFilters,
|
||||
queryBuilder,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceId: authContext.workspace.id,
|
||||
commonQueryParser,
|
||||
});
|
||||
@@ -166,8 +171,9 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: CommonGroupByOutputItem[],
|
||||
_objectMetadataItemId: string,
|
||||
_objectMetadataMaps: ObjectMetadataMaps,
|
||||
_flatObjectMetadata: FlatObjectMetadata,
|
||||
_flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
_flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
_authContext: WorkspaceAuthContext,
|
||||
): Promise<CommonGroupByOutputItem[]> {
|
||||
return queryResult;
|
||||
@@ -175,12 +181,14 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
|
||||
private async addFiltersFromView({
|
||||
args,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
appliedFilters,
|
||||
workspaceId,
|
||||
}: {
|
||||
args: GroupByQueryArgs;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
appliedFilters: ObjectRecordFilter;
|
||||
workspaceId: string;
|
||||
}): Promise<ObjectRecordFilter> {
|
||||
@@ -197,14 +205,21 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
);
|
||||
|
||||
const recordFilters = viewFilters.map((viewFilter) => {
|
||||
const fieldMetadataItem =
|
||||
objectMetadataItemWithFieldMaps.fieldsById[viewFilter.fieldMetadataId];
|
||||
const fieldMetadata =
|
||||
flatFieldMetadataMaps.byId[viewFilter.fieldMetadataId];
|
||||
|
||||
if (!fieldMetadata) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Field metadata not found for field ${viewFilter.fieldMetadataId}`,
|
||||
CommonQueryRunnerExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
id: viewFilter.id,
|
||||
fieldMetadataId: viewFilter.fieldMetadataId,
|
||||
value: convertViewFilterValueToString(viewFilter.value),
|
||||
type: getFilterTypeFromFieldType(fieldMetadataItem.type),
|
||||
type: getFilterTypeFromFieldType(fieldMetadata.type),
|
||||
operand: viewFilter.operand,
|
||||
recordFilterGroupId: viewFilter.viewFilterGroupId,
|
||||
positionInRecordFilterGroup: viewFilter.positionInViewFilterGroup,
|
||||
@@ -221,8 +236,9 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
};
|
||||
});
|
||||
|
||||
const fields = Object.values(
|
||||
objectMetadataItemWithFieldMaps.fieldsById,
|
||||
const fields = getFlatFieldsFromFlatObjectMetadata(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
).map((field) => ({
|
||||
id: field.id,
|
||||
name: field.name,
|
||||
@@ -263,24 +279,26 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
args,
|
||||
appliedFilters,
|
||||
queryBuilder,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
commonQueryParser,
|
||||
}: {
|
||||
args: GroupByQueryArgs;
|
||||
appliedFilters: ObjectRecordFilter;
|
||||
queryBuilder: WorkspaceSelectQueryBuilder<ObjectLiteral>;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
workspaceId: string;
|
||||
commonQueryParser: GraphqlQueryParser;
|
||||
}): Promise<void> {
|
||||
const objectMetadataNameSingular =
|
||||
objectMetadataItemWithFieldMaps.nameSingular;
|
||||
const objectMetadataNameSingular = flatObjectMetadata.nameSingular;
|
||||
|
||||
if (args.viewId) {
|
||||
appliedFilters = await this.addFiltersFromView({
|
||||
args,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
appliedFilters,
|
||||
workspaceId,
|
||||
});
|
||||
@@ -336,7 +354,7 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
|
||||
if (
|
||||
!groupByField.fieldMetadata.settings ||
|
||||
!isFieldMetadataRelationOrMorphRelation(groupByField.fieldMetadata)
|
||||
!isMorphOrRelationFlatFieldMetadata(groupByField.fieldMetadata)
|
||||
) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Field metadata settings are missing or invalid for field ${groupByField.fieldMetadata.name}`,
|
||||
@@ -370,13 +388,14 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
args: CommonInput<GroupByQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<GroupByQueryArgs>> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata, flatFieldMetadataMaps } = queryRunnerContext;
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
+105
-72
@@ -33,10 +33,12 @@ import { buildColumnsToReturn } from 'src/engine/api/graphql/graphql-query-runne
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { hasRecordFieldValue } from 'src/engine/api/graphql/graphql-query-runner/utils/has-record-field-value.util';
|
||||
import { mergeFieldValues } from 'src/engine/api/graphql/graphql-query-runner/utils/merge-field-values.util';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
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 { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
|
||||
@Injectable()
|
||||
export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -50,8 +52,11 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
args: CommonExtendedInput<MergeManyQueryArgs>,
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext,
|
||||
): Promise<ObjectRecord> {
|
||||
const { objectMetadataMaps, objectMetadataItemWithFieldMaps } =
|
||||
queryRunnerContext;
|
||||
const {
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const recordsToMerge = await this.fetchRecordsToMerge(
|
||||
queryRunnerContext,
|
||||
@@ -67,7 +72,8 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
const mergedData = this.performDeepMerge(
|
||||
recordsToMerge,
|
||||
priorityRecord.id,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
args.dryRun ?? false,
|
||||
);
|
||||
|
||||
@@ -84,14 +90,15 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
);
|
||||
|
||||
const queryBuilder = queryRunnerContext.repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
const columnsToReturn = buildColumnsToReturn({
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
await queryBuilder
|
||||
@@ -123,8 +130,9 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
const columnsToSelect = buildColumnsToSelect({
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps: context.objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: context.objectMetadataMaps,
|
||||
flatObjectMetadata: context.flatObjectMetadata,
|
||||
flatObjectMetadataMaps: context.flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: context.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const recordsToMerge = await context.repository.find({
|
||||
@@ -141,8 +149,9 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
|
||||
if (args.dryRun && args.selectedFieldsResult.relations) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps: context.objectMetadataMaps,
|
||||
parentObjectMetadataItem: context.objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps: context.flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: context.flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem: context.flatObjectMetadata,
|
||||
parentObjectRecords: recordsToMerge as ObjectRecord[],
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
@@ -182,7 +191,8 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
private performDeepMerge(
|
||||
recordsToMerge: ObjectRecord[],
|
||||
priorityRecordId: string,
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
isDryRun = false,
|
||||
): Partial<ObjectRecord> {
|
||||
const mergedResult: Partial<ObjectRecord> = {};
|
||||
@@ -194,7 +204,8 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
if (
|
||||
!this.shouldExcludeFieldFromMerge(
|
||||
fieldName,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
)
|
||||
) {
|
||||
allFieldNames.add(fieldName);
|
||||
@@ -218,9 +229,12 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
} else if (recordsWithValues.length === 1) {
|
||||
mergedResult[fieldName] = recordsWithValues[0].value;
|
||||
} else {
|
||||
const fieldMetadata = Object.values(
|
||||
objectMetadataItemWithFieldMaps.fieldsById,
|
||||
).find((field) => field?.name === fieldName);
|
||||
const { fieldIdByName } = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
const fieldMetadata =
|
||||
flatFieldMetadataMaps.byId[fieldIdByName[fieldName]];
|
||||
|
||||
if (!fieldMetadata) {
|
||||
return;
|
||||
@@ -247,11 +261,14 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
|
||||
private shouldExcludeFieldFromMerge(
|
||||
fieldName: string,
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
): boolean {
|
||||
const fieldMetadata = Object.values(
|
||||
objectMetadataItemWithFieldMaps.fieldsById,
|
||||
).find((field) => field?.name === fieldName);
|
||||
const { fieldIdByName } = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
const fieldMetadata = flatFieldMetadataMaps.byId[fieldIdByName[fieldName]];
|
||||
|
||||
return fieldMetadata?.isSystem ?? false;
|
||||
}
|
||||
@@ -276,18 +293,23 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
priorityRecordId: string,
|
||||
mergedData: Partial<ObjectRecord>,
|
||||
): Promise<ObjectRecord> {
|
||||
const { objectMetadataItemWithFieldMaps, objectMetadataMaps, repository } =
|
||||
queryRunnerContext;
|
||||
const {
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
repository,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
const columnsToReturn = buildColumnsToReturn({
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedObjectRecords = await queryBuilder
|
||||
@@ -314,46 +336,54 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
fromIds: string[],
|
||||
toId: string,
|
||||
): Promise<void> {
|
||||
const { objectMetadataMaps, objectMetadataItemWithFieldMaps } = context;
|
||||
const {
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
} = context;
|
||||
|
||||
const relationFieldsPointingToCurrentObject = Object.values(
|
||||
objectMetadataMaps.byId,
|
||||
)
|
||||
.filter(isDefined)
|
||||
.flatMap((metadata) => {
|
||||
const relationFields = Object.values(metadata.fieldsById)
|
||||
.filter(isDefined)
|
||||
.filter((field) =>
|
||||
isFieldMetadataEntityOfType(field, FieldMetadataType.RELATION),
|
||||
)
|
||||
.filter(
|
||||
(field) =>
|
||||
field.relationTargetObjectMetadataId ===
|
||||
objectMetadataItemWithFieldMaps.id && field.isActive,
|
||||
);
|
||||
const relationFieldsPointingToCurrentObject: Array<{
|
||||
objectMetadata: FlatObjectMetadata;
|
||||
fieldName: string;
|
||||
fieldId: string;
|
||||
joinColumnName: string | undefined;
|
||||
}> = [];
|
||||
|
||||
return relationFields
|
||||
.filter((field) => {
|
||||
const relationSettings =
|
||||
field.settings as FieldMetadataRelationSettings;
|
||||
for (const field of Object.values(flatFieldMetadataMaps.byId).filter(
|
||||
isDefined,
|
||||
)) {
|
||||
if (
|
||||
!isFlatFieldMetadataOfType(field, FieldMetadataType.RELATION) ||
|
||||
field.relationTargetObjectMetadataId !== flatObjectMetadata.id ||
|
||||
!field.isActive
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return (
|
||||
relationSettings?.relationType === RelationType.MANY_TO_ONE &&
|
||||
relationSettings?.joinColumnName
|
||||
);
|
||||
})
|
||||
.map((field) => {
|
||||
const relationSettings =
|
||||
field.settings as FieldMetadataRelationSettings;
|
||||
const relationSettings = field.settings as
|
||||
| FieldMetadataRelationSettings
|
||||
| undefined;
|
||||
|
||||
return {
|
||||
objectMetadata: metadata,
|
||||
fieldName: field.name,
|
||||
fieldId: field.id,
|
||||
joinColumnName: relationSettings.joinColumnName,
|
||||
};
|
||||
});
|
||||
if (
|
||||
relationSettings?.relationType !== RelationType.MANY_TO_ONE ||
|
||||
!relationSettings?.joinColumnName
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const objMetadata = flatObjectMetadataMaps.byId[field.objectMetadataId];
|
||||
|
||||
if (!objMetadata) {
|
||||
continue;
|
||||
}
|
||||
|
||||
relationFieldsPointingToCurrentObject.push({
|
||||
objectMetadata: objMetadata,
|
||||
fieldName: field.name,
|
||||
fieldId: field.id,
|
||||
joinColumnName: relationSettings.joinColumnName,
|
||||
});
|
||||
}
|
||||
|
||||
for (const relationField of relationFieldsPointingToCurrentObject) {
|
||||
if (!relationField.joinColumnName) {
|
||||
@@ -396,8 +426,9 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
updatedRecords: ObjectRecord[];
|
||||
}): Promise<void> {
|
||||
const {
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
@@ -405,8 +436,9 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
|
||||
if (args.selectedFieldsResult.relations) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem: flatObjectMetadata,
|
||||
parentObjectRecords: updatedRecords,
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
@@ -430,8 +462,9 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord,
|
||||
_objectMetadataItemId: string,
|
||||
_objectMetadataMaps: ObjectMetadataMaps,
|
||||
_flatObjectMetadata: FlatObjectMetadata,
|
||||
_flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
_flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
_authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord> {
|
||||
return queryResult;
|
||||
@@ -441,13 +474,13 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
args: CommonInput<MergeManyQueryArgs>,
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
assertMutationNotOnRemoteObject(flatObjectMetadata);
|
||||
|
||||
if (!isDefined(objectMetadataItemWithFieldMaps.duplicateCriteria)) {
|
||||
if (!isDefined(flatObjectMetadata.duplicateCriteria)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Merge is only available for objects with duplicate criteria. Object '${objectMetadataItemWithFieldMaps.nameSingular}' does not have duplicate criteria defined.`,
|
||||
`Merge is only available for objects with duplicate criteria. Object '${flatObjectMetadata.nameSingular}' does not have duplicate criteria defined.`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
+25
-18
@@ -22,8 +22,10 @@ import {
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { buildColumnsToReturn } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-return';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
@Injectable()
|
||||
export class CommonRestoreManyQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -41,26 +43,28 @@ export class CommonRestoreManyQueryRunnerService extends CommonBaseQueryRunnerSe
|
||||
authContext,
|
||||
rolePermissionConfig,
|
||||
workspaceDataSource,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
commonQueryParser,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
commonQueryParser.applyFilterToBuilder(
|
||||
queryBuilder,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
args.filter,
|
||||
);
|
||||
|
||||
const columnsToReturn = buildColumnsToReturn({
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const restoredObjectRecords = await queryBuilder
|
||||
@@ -73,10 +77,10 @@ export class CommonRestoreManyQueryRunnerService extends CommonBaseQueryRunnerSe
|
||||
|
||||
if (isDefined(args.selectedFieldsResult.relations)) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem: flatObjectMetadata,
|
||||
parentObjectRecords: restoredRecords,
|
||||
//TODO : Refacto-common - Typing to fix when switching processNestedRelationsHelper to Common
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
FindOptionsRelations<ObjectLiteral>
|
||||
@@ -96,27 +100,30 @@ export class CommonRestoreManyQueryRunnerService extends CommonBaseQueryRunnerSe
|
||||
args: CommonInput<RestoreManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<RestoreManyQueryArgs>> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata, flatFieldMetadataMaps } = queryRunnerContext;
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord[],
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord[]> {
|
||||
return this.commonResultGettersService.processRecordArray(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
@@ -125,9 +132,9 @@ export class CommonRestoreManyQueryRunnerService extends CommonBaseQueryRunnerSe
|
||||
args: RestoreManyQueryArgs,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
) {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
assertMutationNotOnRemoteObject(flatObjectMetadata);
|
||||
if (!isDefined(args.filter)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Filter is required',
|
||||
|
||||
+11
-7
@@ -20,8 +20,10 @@ import {
|
||||
RestoreOneQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
@Injectable()
|
||||
export class CommonRestoreOneQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -67,14 +69,16 @@ export class CommonRestoreOneQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord> {
|
||||
return this.commonResultGettersService.processRecord(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
@@ -83,9 +87,9 @@ export class CommonRestoreOneQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
args: CommonInput<RestoreOneQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
assertMutationNotOnRemoteObject(flatObjectMetadata);
|
||||
assertIsValidUuid(args.id);
|
||||
}
|
||||
}
|
||||
|
||||
+31
-23
@@ -22,8 +22,10 @@ import {
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { buildColumnsToReturn } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-return';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
@Injectable()
|
||||
export class CommonUpdateManyQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -41,26 +43,28 @@ export class CommonUpdateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
authContext,
|
||||
rolePermissionConfig,
|
||||
workspaceDataSource,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
commonQueryParser,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const queryBuilder = repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
commonQueryParser.applyFilterToBuilder(
|
||||
queryBuilder,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
args.filter,
|
||||
);
|
||||
|
||||
const columnsToReturn = buildColumnsToReturn({
|
||||
select: args.selectedFieldsResult.select,
|
||||
relations: args.selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedObjectRecords = await queryBuilder
|
||||
@@ -73,10 +77,10 @@ export class CommonUpdateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
if (isDefined(args.selectedFieldsResult.relations)) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem: flatObjectMetadata,
|
||||
parentObjectRecords: updatedRecords,
|
||||
//TODO : Refacto-common - Typing to fix when switching processNestedRelationsHelper to Common
|
||||
relations: args.selectedFieldsResult.relations as Record<
|
||||
string,
|
||||
FindOptionsRelations<ObjectLiteral>
|
||||
@@ -96,20 +100,22 @@ export class CommonUpdateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
args: CommonInput<UpdateManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<UpdateManyQueryArgs>> {
|
||||
const { authContext, objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { authContext, flatObjectMetadata, flatFieldMetadataMaps } =
|
||||
queryRunnerContext;
|
||||
|
||||
return {
|
||||
...args,
|
||||
filter:
|
||||
this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
) || {},
|
||||
filter: this.queryRunnerArgsFactory.overrideFilterByFieldMetadata(
|
||||
args.filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
data: (
|
||||
await this.dataArgProcessor.process({
|
||||
partialRecordInputs: [args.data],
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
shouldBackfillPositionIfUndefined: false,
|
||||
})
|
||||
)[0],
|
||||
@@ -120,9 +126,9 @@ export class CommonUpdateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
args: CommonInput<UpdateManyQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
assertMutationNotOnRemoteObject(flatObjectMetadata);
|
||||
if (!args.filter) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Filter is required',
|
||||
@@ -135,14 +141,16 @@ export class CommonUpdateManyQueryRunnerService extends CommonBaseQueryRunnerSer
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord[],
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord[]> {
|
||||
return await this.commonResultGettersService.processRecordArray(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
+15
-9
@@ -19,8 +19,10 @@ import {
|
||||
UpdateOneQueryArgs,
|
||||
} from 'src/engine/api/common/types/common-query-args.type';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
@Injectable()
|
||||
export class CommonUpdateOneQueryRunnerService extends CommonBaseQueryRunnerService<
|
||||
@@ -60,7 +62,8 @@ export class CommonUpdateOneQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
args: CommonInput<UpdateOneQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<CommonInput<UpdateOneQueryArgs>> {
|
||||
const { authContext, objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { authContext, flatObjectMetadata, flatFieldMetadataMaps } =
|
||||
queryRunnerContext;
|
||||
|
||||
return {
|
||||
...args,
|
||||
@@ -68,7 +71,8 @@ export class CommonUpdateOneQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
await this.dataArgProcessor.process({
|
||||
partialRecordInputs: [args.data],
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
shouldBackfillPositionIfUndefined: false,
|
||||
})
|
||||
)[0],
|
||||
@@ -77,14 +81,16 @@ export class CommonUpdateOneQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
|
||||
async processQueryResult(
|
||||
queryResult: ObjectRecord,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<ObjectRecord> {
|
||||
return this.commonResultGettersService.processRecord(
|
||||
queryResult,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
@@ -93,9 +99,9 @@ export class CommonUpdateOneQueryRunnerService extends CommonBaseQueryRunnerServ
|
||||
args: CommonInput<UpdateOneQueryArgs>,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<void> {
|
||||
const { objectMetadataItemWithFieldMaps } = queryRunnerContext;
|
||||
const { flatObjectMetadata } = queryRunnerContext;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItemWithFieldMaps);
|
||||
assertMutationNotOnRemoteObject(flatObjectMetadata);
|
||||
assertIsValidUuid(args.id);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const getObjectAlias = (
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
): string => {
|
||||
return objectMetadataItemWithFieldMaps.nameSingular;
|
||||
return flatObjectMetadata.nameSingular;
|
||||
};
|
||||
|
||||
+34
-22
@@ -15,8 +15,12 @@ import { AttachmentQueryResultGetterHandler } from 'src/engine/api/graphql/works
|
||||
import { PersonQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/person-query-result-getter.handler';
|
||||
import { WorkspaceMemberQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/workspace-member-query-result-getter.handler';
|
||||
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
// TODO: find a way to prevent conflict between handlers executing logic on object relations
|
||||
// And this factory that is also executing logic on object relations
|
||||
@@ -45,8 +49,9 @@ export class CommonResultGettersService {
|
||||
|
||||
public async processRecordArray(
|
||||
recordArray: ObjectRecord[],
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
workspaceId: string,
|
||||
) {
|
||||
return await Promise.all(
|
||||
@@ -54,8 +59,9 @@ export class CommonResultGettersService {
|
||||
async (record: ObjectRecord) =>
|
||||
await this.processRecord(
|
||||
record,
|
||||
objectMetadataItemId,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
),
|
||||
),
|
||||
@@ -64,28 +70,26 @@ export class CommonResultGettersService {
|
||||
|
||||
public async processRecord(
|
||||
record: ObjectRecord,
|
||||
objectMetadataItemId: string,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
workspaceId: string,
|
||||
): Promise<ObjectRecord> {
|
||||
const objectMetadataMapItem = objectMetadataMaps.byId[objectMetadataItemId];
|
||||
const handler = this.getHandler(flatObjectMetadata.nameSingular);
|
||||
|
||||
if (!isDefined(objectMetadataMapItem)) {
|
||||
throw new Error('Object metadata map item is not defined');
|
||||
}
|
||||
|
||||
const handler = this.getHandler(objectMetadataMapItem.nameSingular);
|
||||
const { fieldIdByName } = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
|
||||
const relationFields = Object.keys(record)
|
||||
.map(
|
||||
(recordFieldName) =>
|
||||
objectMetadataMapItem.fieldsById[
|
||||
objectMetadataMapItem.fieldIdByName[recordFieldName]
|
||||
],
|
||||
flatFieldMetadataMaps.byId[fieldIdByName[recordFieldName]],
|
||||
)
|
||||
.filter(isDefined)
|
||||
.filter((fieldMetadata) =>
|
||||
isFieldMetadataEntityOfType(fieldMetadata, FieldMetadataType.RELATION),
|
||||
isFlatFieldMetadataOfType(fieldMetadata, FieldMetadataType.RELATION),
|
||||
);
|
||||
|
||||
const relationFieldsProcessedMap = {} as Record<
|
||||
@@ -104,18 +108,26 @@ export class CommonResultGettersService {
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetFlatObjectMetadata =
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: relationField.relationTargetObjectMetadataId,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
relationFieldsProcessedMap[relationField.name] =
|
||||
relationField.settings?.relationType === RelationType.ONE_TO_MANY
|
||||
? await this.processRecordArray(
|
||||
record[relationField.name],
|
||||
relationField.relationTargetObjectMetadataId,
|
||||
objectMetadataMaps,
|
||||
targetFlatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
)
|
||||
: await this.processRecord(
|
||||
record[relationField.name],
|
||||
relationField.relationTargetObjectMetadataId,
|
||||
objectMetadataMaps,
|
||||
targetFlatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
+7
-4
@@ -1,9 +1,12 @@
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export type CommonBaseQueryRunnerContext = {
|
||||
authContext: AuthContext;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
objectIdByNameSingular: Record<string, string>;
|
||||
};
|
||||
|
||||
+148
-92
@@ -1,24 +1,151 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type WorkspaceEntityDuplicateCriteria } from 'src/engine/api/graphql/workspace-query-builder/types/workspace-entity-duplicate-criteria.type';
|
||||
import { getMockFieldMetadataEntity } from 'src/utils/__test__/get-field-metadata-entity.mock';
|
||||
import { getMockObjectMetadataItemWithFieldsMaps } from 'src/utils/__test__/get-object-metadata-item-with-fields-maps.mock';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
const workspaceId = '20202020-1c25-4d02-bf25-6aeccf7ea419';
|
||||
const objectMetadataId = 'person-object-id';
|
||||
|
||||
export const mockPersonObjectMetadataWithFieldMaps = (
|
||||
const mockFieldMetadatas: FlatFieldMetadata[] = [
|
||||
{
|
||||
id: 'name-id',
|
||||
type: FieldMetadataType.FULL_NAME,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
defaultValue: {
|
||||
lastName: "''",
|
||||
firstName: "''",
|
||||
},
|
||||
objectMetadataId,
|
||||
isNullable: true,
|
||||
isLabelSyncedWithName: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
universalIdentifier: 'name-id',
|
||||
viewFieldIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
applicationId: null,
|
||||
} as unknown as FlatFieldMetadata,
|
||||
{
|
||||
id: 'emails-id',
|
||||
type: FieldMetadataType.EMAILS,
|
||||
name: 'emails',
|
||||
label: 'Emails',
|
||||
defaultValue: {
|
||||
primaryEmail: "''",
|
||||
additionalEmails: null,
|
||||
},
|
||||
objectMetadataId,
|
||||
isNullable: true,
|
||||
isLabelSyncedWithName: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
universalIdentifier: 'emails-id',
|
||||
viewFieldIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
applicationId: null,
|
||||
} as unknown as FlatFieldMetadata,
|
||||
{
|
||||
id: 'linkedinLink-id',
|
||||
type: FieldMetadataType.LINKS,
|
||||
name: 'linkedinLink',
|
||||
label: 'Linkedin',
|
||||
defaultValue: {
|
||||
primaryLinkUrl: "''",
|
||||
secondaryLinks: [],
|
||||
primaryLinkLabel: "''",
|
||||
},
|
||||
objectMetadataId,
|
||||
isNullable: true,
|
||||
isLabelSyncedWithName: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
universalIdentifier: 'linkedinLink-id',
|
||||
viewFieldIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
applicationId: null,
|
||||
} as unknown as FlatFieldMetadata,
|
||||
{
|
||||
id: 'jobTitle-id',
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'jobTitle',
|
||||
label: 'Job Title',
|
||||
defaultValue: "''",
|
||||
objectMetadataId,
|
||||
isNullable: false,
|
||||
isLabelSyncedWithName: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
universalIdentifier: 'jobTitle-id',
|
||||
viewFieldIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
applicationId: null,
|
||||
} as unknown as FlatFieldMetadata,
|
||||
];
|
||||
|
||||
export const mockPersonFlatFieldMetadataMaps =
|
||||
(): FlatEntityMaps<FlatFieldMetadata> => ({
|
||||
byId: mockFieldMetadatas.reduce(
|
||||
(acc, field) => {
|
||||
acc[field.id] = field;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, FlatFieldMetadata>,
|
||||
),
|
||||
idByUniversalIdentifier: mockFieldMetadatas.reduce(
|
||||
(acc, field) => {
|
||||
acc[field.universalIdentifier] = field.id;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
),
|
||||
universalIdentifiersByApplicationId: {},
|
||||
});
|
||||
|
||||
export const mockPersonFlatObjectMetadataMaps = (
|
||||
duplicateCriteria: WorkspaceEntityDuplicateCriteria[],
|
||||
) =>
|
||||
getMockObjectMetadataItemWithFieldsMaps({
|
||||
id: '',
|
||||
): FlatEntityMaps<FlatObjectMetadata> => {
|
||||
const flatObjectMetadata = mockPersonFlatObjectMetadata(duplicateCriteria);
|
||||
|
||||
return {
|
||||
byId: {
|
||||
[flatObjectMetadata.id]: flatObjectMetadata,
|
||||
},
|
||||
idByUniversalIdentifier: {
|
||||
[flatObjectMetadata.universalIdentifier as string]: flatObjectMetadata.id,
|
||||
},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
};
|
||||
};
|
||||
|
||||
export const mockPersonFlatObjectMetadata = (
|
||||
duplicateCriteria: WorkspaceEntityDuplicateCriteria[],
|
||||
): FlatObjectMetadata =>
|
||||
({
|
||||
id: objectMetadataId,
|
||||
icon: 'Icon123',
|
||||
standardId: '',
|
||||
nameSingular: 'person',
|
||||
namePlural: 'people',
|
||||
labelSingular: 'Person',
|
||||
labelPlural: 'People',
|
||||
description: 'A person',
|
||||
targetTableName: 'DEPRECATED',
|
||||
targetTableName: 'person',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
@@ -29,87 +156,16 @@ export const mockPersonObjectMetadataWithFieldMaps = (
|
||||
labelIdentifierFieldMetadataId: '',
|
||||
imageIdentifierFieldMetadataId: '',
|
||||
workspaceId,
|
||||
indexMetadatas: [],
|
||||
fieldIdByName: {
|
||||
name: 'name-id',
|
||||
emails: 'emails-id',
|
||||
linkedinLink: 'linkedinLink-id',
|
||||
jobTitle: 'jobTitle-id',
|
||||
},
|
||||
fieldIdByJoinColumnName: {},
|
||||
fieldsById: {
|
||||
'name-id': getMockFieldMetadataEntity({
|
||||
workspaceId,
|
||||
objectMetadataId: '',
|
||||
id: 'name-id',
|
||||
type: FieldMetadataType.FULL_NAME,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
defaultValue: {
|
||||
lastName: "''",
|
||||
firstName: "''",
|
||||
},
|
||||
description: "Contact's name",
|
||||
isCustom: false,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
isLabelSyncedWithName: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}),
|
||||
'emails-id': getMockFieldMetadataEntity({
|
||||
workspaceId,
|
||||
objectMetadataId: '',
|
||||
id: 'emails-id',
|
||||
type: FieldMetadataType.EMAILS,
|
||||
name: 'emails',
|
||||
label: 'Emails',
|
||||
defaultValue: {
|
||||
primaryEmail: "''",
|
||||
additionalEmails: null,
|
||||
},
|
||||
description: "Contact's Emails",
|
||||
isCustom: false,
|
||||
isNullable: true,
|
||||
isLabelSyncedWithName: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}),
|
||||
'linkedinLink-id': getMockFieldMetadataEntity({
|
||||
workspaceId,
|
||||
objectMetadataId: '',
|
||||
id: 'linkedinLink-id',
|
||||
type: FieldMetadataType.LINKS,
|
||||
name: 'linkedinLink',
|
||||
label: 'Linkedin',
|
||||
defaultValue: {
|
||||
primaryLinkUrl: "''",
|
||||
secondaryLinks: [],
|
||||
primaryLinkLabel: "''",
|
||||
},
|
||||
description: "Contact's Linkedin account",
|
||||
isCustom: false,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
isLabelSyncedWithName: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}),
|
||||
'jobTitle-id': getMockFieldMetadataEntity({
|
||||
workspaceId,
|
||||
objectMetadataId: '',
|
||||
id: 'jobTitle-id',
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'jobTitle',
|
||||
label: 'Job Title',
|
||||
defaultValue: "''",
|
||||
description: "Contact's job title",
|
||||
isCustom: false,
|
||||
isNullable: false,
|
||||
isUnique: false,
|
||||
isLabelSyncedWithName: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}),
|
||||
},
|
||||
});
|
||||
universalIdentifier: objectMetadataId,
|
||||
indexMetadataIds: [],
|
||||
fieldMetadataIds: mockFieldMetadatas.map((field) => field.id),
|
||||
viewIds: [],
|
||||
applicationId: null,
|
||||
isLabelSyncedWithName: false,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
shortcut: null,
|
||||
description: null,
|
||||
standardOverrides: null,
|
||||
isUIReadOnly: false,
|
||||
}) as FlatObjectMetadata;
|
||||
|
||||
+11
-5
@@ -2,19 +2,25 @@ import { Brackets, NotBrackets, type WhereExpressionBuilder } from 'typeorm';
|
||||
|
||||
import { type ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
|
||||
|
||||
import { GraphqlQueryFilterFieldParser } from './graphql-query-filter-field.parser';
|
||||
|
||||
export class GraphqlQueryFilterConditionParser {
|
||||
private objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
private flatObjectMetadata: FlatObjectMetadata;
|
||||
private queryFilterFieldParser: GraphqlQueryFilterFieldParser;
|
||||
|
||||
constructor(objectMetadataMapItem: ObjectMetadataItemWithFieldMaps) {
|
||||
this.objectMetadataMapItem = objectMetadataMapItem;
|
||||
constructor(
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) {
|
||||
this.flatObjectMetadata = flatObjectMetadata;
|
||||
this.queryFilterFieldParser = new GraphqlQueryFilterFieldParser(
|
||||
this.objectMetadataMapItem,
|
||||
this.flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+27
-12
@@ -1,5 +1,5 @@
|
||||
import { compositeTypeDefinitions } from 'twenty-shared/types';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { type WhereExpressionBuilder } from 'typeorm';
|
||||
|
||||
import {
|
||||
@@ -7,18 +7,35 @@ import {
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import { computeWhereConditionParts } from 'src/engine/api/graphql/graphql-query-runner/utils/compute-where-condition-parts';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
|
||||
const ARRAY_OPERATORS = ['in', 'contains', 'notContains'];
|
||||
|
||||
export class GraphqlQueryFilterFieldParser {
|
||||
private objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
private flatObjectMetadata: FlatObjectMetadata;
|
||||
private flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
private fieldIdByName: Record<string, string>;
|
||||
private fieldIdByJoinColumnName: Record<string, string>;
|
||||
|
||||
constructor(objectMetadataMapItem: ObjectMetadataItemWithFieldMaps) {
|
||||
this.objectMetadataMapItem = objectMetadataMapItem;
|
||||
constructor(
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) {
|
||||
this.flatObjectMetadata = flatObjectMetadata;
|
||||
this.flatFieldMetadataMaps = flatFieldMetadataMaps;
|
||||
|
||||
const fieldMaps = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
|
||||
this.fieldIdByName = fieldMaps.fieldIdByName;
|
||||
this.fieldIdByJoinColumnName = fieldMaps.fieldIdByJoinColumnName;
|
||||
}
|
||||
|
||||
public parse(
|
||||
@@ -30,13 +47,11 @@ export class GraphqlQueryFilterFieldParser {
|
||||
isFirst = false,
|
||||
): void {
|
||||
const fieldMetadataId =
|
||||
this.objectMetadataMapItem.fieldIdByName[`${key}`] ||
|
||||
this.objectMetadataMapItem.fieldIdByJoinColumnName[`${key}`];
|
||||
this.fieldIdByName[`${key}`] || this.fieldIdByJoinColumnName[`${key}`];
|
||||
|
||||
const fieldMetadata =
|
||||
this.objectMetadataMapItem.fieldsById[fieldMetadataId];
|
||||
const fieldMetadata = this.flatFieldMetadataMaps.byId[fieldMetadataId];
|
||||
|
||||
if (!fieldMetadata) {
|
||||
if (!isDefined(fieldMetadata)) {
|
||||
throw new Error(`Field metadata not found for field: ${key}`);
|
||||
}
|
||||
|
||||
@@ -77,7 +92,7 @@ export class GraphqlQueryFilterFieldParser {
|
||||
|
||||
private parseCompositeFieldForFilter(
|
||||
queryBuilder: WhereExpressionBuilder,
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
objectNameSingular: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
fieldValue: any,
|
||||
|
||||
+54
-41
@@ -32,12 +32,13 @@ import {
|
||||
type AggregationField,
|
||||
getAvailableAggregationsFromObjectFields,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { formatColumnNameForRelationField } from 'src/engine/twenty-orm/utils/format-column-name-for-relation-field.util';
|
||||
import { formatColumnNamesFromCompositeFieldAndSubfields } from 'src/engine/twenty-orm/utils/format-column-names-from-composite-field-and-subfield.util';
|
||||
|
||||
@@ -47,15 +48,28 @@ export type OrderByCondition = {
|
||||
};
|
||||
|
||||
export class GraphqlQueryOrderFieldParser {
|
||||
private objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
private objectMetadataMaps?: ObjectMetadataMaps;
|
||||
private flatObjectMetadata: FlatObjectMetadata;
|
||||
private flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
private flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
private fieldIdByName: Record<string, string>;
|
||||
private fieldIdByJoinColumnName: Record<string, string>;
|
||||
|
||||
constructor(
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps?: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) {
|
||||
this.objectMetadataMapItem = objectMetadataMapItem;
|
||||
this.objectMetadataMaps = objectMetadataMaps;
|
||||
this.flatObjectMetadata = flatObjectMetadata;
|
||||
this.flatObjectMetadataMaps = flatObjectMetadataMaps;
|
||||
this.flatFieldMetadataMaps = flatFieldMetadataMaps;
|
||||
|
||||
const fieldMaps = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
|
||||
this.fieldIdByName = fieldMaps.fieldIdByName;
|
||||
this.fieldIdByJoinColumnName = fieldMaps.fieldIdByJoinColumnName;
|
||||
}
|
||||
|
||||
parse(
|
||||
@@ -67,10 +81,10 @@ export class GraphqlQueryOrderFieldParser {
|
||||
(acc, item) => {
|
||||
Object.entries(item).forEach(([fieldName, orderByDirection]) => {
|
||||
const fieldMetadataId =
|
||||
this.objectMetadataMapItem.fieldIdByName[fieldName] ||
|
||||
this.objectMetadataMapItem.fieldIdByJoinColumnName[fieldName];
|
||||
this.fieldIdByName[fieldName] ||
|
||||
this.fieldIdByJoinColumnName[fieldName];
|
||||
const fieldMetadata =
|
||||
this.objectMetadataMapItem.fieldsById[fieldMetadataId];
|
||||
this.flatFieldMetadataMaps.byId[fieldMetadataId];
|
||||
|
||||
if (!fieldMetadata || orderByDirection === undefined) {
|
||||
throw new GraphqlQueryRunnerException(
|
||||
@@ -92,9 +106,7 @@ export class GraphqlQueryOrderFieldParser {
|
||||
const orderByCasting =
|
||||
this.getOptionalOrderByCasting(fieldMetadata);
|
||||
|
||||
const columnName = isFieldMetadataRelationOrMorphRelation(
|
||||
fieldMetadata,
|
||||
)
|
||||
const columnName = isMorphOrRelationFlatFieldMetadata(fieldMetadata)
|
||||
? formatColumnNameForRelationField(
|
||||
fieldMetadata.name,
|
||||
fieldMetadata.settings,
|
||||
@@ -124,17 +136,19 @@ export class GraphqlQueryOrderFieldParser {
|
||||
}): Record<string, OrderByCondition>[] {
|
||||
let parsedOrderBy: Record<string, OrderByCondition>[] = [];
|
||||
|
||||
const fields = this.flatObjectMetadata.fieldMetadataIds
|
||||
.map((id) => this.flatFieldMetadataMaps.byId[id])
|
||||
.filter(isDefined);
|
||||
|
||||
const availableAggregations: Record<string, AggregationField> =
|
||||
getAvailableAggregationsFromObjectFields(
|
||||
Object.values(this.objectMetadataMapItem.fieldsById),
|
||||
);
|
||||
getAvailableAggregationsFromObjectFields(fields);
|
||||
|
||||
for (const orderByArg of orderBy) {
|
||||
if (this.isAggregateOrderByArg(orderByArg)) {
|
||||
const parsedAggregateOrderBy = this.parseAggregateOrderByArg(
|
||||
availableAggregations,
|
||||
orderByArg,
|
||||
this.objectMetadataMapItem,
|
||||
this.flatObjectMetadata,
|
||||
);
|
||||
|
||||
parsedOrderBy.push(parsedAggregateOrderBy);
|
||||
@@ -148,10 +162,8 @@ export class GraphqlQueryOrderFieldParser {
|
||||
}
|
||||
|
||||
const fieldName = Object.keys(orderByArg)[0];
|
||||
const fieldMetadataId =
|
||||
this.objectMetadataMapItem.fieldIdByName[fieldName];
|
||||
const fieldMetadata =
|
||||
this.objectMetadataMapItem.fieldsById[fieldMetadataId];
|
||||
const fieldMetadataId = this.fieldIdByName[fieldName];
|
||||
const fieldMetadata = this.flatFieldMetadataMaps.byId[fieldMetadataId];
|
||||
|
||||
if (!isDefined(fieldMetadata)) {
|
||||
throw new UserInputError(`Cannot orderBy unknown field: ${fieldName}.`);
|
||||
@@ -162,7 +174,7 @@ export class GraphqlQueryOrderFieldParser {
|
||||
this.parseObjectRecordOrderByForScalarField({
|
||||
groupByFields,
|
||||
orderByArg,
|
||||
objectMetadataItemWithFieldMaps: this.objectMetadataMapItem,
|
||||
flatObjectMetadata: this.flatObjectMetadata,
|
||||
fieldMetadata,
|
||||
});
|
||||
|
||||
@@ -218,7 +230,7 @@ export class GraphqlQueryOrderFieldParser {
|
||||
this.parseObjectRecordOrderByForCompositeField({
|
||||
groupByFields,
|
||||
orderByArg,
|
||||
objectMetadataItemWithFieldMaps: this.objectMetadataMapItem,
|
||||
flatObjectMetadata: this.flatObjectMetadata,
|
||||
fieldMetadata,
|
||||
});
|
||||
|
||||
@@ -237,7 +249,7 @@ export class GraphqlQueryOrderFieldParser {
|
||||
}
|
||||
|
||||
getOptionalOrderByCasting(
|
||||
fieldMetadata: Pick<FieldMetadataEntity, 'type'>,
|
||||
fieldMetadata: Pick<FlatFieldMetadata, 'type'>,
|
||||
): string {
|
||||
if (
|
||||
fieldMetadata.type === FieldMetadataType.SELECT ||
|
||||
@@ -379,7 +391,7 @@ export class GraphqlQueryOrderFieldParser {
|
||||
parseAggregateOrderByArg = (
|
||||
availableAggregations: Record<string, AggregationField>,
|
||||
orderByArg: AggregateOrderByWithGroupByField,
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
): Record<string, OrderByCondition> => {
|
||||
const aggregate = orderByArg.aggregate;
|
||||
|
||||
@@ -399,7 +411,7 @@ export class GraphqlQueryOrderFieldParser {
|
||||
|
||||
const aggregateExpression = ProcessAggregateHelper.getAggregateExpression(
|
||||
aggregateField,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
if (!isDefined(aggregateExpression)) {
|
||||
@@ -419,13 +431,13 @@ export class GraphqlQueryOrderFieldParser {
|
||||
parseObjectRecordOrderByForScalarField = ({
|
||||
groupByFields,
|
||||
orderByArg,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
fieldMetadata,
|
||||
}: {
|
||||
groupByFields: GroupByField[];
|
||||
orderByArg: ObjectRecordOrderByForScalarField;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
fieldMetadata: FlatFieldMetadata;
|
||||
}): Record<string, OrderByCondition> | null => {
|
||||
const fieldIsInGroupBy = groupByFields.some(
|
||||
(groupByField) => groupByField.fieldMetadata.id === fieldMetadata.id,
|
||||
@@ -445,7 +457,7 @@ export class GraphqlQueryOrderFieldParser {
|
||||
}
|
||||
|
||||
return {
|
||||
[`"${objectMetadataItemWithFieldMaps.nameSingular}"."${fieldMetadata.name}"${orderByCasting}`]:
|
||||
[`"${flatObjectMetadata.nameSingular}"."${fieldMetadata.name}"${orderByCasting}`]:
|
||||
convertOrderByToFindOptionsOrder(orderByDirection),
|
||||
};
|
||||
};
|
||||
@@ -453,13 +465,13 @@ export class GraphqlQueryOrderFieldParser {
|
||||
parseObjectRecordOrderByForCompositeField = ({
|
||||
groupByFields,
|
||||
orderByArg,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
fieldMetadata,
|
||||
}: {
|
||||
groupByFields: GroupByField[];
|
||||
orderByArg: ObjectRecordOrderByForCompositeField;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
fieldMetadata: FlatFieldMetadata;
|
||||
}): Record<string, OrderByCondition> | null => {
|
||||
const fieldName = Object.keys(orderByArg)[0];
|
||||
const orderBySubField = orderByArg[fieldName];
|
||||
@@ -491,7 +503,7 @@ export class GraphqlQueryOrderFieldParser {
|
||||
return parseCompositeFieldForOrder(
|
||||
fieldMetadata,
|
||||
orderBySubField,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -556,7 +568,7 @@ export class GraphqlQueryOrderFieldParser {
|
||||
}: {
|
||||
groupByFields: GroupByField[];
|
||||
orderByArg: ObjectRecordOrderByForRelationField;
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
fieldMetadata: FlatFieldMetadata;
|
||||
}): Record<string, OrderByCondition> | null => {
|
||||
const {
|
||||
associatedGroupByField,
|
||||
@@ -565,7 +577,8 @@ export class GraphqlQueryOrderFieldParser {
|
||||
} = prepareForOrderByRelationFieldParsing({
|
||||
orderByArg,
|
||||
fieldMetadata,
|
||||
objectMetadataMaps: this.objectMetadataMaps,
|
||||
flatObjectMetadataMaps: this.flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: this.flatFieldMetadataMaps,
|
||||
groupByFields,
|
||||
});
|
||||
|
||||
@@ -709,9 +722,9 @@ export class GraphqlQueryOrderFieldParser {
|
||||
| ObjectRecordOrderByForCompositeField
|
||||
| ObjectRecordOrderByWithGroupByDateField
|
||||
| ObjectRecordOrderByForRelationField,
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
): orderByArg is ObjectRecordOrderByForRelationField => {
|
||||
if (!isFieldMetadataRelationOrMorphRelation(fieldMetadata)) {
|
||||
if (!isMorphOrRelationFlatFieldMetadata(fieldMetadata)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -6,11 +6,11 @@ import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
import { type OrderByCondition } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-order/graphql-query-order.parser';
|
||||
import { convertOrderByToFindOptionsOrder } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-order/utils/convert-order-by-to-find-options-order';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
|
||||
export const parseCompositeFieldForOrder = (
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any,
|
||||
objectNameSingular: string,
|
||||
|
||||
+21
-19
@@ -8,26 +8,24 @@ import {
|
||||
type GroupByRelationField,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/types/group-by-field.types';
|
||||
import { isGroupByRelationField } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/is-group-by-relation-field.util';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const prepareForOrderByRelationFieldParsing = ({
|
||||
orderByArg,
|
||||
fieldMetadata,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
groupByFields,
|
||||
}: {
|
||||
orderByArg: ObjectRecordOrderByForRelationField;
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
objectMetadataMaps?: ObjectMetadataMaps;
|
||||
fieldMetadata: FlatFieldMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
groupByFields: GroupByField[];
|
||||
}) => {
|
||||
if (!objectMetadataMaps) {
|
||||
throw new UserInputError(
|
||||
`Object metadata maps not found for field ${fieldMetadata.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
const relationFieldName = Object.keys(orderByArg)[0];
|
||||
const nestedFieldOrderByObject = orderByArg[relationFieldName];
|
||||
|
||||
@@ -57,17 +55,22 @@ export const prepareForOrderByRelationFieldParsing = ({
|
||||
);
|
||||
}
|
||||
|
||||
const targetObjectMetadataItem =
|
||||
objectMetadataMaps?.byId[fieldMetadata.relationTargetObjectMetadataId];
|
||||
const targetObjectMetadata =
|
||||
flatObjectMetadataMaps.byId[fieldMetadata.relationTargetObjectMetadataId];
|
||||
|
||||
if (!isDefined(targetObjectMetadataItem)) {
|
||||
if (!isDefined(targetObjectMetadata)) {
|
||||
throw new UserInputError(
|
||||
`Target object metadata item not found for field ${fieldMetadata.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
const nestedFieldMetadataId =
|
||||
targetObjectMetadataItem?.fieldIdByName[nestedFieldName];
|
||||
const { fieldIdByName: targetFieldIdByName } =
|
||||
buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
targetObjectMetadata,
|
||||
);
|
||||
|
||||
const nestedFieldMetadataId = targetFieldIdByName[nestedFieldName];
|
||||
|
||||
if (!isDefined(nestedFieldMetadataId)) {
|
||||
throw new UserInputError(
|
||||
@@ -75,12 +78,11 @@ export const prepareForOrderByRelationFieldParsing = ({
|
||||
);
|
||||
}
|
||||
|
||||
const nestedFieldMetadata =
|
||||
targetObjectMetadataItem?.fieldsById[nestedFieldMetadataId];
|
||||
const nestedFieldMetadata = flatFieldMetadataMaps.byId[nestedFieldMetadataId];
|
||||
|
||||
if (!isDefined(nestedFieldMetadata) || !isDefined(nestedFieldMetadataId)) {
|
||||
throw new UserInputError(
|
||||
`Nested field "${nestedFieldName}" not found in target object "${targetObjectMetadataItem.nameSingular}"`,
|
||||
`Nested field "${nestedFieldName}" not found in target object "${targetObjectMetadata.nameSingular}"`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+12
-5
@@ -1,21 +1,28 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type GraphqlQuerySelectedFieldsResult } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-selected-fields/graphql-selected-fields.parser';
|
||||
import {
|
||||
type AggregationField,
|
||||
getAvailableAggregationsFromObjectFields,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export class GraphqlQuerySelectedFieldsAggregateParser {
|
||||
parse(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
graphqlSelectedFields: Partial<Record<string, any>>,
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
accumulator: GraphqlQuerySelectedFieldsResult,
|
||||
): void {
|
||||
const fields = flatObjectMetadata.fieldMetadataIds
|
||||
.map((id) => flatFieldMetadataMaps.byId[id])
|
||||
.filter(isDefined);
|
||||
|
||||
const availableAggregations: Record<string, AggregationField> =
|
||||
getAvailableAggregationsFromObjectFields(
|
||||
Object.values(objectMetadataMapItem.fieldsById),
|
||||
);
|
||||
getAvailableAggregationsFromObjectFields(fields);
|
||||
|
||||
for (const selectedField of Object.keys(graphqlSelectedFields)) {
|
||||
const selectedAggregation = availableAggregations[selectedField];
|
||||
|
||||
+27
-13
@@ -1,20 +1,28 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
GraphqlQuerySelectedFieldsParser,
|
||||
type GraphqlQuerySelectedFieldsResult,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-selected-fields/graphql-selected-fields.parser';
|
||||
import { getTargetObjectMetadataOrThrow } from 'src/engine/api/graphql/graphql-query-runner/utils/get-target-object-metadata.util';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export class GraphqlQuerySelectedFieldsRelationParser {
|
||||
private objectMetadataMaps: ObjectMetadataMaps;
|
||||
private flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
private flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
|
||||
constructor(objectMetadataMaps: ObjectMetadataMaps) {
|
||||
this.objectMetadataMaps = objectMetadataMaps;
|
||||
constructor(
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) {
|
||||
this.flatObjectMetadataMaps = flatObjectMetadataMaps;
|
||||
this.flatFieldMetadataMaps = flatFieldMetadataMaps;
|
||||
}
|
||||
|
||||
parseRelationField(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
fieldKey: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
fieldValue: any,
|
||||
@@ -26,18 +34,24 @@ export class GraphqlQuerySelectedFieldsRelationParser {
|
||||
|
||||
accumulator.relations[fieldKey] = true;
|
||||
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
fieldMetadata,
|
||||
this.objectMetadataMaps,
|
||||
);
|
||||
if (!isDefined(fieldMetadata.relationTargetObjectMetadataId)) {
|
||||
throw new Error(
|
||||
`Relation target object metadata id not found for field ${fieldMetadata.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
const targetObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: fieldMetadata.relationTargetObjectMetadataId,
|
||||
flatEntityMaps: this.flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const fieldParser = new GraphqlQuerySelectedFieldsParser(
|
||||
this.objectMetadataMaps,
|
||||
this.flatObjectMetadataMaps,
|
||||
this.flatFieldMetadataMaps,
|
||||
);
|
||||
const relationAccumulator = fieldParser.parse(
|
||||
fieldValue,
|
||||
targetObjectMetadata,
|
||||
this.objectMetadataMaps,
|
||||
);
|
||||
|
||||
accumulator.select[fieldKey] = {
|
||||
|
||||
+39
-31
@@ -6,12 +6,13 @@ import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { GraphqlQuerySelectedFieldsAggregateParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-selected-fields/graphql-selected-fields-aggregate.parser';
|
||||
import { GraphqlQuerySelectedFieldsRelationParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-selected-fields/graphql-selected-fields-relation.parser';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
|
||||
export type GraphqlQuerySelectedFieldsResult = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -25,18 +26,27 @@ export type GraphqlQuerySelectedFieldsResult = {
|
||||
export class GraphqlQuerySelectedFieldsParser {
|
||||
private graphqlQuerySelectedFieldsRelationParser: GraphqlQuerySelectedFieldsRelationParser;
|
||||
private aggregateParser: GraphqlQuerySelectedFieldsAggregateParser;
|
||||
private flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
private flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
|
||||
constructor(objectMetadataMaps: ObjectMetadataMaps) {
|
||||
constructor(
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) {
|
||||
this.flatObjectMetadataMaps = flatObjectMetadataMaps;
|
||||
this.flatFieldMetadataMaps = flatFieldMetadataMaps;
|
||||
this.graphqlQuerySelectedFieldsRelationParser =
|
||||
new GraphqlQuerySelectedFieldsRelationParser(objectMetadataMaps);
|
||||
new GraphqlQuerySelectedFieldsRelationParser(
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
this.aggregateParser = new GraphqlQuerySelectedFieldsAggregateParser();
|
||||
}
|
||||
|
||||
parse(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
graphqlSelectedFields: Partial<Record<string, any>>,
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
): GraphqlQuerySelectedFieldsResult {
|
||||
const accumulator: GraphqlQuerySelectedFieldsResult = {
|
||||
select: {},
|
||||
@@ -47,9 +57,8 @@ export class GraphqlQuerySelectedFieldsParser {
|
||||
if (this.isRootConnection(graphqlSelectedFields)) {
|
||||
this.parseConnectionField(
|
||||
graphqlSelectedFields,
|
||||
objectMetadataMapItem,
|
||||
flatObjectMetadata,
|
||||
accumulator,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
return accumulator;
|
||||
@@ -57,15 +66,15 @@ export class GraphqlQuerySelectedFieldsParser {
|
||||
|
||||
this.aggregateParser.parse(
|
||||
graphqlSelectedFields,
|
||||
objectMetadataMapItem,
|
||||
flatObjectMetadata,
|
||||
this.flatFieldMetadataMaps,
|
||||
accumulator,
|
||||
);
|
||||
|
||||
this.parseRecordFields(
|
||||
graphqlSelectedFields,
|
||||
objectMetadataMapItem,
|
||||
flatObjectMetadata,
|
||||
accumulator,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
return accumulator;
|
||||
@@ -74,15 +83,17 @@ export class GraphqlQuerySelectedFieldsParser {
|
||||
private parseRecordFields(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
graphqlSelectedFields: Partial<Record<string, any>>,
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
accumulator: GraphqlQuerySelectedFieldsResult,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
): void {
|
||||
for (const fieldMetadata of Object.values(
|
||||
objectMetadataMapItem.fieldsById,
|
||||
)) {
|
||||
for (const fieldMetadataId of flatObjectMetadata.fieldMetadataIds) {
|
||||
const fieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: fieldMetadataId,
|
||||
flatEntityMaps: this.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (
|
||||
isFieldMetadataEntityOfType(fieldMetadata, FieldMetadataType.RELATION)
|
||||
isFlatFieldMetadataOfType(fieldMetadata, FieldMetadataType.RELATION)
|
||||
) {
|
||||
const joinColumnName = fieldMetadata.settings?.joinColumnName;
|
||||
|
||||
@@ -111,13 +122,15 @@ export class GraphqlQuerySelectedFieldsParser {
|
||||
}
|
||||
|
||||
if (
|
||||
isFieldMetadataEntityOfType(
|
||||
isFlatFieldMetadataOfType(
|
||||
fieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
) {
|
||||
const targetObjectMetadata =
|
||||
objectMetadataMaps.byId[fieldMetadata.relationTargetObjectMetadataId];
|
||||
this.flatObjectMetadataMaps.byId[
|
||||
fieldMetadata.relationTargetObjectMetadataId
|
||||
];
|
||||
|
||||
if (
|
||||
!fieldMetadata.settings?.relationType ||
|
||||
@@ -182,24 +195,19 @@ export class GraphqlQuerySelectedFieldsParser {
|
||||
private parseConnectionField(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
graphqlSelectedFields: Partial<Record<string, any>>,
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
accumulator: GraphqlQuerySelectedFieldsResult,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
): void {
|
||||
this.aggregateParser.parse(
|
||||
graphqlSelectedFields,
|
||||
objectMetadataMapItem,
|
||||
flatObjectMetadata,
|
||||
this.flatFieldMetadataMaps,
|
||||
accumulator,
|
||||
);
|
||||
|
||||
const node = graphqlSelectedFields.edges.node;
|
||||
|
||||
this.parseRecordFields(
|
||||
node,
|
||||
objectMetadataMapItem,
|
||||
accumulator,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
this.parseRecordFields(node, flatObjectMetadata, accumulator);
|
||||
}
|
||||
|
||||
private isRootConnection(
|
||||
@@ -210,7 +218,7 @@ export class GraphqlQuerySelectedFieldsParser {
|
||||
}
|
||||
|
||||
private parseCompositeField(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
fieldValue: any,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
+20
-31
@@ -6,10 +6,6 @@ import {
|
||||
type ObjectRecordOrderBy,
|
||||
} from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
|
||||
import {
|
||||
GraphqlQueryRunnerException,
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import { GraphqlQueryFilterConditionParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/graphql-query-filter-condition.parser';
|
||||
import { GraphqlQueryOrderFieldParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-order/graphql-query-order.parser';
|
||||
import {
|
||||
@@ -17,30 +13,35 @@ import {
|
||||
type GraphqlQuerySelectedFieldsResult,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-selected-fields/graphql-selected-fields.parser';
|
||||
import { type GroupByField } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/types/group-by-field.types';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
|
||||
|
||||
export class GraphqlQueryParser {
|
||||
private objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
private objectMetadataMaps: ObjectMetadataMaps;
|
||||
private flatObjectMetadata: FlatObjectMetadata;
|
||||
private flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
private flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
private filterConditionParser: GraphqlQueryFilterConditionParser;
|
||||
private orderFieldParser: GraphqlQueryOrderFieldParser;
|
||||
|
||||
constructor(
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) {
|
||||
this.objectMetadataMapItem = objectMetadataMapItem;
|
||||
this.objectMetadataMaps = objectMetadataMaps;
|
||||
this.flatObjectMetadata = flatObjectMetadata;
|
||||
this.flatObjectMetadataMaps = flatObjectMetadataMaps;
|
||||
this.flatFieldMetadataMaps = flatFieldMetadataMaps;
|
||||
|
||||
this.filterConditionParser = new GraphqlQueryFilterConditionParser(
|
||||
this.objectMetadataMapItem,
|
||||
this.flatObjectMetadata,
|
||||
this.flatFieldMetadataMaps,
|
||||
);
|
||||
this.orderFieldParser = new GraphqlQueryOrderFieldParser(
|
||||
this.objectMetadataMapItem,
|
||||
this.objectMetadataMaps,
|
||||
this.flatObjectMetadata,
|
||||
this.flatObjectMetadataMaps,
|
||||
this.flatFieldMetadataMaps,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -143,26 +144,14 @@ export class GraphqlQueryParser {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
graphqlSelectedFields: Partial<Record<string, any>>,
|
||||
): GraphqlQuerySelectedFieldsResult {
|
||||
const objectMetadataMapItem = getObjectMetadataMapItemByNameSingular(
|
||||
this.objectMetadataMaps,
|
||||
this.objectMetadataMapItem.nameSingular,
|
||||
);
|
||||
|
||||
if (!objectMetadataMapItem) {
|
||||
throw new GraphqlQueryRunnerException(
|
||||
`Could not find object metadata for ${this.objectMetadataMapItem.nameSingular}`,
|
||||
GraphqlQueryRunnerExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const selectedFieldsParser = new GraphqlQuerySelectedFieldsParser(
|
||||
this.objectMetadataMaps,
|
||||
this.flatObjectMetadataMaps,
|
||||
this.flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
return selectedFieldsParser.parse(
|
||||
graphqlSelectedFields,
|
||||
this.objectMetadataMapItem,
|
||||
this.objectMetadataMaps,
|
||||
this.flatObjectMetadata,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,21 +1,21 @@
|
||||
import { type ObjectRecordGroupByDateGranularity } from 'twenty-shared/types';
|
||||
import { type FirstDayOfTheWeek } from 'twenty-shared/utils';
|
||||
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
export type GroupByRegularField = {
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
fieldMetadata: FlatFieldMetadata;
|
||||
subFieldName?: string;
|
||||
};
|
||||
export type GroupByDateField = {
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
fieldMetadata: FlatFieldMetadata;
|
||||
subFieldName?: string;
|
||||
dateGranularity: ObjectRecordGroupByDateGranularity;
|
||||
weekStartDay?: FirstDayOfTheWeek;
|
||||
};
|
||||
export type GroupByRelationField = {
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
nestedFieldMetadata: FieldMetadataEntity;
|
||||
fieldMetadata: FlatFieldMetadata;
|
||||
nestedFieldMetadata: FlatFieldMetadata;
|
||||
nestedSubFieldName?: string;
|
||||
dateGranularity?: ObjectRecordGroupByDateGranularity;
|
||||
weekStartDay?: FirstDayOfTheWeek;
|
||||
|
||||
+15
-10
@@ -7,8 +7,9 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type CommonGroupByOutputItem } from 'src/engine/api/common/types/common-group-by-output-item.type';
|
||||
import { type GroupByDefinition } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/types/group-by-definition.type';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
||||
|
||||
export const formatResultWithGroupByDimensionValues = async ({
|
||||
@@ -16,8 +17,9 @@ export const formatResultWithGroupByDimensionValues = async ({
|
||||
groupByDefinitions,
|
||||
aggregateFieldNames,
|
||||
recordsResult,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
processRecord,
|
||||
}: {
|
||||
groupsResult: Record<string, unknown>[];
|
||||
@@ -25,8 +27,9 @@ export const formatResultWithGroupByDimensionValues = async ({
|
||||
aggregateFieldNames: string[];
|
||||
processRecord?: (record: ObjectRecord) => Promise<ObjectRecord>;
|
||||
recordsResult?: Array<Record<string, unknown>>;
|
||||
objectMetadataItemWithFieldMaps?: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps?: ObjectMetadataMaps;
|
||||
flatObjectMetadata?: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps?: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps?: FlatEntityMaps<FlatFieldMetadata>;
|
||||
}): Promise<CommonGroupByOutputItem[]> => {
|
||||
const formattedResult: CommonGroupByOutputItem[] = [];
|
||||
|
||||
@@ -34,8 +37,9 @@ export const formatResultWithGroupByDimensionValues = async ({
|
||||
|
||||
if (isDefined(recordsResult)) {
|
||||
if (
|
||||
!isDefined(objectMetadataItemWithFieldMaps) ||
|
||||
!isDefined(objectMetadataMaps)
|
||||
!isDefined(flatObjectMetadata) ||
|
||||
!isDefined(flatObjectMetadataMaps) ||
|
||||
!isDefined(flatFieldMetadataMaps)
|
||||
) {
|
||||
throw new Error('Metadata are required to format result');
|
||||
}
|
||||
@@ -55,8 +59,9 @@ export const formatResultWithGroupByDimensionValues = async ({
|
||||
await processRecord(
|
||||
formatResult(
|
||||
record,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
+22
-12
@@ -1,5 +1,5 @@
|
||||
import { isDefined } from 'class-validator';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type GroupByResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
|
||||
@@ -7,19 +7,28 @@ import { type GroupByField } from 'src/engine/api/graphql/graphql-query-runner/g
|
||||
import { isGroupByDateFieldDefinition } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/is-group-by-date-field-definition.util';
|
||||
import { parseGroupByRelationField } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/parse-group-by-relation-field.util';
|
||||
import { validateSingleKeyForGroupByOrThrow } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/validate-single-key-for-group-by-or-throw.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const parseGroupByArgs = (
|
||||
args: GroupByResolverArgs,
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
): GroupByField[] => {
|
||||
const groupByFieldNames = args.groupBy;
|
||||
|
||||
const groupByFields: GroupByField[] = [];
|
||||
|
||||
const { fieldIdByName, fieldIdByJoinColumnName } =
|
||||
buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
|
||||
for (const fieldNames of groupByFieldNames) {
|
||||
validateSingleKeyForGroupByOrThrow({
|
||||
groupByKeys: Object.keys(fieldNames),
|
||||
@@ -29,17 +38,17 @@ export const parseGroupByArgs = (
|
||||
|
||||
for (const fieldName of Object.keys(fieldNames)) {
|
||||
const fieldMetadataId =
|
||||
objectMetadataItemWithFieldMaps.fieldIdByName[fieldName] ||
|
||||
objectMetadataItemWithFieldMaps.fieldIdByJoinColumnName[fieldName];
|
||||
const fieldMetadata =
|
||||
objectMetadataItemWithFieldMaps.fieldsById[fieldMetadataId];
|
||||
fieldIdByName[fieldName] || fieldIdByJoinColumnName[fieldName];
|
||||
const fieldMetadata = fieldMetadataId
|
||||
? flatFieldMetadataMaps.byId[fieldMetadataId]
|
||||
: undefined;
|
||||
|
||||
if (!isDefined(fieldMetadata) || !isDefined(fieldMetadataId)) {
|
||||
throw new Error(`Unidentified field in groupBy: ${fieldName}`);
|
||||
}
|
||||
|
||||
const isGroupByRelationField =
|
||||
isFieldMetadataRelationOrMorphRelation(fieldMetadata) &&
|
||||
isMorphOrRelationFlatFieldMetadata(fieldMetadata) &&
|
||||
typeof fieldNames[fieldName] === 'object' &&
|
||||
fieldNames[fieldName] !== null &&
|
||||
!isGroupByDateFieldDefinition(fieldNames[fieldName]);
|
||||
@@ -50,7 +59,8 @@ export const parseGroupByArgs = (
|
||||
fieldNames,
|
||||
fieldName,
|
||||
fieldMetadata,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
groupByFields,
|
||||
});
|
||||
|
||||
|
||||
+38
-23
@@ -1,6 +1,6 @@
|
||||
import { UserInputError } from 'apollo-server-core';
|
||||
import { isDefined } from 'class-validator';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
GraphqlQueryRunnerException,
|
||||
@@ -11,31 +11,41 @@ import { type DateFieldGroupByDefinition } from 'src/engine/api/graphql/graphql-
|
||||
import { type GroupByField } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/types/group-by-field.types';
|
||||
import { isGroupByDateFieldDefinition } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/is-group-by-date-field-definition.util';
|
||||
import { validateSingleKeyForGroupByOrThrow } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/validate-single-key-for-group-by-or-throw.util';
|
||||
import { getTargetObjectMetadataOrThrow } from 'src/engine/api/graphql/graphql-query-runner/utils/get-target-object-metadata.util';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
const getNestedFieldMetadataDetails = ({
|
||||
fieldNames,
|
||||
fieldName,
|
||||
fieldMetadata,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
}: {
|
||||
fieldNames: Record<string, unknown>;
|
||||
fieldName: string;
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
fieldMetadata: FlatFieldMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
}) => {
|
||||
const nestedFieldGroupByDefinitions = fieldNames[fieldName] as
|
||||
| Record<string, boolean>
|
||||
| Record<string, CompositeFieldGroupByDefinition>
|
||||
| Record<string, DateFieldGroupByDefinition>;
|
||||
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
fieldMetadata,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
if (!isDefined(fieldMetadata.relationTargetObjectMetadataId)) {
|
||||
throw new UserInputError(
|
||||
`Relation target object metadata id not found for field ${fieldMetadata.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
const targetObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: fieldMetadata.relationTargetObjectMetadataId,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const nestedFieldNames = Object.keys(nestedFieldGroupByDefinitions);
|
||||
|
||||
@@ -46,10 +56,13 @@ const getNestedFieldMetadataDetails = ({
|
||||
});
|
||||
|
||||
const nestedFieldName = nestedFieldNames[0];
|
||||
const nestedFieldMetadataId =
|
||||
targetObjectMetadata.fieldIdByName[nestedFieldName];
|
||||
const nestedFieldMetadata =
|
||||
targetObjectMetadata.fieldsById[nestedFieldMetadataId];
|
||||
const { fieldIdByName } = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
targetObjectMetadata,
|
||||
);
|
||||
|
||||
const nestedFieldMetadataId = fieldIdByName[nestedFieldName];
|
||||
const nestedFieldMetadata = flatFieldMetadataMaps.byId[nestedFieldMetadataId];
|
||||
|
||||
if (!isDefined(nestedFieldMetadata) || !isDefined(nestedFieldMetadataId)) {
|
||||
throw new GraphqlQueryRunnerException(
|
||||
@@ -83,8 +96,8 @@ const handleNestedCompositeField = ({
|
||||
}: {
|
||||
nestedFieldGroupByDefinition: CompositeFieldGroupByDefinition;
|
||||
nestedFieldName: string;
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
nestedFieldMetadata: FieldMetadataEntity;
|
||||
fieldMetadata: FlatFieldMetadata;
|
||||
nestedFieldMetadata: FlatFieldMetadata;
|
||||
groupByFields: GroupByField[];
|
||||
}) => {
|
||||
if (
|
||||
@@ -127,13 +140,15 @@ export const parseGroupByRelationField = ({
|
||||
fieldNames,
|
||||
fieldName,
|
||||
fieldMetadata,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
groupByFields,
|
||||
}: {
|
||||
fieldNames: Record<string, unknown>;
|
||||
fieldName: string;
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
fieldMetadata: FlatFieldMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
groupByFields: GroupByField[];
|
||||
}) => {
|
||||
const { nestedFieldGroupByDefinition, nestedFieldMetadata, nestedFieldName } =
|
||||
@@ -141,7 +156,8 @@ export const parseGroupByRelationField = ({
|
||||
fieldNames,
|
||||
fieldName,
|
||||
fieldMetadata,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
// Handle date fields in nested relations
|
||||
@@ -150,8 +166,7 @@ export const parseGroupByRelationField = ({
|
||||
nestedFieldMetadata.type === FieldMetadataType.DATE_TIME) &&
|
||||
isGroupByDateFieldDefinition(nestedFieldGroupByDefinition)
|
||||
) {
|
||||
const dateFieldDefinition =
|
||||
nestedFieldGroupByDefinition as DateFieldGroupByDefinition;
|
||||
const dateFieldDefinition = nestedFieldGroupByDefinition;
|
||||
|
||||
groupByFields.push({
|
||||
fieldMetadata,
|
||||
|
||||
+32
-22
@@ -18,8 +18,9 @@ import { formatResultWithGroupByDimensionValues } from 'src/engine/api/graphql/g
|
||||
import { getGroupLimit } from 'src/engine/api/graphql/graphql-query-runner/group-by/utils/get-group-limit.util';
|
||||
import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper';
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
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 { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
|
||||
@@ -66,16 +67,18 @@ export class GroupByWithRecordsService {
|
||||
authContext,
|
||||
workspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
repository,
|
||||
} = queryRunnerContext;
|
||||
|
||||
const columnsToSelect = buildColumnsToSelect({
|
||||
select: selectedFieldsResult.select,
|
||||
relations: selectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const queryBuilderWithPartitionBy = this.addPartitionByToQueryBuilder({
|
||||
@@ -85,16 +88,18 @@ export class GroupByWithRecordsService {
|
||||
groupByDefinitions,
|
||||
repository,
|
||||
orderByForRecords,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const recordsResult = await queryBuilderWithPartitionBy.getRawMany();
|
||||
|
||||
if (!isEmpty(selectedFieldsResult.relations)) {
|
||||
await this.processNestedRelationsHelper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: objectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem: flatObjectMetadata,
|
||||
parentObjectRecords: recordsResult.flatMap((group) => group.records),
|
||||
parentObjectRecordsAggregatedValues: {},
|
||||
relations: selectedFieldsResult.relations,
|
||||
@@ -112,13 +117,15 @@ export class GroupByWithRecordsService {
|
||||
recordsResult,
|
||||
groupByDefinitions,
|
||||
aggregateFieldNames: Object.keys(selectedFieldsResult.aggregate),
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
processRecord: (record: ObjectRecord) =>
|
||||
this.commonResultGettersService.processRecord(
|
||||
record,
|
||||
objectMetadataItemWithFieldMaps.id,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
authContext.workspace.id,
|
||||
),
|
||||
});
|
||||
@@ -131,8 +138,9 @@ export class GroupByWithRecordsService {
|
||||
groupByDefinitions,
|
||||
repository,
|
||||
orderByForRecords,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
}: {
|
||||
queryBuilderForSubQuery: WorkspaceSelectQueryBuilder<ObjectLiteral>;
|
||||
columnsToSelect: Record<string, boolean>;
|
||||
@@ -140,8 +148,9 @@ export class GroupByWithRecordsService {
|
||||
groupByDefinitions: GroupByDefinition[];
|
||||
repository: WorkspaceRepository<ObjectLiteral>;
|
||||
orderByForRecords: ObjectRecordOrderBy;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
}): WorkspaceSelectQueryBuilder<ObjectLiteral> {
|
||||
const groupByExpressions = groupByDefinitions
|
||||
.map((def) => def.expression)
|
||||
@@ -157,7 +166,7 @@ export class GroupByWithRecordsService {
|
||||
queryBuilderForSubQuery,
|
||||
);
|
||||
|
||||
const objectAlias = getObjectAlias(objectMetadataItemWithFieldMaps);
|
||||
const objectAlias = getObjectAlias(flatObjectMetadata);
|
||||
|
||||
const recordSelectWithAlias = Object.keys(columnsToSelect)
|
||||
.map((col) => `"${objectAlias}"."${col}" as "${SUB_QUERY_PREFIX}${col}"`)
|
||||
@@ -175,14 +184,15 @@ export class GroupByWithRecordsService {
|
||||
|
||||
if (!isEmpty(orderByForRecords)) {
|
||||
const graphqlQueryParser = new GraphqlQueryParser(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
graphqlQueryParser.applyOrderToBuilder(
|
||||
subQuery,
|
||||
orderByForRecords,
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+34
-88
@@ -16,21 +16,29 @@ import {
|
||||
import { encodeCursor } from 'src/engine/api/graphql/graphql-query-runner/utils/cursors.util';
|
||||
import { getTargetObjectMetadataOrThrow } from 'src/engine/api/graphql/graphql-query-runner/utils/get-target-object-metadata.util';
|
||||
import { type AggregationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { isFieldMetadataTypeMorphRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-morph-relation.util';
|
||||
import { isFieldMetadataTypeRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-relation.util';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
import { isPlainObject } from 'src/utils/is-plain-object';
|
||||
|
||||
// TODO: Refacto-common - Rename CommonRecordsToGraphqlConnectionHelper
|
||||
export class ObjectRecordsToGraphqlConnectionHelper {
|
||||
private objectMetadataMaps: ObjectMetadataMaps;
|
||||
private flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
private flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
private objectIdByNameSingular: Record<string, string>;
|
||||
|
||||
constructor(objectMetadataMaps: ObjectMetadataMaps) {
|
||||
this.objectMetadataMaps = objectMetadataMaps;
|
||||
constructor(
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
objectIdByNameSingular: Record<string, string>,
|
||||
) {
|
||||
this.flatObjectMetadataMaps = flatObjectMetadataMaps;
|
||||
this.flatFieldMetadataMaps = flatFieldMetadataMaps;
|
||||
this.objectIdByNameSingular = objectIdByNameSingular;
|
||||
}
|
||||
|
||||
public createConnection<T extends ObjectRecord = ObjectRecord>({
|
||||
@@ -154,22 +162,22 @@ export class ObjectRecordsToGraphqlConnectionHelper {
|
||||
);
|
||||
}
|
||||
|
||||
const objectMetadata = getObjectMetadataMapItemByNameSingular(
|
||||
this.objectMetadataMaps,
|
||||
objectName,
|
||||
);
|
||||
const objectMetadataId = this.objectIdByNameSingular[objectName];
|
||||
|
||||
if (!objectMetadata) {
|
||||
throw new GraphqlQueryRunnerException(
|
||||
`Object metadata not found for ${objectName}`,
|
||||
GraphqlQueryRunnerExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: objectMetadataId,
|
||||
flatEntityMaps: this.flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const processedObjectRecord: Record<string, any> = {};
|
||||
|
||||
for (const fieldMetadata of Object.values(objectMetadata.fieldsById)) {
|
||||
for (const fieldId of flatObjectMetadata.fieldMetadataIds) {
|
||||
const fieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: fieldId,
|
||||
flatEntityMaps: this.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
const objectValue = objectRecord[fieldMetadata.name];
|
||||
|
||||
@@ -183,75 +191,13 @@ export class ObjectRecordsToGraphqlConnectionHelper {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isFieldMetadataTypeRelation(fieldMetadata)) {
|
||||
const fieldMetadataNameWithId = `${fieldMetadata.name}Id`;
|
||||
|
||||
if (isDefined(objectRecord[fieldMetadataNameWithId])) {
|
||||
processedObjectRecord[fieldMetadataNameWithId] =
|
||||
objectRecord[fieldMetadataNameWithId];
|
||||
}
|
||||
|
||||
const objectValue = objectRecord[fieldMetadata.name];
|
||||
|
||||
if (!isDefined(objectValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Array.isArray(objectValue)) {
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
fieldMetadata,
|
||||
this.objectMetadataMaps,
|
||||
);
|
||||
|
||||
processedObjectRecord[fieldMetadata.name] = this.createConnection({
|
||||
objectRecords: objectValue,
|
||||
parentObjectRecord: objectRecord,
|
||||
objectRecordsAggregatedValues:
|
||||
objectRecordsAggregatedValues[fieldMetadata.name],
|
||||
selectedAggregatedFields:
|
||||
selectedAggregatedFields[fieldMetadata.name],
|
||||
objectName: targetObjectMetadata.nameSingular,
|
||||
take,
|
||||
totalCount:
|
||||
objectRecordsAggregatedValues[fieldMetadata.name]?.totalCount ??
|
||||
objectValue.length,
|
||||
order,
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
depth: depth + 1,
|
||||
});
|
||||
} else if (isPlainObject(objectValue)) {
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
fieldMetadata,
|
||||
this.objectMetadataMaps,
|
||||
);
|
||||
|
||||
processedObjectRecord[fieldMetadata.name] = this.processRecord({
|
||||
objectRecord: objectValue,
|
||||
objectRecordsAggregatedValues:
|
||||
objectRecordsAggregatedValues[fieldMetadata.name],
|
||||
selectedAggregatedFields:
|
||||
selectedAggregatedFields[fieldMetadata.name],
|
||||
objectName: targetObjectMetadata.nameSingular,
|
||||
take,
|
||||
totalCount,
|
||||
order,
|
||||
depth: depth + 1,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isFieldMetadataTypeMorphRelation(fieldMetadata)) {
|
||||
if (isMorphOrRelationFlatFieldMetadata(fieldMetadata)) {
|
||||
const targetObjectMetadata =
|
||||
this.objectMetadataMaps.byId[
|
||||
this.flatObjectMetadataMaps.byId[
|
||||
fieldMetadata.relationTargetObjectMetadataId
|
||||
];
|
||||
|
||||
if (
|
||||
!fieldMetadata.settings?.relationType ||
|
||||
!isDefined(targetObjectMetadata)
|
||||
) {
|
||||
if (!isDefined(targetObjectMetadata)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -287,9 +233,9 @@ export class ObjectRecordsToGraphqlConnectionHelper {
|
||||
depth: depth + 1,
|
||||
});
|
||||
} else if (isPlainObject(objectValue)) {
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
const targetObjectMetadataOrThrow = getTargetObjectMetadataOrThrow(
|
||||
fieldMetadata,
|
||||
this.objectMetadataMaps,
|
||||
this.flatObjectMetadataMaps,
|
||||
);
|
||||
|
||||
processedObjectRecord[fieldMetadata.name] = this.processRecord({
|
||||
@@ -298,7 +244,7 @@ export class ObjectRecordsToGraphqlConnectionHelper {
|
||||
objectRecordsAggregatedValues[fieldMetadata.name],
|
||||
selectedAggregatedFields:
|
||||
selectedAggregatedFields[fieldMetadata.name],
|
||||
objectName: targetObjectMetadata.nameSingular,
|
||||
objectName: targetObjectMetadataOrThrow.nameSingular,
|
||||
take,
|
||||
totalCount,
|
||||
order,
|
||||
@@ -324,7 +270,7 @@ export class ObjectRecordsToGraphqlConnectionHelper {
|
||||
}
|
||||
|
||||
private processCompositeField(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
fieldValue: any,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
+62
-48
@@ -11,13 +11,14 @@ import {
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import { ProcessAggregateHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-aggregate.helper';
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { getFieldMetadataFromGraphQLField } from 'src/engine/api/graphql/graphql-query-runner/utils/get-field-metadata-from-graphql-field.util';
|
||||
import { getTargetObjectMetadataOrThrow } from 'src/engine/api/graphql/graphql-query-runner/utils/get-target-object-metadata.util';
|
||||
import { type AggregationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||
import { type WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
@@ -28,7 +29,8 @@ export class ProcessNestedRelationsV2Helper {
|
||||
constructor() {}
|
||||
|
||||
public async processNestedRelations<T extends ObjectRecord = ObjectRecord>({
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem,
|
||||
parentObjectRecords,
|
||||
parentObjectRecordsAggregatedValues = {},
|
||||
@@ -40,8 +42,9 @@ export class ProcessNestedRelationsV2Helper {
|
||||
rolePermissionConfig,
|
||||
selectedFields,
|
||||
}: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
parentObjectMetadataItem: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
parentObjectMetadataItem: FlatObjectMetadata;
|
||||
parentObjectRecords: T[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
parentObjectRecordsAggregatedValues?: Record<string, any>;
|
||||
@@ -57,7 +60,8 @@ export class ProcessNestedRelationsV2Helper {
|
||||
const processRelationTasks = Object.entries(relations).map(
|
||||
([sourceFieldName, nestedRelations]) =>
|
||||
this.processRelation({
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem,
|
||||
parentObjectRecords,
|
||||
parentObjectRecordsAggregatedValues,
|
||||
@@ -79,7 +83,8 @@ export class ProcessNestedRelationsV2Helper {
|
||||
}
|
||||
|
||||
private async processRelation<T extends ObjectRecord = ObjectRecord>({
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem,
|
||||
parentObjectRecords,
|
||||
parentObjectRecordsAggregatedValues,
|
||||
@@ -92,8 +97,9 @@ export class ProcessNestedRelationsV2Helper {
|
||||
rolePermissionConfig,
|
||||
selectedFields,
|
||||
}: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
parentObjectMetadataItem: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
parentObjectMetadataItem: FlatObjectMetadata;
|
||||
parentObjectRecords: T[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
parentObjectRecordsAggregatedValues: Record<string, any>;
|
||||
@@ -106,12 +112,20 @@ export class ProcessNestedRelationsV2Helper {
|
||||
rolePermissionConfig?: RolePermissionConfig;
|
||||
selectedFields: Record<string, unknown>;
|
||||
}): Promise<void> {
|
||||
const sourceFieldMetadata = getFieldMetadataFromGraphQLField({
|
||||
objectMetadataItem: parentObjectMetadataItem,
|
||||
graphQLField: sourceFieldName,
|
||||
objectMetadataMaps,
|
||||
const { fieldIdByName } = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem,
|
||||
);
|
||||
|
||||
const sourceFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: fieldIdByName[sourceFieldName],
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!sourceFieldMetadata) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!isFieldMetadataEntityOfType(
|
||||
sourceFieldMetadata,
|
||||
@@ -136,7 +150,8 @@ export class ProcessNestedRelationsV2Helper {
|
||||
const relationType = sourceFieldMetadata.settings?.relationType;
|
||||
const { targetRelationName, targetObjectMetadata, targetRelation } =
|
||||
this.getTargetObjectMetadata({
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem,
|
||||
sourceFieldName,
|
||||
});
|
||||
@@ -155,8 +170,9 @@ export class ProcessNestedRelationsV2Helper {
|
||||
const columnsToSelect = buildColumnsToSelect({
|
||||
select: selectedFields,
|
||||
relations: nestedRelations,
|
||||
objectMetadataItemWithFieldMaps: targetObjectMetadata,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata: targetObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
targetObjectQueryBuilder = targetObjectQueryBuilder.setFindOptions({
|
||||
@@ -208,23 +224,11 @@ export class ProcessNestedRelationsV2Helper {
|
||||
relationType,
|
||||
});
|
||||
|
||||
const targetObjectMetadataItemWithFieldsMaps =
|
||||
getObjectMetadataMapItemByNameSingular(
|
||||
objectMetadataMaps,
|
||||
targetObjectMetadata.nameSingular,
|
||||
);
|
||||
|
||||
if (!targetObjectMetadataItemWithFieldsMaps) {
|
||||
throw new GraphqlQueryRunnerException(
|
||||
`Object ${targetObjectMetadata.nameSingular} not found`,
|
||||
GraphqlQueryRunnerExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (Object.keys(nestedRelations).length > 0) {
|
||||
await this.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
parentObjectMetadataItem: targetObjectMetadataItemWithFieldsMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem: targetObjectMetadata,
|
||||
parentObjectRecords: relationResults as ObjectRecord[],
|
||||
parentObjectRecordsAggregatedValues: relationAggregatedFieldsResult,
|
||||
relations: nestedRelations as Record<
|
||||
@@ -242,23 +246,36 @@ export class ProcessNestedRelationsV2Helper {
|
||||
}
|
||||
|
||||
private getTargetObjectMetadata({
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem,
|
||||
sourceFieldName,
|
||||
}: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
parentObjectMetadataItem: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
parentObjectMetadataItem: FlatObjectMetadata;
|
||||
sourceFieldName: string;
|
||||
}) {
|
||||
const targetFieldMetadata = getFieldMetadataFromGraphQLField({
|
||||
objectMetadataItem: parentObjectMetadataItem,
|
||||
graphQLField: sourceFieldName,
|
||||
objectMetadataMaps,
|
||||
const { fieldIdByName } = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem,
|
||||
);
|
||||
|
||||
const targetFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: fieldIdByName[sourceFieldName],
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!targetFieldMetadata) {
|
||||
throw new GraphqlQueryRunnerException(
|
||||
`Field ${sourceFieldName} not found on object ${parentObjectMetadataItem.nameSingular}`,
|
||||
GraphqlQueryRunnerExceptionCode.FIELD_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
targetFieldMetadata,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -272,14 +289,11 @@ export class ProcessNestedRelationsV2Helper {
|
||||
}
|
||||
|
||||
const targetRelation =
|
||||
objectMetadataMaps.byId[
|
||||
targetFieldMetadata.relationTargetObjectMetadataId
|
||||
]?.fieldsById[targetFieldMetadata.relationTargetFieldMetadataId];
|
||||
flatFieldMetadataMaps.byId[
|
||||
targetFieldMetadata.relationTargetFieldMetadataId
|
||||
];
|
||||
|
||||
const targetRelationName =
|
||||
objectMetadataMaps.byId[
|
||||
targetFieldMetadata.relationTargetObjectMetadataId
|
||||
]?.fieldsById[targetFieldMetadata.relationTargetFieldMetadataId]?.name;
|
||||
const targetRelationName = targetRelation?.name;
|
||||
|
||||
return { targetRelationName, targetObjectMetadata, targetRelation };
|
||||
}
|
||||
|
||||
+10
-6
@@ -6,8 +6,9 @@ import { type FindOptionsRelations, type ObjectLiteral } from 'typeorm';
|
||||
import { ProcessNestedRelationsV2Helper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations-v2.helper';
|
||||
import { type AggregationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||
import { RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
|
||||
@@ -18,7 +19,8 @@ export class ProcessNestedRelationsHelper {
|
||||
) {}
|
||||
|
||||
public async processNestedRelations<T extends ObjectRecord = ObjectRecord>({
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem,
|
||||
parentObjectRecords,
|
||||
parentObjectRecordsAggregatedValues = {},
|
||||
@@ -30,8 +32,9 @@ export class ProcessNestedRelationsHelper {
|
||||
rolePermissionConfig,
|
||||
selectedFields,
|
||||
}: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
parentObjectMetadataItem: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
parentObjectMetadataItem: FlatObjectMetadata;
|
||||
parentObjectRecords: T[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
parentObjectRecordsAggregatedValues?: Record<string, any>;
|
||||
@@ -45,7 +48,8 @@ export class ProcessNestedRelationsHelper {
|
||||
selectedFields: Record<string, any>;
|
||||
}): Promise<void> {
|
||||
return this.processNestedRelationsV2Helper.processNestedRelations({
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
parentObjectMetadataItem,
|
||||
parentObjectRecords,
|
||||
parentObjectRecordsAggregatedValues,
|
||||
|
||||
+280
-143
@@ -3,115 +3,185 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
describe('buildColumnsToSelect', () => {
|
||||
const mockObjectMetadataItemWithFieldMaps: any = {
|
||||
id: 'a55d8cad-4c3d-4c8a-82b5-539a36de5605',
|
||||
standardId: '20202020-e674-48e5-a542-72570eee7213',
|
||||
dataSourceId: 'd161a12f-1daa-4a0b-887c-96a48a3d9ecf',
|
||||
nameSingular: 'person',
|
||||
namePlural: 'people',
|
||||
labelSingular: 'Person',
|
||||
labelPlural: 'People',
|
||||
description: 'A person',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isAuditLogged: true,
|
||||
isSearchable: true,
|
||||
duplicateCriteria: [
|
||||
['nameFirstName', 'nameLastName'],
|
||||
['linkedinLinkPrimaryLinkUrl'],
|
||||
['emailsPrimaryEmail'],
|
||||
],
|
||||
shortcut: 'P',
|
||||
labelIdentifierFieldMetadataId: '92414583-c6a6-4c98-bae7-6ce318bd3423',
|
||||
imageIdentifierFieldMetadataId: '6bfcecc4-1866-4254-ba6b-6f22246819bb',
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
createdAt: new Date('2025-07-10T10:58:54.536Z'),
|
||||
updatedAt: new Date('2025-07-10T10:58:54.536Z'),
|
||||
indexMetadatas: [],
|
||||
fieldsById: {
|
||||
'92414583-c6a6-4c98-bae7-6ce318bd3423': {
|
||||
id: '92414583-c6a6-4c98-bae7-6ce318bd3423',
|
||||
type: FieldMetadataType.FULL_NAME,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
defaultValue: {
|
||||
lastName: "''",
|
||||
firstName: "''",
|
||||
},
|
||||
description: "Contact's name",
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
options: undefined,
|
||||
settings: undefined,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: undefined,
|
||||
relationTargetObjectMetadataId: undefined,
|
||||
objectMetadataId: 'a55d8cad-4c3d-4c8a-82b5-539a36de5605',
|
||||
createdAt: new Date('2025-07-10T10:58:54.536Z'),
|
||||
updatedAt: new Date('2025-07-10T10:58:54.536Z'),
|
||||
},
|
||||
'30dc1370-bb1a-42e3-abbc-a40edf1c6796': {
|
||||
id: '30dc1370-bb1a-42e3-abbc-a40edf1c6796',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'company',
|
||||
label: 'Company',
|
||||
defaultValue: null,
|
||||
description: "Contact's company",
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
options: undefined,
|
||||
settings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'companyId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '83cd9e7f-dfbc-4b93-a0a7-4c04b76d2009',
|
||||
relationTargetObjectMetadataId: '9af20778-2f2c-4e22-ae83-2e77e479b57c',
|
||||
objectMetadataId: 'a55d8cad-4c3d-4c8a-82b5-539a36de5605',
|
||||
createdAt: new Date('2025-07-10T10:58:54.536Z'),
|
||||
updatedAt: new Date('2025-07-10T10:58:54.536Z'),
|
||||
},
|
||||
},
|
||||
fieldIdByName: {
|
||||
name: '92414583-c6a6-4c98-bae7-6ce318bd3423',
|
||||
company: '30dc1370-bb1a-42e3-abbc-a40edf1c6796',
|
||||
},
|
||||
fieldIdByJoinColumnName: {
|
||||
companyId: '30dc1370-bb1a-42e3-abbc-a40edf1c6796',
|
||||
},
|
||||
};
|
||||
const workspaceId = '20202020-1c25-4d02-bf25-6aeccf7ea419';
|
||||
const personObjectId = 'a55d8cad-4c3d-4c8a-82b5-539a36de5605';
|
||||
const companyObjectId = '9af20778-2f2c-4e22-ae83-2e77e479b57c';
|
||||
|
||||
const mockObjectMetadataMaps = {
|
||||
byId: {
|
||||
'9af20778-2f2c-4e22-ae83-2e77e479b57c': {
|
||||
id: '9af20778-2f2c-4e22-ae83-2e77e479b57c',
|
||||
nameSingular: 'company',
|
||||
namePlural: 'companies',
|
||||
},
|
||||
const nameFieldId = '92414583-c6a6-4c98-bae7-6ce318bd3423';
|
||||
const companyFieldId = '30dc1370-bb1a-42e3-abbc-a40edf1c6796';
|
||||
|
||||
const createMockField = (
|
||||
overrides: Partial<FlatFieldMetadata> & {
|
||||
id: string;
|
||||
name: string;
|
||||
type: FieldMetadataType;
|
||||
objectMetadataId: string;
|
||||
},
|
||||
};
|
||||
): FlatFieldMetadata =>
|
||||
({
|
||||
workspaceId,
|
||||
isNullable: true,
|
||||
isLabelSyncedWithName: true,
|
||||
createdAt: new Date('2025-07-10T10:58:54.536Z'),
|
||||
updatedAt: new Date('2025-07-10T10:58:54.536Z'),
|
||||
universalIdentifier: overrides.id,
|
||||
viewFieldIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
applicationId: null,
|
||||
label: overrides.name,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isUnique: false,
|
||||
...overrides,
|
||||
}) as FlatFieldMetadata;
|
||||
|
||||
const nameField = createMockField({
|
||||
id: nameFieldId,
|
||||
type: FieldMetadataType.FULL_NAME,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
objectMetadataId: personObjectId,
|
||||
defaultValue: {
|
||||
lastName: "''",
|
||||
firstName: "''",
|
||||
},
|
||||
description: "Contact's name",
|
||||
icon: 'IconUser',
|
||||
});
|
||||
|
||||
const companyRelationField = createMockField({
|
||||
id: companyFieldId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'company',
|
||||
label: 'Company',
|
||||
objectMetadataId: personObjectId,
|
||||
defaultValue: null,
|
||||
description: "Contact's company",
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
settings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'companyId',
|
||||
},
|
||||
relationTargetFieldMetadataId: '83cd9e7f-dfbc-4b93-a0a7-4c04b76d2009',
|
||||
relationTargetObjectMetadataId: companyObjectId,
|
||||
});
|
||||
|
||||
const buildMockFlatObjectMetadata = (
|
||||
fieldIds: string[],
|
||||
): FlatObjectMetadata =>
|
||||
({
|
||||
id: personObjectId,
|
||||
standardId: '20202020-e674-48e5-a542-72570eee7213',
|
||||
nameSingular: 'person',
|
||||
namePlural: 'people',
|
||||
labelSingular: 'Person',
|
||||
labelPlural: 'People',
|
||||
description: 'A person',
|
||||
icon: 'IconUser',
|
||||
targetTableName: 'DEPRECATED',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isAuditLogged: true,
|
||||
isSearchable: true,
|
||||
duplicateCriteria: [
|
||||
['nameFirstName', 'nameLastName'],
|
||||
['linkedinLinkPrimaryLinkUrl'],
|
||||
['emailsPrimaryEmail'],
|
||||
],
|
||||
shortcut: 'P',
|
||||
labelIdentifierFieldMetadataId: nameFieldId,
|
||||
imageIdentifierFieldMetadataId: '6bfcecc4-1866-4254-ba6b-6f22246819bb',
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId,
|
||||
createdAt: new Date('2025-07-10T10:58:54.536Z'),
|
||||
updatedAt: new Date('2025-07-10T10:58:54.536Z'),
|
||||
universalIdentifier: personObjectId,
|
||||
fieldMetadataIds: fieldIds,
|
||||
indexMetadataIds: [],
|
||||
viewIds: [],
|
||||
applicationId: null,
|
||||
}) as unknown as FlatObjectMetadata;
|
||||
|
||||
const buildFlatFieldMetadataMaps = (
|
||||
fields: FlatFieldMetadata[],
|
||||
): FlatEntityMaps<FlatFieldMetadata> => ({
|
||||
byId: fields.reduce(
|
||||
(acc, field) => {
|
||||
acc[field.id] = field;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, FlatFieldMetadata>,
|
||||
),
|
||||
idByUniversalIdentifier: fields.reduce(
|
||||
(acc, field) => {
|
||||
acc[field.universalIdentifier] = field.id;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
),
|
||||
universalIdentifiersByApplicationId: {},
|
||||
});
|
||||
|
||||
const buildFlatObjectMetadataMaps = (
|
||||
objects: FlatObjectMetadata[],
|
||||
): FlatEntityMaps<FlatObjectMetadata> => ({
|
||||
byId: objects.reduce(
|
||||
(acc, obj) => {
|
||||
acc[obj.id] = obj;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, FlatObjectMetadata>,
|
||||
),
|
||||
idByUniversalIdentifier: objects.reduce(
|
||||
(acc, obj) => {
|
||||
if (obj.universalIdentifier) {
|
||||
acc[obj.universalIdentifier] = obj.id;
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
),
|
||||
universalIdentifiersByApplicationId: {},
|
||||
});
|
||||
|
||||
const companyObjectMetadata: FlatObjectMetadata = {
|
||||
id: companyObjectId,
|
||||
nameSingular: 'company',
|
||||
namePlural: 'companies',
|
||||
fieldMetadataIds: [],
|
||||
indexMetadataIds: [],
|
||||
viewIds: [],
|
||||
} as unknown as FlatObjectMetadata;
|
||||
|
||||
it('should build columns to select with relation fields', () => {
|
||||
const flatObjectMetadata = buildMockFlatObjectMetadata([
|
||||
nameFieldId,
|
||||
companyFieldId,
|
||||
]);
|
||||
const flatFieldMetadataMaps = buildFlatFieldMetadataMaps([
|
||||
nameField,
|
||||
companyRelationField,
|
||||
]);
|
||||
const flatObjectMetadataMaps = buildFlatObjectMetadataMaps([
|
||||
flatObjectMetadata,
|
||||
companyObjectMetadata,
|
||||
]);
|
||||
|
||||
const select = {
|
||||
nameFirstName: true,
|
||||
company: {
|
||||
@@ -127,8 +197,9 @@ describe('buildColumnsToSelect', () => {
|
||||
const result = buildColumnsToSelect({
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -139,6 +210,19 @@ describe('buildColumnsToSelect', () => {
|
||||
});
|
||||
|
||||
it('should build columns to select without relation fields', () => {
|
||||
const flatObjectMetadata = buildMockFlatObjectMetadata([
|
||||
nameFieldId,
|
||||
companyFieldId,
|
||||
]);
|
||||
const flatFieldMetadataMaps = buildFlatFieldMetadataMaps([
|
||||
nameField,
|
||||
companyRelationField,
|
||||
]);
|
||||
const flatObjectMetadataMaps = buildFlatObjectMetadataMaps([
|
||||
flatObjectMetadata,
|
||||
companyObjectMetadata,
|
||||
]);
|
||||
|
||||
const select = {
|
||||
nameFirstName: true,
|
||||
nameLastName: true,
|
||||
@@ -149,8 +233,9 @@ describe('buildColumnsToSelect', () => {
|
||||
const result = buildColumnsToSelect({
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -161,6 +246,19 @@ describe('buildColumnsToSelect', () => {
|
||||
});
|
||||
|
||||
it('should filter out non-boolean values from select', () => {
|
||||
const flatObjectMetadata = buildMockFlatObjectMetadata([
|
||||
nameFieldId,
|
||||
companyFieldId,
|
||||
]);
|
||||
const flatFieldMetadataMaps = buildFlatFieldMetadataMaps([
|
||||
nameField,
|
||||
companyRelationField,
|
||||
]);
|
||||
const flatObjectMetadataMaps = buildFlatObjectMetadataMaps([
|
||||
flatObjectMetadata,
|
||||
companyObjectMetadata,
|
||||
]);
|
||||
|
||||
const select = {
|
||||
nameFirstName: true,
|
||||
nameLastName: false,
|
||||
@@ -172,8 +270,9 @@ describe('buildColumnsToSelect', () => {
|
||||
const result = buildColumnsToSelect({
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -183,6 +282,19 @@ describe('buildColumnsToSelect', () => {
|
||||
});
|
||||
|
||||
it('should handle relation field that is not a relation type', () => {
|
||||
const flatObjectMetadata = buildMockFlatObjectMetadata([
|
||||
nameFieldId,
|
||||
companyFieldId,
|
||||
]);
|
||||
const flatFieldMetadataMaps = buildFlatFieldMetadataMaps([
|
||||
nameField,
|
||||
companyRelationField,
|
||||
]);
|
||||
const flatObjectMetadataMaps = buildFlatObjectMetadataMaps([
|
||||
flatObjectMetadata,
|
||||
companyObjectMetadata,
|
||||
]);
|
||||
|
||||
const select = {
|
||||
nameFirstName: true,
|
||||
};
|
||||
@@ -194,8 +306,9 @@ describe('buildColumnsToSelect', () => {
|
||||
const result = buildColumnsToSelect({
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -205,21 +318,32 @@ describe('buildColumnsToSelect', () => {
|
||||
});
|
||||
|
||||
it('should handle relation field that is not MANY_TO_ONE', () => {
|
||||
const mockObjectMetadataWithOneToMany: any = {
|
||||
...mockObjectMetadataItemWithFieldMaps,
|
||||
fieldsById: {
|
||||
...mockObjectMetadataItemWithFieldMaps.fieldsById,
|
||||
'30dc1370-bb1a-42e3-abbc-a40edf1c6796': {
|
||||
...mockObjectMetadataItemWithFieldMaps.fieldsById[
|
||||
'30dc1370-bb1a-42e3-abbc-a40edf1c6796'
|
||||
],
|
||||
settings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
joinColumnName: 'companyId',
|
||||
},
|
||||
},
|
||||
const oneToManyCompanyField = createMockField({
|
||||
id: companyFieldId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'company',
|
||||
label: 'Company',
|
||||
objectMetadataId: personObjectId,
|
||||
defaultValue: null,
|
||||
settings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
joinColumnName: 'companyId',
|
||||
},
|
||||
};
|
||||
relationTargetObjectMetadataId: companyObjectId,
|
||||
});
|
||||
|
||||
const flatObjectMetadata = buildMockFlatObjectMetadata([
|
||||
nameFieldId,
|
||||
companyFieldId,
|
||||
]);
|
||||
const flatFieldMetadataMaps = buildFlatFieldMetadataMaps([
|
||||
nameField,
|
||||
oneToManyCompanyField,
|
||||
]);
|
||||
const flatObjectMetadataMaps = buildFlatObjectMetadataMaps([
|
||||
flatObjectMetadata,
|
||||
companyObjectMetadata,
|
||||
]);
|
||||
|
||||
const select = {
|
||||
nameFirstName: true,
|
||||
@@ -232,8 +356,9 @@ describe('buildColumnsToSelect', () => {
|
||||
const result = buildColumnsToSelect({
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataWithOneToMany,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -243,21 +368,32 @@ describe('buildColumnsToSelect', () => {
|
||||
});
|
||||
|
||||
it('should handle relation field without joinColumnName', () => {
|
||||
const mockObjectMetadataWithoutJoinColumn: any = {
|
||||
...mockObjectMetadataItemWithFieldMaps,
|
||||
fieldsById: {
|
||||
...mockObjectMetadataItemWithFieldMaps.fieldsById,
|
||||
'30dc1370-bb1a-42e3-abbc-a40edf1c6796': {
|
||||
...mockObjectMetadataItemWithFieldMaps.fieldsById[
|
||||
'30dc1370-bb1a-42e3-abbc-a40edf1c6796'
|
||||
],
|
||||
settings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: null,
|
||||
},
|
||||
},
|
||||
const noJoinColumnCompanyField = createMockField({
|
||||
id: companyFieldId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'company',
|
||||
label: 'Company',
|
||||
objectMetadataId: personObjectId,
|
||||
defaultValue: null,
|
||||
settings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: null,
|
||||
},
|
||||
};
|
||||
relationTargetObjectMetadataId: companyObjectId,
|
||||
});
|
||||
|
||||
const flatObjectMetadata = buildMockFlatObjectMetadata([
|
||||
nameFieldId,
|
||||
companyFieldId,
|
||||
]);
|
||||
const flatFieldMetadataMaps = buildFlatFieldMetadataMaps([
|
||||
nameField,
|
||||
noJoinColumnCompanyField,
|
||||
]);
|
||||
const flatObjectMetadataMaps = buildFlatObjectMetadataMaps([
|
||||
flatObjectMetadata,
|
||||
companyObjectMetadata,
|
||||
]);
|
||||
|
||||
const select = {
|
||||
nameFirstName: true,
|
||||
@@ -270,8 +406,9 @@ describe('buildColumnsToSelect', () => {
|
||||
const result = buildColumnsToSelect({
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataWithoutJoinColumn,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
|
||||
+12
-8
@@ -1,24 +1,28 @@
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const buildColumnsToReturn = ({
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
}: {
|
||||
select: Record<string, unknown>;
|
||||
relations: Record<string, unknown>;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
}): string[] => {
|
||||
return Object.entries(
|
||||
buildColumnsToSelect({
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
}),
|
||||
)
|
||||
.filter(([_columnName, value]) => value === true)
|
||||
|
||||
+34
-30
@@ -1,32 +1,32 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { isFieldMetadataTypeMorphRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-morph-relation.util';
|
||||
import { isFieldMetadataTypeRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-relation.util';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const buildColumnsToSelect = ({
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
}: {
|
||||
select: Record<string, unknown>;
|
||||
relations: Record<string, unknown>;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: {
|
||||
byId: Partial<
|
||||
Record<
|
||||
string,
|
||||
Pick<ObjectMetadataItemWithFieldMaps, 'nameSingular' | 'namePlural'>
|
||||
>
|
||||
>;
|
||||
};
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
}) => {
|
||||
const requiredRelationColumns = getRequiredRelationColumns(
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
const fieldsToSelect: Record<string, boolean> = Object.entries(select)
|
||||
@@ -44,20 +44,19 @@ export const buildColumnsToSelect = ({
|
||||
|
||||
const getRequiredRelationColumns = (
|
||||
relations: Record<string, unknown>,
|
||||
objectMetadataItem: Pick<ObjectMetadataItemWithFieldMaps, 'fieldsById'>,
|
||||
objectMetadataMaps: {
|
||||
byId: Partial<
|
||||
Record<
|
||||
string,
|
||||
Pick<ObjectMetadataItemWithFieldMaps, 'nameSingular' | 'namePlural'>
|
||||
>
|
||||
>;
|
||||
},
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
): string[] => {
|
||||
const requiredColumns: string[] = [];
|
||||
|
||||
for (const fieldMetadata of Object.values(objectMetadataItem.fieldsById)) {
|
||||
if (isFieldMetadataTypeRelation(fieldMetadata)) {
|
||||
for (const fieldId of flatObjectMetadata.fieldMetadataIds) {
|
||||
const fieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: fieldId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (isFlatFieldMetadataOfType(fieldMetadata, FieldMetadataType.RELATION)) {
|
||||
const relationValue = relations[fieldMetadata.name];
|
||||
|
||||
if (
|
||||
@@ -71,9 +70,14 @@ const getRequiredRelationColumns = (
|
||||
requiredColumns.push(fieldMetadata.settings.joinColumnName);
|
||||
}
|
||||
|
||||
if (isFieldMetadataTypeMorphRelation(fieldMetadata)) {
|
||||
const targetObjectMetadata =
|
||||
objectMetadataMaps.byId[fieldMetadata.relationTargetObjectMetadataId];
|
||||
if (
|
||||
isFlatFieldMetadataOfType(fieldMetadata, FieldMetadataType.MORPH_RELATION)
|
||||
) {
|
||||
const targetObjectMetadata = fieldMetadata.relationTargetObjectMetadataId
|
||||
? flatObjectMetadataMaps.byId[
|
||||
fieldMetadata.relationTargetObjectMetadataId
|
||||
]
|
||||
: undefined;
|
||||
|
||||
if (
|
||||
!fieldMetadata.settings?.relationType ||
|
||||
|
||||
+46
-31
@@ -2,56 +2,71 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { getTargetObjectMetadataOrThrow } from 'src/engine/api/graphql/graphql-query-runner/utils/get-target-object-metadata.util';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isFieldMetadataTypeMorphRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-morph-relation.util';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { getFlatFieldsFromFlatObjectMetadata } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-flat-fields-for-flat-object-metadata.util';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
type GetFieldMetadataFromGraphQLFieldArgs = {
|
||||
objectMetadataItem: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
graphQLField: string;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
};
|
||||
|
||||
export function getFieldMetadataFromGraphQLField({
|
||||
objectMetadataItem,
|
||||
flatObjectMetadata,
|
||||
graphQLField,
|
||||
objectMetadataMaps,
|
||||
}: GetFieldMetadataFromGraphQLFieldArgs) {
|
||||
const sourceFieldMetadataId = objectMetadataItem.fieldIdByName[graphQLField];
|
||||
let sourceFieldMetadata =
|
||||
objectMetadataItem.fieldsById[sourceFieldMetadataId];
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
}: GetFieldMetadataFromGraphQLFieldArgs): FlatFieldMetadata | undefined {
|
||||
const { fieldIdByName } = buildFieldMapsFromFlatObjectMetadata(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
);
|
||||
|
||||
const sourceFieldMetadataId = fieldIdByName[graphQLField];
|
||||
let sourceFieldMetadata = sourceFieldMetadataId
|
||||
? flatFieldMetadataMaps.byId[sourceFieldMetadataId]
|
||||
: undefined;
|
||||
|
||||
// If empty, it could be a morph relation
|
||||
if (!isDefined(sourceFieldMetadata)) {
|
||||
const morphRelationsWithTargetObjectMetadata = Object.values(
|
||||
objectMetadataItem.fieldsById,
|
||||
)
|
||||
.filter(
|
||||
(fieldMetadata) =>
|
||||
fieldMetadata.type === FieldMetadataType.MORPH_RELATION,
|
||||
const morphRelationsWithTargetObjectMetadata =
|
||||
getFlatFieldsFromFlatObjectMetadata(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
)
|
||||
.map((fieldMetadata) => {
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
fieldMetadata,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
.filter(
|
||||
(fieldMetadata) =>
|
||||
fieldMetadata.type === FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
.map((fieldMetadata) => {
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
fieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
);
|
||||
|
||||
return {
|
||||
fieldMetadata,
|
||||
targetObjectMetadata,
|
||||
};
|
||||
});
|
||||
return {
|
||||
fieldMetadata,
|
||||
targetObjectMetadata,
|
||||
};
|
||||
});
|
||||
|
||||
const possibleGraphQLFieldNames: {
|
||||
graphQLField: string;
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
targetObjectMetadata: ObjectMetadataItemWithFieldMaps;
|
||||
fieldMetadata: FlatFieldMetadata;
|
||||
targetObjectMetadata: FlatObjectMetadata;
|
||||
}[] = [];
|
||||
|
||||
morphRelationsWithTargetObjectMetadata.map((morphRelation) => {
|
||||
if (
|
||||
!isFieldMetadataTypeMorphRelation(morphRelation.fieldMetadata) ||
|
||||
!isFlatFieldMetadataOfType(
|
||||
morphRelation.fieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
) ||
|
||||
!morphRelation.fieldMetadata.settings?.relationType
|
||||
) {
|
||||
return;
|
||||
|
||||
+7
-6
@@ -2,13 +2,14 @@ import {
|
||||
GraphqlQueryRunnerException,
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const getTargetObjectMetadataOrThrow = (
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
) => {
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
): FlatObjectMetadata => {
|
||||
if (!fieldMetadata.relationTargetObjectMetadataId) {
|
||||
throw new GraphqlQueryRunnerException(
|
||||
`Relation target object metadata id not found for field ${fieldMetadata.name}`,
|
||||
@@ -17,7 +18,7 @@ export const getTargetObjectMetadataOrThrow = (
|
||||
}
|
||||
|
||||
const targetObjectMetadata =
|
||||
objectMetadataMaps.byId[fieldMetadata.relationTargetObjectMetadataId];
|
||||
flatObjectMetadataMaps.byId[fieldMetadata.relationTargetObjectMetadataId];
|
||||
|
||||
if (!targetObjectMetadata) {
|
||||
throw new GraphqlQueryRunnerException(
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
import { type GraphQLResolveInfo } from 'graphql';
|
||||
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
export interface WorkspaceQueryRunnerOptions {
|
||||
authContext: AuthContext;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
info: GraphQLResolveInfo;
|
||||
}
|
||||
+12
-62
@@ -1,9 +1,7 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type QueryFailedError } from 'typeorm';
|
||||
|
||||
import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import {
|
||||
TwentyORMException,
|
||||
TwentyORMExceptionCode,
|
||||
@@ -14,64 +12,16 @@ interface PostgreSQLError extends QueryFailedError {
|
||||
}
|
||||
|
||||
export const handleDuplicateKeyError = (
|
||||
error: PostgreSQLError,
|
||||
objectMetadata: ObjectMetadataItemWithFieldMaps,
|
||||
_error: PostgreSQLError,
|
||||
_objectMetadata: FlatObjectMetadata,
|
||||
) => {
|
||||
const indexNameMatch = error.message.match(/"([^"]+)"/);
|
||||
|
||||
const duplicatedValues = error?.detail?.match(/=\(([^)]+)\)/)?.[1];
|
||||
|
||||
if (indexNameMatch) {
|
||||
const indexName = indexNameMatch[1];
|
||||
|
||||
const deletedAtFieldMetadataId = objectMetadata.fieldIdByName['deletedAt'];
|
||||
|
||||
const affectedColumns = objectMetadata.indexMetadatas
|
||||
.find((index) => index.name === indexName)
|
||||
?.indexFieldMetadatas?.filter(
|
||||
(field) => field.fieldMetadataId !== deletedAtFieldMetadataId,
|
||||
)
|
||||
.map((indexField) => {
|
||||
const fieldMetadata =
|
||||
objectMetadata.fieldsById[indexField.fieldMetadataId];
|
||||
|
||||
return fieldMetadata?.label;
|
||||
});
|
||||
|
||||
if (!isDefined(affectedColumns)) {
|
||||
throw new TwentyORMException(
|
||||
`A duplicate entry was detected`,
|
||||
TwentyORMExceptionCode.DUPLICATE_ENTRY_DETECTED,
|
||||
{
|
||||
userFriendlyMessage: msg`This record already exists. Please check your data and try again.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const columnNames = affectedColumns.join(', ');
|
||||
|
||||
if (affectedColumns?.length === 1) {
|
||||
const fieldName = columnNames.toLowerCase();
|
||||
|
||||
throw new UserInputError(
|
||||
`Duplicate ${columnNames} ${duplicatedValues ? `with value ${duplicatedValues}` : ''}. Please set a unique one.`,
|
||||
{
|
||||
userFriendlyMessage: duplicatedValues
|
||||
? msg`This ${fieldName} with value ${duplicatedValues} is already taken. Please choose a different value.`
|
||||
: msg`This ${fieldName} is already taken. Please choose a different value.`,
|
||||
isExpected: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const fieldNames = columnNames.toLowerCase();
|
||||
|
||||
throw new TwentyORMException(
|
||||
`A duplicate entry was detected. The combination of ${columnNames} must be unique.`,
|
||||
TwentyORMExceptionCode.DUPLICATE_ENTRY_DETECTED,
|
||||
{
|
||||
userFriendlyMessage: msg`This combination of ${fieldNames} already exists. Please use different values.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
// Since we no longer have indexMetadatas in FlatObjectMetadata,
|
||||
// we provide a generic error message
|
||||
throw new TwentyORMException(
|
||||
`A duplicate entry was detected`,
|
||||
TwentyORMExceptionCode.DUPLICATE_ENTRY_DETECTED,
|
||||
{
|
||||
userFriendlyMessage: msg`This record already exists. Please check your data and try again.`,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
+4
-3
@@ -41,14 +41,15 @@ export class CreateManyResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return records.map((record: ObjectRecord) =>
|
||||
typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
}),
|
||||
|
||||
+4
-3
@@ -40,13 +40,14 @@ export class CreateOneResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
});
|
||||
|
||||
+4
-3
@@ -41,14 +41,15 @@ export class DeleteManyResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return records.map((record: ObjectRecord) =>
|
||||
typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
}),
|
||||
|
||||
+4
-3
@@ -40,13 +40,14 @@ export class DeleteOneResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
});
|
||||
|
||||
+4
-3
@@ -41,14 +41,15 @@ export class DestroyManyResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return records.map((record: ObjectRecord) =>
|
||||
typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
}),
|
||||
|
||||
+4
-3
@@ -40,13 +40,14 @@ export class DestroyOneResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
});
|
||||
|
||||
+4
-3
@@ -42,14 +42,15 @@ export class FindDuplicatesResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return paginatedDuplicates.map((duplicate) =>
|
||||
typeORMObjectRecordsParser.createConnection({
|
||||
objectRecords: duplicate.records,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: duplicate.records.length,
|
||||
totalCount: duplicate.totalCount,
|
||||
order: [{ id: OrderByDirection.AscNullsFirst }],
|
||||
|
||||
+4
-3
@@ -47,15 +47,16 @@ export class FindManyResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return typeORMObjectRecordsParser.createConnection({
|
||||
objectRecords: records,
|
||||
objectRecordsAggregatedValues: aggregatedValues,
|
||||
selectedAggregatedFields: selectedFieldsResult.aggregate,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: args.first ?? args.last ?? QUERY_MAX_RECORDS,
|
||||
totalCount,
|
||||
order: args.orderBy,
|
||||
|
||||
+4
-3
@@ -40,13 +40,14 @@ export class FindOneResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
});
|
||||
|
||||
+4
-3
@@ -40,7 +40,9 @@ export class GroupByResolverFactory
|
||||
try {
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
const results = await this.commonGroupByQueryRunnerService.execute(
|
||||
@@ -51,8 +53,7 @@ export class GroupByResolverFactory
|
||||
const formattedResults = results.map((group) => {
|
||||
const formattedRecords = typeORMObjectRecordsParser.createConnection({
|
||||
objectRecords: group.records ?? [],
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
objectRecordsAggregatedValues: {},
|
||||
selectedAggregatedFields: {},
|
||||
take: group.records?.length || 0,
|
||||
|
||||
+4
-3
@@ -40,13 +40,14 @@ export class MergeManyResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
});
|
||||
|
||||
+4
-3
@@ -41,14 +41,15 @@ export class RestoreManyResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return records.map((record: ObjectRecord) =>
|
||||
typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
}),
|
||||
|
||||
+4
-3
@@ -40,13 +40,14 @@ export class RestoreOneResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
});
|
||||
|
||||
+4
-3
@@ -41,14 +41,15 @@ export class UpdateManyResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return records.map((record: ObjectRecord) =>
|
||||
typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
}),
|
||||
|
||||
+4
-3
@@ -40,13 +40,14 @@ export class UpdateOneResolverFactory
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionHelper(
|
||||
internalContext.objectMetadataMaps,
|
||||
internalContext.flatObjectMetadataMaps,
|
||||
internalContext.flatFieldMetadataMaps,
|
||||
internalContext.objectIdByNameSingular,
|
||||
);
|
||||
|
||||
return typeORMObjectRecordsParser.processRecord({
|
||||
objectRecord: record,
|
||||
objectName:
|
||||
internalContext.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
objectName: internalContext.flatObjectMetadata.nameSingular,
|
||||
take: 1,
|
||||
totalCount: 1,
|
||||
});
|
||||
|
||||
+23
-32
@@ -17,11 +17,10 @@ import {
|
||||
AuthExceptionCode,
|
||||
} from 'src/engine/core-modules/auth/auth.exception';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { getResolverName } from 'src/engine/utils/get-resolver-name.util';
|
||||
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
|
||||
import { shouldExcludeFromWorkspaceApi } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/should-exclude-from-workspace-api.util';
|
||||
|
||||
import { CreateManyResolverFactory } from './factories/create-many-resolver.factory';
|
||||
import { CreateOneResolverFactory } from './factories/create-one-resolver.factory';
|
||||
@@ -57,12 +56,13 @@ export class WorkspaceResolverFactory {
|
||||
private readonly mergeManyResolverFactory: MergeManyResolverFactory,
|
||||
private readonly groupByResolverFactory: GroupByResolverFactory,
|
||||
private readonly workspaceResolverBuilderService: WorkspaceResolverBuilderService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
|
||||
async create(
|
||||
authContext: AuthContext,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
objectIdByNameSingular: Record<string, string>,
|
||||
workspaceResolverBuilderMethods: WorkspaceResolverBuilderMethods,
|
||||
): Promise<IResolvers> {
|
||||
const factories = new Map<
|
||||
@@ -99,30 +99,17 @@ export class WorkspaceResolverFactory {
|
||||
);
|
||||
}
|
||||
|
||||
const workspaceFeatureFlagsMap =
|
||||
await this.featureFlagService.getWorkspaceFeatureFlagsMap(workspaceId);
|
||||
|
||||
for (const objectMetadata of Object.values(objectMetadataMaps.byId).filter(
|
||||
isDefined,
|
||||
)) {
|
||||
if (
|
||||
shouldExcludeFromWorkspaceApi(
|
||||
objectMetadata,
|
||||
standardObjectMetadataDefinitions,
|
||||
workspaceFeatureFlagsMap,
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const flatObjectMetadata of Object.values(
|
||||
flatObjectMetadataMaps.byId,
|
||||
).filter(isDefined)) {
|
||||
// Generate query resolvers
|
||||
for (const methodName of workspaceResolverBuilderMethods.queries) {
|
||||
const resolverName = getResolverName(objectMetadata, methodName);
|
||||
const resolverName = getResolverName(flatObjectMetadata, methodName);
|
||||
const resolverFactory = factories.get(methodName);
|
||||
|
||||
if (!resolverFactory) {
|
||||
this.logger.error(`Unknown query resolver type: ${methodName}`, {
|
||||
objectMetadata,
|
||||
flatObjectMetadata,
|
||||
methodName,
|
||||
resolverName,
|
||||
});
|
||||
@@ -132,27 +119,29 @@ export class WorkspaceResolverFactory {
|
||||
|
||||
if (
|
||||
this.workspaceResolverBuilderService.shouldBuildResolver(
|
||||
objectMetadata,
|
||||
flatObjectMetadata,
|
||||
methodName,
|
||||
)
|
||||
) {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
resolvers.Query[resolverName] = resolverFactory.create({
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps: objectMetadata,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
objectIdByNameSingular,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Generate mutation resolvers
|
||||
for (const methodName of workspaceResolverBuilderMethods.mutations) {
|
||||
const resolverName = getResolverName(objectMetadata, methodName);
|
||||
const resolverName = getResolverName(flatObjectMetadata, methodName);
|
||||
const resolverFactory = factories.get(methodName);
|
||||
|
||||
if (!resolverFactory) {
|
||||
this.logger.error(`Unknown mutation resolver type: ${methodName}`, {
|
||||
objectMetadata,
|
||||
flatObjectMetadata,
|
||||
methodName,
|
||||
resolverName,
|
||||
});
|
||||
@@ -162,15 +151,17 @@ export class WorkspaceResolverFactory {
|
||||
|
||||
if (
|
||||
this.workspaceResolverBuilderService.shouldBuildResolver(
|
||||
objectMetadata,
|
||||
flatObjectMetadata,
|
||||
methodName,
|
||||
)
|
||||
) {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
resolvers.Mutation[resolverName] = resolverFactory.create({
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
objectMetadataItemWithFieldMaps: objectMetadata,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
objectIdByNameSingular,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+10
-7
@@ -9,9 +9,9 @@ import {
|
||||
type FieldMetadataComplexOption,
|
||||
type FieldMetadataDefaultOption,
|
||||
} from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { transformEnumValue } from 'src/engine/utils/transform-enum-value';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@@ -23,25 +23,28 @@ export class EnumFieldMetadataGqlEnumTypeGenerator {
|
||||
|
||||
constructor(private readonly gqlTypesStorage: GqlTypesStorage) {}
|
||||
|
||||
public buildAndStore(objectMetadata: ObjectMetadataEntity) {
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
public buildAndStore(
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
fields: FlatFieldMetadata[],
|
||||
) {
|
||||
for (const fieldMetadata of fields) {
|
||||
if (!isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.gqlTypesStorage.addGqlType(
|
||||
computeEnumFieldGqlTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
fieldMetadata.name,
|
||||
),
|
||||
this.generateEnum(objectMetadata.nameSingular, fieldMetadata),
|
||||
this.generateEnum(flatObjectMetadata.nameSingular, fieldMetadata),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private generateEnum(
|
||||
objectName: string,
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
): GraphQLEnumType {
|
||||
// FixMe: It's a hack until Typescript get fixed on union types for reduce function
|
||||
// https://github.com/microsoft/TypeScript/issues/36390
|
||||
|
||||
+60
-27
@@ -4,6 +4,7 @@ import {
|
||||
type CompositeType,
|
||||
compositeTypeDefinitions,
|
||||
} from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { CompositeFieldMetadataGqlEnumTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/enum-types/composite-field-metadata-gql-enum-type.generator';
|
||||
import { EnumFieldMetadataGqlEnumTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/enum-types/enum-field-metadata-gql-enum-type.generator';
|
||||
@@ -19,9 +20,10 @@ import { ObjectMetadataGqlObjectTypeGenerator } from 'src/engine/api/graphql/wor
|
||||
import { ObjectMetadataWithRelationsGqlObjectTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/object-types/object-metadata-with-relations-gql-object-type.generator';
|
||||
import { MutationTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/root-types/mutation-type.generator';
|
||||
import { QueryTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/root-types/query-type.generator';
|
||||
import { objectContainsMorphRelationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/object-contains-morph-relation-field.util';
|
||||
import { objectContainsRelationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/object-contains-relation-field';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
import { getFlatFieldsFromFlatObjectMetadata } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-flat-fields-for-flat-object-metadata.util';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFieldMetadataType } from 'src/engine/utils/is-morph-or-relation-field-metadata-type.util';
|
||||
|
||||
@Injectable()
|
||||
export class GqlTypeGenerator {
|
||||
@@ -44,14 +46,14 @@ export class GqlTypeGenerator {
|
||||
private readonly mutationTypeGenerator: MutationTypeGenerator,
|
||||
) {}
|
||||
|
||||
async buildAndStore(objectMetadataCollection: ObjectMetadataEntity[]) {
|
||||
async buildAndStore(context: SchemaGenerationContext) {
|
||||
const compositeTypeCollection = [...compositeTypeDefinitions.values()];
|
||||
|
||||
this.buildAndStoreCompositeFieldMetadataGqlTypes(compositeTypeCollection);
|
||||
this.buildAndStoreDateFieldMetadataGroupByGqlTypes();
|
||||
this.buildAndStoreObjectMetadataGqlTypes(objectMetadataCollection);
|
||||
await this.queryTypeGenerator.buildAndStore(objectMetadataCollection);
|
||||
this.mutationTypeGenerator.buildAndStore(objectMetadataCollection);
|
||||
this.buildAndStoreObjectMetadataGqlTypes(context);
|
||||
await this.queryTypeGenerator.buildAndStore(context);
|
||||
this.mutationTypeGenerator.buildAndStore(context);
|
||||
}
|
||||
|
||||
private buildAndStoreCompositeFieldMetadataGqlTypes(
|
||||
@@ -75,36 +77,67 @@ export class GqlTypeGenerator {
|
||||
}
|
||||
|
||||
private buildAndStoreObjectMetadataGqlTypes(
|
||||
dynamicObjectMetadataCollection: ObjectMetadataEntity[],
|
||||
context: SchemaGenerationContext,
|
||||
) {
|
||||
const { flatObjectMetadataMaps, flatFieldMetadataMaps } = context;
|
||||
const objectMetadataCollection = Object.values(
|
||||
flatObjectMetadataMaps.byId,
|
||||
).filter(isDefined);
|
||||
|
||||
this.logger.log(
|
||||
`Generating metadata objects: [${dynamicObjectMetadataCollection
|
||||
`Generating metadata objects: [${objectMetadataCollection
|
||||
.map((object) => object.nameSingular)
|
||||
.join(', ')}]`,
|
||||
);
|
||||
|
||||
for (const objectMetadata of dynamicObjectMetadataCollection) {
|
||||
this.enumFieldMetadataGqlEnumTypeGenerator.buildAndStore(objectMetadata);
|
||||
this.objectMetadataGqlObjectTypeGenerator.buildAndStore(objectMetadata);
|
||||
this.edgeGqlObjectTypeGenerator.buildAndStore(objectMetadata);
|
||||
this.connectionGqlObjectTypeGenerator.buildAndStore(objectMetadata);
|
||||
this.groupByConnectionGqlObjectTypeGenerator.buildAndStore(
|
||||
objectMetadata,
|
||||
);
|
||||
this.relationConnectGqlInputTypeGenerator.buildAndStore(objectMetadata);
|
||||
this.objectMetadataGqlInputTypeGenerator.buildAndStore(
|
||||
objectMetadata,
|
||||
dynamicObjectMetadataCollection,
|
||||
for (const flatObjectMetadata of objectMetadataCollection) {
|
||||
const fields = getFlatFieldsFromFlatObjectMetadata(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
if (
|
||||
objectContainsRelationField(objectMetadata) ||
|
||||
objectContainsMorphRelationField(objectMetadata)
|
||||
)
|
||||
this.enumFieldMetadataGqlEnumTypeGenerator.buildAndStore(
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
);
|
||||
this.objectMetadataGqlObjectTypeGenerator.buildAndStore(
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
);
|
||||
this.edgeGqlObjectTypeGenerator.buildAndStore(flatObjectMetadata);
|
||||
this.connectionGqlObjectTypeGenerator.buildAndStore(
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
);
|
||||
this.groupByConnectionGqlObjectTypeGenerator.buildAndStore(
|
||||
flatObjectMetadata,
|
||||
);
|
||||
this.relationConnectGqlInputTypeGenerator.buildAndStore(
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
context,
|
||||
);
|
||||
this.objectMetadataGqlInputTypeGenerator.buildAndStore(
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
context,
|
||||
);
|
||||
|
||||
if (this.objectContainsRelationOrMorphField(fields)) {
|
||||
this.objectMetadataWithRelationsGqlObjectTypeGenerator.buildAndStore(
|
||||
objectMetadata,
|
||||
dynamicObjectMetadataCollection,
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
context,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private objectContainsRelationOrMorphField(
|
||||
fields: FlatFieldMetadata[],
|
||||
): boolean {
|
||||
return fields.some((field) =>
|
||||
isMorphOrRelationFieldMetadataType(field.type),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+28
-19
@@ -16,15 +16,16 @@ import {
|
||||
TypeOptions,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
import { computeFieldInputTypeOptions } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-field-input-type-options.util';
|
||||
import { computeCompositeFieldInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-input-type-key.util';
|
||||
import { computeEnumFieldGqlTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-enum-field-gql-type-key.util';
|
||||
import { computeObjectMetadataInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-input-type.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
@@ -38,15 +39,20 @@ export class ObjectMetadataCreateGqlInputTypeGenerator {
|
||||
private readonly typeMapperService: TypeMapperService,
|
||||
) {}
|
||||
|
||||
public buildAndStore(objectMetadata: ObjectMetadataEntity) {
|
||||
public buildAndStore(
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
fields: FlatFieldMetadata[],
|
||||
_context: SchemaGenerationContext,
|
||||
) {
|
||||
const inputType = new GraphQLInputObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.Create.toString()}Input`,
|
||||
description: objectMetadata.description,
|
||||
fields: () => this.generateFields(objectMetadata),
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.Create.toString()}Input`,
|
||||
description: flatObjectMetadata.description,
|
||||
fields: () =>
|
||||
this.generateFields(flatObjectMetadata.nameSingular, fields),
|
||||
}) as GraphQLInputObjectType;
|
||||
|
||||
const key = computeObjectMetadataInputTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
GqlInputTypeDefinitionKind.Create,
|
||||
);
|
||||
|
||||
@@ -54,11 +60,12 @@ export class ObjectMetadataCreateGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
objectNameSingular: string,
|
||||
fields: FlatFieldMetadata[],
|
||||
): GraphQLInputFieldConfigMap {
|
||||
const allGeneratedFields: GraphQLInputFieldConfigMap = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
for (const fieldMetadata of fields) {
|
||||
const typeOptions = computeFieldInputTypeOptions(
|
||||
fieldMetadata,
|
||||
GqlInputTypeDefinitionKind.Create,
|
||||
@@ -66,24 +73,26 @@ export class ObjectMetadataCreateGqlInputTypeGenerator {
|
||||
|
||||
let generatedFields;
|
||||
|
||||
if (isFieldMetadataRelationOrMorphRelation(fieldMetadata)) {
|
||||
if (isMorphOrRelationFlatFieldMetadata(fieldMetadata)) {
|
||||
const relationFieldMetadata = fieldMetadata;
|
||||
|
||||
generatedFields = {
|
||||
...this.relationFieldMetadataGqlInputTypeGenerator.generateSimpleRelationFieldCreateOrUpdateInputType(
|
||||
{
|
||||
fieldMetadata,
|
||||
fieldMetadata: relationFieldMetadata,
|
||||
typeOptions,
|
||||
},
|
||||
),
|
||||
...this.relationFieldMetadataGqlInputTypeGenerator.generateConnectRelationFieldInputType(
|
||||
{
|
||||
fieldMetadata,
|
||||
fieldMetadata: relationFieldMetadata,
|
||||
typeOptions,
|
||||
},
|
||||
),
|
||||
};
|
||||
} else if (isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||
generatedFields = this.generateEnumFieldCreateInputType(
|
||||
objectMetadata,
|
||||
objectNameSingular,
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
);
|
||||
@@ -106,12 +115,12 @@ export class ObjectMetadataCreateGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateEnumFieldCreateInputType(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
objectNameSingular: string,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const key = computeEnumFieldGqlTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
objectNameSingular,
|
||||
fieldMetadata.name,
|
||||
);
|
||||
|
||||
@@ -141,7 +150,7 @@ export class ObjectMetadataCreateGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateCompositeFieldCreateInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const key = computeCompositeFieldInputTypeKey(
|
||||
@@ -175,7 +184,7 @@ export class ObjectMetadataCreateGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateAtomicFieldCreateInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const type = this.typeMapperService.mapToScalarType(
|
||||
|
||||
+27
-19
@@ -21,11 +21,11 @@ import { computeCompositeFieldInputTypeKey } from 'src/engine/api/graphql/worksp
|
||||
import { computeEnumFieldGqlTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-enum-field-gql-type-key.util';
|
||||
import { computeObjectMetadataInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-input-type.util';
|
||||
import { createGqlEnumFilterType } from 'src/engine/api/graphql/workspace-schema-builder/utils/create-gql-enum-filter-type.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
@@ -39,15 +39,19 @@ export class ObjectMetadataFilterGqlInputTypeGenerator {
|
||||
private readonly typeMapperService: TypeMapperService,
|
||||
) {}
|
||||
|
||||
public buildAndStore(objectMetadata: ObjectMetadataEntity) {
|
||||
public buildAndStore(
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
fields: FlatFieldMetadata[],
|
||||
) {
|
||||
const inputType = new GraphQLInputObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.Filter.toString()}Input`,
|
||||
description: objectMetadata.description,
|
||||
fields: () => this.generateFields(objectMetadata, inputType),
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.Filter.toString()}Input`,
|
||||
description: flatObjectMetadata.description,
|
||||
fields: () =>
|
||||
this.generateFields(flatObjectMetadata.nameSingular, fields, inputType),
|
||||
}) as GraphQLInputObjectType;
|
||||
|
||||
const key = computeObjectMetadataInputTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
GqlInputTypeDefinitionKind.Filter,
|
||||
);
|
||||
|
||||
@@ -55,7 +59,8 @@ export class ObjectMetadataFilterGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
objectNameSingular: string,
|
||||
fields: FlatFieldMetadata[],
|
||||
inputType: GraphQLInputObjectType,
|
||||
): GraphQLInputFieldConfigMap {
|
||||
const andOrType = this.typeMapperService.applyTypeOptions(inputType, {
|
||||
@@ -65,17 +70,20 @@ export class ObjectMetadataFilterGqlInputTypeGenerator {
|
||||
});
|
||||
const allGeneratedFields: GraphQLInputFieldConfigMap = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
fieldMetadata.isNullable = true;
|
||||
for (const fieldMetadata of fields) {
|
||||
const modifiedFieldMetadata = {
|
||||
...fieldMetadata,
|
||||
isNullable: true,
|
||||
};
|
||||
|
||||
const typeOptions = computeFieldInputTypeOptions(
|
||||
fieldMetadata,
|
||||
modifiedFieldMetadata,
|
||||
GqlInputTypeDefinitionKind.Filter,
|
||||
);
|
||||
|
||||
let generatedFields;
|
||||
|
||||
if (isFieldMetadataRelationOrMorphRelation(fieldMetadata)) {
|
||||
if (isMorphOrRelationFlatFieldMetadata(fieldMetadata)) {
|
||||
generatedFields =
|
||||
this.relationFieldMetadataGqlInputTypeGenerator.generateSimpleRelationFieldFilterInputType(
|
||||
{
|
||||
@@ -85,7 +93,7 @@ export class ObjectMetadataFilterGqlInputTypeGenerator {
|
||||
);
|
||||
} else if (isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||
generatedFields = this.generateEnumFieldFilterInputType(
|
||||
objectMetadata,
|
||||
objectNameSingular,
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
);
|
||||
@@ -120,12 +128,12 @@ export class ObjectMetadataFilterGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateEnumFieldFilterInputType(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
objectNameSingular: string,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const key = computeEnumFieldGqlTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
objectNameSingular,
|
||||
fieldMetadata.name,
|
||||
);
|
||||
|
||||
@@ -152,7 +160,7 @@ export class ObjectMetadataFilterGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateCompositeFieldFilterInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const key = computeCompositeFieldInputTypeKey(
|
||||
@@ -181,7 +189,7 @@ export class ObjectMetadataFilterGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateAtomicFieldFilterInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const type = this.typeMapperService.mapToFilterType(
|
||||
|
||||
+18
-28
@@ -16,14 +16,13 @@ import { GROUP_BY_DATE_GRANULARITY_INPUT_KEY } from 'src/engine/api/graphql/work
|
||||
import { RelationFieldMetadataGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/relation-field-metadata-gql-type.generator';
|
||||
import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
import { computeCompositeFieldInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-input-type-key.util';
|
||||
import { computeObjectMetadataInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-input-type.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { generateObjectMetadataMaps } from 'src/engine/metadata-modules/utils/generate-object-metadata-maps.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
@@ -31,8 +30,6 @@ export class ObjectMetadataGroupByGqlInputTypeGenerator {
|
||||
private readonly logger = new Logger(
|
||||
ObjectMetadataGroupByGqlInputTypeGenerator.name,
|
||||
);
|
||||
private objectMetadataCollection: ObjectMetadataEntity[] = [];
|
||||
private objectMetadataMaps: ObjectMetadataMaps | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly gqlTypesStorage: GqlTypesStorage,
|
||||
@@ -41,24 +38,18 @@ export class ObjectMetadataGroupByGqlInputTypeGenerator {
|
||||
) {}
|
||||
|
||||
public buildAndStore(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
objectMetadataCollection?: ObjectMetadataEntity[],
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
fields: FlatFieldMetadata[],
|
||||
context: SchemaGenerationContext,
|
||||
) {
|
||||
if (isDefined(objectMetadataCollection)) {
|
||||
this.objectMetadataCollection = objectMetadataCollection;
|
||||
this.objectMetadataMaps = generateObjectMetadataMaps(
|
||||
objectMetadataCollection,
|
||||
);
|
||||
}
|
||||
|
||||
const inputType = new GraphQLInputObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.GroupBy.toString()}Input`,
|
||||
description: objectMetadata.description,
|
||||
fields: () => this.generateFields(objectMetadata),
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.GroupBy.toString()}Input`,
|
||||
description: flatObjectMetadata.description,
|
||||
fields: () => this.generateFields(fields, context),
|
||||
}) as GraphQLInputObjectType;
|
||||
|
||||
const key = computeObjectMetadataInputTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
GqlInputTypeDefinitionKind.GroupBy,
|
||||
);
|
||||
|
||||
@@ -66,17 +57,16 @@ export class ObjectMetadataGroupByGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
fields: FlatFieldMetadata[],
|
||||
context: SchemaGenerationContext,
|
||||
): GraphQLInputFieldConfigMap {
|
||||
const allGeneratedFields: GraphQLInputFieldConfigMap = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
const generatedField = isFieldMetadataRelationOrMorphRelation(
|
||||
fieldMetadata,
|
||||
)
|
||||
for (const fieldMetadata of fields) {
|
||||
const generatedField = isMorphOrRelationFlatFieldMetadata(fieldMetadata)
|
||||
? this.relationFieldMetadataGqlInputTypeGenerator.generateSimpleRelationFieldGroupByInputType(
|
||||
fieldMetadata,
|
||||
this.objectMetadataMaps,
|
||||
context,
|
||||
)
|
||||
: this.generateField(fieldMetadata);
|
||||
|
||||
@@ -86,7 +76,7 @@ export class ObjectMetadataGroupByGqlInputTypeGenerator {
|
||||
return allGeneratedFields;
|
||||
}
|
||||
|
||||
private generateField(fieldMetadata: FieldMetadataEntity) {
|
||||
private generateField(fieldMetadata: FlatFieldMetadata) {
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type))
|
||||
return this.generateCompositeFieldGroupByInputType(fieldMetadata);
|
||||
|
||||
@@ -124,7 +114,7 @@ export class ObjectMetadataGroupByGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateCompositeFieldGroupByInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
) {
|
||||
const key = computeCompositeFieldInputTypeKey(
|
||||
fieldMetadata.type,
|
||||
|
||||
+22
-11
@@ -6,7 +6,9 @@ import { ObjectMetadataGroupByGqlInputTypeGenerator } from 'src/engine/api/graph
|
||||
import { ObjectMetadataOrderByGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/order-by-input/object-metadata-order-by-gql-input-type.generator';
|
||||
import { ObjectMetadataOrderByWithGroupByGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/order-by-input/object-metadata-order-by-with-group-by-gql-input-type.generator';
|
||||
import { ObjectMetadataUpdateGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/update-input/object-metadata-update-gql-input-type.generator';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class ObjectMetadataGqlInputTypeGenerator {
|
||||
@@ -20,28 +22,37 @@ export class ObjectMetadataGqlInputTypeGenerator {
|
||||
) {}
|
||||
|
||||
public buildAndStore(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
objectMetadataCollection?: ObjectMetadataEntity[],
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
fields: FlatFieldMetadata[],
|
||||
context: SchemaGenerationContext,
|
||||
) {
|
||||
this.objectMetadataCreateGqlInputTypeGenerator.buildAndStore(
|
||||
objectMetadata,
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
context,
|
||||
);
|
||||
this.objectMetadataUpdateGqlInputTypeGenerator.buildAndStore(
|
||||
objectMetadata,
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
context,
|
||||
);
|
||||
this.objectMetadataFilterGqlInputTypeGenerator.buildAndStore(
|
||||
objectMetadata,
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
);
|
||||
this.objectMetadataOrderByGqlInputTypeGenerator.buildAndStore({
|
||||
objectMetadata,
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
});
|
||||
this.objectMetadataOrderByWithGroupByGqlInputTypeGenerator.buildAndStore({
|
||||
objectMetadata,
|
||||
objectMetadataCollection,
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
context,
|
||||
});
|
||||
this.objectMetadataGroupByGqlInputTypeGenerator.buildAndStore(
|
||||
objectMetadata,
|
||||
objectMetadataCollection,
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
context,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-15
@@ -12,13 +12,12 @@ import {
|
||||
TypeOptions,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
import { computeFieldInputTypeOptions } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-field-input-type-options.util';
|
||||
import { computeCompositeFieldInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-input-type-key.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
|
||||
@Injectable()
|
||||
export class ObjectMetadataOrderByBaseGenerator {
|
||||
@@ -29,35 +28,38 @@ export class ObjectMetadataOrderByBaseGenerator {
|
||||
) {}
|
||||
|
||||
public generateFields({
|
||||
objectMetadata,
|
||||
fields,
|
||||
logger,
|
||||
orderByDateGranularity,
|
||||
objectMetadataMaps,
|
||||
context,
|
||||
}: {
|
||||
objectMetadata: ObjectMetadataEntity;
|
||||
fields: FlatFieldMetadata[];
|
||||
logger: Logger;
|
||||
orderByDateGranularity?: boolean;
|
||||
objectMetadataMaps?: ObjectMetadataMaps;
|
||||
context?: SchemaGenerationContext;
|
||||
}): GraphQLInputFieldConfigMap {
|
||||
const allGeneratedFields: GraphQLInputFieldConfigMap = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
fieldMetadata.isNullable = true;
|
||||
for (const fieldMetadata of fields) {
|
||||
const modifiedFieldMetadata = {
|
||||
...fieldMetadata,
|
||||
isNullable: true,
|
||||
};
|
||||
|
||||
const typeOptions = computeFieldInputTypeOptions(
|
||||
fieldMetadata,
|
||||
modifiedFieldMetadata,
|
||||
GqlInputTypeDefinitionKind.OrderBy,
|
||||
);
|
||||
|
||||
let generatedFields;
|
||||
|
||||
if (isFieldMetadataRelationOrMorphRelation(fieldMetadata)) {
|
||||
if (isMorphOrRelationFlatFieldMetadata(fieldMetadata)) {
|
||||
generatedFields =
|
||||
this.relationFieldMetadataGqlInputTypeGenerator.generateSimpleRelationFieldOrderByInputType(
|
||||
{
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
objectMetadataMaps,
|
||||
context,
|
||||
},
|
||||
);
|
||||
} else if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
@@ -82,7 +84,7 @@ export class ObjectMetadataOrderByBaseGenerator {
|
||||
}
|
||||
|
||||
private generateCompositeFieldOrderByInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
typeOptions: TypeOptions,
|
||||
logger: Logger,
|
||||
) {
|
||||
@@ -117,7 +119,7 @@ export class ObjectMetadataOrderByBaseGenerator {
|
||||
logger,
|
||||
orderByDateGranularity,
|
||||
}: {
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
fieldMetadata: FlatFieldMetadata;
|
||||
typeOptions: TypeOptions;
|
||||
logger: Logger;
|
||||
orderByDateGranularity?: boolean;
|
||||
|
||||
+10
-7
@@ -6,7 +6,8 @@ import { GqlInputTypeDefinitionKind } from 'src/engine/api/graphql/workspace-sch
|
||||
import { ObjectMetadataOrderByBaseGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/order-by-input/object-metadata-order-by-base.generator';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { computeObjectMetadataInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-input-type.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
@@ -20,22 +21,24 @@ export class ObjectMetadataOrderByGqlInputTypeGenerator {
|
||||
) {}
|
||||
|
||||
public buildAndStore({
|
||||
objectMetadata,
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
}: {
|
||||
objectMetadata: ObjectMetadataEntity;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
fields: FlatFieldMetadata[];
|
||||
}) {
|
||||
const inputType = new GraphQLInputObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.OrderBy.toString()}Input`,
|
||||
description: objectMetadata.description,
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.OrderBy.toString()}Input`,
|
||||
description: flatObjectMetadata.description,
|
||||
fields: () =>
|
||||
this.objectMetadataOrderByBaseGenerator.generateFields({
|
||||
objectMetadata,
|
||||
fields,
|
||||
logger: this.logger,
|
||||
}),
|
||||
}) as GraphQLInputObjectType;
|
||||
|
||||
const key = computeObjectMetadataInputTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
GqlInputTypeDefinitionKind.OrderBy,
|
||||
);
|
||||
|
||||
|
||||
+43
-37
@@ -15,16 +15,15 @@ import {
|
||||
TypeOptions,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
import { computeFieldInputTypeOptions } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-field-input-type-options.util';
|
||||
import { computeCompositeFieldInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-input-type-key.util';
|
||||
import { computeObjectMetadataInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-input-type.util';
|
||||
import { getAvailableAggregationsFromObjectFields } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { generateObjectMetadataMaps } from 'src/engine/metadata-modules/utils/generate-object-metadata-maps.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
@@ -39,29 +38,24 @@ export class ObjectMetadataOrderByWithGroupByGqlInputTypeGenerator {
|
||||
private readonly typeMapperService: TypeMapperService,
|
||||
) {}
|
||||
|
||||
private objectMetadataMaps?: ObjectMetadataMaps;
|
||||
|
||||
public buildAndStore({
|
||||
objectMetadata,
|
||||
objectMetadataCollection,
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
context,
|
||||
}: {
|
||||
objectMetadata: ObjectMetadataEntity;
|
||||
objectMetadataCollection?: ObjectMetadataEntity[];
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
fields: FlatFieldMetadata[];
|
||||
context: SchemaGenerationContext;
|
||||
}) {
|
||||
if (isDefined(objectMetadataCollection)) {
|
||||
this.objectMetadataMaps = generateObjectMetadataMaps(
|
||||
objectMetadataCollection,
|
||||
);
|
||||
}
|
||||
|
||||
const inputType = new GraphQLInputObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.OrderByWithGroupBy.toString()}Input`,
|
||||
description: objectMetadata.description,
|
||||
fields: () => this.generateFields(objectMetadata),
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.OrderByWithGroupBy.toString()}Input`,
|
||||
description: flatObjectMetadata.description,
|
||||
fields: () =>
|
||||
this.generateFields(flatObjectMetadata.nameSingular, fields, context),
|
||||
}) as GraphQLInputObjectType;
|
||||
|
||||
const key = computeObjectMetadataInputTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
GqlInputTypeDefinitionKind.OrderByWithGroupBy,
|
||||
);
|
||||
|
||||
@@ -69,36 +63,47 @@ export class ObjectMetadataOrderByWithGroupByGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
objectNameSingular: string,
|
||||
fields: FlatFieldMetadata[],
|
||||
context: SchemaGenerationContext,
|
||||
): GraphQLInputFieldConfigMap {
|
||||
return {
|
||||
...this.generateOrderByOnAggregateFields(objectMetadata),
|
||||
...this.generateOrderByOnDimensionValuesFields(objectMetadata),
|
||||
...this.generateOrderByOnAggregateFields(
|
||||
objectNameSingular,
|
||||
fields,
|
||||
context,
|
||||
),
|
||||
...this.generateOrderByOnDimensionValuesFields(fields, context),
|
||||
};
|
||||
}
|
||||
|
||||
private generateOrderByOnAggregateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
objectNameSingular: string,
|
||||
fields: FlatFieldMetadata[],
|
||||
context: SchemaGenerationContext,
|
||||
): GraphQLInputFieldConfigMap {
|
||||
const allAggregatedFields: GraphQLInputFieldConfigMap = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
fieldMetadata.isNullable = true;
|
||||
for (const fieldMetadata of fields) {
|
||||
const modifiedFieldMetadata = {
|
||||
...fieldMetadata,
|
||||
isNullable: true,
|
||||
};
|
||||
|
||||
const typeOptions = computeFieldInputTypeOptions(
|
||||
fieldMetadata,
|
||||
modifiedFieldMetadata,
|
||||
GqlInputTypeDefinitionKind.OrderBy,
|
||||
);
|
||||
|
||||
let generatedFields;
|
||||
|
||||
if (isFieldMetadataRelationOrMorphRelation(fieldMetadata)) {
|
||||
if (isMorphOrRelationFlatFieldMetadata(fieldMetadata)) {
|
||||
generatedFields =
|
||||
this.relationFieldMetadataGqlInputTypeGenerator.generateSimpleRelationFieldOrderByInputType(
|
||||
{
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
objectMetadataMaps: this.objectMetadataMaps,
|
||||
context,
|
||||
},
|
||||
);
|
||||
} else if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
@@ -117,7 +122,7 @@ export class ObjectMetadataOrderByWithGroupByGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
const aggregateInputType = new GraphQLInputObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.OrderByWithGroupBy.toString()}AggregateInput`,
|
||||
name: `${pascalCase(objectNameSingular)}${GqlInputTypeDefinitionKind.OrderByWithGroupBy.toString()}AggregateInput`,
|
||||
description: 'Aggregate-based ordering',
|
||||
fields: () => allAggregatedFields,
|
||||
}) as GraphQLInputObjectType;
|
||||
@@ -131,18 +136,19 @@ export class ObjectMetadataOrderByWithGroupByGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateOrderByOnDimensionValuesFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
fields: FlatFieldMetadata[],
|
||||
context: SchemaGenerationContext,
|
||||
) {
|
||||
return this.objectMetadataOrderByBaseGenerator.generateFields({
|
||||
objectMetadata,
|
||||
fields,
|
||||
logger: this.logger,
|
||||
orderByDateGranularity: true,
|
||||
objectMetadataMaps: this.objectMetadataMaps,
|
||||
context,
|
||||
});
|
||||
}
|
||||
|
||||
private generateCompositeFieldOrderByInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const key = computeCompositeFieldInputTypeKey(
|
||||
@@ -169,7 +175,7 @@ export class ObjectMetadataOrderByWithGroupByGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateAtomicFieldOrderByInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
typeOptions: TypeOptions,
|
||||
) {
|
||||
const orderByType = this.typeMapperService.mapToOrderByType(
|
||||
@@ -193,7 +199,7 @@ export class ObjectMetadataOrderByWithGroupByGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateAggregateFieldOrderByInputType(
|
||||
fieldMetadata: FieldMetadataEntity,
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
) {
|
||||
const aggregations = getAvailableAggregationsFromObjectFields([
|
||||
fieldMetadata,
|
||||
|
||||
+59
-26
@@ -8,15 +8,16 @@ import {
|
||||
GraphQLString,
|
||||
} from 'graphql';
|
||||
import { RELATION_NESTED_QUERY_KEYWORDS } from 'twenty-shared/constants';
|
||||
import { getUniqueConstraintsFields } from 'twenty-shared/utils';
|
||||
import { compositeTypeDefinitions } from 'twenty-shared/types';
|
||||
import { getUniqueConstraintsFields, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
import { computeRelationConnectInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-relation-connect-input-type-key.util';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
@@ -26,44 +27,76 @@ export class RelationConnectGqlInputTypeGenerator {
|
||||
private readonly gqlTypesStorage: GqlTypesStorage,
|
||||
) {}
|
||||
|
||||
public buildAndStore(objectMetadata: ObjectMetadataEntity) {
|
||||
const relationConnectGqlInputType =
|
||||
this.generateRelationConnectInputType(objectMetadata);
|
||||
const key = computeRelationConnectInputTypeKey(objectMetadata.id);
|
||||
public buildAndStore(
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
fields: FlatFieldMetadata[],
|
||||
context: SchemaGenerationContext,
|
||||
) {
|
||||
const relationConnectGqlInputType = this.generateRelationConnectInputType(
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
context,
|
||||
);
|
||||
const key = computeRelationConnectInputTypeKey(flatObjectMetadata.id);
|
||||
|
||||
this.gqlTypesStorage.addGqlType(key, relationConnectGqlInputType);
|
||||
}
|
||||
|
||||
private generateRelationConnectInputType(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
fields: FlatFieldMetadata[],
|
||||
context: SchemaGenerationContext,
|
||||
): GraphQLInputObjectType {
|
||||
return new GraphQLInputObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}RelationInput`,
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}RelationInput`,
|
||||
fields: () => ({
|
||||
[RELATION_NESTED_QUERY_KEYWORDS.CONNECT]: {
|
||||
type: new GraphQLInputObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}ConnectInput`,
|
||||
fields: this.generateRelationWhereInputType(objectMetadata),
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}ConnectInput`,
|
||||
fields: this.generateRelationWhereInputType(
|
||||
flatObjectMetadata,
|
||||
fields,
|
||||
context,
|
||||
),
|
||||
}),
|
||||
description: `Connect to a ${objectMetadata.nameSingular} record`,
|
||||
description: `Connect to a ${flatObjectMetadata.nameSingular} record`,
|
||||
},
|
||||
[RELATION_NESTED_QUERY_KEYWORDS.DISCONNECT]: {
|
||||
type: GraphQLBoolean,
|
||||
description: `Disconnect from a ${objectMetadata.nameSingular} record`,
|
||||
description: `Disconnect from a ${flatObjectMetadata.nameSingular} record`,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
private generateRelationWhereInputType(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
fields: FlatFieldMetadata[],
|
||||
context: SchemaGenerationContext,
|
||||
): Record<string, GraphQLInputFieldConfig> {
|
||||
const uniqueConstraints = getUniqueConstraintsFields<
|
||||
FieldMetadataEntity,
|
||||
ObjectMetadataEntity
|
||||
>(objectMetadata);
|
||||
const { flatIndexMaps } = context;
|
||||
|
||||
const fields: Record<
|
||||
const indexMetadatas = flatObjectMetadata.indexMetadataIds
|
||||
.map((indexId) => flatIndexMaps.byId[indexId])
|
||||
.filter(isDefined)
|
||||
.map((flatIndex) => ({
|
||||
id: flatIndex.id,
|
||||
isUnique: flatIndex.isUnique,
|
||||
indexFieldMetadatas: flatIndex.flatIndexFieldMetadatas,
|
||||
}));
|
||||
|
||||
const objectWithIndexes = {
|
||||
id: flatObjectMetadata.id,
|
||||
indexMetadatas,
|
||||
fields,
|
||||
};
|
||||
|
||||
const uniqueConstraints = getUniqueConstraintsFields<
|
||||
FlatFieldMetadata,
|
||||
typeof objectWithIndexes
|
||||
>(objectWithIndexes);
|
||||
|
||||
const inputFields: Record<
|
||||
string,
|
||||
{ type: GraphQLInputType; description: string }
|
||||
> = {};
|
||||
@@ -101,11 +134,11 @@ export class RelationConnectGqlInputTypeGenerator {
|
||||
});
|
||||
|
||||
const compositeInputType = new GraphQLInputObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${pascalCase(field.name)}WhereInput`,
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}${pascalCase(field.name)}WhereInput`,
|
||||
fields: () => compositeFields,
|
||||
});
|
||||
|
||||
fields[field.name] = {
|
||||
inputFields[field.name] = {
|
||||
type: compositeInputType,
|
||||
description: `Connect by ${field.label || field.name}`,
|
||||
};
|
||||
@@ -116,7 +149,7 @@ export class RelationConnectGqlInputTypeGenerator {
|
||||
{ settings: field.settings, isIdField: field.name === 'id' },
|
||||
);
|
||||
|
||||
fields[field.name] = {
|
||||
inputFields[field.name] = {
|
||||
type: scalarType || GraphQLString,
|
||||
description: `Connect by ${field.label || field.name}`,
|
||||
};
|
||||
@@ -127,15 +160,15 @@ export class RelationConnectGqlInputTypeGenerator {
|
||||
return {
|
||||
[RELATION_NESTED_QUERY_KEYWORDS.CONNECT_WHERE]: {
|
||||
type: new GraphQLInputObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}WhereUniqueInput`,
|
||||
fields: () => fields,
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}WhereUniqueInput`,
|
||||
fields: () => inputFields,
|
||||
}),
|
||||
description: `Find a ${objectMetadata.nameSingular} record based on its unique fields: ${this.formatConstraints(uniqueConstraints)}`,
|
||||
description: `Find a ${flatObjectMetadata.nameSingular} record based on its unique fields: ${this.formatConstraints(uniqueConstraints)}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private formatConstraints(constraints: FieldMetadataEntity[][]) {
|
||||
private formatConstraints(constraints: FlatFieldMetadata[][]) {
|
||||
return constraints
|
||||
.map((constraint) => constraint.map((field) => field.name).join(' and '))
|
||||
.join(' or ');
|
||||
|
||||
+18
-14
@@ -14,11 +14,11 @@ import {
|
||||
TypeOptions,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
import { computeObjectMetadataInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-input-type.util';
|
||||
import { computeRelationConnectInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-relation-connect-input-type-key.util';
|
||||
import { extractGraphQLRelationFieldNames } from 'src/engine/api/graphql/workspace-schema-builder/utils/extract-graphql-relation-field-names.util';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class RelationFieldMetadataGqlInputTypeGenerator {
|
||||
@@ -35,7 +35,7 @@ export class RelationFieldMetadataGqlInputTypeGenerator {
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
}: {
|
||||
fieldMetadata: FieldMetadataEntity<
|
||||
fieldMetadata: FlatFieldMetadata<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>;
|
||||
typeOptions: TypeOptions;
|
||||
@@ -77,7 +77,7 @@ export class RelationFieldMetadataGqlInputTypeGenerator {
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
}: {
|
||||
fieldMetadata: FieldMetadataEntity<
|
||||
fieldMetadata: FlatFieldMetadata<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>;
|
||||
typeOptions: TypeOptions;
|
||||
@@ -118,13 +118,13 @@ export class RelationFieldMetadataGqlInputTypeGenerator {
|
||||
public generateSimpleRelationFieldOrderByInputType({
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
objectMetadataMaps,
|
||||
context,
|
||||
}: {
|
||||
fieldMetadata: FieldMetadataEntity<
|
||||
fieldMetadata: FlatFieldMetadata<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>;
|
||||
typeOptions: TypeOptions;
|
||||
objectMetadataMaps?: ObjectMetadataMaps;
|
||||
context?: SchemaGenerationContext;
|
||||
}) {
|
||||
if (fieldMetadata.settings?.relationType === RelationType.ONE_TO_MANY)
|
||||
return {};
|
||||
@@ -158,10 +158,12 @@ export class RelationFieldMetadataGqlInputTypeGenerator {
|
||||
|
||||
if (
|
||||
isDefined(fieldMetadata.relationTargetObjectMetadataId) &&
|
||||
isDefined(objectMetadataMaps)
|
||||
isDefined(context)
|
||||
) {
|
||||
const targetObjectMetadata =
|
||||
objectMetadataMaps.byId[fieldMetadata.relationTargetObjectMetadataId];
|
||||
context.flatObjectMetadataMaps.byId[
|
||||
fieldMetadata.relationTargetObjectMetadataId
|
||||
];
|
||||
|
||||
if (isDefined(targetObjectMetadata)) {
|
||||
const targetOrderByInputTypeKey = computeObjectMetadataInputTypeKey(
|
||||
@@ -189,10 +191,10 @@ export class RelationFieldMetadataGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
public generateSimpleRelationFieldGroupByInputType(
|
||||
fieldMetadata: FieldMetadataEntity<
|
||||
fieldMetadata: FlatFieldMetadata<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>,
|
||||
objectMetadataMaps?: ObjectMetadataMaps,
|
||||
context?: SchemaGenerationContext,
|
||||
): GraphQLInputFieldConfigMap {
|
||||
if (fieldMetadata.settings?.relationType === RelationType.ONE_TO_MANY)
|
||||
return {};
|
||||
@@ -204,10 +206,12 @@ export class RelationFieldMetadataGqlInputTypeGenerator {
|
||||
|
||||
if (
|
||||
isDefined(fieldMetadata.relationTargetObjectMetadataId) &&
|
||||
isDefined(objectMetadataMaps)
|
||||
isDefined(context)
|
||||
) {
|
||||
const targetObjectMetadata =
|
||||
objectMetadataMaps.byId[fieldMetadata.relationTargetObjectMetadataId];
|
||||
context.flatObjectMetadataMaps.byId[
|
||||
fieldMetadata.relationTargetObjectMetadataId
|
||||
];
|
||||
|
||||
if (isDefined(targetObjectMetadata)) {
|
||||
const targetGroupByInputTypeKey = computeObjectMetadataInputTypeKey(
|
||||
@@ -238,7 +242,7 @@ export class RelationFieldMetadataGqlInputTypeGenerator {
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
}: {
|
||||
fieldMetadata: FieldMetadataEntity<
|
||||
fieldMetadata: FlatFieldMetadata<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>;
|
||||
typeOptions: TypeOptions;
|
||||
|
||||
+24
-13
@@ -15,14 +15,16 @@ import { GqlInputTypeDefinitionKind } from 'src/engine/api/graphql/workspace-sch
|
||||
import { RelationFieldMetadataGqlInputTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/input-types/relation-field-metadata-gql-type.generator';
|
||||
import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
import { computeFieldInputTypeOptions } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-field-input-type-options.util';
|
||||
import { computeCompositeFieldInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-input-type-key.util';
|
||||
import { computeEnumFieldGqlTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-enum-field-gql-type-key.util';
|
||||
import { computeObjectMetadataInputTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-input-type.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
@@ -36,15 +38,20 @@ export class ObjectMetadataUpdateGqlInputTypeGenerator {
|
||||
private readonly typeMapperService: TypeMapperService,
|
||||
) {}
|
||||
|
||||
public buildAndStore(objectMetadata: ObjectMetadataEntity) {
|
||||
public buildAndStore(
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
fields: FlatFieldMetadata[],
|
||||
_context: SchemaGenerationContext,
|
||||
) {
|
||||
const inputType = new GraphQLInputObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.Update.toString()}Input`,
|
||||
description: objectMetadata.description,
|
||||
fields: () => this.generateFields(objectMetadata),
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}${GqlInputTypeDefinitionKind.Update.toString()}Input`,
|
||||
description: flatObjectMetadata.description,
|
||||
fields: () =>
|
||||
this.generateFields(flatObjectMetadata.nameSingular, fields),
|
||||
}) as GraphQLInputObjectType;
|
||||
|
||||
const key = computeObjectMetadataInputTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
GqlInputTypeDefinitionKind.Update,
|
||||
);
|
||||
|
||||
@@ -52,21 +59,25 @@ export class ObjectMetadataUpdateGqlInputTypeGenerator {
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
objectNameSingular: string,
|
||||
fields: FlatFieldMetadata[],
|
||||
): GraphQLInputFieldConfigMap {
|
||||
const allGeneratedFields: GraphQLInputFieldConfigMap = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
fieldMetadata.isNullable = true;
|
||||
for (const fieldMetadata of fields) {
|
||||
const modifiedFieldMetadata = {
|
||||
...fieldMetadata,
|
||||
isNullable: true,
|
||||
};
|
||||
|
||||
const typeOptions = computeFieldInputTypeOptions(
|
||||
fieldMetadata,
|
||||
modifiedFieldMetadata,
|
||||
GqlInputTypeDefinitionKind.Update,
|
||||
);
|
||||
|
||||
let generatedFields;
|
||||
|
||||
if (isFieldMetadataRelationOrMorphRelation(fieldMetadata)) {
|
||||
if (isMorphOrRelationFlatFieldMetadata(fieldMetadata)) {
|
||||
generatedFields = {
|
||||
...this.relationFieldMetadataGqlInputTypeGenerator.generateSimpleRelationFieldCreateOrUpdateInputType(
|
||||
{
|
||||
@@ -90,7 +101,7 @@ export class ObjectMetadataUpdateGqlInputTypeGenerator {
|
||||
|
||||
if (isEnumFieldMetadataType(fieldMetadata.type)) {
|
||||
const key = computeEnumFieldGqlTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
objectNameSingular,
|
||||
fieldMetadata.name,
|
||||
);
|
||||
|
||||
|
||||
+4
-5
@@ -4,18 +4,17 @@ import {
|
||||
type AggregationField,
|
||||
getAvailableAggregationsFromObjectFields,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
type AggregationGraphQLType = Pick<AggregationField, 'type' | 'description'>;
|
||||
|
||||
@Injectable()
|
||||
export class AggregationObjectTypeGenerator {
|
||||
public generate(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
flatFields: FlatFieldMetadata[],
|
||||
): Record<string, AggregationGraphQLType> {
|
||||
const availableAggregations = getAvailableAggregationsFromObjectFields(
|
||||
objectMetadata.fields,
|
||||
);
|
||||
const availableAggregations =
|
||||
getAvailableAggregationsFromObjectFields(flatFields);
|
||||
|
||||
return Object.entries(availableAggregations).reduce<
|
||||
Record<string, AggregationGraphQLType>
|
||||
|
||||
+16
-14
@@ -10,7 +10,8 @@ import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-build
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { GraphQLOutputTypeFieldConfigMap } from 'src/engine/api/graphql/workspace-schema-builder/types/graphql-field-config-map.types';
|
||||
import { computeObjectMetadataObjectTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-object-type-key.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
@@ -23,51 +24,52 @@ export class ConnectionGqlObjectTypeGenerator {
|
||||
private readonly gqlTypesStorage: GqlTypesStorage,
|
||||
) {}
|
||||
|
||||
public buildAndStore(objectMetadata: ObjectMetadataEntity) {
|
||||
public buildAndStore(
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFields: FlatFieldMetadata[],
|
||||
) {
|
||||
const kind = ObjectTypeDefinitionKind.Connection;
|
||||
|
||||
const key = computeObjectMetadataObjectTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
kind,
|
||||
);
|
||||
|
||||
this.gqlTypesStorage.addGqlType(
|
||||
key,
|
||||
new GraphQLObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${kind.toString()}`,
|
||||
description: objectMetadata.description,
|
||||
fields: () => this.generateFields(objectMetadata),
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}${kind.toString()}`,
|
||||
description: flatObjectMetadata.description,
|
||||
fields: () => this.generateFields(flatObjectMetadata, flatFields),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFields: FlatFieldMetadata[],
|
||||
): GraphQLOutputTypeFieldConfigMap {
|
||||
const fields: GraphQLOutputTypeFieldConfigMap = {};
|
||||
|
||||
const aggregatedFields =
|
||||
this.aggregationObjectTypeGenerator.generate(objectMetadata);
|
||||
this.aggregationObjectTypeGenerator.generate(flatFields);
|
||||
|
||||
Object.assign(fields, aggregatedFields);
|
||||
|
||||
const edgeType = this.gqlTypesStorage.getGqlTypeByKey(
|
||||
computeObjectMetadataObjectTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
ObjectTypeDefinitionKind.Edge,
|
||||
),
|
||||
);
|
||||
|
||||
if (!isDefined(edgeType) || isInputObjectType(edgeType)) {
|
||||
this.logger.error(
|
||||
`Edge type for ${objectMetadata.nameSingular} was not found. Please, check if you have defined it.`,
|
||||
{
|
||||
objectMetadata,
|
||||
},
|
||||
`Edge type for ${flatObjectMetadata.nameSingular} was not found. Please, check if you have defined it.`,
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Edge type for ${objectMetadata.nameSingular} was not found. Please, check if you have defined it.`,
|
||||
`Edge type for ${flatObjectMetadata.nameSingular} was not found. Please, check if you have defined it.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+10
-13
@@ -9,7 +9,7 @@ import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-build
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { GraphQLOutputTypeFieldConfigMap } from 'src/engine/api/graphql/workspace-schema-builder/types/graphql-field-config-map.types';
|
||||
import { computeObjectMetadataObjectTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-object-type-key.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
@@ -21,30 +21,30 @@ export class EdgeGqlObjectTypeGenerator {
|
||||
private readonly gqlTypesStorage: GqlTypesStorage,
|
||||
) {}
|
||||
|
||||
public buildAndStore(objectMetadata: ObjectMetadataEntity) {
|
||||
public buildAndStore(flatObjectMetadata: FlatObjectMetadata) {
|
||||
const kind = ObjectTypeDefinitionKind.Edge;
|
||||
const key = computeObjectMetadataObjectTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
kind,
|
||||
);
|
||||
|
||||
this.gqlTypesStorage.addGqlType(
|
||||
key,
|
||||
new GraphQLObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${kind.toString()}`,
|
||||
description: objectMetadata.description,
|
||||
fields: () => this.generateFields(objectMetadata),
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}${kind.toString()}`,
|
||||
description: flatObjectMetadata.description,
|
||||
fields: () => this.generateFields(flatObjectMetadata.nameSingular),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
objectNameSingular: string,
|
||||
): GraphQLOutputTypeFieldConfigMap {
|
||||
const fields: GraphQLOutputTypeFieldConfigMap = {};
|
||||
|
||||
const key = computeObjectMetadataObjectTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
objectNameSingular,
|
||||
ObjectTypeDefinitionKind.Plain,
|
||||
);
|
||||
|
||||
@@ -52,14 +52,11 @@ export class EdgeGqlObjectTypeGenerator {
|
||||
|
||||
if (!isDefined(objectType) || isInputObjectType(objectType)) {
|
||||
this.logger.error(
|
||||
`Node type for ${objectMetadata.nameSingular} was not found. Please, check if you have defined it.`,
|
||||
{
|
||||
objectMetadata,
|
||||
},
|
||||
`Node type for ${objectNameSingular} was not found. Please, check if you have defined it.`,
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Node type for ${objectMetadata.nameSingular} was not found. Please, check if you have defined it.`,
|
||||
`Node type for ${objectNameSingular} was not found. Please, check if you have defined it.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+10
-13
@@ -9,7 +9,7 @@ import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-build
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { GraphQLOutputTypeFieldConfigMap } from 'src/engine/api/graphql/workspace-schema-builder/types/graphql-field-config-map.types';
|
||||
import { computeObjectMetadataObjectTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-object-type-key.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
@@ -23,46 +23,43 @@ export class GroupByConnectionGqlObjectTypeGenerator {
|
||||
private readonly gqlTypesStorage: GqlTypesStorage,
|
||||
) {}
|
||||
|
||||
public buildAndStore(objectMetadata: ObjectMetadataEntity) {
|
||||
public buildAndStore(flatObjectMetadata: FlatObjectMetadata) {
|
||||
const kind = ObjectTypeDefinitionKind.GroupByConnection;
|
||||
|
||||
const key = computeObjectMetadataObjectTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
kind,
|
||||
);
|
||||
|
||||
this.gqlTypesStorage.addGqlType(
|
||||
key,
|
||||
new GraphQLObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${kind.toString()}`,
|
||||
description: objectMetadata.description,
|
||||
fields: () => this.generateFields(objectMetadata),
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}${kind.toString()}`,
|
||||
description: flatObjectMetadata.description,
|
||||
fields: () => this.generateFields(flatObjectMetadata.nameSingular),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
objectNameSingular: string,
|
||||
): GraphQLOutputTypeFieldConfigMap {
|
||||
const fields: GraphQLOutputTypeFieldConfigMap = {};
|
||||
|
||||
const connection = this.gqlTypesStorage.getGqlTypeByKey(
|
||||
computeObjectMetadataObjectTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
objectNameSingular,
|
||||
ObjectTypeDefinitionKind.Connection,
|
||||
),
|
||||
);
|
||||
|
||||
if (!isDefined(connection) || !isObjectType(connection)) {
|
||||
this.logger.error(
|
||||
`Connection type for ${objectMetadata.nameSingular} was not found.`,
|
||||
{
|
||||
objectMetadata,
|
||||
},
|
||||
`Connection type for ${objectNameSingular} was not found.`,
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Connection type for ${objectMetadata.nameSingular} was not found.`,
|
||||
`Connection type for ${objectNameSingular} was not found.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+20
-18
@@ -16,11 +16,11 @@ import { GraphQLOutputTypeFieldConfigMap } from 'src/engine/api/graphql/workspac
|
||||
import { computeCompositeFieldObjectTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-composite-field-object-type-key.util';
|
||||
import { computeEnumFieldGqlTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-enum-field-gql-type-key.util';
|
||||
import { computeObjectMetadataObjectTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-object-type-key.util';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { isMorphOrRelationFieldMetadataType } from 'src/engine/utils/is-morph-or-relation-field-metadata-type.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { pascalCase } from 'src/utils/pascal-case';
|
||||
|
||||
@Injectable()
|
||||
@@ -35,29 +35,33 @@ export class ObjectMetadataGqlObjectTypeGenerator {
|
||||
private readonly typeMapperService: TypeMapperService,
|
||||
) {}
|
||||
|
||||
public buildAndStore(objectMetadata: ObjectMetadataEntity) {
|
||||
public buildAndStore(
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
fields: FlatFieldMetadata[],
|
||||
) {
|
||||
const kind = ObjectTypeDefinitionKind.Plain;
|
||||
const key = computeObjectMetadataObjectTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
ObjectTypeDefinitionKind.Plain,
|
||||
);
|
||||
|
||||
this.gqlTypesStorage.addGqlType(
|
||||
key,
|
||||
new GraphQLObjectType({
|
||||
name: `${pascalCase(objectMetadata.nameSingular)}${kind.toString()}`,
|
||||
description: objectMetadata.description,
|
||||
fields: this.generateFields(objectMetadata),
|
||||
name: `${pascalCase(flatObjectMetadata.nameSingular)}${kind.toString()}`,
|
||||
description: flatObjectMetadata.description,
|
||||
fields: this.generateFields(flatObjectMetadata.nameSingular, fields),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
objectNameSingular: string,
|
||||
fields: FlatFieldMetadata[],
|
||||
): GraphQLOutputTypeFieldConfigMap {
|
||||
const allGeneratedFields: GraphQLOutputTypeFieldConfigMap = {};
|
||||
|
||||
for (const field of objectMetadata.fields) {
|
||||
for (const field of fields) {
|
||||
const typeFactoryOptions = {
|
||||
nullable: field.isNullable ?? undefined,
|
||||
isArray: field.type === FieldMetadataType.MULTI_SELECT,
|
||||
@@ -66,13 +70,11 @@ export class ObjectMetadataGqlObjectTypeGenerator {
|
||||
isIdField: false,
|
||||
};
|
||||
|
||||
if (isMorphOrRelationFieldMetadataType(field.type)) {
|
||||
if (isMorphOrRelationFlatFieldMetadata(field)) {
|
||||
const relationFieldObjectType =
|
||||
this.relationFieldMetadataGqlObjectTypeGenerator.generateRelationFieldObjectType(
|
||||
{
|
||||
fieldMetadata: field as FieldMetadataEntity<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>,
|
||||
fieldMetadata: field,
|
||||
typeOptions: typeFactoryOptions,
|
||||
},
|
||||
);
|
||||
@@ -96,7 +98,7 @@ export class ObjectMetadataGqlObjectTypeGenerator {
|
||||
!isDefined(compositeFieldObjectType) ||
|
||||
isInputObjectType(compositeFieldObjectType)
|
||||
) {
|
||||
const message = `Could not find a GraphQL output type for ${field.name} composite field metadata of object ${objectMetadata.nameSingular}`;
|
||||
const message = `Could not find a GraphQL output type for ${field.name} composite field metadata of object ${objectNameSingular}`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type: field.type,
|
||||
@@ -109,7 +111,7 @@ export class ObjectMetadataGqlObjectTypeGenerator {
|
||||
type = compositeFieldObjectType;
|
||||
} else if (isEnumFieldMetadataType(field.type)) {
|
||||
const enumFieldEnumTypeKey = computeEnumFieldGqlTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
objectNameSingular,
|
||||
field.name,
|
||||
);
|
||||
|
||||
@@ -120,7 +122,7 @@ export class ObjectMetadataGqlObjectTypeGenerator {
|
||||
!isDefined(enumFieldEnumType) ||
|
||||
isInputObjectType(enumFieldEnumType)
|
||||
) {
|
||||
const message = `Could not find a GraphQL output type for ${field.name} enum field metadata of object ${objectMetadata.nameSingular}`;
|
||||
const message = `Could not find a GraphQL output type for ${field.name} enum field metadata of object ${objectNameSingular}`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type: field.type,
|
||||
@@ -138,7 +140,7 @@ export class ObjectMetadataGqlObjectTypeGenerator {
|
||||
);
|
||||
|
||||
if (!isDefined(type)) {
|
||||
const message = `Could not find a GraphQL output type for ${field.name} scalar field metadata of object ${objectMetadata.nameSingular}`;
|
||||
const message = `Could not find a GraphQL output type for ${field.name} scalar field metadata of object ${objectNameSingular}`;
|
||||
|
||||
this.logger.error(message, {
|
||||
type: field.type,
|
||||
|
||||
+49
-59
@@ -15,12 +15,13 @@ import { ObjectTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schem
|
||||
import { ArgsTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/args-type/args-type.generator';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { GraphQLOutputTypeFieldConfigMap } from 'src/engine/api/graphql/workspace-schema-builder/types/graphql-field-config-map.types';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
import { computeObjectMetadataObjectTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-object-type-key.util';
|
||||
import { getResolverArgs } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util';
|
||||
import { objectContainsRelationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/object-contains-relation-field';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { isMorphOrRelationFieldMetadataType } from 'src/engine/utils/is-morph-or-relation-field-metadata-type.util';
|
||||
|
||||
@Injectable()
|
||||
export class ObjectMetadataWithRelationsGqlObjectTypeGenerator {
|
||||
@@ -34,11 +35,12 @@ export class ObjectMetadataWithRelationsGqlObjectTypeGenerator {
|
||||
) {}
|
||||
|
||||
public buildAndStore(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
objectMetadataCollection: ObjectMetadataEntity[],
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadatas: FlatFieldMetadata[],
|
||||
context: SchemaGenerationContext,
|
||||
) {
|
||||
const key = computeObjectMetadataObjectTypeKey(
|
||||
objectMetadata.nameSingular,
|
||||
flatObjectMetadata.nameSingular,
|
||||
ObjectTypeDefinitionKind.Plain,
|
||||
);
|
||||
|
||||
@@ -46,28 +48,26 @@ export class ObjectMetadataWithRelationsGqlObjectTypeGenerator {
|
||||
|
||||
if (!isDefined(gqlType) || !isObjectType(gqlType)) {
|
||||
this.logger.error(
|
||||
`Could not find a GraphQL type for ${objectMetadata.id.toString()}`,
|
||||
{
|
||||
objectMetadata,
|
||||
},
|
||||
`Could not find a GraphQL type for ${flatObjectMetadata.id.toString()}`,
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Could not find a GraphQL type for ${objectMetadata.id.toString()}`,
|
||||
`Could not find a GraphQL type for ${flatObjectMetadata.id.toString()}`,
|
||||
);
|
||||
}
|
||||
|
||||
const containsRelationOrMorphField = flatFieldMetadatas.some((field) =>
|
||||
isMorphOrRelationFieldMetadataType(field.type),
|
||||
);
|
||||
|
||||
// Security check to avoid extending an object that does not need to be extended
|
||||
if (!objectContainsRelationField(objectMetadata)) {
|
||||
if (!containsRelationOrMorphField) {
|
||||
this.logger.error(
|
||||
`This object does not need to be extended: ${objectMetadata.id.toString()}`,
|
||||
{
|
||||
objectMetadata,
|
||||
},
|
||||
`This object does not need to be extended: ${flatObjectMetadata.id.toString()}`,
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`This object does not need to be extended: ${objectMetadata.id.toString()}`,
|
||||
`This object does not need to be extended: ${flatObjectMetadata.id.toString()}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,66 +81,57 @@ export class ObjectMetadataWithRelationsGqlObjectTypeGenerator {
|
||||
...config,
|
||||
fields: () => ({
|
||||
...config.fields,
|
||||
...this.generateFields(objectMetadata, objectMetadataCollection),
|
||||
...this.generateFields(flatFieldMetadatas, context),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
objectMetadataCollection: ObjectMetadataEntity[],
|
||||
flatFieldMetadatas: FlatFieldMetadata[],
|
||||
context: SchemaGenerationContext,
|
||||
): GraphQLOutputTypeFieldConfigMap {
|
||||
const fields: GraphQLOutputTypeFieldConfigMap = {};
|
||||
const outputFields: GraphQLOutputTypeFieldConfigMap = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
// Ignore non-relation fields as they are already defined
|
||||
const isRelation =
|
||||
isFieldMetadataEntityOfType(
|
||||
fieldMetadata,
|
||||
FieldMetadataType.RELATION,
|
||||
) ||
|
||||
isFieldMetadataEntityOfType(
|
||||
fieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
);
|
||||
|
||||
if (!isRelation) {
|
||||
for (const flatFieldMetadata of flatFieldMetadatas) {
|
||||
if (!isMorphOrRelationFlatFieldMetadata(flatFieldMetadata)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fieldMetadata.settings) {
|
||||
if (!flatFieldMetadata.settings) {
|
||||
throw new Error(
|
||||
`Field Metadata of type RELATION or MORPH_RELATION with id ${fieldMetadata.id} has no settings`,
|
||||
`Field Metadata of type RELATION or MORPH_RELATION with id ${flatFieldMetadata.id} has no settings`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!fieldMetadata.relationTargetObjectMetadataId) {
|
||||
if (!flatFieldMetadata.relationTargetObjectMetadataId) {
|
||||
throw new Error(
|
||||
`Field Metadata of type RELATION or MORPH_RELATION with id ${fieldMetadata.id} has no relation target object metadata id`,
|
||||
`Field Metadata of type RELATION or MORPH_RELATION with id ${flatFieldMetadata.id} has no relation target object metadata id`,
|
||||
);
|
||||
}
|
||||
|
||||
const objectMetadataTarget = objectMetadataCollection.find(
|
||||
(objectMetadata) =>
|
||||
objectMetadata.id === fieldMetadata.relationTargetObjectMetadataId,
|
||||
);
|
||||
const objectMetadataTarget =
|
||||
context.flatObjectMetadataMaps.byId[
|
||||
flatFieldMetadata.relationTargetObjectMetadataId
|
||||
];
|
||||
|
||||
if (!isDefined(objectMetadataTarget)) {
|
||||
throw new Error(
|
||||
`Field Metadata of type RELATION or MORPH_RELATION with id ${fieldMetadata.id} has no relation target object metadata`,
|
||||
`Field Metadata of type RELATION or MORPH_RELATION with id ${flatFieldMetadata.id} has no relation target object metadata`,
|
||||
);
|
||||
}
|
||||
|
||||
const targetObjectMetadataGqlObjectType =
|
||||
this.fetchTargetObjectMetadataGqlObjectType(
|
||||
fieldMetadata,
|
||||
flatFieldMetadata,
|
||||
objectMetadataTarget,
|
||||
);
|
||||
|
||||
let argsType: GraphQLFieldConfigArgumentMap | undefined = undefined;
|
||||
|
||||
if (fieldMetadata.settings.relationType === RelationType.ONE_TO_MANY) {
|
||||
if (
|
||||
flatFieldMetadata.settings?.relationType === RelationType.ONE_TO_MANY
|
||||
) {
|
||||
const args = getResolverArgs('findMany');
|
||||
|
||||
argsType = this.argsTypeGenerator.generate({
|
||||
@@ -149,31 +140,31 @@ export class ObjectMetadataWithRelationsGqlObjectTypeGenerator {
|
||||
});
|
||||
}
|
||||
|
||||
fields[fieldMetadata.name] = {
|
||||
outputFields[flatFieldMetadata.name] = {
|
||||
type: targetObjectMetadataGqlObjectType,
|
||||
args: argsType,
|
||||
description: fieldMetadata.description,
|
||||
description: flatFieldMetadata.description,
|
||||
};
|
||||
}
|
||||
|
||||
return fields;
|
||||
return outputFields;
|
||||
}
|
||||
|
||||
private fetchTargetObjectMetadataGqlObjectType(
|
||||
fieldMetadata: FieldMetadataEntity<
|
||||
flatFieldMetadata: FlatFieldMetadata<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>,
|
||||
objectMetadataTarget: ObjectMetadataEntity,
|
||||
objectMetadataTarget: FlatObjectMetadata,
|
||||
): GraphQLOutputType {
|
||||
if (!fieldMetadata.settings) {
|
||||
if (!flatFieldMetadata.settings) {
|
||||
throw new Error(
|
||||
`Field Metadata of type RELATION or MORPH_RELATION with id ${fieldMetadata.id} has no settings`,
|
||||
`Field Metadata of type RELATION or MORPH_RELATION with id ${flatFieldMetadata.id} has no settings`,
|
||||
);
|
||||
}
|
||||
|
||||
const key = computeObjectMetadataObjectTypeKey(
|
||||
objectMetadataTarget.nameSingular,
|
||||
fieldMetadata.settings.relationType === RelationType.ONE_TO_MANY
|
||||
flatFieldMetadata.settings.relationType === RelationType.ONE_TO_MANY
|
||||
? ObjectTypeDefinitionKind.Connection
|
||||
: ObjectTypeDefinitionKind.Plain,
|
||||
);
|
||||
@@ -186,13 +177,12 @@ export class ObjectMetadataWithRelationsGqlObjectTypeGenerator {
|
||||
!isObjectType(targetObjectMetadataGqlObjectType)
|
||||
) {
|
||||
this.logger.error(
|
||||
`Could not find a relation type for ${fieldMetadata.id}`,
|
||||
{
|
||||
fieldMetadata,
|
||||
},
|
||||
`Could not find a relation type for ${flatFieldMetadata.id}`,
|
||||
);
|
||||
|
||||
throw new Error(`Could not find a relation type for ${fieldMetadata.id}`);
|
||||
throw new Error(
|
||||
`Could not find a relation type for ${flatFieldMetadata.id}`,
|
||||
);
|
||||
}
|
||||
|
||||
return targetObjectMetadataGqlObjectType;
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ import {
|
||||
TypeOptions,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { extractGraphQLRelationFieldNames } from 'src/engine/api/graphql/workspace-schema-builder/utils/extract-graphql-relation-field-names.util';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
@Injectable()
|
||||
export class RelationFieldMetadataGqlObjectTypeGenerator {
|
||||
@@ -22,7 +22,7 @@ export class RelationFieldMetadataGqlObjectTypeGenerator {
|
||||
fieldMetadata,
|
||||
typeOptions,
|
||||
}: {
|
||||
fieldMetadata: FieldMetadataEntity<
|
||||
fieldMetadata: FlatFieldMetadata<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>;
|
||||
typeOptions: TypeOptions;
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@ import { workspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/work
|
||||
import { GqlOperation } from 'src/engine/api/graphql/workspace-schema-builder/enums/gql-operation.enum';
|
||||
import { RootTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/root-types/root-type.generator';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
|
||||
@Injectable()
|
||||
export class MutationTypeGenerator {
|
||||
@@ -15,9 +15,9 @@ export class MutationTypeGenerator {
|
||||
private readonly gqlTypesStorage: GqlTypesStorage,
|
||||
) {}
|
||||
|
||||
buildAndStore(objectMetadataCollection: ObjectMetadataEntity[]) {
|
||||
buildAndStore(context: SchemaGenerationContext) {
|
||||
return this.rootTypeGenerator.buildAndStore(
|
||||
objectMetadataCollection,
|
||||
context,
|
||||
[...workspaceResolverBuilderMethodNames.mutations],
|
||||
GqlOperation.Mutation,
|
||||
);
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@ import { workspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/work
|
||||
import { GqlOperation } from 'src/engine/api/graphql/workspace-schema-builder/enums/gql-operation.enum';
|
||||
import { RootTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/root-types/root-type.generator';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
|
||||
@Injectable()
|
||||
export class QueryTypeGenerator {
|
||||
@@ -15,9 +15,9 @@ export class QueryTypeGenerator {
|
||||
private readonly gqlTypesStorage: GqlTypesStorage,
|
||||
) {}
|
||||
|
||||
async buildAndStore(objectMetadataCollection: ObjectMetadataEntity[]) {
|
||||
async buildAndStore(context: SchemaGenerationContext) {
|
||||
return this.rootTypeGenerator.buildAndStore(
|
||||
objectMetadataCollection,
|
||||
context,
|
||||
workspaceResolverBuilderMethodNames.queries,
|
||||
GqlOperation.Query,
|
||||
);
|
||||
|
||||
+8
-3
@@ -11,10 +11,11 @@ import { ObjectTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schem
|
||||
import { ArgsTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/args-type/args-type.generator';
|
||||
import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { GqlTypesStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/gql-types.storage';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
import { GraphQLRootTypeFieldConfigMap } from 'src/engine/api/graphql/workspace-schema-builder/types/graphql-field-config-map.types';
|
||||
import { computeObjectMetadataObjectTypeKey } from 'src/engine/api/graphql/workspace-schema-builder/utils/compute-stored-gql-type-key-utils/compute-object-metadata-object-type-key.util';
|
||||
import { getResolverArgs } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { getResolverName } from 'src/engine/utils/get-resolver-name.util';
|
||||
|
||||
@Injectable()
|
||||
@@ -29,7 +30,7 @@ export class RootTypeGenerator {
|
||||
) {}
|
||||
|
||||
buildAndStore(
|
||||
objectMetadataCollection: ObjectMetadataEntity[],
|
||||
context: SchemaGenerationContext,
|
||||
workspaceResolverMethodNames: readonly WorkspaceResolverBuilderMethodNames[],
|
||||
objectTypeName: GqlOperation,
|
||||
) {
|
||||
@@ -47,6 +48,10 @@ export class RootTypeGenerator {
|
||||
);
|
||||
}
|
||||
|
||||
const objectMetadataCollection = Object.values(
|
||||
context.flatObjectMetadataMaps.byId,
|
||||
).filter(isDefined);
|
||||
|
||||
this.gqlTypesStorage.addGqlType(
|
||||
objectTypeName,
|
||||
new GraphQLObjectType({
|
||||
@@ -60,7 +65,7 @@ export class RootTypeGenerator {
|
||||
}
|
||||
|
||||
private generateFields(
|
||||
objectMetadataCollection: ObjectMetadataEntity[],
|
||||
objectMetadataCollection: FlatObjectMetadata[],
|
||||
workspaceResolverMethodNames: readonly WorkspaceResolverBuilderMethodNames[],
|
||||
): GraphQLRootTypeFieldConfigMap {
|
||||
const fieldConfigMap: GraphQLRootTypeFieldConfigMap = {};
|
||||
|
||||
+7
-4
@@ -1,9 +1,12 @@
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export interface WorkspaceSchemaBuilderContext {
|
||||
authContext: AuthContext;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
objectIdByNameSingular: Record<string, string>;
|
||||
}
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export type SchemaGenerationContext = {
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
flatIndexMaps: FlatEntityMaps<FlatIndexMetadata>;
|
||||
};
|
||||
+4
-4
@@ -2,15 +2,15 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { GqlInputTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schema-builder/enums/gql-input-type-definition-kind.enum';
|
||||
import { type TypeOptions } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
export const computeFieldInputTypeOptions = <T extends FieldMetadataType>(
|
||||
fieldMetadata: FieldMetadataEntity<T>,
|
||||
export const computeFieldInputTypeOptions = (
|
||||
fieldMetadata: FlatFieldMetadata,
|
||||
kind: GqlInputTypeDefinitionKind,
|
||||
): TypeOptions => {
|
||||
return {
|
||||
nullable: fieldMetadata.isNullable ?? undefined,
|
||||
defaultValue: fieldMetadata.defaultValue,
|
||||
defaultValue: fieldMetadata.defaultValue ?? undefined,
|
||||
isArray:
|
||||
kind !== GqlInputTypeDefinitionKind.Filter &&
|
||||
fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
|
||||
+4
-3
@@ -1,13 +1,14 @@
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
export const extractGraphQLRelationFieldNames = (
|
||||
fieldMetadata: FieldMetadataEntity<
|
||||
fieldMetadata: FlatFieldMetadata<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>,
|
||||
) => {
|
||||
const joinColumnName = fieldMetadata.settings?.joinColumnName;
|
||||
const settings = fieldMetadata.settings;
|
||||
const joinColumnName = settings?.joinColumnName;
|
||||
|
||||
if (!joinColumnName) {
|
||||
throw new Error('Join column name is not defined');
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { capitalize, isFieldMetadataDateKind } from 'twenty-shared/utils';
|
||||
|
||||
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { getSubfieldsForAggregateOperation } from 'src/engine/twenty-orm/utils/get-subfields-for-aggregate-operation.util';
|
||||
|
||||
export type AggregationField = {
|
||||
@@ -20,7 +20,7 @@ export type AggregationField = {
|
||||
};
|
||||
|
||||
export const getAvailableAggregationsFromObjectFields = (
|
||||
fields: FieldMetadataEntity[],
|
||||
fields: FlatFieldMetadata[],
|
||||
): Record<string, AggregationField> => {
|
||||
return fields.reduce<Record<string, AggregationField>>(
|
||||
(acc, field) => {
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const getFlatFieldsFromFlatObjectMetadata = (
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
): FlatFieldMetadata[] => {
|
||||
return flatObjectMetadata.fieldMetadataIds
|
||||
.map((fieldId) => flatFieldMetadataMaps.byId[fieldId])
|
||||
.filter(isDefined);
|
||||
};
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { isMorphRelationFieldMetadataType } from 'src/engine/utils/is-morph-relation-field-metadata-type.util';
|
||||
|
||||
export const objectContainsMorphRelationField = (
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
): boolean => {
|
||||
return objectMetadata.fields.some((field) =>
|
||||
isMorphRelationFieldMetadataType(field.type),
|
||||
);
|
||||
};
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { isMorphOrRelationFieldMetadataType } from 'src/engine/utils/is-morph-or-relation-field-metadata-type.util';
|
||||
|
||||
export const objectContainsRelationField = (
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
): boolean => {
|
||||
return objectMetadata.fields.some((field) =>
|
||||
isMorphOrRelationFieldMetadataType(field.type),
|
||||
);
|
||||
};
|
||||
+3
-3
@@ -6,7 +6,7 @@ import { GqlTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builde
|
||||
import { OrphanedTypesGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/orphaned-types.generator';
|
||||
import { MutationTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/root-types/mutation-type.generator';
|
||||
import { QueryTypeGenerator } from 'src/engine/api/graphql/workspace-schema-builder/graphql-type-generators/root-types/query-type.generator';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type SchemaGenerationContext } from 'src/engine/api/graphql/workspace-schema-builder/types/schema-generation-context.type';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceGraphQLSchemaGenerator {
|
||||
@@ -18,9 +18,9 @@ export class WorkspaceGraphQLSchemaGenerator {
|
||||
) {}
|
||||
|
||||
async generateSchema(
|
||||
objectMetadataCollection: ObjectMetadataEntity[],
|
||||
context: SchemaGenerationContext,
|
||||
): Promise<GraphQLSchema> {
|
||||
await this.gqlTypeGenerator.buildAndStore(objectMetadataCollection);
|
||||
await this.gqlTypeGenerator.buildAndStore(context);
|
||||
|
||||
// Assemble schema
|
||||
const schema = new GraphQLSchema({
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
AuthExceptionCode,
|
||||
} from 'src/engine/core-modules/auth/auth.exception';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import {
|
||||
FlatEntityMapsException,
|
||||
@@ -22,11 +21,7 @@ import {
|
||||
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { buildObjectIdByNameMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-id-by-name-maps.util';
|
||||
import { buildObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-metadata-item-with-field-maps.util';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
|
||||
import { shouldExcludeFromWorkspaceApi } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/should-exclude-from-workspace-api.util';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceSchemaFactory {
|
||||
@@ -37,7 +32,6 @@ export class WorkspaceSchemaFactory {
|
||||
private readonly workspaceResolverFactory: WorkspaceResolverFactory,
|
||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
|
||||
async createGraphQLSchema(authContext: AuthContext): Promise<GraphQLSchema> {
|
||||
@@ -82,8 +76,12 @@ export class WorkspaceSchemaFactory {
|
||||
);
|
||||
}
|
||||
|
||||
const workspaceFeatureFlagsMap =
|
||||
await this.featureFlagService.getWorkspaceFeatureFlagsMap(workspaceId);
|
||||
if (!isDefined(flatFieldMetadataMaps)) {
|
||||
throw new FlatEntityMapsException(
|
||||
'Field metadata collection not found',
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
let metadataVersion =
|
||||
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
|
||||
@@ -99,37 +97,7 @@ export class WorkspaceSchemaFactory {
|
||||
const { idByNameSingular } = buildObjectIdByNameMaps(
|
||||
flatObjectMetadataMaps,
|
||||
);
|
||||
const objectMetadataMaps: ObjectMetadataMaps = {
|
||||
byId: {},
|
||||
idByNameSingular,
|
||||
};
|
||||
|
||||
for (const [id, flatObj] of Object.entries(flatObjectMetadataMaps.byId)) {
|
||||
if (isDefined(flatObj)) {
|
||||
objectMetadataMaps.byId[id] = buildObjectMetadataItemWithFieldMaps(
|
||||
flatObj,
|
||||
flatFieldMetadataMaps,
|
||||
flatIndexMaps,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const objectMetadataCollection = Object.values(objectMetadataMaps.byId)
|
||||
.filter(isDefined)
|
||||
.map((objectMetadataItem) => ({
|
||||
...objectMetadataItem,
|
||||
fields: Object.values(objectMetadataItem.fieldsById),
|
||||
indexes: objectMetadataItem.indexMetadatas,
|
||||
}))
|
||||
.filter((objectMetadata) => {
|
||||
return !shouldExcludeFromWorkspaceApi(
|
||||
objectMetadata,
|
||||
standardObjectMetadataDefinitions,
|
||||
workspaceFeatureFlagsMap,
|
||||
);
|
||||
});
|
||||
|
||||
// Get typeDefs from cache
|
||||
let typeDefs = await this.workspaceCacheStorageService.getGraphQLTypeDefs(
|
||||
authContext.workspace.id,
|
||||
metadataVersion,
|
||||
@@ -140,12 +108,13 @@ export class WorkspaceSchemaFactory {
|
||||
metadataVersion,
|
||||
);
|
||||
|
||||
// If typeDefs are not cached, generate them
|
||||
if (!typeDefs || !usedScalarNames) {
|
||||
const autoGeneratedSchema =
|
||||
await this.workspaceGraphQLSchemaGenerator.generateSchema(
|
||||
objectMetadataCollection,
|
||||
);
|
||||
await this.workspaceGraphQLSchemaGenerator.generateSchema({
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatIndexMaps,
|
||||
});
|
||||
|
||||
usedScalarNames =
|
||||
this.scalarsExplorerService.getUsedScalarNames(autoGeneratedSchema);
|
||||
@@ -165,7 +134,9 @@ export class WorkspaceSchemaFactory {
|
||||
|
||||
const autoGeneratedResolvers = await this.workspaceResolverFactory.create(
|
||||
authContext,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
idByNameSingular,
|
||||
workspaceResolverBuilderMethodNames,
|
||||
);
|
||||
const scalarsResolvers =
|
||||
|
||||
@@ -22,15 +22,16 @@ import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspac
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { buildObjectIdByNameMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-id-by-name-maps.util';
|
||||
import { buildObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-metadata-item-with-field-maps.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import {
|
||||
PermissionsException,
|
||||
PermissionsExceptionCode,
|
||||
PermissionsExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
@@ -128,21 +129,24 @@ export abstract class RestApiBaseHandler {
|
||||
async computeSelectedFields({
|
||||
authContext,
|
||||
depth,
|
||||
objectMetadataMapItem,
|
||||
objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
}: {
|
||||
authContext: WorkspaceAuthContext;
|
||||
depth?: Depth | undefined;
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
}): Promise<CommonSelectedFields> {
|
||||
const { objectsPermissions } =
|
||||
await this.getObjectsPermissions(authContext);
|
||||
|
||||
return this.restToCommonSelectedFieldsHandler.computeFromDepth({
|
||||
objectsPermissions,
|
||||
objectMetadataMaps,
|
||||
objectMetadataMapItem,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
depth,
|
||||
});
|
||||
}
|
||||
@@ -150,15 +154,21 @@ export abstract class RestApiBaseHandler {
|
||||
async buildCommonOptions(request: AuthenticatedRequest) {
|
||||
const { object: parsedObject } = parseCorePath(request);
|
||||
|
||||
const { objectMetadataMaps, objectMetadataMapItem } =
|
||||
await this.getObjectMetadata(request, parsedObject);
|
||||
const {
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
objectIdByNameSingular,
|
||||
} = await this.getObjectMetadata(request, parsedObject);
|
||||
|
||||
const authContext = this.getAuthContextFromRequest(request);
|
||||
|
||||
return {
|
||||
authContext,
|
||||
objectMetadataItemWithFieldMaps: objectMetadataMapItem,
|
||||
objectMetadataMaps: objectMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
objectIdByNameSingular,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -166,8 +176,11 @@ export abstract class RestApiBaseHandler {
|
||||
request: AuthenticatedRequest,
|
||||
parsedObject: string,
|
||||
): Promise<{
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
flatIndexMaps: FlatEntityMaps<FlatIndexMetadata>;
|
||||
objectIdByNameSingular: Record<string, string>;
|
||||
}> {
|
||||
const { workspace } =
|
||||
await this.accessTokenService.validateTokenByRequest(request);
|
||||
@@ -242,16 +255,10 @@ export abstract class RestApiBaseHandler {
|
||||
const workspaceFeatureFlagsMap =
|
||||
await this.featureFlagService.getWorkspaceFeatureFlagsMap(workspace.id);
|
||||
|
||||
const objectMetadataItem = buildObjectMetadataItemWithFieldMaps(
|
||||
flatObjectMetadataItem,
|
||||
flatFieldMetadataMaps,
|
||||
flatIndexMaps,
|
||||
);
|
||||
|
||||
// Check if this entity is workspace-gated and should be blocked from workspace API
|
||||
if (
|
||||
shouldExcludeFromWorkspaceApi(
|
||||
objectMetadataItem,
|
||||
flatObjectMetadataItem,
|
||||
standardObjectMetadataDefinitions,
|
||||
workspaceFeatureFlagsMap,
|
||||
)
|
||||
@@ -261,24 +268,12 @@ export abstract class RestApiBaseHandler {
|
||||
);
|
||||
}
|
||||
|
||||
const objectMetadataMaps: ObjectMetadataMaps = {
|
||||
byId: {},
|
||||
idByNameSingular,
|
||||
};
|
||||
|
||||
for (const [id, flatObj] of Object.entries(flatObjectMetadataMaps.byId)) {
|
||||
if (isDefined(flatObj)) {
|
||||
objectMetadataMaps.byId[id] = buildObjectMetadataItemWithFieldMaps(
|
||||
flatObj,
|
||||
flatFieldMetadataMaps,
|
||||
flatIndexMaps,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
objectMetadataMaps,
|
||||
objectMetadataMapItem: objectMetadataItem,
|
||||
flatObjectMetadata: flatObjectMetadataItem,
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatIndexMaps,
|
||||
objectIdByNameSingular: idByNameSingular,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user