Extra CreateDTOClass fields are available in the afterInsert event if using an AutoResolver but not if using a custom CRUDResolver
RyanLackie opened this issue · 3 comments
Describe the bug
When triggering an afterInsert
event within a typeorm
EventSubscriber
, any fields that are part of the CreateDTOClass
and not part of the DTOClass
, are available when the used resolver is an AutoResolver
but not when the resolver is a custom CRUDResolver
.
Thank you for taking a look. Please let me know if you have any questions.
To Reproduce
https://github.com/RyanLackie/nestjs-query-issue-example
Related Example Files:
ExampleEntity:
@Entity()
export class ExampleEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column()
someField: string;
}
ExampleDTO:
@ObjectType('Example')
export class ExampleDTO {
@IDField(() => ID)
id: string;
@Field({ nullable: true })
someField: string;
}
CreateExampleDTO:
@InputType()
export class CreateExampleDTO {
@Field()
someField: string;
@Field()
someExtraField: string;
}
EventSubscriber
@EventSubscriber()
export class ExampleSubscriber<ExampleEntity>
implements EntitySubscriberInterface<ExampleEntity>
{
constructor(dataSource: DataSource) {
dataSource.subscribers.push(this);
}
listenTo() {
return ExampleEntity;
}
async afterInsert(event: InsertEvent<ExampleEntity>) {
console.log('event.entity: ', event.entity);
}
}
- Set the module definition to match:
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([ExampleEntity])],
resolvers: [
{
DTOClass: ExampleDTO,
CreateDTOClass: CreateExampleDTO,
EntityClass: ExampleEntity,
},
],
}),
],
providers: [ExampleSubscriber],
})
- Preform a
createOneExample
mutation with asomeExtraField
value - The
EventSubscriber
should log an entity with the same fieldsomeExtraField
and value of what was sent in the mutation
vs
- Set the module and resolver to be:
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([ExampleEntity])],
dtos: [
{
DTOClass: ExampleDTO,
CreateDTOClass: CreateExampleDTO,
},
],
}),
],
providers: [ExampleResolver, ExampleSubscriber],
})
export class ExampleModule {}
@Resolver(() => ExampleDTO)
export class ExampleResolver extends CRUDResolver(ExampleDTO, {
CreateDTOClass: CreateExampleDTO,
}) {
constructor(
@InjectQueryService(ExampleEntity)
readonly service: QueryService<ExampleEntity>,
) {
super(service);
}
}
- Preform a
createOneExample
mutation with asomeExtraField
value - The
EventSubscriber
should now no longer log an entity with the same fieldsomeExtraField
and value of what was sent in the mutation
Expected behavior
I would expect both functionalities to expose the same data to the typeorm
subscriber functions.
Desktop (please complete the following information):
- Node Version:
v18.14.1
- Nestjs-query Versions:
"@ptc-org/nestjs-query-core": "3.0.0",
"@ptc-org/nestjs-query-graphql": "3.0.0",
"@ptc-org/nestjs-query-typeorm": "3.0.0",
- Typeorm Version:
0.3.17
If you add the field someExtraField
to the entity without the @Column
does it than work?
Hey, thanks for getting back so fast.
If someExtraField
is added to the entity without the @Column
decorator, the entity printed in the EventSubscriber
has the field but it is undefined
and doesn't have the value provided in the CreateExampleDTO
.
Here is a minimum repo with code to reproduce what I found.
Edit: I updated the classes in the description to match what's in the linked repo.
Sorry for the late reply, completely missed this!
The issue is very weird as when you extend CRUDResolver
it will be the same as defining resolvers
inside your module. I think the root cause is the code below (which is executed before creating the record).
private async ensureIsEntityAndDoesNotExist(entity: DeepPartial<Entity>): Promise<Entity> {
if (!(entity instanceof this.EntityClass)) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return this.ensureEntityDoesNotExist(this.repo.create(entity))
}
return this.ensureEntityDoesNotExist(entity)
}
I cannot seem to find out why in one case the CreateDTOClass extends it's entity and not when you define a resolver your self.
An easy fix in this case is to make sure that your CreateDTOClass extends your entity, in case of your example:
export class CreateExampleDTO extends ExampleEntity {
I also do something like this in my own API's, there I extend the main DTO with for example PickType
/ OmitType
from NestJS, the main DTO in it's turn than extends the entity it belongs to.
I'm going to keep this bug open so when I have a bit more time I can debug it a bit more.