Fix record creation (#8664)

## Context
Now that each operation has its own resolver, we need to make sure they
all map to query arg getters. CreateOne was not properly mapped to the
position getter which made record creation fail because "position:
first" was not properly converted to a float.
Also fixing queries with custom object where we were wrongly using the
table name instead of entity name
This commit is contained in:
Weiko
2024-11-21 22:53:22 +01:00
committed by GitHub
parent 1c04b2b0b7
commit 04c359a5dc
13 changed files with 150 additions and 64 deletions

View File

@ -1,12 +1,13 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CreatedByPreQueryHook } from 'src/engine/core-modules/actor/query-hooks/created-by.pre-query-hook';
import { CreatedByCreateManyPreQueryHook } from 'src/engine/core-modules/actor/query-hooks/created-by.create-many.pre-query-hook';
import { CreatedByCreateOnePreQueryHook } from 'src/engine/core-modules/actor/query-hooks/created-by.create-one.pre-query-hook';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
@Module({
imports: [TypeOrmModule.forFeature([FieldMetadataEntity], 'metadata')],
providers: [CreatedByPreQueryHook],
exports: [CreatedByPreQueryHook],
providers: [CreatedByCreateManyPreQueryHook, CreatedByCreateOnePreQueryHook],
exports: [CreatedByCreateManyPreQueryHook, CreatedByCreateOnePreQueryHook],
})
export class ActorModule {}

View File

@ -27,8 +27,10 @@ type CustomWorkspaceItem = Omit<
};
@WorkspaceQueryHook(`*.createMany`)
export class CreatedByPreQueryHook implements WorkspaceQueryHookInstance {
private readonly logger = new Logger(CreatedByPreQueryHook.name);
export class CreatedByCreateManyPreQueryHook
implements WorkspaceQueryHookInstance
{
private readonly logger = new Logger(CreatedByCreateManyPreQueryHook.name);
constructor(
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,

View File

@ -0,0 +1,117 @@
import { Logger } from '@nestjs/common/services/logger.service';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface';
import { CreateOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import { buildCreatedByFromWorkspaceMember } from 'src/engine/core-modules/actor/utils/build-created-by-from-workspace-member.util';
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import {
ActorMetadata,
FieldActorSource,
} from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
type CustomWorkspaceItem = Omit<
CustomWorkspaceEntity,
'createdAt' | 'updatedAt'
> & {
createdAt: string;
updatedAt: string;
};
@WorkspaceQueryHook(`*.createOne`)
export class CreatedByCreateOnePreQueryHook
implements WorkspaceQueryHookInstance
{
private readonly logger = new Logger(CreatedByCreateOnePreQueryHook.name);
constructor(
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
@InjectRepository(FieldMetadataEntity, 'metadata')
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
) {}
async execute(
authContext: AuthContext,
objectName: string,
payload: CreateOneResolverArgs<CustomWorkspaceItem>,
): Promise<CreateOneResolverArgs<CustomWorkspaceItem>> {
let createdBy: ActorMetadata | null = null;
// TODO: Once all objects have it, we can remove this check
const createdByFieldMetadata = await this.fieldMetadataRepository.findOne({
where: {
object: {
nameSingular: objectName,
},
name: 'createdBy',
workspaceId: authContext.workspace.id,
},
});
if (!createdByFieldMetadata) {
return payload;
}
// If user is logged in, we use the workspace member
if (authContext.workspaceMemberId && authContext.user) {
createdBy = buildCreatedByFromWorkspaceMember(
authContext.workspaceMemberId,
authContext.user,
);
// TODO: remove that code once we have the workspace member id in all tokens
} else if (authContext.user) {
this.logger.warn("User doesn't have a workspace member id in the token");
const workspaceMemberRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
authContext.workspace.id,
'workspaceMember',
);
const workspaceMember = await workspaceMemberRepository.findOne({
where: {
userId: authContext.user?.id,
},
});
if (!workspaceMember) {
throw new Error(
`Workspace member can't be found for user ${authContext.user.id}`,
);
}
createdBy = {
source: FieldActorSource.MANUAL,
workspaceMemberId: workspaceMember.id,
name: `${workspaceMember.name.firstName} ${workspaceMember.name.lastName}`,
};
}
if (authContext.apiKey) {
createdBy = {
source: FieldActorSource.API,
name: authContext.apiKey.name,
};
}
// Front-end can fill the source field
if (
createdBy &&
(!payload.data.createdBy || !payload.data.createdBy?.name)
) {
payload.data.createdBy = {
...createdBy,
source: payload.data.createdBy?.source ?? createdBy.source,
};
}
return payload;
}
}