Git Product home page Git Product logo

graphql-codegen-factories's Introduction

graphql-codegen-factories

graphql-codegen-factories is a plugin for GraphQL Code Generator that generates factories from a GraphQL schema and operations. The factories can then be used to mock data, e.g for testing or seeding a database.

For example, given this GraphQL schema:

type User {
  id: ID!
  username: String!
}

The following factory will be generated:

export type User = /* generated by @graphql-codegen/typescript */;

export function createUserMock(props: Partial<User> = {}): User {
  return {
    __typename: "User",
    id: "",
    username: "",
    ...props,
  };
}

It is also possible to generate factories from an operation, for example:

query GetUser {
  user {
    id
    username
  }
}

Will result in the following factories:

export type GetUserQuery = /* generated by @graphql-codegen/typescript-operations */;

export function createGetUserQueryMock(props: Partial<GetUserQuery> = {}): GetUserQuery {
  return {
    __typename: "Query",
    user: createGetUserQueryMock_user({}),
    ...props,
  };
}

export function createGetUserQueryMock_user(props: Partial<GetUserQuery["user"]> = {}): GetUserQuery["user"] {
  return {
    __typename: "User",
    id: "",
    username: "",
    ...props,
  };
}

You can also use a fake data generator to generate realistic factories such as:

import { faker } from "@faker-js/faker";

export function createUserMock(props: Partial<User> = {}): User {
  return {
    __typename: "User",
    id: faker.random.alphaNumeric(16),
    username: faker.lorem.word(),
    ...props,
  };
}

Showcase

Are you using this plugin? Let us know!

Contributors

graphql-codegen-factories's People

Contributors

dependabot[bot] avatar ertrzyiks avatar jongbelegen avatar zhouzi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

graphql-codegen-factories's Issues

Union factory has type error in certain scenarios

When generating factories based on GitHub's GraphQL schema, I noticed errors for some union factories, e.g:

export function createBranchActorAllowanceActorMock(
  props: Partial<Types.BranchActorAllowanceActor> = {}
): Types.BranchActorAllowanceActor {
  switch (props.__typename) {
    case "Team":
      return createTeamMock(props);
    case "User":
      return createUserMock(props);
    case undefined:
    default:
      return createBranchActorAllowanceActorMock({
        __typename: "Team",
        ...props,
      });
  }
}

Types of property '__typename' are incompatible.
  Type '"User" | "Team"' is not assignable to type '"User"'.
    Type '"Team"' is not assignable to type '"User"'

It can be fixed this way:

export function createBranchActorAllowanceActorMock(
  props: Partial<Types.BranchActorAllowanceActor> = {}
): Types.BranchActorAllowanceActor {
  switch (props.__typename) {
    case "Team":
      return createTeamMock(props);
    case "User":
      return createUserMock(props);
    case undefined:
    default:
+    const { __typename, ...rest } = props;
      return createBranchActorAllowanceActorMock({
        __typename: "Team",
+       ...rest,
-        ...props,
      });
  }
}

For most them it's fine but sometimes it fails. Not sure why yet.

Setup a realistic demo example

Currently the example is pretty basic and is not a very good representation of what the plugin does and how it can be useful. It would be great to replace it with an actual React application and GraphQL server. This way it could showcase how this plugin can be used and what it is used for. Also, this application would serve as integration tests (we can build it and verify the types).

It should use as many GraphQL features as possible, for example unions, interfaces, fragments, directives, and aliases. It should also show different ways of taking advantage of this plugin, for example by seeding a database (probably in-memory, a real database would be overkill and a pain to setup in StackBlitz) and by mocking data in tests (backend and frontend).

Document factories with JSDoc

The factories could be decorated with JSDoc based on the schema's documentation. This way it would be pulled by IDEs and could provide a great DX.

Warn when a factory would lead to an infinite loop

Let's say I have a GraphQL schema like below:

type ProductCategory {
  parent_category: ProductCategory
}

