This is a Gatsby version 2.*.* source plugin that feeds the GraphQL tree with Cockpit Headless CMS collections and singletons data.
Actually, it supports querying raw texts (and any trivial field types), Markdown, images, galleries, assets, sets, repeaters, layout(-grid)s (currently only without nested images/assets), objects, linked collections and internationalization.
npm install --save @fika/gatsby-source-cockpit
This project has gatsby-source-filesystem
, gatsby
and react
as peer dependencies, don't forget to install them as well.
npm install --save gatsby-source-filesystem gatsby react
While developing a GatsbyJS source plugin, it is useful to have a GatsbyJS project on the side in order to test it. To use the local plugin in your project instead of the one on NPM, you can run:
// In the plugin's folder
npm link
// In the GatsbyJS project's folder
npm link @fika/gatsby-source-cockpit
You'll have to install the plugin's peer dependencies in the plugin's folder as well (without saving them):
npm install --no-save gatsby react
Then, in order to unlink the local plugin and use the one from NPM again:
npm uninstall --no-save @fika/gatsby-source-cockpit
npm install
Add this to your project gatsby-config.js
file:
plugins: [
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'src',
path: `${__dirname}/src/`,
},
},
{
resolve: '@fika/gatsby-source-cockpit',
options: {
token: 'YOUR_COCKPIT_API_TOKEN',
baseUrl:
'YOUR_COCKPIT_API_BASE_URL', // (1)
locales: ['EVERY_LANGUAGE_KEYS_DEFINED_IN_YOUR_COCKPIT_CONFIGURATION'], // (2)
collections: [], // (3)
singletons: [], // (4)
aliases: {
collection: {
A_COLLECTION_NAME: 'AN_ALIAS',
…
},
singleton: {
A_SINGLETON_NAME: 'AN_ALIAS',
…
}
}, // (5)
brokenImageReplacement: 'AN_URL_TO_AN_IMAGE', // (6)
},
},
]
Notes:
'http://localhost:8080'
.['en', 'fr']
.['Products', 'Menu']
collections
parameter, but for the Cockpit singletons.{ collection: { Team: 'Teams' } }
.null
, the detected broken images will be removed. If an URL to an image, the broken image will be replaced with this image.Adding the gatsby-source-filesystem
dependency to your project grants access to the publicURL
field resolver attribute on the file nodes that this plugin generates by extending the GraphQL type of the file nodes. So, as you can guess, the path specified in the plugin options could be anything, we do not need it to load any local files, we are just taking advantage of its extension of the file node type.
Collections and singletons are converted into nodes. You can access many collection entries at once with this syntax:
(The collection is named 'team' or 'Team' in Cockpit.)
{
allCockpitTeam(filter: { spiritAnimal: { eq: "tiger" } }) { // (1)
edges {
node { // (2)
cockpitId // (3)
cockpitCreated // (3)
cockpitModified // (3)
cockpitBy // (3)
cockpitModifiedBy // (3)
TeamMember1
TeamMember2
TeamMember3
childrenCockpitTeam { ... } // (4)
}
}
}
}
Notes:
_id
), creation and modification dates and authors' (ids for now) that way.Or you can access one entry at the time or a singleton that way:
(The collection is named 'definition' or 'Definition' in Cockpit.)
query($locale: String) { // (1)
cockpitDefinition(cockpitId: { eq: "5bc78a3679ef0740297b4u04" }, lang: { eq: $locale }) { // (2)
header {
type
value
}
}
}
Notes:
query
with a name or not is optional in GraphQL. However, if you want to use variables from your page context, it is mandatory.lang
attribute.(The singleton is named 'vegetable' or 'Vegetable' in Cockpit.)
{
cockpitVegetable(lang: { eq: "en" }) {
category {
type
value
}
}
}
Text fields with the option { "slug": true }
can access the slug that way:
{
allCockpitBlogPost {
edges {
node {
title {
slug
}
}
}
}
}
Collection-Link fields will see their value attribute refering to another or many others collection(s) node(s) (GraphQL foreign key). One to many Collection-Links are only supported for multiple entries of a single collection. This an example with a TeamMember collection entry linked within a Team collection:
{
allCockpitTeam {
edges {
node {
Header {
type
value
}
TeamMember {
type // (1)
value { // (2)
id
Name {
value
}
Task {
value
}
}
}
}
}
}
}
Notes:
'collectionlink'
and it was originally refering to an entry of the TeamMember collection.Image and gallery fields nested within a collection or singleton will be downloaded and will get one or more file(s) node(s) attached under the value
attribute like this:
(You can then access the child(ren) node(s) a plugin like gatsby-transformer-sharp
would create.)
{
allCockpitTeamMember {
edges {
node {
Portrait {
value {
publicURL // (1)
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
}
}
}
}
Notes:
gatsby-transformer-sharp
which is the case for svg
and gif
files.Just like image fields, asset fields nested within a collection or singleton will be downloaded and will get a file node attached under the value
attribute.
You can access the file regardless of its type (document, video, etc.) using the publicURL
field resolver attribute on the file node.
Markdown fields nested within a collection or singleton will get a custom Markdown node attached under the value
attribute. It mimics a file node — even if there is no existing Markdown file — in order to allow plugins like gatsby-transformer-remark
to process them. Moreover, images and assets embedded into the Markdown are downloaded and their paths are updated accordingly. Example:
(You can then access the child node a plugin like gatsby-transformer-remark
would create.)
{
allCockpitDefinition {
edges {
node {
Text {
value {
childMarkdownRemark {
html
}
internal {
content // (1)
}
}
}
}
}
}
}
Notes:
The set field type allows to logically group a number of other fields together.
You can then access their values as an object in the value
of the set field.
{
allCockpitTeamMember {
edges {
node {
ContactData { // field of type set
value {
telephone {
value
}
fax {
value
}
email {
value
}
}
}
}
}
}
}
Repeater fields are one of the most powerful fields in Cockpit and allow support for two distinct use cases:
[Image, Image, Image]
)[Image, Text, Set]
)For the first case the values can be queried almost like a normal scalar field. The only difference is that two nested values are needed with the first one representing the array and the second one the value in the array.
{
allCockpitTeamMember {
edges {
node {
responsibilities { // field of type repeater
value { // value of repeater (array)
value // value of repeated field
}
}
}
}
}
The second case is a bit more complicated - in order to not cause any GraphQL Schema conflicts each array value must be of the same type. To achieve this the gatsby-source-cockpit
plugin implicitly wraps the values in a set field, generating one field in the set for each fields
option supplied in the repeater configuration.
E.g. assuming the repeater field is configured with these options:
{
"fields": [
{
"name": "title",
"type": "text",
"label": "Some text",
},
{
"name": "photo",
"type": "image",
"label": "Funny Photo",
}
]
}
then the following query is necessary to get the data:
{
allCockpitTeamMember {
edges {
node {
responsibilities { // field of type repeater
value { // value of repeater (array)
title {
value
}
photo {
value {
...
}
}
}
}
}
}
}
Note: For this to work the fields specified in the field
option need to have a name
attribute which is not required by Cockpit itself. If the name attribute is not set, the plugin will print a warning to the console and generate a name
value out of the value of the label
attribute but it is recommended to explicitly specify the name
value.
The layout(-grid) field type allows to compose a view with UI components (buttons, divider, text, custom components, …).
You can access the whole components hierarchy using the parsed
field. The type of each component is set in the component
field. All the component settings defined in Cockpit are present in the settings
field. Some of them are raw, others are processed:
class
is changed for className
;style
is processed into a CSS-in-JS-like object;html
is accessible unchanged into html
, but also accessible processed into html_sanitize
(which is sanitized) and into html_react
(which is a JS representation of a React component).E.g.
{
allCockpitPageLayout {
edges {
node {
layout {
value {
parsed
}
}
}
}
}
}
Note: Images and assets used in a layout aren't currently supported.
The object field type allows to structure data as a JSON object. You can get the whole object using the data
field.
E.g.
{
allCockpitLogEntries {
edges {
node {
logEntry {
value {
data
}
}
}
}
}
}