MichalLytek / typegraphql-prisma

Prisma generator to emit TypeGraphQL types and CRUD resolvers from your Prisma schema
https://prisma.typegraphql.com
MIT License
891 stars 113 forks source link

Upgrading to Prisma 5.0.0 and typegraphql-prisma 0.27.0 breaking _count field resolver. New required fields on "ModelCount" class #408

Closed erikmellum closed 1 year ago

erikmellum commented 1 year ago

Describe the Bug Each relation on autogenerated ActivityCount class has new fields that are required, ex. getChannel_types

Prisma 5.0.0, typegraphql-prisma 0.27.0 Generated Type for ActivityCount:

import { ActivityCountAgent_states_mappingArgs } from "./args/ActivityCountAgent_states_mappingArgs";
import { ActivityCountChannel_typesArgs } from "./args/ActivityCountChannel_typesArgs";
import { ActivityCountDefault_shift_activityArgs } from "./args/ActivityCountDefault_shift_activityArgs";
import { ActivityCountDefault_shift_activity_scheduleArgs } from "./args/ActivityCountDefault_shift_activity_scheduleArgs";
import { ActivityCountSchedule_activitiesArgs } from "./args/ActivityCountSchedule_activitiesArgs";
import { ActivityCountShift_day_activitiesArgs } from "./args/ActivityCountShift_day_activitiesArgs";
import { ActivityCountTime_off_requestsArgs } from "./args/ActivityCountTime_off_requestsArgs";
export declare class ActivityCount {
    channel_types: number;
    schedule_activities: number;
    agent_states_mapping: number;
    shift_day_activities: number;
    default_shift_activity: number;
    default_shift_activity_schedule: number;
    time_off_requests: number;
    getChannel_types(root: ActivityCount, args: ActivityCountChannel_typesArgs): number;
    getSchedule_activities(root: ActivityCount, args: ActivityCountSchedule_activitiesArgs): number;
    getAgent_states_mapping(root: ActivityCount, args: ActivityCountAgent_states_mappingArgs): number;
    getShift_day_activities(root: ActivityCount, args: ActivityCountShift_day_activitiesArgs): number;
    getDefault_shift_activity(root: ActivityCount, args: ActivityCountDefault_shift_activityArgs): number;
    getDefault_shift_activity_schedule(root: ActivityCount, args: ActivityCountDefault_shift_activity_scheduleArgs): number;
    getTime_off_requests(root: ActivityCount, args: ActivityCountTime_off_requestsArgs): number;
}

Prisma 4.14.1, typegraphql-prisma 0.25.1 Generated Type for ActivityCount:

export declare class ActivityCount {
    channel_types: number;
    schedule_activities: number;
    agent_states_mapping: number;
    shift_day_activities: number;
    default_shift_activity: number;
    default_shift_activity_schedule: number;
    time_off_requests: number;
}

Relations in the ActivityModel now require a get method in the ActivityCount class

model Activity {
  /// @TypeGraphQL.omit(input: ["create", "update"])
  activity_id                String        @id @default(cuid()) @db.VarChar(30)
  /// @TypeGraphQL.omit(output: true)
  account                    Account       @relation(fields: [account_id], references: [id])
  /// @TypeGraphQL.omit(output: true, input: true)
  account_id                 String        @default("")
  name                       String        @default("")
  text_color                 String        @default("")
  bg_color                   String        @default("")
  activity_type              ActivityType  @relation(fields: [activity_type_id], references: [activity_type_id])
  activity_type_id           String        @db.VarChar(30)
  channel_types              ChannelType[]
  default_duration           Int           @default(0) // In minutes
  should_calculate_adherence Boolean       @default(false)
  is_paid                    Boolean       @default(false)
  allow_for_time_off         Boolean       @default(false)
  /// @TypeGraphQL.omit(output: true)
  is_deleted                 Boolean       @default(false)
  /// @TypeGraphQL.omit(input: ["create", "update"])
  create_time                DateTime      @default(now())
  /// @TypeGraphQL.omit(input: ["create", "update"])
  create_by                  String        @default("")
  /// @TypeGraphQL.omit(input: ["create", "update"])
  modify_time                DateTime      @updatedAt
  /// @TypeGraphQL.omit(input: ["create", "update"])
  modify_by                  String        @default("")
  /// @TypeGraphQL.omit(output: true)
  modify                     User?         @relation("ModifyActivities", fields: [modify_by], references: [user_id])
  create                     User?         @relation("CreateActivities", fields: [create_by], references: [user_id])

  schedule_activities             ScheduleActivity[]
  agent_states_mapping            AgentStateActivityMapping[]
  shift_day_activities            ShiftDayActivity[]
  default_shift_activity          Shift[]                     @relation("DefaultShiftActivity")
  default_shift_activity_schedule ScheduleDay[]               @relation("DefaultShiftActivitySchedule")
  time_off_requests               TimeOffRequest[]

  @@index([activity_type_id], map: "i_activity_type_id")
  @@index([account_id], map: "i_account_id")
  @@map("wfm_activities")
}

