kontent-ai / gatsby-packages

Monorepo with Gatsby Kontent packages.
https://www.gatsbyjs.org/plugins/?=%40kentico%2Fkontent-
MIT License
33 stars 24 forks source link

Missing data or fields in the GraphQL schema #59

Closed Simply007 closed 4 years ago

Simply007 commented 5 years ago

Brief bug description

The issue is described in https://rshackleton.co.uk/articles/gotchas-and-workarounds-with-gatsby-and-kentico-cloud-part-1

Repro steps

  1. Create a content type with at least one text field
  2. Connect project to Gatsby (DON'T CREATE A CONTENT ITEM BASED ON THIS CONTENT TYPE)
  3. Query the content type
    {
    allKenticoCloudItem<CONTENTTYPECODENAME> {
    edges {
      node {
        system {
          codename
        }
        elements {
          <ELEMENTNAME> {
            value
          }
        }
      }
    }
    }
    }
  4. See error
    {
    "errors": [
    {
      "message": "Cannot query field \"<CONTENTTYPECODENAME>\" on type \"Query\".",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ]
    }
    ]
    }

Expected behavior

The query should return empty data without error.

Gatsby schema customization might solve the issue

GraphQL schema generator might help with implementation: https://github.com/Kentico/kentico-cloud-graphql-schema-generator

Additional context

Twitter feed: https://twitter.com/shackleberry112/status/1132245949491896320 @rshackleton's article with description how to implement the schema customization on the application https://rshackleton.co.uk/articles/learning-about-gatsby-schema-customisation-with-kontent-ai

Simply007 commented 5 years ago

The current workaround is to create a dummy item for all content types with all the fields filled as it is in WordPress: https://github.com/gatsbyjs/gatsby/issues/5296

rshackleton commented 5 years ago

@Simply007 are we in a position to be able to resolve this issue now with the new schema customisation API?

https://github.com/gatsbyjs/gatsby/blob/master/docs/docs/schema-customization.md

Specifically the ability to opt-out of type inference and therefore specify custom types for fields:

https://github.com/gatsbyjs/gatsby/blob/master/docs/docs/schema-customization.md#opting-out-of-type-inference

It seems like this would allow the schema to properly match the Kentico Cloud JS SDK and will avoid the issue of missing fields when data is unavailable.

rshackleton commented 5 years ago

Current Problems

To be honest it feels like a source plugin based on a dynamic schema like Kentico Cloud should opt for Type Builders - https://www.gatsbyjs.org/docs/schema-customization/#gatsby-type-builders

This should allow types to be constructed from scratch. There are many types which should be defined once and shared, for example:

However, probably the worst thing for a consumer of the plugin is that all of these issues are so difficult to fix using the new schema customization as the original type definition produces so many unique types. Currently if we wanted to add a resolver to all Asset fields we would need to manually add a resolver to each unique Asset field by name, e.g.:

These are both Asset fields, from the same content type snippet on two different content models. However they result in two different types being created.

Future Opportunities

Also, on the topic of content type snippets. The schema customization API opens up some serious opportunities to make the schema more composable and usable when querying. For example, if we had two different types that use the same content type snippet there could be an interface defined to make sure these fields are shared:

interface SnippetMetadata {
  metadata__page_title: String!
  metadata__page_description: String!
  metadata__page_keywords: String!
}

type KenticoCloudItemHomePage implements Node & SnippetMetadata {
  metadata__page_title: String!
  metadata__page_description: String!
  metadata__page_keywords: String!
}

type KenticoCloudItemBlogPost implements Node & SnippetMetadata {
  metadata__page_title: String!
  metadata__page_description: String!
  metadata__page_keywords: String!
}

Furthermore, fragments could then be used to make querying types for specific snippets even easier!

{
  fragment SnippetMetadataFragment on SnippetMetadata {
    metadata__page_title
    metadata__page_description
    metadata__page_keywords
  }

  allKenticoCloudItemHomePage {
    edges {
      node {
        elements {
          ...SnippetMetadataFragment
        }
      }
    }
  }

  allKenticoCloudItemBlogPost {
    edges {
      node {
        elements {
          ...SnippetMetadataFragment
        }
      }
    }
  }
}

Note: I've simplified what a field is here, really they would need to share some other complex type such as TextField with the appropriate name, type, value properties.

Summary

I think the next major version (v7?) should be focused on re-architecting the type generation to use the new schema customisation API. The benefits to be gained are enormous, it will make the schema much more sane and predictable and should mean the plugin is subject to fewer bugs and mean consumers can get on with the important things... Like building awesome and fast websites! 😊

Sorry for the wall of text!

rshackleton commented 5 years ago

An update on my previous comment, I've put together a POC for the explicit type generation.

https://github.com/rshackleton/gatsby-site-playground/blob/master/plugins/gatsby-source-kontent-rs/gatsby-node.js

Currently this does the following:

In it's current state the schema displays all possible fields on a particular type, most are defined with basic scalar types as the Element specific types are not yet defined.

image

Also I've not looked at any resolution of linked items or rich text yet.

Anyway, felt this could be a good reference point for this issue to be looked at.

rshackleton commented 5 years ago

Latest updates to my POC have added support for modular_content fields via the @link directive.

type KontentModularContentElement implements KontentElement @infer {
  name: String!
  type: String!
  value: [KontentItem] @link(by: "system.codename")
}

This results in a field that handles null values, always has the KontentItem interface as it's value type and can therefore be spread across to access type-specific fields:

query MyQuery {
  allKontentItemAbout {
    nodes {
      elements {
        sectionsSections {
          value {
            system {
              type
            }
            ... on KontentItemTwinColumnSection {
              id
              elements {
                title {
                  value
                }
              }
            }
          }
        }
      }
    }
  }
}
{
  "data": {
    "allKontentItemAbout": {
      "nodes": [
        {
          "elements": {
            "sectionsSections": {
              "value": null
            }
          }
        },
        {
          "elements": {
            "sectionsSections": {
              "value": [
                {
                  "system": {
                    "type": "twin_column_section"
                  },
                  "id": "97ff5c85-4f37-5666-8852-b54817934702",
                  "elements": {
                    "title": {
                      "value": "The Project"
                    }
                  }
                },
                {
                  "system": {
                    "type": "triple_column"
                  }
                },
                {
                  "system": {
                    "type": "partnership"
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}
Simply007 commented 4 years ago

Update:

hey all - @makma and I are currently working on the schema definition for Kontent Gatsby Source plugin.

See the code in the schema-definition branch.

πŸ‘ πŸ‘ πŸ‘ Thanks @rshackleton! Your showcase was extremely valuable and mentioned implementation is applying most of the suggestions from your showcase!