tsedio / tsed

:triangular_ruler: Ts.ED is a Node.js and TypeScript framework on top of Express to write your application with TypeScript (or ES6). It provides a lot of decorators and guideline to make your code more readable and less error-prone. ⭐️ Star to support our work!
https://tsed.io/
MIT License
2.86k stars 284 forks source link

Support for computed fields in prisma plugin? #2808

Open shivanshtalwar0 opened 2 months ago

shivanshtalwar0 commented 2 months ago

Is your feature request related to a problem? Please describe.

currently it seems computed fields are not supported in tsed/prisma out of the box

Describe the solution you'd like

according to https://www.prisma.io/docs/orm/prisma-client/queries/computed-fields i believe it should be straightforward to add the support

Describe alternatives you've considered

No response

Additional context

No response

Acceptance criteria

No response

Romakita commented 2 months ago

I don’t see how we can reflect that with the generator. Tsed use the schema to generate field and computed fields is declared outside of the schema.

see you

shivanshtalwar0 commented 2 months ago

currently i do this

    async $onReady(): Promise<any> {
        this.extend<VideoCallModel>("videoCall", {
            duration: {
                needs: { acceptedAt: true, endedAt: true },
                compute(item) {
                    if (item.acceptedAt && item.endedAt) {
                        return (item.endedAt.getTime() - item.acceptedAt.getTime())/1000
                    }
                    return null
                },
            },
        })
    }
@Service()
export class BaseService<T> implements OnInit {
    private prismaService: PrismaService

    extend<T>(model: string, computedFields: Record<string, {
        needs: Partial<Record<keyof T, boolean>>
        compute: (model: T) => any
    }>) {
        const data = this.prismaService.$extends({
            result: {
                [model]: computedFields as any
            }
        })
        this.repositoryContainer = data[model]
    }

}

maybe some other solution that we can build on top of it?

Romakita commented 2 months ago

Hello @shivanshtalwar0

I have no idea to simplify that. Excepted through a configuration.

@Configuration({

   prisma: {
      extends: {
         "videoCall": {
          duration: {
                needs: { acceptedAt: true, endedAt: true },
                compute(item) {
                    if (item.acceptedAt && item.endedAt) {
                        return (item.endedAt.getTime() - item.acceptedAt.getTime())/1000
                    }
                    return null
                },
               },
          }
      }
   }
}

or using DI container:

import {extends} from "@tsed/prisma";

// maybe instead videoCall we can give VideoCallModel and retrieve the collection name

extends("videoCall", {
          duration: {
                needs: { acceptedAt: true, endedAt: true },
                compute(item) {
                    if (item.acceptedAt && item.endedAt) {
                        return (item.endedAt.getTime() - item.acceptedAt.getTime())/1000
                    }
                    return null
                },
               },
          }
})

but VideoCallModel.duration be generated by typescript so type checking won't works. Prisma schema is the source of truth and there is no way to generate computed properties from schema. So adding this will only create problems if the typings are not correct.

shivanshtalwar0 commented 2 months ago

Thanks for getting back to this issue, yeah if there will be no types on prisma model on typescript it defeats the whole point of typescript (doing "any" forcefully to read properties from model) but with my implementation typing works but it isn't a standard solution but again it will not have support for any of tsed model decorators so i guess i will have to do the ugly way only Therefore you can close it or wait until you think you get any eureka moment ^^

shivanshtalwar0 commented 2 months ago

On second thoughts how about if we can solve it using special service like i did with support for tsed decorators for model? Lmk what u think

Romakita commented 2 months ago

On second thoughts how about if we can solve it using special service like i did with support for tsed decorators for model? Lmk what u think

I don't see what you mean. Please write a short (hypothetical) code you have in mind.

shivanshtalwar0 commented 2 months ago

See the current implementation that i shared above something like that but somehow make it generic so users wanting to have computed field support should strictly resort to this service for a specific model needs be it fetching data updating data in database for example a service like PrismaComputedService which users can inject into controllers ot elsewhere and define computed fields programmatically for any model having single instance of this service like i did since with my implementation typing works fine

shivanshtalwar commented 2 months ago

something like this not sure how easy or hard it is

import {PrismaComputedService} from '@prisma/core'
@Controller()
class MyComputedTester implements OnReady{
async $OnReady(){
this.pcs.extend<VideoCallModel>("videoCall", {
            duration: {
                needs: { acceptedAt: true, endedAt: true },
                compute(item) {
                    if (item.acceptedAt && item.endedAt) {
                        return (item.endedAt.getTime() - item.acceptedAt.getTime())/1000
                    }
                    return null
                },
            },
        })
}
@Inject()
pcs:PrismaComputedService

@Get()
getAll(){
return this.pcs.VideoCallRepository.find()
}

}