ardeois / graphql-codegen-typescript-mock-data

[GraphQL Codegen Plugin](https://github.com/dotansimha/graphql-code-generator) for building mock data based on the schema.
MIT License
132 stars 47 forks source link

Add per field generation #106

Closed emab closed 1 year ago

emab commented 1 year ago

This PR adds something that would come in super handy for a project I'm working on - in summary:

Along with adding the above functionality, I've added some tests to verify the behaviour. I'm not entirely sure how much this will affect performance - I've run it against a schema of around 2000 lines on a laptop and the time went from ~19 seconds to ~22 seconds for a full generation (starting from zero files, generating type file then generating mock data file).

I'll go through each step and explain a bit more about why I think it's useful:

Providing generator for a specific field

Currently you can provide generators to scalars only. This is fine - however I'm not sure how scalable it is to provide a scalar for values that you want to generate a more specific value for. Take for example a field named email. It's safe to assume you'll always want an email here - so instead of it being a String which gets a single word generated, you can specify that an email is generated instead.

This PR allows you to specify a generator for a field with a given name using the following:

plugins:
  - typescript-mock-data:
      scalars:
        Date: date.future
      fieldGeneration:
        _all:
          email: internet.email

This would generate an email for any field called email.

If you want to be more specific, you can provide the typeName of a given field. For example you might not want all startDate fields to give a date in the future. If you have a type User and wanted to give a specific generator to the startDate field you can do the following:

plugins:
  - typescript-mock-data:
      scalars:
        Date: date.future
      fieldGeneration:
        User:
            startDate: date.past

It gives finer control over generated values, whilst still allowing you to define scalar generation if you don't want to provide individual generation options.

Passing arguments to the generator

We have the ability to do this in the scalar map. The same type has been used again so that the user can provide any number of arguments to the function.

Call another function (with arguments) on result

For me this was necessary when I needed to generate a date in a specific format. Casual does a good job of this by allowing you to pass format - however the faker library has more options but spits out a Date object. Being able to call toISOString() is really useful in this situation.

Provide locale

This allows you to change the output of faker so that zipCode can output a UK postcode - something that's useful for projects based in the UK.

Example configuration


const config = {
  // ...
  generateLibrary: "faker",
  locale: "en_GB",
  fieldGeneration: {
    _all: {
      dataId: "database.mongodbObjectId",
      addressLine1: "address.buildingNumber",
      addressLine2: "address.street",
      postcode: "address.zipCode",
      stateProvinceCounty: "address.county",
      date: {
        generator: "date.future",
        arguments: [10],
        extra: {
          function: "toISOString",
          arguments: [],
      },
    },
    UKUser: {
      city: "address.city",
      email: "internet.email",
  },
}
emab commented 1 year ago

Having discussed this with people - something that came up that may be more useful is a mapping of TypeName -> FieldName -> Custom generator to provide even more granular control over generation. This assumes all fields with the same name will have the same generation, which is fine for some cases but not all.

emab commented 1 year ago

Having discussed this with people - something that came up that may be more useful is a mapping of TypeName -> FieldName -> Custom generator to provide even more granular control over generation. This assumes all fields with the same name will have the same generation, which is fine for some cases but not all.

Have implemented this in the PR

EtienneExalgo commented 1 year ago

Very good idea ! I was looking for a solution to mock fields individually and I was supprised not to be able to do it easilly. Will try this PR

emab commented 1 year ago

Very good idea ! I was looking for a solution to mock fields individually and I was supprised not to be able to do it easilly.

Will try this PR

Let me know how it goes and if you encounter any issues! Happy to modify it further if needed.

EtienneExalgo commented 1 year ago

Very good idea ! I was looking for a solution to mock fields individually and I was supprised not to be able to do it easilly. Will try this PR

Let me know how it goes and if you encounter any issues! Happy to modify it further if needed.

Well it worked well. This feature is very usefull to me. Thanks.

ardeois commented 1 year ago

@emab I did some changes on your PR:

Thanks again for your contribution!

ardeois commented 1 year ago

Available in version 3.1.0