zenstackhq / zenstack

Fullstack TypeScript toolkit that enhances Prisma ORM with flexible Authorization layer for RBAC/ABAC/PBAC/ReBAC, offering auto-generated type-safe APIs and frontend hooks.
https://zenstack.dev
MIT License
2.03k stars 85 forks source link

Cannot select where on fields from base model when using inheritance #1410

Closed kennyboy55 closed 4 months ago

kennyboy55 commented 4 months ago

Description and expected behavior I have a model which uses inheritance. When creating a query with the enhanced client, I can use all the fields from the base model and the derived model in my where clause. However, only the field from the derived model work.

Environment

Code

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

enum DrinkType {
  Drink
  Beer
  Wine
  Soda
  Cocktail
}

enum ContainerType {
  BeerBottle
  WineBottle
  PlasticBottle
  Can
  Carton
}

model Drink {
  id                Int   @id @default(autoincrement())
  slug              String @unique

  manufacturer_id   Int
  manufacturer      Manufacturer @relation(fields: [manufacturer_id], references: [id])

  type              DrinkType

  name              String @unique
  description       String
  abv               Float
  image             String?

  gluten            Boolean
  lactose           Boolean
  organic           Boolean

  containers        Container[]

  @@delegate(type)

  @@allow('all', true)
}

model Beer extends Drink {
  style_id         Int
  style            BeerStyle @relation(fields: [style_id], references: [id])

  ibu              Float?

  @@allow('all', true)
}

model BeerStyle {
  id               Int @id @default(autoincrement())

  name             String @unique
  color            String

  beers            Beer[]

  @@allow('all', true)
}

model Wine extends Drink {
  style_id         Int
  style            WineStyle @relation(fields: [style_id], references: [id])

  heavy_score      Int?
  tannine_score    Int?
  dry_score        Int?
  fresh_score      Int?
  notes            String?

  @@allow('all', true)
}

model WineStyle {
  id               Int @id @default(autoincrement())

  name             String @unique
  color            String

  wines            Wine[]

  @@allow('all', true)
}

model Soda extends Drink {
  carbonated       Boolean

  @@allow('all', true)
}

model Cocktail extends Drink {
  mix       Boolean

  @@allow('all', true)
}

model Container {
  barcode          String @id

  drink_id         Int
  drink            Drink @relation(fields: [drink_id], references: [id])

  type             ContainerType
  volume           Int
  portions         Int?

  inventory        Int @default(0)

  @@allow('all', true)
}

model Manufacturer {
  id               Int   @id @default(autoincrement())

  country_id       String
  country          Country @relation(fields: [country_id], references: [code])

  name             String @unique
  description      String?
  image            String?

  drinks            Drink[]

  @@allow('all', true)
}

model Country {
  code             String @id
  name             String

  manufacturers    Manufacturer[]

  @@allow('all', true)
}
await dbe.beer.findMany({
    include: {style: true, manufacturer: true},
    where: {NOT: {gluten: true}}
})
Error calling enhanced Prisma method `beer.findMany`: 
Invalid `prisma.beer.findMany()` invocation:

{
  include: {
    delegate_aux_drink: {
      include: {
        manufacturer: true
      }
    },
    style: true
  },
  where: {
    NOT: {
      gluten: true,
      ~~~~~~
?     AND?: BeerWhereInput | BeerWhereInput[],
?     OR?: BeerWhereInput[],
?     NOT?: BeerWhereInput | BeerWhereInput[],
?     id?: IntFilter | Int,
?     style_id?: IntFilter | Int,
?     ibu?: FloatNullableFilter | Float | Null,
?     style?: BeerStyleRelationFilter | BeerStyleWhereInput,
?     delegate_aux_drink?: DrinkRelationFilter | DrinkWhereInput
    }
  }
}