And this generates mock function like below:

export function createProductCategoryMock(props: Partial<ProductCategory> = {}): ProductCategory {
  return {
    parent_category: createProductCategoryMock({}),
    ...props,
  }
}

And this cause Maximum call stack size exceeded error. Do I just need to change the parent type different name? Any thoughts? Thanks.

Add example on how to use with faker (or equivalent)

Using this package with faker to generate realistic mocks should be straightforward. But it's such a common use case that I think it would be great to add an example for it.

I have not tried it but it should boil down to:

  1. Use graphql-codegen's add plugin to prepend import { faker } from '@faker-js/faker'; to the generated files.
  2. Define the scalars' defaults to use faker:
    scalarDefaults:
      ID: faker.random.alphaNumeric(16)
      Email: faker.internet.email()

Support nonOptionalTypename: false

By default the __typename field is added to the queries as optional. It is not compatible with extraction code that is currently used for example Extract<BlogListQueryQuery, { __typename: 'Query' }>

I had to enable my codegen config:

config:
  nonOptionalTypename: true

I suggest the plugin either to recommend using nonOptionalTypename: true or support typename field being optional.

schemaFactoriesPath should not resolve to local path when it starts with a tilde

Currently schemaFactoriesPath automatically adds ./ at its start when it doesn't start with a .. It makes it impossible to reference a node module. near-operation-file-preset makes it possible to do so by prepending a tilde:

If you wish to use an NPM package or a local workspace package, make sure to prefix the package name with ~.

It should also work with factories that are not generated in the same directory as the schema factories. So if the path starts with a ~, that character should be removed and not resolved locally.

Point to another generated file type?

I generated my typescript types in one file, but I want to generate these factories in a different file. Is it possible to add a config to define where to import these types from?

generates:
  ./src/testing/__generated__/factories.ts:
    plugins:
      - graphql-codegen-factories
    config:
      factoryName: create{Type}
  ./src/graphql/types/__generated__/type.ts:
    plugins:
      - typescript
    config:
      scalars:
        DateTime: string

Doing the above generates the functions fine, but TS complains because it can't find the type.
Screen Shot 2022-02-21 at 6 03 36 PM

Add a readme to the examples and list them on the website

It would be great to add a readme to the examples to explain how they work. These readmes could then be automatically copied to a docs/docs/examples/ folder when building the website. They could serve as a guide for advanced features such as #41

Generate factories for the fragments

Currently, the operations plugin doesn't generate factories for fragments. Instead, it inlines the code every time the fragment is used. It could be great to generate factories for the fragments and use them where they appear (instead of inlining them and duplicating the code).

There are a few things to keep in mind, though:

  • Not sure if it should generate factories for external fragments (we should have a look at what typescript-operations does, it it generates types for them or not).
  • Not sure if we can end up in a situation where a fragment has to be imported (because of external fragments or else).

Nested factories for the operations plugin

In the example from readme I noticed that the main plugin is based on the nested factories. Factory for one type reuses the factory for another type.

export function createUserMock(props: Partial<User>): User {
  return {
    __typename: "User",
    companion: createDogMock({}),
    id: "",
    role: UserRole.Admin,
    ...props,
  };
}

The operation plugins however returns just a minimal object that matches the query response interface. It has limited value as to provide data for different scenarios we still need to write huge part of the responses by hand. The idea from the main plugin applied to the operations plugin would be a game changer in the graphql mocking in my opinion.

My current output:

export type LoadCategoriesQuery = {
  __typename?: "Query",
  allRecipeCategory: {
    __typename?: "RecipeCategoryConnection",
    edges: Array<{
      __typename?: "RecipeCategoryEdge",
      node: {
        __typename?: "RecipeCategory",
        name?: string | null,
        slug?: string | null,
      },
    }>,
  },
};


