[POC] add graphql query runner (#6747)
## Context The goal is to replace pg_graphql with our own ORM wrapper (TwentyORM). This PR tries to add some parsing logic to convert graphql requests to send to the ORM to replace pg_graphql implementation. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -57,7 +57,7 @@ export class EntitySchemaRelationFactory {
|
||||
target: relationDetails.target,
|
||||
inverseSide: relationDetails.inverseSide,
|
||||
joinColumn: relationDetails.joinColumn,
|
||||
};
|
||||
} satisfies EntitySchemaRelationOptions;
|
||||
}
|
||||
|
||||
return entitySchemaRelationMap;
|
||||
|
||||
@ -9,11 +9,13 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.service';
|
||||
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||
import { EntitySchemaFactory } from 'src/engine/twenty-orm/factories/entity-schema.factory';
|
||||
import { workspaceDataSourceCacheInstance } from 'src/engine/twenty-orm/twenty-orm-core.module';
|
||||
import { CacheManager } from 'src/engine/twenty-orm/storage/cache-manager.storage';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceDatasourceFactory {
|
||||
private cacheManager = new CacheManager<WorkspaceDataSource>();
|
||||
|
||||
constructor(
|
||||
private readonly dataSourceService: DataSourceService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
@ -48,7 +50,7 @@ export class WorkspaceDatasourceFactory {
|
||||
);
|
||||
}
|
||||
|
||||
const workspaceDataSource = await workspaceDataSourceCacheInstance.execute(
|
||||
const workspaceDataSource = await this.cacheManager.execute(
|
||||
`${workspaceId}-${latestWorkspaceMetadataVersion}`,
|
||||
async () => {
|
||||
let cachedObjectMetadataCollection =
|
||||
|
||||
@ -1,81 +0,0 @@
|
||||
import {
|
||||
DynamicModule,
|
||||
Global,
|
||||
Logger,
|
||||
Module,
|
||||
OnApplicationShutdown,
|
||||
} from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import {
|
||||
TwentyORMModuleAsyncOptions,
|
||||
TwentyORMOptions,
|
||||
} from 'src/engine/twenty-orm/interfaces/twenty-orm-options.interface';
|
||||
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||
import { entitySchemaFactories } from 'src/engine/twenty-orm/factories';
|
||||
import { EntitySchemaFactory } from 'src/engine/twenty-orm/factories/entity-schema.factory';
|
||||
import { CacheManager } from 'src/engine/twenty-orm/storage/cache-manager.storage';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { ConfigurableModuleClass } from 'src/engine/twenty-orm/twenty-orm.module-definition';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
|
||||
export const workspaceDataSourceCacheInstance =
|
||||
new CacheManager<WorkspaceDataSource>();
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'),
|
||||
DataSourceModule,
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspaceMetadataVersionModule,
|
||||
],
|
||||
providers: [
|
||||
...entitySchemaFactories,
|
||||
TwentyORMManager,
|
||||
TwentyORMGlobalManager,
|
||||
],
|
||||
exports: [EntitySchemaFactory, TwentyORMManager, TwentyORMGlobalManager],
|
||||
})
|
||||
export class TwentyORMCoreModule
|
||||
extends ConfigurableModuleClass
|
||||
implements OnApplicationShutdown
|
||||
{
|
||||
private static readonly logger = new Logger(TwentyORMCoreModule.name);
|
||||
|
||||
static register(options: TwentyORMOptions): DynamicModule {
|
||||
const dynamicModule = super.register(options);
|
||||
|
||||
return {
|
||||
...dynamicModule,
|
||||
providers: [...(dynamicModule.providers ?? [])],
|
||||
exports: [...(dynamicModule.exports ?? [])],
|
||||
};
|
||||
}
|
||||
|
||||
static registerAsync(
|
||||
asyncOptions: TwentyORMModuleAsyncOptions,
|
||||
): DynamicModule {
|
||||
const dynamicModule = super.registerAsync(asyncOptions);
|
||||
|
||||
return {
|
||||
...dynamicModule,
|
||||
providers: [...(dynamicModule.providers ?? [])],
|
||||
exports: [...(dynamicModule.exports ?? [])],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys all data sources on application shutdown
|
||||
*/
|
||||
async onApplicationShutdown() {
|
||||
workspaceDataSourceCacheInstance.clear((dataSource) =>
|
||||
dataSource.destroy(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,40 +1,28 @@
|
||||
import { DynamicModule, Global, Module } from '@nestjs/common';
|
||||
import { EntityClassOrSchema } from '@nestjs/typeorm/dist/interfaces/entity-class-or-schema.type';
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import {
|
||||
TwentyORMModuleAsyncOptions,
|
||||
TwentyORMOptions,
|
||||
} from 'src/engine/twenty-orm/interfaces/twenty-orm-options.interface';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { entitySchemaFactories } from 'src/engine/twenty-orm/factories';
|
||||
import { EntitySchemaFactory } from 'src/engine/twenty-orm/factories/entity-schema.factory';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
|
||||
import { TwentyORMCoreModule } from 'src/engine/twenty-orm/twenty-orm-core.module';
|
||||
|
||||
// Todo: remove this file
|
||||
@Global()
|
||||
@Module({})
|
||||
export class TwentyORMModule {
|
||||
static register(options: TwentyORMOptions): DynamicModule {
|
||||
return {
|
||||
module: TwentyORMModule,
|
||||
imports: [TwentyORMCoreModule.register(options)],
|
||||
};
|
||||
}
|
||||
|
||||
static forFeature(_objects: EntityClassOrSchema[] = []): DynamicModule {
|
||||
const providers = [];
|
||||
|
||||
return {
|
||||
module: TwentyORMModule,
|
||||
providers: providers,
|
||||
exports: providers,
|
||||
};
|
||||
}
|
||||
|
||||
static registerAsync(
|
||||
asyncOptions: TwentyORMModuleAsyncOptions,
|
||||
): DynamicModule {
|
||||
return {
|
||||
module: TwentyORMModule,
|
||||
imports: [TwentyORMCoreModule.registerAsync(asyncOptions)],
|
||||
};
|
||||
}
|
||||
}
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'),
|
||||
DataSourceModule,
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspaceMetadataVersionModule,
|
||||
],
|
||||
providers: [
|
||||
...entitySchemaFactories,
|
||||
TwentyORMManager,
|
||||
TwentyORMGlobalManager,
|
||||
],
|
||||
exports: [EntitySchemaFactory, TwentyORMManager, TwentyORMGlobalManager],
|
||||
})
|
||||
export class TwentyORMModule {}
|
||||
|
||||
@ -21,7 +21,10 @@ export async function determineRelationDetails(
|
||||
let fromObjectMetadata: ObjectMetadataEntity | undefined =
|
||||
fieldMetadata.object;
|
||||
let toObjectMetadata: ObjectMetadataEntity | undefined =
|
||||
relationMetadata.toObjectMetadata;
|
||||
objectMetadataCollection.find(
|
||||
(objectMetadata) =>
|
||||
objectMetadata.id === relationMetadata.toObjectMetadataId,
|
||||
);
|
||||
|
||||
// RelationMetadata always store the relation from the perspective of the `from` object, MANY_TO_ONE relations are not stored yet
|
||||
if (relationType === 'many-to-one') {
|
||||
@ -37,6 +40,16 @@ export async function determineRelationDetails(
|
||||
throw new Error('Object metadata not found');
|
||||
}
|
||||
|
||||
const toFieldMetadata = toObjectMetadata.fields.find((field) =>
|
||||
relationType === 'many-to-one'
|
||||
? field.id === relationMetadata.fromFieldMetadataId
|
||||
: field.id === relationMetadata.toFieldMetadataId,
|
||||
);
|
||||
|
||||
if (!toFieldMetadata) {
|
||||
throw new Error('To field metadata not found');
|
||||
}
|
||||
|
||||
// TODO: Support many to many relations
|
||||
if (relationType === 'many-to-many') {
|
||||
throw new Error('Many to many relations are not supported yet');
|
||||
@ -45,7 +58,7 @@ export async function determineRelationDetails(
|
||||
return {
|
||||
relationType,
|
||||
target: toObjectMetadata.nameSingular,
|
||||
inverseSide: fromObjectMetadata.nameSingular,
|
||||
inverseSide: toFieldMetadata.name,
|
||||
joinColumn:
|
||||
// TODO: This will work for now but we need to handle this better in the future for custom names on the join column
|
||||
relationType === 'many-to-one' ||
|
||||
|
||||
Reference in New Issue
Block a user