gid-oss / dataui-nestjs-crud

NestJs CRUD for RESTful APIs
https://github.com/nestjsx/crud/wiki
MIT License
130 stars 31 forks source link

@Crud dynamic params and ManyToOne Relationships incorrect column mapping #44

Open dfliess opened 7 months ago

dfliess commented 7 months ago

Hi everyone,

First of all, I'd like to extend my gratitude to the maintainers for your efforts in keeping this project both usable and up-to-date.

I'm encountering an issue when using the @Crud controller's params option, specifically when it involves ManyToOne relationships. Below is a description of the problem and the technical context:

Dependencies:

"dependencies": {
    "@dataui/crud": "^5.3.3",
    "@dataui/crud-typeorm": "^5.3.3",
    "@nestjs/common": "10.3.7",
    "@nestjs/config": "3.2.2",
    "@nestjs/core": "10.3.7",
    "@nestjs/platform-express": "10.3.7",
    "@nestjs/swagger": "7.3.1",
    "@nestjs/typeorm": "10.0.2",
    "class-transformer": "0.5.1",
    "class-validator": "0.14.0",
    "pg": "8.11.2",
    "reflect-metadata": "0.1.13",
    "typeorm": "0.3.20"
  }

Relevant Code:

import { Crud, CrudController } from '@dataui/crud';
import { Controller } from '@nestjs/common';
import { ArticleService } from './article.service';
import { Article } from './entities/article.entity';

@Controller('/author/:authorId/article')
@Crud({
  model: {
    type: Article,
  },
  params: {
    authorId: {
      field: 'author',
      type: 'number',
    },
  },
})
export class ArticleController implements CrudController<Article> {
  constructor(public service: ArticleService) {}
}
import { Author } from 'src/author/entities/author.entity';
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Article {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @ManyToOne(() => Author, {
    nullable: false,
  })
  author: Author;
}
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Author {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;
}

Issue:

When I make a GET request to /author/1/article, I encounter a 500 error with the following log:

error: error: column Article.authorId.id does not exist ERROR [ExceptionsHandler] column Article.authorId.id does not exist QueryFailedError: column Article.authorId.id does not exist at PostgresQueryRunner.query (./api/src/driver/postgres/PostgresQueryRunner.ts:331:19) at processTicksAndRejections (node:internal/process/task_queues:95:5) at SelectQueryBuilder.loadRawResults (./api/src/query-builder/SelectQueryBuilder.ts:3805:25) at SelectQueryBuilder.executeEntitiesAndRawResults (./api/src/query-builder/SelectQueryBuilder.ts:3551:26) at SelectQueryBuilder.getRawAndEntities (./api/src/query-builder/SelectQueryBuilder.ts:1670:29) at SelectQueryBuilder.getMany (./api/src/query-builder/SelectQueryBuilder.ts:1760:25)

The generated SQL query:

query failed: SELECT "Article"."id" AS "Article_id", "Article"."title" AS "Article_title", "Article"."authorId" FROM "article" "Article" WHERE ("Article"."authorId.id" = $1) -- PARAMETERS: [1]

Further Investigation:

I discovered that the TypeOrmCrudService utilizes the getFieldWithAlias function, potentially mishandling the mapping of properties to database columns. This function relies on entityColumnsHash, which is initialized using TypeORM's column metadata in onInitMapEntityColumns. Intriguingly, TypeORM provides a databasePath = 'authorId.id', which doesn't seem correct.

image

Modifying the initialization line in the TypeOrmCrudService to use databaseName instead of databasePath appears to solve the issue:

this.entityColumnsHash[prop.propertyName] = prop.databaseName;

Although this adjustment resolves the immediate problem, I'm uncertain if it's the correct fix, or if the underlying issue might be within TypeORM itself.

I would appreciate any help or comments