Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasbordeau committed Nov 13, 2024
1 parent 025a135 commit c7efc99
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export class PersonQueryResultGetterHandler
person: PersonWorkspaceEntity,
workspaceId: string,
): Promise<PersonWorkspaceEntity> {
console.log('PersonQueryResultGetterHandler.handle');

if (!person.id || !person?.avatarUrl) {
return person;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { Record as ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';

export interface QueryResultGetterHandlerInterface {
handle(result: any, workspaceId: string): Promise<any>;
handle(
objectRecord: ObjectRecord,
workspaceId: string,
): Promise<ObjectRecord>;
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,41 @@
import { Injectable } from '@nestjs/common';

import { Record as ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
import { QueryResultGetterHandlerInterface } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-getter-handler.interface';
import { IConnection } from 'src/engine/api/graphql/workspace-query-runner/interfaces/connection.interface';
import { IEdge } from 'src/engine/api/graphql/workspace-query-runner/interfaces/edge.interface';
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';

import { ActivityQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/activity-query-result-getter.handler';
import { AttachmentQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/attachment-query-result-getter.handler';
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 {
isPossibleFieldValueAConnection,
isPossibleFieldValueARecordArray,
} from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/utils/isResultAConnection';
import { FileService } from 'src/engine/core-modules/file/services/file.service';
import { LogExecutionTime } from 'src/engine/decorators/observability/log-execution-time.decorator';
import { ObjectMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';

export type PossibleFieldValue =
| IConnection<ObjectRecord>
| { records: ObjectRecord[] }
| ObjectRecord;

// 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
// Right now the factory will override any change made on relations by the handlers
@Injectable()
export class QueryResultGettersFactory {
private handlers: Map<string, QueryResultGetterHandlerInterface>;

constructor(private readonly fileService: FileService) {
constructor(
private readonly fileService: FileService,
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
) {
this.initializeHandlers();
}

Expand All @@ -30,37 +52,160 @@ export class QueryResultGettersFactory {
]);
}

async processConnection(
connection: IConnection<ObjectRecord>,
objectMetadataItemId: string,
objectMetadataMap: ObjectMetadataMap,
workspaceId: string,
): Promise<any> {
return {
...connection,
edges: await Promise.all(
connection.edges.map(async (edge: IEdge<ObjectRecord>) => ({
...edge,
node: await this.processRecord(
edge.node,
objectMetadataItemId,
objectMetadataMap,
workspaceId,
),
})),
),
};
}

async processRecordArray(
result: { records: ObjectRecord[] },
objectMetadataItemId: string,
objectMetadataMap: ObjectMetadataMap,
workspaceId: string,
) {
return {
...result,
records: await Promise.all(
result.records.map(
async (record: ObjectRecord) =>
await this.processRecord(
record,
objectMetadataItemId,
objectMetadataMap,
workspaceId,
),
),
),
};
}

async processRecord(
record: ObjectRecord,
objectMetadataItemId: string,
objectMetadataMap: ObjectMetadataMap,
workspaceId: string,
): Promise<ObjectRecord> {
const objectMetadataMapItem = objectMetadataMap[objectMetadataItemId];

const handler = this.getHandler(objectMetadataMapItem.nameSingular);

const relationFields = Object.keys(record)
.map((recordFieldName) => objectMetadataMapItem.fields[recordFieldName])
.filter((fieldMetadata) =>
isRelationFieldMetadataType(fieldMetadata.type),
);

// console.log({
// relationFields,
// });

// const relationFieldsProcessedMap = relationFields.reduce<
// Record<string, PossibleFieldValue>
// >((relationFieldMap, fieldMetadata) => {
// const relationMetadata =
// fieldMetadata.fromRelationMetadata ?? fieldMetadata.toRelationMetadata;

// if (!isDefined(relationMetadata)) {
// throw new Error('Relation metadata is not defined');
// }

// // TODO: computing this by taking the opposite of the current object metadata id
// // is really less than ideal. This should be computed based on the relation metadata
// // But right now it is too complex with the current structure and / or lack of utils
// // around the possible combinations with relation metadata from / to + MANY_TO_ONE / ONE_TO_MANY
// const relationObjectMetadataItemId =
// relationMetadata.fromObjectMetadataId === objectMetadataItemId
// ? relationMetadata.toObjectMetadataId
// : relationMetadata.fromObjectMetadataId;

// const relationObjectMetadataItem =
// objectMetadataMap[relationObjectMetadataItemId];

// if (!isDefined(relationObjectMetadataItem)) {
// throw new Error(
// `Object metadata not found for id ${relationObjectMetadataItemId}`,
// );
// }

// relationFieldMap[fieldMetadata.name] = this.processRecord(
// record[fieldMetadata.name],
// relationObjectMetadataItem.id,
// objectMetadataMap,
// workspaceId,
// );

// return relationFieldMap;
// }, {});

const objectRecordProcessedWithoutRelationFields = await handler.handle(
record,
workspaceId,
);

const newRecord = {
...objectRecordProcessedWithoutRelationFields,
// ...relationFieldsProcessedMap,
};

return newRecord;
}

@LogExecutionTime('QueryResultGettersFactory.create')
async create(
result: any,
result: PossibleFieldValue,
objectMetadataItem: ObjectMetadataInterface,
workspaceId: string,
): Promise<any> {
const handler = this.getHandler(objectMetadataItem.nameSingular);

if (result.edges) {
return {
...result,
edges: await Promise.all(
result.edges.map(async (edge: any) => ({
...edge,
node: await handler.handle(edge.node, workspaceId),
})),
),
};
}
const objectMetadataMap =
await this.workspaceMetadataCacheService.getWorkspaceObjectMetadataMap(
workspaceId,
);

if (result.records) {
return {
...result,
records: await Promise.all(
result.records.map(
async (item: any) => await handler.handle(item, workspaceId),
),
),
};
}
// console.log(
// 'QueryResultGettersFactory.create',
// objectMetadataItem.nameSingular,
// result,
// );

return await handler.handle(result, workspaceId);
if (isPossibleFieldValueAConnection(result)) {
return await this.processConnection(
result,
objectMetadataItem.id,
objectMetadataMap,
workspaceId,
);
} else if (isPossibleFieldValueARecordArray(result)) {
return await this.processRecordArray(
result,
objectMetadataItem.id,
objectMetadataMap,
workspaceId,
);
} else {
return await this.processRecord(
result,
objectMetadataItem.id,
objectMetadataMap,
workspaceId,
);
}
}

private getHandler(objectType: string): QueryResultGetterHandlerInterface {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { isDefined } from 'class-validator';

import { Record as ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
import { IConnection } from 'src/engine/api/graphql/workspace-query-runner/interfaces/connection.interface';
import { IEdge } from 'src/engine/api/graphql/workspace-query-runner/interfaces/edge.interface';

import { PossibleFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory';

export const isPossibleFieldValueAConnection = (
result: PossibleFieldValue,
): result is IConnection<ObjectRecord, IEdge<ObjectRecord>> => {
return isDefined((result as any).edges);
};

export const isPossibleFieldValueARecordArray = (
result: PossibleFieldValue,
): result is { records: ObjectRecord[] } => {
return Array.isArray((result as any).records);
};

export const isPossibleFieldValueARecord = (
result: PossibleFieldValue,
): result is ObjectRecord => {
return (
!isPossibleFieldValueAConnection(result) &&
!isPossibleFieldValueARecordArray(result)
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { FileModule } from 'src/engine/core-modules/file/file.module';
import { TelemetryModule } from 'src/engine/core-modules/telemetry/telemetry.module';
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
Expand All @@ -20,6 +21,7 @@ import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listen

@Module({
imports: [
WorkspaceMetadataCacheModule,
AuthModule,
WorkspaceQueryBuilderModule,
WorkspaceDataSourceModule,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Logger } from '@nestjs/common';

import { isDefined } from 'class-validator';

/**
* A decorator function that logs the execution time of the decorated method.
*
* @returns The modified property descriptor with the execution time logging functionality.
*/
export function LogExecutionTime() {
export function LogExecutionTime(label?: string | undefined) {
return function (
target: any,
propertyKey: string,
Expand All @@ -21,7 +23,11 @@ export function LogExecutionTime() {
const end = performance.now();
const executionTime = end - start;

logger.log(`Execution time: ${executionTime.toFixed(2)}ms`);
if (isDefined(label)) {
logger.log(`${label} execution time: ${executionTime.toFixed(2)}ms`);
} else {
logger.log(`Execution time: ${executionTime.toFixed(2)}ms`);
}

return result;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { IndexMetadataModule } from 'src/engine/metadata-modules/index-metadata/
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
import { RelationMetadataGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/relation-metadata/interceptors/relation-metadata-graphql-api-exception.interceptor';
import { RelationMetadataResolver } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.resolver';
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
Expand All @@ -39,6 +40,7 @@ import { RelationMetadataDTO } from './dtos/relation-metadata.dto';
WorkspaceMigrationModule,
WorkspaceCacheStorageModule,
WorkspaceMetadataVersionModule,
WorkspaceMetadataCacheModule,
],
services: [RelationMetadataService],
resolvers: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import { InvalidStringException } from 'src/engine/metadata-modules/utils/exceptions/invalid-string.exception';
import { validateFieldNameAvailabilityOrThrow } from 'src/engine/metadata-modules/utils/validate-field-name-availability.utils';
import { validateMetadataNameValidityOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name-validity.utils';
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
import {
Expand All @@ -33,7 +34,6 @@ import {
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
import { BASE_OBJECT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';

Expand All @@ -55,7 +55,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
private readonly workspaceMigrationService: WorkspaceMigrationService,
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
private readonly indexMetadataService: IndexMetadataService,
) {
super(relationMetadataRepository);
Expand Down Expand Up @@ -458,27 +458,11 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
>,
workspaceId: string,
): Promise<(RelationMetadataEntity | NotFoundException)[]> {
const metadataVersion =
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);

if (!metadataVersion) {
throw new NotFoundException(
`Metadata version not found for workspace ${workspaceId}`,
);
}

const objectMetadataMap =
await this.workspaceCacheStorageService.getObjectMetadataMap(
await this.workspaceMetadataCacheService.getWorkspaceObjectMetadataMap(
workspaceId,
metadataVersion,
);

if (!objectMetadataMap) {
throw new NotFoundException(
`Object metadata map not found for workspace ${workspaceId} and metadata version ${metadataVersion}`,
);
}

const mappedResult = fieldMetadataItems.map((fieldMetadataItem) => {
const objectMetadata =
objectMetadataMap[fieldMetadataItem.objectMetadataId];
Expand Down
Loading

0 comments on commit c7efc99

Please sign in to comment.