samchon / nestia

NestJS Helper Libraries + TypeScript OpenAPI generator
https://nestia.io/
MIT License
1.71k stars 87 forks source link

RangeError: Maximum call stack size exceeded #922

Open Mario2280 opened 2 weeks ago

Mario2280 commented 2 weeks ago

Feature Request

Fix all funtions like validate equals and creating types for highly nested objects. Or if I didn't read the documentation well, how can I solve this problem using your wonderful library

}

And i use it like this

export type PaymentPaginationType = PaginationQueryDto< PaymentCreateDto, Prisma.PaymentWhereInput, Prisma.PaymentSelect

; export const equalPaymentFindAllValidator = createValidateEquals();

The problem is that prisma generates a looping dependency Prisma.PaymentWhereInput:

export type PaymentWhereInput = { AND?: PaymentWhereInput | PaymentWhereInput[] OR?: PaymentWhereInput[] NOT?: PaymentWhereInput | PaymentWhereInput[] id?: StringFilter<"Payment"> | string payment_source_type?: EnumPaymentTypeFilter<"Payment"> | $Enums.PaymentType shop_id?: StringFilter<"Payment"> | string createdAt?: DateTimeFilter<"Payment"> | Date | string shop?: XOR<ShopRelationFilter, ShopWhereInput> }

This code throw error

const randomWhere = typia.random(); console.log(randomWhere);

RangeError: Maximum call stack size exceeded at $ro107 (E:\Work\PR1\tapply-be\test.is-working.ts:3952:11) at E:\Work\PR1\tapply-be\test.is-working.ts:3043:19 at $ro91 (E:\Work\PR1\tapply-be\test.is-working.ts:3050:11) at E:\Work\PR1\tapply-be\test.is-working.ts:2993:19 at $ro90 (E:\Work\PR1\tapply-be\test.is-working.ts:2994:11) at E:\Work\PR1\tapply-be\test.is-working.ts:545:19 at $ro14 (E:\Work\PR1\tapply-be\test.is-working.ts:546:11)
at E:\Work\PR1\tapply-be\test.is-working.ts:1322:19 at $ro35 (E:\Work\PR1\tapply-be\test.is-working.ts:1323:11) at E:\Work\PR1\tapply-be\test.is-working.ts:978:19


- An overview of the suggested solution.
I used one package when I wrote an application using graphql and the type generation library (https://github.com/unlight/prisma-nestjs-graphql). Validation was generated there and there was no problem with large nesting.
PS 

I also noticed that if an object is highly nested and there is not necessarily a cyclic dependency, it generates an error (Error: Error on typia.equals(): no transform has been configured. Read and follow https://typia.io/docs/setup please). I followed all the installation steps, but the problem was not solved. This is most likely a separate issue, but perhaps they are related.

samchon commented 2 weeks ago

Have you tried to run the tsc command?

Mario2280 commented 2 weeks ago

Have you tried to run the tsc command?

No, I tried to run it through ts-node. I'll try it via tsc and post the results.

Mario2280 commented 2 weeks ago

@samchon Here is my router that does not work as originally intended. You were right, I really removed all the errors and tried to build the project. The assembly was successful. But swagger:build showed that I was using incorrect types for validation, namely nested objects and the union type:

@TypedRoute.Get()
  async findAll(
    @Query({ validate: equalPaymentFindAllValidator, type: 'validate' }) 
    paginationQuery: PaymentPaginationType,
    @Res() res: Response,
  ): Promise<Response> {
    try {
      const payments = await this.paymentService.findMany(paginationQuery);
      return res.status(HttpStatus.OK).json(payments);
    } catch (error) {
      return res
        .status(HttpStatus.BAD_REQUEST)
        .json({ message: error.message });
    }
  }

npm run swagger:build:

src\modules\promo-code\promo-code.controller.ts:42:4:PromoCodeController.findAll:paginationQuery - error TS(@nestia/sdk): invalid type detected.

  - union type is not allowed.
  - nested object type is not allowed.

In this regard, I would like to ask 2 questions. These 2 restrictions make it impossible to validate most queries because most often there are nested objects and unions. 1) Is it possible to somehow solve this problem, maybe I somehow misunderstood the documentation and did not use all the capabilities of your wonderful library, or is it still impossible to do?

2) If this cannot be done and the architecture of your library is based on these rules and changing them is impossible due to breaking changes, then how can you implement such things as I tried to do, because I think that this is a fairly popular request for validation of nested objects and unions.