Unknown argument `gluten`. Available options are marked with ?.
    at Module.findDrinksFromSearch (/home/keewijk/projects/beer-inventory/app/models/drinks.server.ts:46:47),
    at loader (/home/keewijk/projects/beer-inventory/app/routes/search.tsx:20:30),
    at async Object.callRouteLoader (/home/keewijk/projects/beer-inventory/node_modules/@remix-run/server-runtime/dist/data.js:62:16),
    at async /home/keewijk/projects/beer-inventory/node_modules/@remix-run/router/dist/router.cjs.js:4229:21,
    at async callLoaderOrAction (/home/keewijk/projects/beer-inventory/node_modules/@remix-run/router/dist/router.cjs.js:4294:16),
    at async Promise.all (index 1),
    at async callDataStrategyImpl (/home/keewijk/projects/beer-inventory/node_modules/@remix-run/router/dist/router.cjs.js:4169:17),
    at async callDataStrategy (/home/keewijk/projects/beer-inventory/node_modules/@remix-run/router/dist/router.cjs.js:3702:19),
    at async loadRouteData (/home/keewijk/projects/beer-inventory/node_modules/@remix-run/router/dist/router.cjs.js:3677:19),
    at async queryImpl (/home/keewijk/projects/beer-inventory/node_modules/@remix-run/router/dist/router.cjs.js:3522:20),
    at async Object.query (/home/keewijk/projects/beer-inventory/node_modules/@remix-run/router/dist/router.cjs.js:3416:18),
    at async handleDocumentRequest (/home/keewijk/projects/beer-inventory/node_modules/@remix-run/server-runtime/dist/server.js:222:15),
    at async requestHandler (/home/keewijk/projects/beer-inventory/node_modules/@remix-run/server-runtime/dist/server.js:141:18),
    at async nodeHandler (/home/keewijk/projects/beer-inventory/node_modules/@remix-run/dev/dist/vite/plugin.js:844:27),
    at async /home/keewijk/projects/beer-inventory/node_modules/@remix-run/dev/dist/vite/plugin.js:847:15 {
  name: 'PrismaClientValidationError',
  clientVersion: '5.13.0',
  internalStack: 'Error calling enhanced Prisma method `beer.findMany`: \n' +
    'Invalid `prisma.beer.findMany()` invocation:\n' +
    '\n' +
    '{\n' +
    '  include: {\n' +
    '    delegate_aux_drink: {\n' +
    '      include: {\n' +
    '        manufacturer: true\n' +
    '      }\n' +
    '    },\n' +
    '    style: true\n' +
    '  },\n' +
    '  where: {\n' +
    '    NOT: {\n' +
    '      gluten: true,\n' +
    '      ~~~~~~\n' +
    '?     AND?: BeerWhereInput | BeerWhereInput[],\n' +
    '?     OR?: BeerWhereInput[],\n' +
    '?     NOT?: BeerWhereInput | BeerWhereInput[],\n' +
    '?     id?: IntFilter | Int,\n' +
    '?     style_id?: IntFilter | Int,\n' +
    '?     ibu?: FloatNullableFilter | Float | Null,\n' +
    '?     style?: BeerStyleRelationFilter | BeerStyleWhereInput,\n' +
    '?     delegate_aux_drink?: DrinkRelationFilter | DrinkWhereInput\n' +
    '    }\n' +
    '  }\n' +
    '}\n' +
    '\n' +
    'Unknown argument `gluten`. Available options are marked with ?.\n' +
    '    at Generator.next (<anonymous>),\n' +
    '    at /home/keewijk/projects/beer-inventory/node_modules/@zenstackhq/runtime/enhancements/policy/handler.js:9:71,\n' +
    '    at new Promise (<anonymous>),\n' +
    '    at __awaiter (/home/keewijk/projects/beer-inventory/node_modules/@zenstackhq/runtime/enhancements/policy/handler.js:5:12),\n' +
    '    at PolicyProxyHandler.doFind (/home/keewijk/projects/beer-inventory/node_modules/@zenstackhq/runtime/enhancements/policy/handler.js:95:16),\n' +
    '    at /home/keewijk/projects/beer-inventory/node_modules/@zenstackhq/runtime/enhancements/policy/handler.js:87:64,\n' +
    '    at cb (/home/keewijk/projects/beer-inventory/node_modules/@zenstackhq/runtime/enhancements/promise.js:23:98),\n' +
    '    at ZenStackPromise.then (/home/keewijk/projects/beer-inventory/node_modules/@zenstackhq/runtime/enhancements/promise.js:32:20),\n' +
    '    at /home/keewijk/projects/beer-inventory/node_modules/@zenstackhq/runtime/enhancements/proxy.js:191:33,\n' +
    '    at new Promise (<anonymous>),\n' +
    '    at /home/keewijk/projects/beer-inventory/node_modules/@zenstackhq/runtime/enhancements/proxy.js:190:28,\n' +
    '    at cb (/home/keewijk/projects/beer-inventory/node_modules/@zenstackhq/runtime/enhancements/promise.js:23:98),\n' +
    '    at ZenStackPromise.then (/home/keewijk/projects/beer-inventory/node_modules/@zenstackhq/runtime/enhancements/promise.js:32:20)'
}

It is trying to add the gluten check to the beer table, but this information is stored in the parent drink table.

ymc9 commented 4 months ago

Hi @kennyboy55 , thanks for reporting it. The gluten filter should be converted into filtering the base relation. I'll make a fix soon.

ymc9 commented 4 months ago

Fixed in 2.1.0