Fix graphql query createMany resolver with nested relations (#7061)
Looks like insert() does not return foreign keys. We could eventually
call findMany after but it seems that's what save() is doing so I'm
replacing insert with save.
```typescript
/**
* Flag to determine whether the entity that is being persisted
* should be reloaded during the persistence operation.
*
* It will work only on databases which does not support RETURNING / OUTPUT statement.
* Enabled by default.
*/
reload?: boolean;
```
Note: save() also does an upsert by default with no way to configure
that so if we want to keep that behaviour we will need to add a check
before
```typescript
if (args.upsert) {
const existingRecords = await repository.findBy({
id: Any(args.data.map((record) => record.id)),
});
...
```
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,8 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { GraphqlQueryRunnerService } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-runner.service';
|
||||
import { WorkspaceQueryHookModule } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.module';
|
||||
import { WorkspaceQueryRunnerModule } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module';
|
||||
|
||||
@Module({
|
||||
imports: [WorkspaceQueryHookModule, WorkspaceQueryRunnerModule],
|
||||
providers: [GraphqlQueryRunnerService],
|
||||
exports: [GraphqlQueryRunnerService],
|
||||
})
|
||||
|
||||
@ -9,22 +9,49 @@ import { IConnection } from 'src/engine/api/graphql/workspace-query-runner/inter
|
||||
import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface';
|
||||
import {
|
||||
CreateManyResolverArgs,
|
||||
CreateOneResolverArgs,
|
||||
DestroyOneResolverArgs,
|
||||
FindManyResolverArgs,
|
||||
FindOneResolverArgs,
|
||||
ResolverArgsType,
|
||||
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { GraphqlQueryCreateManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-many-resolver.service';
|
||||
import { GraphqlQueryDestroyOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-one-resolver.service';
|
||||
import { GraphqlQueryFindManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-many-resolver.service';
|
||||
import { GraphqlQueryFindOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-one-resolver.service';
|
||||
import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory';
|
||||
import {
|
||||
CallWebhookJobsJob,
|
||||
CallWebhookJobsJobData,
|
||||
CallWebhookJobsJobOperation,
|
||||
} from 'src/engine/api/graphql/workspace-query-runner/jobs/call-webhook-jobs.job';
|
||||
import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util';
|
||||
import { WorkspaceQueryHookService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.service';
|
||||
import {
|
||||
WorkspaceQueryRunnerException,
|
||||
WorkspaceQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
|
||||
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||
import { LogExecutionTime } from 'src/engine/decorators/observability/log-execution-time.decorator';
|
||||
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
|
||||
@Injectable()
|
||||
export class GraphqlQueryRunnerService {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly workspaceQueryHookService: WorkspaceQueryHookService,
|
||||
private readonly queryRunnerArgsFactory: QueryRunnerArgsFactory,
|
||||
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
||||
@InjectMessageQueue(MessageQueue.webhookQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
) {}
|
||||
|
||||
@LogExecutionTime()
|
||||
@ -38,7 +65,30 @@ export class GraphqlQueryRunnerService {
|
||||
const graphqlQueryFindOneResolverService =
|
||||
new GraphqlQueryFindOneResolverService(this.twentyORMGlobalManager);
|
||||
|
||||
return graphqlQueryFindOneResolverService.findOne(args, options);
|
||||
const { authContext, objectMetadataItem } = options;
|
||||
|
||||
if (!args.filter || Object.keys(args.filter).length === 0) {
|
||||
throw new WorkspaceQueryRunnerException(
|
||||
'Missing filter argument',
|
||||
WorkspaceQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
const hookedArgs =
|
||||
await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItem.nameSingular,
|
||||
'findOne',
|
||||
args,
|
||||
);
|
||||
|
||||
const computedArgs = (await this.queryRunnerArgsFactory.create(
|
||||
hookedArgs,
|
||||
options,
|
||||
ResolverArgsType.FindOne,
|
||||
)) as FindOneResolverArgs<Filter>;
|
||||
|
||||
return graphqlQueryFindOneResolverService.findOne(computedArgs, options);
|
||||
}
|
||||
|
||||
@LogExecutionTime()
|
||||
@ -53,7 +103,78 @@ export class GraphqlQueryRunnerService {
|
||||
const graphqlQueryFindManyResolverService =
|
||||
new GraphqlQueryFindManyResolverService(this.twentyORMGlobalManager);
|
||||
|
||||
return graphqlQueryFindManyResolverService.findMany(args, options);
|
||||
const { authContext, objectMetadataItem } = options;
|
||||
|
||||
const hookedArgs =
|
||||
await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItem.nameSingular,
|
||||
'findMany',
|
||||
args,
|
||||
);
|
||||
|
||||
const computedArgs = (await this.queryRunnerArgsFactory.create(
|
||||
hookedArgs,
|
||||
options,
|
||||
ResolverArgsType.FindMany,
|
||||
)) as FindManyResolverArgs<Filter, OrderBy>;
|
||||
|
||||
return graphqlQueryFindManyResolverService.findMany(computedArgs, options);
|
||||
}
|
||||
|
||||
@LogExecutionTime()
|
||||
async createOne<ObjectRecord extends IRecord = IRecord>(
|
||||
args: CreateOneResolverArgs<Partial<ObjectRecord>>,
|
||||
options: WorkspaceQueryRunnerOptions,
|
||||
): Promise<ObjectRecord | undefined> {
|
||||
const graphqlQueryCreateManyResolverService =
|
||||
new GraphqlQueryCreateManyResolverService(this.twentyORMGlobalManager);
|
||||
|
||||
const { authContext, objectMetadataItem } = options;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||
|
||||
if (args.data.id) {
|
||||
assertIsValidUuid(args.data.id);
|
||||
}
|
||||
|
||||
const createManyArgs = {
|
||||
data: [args.data],
|
||||
upsert: args.upsert,
|
||||
} as CreateManyResolverArgs<ObjectRecord>;
|
||||
|
||||
const hookedArgs =
|
||||
await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItem.nameSingular,
|
||||
'createMany',
|
||||
createManyArgs,
|
||||
);
|
||||
|
||||
const computedArgs = (await this.queryRunnerArgsFactory.create(
|
||||
hookedArgs,
|
||||
options,
|
||||
ResolverArgsType.CreateMany,
|
||||
)) as CreateManyResolverArgs<ObjectRecord>;
|
||||
|
||||
const results = (await graphqlQueryCreateManyResolverService.createMany(
|
||||
computedArgs,
|
||||
options,
|
||||
)) as ObjectRecord[];
|
||||
|
||||
await this.triggerWebhooks<ObjectRecord>(
|
||||
results,
|
||||
CallWebhookJobsJobOperation.create,
|
||||
options,
|
||||
);
|
||||
|
||||
this.emitCreateEvents<ObjectRecord>(
|
||||
results,
|
||||
authContext,
|
||||
objectMetadataItem,
|
||||
);
|
||||
|
||||
return results?.[0] as ObjectRecord;
|
||||
}
|
||||
|
||||
@LogExecutionTime()
|
||||
@ -64,7 +185,99 @@ export class GraphqlQueryRunnerService {
|
||||
const graphqlQueryCreateManyResolverService =
|
||||
new GraphqlQueryCreateManyResolverService(this.twentyORMGlobalManager);
|
||||
|
||||
return graphqlQueryCreateManyResolverService.createMany(args, options);
|
||||
const { authContext, objectMetadataItem } = options;
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||
|
||||
args.data.forEach((record) => {
|
||||
if (record?.id) {
|
||||
assertIsValidUuid(record.id);
|
||||
}
|
||||
});
|
||||
|
||||
const hookedArgs =
|
||||
await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItem.nameSingular,
|
||||
'createMany',
|
||||
args,
|
||||
);
|
||||
|
||||
const computedArgs = (await this.queryRunnerArgsFactory.create(
|
||||
hookedArgs,
|
||||
options,
|
||||
ResolverArgsType.CreateMany,
|
||||
)) as CreateManyResolverArgs<ObjectRecord>;
|
||||
|
||||
const results = (await graphqlQueryCreateManyResolverService.createMany(
|
||||
computedArgs,
|
||||
options,
|
||||
)) as ObjectRecord[];
|
||||
|
||||
await this.workspaceQueryHookService.executePostQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItem.nameSingular,
|
||||
'createMany',
|
||||
results,
|
||||
);
|
||||
|
||||
await this.triggerWebhooks<ObjectRecord>(
|
||||
results,
|
||||
CallWebhookJobsJobOperation.create,
|
||||
options,
|
||||
);
|
||||
|
||||
this.emitCreateEvents<ObjectRecord>(
|
||||
results,
|
||||
authContext,
|
||||
objectMetadataItem,
|
||||
);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private emitCreateEvents<BaseRecord extends IRecord = IRecord>(
|
||||
records: BaseRecord[],
|
||||
authContext: AuthContext,
|
||||
objectMetadataItem: ObjectMetadataInterface,
|
||||
) {
|
||||
this.workspaceEventEmitter.emit(
|
||||
`${objectMetadataItem.nameSingular}.created`,
|
||||
records.map(
|
||||
(record) =>
|
||||
({
|
||||
userId: authContext.user?.id,
|
||||
recordId: record.id,
|
||||
objectMetadata: objectMetadataItem,
|
||||
properties: {
|
||||
after: record,
|
||||
},
|
||||
}) satisfies ObjectRecordCreateEvent<any>,
|
||||
),
|
||||
authContext.workspace.id,
|
||||
);
|
||||
}
|
||||
|
||||
private async triggerWebhooks<Record>(
|
||||
jobsData: Record[] | undefined,
|
||||
operation: CallWebhookJobsJobOperation,
|
||||
options: WorkspaceQueryRunnerOptions,
|
||||
) {
|
||||
if (!Array.isArray(jobsData)) {
|
||||
return;
|
||||
}
|
||||
jobsData.forEach((jobData) => {
|
||||
this.messageQueueService.add<CallWebhookJobsJobData>(
|
||||
CallWebhookJobsJob.name,
|
||||
{
|
||||
record: jobData,
|
||||
workspaceId: options.authContext.workspace.id,
|
||||
operation,
|
||||
objectMetadataItem: options.objectMetadataItem,
|
||||
},
|
||||
{ retryLimit: 3 },
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@LogExecutionTime()
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
import { InsertResult } from 'typeorm';
|
||||
import graphqlFields from 'graphql-fields';
|
||||
import { In, InsertResult } from 'typeorm';
|
||||
|
||||
import { Record as IRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
|
||||
import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface';
|
||||
import { CreateManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
|
||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { ObjectRecordsToGraphqlConnectionMapper } from 'src/engine/api/graphql/graphql-query-runner/orm-mappers/object-records-to-graphql-connection.mapper';
|
||||
import { getObjectMetadataOrThrow } from 'src/engine/api/graphql/graphql-query-runner/utils/get-object-metadata-or-throw.util';
|
||||
import { generateObjectMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
export class GraphqlQueryCreateManyResolverService {
|
||||
@ -17,17 +22,58 @@ export class GraphqlQueryCreateManyResolverService {
|
||||
args: CreateManyResolverArgs<Partial<ObjectRecord>>,
|
||||
options: WorkspaceQueryRunnerOptions,
|
||||
): Promise<ObjectRecord[] | undefined> {
|
||||
const { authContext, objectMetadataItem } = options;
|
||||
const { authContext, objectMetadataItem, objectMetadataCollection, info } =
|
||||
options;
|
||||
const repository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
authContext.workspace.id,
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
|
||||
const insertResult: InsertResult = !args.upsert
|
||||
? await repository.insert(args.data)
|
||||
: await repository.upsert(args.data, ['id']);
|
||||
const objectMetadataMap = generateObjectMetadataMap(
|
||||
objectMetadataCollection,
|
||||
);
|
||||
const objectMetadata = getObjectMetadataOrThrow(
|
||||
objectMetadataMap,
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
const graphqlQueryParser = new GraphqlQueryParser(
|
||||
objectMetadata.fields,
|
||||
objectMetadataMap,
|
||||
);
|
||||
|
||||
return insertResult.generatedMaps as ObjectRecord[];
|
||||
const selectedFields = graphqlFields(info);
|
||||
|
||||
const { select, relations } = graphqlQueryParser.parseSelectedFields(
|
||||
objectMetadataItem,
|
||||
selectedFields,
|
||||
);
|
||||
|
||||
const objectRecords: InsertResult = !args.upsert
|
||||
? await repository.insert(args.data)
|
||||
: await repository.upsert(args.data, {
|
||||
conflictPaths: ['id'],
|
||||
skipUpdateIfNoValuesChanged: true,
|
||||
});
|
||||
|
||||
const upsertedRecords = await repository.find({
|
||||
where: {
|
||||
id: In(objectRecords.generatedMaps.map((record) => record.id)),
|
||||
},
|
||||
select,
|
||||
relations,
|
||||
});
|
||||
|
||||
const typeORMObjectRecordsParser =
|
||||
new ObjectRecordsToGraphqlConnectionMapper(objectMetadataMap);
|
||||
|
||||
return upsertedRecords.map((record: ObjectRecord) =>
|
||||
typeORMObjectRecordsParser.processRecord(
|
||||
record,
|
||||
objectMetadataItem.nameSingular,
|
||||
1,
|
||||
1,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,9 +61,11 @@ export class GraphqlQueryFindManyResolverService {
|
||||
objectMetadataMap,
|
||||
);
|
||||
|
||||
const selectedFields = graphqlFields(info);
|
||||
|
||||
const { select, relations } = graphqlQueryParser.parseSelectedFields(
|
||||
objectMetadataItem,
|
||||
graphqlFields(info),
|
||||
selectedFields,
|
||||
);
|
||||
const isForwardPagination = !isDefined(args.before);
|
||||
const order = graphqlQueryParser.parseOrder(
|
||||
@ -84,7 +86,10 @@ export class GraphqlQueryFindManyResolverService {
|
||||
relations,
|
||||
take: limit + 1,
|
||||
};
|
||||
const totalCount = await repository.count({ where });
|
||||
|
||||
const totalCount = isDefined(selectedFields.totalCount)
|
||||
? await repository.count({ where })
|
||||
: 0;
|
||||
|
||||
if (cursor) {
|
||||
applyRangeFilter(where, cursor, isForwardPagination);
|
||||
|
||||
@ -51,9 +51,11 @@ export class GraphqlQueryFindOneResolverService {
|
||||
objectMetadataMap,
|
||||
);
|
||||
|
||||
const selectedFields = graphqlFields(info);
|
||||
|
||||
const { select, relations } = graphqlQueryParser.parseSelectedFields(
|
||||
objectMetadataItem,
|
||||
graphqlFields(info),
|
||||
selectedFields,
|
||||
);
|
||||
const where = graphqlQueryParser.parseFilter(args.filter ?? ({} as Filter));
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { GraphqlQueryRunnerModule } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-runner.module';
|
||||
import { WorkspaceQueryBuilderModule } from 'src/engine/api/graphql/workspace-query-builder/workspace-query-builder.module';
|
||||
import { RecordPositionBackfillCommand } from 'src/engine/api/graphql/workspace-query-runner/commands/0-20-record-position-backfill.command';
|
||||
import { workspaceQueryRunnerFactories } from 'src/engine/api/graphql/workspace-query-runner/factories';
|
||||
@ -32,7 +31,6 @@ import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listen
|
||||
AnalyticsModule,
|
||||
DuplicateModule,
|
||||
FileModule,
|
||||
GraphqlQueryRunnerModule,
|
||||
FeatureFlagModule,
|
||||
],
|
||||
providers: [
|
||||
@ -42,6 +40,6 @@ import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listen
|
||||
TelemetryListener,
|
||||
RecordPositionBackfillCommand,
|
||||
],
|
||||
exports: [WorkspaceQueryRunnerService],
|
||||
exports: [WorkspaceQueryRunnerService, ...workspaceQueryRunnerFactories],
|
||||
})
|
||||
export class WorkspaceQueryRunnerModule {}
|
||||
|
||||
@ -6,7 +6,6 @@ import { DataSource, In } from 'typeorm';
|
||||
import {
|
||||
Record as IRecord,
|
||||
RecordFilter,
|
||||
RecordOrderBy,
|
||||
} 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 {
|
||||
@ -16,8 +15,6 @@ import {
|
||||
DeleteOneResolverArgs,
|
||||
DestroyManyResolverArgs,
|
||||
FindDuplicatesResolverArgs,
|
||||
FindManyResolverArgs,
|
||||
FindOneResolverArgs,
|
||||
ResolverArgsType,
|
||||
RestoreManyResolverArgs,
|
||||
UpdateManyResolverArgs,
|
||||
@ -25,7 +22,6 @@ import {
|
||||
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { GraphqlQueryRunnerService } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-runner.service';
|
||||
import { WorkspaceQueryBuilderFactory } from 'src/engine/api/graphql/workspace-query-builder/workspace-query-builder.factory';
|
||||
import { QueryResultGettersFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory';
|
||||
import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory';
|
||||
@ -46,8 +42,6 @@ import { EnvironmentService } from 'src/engine/core-modules/environment/environm
|
||||
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
|
||||
import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event';
|
||||
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||
@ -85,76 +79,8 @@ export class WorkspaceQueryRunnerService {
|
||||
private readonly workspaceQueryHookService: WorkspaceQueryHookService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly duplicateService: DuplicateService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly graphqlQueryRunnerService: GraphqlQueryRunnerService,
|
||||
) {}
|
||||
|
||||
async findMany<
|
||||
Record extends IRecord = IRecord,
|
||||
Filter extends RecordFilter = RecordFilter,
|
||||
OrderBy extends RecordOrderBy = RecordOrderBy,
|
||||
>(
|
||||
args: FindManyResolverArgs<Filter, OrderBy>,
|
||||
options: WorkspaceQueryRunnerOptions,
|
||||
): Promise<IConnection<Record> | undefined> {
|
||||
const { authContext, objectMetadataItem } = options;
|
||||
|
||||
console.log(
|
||||
`running findMany for ${objectMetadataItem.nameSingular} on workspace ${authContext.workspace.id}`,
|
||||
);
|
||||
const hookedArgs =
|
||||
await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItem.nameSingular,
|
||||
'findMany',
|
||||
args,
|
||||
);
|
||||
|
||||
const computedArgs = (await this.queryRunnerArgsFactory.create(
|
||||
hookedArgs,
|
||||
options,
|
||||
ResolverArgsType.FindMany,
|
||||
)) as FindManyResolverArgs<Filter, OrderBy>;
|
||||
|
||||
return this.graphqlQueryRunnerService.findMany(computedArgs, options);
|
||||
}
|
||||
|
||||
async findOne<
|
||||
Record extends IRecord = IRecord,
|
||||
Filter extends RecordFilter = RecordFilter,
|
||||
>(
|
||||
args: FindOneResolverArgs<Filter>,
|
||||
options: WorkspaceQueryRunnerOptions,
|
||||
): Promise<Record | undefined> {
|
||||
if (!args.filter || Object.keys(args.filter).length === 0) {
|
||||
throw new WorkspaceQueryRunnerException(
|
||||
'Missing filter argument',
|
||||
WorkspaceQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
|
||||
);
|
||||
}
|
||||
const { authContext, objectMetadataItem } = options;
|
||||
|
||||
console.log(
|
||||
`running findOne for ${objectMetadataItem.nameSingular} on workspace ${authContext.workspace.id}`,
|
||||
);
|
||||
|
||||
const hookedArgs =
|
||||
await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
objectMetadataItem.nameSingular,
|
||||
'findOne',
|
||||
args,
|
||||
);
|
||||
|
||||
const computedArgs = (await this.queryRunnerArgsFactory.create(
|
||||
hookedArgs,
|
||||
options,
|
||||
ResolverArgsType.FindOne,
|
||||
)) as FindOneResolverArgs<Filter>;
|
||||
|
||||
return this.graphqlQueryRunnerService.findOne(computedArgs, options);
|
||||
}
|
||||
|
||||
async findDuplicates<TRecord extends IRecord = IRecord>(
|
||||
args: FindDuplicatesResolverArgs<Partial<TRecord>>,
|
||||
options: WorkspaceQueryRunnerOptions,
|
||||
@ -233,16 +159,6 @@ export class WorkspaceQueryRunnerService {
|
||||
): Promise<Record[] | undefined> {
|
||||
const { authContext, objectMetadataItem } = options;
|
||||
|
||||
console.log(
|
||||
`running createMany for ${objectMetadataItem.nameSingular} on workspace ${authContext.workspace.id}`,
|
||||
);
|
||||
|
||||
const isQueryRunnerTwentyORMEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsQueryRunnerTwentyORMEnabled,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||
|
||||
if (args.upsert) {
|
||||
@ -269,13 +185,6 @@ export class WorkspaceQueryRunnerService {
|
||||
ResolverArgsType.CreateMany,
|
||||
)) as CreateManyResolverArgs<Record>;
|
||||
|
||||
if (isQueryRunnerTwentyORMEnabled) {
|
||||
return (await this.graphqlQueryRunnerService.createMany(
|
||||
computedArgs,
|
||||
options,
|
||||
)) as Record[];
|
||||
}
|
||||
|
||||
const query = await this.workspaceQueryBuilderFactory.createMany(
|
||||
computedArgs,
|
||||
options,
|
||||
|
||||
@ -7,8 +7,11 @@ import {
|
||||
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { GraphqlQueryRunnerService } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-runner.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
import { WorkspaceQueryRunnerService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateManyResolverFactory
|
||||
@ -18,6 +21,8 @@ export class CreateManyResolverFactory
|
||||
|
||||
constructor(
|
||||
private readonly workspaceQueryRunnerService: WorkspaceQueryRunnerService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly graphqlQueryRunnerService: GraphqlQueryRunnerService,
|
||||
) {}
|
||||
|
||||
create(
|
||||
@ -25,15 +30,27 @@ export class CreateManyResolverFactory
|
||||
): Resolver<CreateManyResolverArgs> {
|
||||
const internalContext = context;
|
||||
|
||||
return async (_source, args, context, info) => {
|
||||
return async (_source, args, _context, info) => {
|
||||
try {
|
||||
return await this.workspaceQueryRunnerService.createMany(args, {
|
||||
const options = {
|
||||
authContext: internalContext.authContext,
|
||||
objectMetadataItem: internalContext.objectMetadataItem,
|
||||
info,
|
||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||
objectMetadataCollection: internalContext.objectMetadataCollection,
|
||||
});
|
||||
};
|
||||
|
||||
const isQueryRunnerTwentyORMEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsQueryRunnerTwentyORMEnabled,
|
||||
internalContext.authContext.workspace.id,
|
||||
);
|
||||
|
||||
if (isQueryRunnerTwentyORMEnabled) {
|
||||
return await this.graphqlQueryRunnerService.createMany(args, options);
|
||||
}
|
||||
|
||||
return await this.workspaceQueryRunnerService.createMany(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
|
||||
@ -7,8 +7,11 @@ import {
|
||||
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { GraphqlQueryRunnerService } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-runner.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
import { WorkspaceQueryRunnerService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateOneResolverFactory
|
||||
@ -18,6 +21,8 @@ export class CreateOneResolverFactory
|
||||
|
||||
constructor(
|
||||
private readonly workspaceQueryRunnerService: WorkspaceQueryRunnerService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly graphqlQueryRunnerService: GraphqlQueryRunnerService,
|
||||
) {}
|
||||
|
||||
create(
|
||||
@ -25,8 +30,26 @@ export class CreateOneResolverFactory
|
||||
): Resolver<CreateOneResolverArgs> {
|
||||
const internalContext = context;
|
||||
|
||||
return async (_source, args, context, info) => {
|
||||
return async (_source, args, _context, info) => {
|
||||
try {
|
||||
const options = {
|
||||
authContext: internalContext.authContext,
|
||||
objectMetadataItem: internalContext.objectMetadataItem,
|
||||
info,
|
||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||
objectMetadataCollection: internalContext.objectMetadataCollection,
|
||||
};
|
||||
|
||||
const isQueryRunnerTwentyORMEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsQueryRunnerTwentyORMEnabled,
|
||||
internalContext.authContext.workspace.id,
|
||||
);
|
||||
|
||||
if (isQueryRunnerTwentyORMEnabled) {
|
||||
return await this.graphqlQueryRunnerService.createOne(args, options);
|
||||
}
|
||||
|
||||
return await this.workspaceQueryRunnerService.createOne(args, {
|
||||
authContext: internalContext.authContext,
|
||||
objectMetadataItem: internalContext.objectMetadataItem,
|
||||
|
||||
@ -7,8 +7,8 @@ import {
|
||||
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { GraphqlQueryRunnerService } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-runner.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
import { WorkspaceQueryRunnerService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service';
|
||||
|
||||
@Injectable()
|
||||
export class FindManyResolverFactory
|
||||
@ -17,7 +17,7 @@ export class FindManyResolverFactory
|
||||
public static methodName = 'findMany' as const;
|
||||
|
||||
constructor(
|
||||
private readonly workspaceQueryRunnerService: WorkspaceQueryRunnerService,
|
||||
private readonly graphqlQueryRunnerService: GraphqlQueryRunnerService,
|
||||
) {}
|
||||
|
||||
create(
|
||||
@ -25,15 +25,17 @@ export class FindManyResolverFactory
|
||||
): Resolver<FindManyResolverArgs> {
|
||||
const internalContext = context;
|
||||
|
||||
return async (_source, args, context, info) => {
|
||||
return async (_source, args, _context, info) => {
|
||||
try {
|
||||
return await this.workspaceQueryRunnerService.findMany(args, {
|
||||
const options = {
|
||||
authContext: internalContext.authContext,
|
||||
objectMetadataItem: internalContext.objectMetadataItem,
|
||||
info,
|
||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||
objectMetadataCollection: internalContext.objectMetadataCollection,
|
||||
});
|
||||
};
|
||||
|
||||
return await this.graphqlQueryRunnerService.findMany(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
|
||||
@ -7,8 +7,8 @@ import {
|
||||
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
|
||||
|
||||
import { GraphqlQueryRunnerService } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-runner.service';
|
||||
import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util';
|
||||
import { WorkspaceQueryRunnerService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service';
|
||||
|
||||
@Injectable()
|
||||
export class FindOneResolverFactory
|
||||
@ -17,7 +17,7 @@ export class FindOneResolverFactory
|
||||
public static methodName = 'findOne' as const;
|
||||
|
||||
constructor(
|
||||
private readonly workspaceQueryRunnerService: WorkspaceQueryRunnerService,
|
||||
private readonly graphqlQueryRunnerService: GraphqlQueryRunnerService,
|
||||
) {}
|
||||
|
||||
create(
|
||||
@ -25,15 +25,17 @@ export class FindOneResolverFactory
|
||||
): Resolver<FindOneResolverArgs> {
|
||||
const internalContext = context;
|
||||
|
||||
return async (_source, args, context, info) => {
|
||||
return async (_source, args, _context, info) => {
|
||||
try {
|
||||
return await this.workspaceQueryRunnerService.findOne(args, {
|
||||
const options = {
|
||||
authContext: internalContext.authContext,
|
||||
objectMetadataItem: internalContext.objectMetadataItem,
|
||||
info,
|
||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||
objectMetadataCollection: internalContext.objectMetadataCollection,
|
||||
});
|
||||
};
|
||||
|
||||
return await this.graphqlQueryRunnerService.findOne(args, options);
|
||||
} catch (error) {
|
||||
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
|
||||
@ -2,13 +2,18 @@ import { Module } from '@nestjs/common';
|
||||
|
||||
import { GraphqlQueryRunnerModule } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-runner.module';
|
||||
import { WorkspaceQueryRunnerModule } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
|
||||
import { WorkspaceResolverFactory } from './workspace-resolver.factory';
|
||||
|
||||
import { workspaceResolverBuilderFactories } from './factories/factories';
|
||||
|
||||
@Module({
|
||||
imports: [WorkspaceQueryRunnerModule, GraphqlQueryRunnerModule],
|
||||
imports: [
|
||||
WorkspaceQueryRunnerModule,
|
||||
GraphqlQueryRunnerModule,
|
||||
FeatureFlagModule,
|
||||
],
|
||||
providers: [...workspaceResolverBuilderFactories, WorkspaceResolverFactory],
|
||||
exports: [WorkspaceResolverFactory],
|
||||
})
|
||||
|
||||
@ -79,6 +79,7 @@ export class EntitySchemaColumnFactory {
|
||||
nullable: fieldMetadata.isNullable,
|
||||
createDate: key === 'createdAt',
|
||||
updateDate: key === 'updatedAt',
|
||||
deleteDate: key === 'deletedAt',
|
||||
array: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
default: defaultValue,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user