export function createLoadCategoriesQueryMock(
  props: Partial<LoadCategoriesQuery>
): LoadCategoriesQuery {
  return {
    allRecipeCategory: {
      __typename: "RecipeCategoryConnection",
      edges: [],
    },

    ...props,
  };
}

To provide a fake response with a single edge I still have plenty of code to write. It would be awesome to additionally have a few more factories for the collections:

export function createLoadCategoriesQueryRecipeCategoryEdgeMock(){  return ({ 
  __typename: 'RecipeCategoryEdge',
  node: {
    __typename: 'RecipeCategory',
    name: '',
    slug: ''
  }
}) }

Operations plugin generates a single item for array types

Using the config like

./src/:
    preset: near-operation-file
    presetConfig:
      extension: .generated.ts
      baseTypesPath: types.ts
    plugins:
      - typescript-operations
      - graphql-codegen-factories/operations

When a selection references an array, the factory returns a single element.

When the

query { 
  allStrapiRecipe {
    edges { # this is an array
      node {
        ...fields
      }
    }
  }
}

Expected

{
  allStrapiRecipe: {
    __typename: 'StrapiRecipeConnection',
    edges: [{
      __typename: 'StrapiRecipeEdge',
      node: {
        __typename: 'StrapiRecipe',
      },
    }]
  },
}  

Actual

{
  allStrapiRecipe: {
    __typename: 'StrapiRecipeConnection',
    edges: {
      __typename: 'StrapiRecipeEdge',
      node: {
        __typename: 'StrapiRecipe',
      },
    },
  }
}  

Tested with yummy-recipes/yummy#58

Add support for interfaces

Given the following schema:

interface Humanoid {
  legs: Int!
}

type Human implements Humanoid {
  name: String!
  legs: Int!
}

type Droid implements Humanoid {
  id: ID!
  legs: int!
  owner: Humanoid!
}

The default value for Droid's owner should not be createHumanoidMock({}) but createHumanMock({}) or createDroidMock({}) (picking the first type that implements the interface is probably reasonable).

Use a better support for the documentation

