mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-23 20:13:21 +03:00
[flexible-schema] Add createOne/createMany with upsert to graphql query runner (#7041)
## Context This PR introduces createOne/createMany through the new graphql query runner. Trying to use twentyOrm wrapper as much as possible, in this case here the args are already converted from "metadata-like" structure (including composite fields) as graphql input to typeorm / raw columns (I had to introduce a little fix there). Keep in mind that I'm not using the new graphql query runner parsing classes here, especially the selected-fields part, because typeorm already returns all the record columns in the InsertResult object (including default values such as id, createdAt, ...). That also means relation objects will be returned as NULL in the gql response but we don't handle nested creation for the moment so it should be fine. Note: also removing the feature flag from findOne/findMany
This commit is contained in:
parent
64756dc699
commit
37d85a716a
@ -8,10 +8,12 @@ import {
|
||||
import { IConnection } from 'src/engine/api/graphql/workspace-query-runner/interfaces/connection.interface';
|
||||
import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface';
|
||||
import {
|
||||
CreateManyResolverArgs,
|
||||
FindManyResolverArgs,
|
||||
FindOneResolverArgs,
|
||||
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
|
||||
import { GraphqlQueryCreateManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-many-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 { LogExecutionTime } from 'src/engine/decorators/observability/log-execution-time.decorator';
|
||||
@ -51,4 +53,15 @@ export class GraphqlQueryRunnerService {
|
||||
|
||||
return graphqlQueryFindManyResolverService.findMany(args, options);
|
||||
}
|
||||
|
||||
@LogExecutionTime()
|
||||
async createMany<ObjectRecord extends IRecord = IRecord>(
|
||||
args: CreateManyResolverArgs<Partial<ObjectRecord>>,
|
||||
options: WorkspaceQueryRunnerOptions,
|
||||
): Promise<ObjectRecord[] | undefined> {
|
||||
const graphqlQueryCreateManyResolverService =
|
||||
new GraphqlQueryCreateManyResolverService(this.twentyORMGlobalManager);
|
||||
|
||||
return graphqlQueryCreateManyResolverService.createMany(args, options);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
import { 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 { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
export class GraphqlQueryCreateManyResolverService {
|
||||
private twentyORMGlobalManager: TwentyORMGlobalManager;
|
||||
|
||||
constructor(twentyORMGlobalManager: TwentyORMGlobalManager) {
|
||||
this.twentyORMGlobalManager = twentyORMGlobalManager;
|
||||
}
|
||||
|
||||
async createMany<ObjectRecord extends IRecord = IRecord>(
|
||||
args: CreateManyResolverArgs<Partial<ObjectRecord>>,
|
||||
options: WorkspaceQueryRunnerOptions,
|
||||
): Promise<ObjectRecord[] | undefined> {
|
||||
const { authContext, objectMetadataItem } = 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']);
|
||||
|
||||
return insertResult.generatedMaps as ObjectRecord[];
|
||||
}
|
||||
}
|
@ -36,19 +36,18 @@ import {
|
||||
} 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 { parseResult } from 'src/engine/api/graphql/workspace-query-runner/utils/parse-result.util';
|
||||
import { withSoftDeleted } from 'src/engine/api/graphql/workspace-query-runner/utils/with-soft-deleted.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 { DuplicateService } from 'src/engine/core-modules/duplicate/duplicate.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';
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
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';
|
||||
@ -99,13 +98,6 @@ export class WorkspaceQueryRunnerService {
|
||||
options: WorkspaceQueryRunnerOptions,
|
||||
): Promise<IConnection<Record> | undefined> {
|
||||
const { authContext, objectMetadataItem } = options;
|
||||
const start = performance.now();
|
||||
|
||||
const isQueryRunnerTwentyORMEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsQueryRunnerTwentyORMEnabled,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
|
||||
const hookedArgs =
|
||||
await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
@ -121,34 +113,7 @@ export class WorkspaceQueryRunnerService {
|
||||
ResolverArgsType.FindMany,
|
||||
)) as FindManyResolverArgs<Filter, OrderBy>;
|
||||
|
||||
if (isQueryRunnerTwentyORMEnabled) {
|
||||
return this.graphqlQueryRunnerService.findMany(computedArgs, options);
|
||||
}
|
||||
|
||||
const query = await this.workspaceQueryBuilderFactory.findMany(
|
||||
computedArgs,
|
||||
{
|
||||
...options,
|
||||
withSoftDeleted: withSoftDeleted(args.filter),
|
||||
},
|
||||
);
|
||||
|
||||
const result = await this.execute(query, authContext.workspace.id);
|
||||
|
||||
const end = performance.now();
|
||||
|
||||
this.logger.log(
|
||||
`query time: ${end - start} ms on query ${
|
||||
options.objectMetadataItem.nameSingular
|
||||
}`,
|
||||
);
|
||||
|
||||
return this.parseResult<IConnection<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'',
|
||||
authContext.workspace.id,
|
||||
);
|
||||
return this.graphqlQueryRunnerService.findMany(computedArgs, options);
|
||||
}
|
||||
|
||||
async findOne<
|
||||
@ -166,12 +131,6 @@ export class WorkspaceQueryRunnerService {
|
||||
}
|
||||
const { authContext, objectMetadataItem } = options;
|
||||
|
||||
const isQueryRunnerTwentyORMEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsQueryRunnerTwentyORMEnabled,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
|
||||
const hookedArgs =
|
||||
await this.workspaceQueryHookService.executePreQueryHooks(
|
||||
authContext,
|
||||
@ -186,27 +145,7 @@ export class WorkspaceQueryRunnerService {
|
||||
ResolverArgsType.FindOne,
|
||||
)) as FindOneResolverArgs<Filter>;
|
||||
|
||||
if (isQueryRunnerTwentyORMEnabled) {
|
||||
return this.graphqlQueryRunnerService.findOne(computedArgs, options);
|
||||
}
|
||||
|
||||
const query = await this.workspaceQueryBuilderFactory.findOne(
|
||||
computedArgs,
|
||||
{
|
||||
...options,
|
||||
withSoftDeleted: withSoftDeleted(args.filter),
|
||||
},
|
||||
);
|
||||
|
||||
const result = await this.execute(query, authContext.workspace.id);
|
||||
const parsedResult = await this.parseResult<IConnection<Record>>(
|
||||
result,
|
||||
objectMetadataItem,
|
||||
'',
|
||||
authContext.workspace.id,
|
||||
);
|
||||
|
||||
return parsedResult?.edges?.[0]?.node;
|
||||
return this.graphqlQueryRunnerService.findOne(computedArgs, options);
|
||||
}
|
||||
|
||||
async findDuplicates<TRecord extends IRecord = IRecord>(
|
||||
@ -283,6 +222,12 @@ export class WorkspaceQueryRunnerService {
|
||||
): Promise<Record[] | undefined> {
|
||||
const { authContext, objectMetadataItem } = options;
|
||||
|
||||
const isQueryRunnerTwentyORMEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsQueryRunnerTwentyORMEnabled,
|
||||
authContext.workspace.id,
|
||||
);
|
||||
|
||||
assertMutationNotOnRemoteObject(objectMetadataItem);
|
||||
|
||||
if (args.upsert) {
|
||||
@ -309,6 +254,13 @@ 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,
|
||||
|
@ -428,9 +428,13 @@ export class WorkspaceRepository<
|
||||
|
||||
const formatedEntity = await this.formatData(entity);
|
||||
const result = await manager.insert(this.target, formatedEntity);
|
||||
const formattedResult = await this.formatResult(result);
|
||||
const formattedResult = await this.formatResult(result.generatedMaps);
|
||||
|
||||
return formattedResult;
|
||||
return {
|
||||
raw: result.raw,
|
||||
generatedMaps: formattedResult,
|
||||
identifiers: result.identifiers,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -470,11 +474,19 @@ export class WorkspaceRepository<
|
||||
|
||||
const formattedEntityOrEntities = await this.formatData(entityOrEntities);
|
||||
|
||||
return manager.upsert(
|
||||
const result = await manager.upsert(
|
||||
this.target,
|
||||
formattedEntityOrEntities,
|
||||
conflictPathsOrOptions,
|
||||
);
|
||||
|
||||
const formattedResult = await this.formatResult(result.generatedMaps);
|
||||
|
||||
return {
|
||||
raw: result.raw,
|
||||
generatedMaps: formattedResult,
|
||||
identifiers: result.identifiers,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user