prisma-labs / graphqlgen

⚙️ Generate type-safe resolvers based upon your GraphQL Schema
MIT License
818 stars 54 forks source link

Graphqlgen still uses undefined + null #417

Closed MathiasKandelborg closed 5 years ago

MathiasKandelborg commented 5 years ago

Description

This should have fixed things right?

Steps to reproduce

Generate a schema, undefined and null will both be generated.

Expected results

Typescript with Prisma needs:

export interface PostWhereInput {
    AND?: PostWhereInput[];
    OR?: PostWhereInput[];
    NOT?: PostWhereInput[];
    id?: string;
    id_not?: string;
   ...
}

Actual results

export interface PostWhereInput {
    AND?: PostWhereInput[] | null;
    OR?: PostWhereInput[] | null;
    NOT?: PostWhereInput[] | null;
    id?: string | null;
    id_not?: string | null;
   ...
}

Versions

jasonkuhrt commented 5 years ago

Hey @MathiasKandelborg, thanks for reporting this.

Can you provide a minimal repro in the form of a GQL schema file? That would help a lot.

MathiasKandelborg commented 5 years ago

Of course, how could I forget. I'm away from the pc right now, I'll provide proper examples first thing in the morning.

MathiasKandelborg commented 5 years ago
type Post {
  id: ID! @unique
  createdAt: DateTime!
  updatedAt: DateTime!

  author: User!

  contents: [PostContent!]! @relation(name: "PostContent", onDelete: CASCADE)

  slug: String! @unique
  imgSrc: String!
  isPublished: Boolean! @default(value: "false")

  comments: [Comment!]! @relation(name: "PostComment", onDelete: CASCADE)
}

Here's the main post type/schema file, I can share the rest but the structure is quite large. Let me know if I can do anything else - I've not looked at graphqlgen source so I'm really unsure what'll be good info and what's redundant.

jasonkuhrt commented 5 years ago

Can you share the schema definition of PostWhereInput?

MathiasKandelborg commented 5 years ago

.graphqlconfig

projects:
  named-app:
    schemaPath: schema.graphql
    extensions:
      endpoints:
        default: http://localhost:4000

  named-database:
    schemaPath: generated/prisma.graphql
    extensions:
      prisma: prisma.yml
      codegen:
        - generator: typescript-client
          output: ./prisma-client/

graphqlgen.yml

language: typescript

schema: schema.graphql

context: ./src/API-Types.ts:Context

models:
  files:
    - ./src/generated/prisma-client
    - ./src/API-Types.ts

output: ./src/generated/graphqlgen.ts

resolver-scaffolding:
  output: ./src/generated/tmp-resolvers/
  layout: file-per-type

Directly from prisma (generated/prisma.graphql):

