graphql-nexus / nexus-plugin-prisma

Deprecated
MIT License
828 stars 118 forks source link

Wrong generated GraphQL input types for relations #413

Closed cimchd closed 5 years ago

cimchd commented 5 years ago

I have a model generated with prisma 2:

model CustomerAccount {
  id        String   @default(cuid()) @id @unique
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  user                    User
  enabled                 Boolean
  assignedTeam            EmployeeGroup?
}

I then generated the GraphQL schema with nexus. Everything seems to be fine, but the generated filter input type QueryFindManyCustomerAccountWhereInput:

input QueryFindManyCustomerAccountWhereInput {
  id: StringFilter
  createdAt: QueryFindManyCustomerAccountFilter
  updatedAt: QueryFindManyCustomerAccountFilter
  enabled: BooleanFilter
  AND: [QueryFindManyCustomerAccountWhereInput!]
  OR: [QueryFindManyCustomerAccountWhereInput!]
  NOT: [QueryFindManyCustomerAccountWhereInput!]
  user: QueryFindManyCustomerAccountWhereInput
  assignedTeam: QueryFindManyCustomerAccountWhereInput
  log: QueryFindManyCustomerAccountWhereInput
}

The problem is that is is self refrencing itself for all relation fields. For user the type should be QueryFindManyUserWhereInput, for assignedTeam QueryFindManyAssignedTeamWhereInput and for log QueryFindManyLogWhereInput

I want to make filtering with GraphQL like this:

query {
  customeraccounts(where: { user: { email: { contains: "prisma" } } }) {
    id
    user {
      email
    }
  }
}

Instead I get the error message:

{
  "error": {
    "errors": [
      {
        "message": "Field \"email\" is not defined by type QueryFindManyCustomerAccountWhereInput.",
        "locations": [
          {
            "line": 2,
            "column": 35
          }
        ],
        "extensions": {
          "code": "GRAPHQL_VALIDATION_FAILED",
          "exception": {
            "stacktrace": [
              "GraphQLError: Field \"email\" is not defined by type QueryFindManyCustomerAccountWhereInput.",
              "    at ObjectField (/api/node_modules/graphql/validation/rules/ValuesOfCorrectType.js:103:29)",
              "    at Object.enter (/api/node_modules/graphql/language/visitor.js:324:29)",
              "    at Object.enter (/api/node_modules/graphql/language/visitor.js:375:25)",
              "    at visit (/api/node_modules/graphql/language/visitor.js:242:26)",
              "    at Object.validate (/api/node_modules/graphql/validation/validate.js:73:24)",
              "    at validate (/api/node_modules/apollo-server-core/dist/requestPipeline.js:212:32)",
              "    at Object.<anonymous> (/api/node_modules/apollo-server-core/dist/requestPipeline.js:125:42)",
              "    at Generator.next (<anonymous>)",
              "    at fulfilled (/api/node_modules/apollo-server-core/dist/requestPipeline.js:5:58)",
              "    at runMicrotasks (<anonymous>)"
            ]
          }
        }
      }
    ]
  }
}

tested with nexus version: 0.12.0-beta.9

Weakky commented 5 years ago

Hey 👋, this is a duplicate of #311. It's now fixed and will be available in the next release of nexus-prisma

cimchd commented 5 years ago

I tested with the latest nexus-prisma version (0.5.0-next.4). There are still not the right input types generated for the graphql schema when I use relations. Still in the generated GraphQL schema all relations with QueryFindMany[parent model]WhereInput are self refrencing instead of using the child model QueryFindMany[child model]WhereInput .

So I am not sure if we have the same understanding of the problem described above.

Weakky commented 5 years ago

Sorry about that, but I'm not able to reproduce.

The following schema + datamodel:

const datamodel = `
model CustomerAccount {
  id        Int @id
  user                    User
  enabled                 Boolean
  assignedTeam            EmployeeGroup?
}

model User {
  id        Int @id
}

model EmployeeGroup {
  id        Int @id
}
`

const User = objectType({
  name: 'User',
  definition(t: any) {
    t.model.id()
  },
})

const CustomerAccount = objectType({
  name: 'CustomerAccount',
  definition(t: any) {
    t.model.id()
    t.model.enabled()
    t.model.assignedTeam()
    t.model.user()
  },
})

const Query = objectType({
  name: 'Query',
  definition(t: any) {
    t.crud.customeraccounts({ filtering: { assignedTeam: true, user: true, enabled: true } })
  },
})

const { schema, typegen } = await generateSchemaAndTypes(datamodel, [
  Query,
  User,
  CustomerAccount
])

console.log(schema)

Will print the following schema:

input BooleanFilter {
  equals: Boolean
  not: Boolean
}

type CustomerAccount {
  id: Int!
  enabled: Boolean!
  assignedTeam: EmployeeGroup
  user: User!
}

input CustomerAccountWhereInput {
  id: IntFilter
  enabled: BooleanFilter
  AND: [CustomerAccountWhereInput!]
  OR: [CustomerAccountWhereInput!]
  NOT: [CustomerAccountWhereInput!]
  user: UserWhereInput
  assignedTeam: EmployeeGroupWhereInput
}

type EmployeeGroup {
  id: Int!
  customerAccount: CustomerAccount
}