The documentation started simple but it got richer. For example, the difference between the two plugins is not super clear. And the operations plugin might have some specific configuration really soon (e.g for #31). I feel like using something such as Docusaurus could help to structure it better with an introduction, a getting started page, some guides and the reference.

Operations plugin doesn't work with unnamed queries

Using the config like

./src/:
    preset: near-operation-file
    presetConfig:
      extension: .generated.ts
      baseTypesPath: types.ts
    plugins:
      - typescript-operations
      - graphql-codegen-factories/operations

and the operation without a name

{ field }

the generated code refers to not existing type Query

export type Unnamed_1_Query = ....

export function createQueryMock(props: Partial<Query>): Query {

It only works with the named query

query getField { field }

Tested with yummy-recipes/yummy#58

Accept nested partials

Another DX improvement idea, what I have in the code now is similar to this:

createBlogListQueryQueryMock_node({
  parsedHeadline: createBlogListQueryQueryMock_parsedHeadline({
    childMarkdownRemark: createBlogListQueryQueryMock_parsedHeadline_childMarkdownRemark({
      html: 'This is pizza',
    })
  })
})

What would be more convenient and much less verbose is if the factory function accepted a nested partial param, like

createBlogListQueryQueryMock_node({
  parsedHeadline: {
    childMarkdownRemark: {
      html: 'This is pizza',
    }
  }
})

but currently it requires me to pass all the data necessary for the operation mock. I would need to add missing __typename and all other fields that are selected even if the example/story/test doesn't care about them.

The factories are already kinda recursive, it shouldn't require drastic changes.

Tricky cases that I anticipate:

  • arrays, could be ignore and keep them full-speced
  • unions, __typename would need to be required

Operations plugin does not include fields from fragments

Hey, thanks a lot for the latest updates!

Here is my config

overwrite: true
schema: "graphql/schema.json"
documents:
  - "src/**/*.{js,jsx}"
  - ./node_modules/gatsby*/!(node_modules)/**/*.js
generates:
  ./src/types.ts:
    plugins:
      - typescript
  ./src/:
    preset: near-operation-file
    presetConfig:
      extension: .generated.ts
      baseTypesPath: types.ts
    plugins:
      - typescript-operations
      - graphql-codegen-factories/operations

and I installed

"graphql-codegen-factories": "^1.0.0-beta.2",

The queries that are include fragments generate the function that misses the fields from the fragment.

#import "../fragments/myData.gql"
query GetMyData {
  data {
    id
    ...MyData
  }
}

It generates an object with just data: { id } not matter what is selected by the fragment.

Make the overrides optional

Currently the overrides props param is required

export function createQueryMock(props: Partial<Query>)

which means that even if we don't want any specific field value to be override we still need to pass an empty object

createBlogListQueryQueryMock() // invalid
createBlogListQueryQueryMock({}) // valid

I suggest to add a default param for convenience:

export function createQueryMock(props: Partial<Query> = {})

compatability with `add` plugin

When running the plugin with typesPath config, i.e. generating to a separate file than the types, and the add plugin (https://the-guild.dev/graphql/codegen/plugins/other/add) the added lines gets added after the import statement.

I get this:

import * as Types from 'libs/some-path-to-the-types-file';


/* eslint-disable no-prototype-builtins, @typescript-eslint/no-non-null-assertion */
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries

but would expect this:

/* eslint-disable no-prototype-builtins, @typescript-eslint/no-non-null-assertion */
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import * as Types from 'libs/some-path-to-the-types-file';

Directives results in [object Object]

When the schema defines a directive

directive @a on FIELD_DEFINITION

the factory includes invalid JS

import * as Types from './types';

[object Object]

Omit optional properties by default?

From #63 (review):

What do you think about not adding the optional properties at all by default? By default the TypeScript plugins set the properties to title?: Maybe<string> and have an option "avoidOptionals" to remove the ?. So I feel like not adding the optional properties unless "avoidOptionals" is set to true would be closer to the TypeScript plugins' defaults and avoid issues similar to the one you faced.

Naming issues with custom Query root type

I think there is a issue when the __typename doesnt follow the "Query" or "Mutation" pattern

Testing the following query (querying through hasura)

query Schedules {
  schedules {
    id
    start_hour
  }
}

typescript-operations generates for me:

export type SchedulesQuery = {
  __typename?: 'query_root',
  schedules: Array<{ __typename?: 'schedules', id: number, start_hour: string }>
}

What graphql-codegen-factories/operations generates for me is:

export function createSchedulesquery_RootMock(props: Partial<Schedulesquery_Root> = {}): Schedulesquery_Root {
  return {
    __typename: "query_root",
    schedules: [],
    ...props,
  };
}

The expected output here should be:

export function createSchedulesQueryMock(props: Partial<SchedulesQuery> = {}): SchedulesQuery {
  return {
    __typename: "query_root",
    schedules: [],
    ...props,
  };
}

Here is what I'm using for my codegen.ts file:

import { CodegenConfig } from '@graphql-codegen/cli'

const config: CodegenConfig = {
  schema: 'http://.....',
  documents: ['src/**/*.ts'],
  ignoreNoDocuments: true, // for better experience with the watcher
  generates: {
    './src/_generated_gql/': {
      preset: 'client',
      plugins: [
        'graphql-codegen-factories/schema',
        'graphql-codegen-factories/operations',
      ],
    },
  },
}

export default config

Problème avec les valeurs par défaut de type enum contenant des underscore

J'ai défini un type enum de la façon suivante:

enum Status {
  TO_BE_VERIFIED
  VERIFIED
}

puis un type contenant ce champ status

type Company {
  status: Status
}

Le mock généré est

export function createCompanyMock(props: Partial<Company>): Company {
  return {
    status: Status.To_Be_Verified // error, should be Status.ToBeVerified
  }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.