input PostWhereInput {
  """Logical AND on all given filters."""
  AND: [PostWhereInput!]

  """Logical OR on all given filters."""
  OR: [PostWhereInput!]

  """Logical NOT on all given filters combined by AND."""
  NOT: [PostWhereInput!]
  id: ID

  """All values that are not equal to given value."""
  id_not: ID

  """All values that are contained in given list."""
  id_in: [ID!]

  """All values that are not contained in given list."""
  id_not_in: [ID!]

  """All values less than the given value."""
  id_lt: ID

  """All values less than or equal the given value."""
  id_lte: ID

  """All values greater than the given value."""
  id_gt: ID

  """All values greater than or equal the given value."""
  id_gte: ID

  """All values containing the given string."""
  id_contains: ID

  """All values not containing the given string."""
  id_not_contains: ID

  """All values starting with the given string."""
  id_starts_with: ID

  """All values not starting with the given string."""
  id_not_starts_with: ID

  """All values ending with the given string."""
  id_ends_with: ID

  """All values not ending with the given string."""
  id_not_ends_with: ID
  createdAt: DateTime

  """All values that are not equal to given value."""
  createdAt_not: DateTime

  """All values that are contained in given list."""
  createdAt_in: [DateTime!]

  """All values that are not contained in given list."""
  createdAt_not_in: [DateTime!]

  """All values less than the given value."""
  createdAt_lt: DateTime

  """All values less than or equal the given value."""
  createdAt_lte: DateTime

  """All values greater than the given value."""
  createdAt_gt: DateTime

  """All values greater than or equal the given value."""
  createdAt_gte: DateTime
  updatedAt: DateTime

  """All values that are not equal to given value."""
  updatedAt_not: DateTime

  """All values that are contained in given list."""
  updatedAt_in: [DateTime!]

  """All values that are not contained in given list."""
  updatedAt_not_in: [DateTime!]

  """All values less than the given value."""
  updatedAt_lt: DateTime

  """All values less than or equal the given value."""
  updatedAt_lte: DateTime

  """All values greater than the given value."""
  updatedAt_gt: DateTime

  """All values greater than or equal the given value."""
  updatedAt_gte: DateTime
  slug: String

  """All values that are not equal to given value."""
  slug_not: String

  """All values that are contained in given list."""
  slug_in: [String!]

  """All values that are not contained in given list."""
  slug_not_in: [String!]

  """All values less than the given value."""
  slug_lt: String

  """All values less than or equal the given value."""
  slug_lte: String

  """All values greater than the given value."""
  slug_gt: String

  """All values greater than or equal the given value."""
  slug_gte: String

  """All values containing the given string."""
  slug_contains: String

  """All values not containing the given string."""
  slug_not_contains: String

  """All values starting with the given string."""
  slug_starts_with: String

  """All values not starting with the given string."""
  slug_not_starts_with: String

  """All values ending with the given string."""
  slug_ends_with: String

  """All values not ending with the given string."""
  slug_not_ends_with: String
  image: String

  """All values that are not equal to given value."""
  image_not: String

  """All values that are contained in given list."""
  image_in: [String!]

  """All values that are not contained in given list."""
  image_not_in: [String!]

  """All values less than the given value."""
  image_lt: String

  """All values less than or equal the given value."""
  image_lte: String

  """All values greater than the given value."""
  image_gt: String

  """All values greater than or equal the given value."""
  image_gte: String

  """All values containing the given string."""
  image_contains: String

  """All values not containing the given string."""
  image_not_contains: String

  """All values starting with the given string."""
  image_starts_with: String

  """All values not starting with the given string."""
  image_not_starts_with: String

  """All values ending with the given string."""
  image_ends_with: String

  """All values not ending with the given string."""
  image_not_ends_with: String
  isPublished: Boolean

  """All values that are not equal to given value."""
  isPublished_not: Boolean
  author: UserWhereInput
  contents_every: PostContentWhereInput
  contents_some: PostContentWhereInput
  contents_none: PostContentWhereInput
  comments_every: CommentWhereInput
  comments_some: CommentWhereInput
  comments_none: CommentWhereInput
}

From src/generated/prisma-client/prisma-schema.ts :

input PostWhereInput {
  id: ID
  id_not: ID
  id_in: [ID!]
  id_not_in: [ID!]
  id_lt: ID
  id_lte: ID
  id_gt: ID
  id_gte: ID
  id_contains: ID
  id_not_contains: ID
  id_starts_with: ID
  id_not_starts_with: ID
  id_ends_with: ID
  id_not_ends_with: ID
  createdAt: DateTime
  createdAt_not: DateTime
  createdAt_in: [DateTime!]
  createdAt_not_in: [DateTime!]
  createdAt_lt: DateTime
  createdAt_lte: DateTime
  createdAt_gt: DateTime
  createdAt_gte: DateTime
  updatedAt: DateTime
  updatedAt_not: DateTime
  updatedAt_in: [DateTime!]
  updatedAt_not_in: [DateTime!]
  updatedAt_lt: DateTime
  updatedAt_lte: DateTime
  updatedAt_gt: DateTime
  updatedAt_gte: DateTime
  author: UserWhereInput
  contents_every: PostContentWhereInput
  contents_some: PostContentWhereInput
  contents_none: PostContentWhereInput
  slug: String
  slug_not: String
  slug_in: [String!]
  slug_not_in: [String!]
  slug_lt: String
  slug_lte: String
  slug_gt: String
  slug_gte: String
  slug_contains: String
  slug_not_contains: String
  slug_starts_with: String
  slug_not_starts_with: String
  slug_ends_with: String
  slug_not_ends_with: String
  image: String
  image_not: String
  image_in: [String!]
  image_not_in: [String!]
  image_lt: String
  image_lte: String
  image_gt: String
  image_gte: String
  image_contains: String
  image_not_contains: String
  image_starts_with: String
  image_not_starts_with: String
  image_ends_with: String
  image_not_ends_with: String
  isPublished: Boolean
  isPublished_not: Boolean
  comments_every: CommentWhereInput
  comments_some: CommentWhereInput
  comments_none: CommentWhereInput
  AND: [PostWhereInput!]
  OR: [PostWhereInput!]
  NOT: [PostWhereInput!]
}

