Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.0] Add basic Contentful source support #1084

Merged
merged 8 commits into from
Jun 3, 2017
Merged

[1.0] Add basic Contentful source support #1084

merged 8 commits into from
Jun 3, 2017

Conversation

mericsson
Copy link
Contributor

Includes:

  • /packages/gatsby-source-contentful which is a prototype level new source plugin for pulling data from the Contentful.com CMS.
  • /examples/using-contentful which is an example app leveraging the new plugin and an existing Contentful site. Site can be seen here: https://gatsby-using-contentful.netlify.com/.

Lots of improvements are possible. Contentful seems like a great fit so I imagine this plugin will evolve tremendously.

@gatsbybot
Copy link
Collaborator

gatsbybot commented Jun 1, 2017

Deploy preview ready!

Built with commit dcc986f

https://deploy-preview-1084--using-drupal.netlify.com

@KyleAMathews
Copy link
Contributor

KyleAMathews commented Jun 1, 2017

Deploy preview failed.

Built with commit dcc986f

https://app.netlify.com/sites/gatsbygram/deploys/5931ed28a700c47395731b70

parent: contentTypeItemId,
children: [],
name: entryItem.name,
...entryItem.fields,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a little concerned about this. Someone's Contentful entries could have fields that conflict with standard Gatsby Node fields. Thoughts? Should this maybe be put under its own field e.g.

entryFields: entryItem.fields ?

Similar issue exists for assetNode. Today, there is no conflict but if Contentful's data model changes that could be an issue. It was already an issue for contentTypeNode.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think our only choice is to prefix any conflicting fields. And tell people when designing their schemas to avoid conflicting if possible with Gatsby's fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, what do you think I should do here then? Just leave it? Sorry not sure who you mean by 'our' and 'people designing their schemas'. :)

Copy link
Contributor

@KyleAMathews KyleAMathews Jun 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:-)

"our" === "us + anyone else who works on this plugin"
"people designing their schemas" === "users of this plugin who read the README"

What I meant was you'd loop over each field and if there's any field that conflicts with a top-level gatsby node field (id, children, parent, fields, internal) we change it e.g. to contentful_children https://www.gatsbyjs.org/docs/node-interface/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with doing this, but doesn't seem ideal. Doing it this way means the GraphQL interface for between Contentful Entries could be inconsistent from one to another because of a field naming collision. (Also, if in the future there are multiple plugins updating the same nodes, it's possible they can conflict with eachother?)

Maybe every plugin should have their own namespace which is prefixed to the field name. So in this case: contentful_productName

Definitely more verbose.. but would provide consistency going forward. I also accept it could be over-engineering it for now. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain more what the fields field on Node is intended for?

// Reserved for plugins who wish to extend other nodes.
fields: Object,

That seems like a decent place to put all the Contentful specific fields. (But again could run into some collision issues if multiple plugins update the same nodes.. not sure if that is really a consideration at this point)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only one plugin is allowed to create a node of a given type so fields is an escape hatch to allow other plugins to add fields to another node without running into the risk of namespace collisions. Also with fields, again only one plugin can touch a given field.

So you only have to worry about fields from Contentful conflicting with the reserved Gatsby node fields. Which there isn't that many. So I think it'd be fine prefixing just conflicting fields (and perhaps logging a warning to encourage people to use a different name if possible).

@gatsbybot
Copy link
Collaborator

gatsbybot commented Jun 1, 2017

Deploy preview ready!

Built with commit dcc986f

https://deploy-preview-1084--gatsbyjs.netlify.com