input EmployeeGroupWhereInput {
  id: IntFilter
  AND: [EmployeeGroupWhereInput!]
  OR: [EmployeeGroupWhereInput!]
  NOT: [EmployeeGroupWhereInput!]
  customerAccount: CustomerAccountWhereInput
}

input IntFilter {
  equals: Int
  not: Int
  in: [Int!]
  notIn: [Int!]
  lt: Int
  lte: Int
  gt: Int
  gte: Int
}

type Query {
  customeraccounts(where: QueryCustomeraccountsWhereInput, skip: Int, after: String, before: String, first: Int, last: Int): [CustomerAccount!]
}

input QueryCustomeraccountsWhereInput {
  enabled: BooleanFilter
  user: UserWhereInput
  assignedTeam: EmployeeGroupWhereInput
}

type User {
  id: Int!
}

input UserWhereInput {
  id: IntFilter
  AND: [UserWhereInput!]
  OR: [UserWhereInput!]
  NOT: [UserWhereInput!]
  customerAccount: CustomerAccountWhereInput
}

Even when filtering is just to true, it works properly too. Would you mind providing some reproduction?

cimchd commented 5 years ago

No problem. I took your minified example and changed it a little bit, using the nexus makeSchema function that I use. Here are the complete steps:

First of all, this is the schema file /prisma/prisma.schema:

datasource db {
  provider = "mysql"
  url      = env("MYSQL_URL")
  default  = true
}

generator photon {
  provider = "photonjs"
}

generator nexus_prisma {
  provider = "nexus-prisma"
}

model CustomerAccount {
  id                      Int @id
  user                    User
  enabled                 Boolean
  assignedTeam            EmployeeGroup?
}

model User {
  id        Int @id
}

model EmployeeGroup {
  id        Int @id
}

Then I was generating the photon client and the nexus client with prisma2 generate Next step was to run the script:

const { makeSchema, objectType } = require('nexus');
const pathGraphqlSchema = './generated/graphql.schema';
const pathNexusTypes  = './generated/nexus.types.ts';

const User = objectType({
  name: 'User',
  definition(t) {
    t.model.id();
  }
});

const EmployeeGroup = objectType({
  name: 'EmployeeGroup',
  definition(t) {
    t.model.id();
  }
});

const CustomerAccount = objectType({
  name: 'CustomerAccount',
  definition(t) {
    t.model.id();
    t.model.enabled();
    t.model.assignedTeam();
    t.model.user();
  }
});

const Query = objectType({
  name: 'Query',
  definition(t) {
    console.dir(t, { depth: 4 });
    t.crud.customeraccounts({
      filtering: true
    });
  }
});

// Nexus
const { nexusPrismaPlugin } = require('@generated/nexus-prisma');
const nexusPrisma = nexusPrismaPlugin({
  photon: ctx => ctx.photon
});

const schema = makeSchema({
  types: [Query, User, EmployeeGroup, CustomerAccount, nexusPrisma],
  outputs: {
    schema: pathGraphqlSchema,
    typegen: pathNexusTypes
  }
});

console.log(schema);

The output of the generated schema was:

### This file was autogenerated by GraphQL Nexus
### Do not make changes to this file directly

input BooleanFilter {
  equals: Boolean
  not: Boolean
}

type CustomerAccount {
  assignedTeam: EmployeeGroup
  enabled: Boolean!
  id: Int!
  user: User!
}

type EmployeeGroup {
  id: Int!
}

input IntFilter {
  equals: Int
  gt: Int
  gte: Int
  in: [Int!]
  lt: Int
  lte: Int
  not: Int
  notIn: [Int!]
}

type Query {
  customeraccounts(after: String, before: String, first: Int, last: Int, skip: Int, where: QueryFindManyCustomerAccountWhereInput): [CustomerAccount!]
}

input QueryFindManyCustomerAccountWhereInput {
  AND: [QueryFindManyCustomerAccountWhereInput!]
  assignedTeam: QueryFindManyCustomerAccountWhereInput
  enabled: BooleanFilter
  id: IntFilter
  NOT: [QueryFindManyCustomerAccountWhereInput!]
  OR: [QueryFindManyCustomerAccountWhereInput!]
  user: QueryFindManyCustomerAccountWhereInput
}

type User {
  id: Int!
}

The error occurs in the generation of QueryFindManyCustomerAccountWhereInput. The type of the relation fields assignedTeam and user is wrong (it's both QueryFindManyCustomerAccountWhereInput, the same type as the input type itself). The correct type should be QueryFindManyEmployeeGroupWhereInput for assignedTeam and QueryFindManyUserWhereInput for user, both the input type of the relation.

Same problem occurs for QueryFindManyUserWhereInput and QueryFindManyEmployeeGroupWhereInput if you generate the queries for them.

Weakky commented 5 years ago

Yeah ok I see, you’re still using a very old version of nexus-prisma. We haven’t updated our docs yet, but please have a look at how it’s done here: https://github.com/prisma-labs/nexus-prisma/tree/master/examples/blog nexus-prisma is no longer a prisma generator. It’s a regular library that you need to install in your deps. Our current sprint is partly dedicated to updating the docs, so that’s gonna be fixed soon 🙏

cimchd commented 5 years ago

Thanks, I have checked the example and it seems to work now. 😀👍