From src/generated/graphqlgen.ts:

  export interface PostWhereInput {
    AND?: PostWhereInput[] | null;
    OR?: PostWhereInput[] | null;
    NOT?: PostWhereInput[] | null;
    id?: string | null;
    id_not?: string | null;
    id_in?: string[] | null;
    id_not_in?: string[] | null;
    id_lt?: string | null;
    id_lte?: string | null;
    id_gt?: string | null;
    id_gte?: string | null;
    id_contains?: string | null;
    id_not_contains?: string | null;
    id_starts_with?: string | null;
    id_not_starts_with?: string | null;
    id_ends_with?: string | null;
    id_not_ends_with?: string | null;
    createdAt?: string | null;
    createdAt_not?: string | null;
    createdAt_in?: string[] | null;
    createdAt_not_in?: string[] | null;
    createdAt_lt?: string | null;
    createdAt_lte?: string | null;
    createdAt_gt?: string | null;
    createdAt_gte?: string | null;
    updatedAt?: string | null;
    updatedAt_not?: string | null;
    updatedAt_in?: string[] | null;
    updatedAt_not_in?: string[] | null;
    updatedAt_lt?: string | null;
    updatedAt_lte?: string | null;
    updatedAt_gt?: string | null;
    updatedAt_gte?: string | null;
    slug?: string | null;
    slug_not?: string | null;
    slug_in?: string[] | null;
    slug_not_in?: string[] | null;
    slug_lt?: string | null;
    slug_lte?: string | null;
    slug_gt?: string | null;
    slug_gte?: string | null;
    slug_contains?: string | null;
    slug_not_contains?: string | null;
    slug_starts_with?: string | null;
    slug_not_starts_with?: string | null;
    slug_ends_with?: string | null;
    slug_not_ends_with?: string | null;
    image?: string | null;
    image_not?: string | null;
    image_in?: string[] | null;
    image_not_in?: string[] | null;
    image_lt?: string | null;
    image_lte?: string | null;
    image_gt?: string | null;
    image_gte?: string | null;
    image_contains?: string | null;
    image_not_contains?: string | null;
    image_starts_with?: string | null;
    image_not_starts_with?: string | null;
    image_ends_with?: string | null;
    image_not_ends_with?: string | null;
    isPublished?: boolean | null;
    isPublished_not?: boolean | null;
    author?: UserWhereInput | null;
    contents_every?: PostContentWhereInput | null;
    contents_some?: PostContentWhereInput | null;
    contents_none?: PostContentWhereInput | null;
    comments_every?: CommentWhereInput | null;
    comments_some?: CommentWhereInput | null;
    comments_none?: CommentWhereInput | null;
  }
jasonkuhrt commented 5 years ago

@MathiasKandelborg thanks for this.

Given:

input PostWhereInput {
  AND: [PostWhereInput!]

The correct resolver arg TypeScript Type is:

export interface PostWhereInput {
    AND?: PostWhereInput[] | null;

So graphqlgen is doing the right thing, as is.

Note to self: We need to improve the documentation and examples so that these and other subtleties can be deeply and centrally explained.

At this point the issue becomes about Prisma, not graphqlgen. Correct me if I am wrong but I believe the issue you are facing is that the Prisma lib param types will not accept undefined.

There are two possible fixes the prisma team can do:

  1. use GraphQL default feature, e.g.:

    input PostWhereInput {
      AND: [PostWhereInput!] = null
  2. change their prisma lib to accept undefined
MathiasKandelborg commented 5 years ago

@jasonkuhrt I thought that it seemed weird. I'm sorry for putting it on graphqlgen, it has just been the order in which I've experienced the issues.

Thanks for taking the time to elaborate, I'll go talk with the prisma team.