graphql-code-generator icon indicating copy to clipboard operation
graphql-code-generator copied to clipboard

Generate mock query with `fragment-masking`

Open marromugi opened this issue 2 years ago • 1 comments

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

I want to generate mock data with fragment-masking and msw

related comminity plugin

  • fragment-masking ( https://the-guild.dev/graphql/codegen/plugins/presets/preset-client )
  • typescript-mock-data( https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-mock-data )

Describe the solution you'd like

  1. add type for reveal fragment-masking sample code is here
type FragmentRef = { " $fragmentRefs"?: { [key in string]: any } } | null;

export type FragmentRevealer<T extends FragmentRef> = Omit<T, " $fragmentRefs"> & T extends {
  " $fragmentRefs"?: { [key in string]: infer TFragment };
}
  ? TFragment extends { " $fragmentName"?: string }
    ? Omit<TFragment, " $fragmentName">
    : TFragment
  : T;

export type QueryRevealer<T extends { [key in string]: any }> = {
  [Key in keyof T]: FragmentRevealer<T[Key]>;
};

// query wrapping
export const mockTeamCardQuery = (
  resolver: ResponseResolver<
    GraphQLRequest<TeamCardQueryVariables>,
    GraphQLContext<QueryRevealer<TeamCardQuery>>,
    any
  >,
) => graphql.query<QueryRevealer<TeamCardQuery>, TeamCardQueryVariables>("TeamCard", resolver);


// I can define data without `$fragmentRef`
mockTeamCardQuery((req, res, ctx) => {
          return res(
            ctx.delay(1000),
            ctx.data({
              team: {
                __typename: "Team",
                id: 3,
                uid: "11",
                name: "Team",
              },
              currentUser: {
                __typename: "User",
                name: "User",
              },
            }),
          );
        }),

// if I don't use QueryRevealer, I need to define data like below
mockTeamCardQuery((req, res, ctx) => {
          return res(
            ctx.delay(1000),
            ctx.data({
              team: {
                __typename: "Team",
                id: 3,
                " $fragmentRefs": {
                  TeamCardProfileFragment: {
                    __typename: "Team",
                    id: 1,
                    uid: "abc",
                    name: "TeamName",
                  },
                },
              },
              currentUser: {
                __typename: "User",
                " $fragmentRefs": {
                  TeamCardUserFragment: {
                    __typename: "User",
                    name: "Me",
                  },
                },
              },
            }),
          );
        }),
  1. add fragment-revealer option to typescript-mock-data config add config if you adopt above type QueryRevealer to mockXXXQuerys type or not https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-mock-data

Describe alternatives you've considered

No response

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

Thank you for amazing library!

I want to generate mock data for develop in local env by using msw.

To generate mock data, we can use below library typescript-mock-data. https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-mock-data

But, if I use the library with fragment-masking(including client-preset), I can't cache response because response type is different from defined type created by typescript-mock-data.

Generated mockQuery`s type is below

export type TeamCardProfileFragment = {
  __typename?: "Team";
  id: number;
  uid: string;
  name: string;
} & { " $fragmentName"?: "TeamCardProfileFragment" };

// query type
export type TeamCardQuery = {
  __typename?: "Query";
  team:
    | ({ __typename?: "Team"; id: number } & {
        " $fragmentRefs"?: { TeamCardProfileFragment: TeamCardProfileFragment };
      })
    | null;
};

// query mock
export const mockTeamCardQuery = (
  resolver: ResponseResolver<
    GraphQLRequest<TeamCardQueryVariables>,
    GraphQLContext<TeamCardQuery>,
    any
  >,
) => graphql.query<TeamCardQuery, TeamCardQueryVariables>("TeamCard", resolver);

I can use query mock like below

mockTeamCardQuery((req, res, ctx) => {
          return res(
            ctx.delay(1000),
            ctx.data({
              team: {
                __typename: "Team",
                id: 3,
                " $fragmentRefs": {
                  TeamCardProfileFragment: {
                    __typename: "Team",
                    id: 1,
                    uid: "abc",
                    name: "TeamName",
                  },
                },
              },
            }),
          );
        }),

I can define mock like above, but graphql client ( urql ) doesn't cache mock response because the response is dfferent from expected response. $fragmentRefs is used for improving type strictness on coding, so it is better to exclude $fragmentRefs from mock query's data.

To solve this problem, we need to fix some code in fragment-masking and typescript-mock-data. I understand it might be a bit challenging, but I would appreciate it if you could consider it.

marromugi avatar Sep 27 '23 10:09 marromugi

Did you manage to solve it by any chance ?

bolyesz avatar May 22 '24 15:05 bolyesz