// First create nodes for each of the entries of that content type
const entryNodes = entryList[i].items.map((entryItem) => {

const entryNode = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not get to linking the entryNodes as described e.g.
brand___NODE: ID_OF_THE_PRODUCT

Not clear to me if I did that how to test it.

I left a note in the /gatsby-source-contentful/README.md about it as a future improvement, but can tackle it now with some more info.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KyleAMathews
Copy link
Contributor

This is looking awesome! There's been a few API changes but I'll take care of fixing those after it gets in. Glad you're working on this as I have a little Contentful side project I'll be starting on next week :-D

// Touch existing Contentful nodes so Gatsby doesn't garbage collect them.
_.values(store.getState().nodes)
.filter(n => n.internal.type.slice(0, 8) === typePrefix)
.forEach(n => touchNode(n.id))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't what we want while we're not using the sync API. Right now an entry could be deleted from Contentful and not be removed from the Gatsby site. Touching is only necessary if we retrieve from a remote API only the updated data so we "touch" the nodes which haven't updated but we know also weren't deleted. And oh lookie, there's even docs for this now :D https://www.gatsbyjs.org/docs/bound-action-creators/#touchNode

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, makes sense. I'll update this and test by removing some entries in Contentful.

@ivanoats
Copy link
Contributor

ivanoats commented Jun 2, 2017

Nice work @mericsson . Really looking forward to this.

@KyleAMathews
Copy link
Contributor

Oh also please remove the yarn.lock and git ignore it.

@0x80
Copy link
Contributor

0x80 commented Jun 2, 2017

Good work @mericsson

If you find the time, I think it would be super useful if you download the assets and insert them as File nodes, so they can be picked up by Sharp and you have control over image size and format from your code. Currently you can find the image url by id, but you have no control over it.

Maybe this is outside of the scope of what you're trying to accomplish in this PR.

@KyleAMathews
Copy link
Contributor

@0x80 Contentful has an image api that could be used for this https://www.contentful.com/developers/docs/references/images-api/#/reference/image-manipulation I think for really large sites this would be preferable as otherwise each developer on a project would have to download all images for a site which again for larger sites would be considerable. I think @mericsson's plan is to expose the image API as a graphql schema.

It'd be cool to have downloading images as an option though as it'd be more flexible + gatsby-like.

_.values(store.getState().nodes)
.filter(n => n.internal.type.slice(0, 8) === typePrefix)
.forEach(n => touchNode(n.id))
.filter(n => n.internal.type.indexOf(typePrefix) === 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh sorry this isn't needed actually either. Gatsby will "garbage collect" any node on startup that isn't either created or touched. Since you're creating (again) every node then we're fine. If something has been deleted on the Contentful side, than it just won't be created again which means Gatsby will kill it. So if we're not syncing the logic is pretty simple. Just create everything. When syncing then you have to worry about telling Gatsby what is and isn't still around.

@mericsson
Copy link
Contributor Author

mericsson commented Jun 2, 2017

New version of this has been committed. I went ahead and did all the Gatsby node referencing. I hadn't realized, but Contentful includes in its payload reference data for data referenced directly by any given entry. So for example, in Contentful if you specify that your Product entry type has a reference to Category, when getting the Product data via API, you will see the Category data inline in the response. To utilize Gatsby node linking, I deleted that data and just included the reference. Feels a little strange to undo that work that Contenful does for you, but also seems good to follow Gatsby node linking thoroughly.

Contentful will not tell you about reverse links. So in the example above, it is Product referencing Category. Category will not tell you about Products that reference it. So I had to do a little bit of gymnastics to make those links. The finished product feels good and you can see some of the reverse linking in action on a category detail page.

@0x80 Additional image support sounds interesting but outside of the scope of what I want to do in this PR.

AFAIK, all the existing comments have been addressed. Let me know if you see issues w/ latest commits. Thanks! @KyleAMathews

@KyleAMathews
Copy link
Contributor

This looks fantastic @mericsson! Thanks for taking this on!

@KyleAMathews KyleAMathews merged commit 81f2510 into gatsbyjs:1.0 Jun 3, 2017
@0x80
Copy link
Contributor

0x80 commented Jun 3, 2017

@mericsson Awesome!

@KyleAMathews don't know how I overlooked or forgot about their image api but I did 😄

@mericsson mericsson deleted the topic/gatsby-source-contentful branch June 7, 2017 22:06
@jlengstorf
Copy link
Contributor

Hiya @mericsson! 👋

This is definitely late, but on behalf of the entire Gatsby community, I wanted to say thank you for being here.

Gatsby is built by awesome people like you. Let us say “thanks” in two ways:

  1. We’d like to send you some Gatsby swag. As a token of our appreciation, you can go to the Gatsby Swag Store and log in with your GitHub account to get a coupon code good for one free piece of swag. (We’ve got t-shirts and hats, plus some socks that are really razzing our berries right now.)
  2. If you’re not already part of it, we just invited you to join the Gatsby organization on GitHub. This will add you to our team of maintainers. You’ll receive an email shortly asking you to confirm. By joining the team, you’ll be able to label issues, review pull requests, and merge approved pull requests.

If you have questions, please don’t hesitate to reach out to us: tweet at @gatsbyjs and we’ll come a-runnin’.

Thanks again! 💪💜

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants