Skip to content

Commit

Permalink
feat(gatsby): Allow custom resolver context (#16359)
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanprobst authored and freiksenet committed Aug 5, 2019
1 parent 18b4e9a commit 142b8ff
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 16 deletions.
1 change: 1 addition & 0 deletions packages/gatsby/src/bootstrap/__tests__/graphql-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const createStore = (schema = {}) => {
getState: () => {
return {
schema,
schemaCustomization: {},
}
},
}
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby/src/bootstrap/graphql-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ const withResolverContext = require(`../schema/context`)
const errorParser = require(`../query/error-parser`).default

const createGraphqlRunner = (store, reporter) => (query, context = {}) => {
const schema = store.getState().schema
const { schema, schemaCustomization } = store.getState()

return graphql(
schema,
query,
context,
withResolverContext(context, schema),
withResolverContext(context, schema, schemaCustomization.context),
context
).then(result => {
if (result.errors) {
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby/src/commands/develop.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,11 @@ async function startServer(program) {
app.use(
graphqlEndpoint,
graphqlHTTP(() => {
const schema = store.getState().schema
const { schema, schemaCustomization } = store.getState()
return {
schema,
graphiql: false,
context: withResolverContext({}, schema),
context: withResolverContext({}, schema, schemaCustomization.context),
formatError(err) {
return {
...formatError(err),
Expand Down
9 changes: 7 additions & 2 deletions packages/gatsby/src/query/query-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,19 @@ type QueryJob = {

// Run query
module.exports = async (queryJob: QueryJob) => {
const { schema, program, webpackCompilationHash } = store.getState()
const {
schema,
schemaCustomization,
program,
webpackCompilationHash,
} = store.getState()

const graphql = (query, context) =>
graphqlFunction(
schema,
query,
context,
withResolverContext(context, schema),
withResolverContext(context, schema, schemaCustomization.context),
context
)

Expand Down
58 changes: 58 additions & 0 deletions packages/gatsby/src/redux/actions/restricted.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @flow
const { camelCase } = require(`lodash`)
const report = require(`gatsby-cli/lib/reporter`)

import type { Plugin } from "./types"
Expand Down Expand Up @@ -261,6 +262,60 @@ actions.createFieldExtension = (
}
}

/**
* Make functionality available on field resolver `context`
*
* @availableIn [createSchemaCustomization]
*
* @param {object} context Object to make available on `context`.
* When called from a plugin, the context value will be namespaced under
* the camel-cased plugin name without the "gatsby-" prefix
* @example
* const getHtml = md => remark().use(html).process(md)
* exports.createSchemaCustomization = ({ actions }) => {
* actions.createResolverContext({ getHtml })
* }
* // The context value can then be accessed in any field resolver like this:
* exports.createSchemaCustomization = ({ actions }) => {
* actions.createTypes(schema.buildObjectType({
* name: 'Test',
* interfaces: ['Node'],
* fields: {
* md: {
* type: 'String!',
* async resolve(source, args, context, info) {
* const processed = await context.transformerRemark.getHtml(source.internal.contents)
* return processed.contents
* }
* }
* }
* }))
* }
*/
actions.createResolverContext = (
context: object,
plugin: Plugin,
traceId?: string
) => dispatch => {
if (!context || typeof context !== `object`) {
report.error(
`Expected context value passed to \`createResolverContext\` to be an object. Received "${context}".`
)
} else {
const { name } = plugin || {}
const payload =
!name || name === `default-site-plugin`
? context
: { [camelCase(name.replace(/^gatsby-/, ``))]: context }
dispatch({
type: `CREATE_RESOLVER_CONTEXT`,
plugin,
traceId,
payload,
})
}
}

const withDeprecationWarning = (actionName, action, api, allowedIn) => (
...args
) => {
Expand Down Expand Up @@ -336,6 +391,9 @@ const availableActionsByAPI = mapAvailableActionsToAPIs({
[ALLOWED_IN]: [`sourceNodes`, `createSchemaCustomization`],
[DEPRECATED_IN]: [`onPreInit`, `onPreBootstrap`],
},
createResolverContext: {
[ALLOWED_IN]: [`createSchemaCustomization`],
},
addThirdPartySchema: {
[ALLOWED_IN]: [`sourceNodes`, `createSchemaCustomization`],
[DEPRECATED_IN]: [`onPreInit`, `onPreBootstrap`],
Expand Down
17 changes: 11 additions & 6 deletions packages/gatsby/src/redux/prepare-nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ const enhancedNodeCacheId = ({ node, args }) =>
function awaitSiftField(fields, node, k) {
const field = fields[k]
if (field.resolve) {
const { schema } = store.getState()
return field.resolve(node, {}, withResolverContext({}, schema), {
fieldName: k,
schema,
returnType: field.type,
})
const { schema, schemaCustomization } = store.getState()
return field.resolve(
node,
{},
withResolverContext({}, schema, schemaCustomization.context),
{
fieldName: k,
schema,
returnType: field.type,
}
)
} else if (node[k] !== undefined) {
return node[k]
}
Expand Down
9 changes: 9 additions & 0 deletions packages/gatsby/src/redux/reducers/schema-customization.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module.exports = (
state = {
composer: null,
context: {},
fieldExtensions: {},
thirdPartySchemas: [],
types: [],
Expand Down Expand Up @@ -48,9 +49,17 @@ module.exports = (
fieldExtensions: { ...state.fieldExtensions, [name]: extension },
}
}
case `CREATE_RESOLVER_CONTEXT`: {
const context = action.payload
return {
...state,
context: { ...state.context, ...context },
}
}
case `DELETE_CACHE`:
return {
composer: null,
context: {},
fieldExtensions: {},
thirdPartySchemas: [],
types: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,15 @@ async function queryResult(nodes, query) {
nodes.forEach(node => store.dispatch({ type: `CREATE_NODE`, payload: node }))

await build({})
const { schema } = store.getState()
const { schema, schemaCustomization } = store.getState()

const context = { path: `foo` }
return graphql(schema, query, undefined, withResolverContext(context))
return graphql(
schema,
query,
undefined,
withResolverContext(context, schema, schemaCustomization.context)
)
}

describe(`filtering on linked nodes`, () => {
Expand Down
Loading

0 comments on commit 142b8ff

Please sign in to comment.