Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extra CreateDTOClass fields are available in the afterInsert event if using an AutoResolver but not if using a custom CRUDResolver #153

Open
RyanLackie opened this issue Jul 16, 2023 · 3 comments
Labels
bug Something isn't working

Comments

@RyanLackie
Copy link

RyanLackie commented Jul 16, 2023

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);
    }
}
  1. Set the module definition to match:
@Module({
    imports: [
        NestjsQueryGraphQLModule.forFeature({
            imports: [NestjsQueryTypeOrmModule.forFeature([ExampleEntity])],
            resolvers: [
                {
                    DTOClass: ExampleDTO,
                    CreateDTOClass: CreateExampleDTO,
                    EntityClass: ExampleEntity,
                },
            ],
        }),
    ],
    providers: [ExampleSubscriber],
})
  1. Preform a createOneExample mutation with a someExtraField value
  2. The EventSubscriber should log an entity with the same field someExtraField and value of what was sent in the mutation

vs

  1. 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);
    }
}
  1. Preform a createOneExample mutation with a someExtraField value
  2. The EventSubscriber should now no longer log an entity with the same field someExtraField 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
@RyanLackie RyanLackie added the bug Something isn't working label Jul 16, 2023
@TriPSs
Copy link
Owner

TriPSs commented Jul 17, 2023

If you add the field someExtraField to the entity without the @Column does it than work?

@RyanLackie
Copy link
Author

RyanLackie commented Jul 17, 2023

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.

@TriPSs
Copy link
Owner

TriPSs commented Aug 1, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants