zenstack icon indicating copy to clipboard operation
zenstack copied to clipboard

[Feature Request] Allow overridding of model properties when importing

Open jasonmacdonald opened this issue 6 months ago • 0 comments

Currently, imports are tightly coupled to each other because Prisma requires the relationship to be defined on both sides using the other-sides type. However, each model may be defined in a separate module where it's desirable to treat them as if they are isolated.

One way to resolve this could be to allow an abstract models properties to be overridden by the concrete class, kind of like an override in javascript. Currently, if you try to override a property that was defined in an abstract model it will error with Duplicated declaration name.

I think there's a good use-case for this feature if you consider it from the perspective of an NPM package, which could export an abstract model it expects you to extend, but would also allow you to override the properties relation types with custom ones, as long as their interfaces matched.

Here is a somewhat arbitrary example to illustrate what I'm thinking..

schema.zmodel - @someOrg/UserPlugin

abstract model AbstractProfile {
    id        String       @id @unique @default(uuid()) @db.Uuid
    firstName String
    lastName  String
    
    // Relation
    user      AbstractUser
}

abstract model AbstractUser {
    id        String          @id @unique @default(uuid()) @db.Uuid
    email     String          @unique
    password  String          @db.Char(97)
    profileId String          @unique @db.Uuid

    // Relation
    profile   AbstractProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
}

schema.zmodel - App

import '@someOrg/UserPlugin'

/** User adds additional fields */
model User extends AbstractUser {
    isLocked  Boolean  @default(false)
    lastLogin DateTime @default(now()) @map("last_login")

    // Relation
    profile   Profile  @relation(fields: [profileId], references: [id], onDelete: Cascade)
}

/** Profile adds additional fields */
model Profile extends AbstractProfile {
    email     String     @unique @Validator.IsEmail()
    title     String?
    phone     String?    @Validator.IsPhoneNumber()
    mobile    String?    @Validator.IsPhoneNumber()
    image     String?    @Validator.IsUrl()
    locale    String?    @default("en") @Validator.IsLocale()

    // Relation
    user User
}

Right now, you either define the relation in the abstract, which means it's fixed to what model it references and you lose reference to the additional fields when being returned from the plugin. Or, you leave it out of the abstract and only define the relation in the concrete model. But doing this means that the plugin cannot rely on the relationship existing in it's own code.

If you could override as shown above, the plugin can use its internal representation of an "extended" User and Profile to code against and still have it match the user's App version when returned from prisma/client just with additional fields.

Maybe this is already possible, and I'm missing something or thinking about it incorrectly. I'm open to suggestions, but I'm trying to find a way to handle relations when the two schemas are separated by a @Module. I need to define the relationship for permissions that require access through a predicate, for example, but don't necessarily care about knowing all of the model's other properties that might be specific to the app.

jasonmacdonald avatar Aug 02 '24 14:08 jasonmacdonald