test-data icon indicating copy to clipboard operation
test-data copied to clipboard

[Idea] Opinionated (graphql) transformer

Open emmenko opened this issue 5 years ago • 3 comments

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

At the moment 98% of our graphql transformers use the addFields to set the __typename.

Describe the solution you'd like

Maybe we should provide a preconfigured graphql transformer that always sets the __typename.

For example:

Transformer.graphql('Author')

Transformer.graphql('Author', {
  buildFields: ['books'],
})

Describe alternatives you've considered

Instead of Transformer.graphql, we can have a GraphqlTransformer.

emmenko avatar Nov 02 '20 14:11 emmenko

@pa3 @tdeekens any thoughts about this?

emmenko avatar Nov 02 '20 14:11 emmenko

I like the idea but I also like that all transformers have the same API no matter their type 🤔.

One thing, unrelated to this, is that I wonder if we should "auto-build" nested fields if possible. That removes already quite a bit of cruft.

tdeekens avatar Nov 02 '20 15:11 tdeekens

I encountered a use case where having __typename in the generated data is not convenient. It's when generating data related to GraphQL Input Types, such as AttributeGroupDraft which is used as an argument to the createAttributeGroup mutation. Because __typename is not allowed on Input Types, the related mutation will fail. To solve this, __typenames can be stripped from the generated data by transformers.

// Generator
const generator = Generator({
  fields: {
    name: fake(() => LocalizedString.random()),
    description: fake((f) =>
      f.random.arrayElement([null, LocalizedString.random()])
    ),
  },
});

// Transformers
function omitTypename(object) {
  return omitDeep(object, '__typename');
}
const transformers = {
  graphql: Transformer('graphql', {
    replaceFields({ fields }) {
      return {
        ...fields,
        name: buildField(fields.name, 'graphql').map(omitTypename),
        description:
          fields.description &&
          buildField(fields.description, 'graphql').map(omitTypename),
      };
    },
  }),
};

// Usage
const attributeGroupDraft = AttributeGroupDraft.random().buildGraphql();

However, this is not ideal as doing this will become redundant (needs to be done for each generated GraphQL data). One way to solve this is to get inspired by how Apollo GraphQL handles __typename, and provide a new addTypename option to the buildGraphql builder, with a default value of true. This way, the consumer can correctly generate input types by setting the option value to false.

// Generator
const generator = Generator({
  fields: {
    name: fake(() => LocalizedString.random()),
    description: fake((f) =>
      f.random.arrayElement([null, LocalizedString.random()])
    ),
  },
});

// Transformers
-function omitTypename(object) {
-  return omitDeep(object, '__typename');
-}
const transformers = {
  graphql: Transformer('graphql', {
-    replaceFields({ fields }) {
-      return {
-        ...fields,
-        name: buildField(fields.name, 'graphql').map(omitTypename),
-        description:
-          fields.description &&
-          buildField(fields.description, 'graphql').map(omitTypename),
-      };
-    },
+    buildFields: ['name', 'description'],
  }),
};

// Usage
- const attributeGroupDraft = AttributeGroupDraft.random().buildGraphql();
+ const attributeGroupDraft = AttributeGroupDraft.random().buildGraphql({ addTypename: false });

ahmehri avatar Jun 10 '22 15:06 ahmehri