_count field resolver now has an invalid type in prisma 5.0.0 and typegraphql-prisma 0.27.0 image

To Reproduce

When we upgraded from:

Prisma 4.14.1 and typegraphql-prisma 0.25.1 to

Prisma 5.0.0 and typegraphql-prisma 0.27.0

We began seeing new fields in the "ActivityCount" resolver. This is also happening in other "ModelCount" generated classes.

As a result, our _count FieldResolvers no longer work

Expected Behavior

I expected the ActivityCount class to have the same properties after upgrading as it did before upgrading. I'm not sure if we have done something to cause this or if it is a legitimate bug due to the upgrade. The only change made in my test was the version of the libraries.

Environment (please complete the following information):

MichalLytek commented 1 year ago

New major release = breaking changes. It was mentioned in the release notes. typegraphql-prisma now emits code for all the Prisma features that were previously hidden behind a preview features flags. I don't plan to do anything about it.

MichalLytek commented 1 year ago

Further explanation:

The need for get is due to the output types for count are now having args (like where):

image

When you check in your generated schema SDL, you will see that. If you reuse that generated types, you also need to figure out how to support it. typegraphql-prisma does a trick with get while the whole logic sits in the main CRUD resolvers body.

erikmellum commented 1 year ago

Thanks for the response @MichalLytek. I understand there are no plans to fix this, but I am still noticing some behaviors that I can't yet explain.

I'll document what we are finding here in case others run into this issue. The autogenerated "Count" types is presenting type errors, even when just returning a result from findMany. I noticed some strange behaviors when spreading the _count property of type "ActivityCount".

Notice here the _count has the correct type: "ActivityCount". This type does include the necessary get functions:

image

ActivityCount type def:

import { ActivityCountAgent_states_mappingArgs } from "./args/ActivityCountAgent_states_mappingArgs";
import { ActivityCountChannel_typesArgs } from "./args/ActivityCountChannel_typesArgs";
import { ActivityCountDefault_shift_activityArgs } from "./args/ActivityCountDefault_shift_activityArgs";
import { ActivityCountDefault_shift_activity_scheduleArgs } from "./args/ActivityCountDefault_shift_activity_scheduleArgs";
import { ActivityCountSchedule_activitiesArgs } from "./args/ActivityCountSchedule_activitiesArgs";
import { ActivityCountShift_day_activitiesArgs } from "./args/ActivityCountShift_day_activitiesArgs";
import { ActivityCountTime_off_requestsArgs } from "./args/ActivityCountTime_off_requestsArgs";
export declare class ActivityCount {
    channel_types: number;
    schedule_activities: number;
    agent_states_mapping: number;
    shift_day_activities: number;
    default_shift_activity: number;
    default_shift_activity_schedule: number;
    time_off_requests: number;
    getChannel_types(root: ActivityCount, args: ActivityCountChannel_typesArgs): number;
    getSchedule_activities(root: ActivityCount, args: ActivityCountSchedule_activitiesArgs): number;
    getAgent_states_mapping(root: ActivityCount, args: ActivityCountAgent_states_mappingArgs): number;
    getShift_day_activities(root: ActivityCount, args: ActivityCountShift_day_activitiesArgs): number;
    getDefault_shift_activity(root: ActivityCount, args: ActivityCountDefault_shift_activityArgs): number;
    getDefault_shift_activity_schedule(root: ActivityCount, args: ActivityCountDefault_shift_activity_scheduleArgs): number;
    getTime_off_requests(root: ActivityCount, args: ActivityCountTime_off_requestsArgs): number;
}

After the spread operation we have lost all of our get* functions (not sure how this happens yet).

image

As a result of losing our get functions when spreading, we get a type error on the return here:

image

Adding as ActivityCount bandages over the type error:

image

But the plot thickens - the same type error is present in the response from prisma.findMany

image

image

findManyActivity implementation is calling prisma.findMany

image

Drop as Activity[] on the result of the findMany, type error goes away

image

image

One of the challenges this presents for us is the number of models we have that use _count and the autogenerated type for _count. Maintaining types for all of these _count results would not be trivial, but a bigger concern at this point is the issue with the type response from findMany

MichalLytek commented 1 year ago

The generated XYZCount type should not be used manually, as it's only faking the argument. Just write your own version and use with the previous code you had.

erikmellum commented 1 year ago

@MichalLytek thank you for the suggestion to write our own version, happy to do that. Is the intent to no longer use the XYZModel type manually as well?