I'd really like to fix this because I was so impressed with your library that I implemented it into my work project. 🙏🙏🙏

UDP I tried replacing TypedQuery with TypedBody cause it doesn't have these restrictions., but as expected I got the same error:

Analyzing reflections
  - controllers: #17
  - paths: #94
  - routes: #94
Analyzing source codes
Generating Swagger Documents
RangeError: Maximum call stack size exceeded
    at assign (<anonymous>)
    at node_modules\typia\src\programmers\internal\application_v31_schema.ts:32:7 
    at node_modules\typia\src\programmers\internal\application_escaped.ts:14:44   
    at node_modules\typia\src\programmers\internal\application_v31_schema.ts:51:73
    at node_modules\typia\src\programmers\internal\application_array.ts:19:16     
    at node_modules\typia\src\programmers\internal\application_v31_schema.ts:71:71
    at node_modules\typia\src\programmers\internal\application_array.ts:19:16     
    at node_modules\typia\src\programmers\internal\application_v31_schema.ts:71:71
    at node_modules\typia\src\programmers\internal\application_array.ts:19:16     
    at node_modules\typia\src\programmers\internal\application_v31_schema.ts:71:71

As I said earlier, validation breaks down when analyzing looped links( I think there is a mistake in this place:

export type PaymentPaginationType = PaginationQueryDto<
  PaymentCreateDto,
  Omit<Prisma.PaymentWhereInput, 'AND' | 'OR' | 'NOT' | 'shop'>         <--It’s strange that I even decided to exclude 
                                                                properties that create loopholes, but it didn’t help
>;

  export type PaymentWhereInput = { <--------------------many circular dependencies
    AND?: PaymentWhereInput | PaymentWhereInput[]
    OR?: PaymentWhereInput[]
    NOT?: PaymentWhereInput | PaymentWhereInput[]
    id?: StringFilter<"Payment"> | string
    payment_source_type?: EnumPaymentTypeFilter<"Payment"> | $Enums.PaymentType
    shop_id?: StringFilter<"Payment"> | string
    createdAt?: DateTimeFilter<"Payment"> | Date | string
    shop?: XOR<ShopRelationFilter, ShopWhereInput>
  }

Is there some way to fix this in your library?

samchon commented 2 weeks ago
  1. Query parameter cannot be union type. Considering the characteristics of url query parameters, union type is equivalent with any like type.
  2. Nested object type is not supported either. No way to represent nested object type in the url query parameters. If you write x.y.z=3 text, it means not nested object construction statement, but just `query["x.y.z"] = "3".
  3. Therefore, if you want both union and nested type supporting, you have to use request body instead
Mario2280 commented 2 weeks ago

@samchon Yes, that's what I understood, but you probably misunderstood the last part of my message. I tried using TypedBody and encountered a stack overflow due to a looped reference. That is, my problem is not solved. Is there a plan to fix this in the future or could you tell me where I could look and fix it in your code myself? I just need to write a crutch that would limit looped repetitions to, for example, 3 times.

samchon commented 2 weeks ago

Can you give me a reproducible repo with the @TypedBody() decorated recursive type?

Mario2280 commented 2 weeks ago

@samchon Yes give me a couple of minutes

Mario2280 commented 2 weeks ago

@samchon I tried to reproduce this task using route 1 as an example. But something strange happens (it works). When I write the same thing in a working project, this error appears with recursion. I'll try to fix my working repository exactly like in this repository (https://github.com/Mario2280/Nestia_test_recursion) and post the results to see if the problem is fixed

samchon commented 2 weeks ago

If you case is belonged to this issue (https://github.com/samchon/typia/issues/1108), it may be fixed by updating the typia version.

Mario2280 commented 2 weeks ago

@samchon Will this fix work for types and for interfaces? Because I can’t change it if prisma generate will create a bunch of types (and interfaces will be needed)

UDP I did everything 1 in 1 in my working project, there in the test project the link to which I sent above. This is the error I get:

src/modules/payment/payment.controller.ts:48:5 - error TS(@nestia.core.TypedBody): unsupported type detected

- { id: string | RecommendationProductItemIdRecommendedItemIdCompoundUniqueInput; productItemId_recommendedItemId: string | RecommendationProductItemIdRecommendedItemIdCompoundUniqueInput; } & { ...; }.id: Prisma.RecommendationProductItemIdRecommendedItemIdCompoundUniqueInput & string
  - nonsensible intersection

- { id: string | RecommendationProductItemIdRecommendedItemIdCompoundUniqueInput; productItemId_recommendedItemId: string | RecommendationProductItemIdRecommendedItemIdCompoundUniqueInput; } & { ...; }.productItemId_recommendedItemId: string & Prisma.RecommendationProductItemIdRecommendedItemIdCompoundUniqueInput
  - nonsensible intersection

48     @Body()
       ~~~~~~~
49     paginationQuery: PaymentPaginationType,
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Here is a short sequence of how these tables are connected. I tried to recreate the same thing in a test project, but I couldn’t find the source of the error (I had an assumption that it had something to do with the many-to-many relationship in the database):

model Payment{
  id          String   @id @default(uuid())
  shopId      String
  shop        Shop     @relation(fields: [shopId], references: [id])
}
model Shop {
  id               String             @id @default(uuid())
  Recommendation   Recommendation[]
}
model Recommendation {
  id                String      @id @default(uuid())
  productItemId     String
  recommendedItemId String
  productItem       ProductItem @relation("ProductRecommendations", fields: [productItemId], references: [id], onDelete: Cascade)
  recommendedItem   ProductItem @relation("RecommendedProductItems", fields: [recommendedItemId], references: [id], onDelete: Cascade)
  shopId            String
  shop              Shop        @relation(fields: [shopId], references: [id])

  @@unique([productItemId, recommendedItemId])
}
model ProductItem {
  id               String           @id @default(uuid())
  recommendations  Recommendation[] @relation("ProductRecommendations")
  recommendedBy    Recommendation[] @relation("RecommendedProductItems")
  shopId           String
  shop             Shop             @relation(fields: [shopId], references: [id])
}

I tried for several hours to figure out what the error was, but I couldn’t find it.

samchon commented 2 weeks ago

The nonsensible intersection message comes by typia when contradictory type comes. In your reply, I found the reason why. Prisma.RecommendationProductItemIdRecommendedItemIdCompoundUniqueInput & string type is the case. It is the tremendous ridiculuous type because string and object type can't be intersected.

In such reason, I'm not using Prisma generated types as DTO directly. In the actual service, DTO cannot be 1:1 correspondent with ORM schemes, because the DTO has been composed by combinating DB entities complicately.

Mario2280 commented 2 weeks ago

@samchon Can I put a stub specifically for this type to avoid errors and complete the generation of js code for validation without errors? This is very important to me because when I saw your library, the first thing I thought was to do something similar using Prisma types and try to implement it into a working project to validate complex queries with select where include commands(like this libs but it works with https://github.com/unlight/prisma-nestjs-graphql). I most likely understand that pure types are built into the core of your library and this rule is difficult to change, but imagine if it were possible to handle such problematic situations, this would give your library a very powerful feature. Do you have any ideas on how to get around this problem? Or there is no way out and I will have to register all these interfaces manually and eliminate this error (this is a lot of work, I would not like to do this...