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

feat(gatsby-recipes): Spike out contentful provider and basic rendering #24655

Merged
merged 2 commits into from
May 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 37 additions & 4 deletions packages/gatsby-recipes/src/gui.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ const ContentfulSpace = ({ _uuid, ...props }) => {
},
})

const { children, ...editableProps } = props

const setProp = key => e => {
const newState = {
...inputState,
Expand All @@ -193,7 +195,7 @@ const ContentfulSpace = ({ _uuid, ...props }) => {

return (
<form key={_uuid}>
{Object.entries(props).map(([key, value]) => (
{Object.entries(editableProps).map(([key, value]) => (
<label key={key}>
{key} <br />
<input
Expand Down Expand Up @@ -263,6 +265,8 @@ const components = {
RecipeIntroduction: props => <div>{props.children}</div>,
RecipeStep: props => <div>{props.children}</div>,
ContentfulSpace,
ContentfulEnvironment: () => null,
ContentfulType: () => null,
}

const log = (label, textOrObj) => {
Expand Down Expand Up @@ -434,6 +438,11 @@ const RecipeGui = ({
}}
/>
)}
{resourcePlan.resourceChildren
? resourcePlan.resourceChildren.map(resource => (
<ResourcePlan key={resource._uuid} resourcePlan={resource} />
))
: null}
</div>
)

Expand All @@ -444,13 +453,10 @@ const RecipeGui = ({
date: new Date(),
})

console.log(state.context.plan, i)
const stepResources = state.context?.plan?.filter(
p => parseInt(p._stepMetadata.step, 10) === i + 1
)

console.log({ stepResources })

const [complete, setComplete] = useState(false)
if (output.title !== `` && output.body !== ``) {
setTimeout(() => {
Expand Down Expand Up @@ -715,6 +721,30 @@ const RecipeGui = ({
return `Install Recipe`
}

const ResourceChildren = ({ resourceChildren }) => {
if (!resourceChildren || !resourceChildren.length) {
return null
}

return (
<Styled.ul sx={{ pl: 3, marginTop: 0, mb: 5 }}>
{resourceChildren.map(resource => (
<Styled.li
sx={{
listStyleType: `none`,
}}
key={resource._uuid}
>
<ResourceMessage resource={resource} />
<ResourceChildren
resourceChildren={resource.resourceChildren}
/>
</Styled.li>
))}
</Styled.ul>
)
}

return (
<InputProvider value={state.context.inputs || {}}>
<Wrapper>
Expand Down Expand Up @@ -767,6 +797,9 @@ const RecipeGui = ({
key={`${resourceName}-plan-${i}`}
>
<ResourceMessage resource={p} />
<ResourceChildren
resourceChildren={p.resourceChildren}
/>
</Styled.li>
))}
</Styled.ul>
Expand Down
66 changes: 66 additions & 0 deletions packages/gatsby-recipes/src/providers/contentful/environment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import Joi from "@hapi/joi"

import client from "./client"
import space from "./client"
import resourceSchema from "../resource-schema"

const create = async (context, { name }) => {
const spaceId = context.ContentfulSpace.id
const space = await client.getSpace(spaceId)
const environment = await space.createEnvironment({ name })

return {
name: environment.name,
id: environment.sys.id,
_message: message(environment),
}
}

const read = async (context, name) => {
console.log({ context, name })

return undefined
}

const destroy = async (_context, id) => {}

const all = async () => {}

const schema = {
name: Joi.string(),
...resourceSchema,
}

const validate = resource =>
Joi.validate(resource, schema, { abortEarly: false })

const plan = async (context, { id, name }) => {
console.log({ context, name, id })
const currentResource = await read(context, id || name)

if (!currentResource) {
return {
currentState: ``,
describe: `Create Contentful environment ${name}`,
diffExists: true,
skipDiff: true,
}
} else {
return {
currentState: currentResource,
describe: `Contentful environment ${name} already exists`,
diff: ``,
}
}
}

const message = resource => `Created Contentful environment "${resource.name}"`

module.exports.schema = schema
module.exports.validate = validate
module.exports.plan = plan
module.exports.create = create
module.exports.read = read
module.exports.update = create
module.exports.destroy = destroy
module.exports.all = all
69 changes: 69 additions & 0 deletions packages/gatsby-recipes/src/providers/contentful/type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import Joi from "@hapi/joi"

import client from "./client"
import space from "./client"
import resourceSchema from "../resource-schema"
import getGraphqlFields from "../utils/get-graphql-fields"

const GRAPHQL_FIELD_OPTIONS = {
metadata: [`type`, `name`],
}

const create = async (context, { schema }) => {
const spaceId = context.ContentfulSpace.id
const space = await client.getSpace(spaceId)

const fields = getGraphqlFields(schema, GRAPHQL_FIELD_OPTIONS)[0]
const contentType = await space.createContentTypeWithId(fields.name, fields)
await contentType.publish()

return {
name: contentType.name,
id: contentType.sys.id,
_message: message(contentType),
}
}

const read = async (context, name) => {}

const destroy = async (_context, id) => {}

const all = async () => {}

const schema = {
schema: Joi.string(),
...resourceSchema,
}

const validate = resource =>
Joi.validate(resource, schema, { abortEarly: false })

const plan = async (context, { id, schema }) => {
const currentResource = await read(context, id)

if (!currentResource) {
return {
currentState: ``,
describe: `Create Contentful type`,
diffExists: true,
skipDiff: true,
}
} else {
return {
currentState: currentResource,
describe: `Contentful type ${currentResource.name} already exists`,
diff: ``,
}
}
}

const message = resource => `Created Contentful type "${resource.name}"`

module.exports.schema = schema
module.exports.validate = validate
module.exports.plan = plan
module.exports.create = create
module.exports.read = read
module.exports.update = create
module.exports.destroy = destroy
module.exports.all = all
16 changes: 10 additions & 6 deletions packages/gatsby-recipes/src/providers/utils/get-graphql-fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ const {
SchemaDirectiveVisitor,
} = require(`graphql-tools`)

const gqlFieldsToObject = fields =>
const gqlFieldsToArray = fields =>
Object.entries(fields).reduce((acc, [key, value]) => {
acc[key] = {
const metadata = value.metadata || {}
const field = {
id: key,
type: value.type,
metadata: value.metadata,
name: key,
...metadata,
}
return acc
}, {})

return [...acc, field]
}, [])

class MetadataDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
Expand Down Expand Up @@ -55,7 +59,7 @@ module.exports = (typeDefs, { metadata } = {}) => {
.map(([key, value]) => {
return {
name: key,
fields: gqlFieldsToObject(value._fields),
fields: gqlFieldsToArray(value._fields),
}
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ test(`get-graphql-fields returns an array of fields`, () => {
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"fields": Object {
"body": Object {
"metadata": Object {
"title": "Content",
"type": "Text",
},
"fields": Array [
Object {
"id": "title",
"name": "title",
"type": "String",
},
"title": Object {
"metadata": undefined,
"type": "String",
Object {
"id": "body",
"name": "body",
"title": "Content",
"type": "Text",
},
},
],
"name": "BlogPost",
},
]
Expand Down
29 changes: 21 additions & 8 deletions packages/gatsby-recipes/src/renderer/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import resources from "../resources"
import RecipesReconciler from "./reconciler"
import ErrorBoundary from "./error-boundary"
import transformToPlan from "./transform-to-plan-structure"
import { ResourceProvider } from "./resource-provider"
import { ResourceProvider, useResourceContext } from "./resource-provider"
import { useRecipeStep } from "./step-component"
import { InputProvider, useInputByUuid } from "./input-provider"

Expand Down Expand Up @@ -53,12 +53,13 @@ const ResourceComponent = ({
const { mode } = useMode()
const step = useRecipeStep()
const inputProps = useInputByUuid(_uuid)
const resourceContext = useResourceContext()
const userProps = getUserProps(props)
const allProps = { ...props, ...inputProps }

const resourceData = handleResource(
Resource,
{ root: process.cwd(), _uuid, mode },
{ ...resourceContext, root: process.cwd(), _uuid, mode },
allProps
)

Expand Down Expand Up @@ -96,6 +97,7 @@ const handleResource = (resourceName, context, props) => {
const { mode } = context

const key = JSON.stringify({ resourceName, ...props, mode })

const cachedResult = cache.get(key)

if (cachedResult) {
Expand All @@ -106,12 +108,23 @@ const handleResource = (resourceName, context, props) => {

let promise
try {
promise = resources[resourceName][fn](context, props)
.then(result => cache.set(key, result))
.catch(e => {
console.log(e)
throw e
})
promise = new Promise((resolve, reject) => {
// Multiple of the same promises can be queued due to re-rendering
// so this first checks for the cached result again before executing
// the request.
const cachedValue = cache.get(key)
if (cachedValue) {
resolve(cachedValue)
}

resources[resourceName][fn](context, props)
.then(result => cache.set(key, result))
.then(resolve)
.catch(e => {
console.log(e)
reject(e)
})
})
} catch (e) {
throw e
}
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-recipes/src/renderer/resource-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React, { useContext } from "react"

const ResourceContext = React.createContext({})

export const useResourceContext = resourceName => {
export const useResourceContext = () => {
const context = useContext(ResourceContext)
return context[resourceName]
return context
}

export const ResourceProvider = ({ data: providedData, children }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@ const transform = (props = {}) => {
return [...acc, ...childResourcePlans]
}

const { _props, ...plan } = JSON.parse(curr.children[0].text)
const [rawResource, ...resourceChildren] = curr.children
const { _props, ...plan } = JSON.parse(rawResource.text)

const resourcePlan = {
resourceName: curr.type,
resourceDefinitions: _props,
...plan,
}

if (resourceChildren.length) {
resourcePlan.resourceChildren = transform({ children: resourceChildren })
}

return [...acc, resourcePlan]
}, [])

Expand Down
Loading