The autogenerated XYZModel is also no longer useable when using include: { _count: true } in prisma calls such as findMany or create. Is it at all possible to make the get* props optional when the XYZCount is generated?

I did create my own XYZCount class manually, it just didn't fix the issue since the return of findMany call no longer matches XYZModel due to it referencing XYZCount.

Similar to above, I'm happy to write our own XYZModel type as well if that is the intent. Perhaps we misunderstood the use case?

See the example findMany and type definition below:

They key detail is that using the Activity from @generated/type-graphql is no longer valid as a return type. This also affects returns from prisma.xyzModel.create

import { Activity, ActivityType, ChannelType } from '@generated/type-graphql'

@Authorized(supervisorEdit, adminEdit)
  @Query(() => [Activity]) // Using Activity from @generated/type-graphql is no longer valid
  async findManyActivity(
    @Ctx() { request, activityService, prisma }: Context,
    @Args(() => FindManyActivityArgs)
    data: FindManyActivityArgs
  ): Promise<Activity[]> {
    const { accountId } = getAccountIdAndUserIdFromToken(request)
    const orderBy = activityService.getActivityOrderBy(data.sortColumn, data.sortOrder)
    return await prisma.activity.findMany({
      ...activityService.getFindManyActivityWhereInput(data, accountId),
      take: data.take,
      skip: data.skip,
      orderBy,
      include: {
        _count: true,
      },
    })
  }

Autogenerated XYZModel Example:

  import { Account } from "../models/Account";
import { ActivityType } from "../models/ActivityType";
import { AgentStateActivityMapping } from "../models/AgentStateActivityMapping";
import { ChannelType } from "../models/ChannelType";
import { ScheduleActivity } from "../models/ScheduleActivity";
import { ScheduleDay } from "../models/ScheduleDay";
import { Shift } from "../models/Shift";
import { ShiftDayActivity } from "../models/ShiftDayActivity";
import { TimeOffRequest } from "../models/TimeOffRequest";
import { User } from "../models/User";
import { ActivityCount } from "../resolvers/outputs/ActivityCount";
export declare class Activity {
    activity_id: string;
    account?: Account;
    account_id?: string;
    name: string;
    text_color: string;
    bg_color: string;
    activity_type?: ActivityType;
    activity_type_id: string;
    channel_types?: ChannelType[];
    default_duration: number;
    should_calculate_adherence: boolean;
    is_paid: boolean;
    allow_for_time_off: boolean;
    is_deleted?: boolean;
    create_time: Date;
    create_by: string;
    modify_time: Date;
    modify_by: string;
    modify?: User | null;
    create?: User | null;
    schedule_activities?: ScheduleActivity[];
    agent_states_mapping?: AgentStateActivityMapping[];
    shift_day_activities?: ShiftDayActivity[];
    default_shift_activity?: Shift[];
    default_shift_activity_schedule?: ScheduleDay[];
    time_off_requests?: TimeOffRequest[];
    _count?: ActivityCount | null;
}

Autogenerated XYZCount

import { ActivityCountAgent_states_mappingArgs } from "./args/ActivityCountAgent_states_mappingArgs";
import { ActivityCountChannel_typesArgs } from "./args/ActivityCountChannel_typesArgs";
import { ActivityCountDefault_shift_activityArgs } from "./args/ActivityCountDefault_shift_activityArgs";
import { ActivityCountDefault_shift_activity_scheduleArgs } from "./args/ActivityCountDefault_shift_activity_scheduleArgs";
import { ActivityCountSchedule_activitiesArgs } from "./args/ActivityCountSchedule_activitiesArgs";
import { ActivityCountShift_day_activitiesArgs } from "./args/ActivityCountShift_day_activitiesArgs";
import { ActivityCountTime_off_requestsArgs } from "./args/ActivityCountTime_off_requestsArgs";
export declare class ActivityCount {
    channel_types: number;
    schedule_activities: number;
    agent_states_mapping: number;
    shift_day_activities: number;
    default_shift_activity: number;
    default_shift_activity_schedule: number;
    time_off_requests: number;
    getChannel_types(root: ActivityCount, args: ActivityCountChannel_typesArgs): number;
    getSchedule_activities(root: ActivityCount, args: ActivityCountSchedule_activitiesArgs): number;
    getAgent_states_mapping(root: ActivityCount, args: ActivityCountAgent_states_mappingArgs): number;
    getShift_day_activities(root: ActivityCount, args: ActivityCountShift_day_activitiesArgs): number;
    getDefault_shift_activity(root: ActivityCount, args: ActivityCountDefault_shift_activityArgs): number;
    getDefault_shift_activity_schedule(root: ActivityCount, args: ActivityCountDefault_shift_activity_scheduleArgs): number;
    getTime_off_requests(root: ActivityCount, args: ActivityCountTime_off_requestsArgs): number;
}

Error:

image