Skip to content

Commit

Permalink
feat(gatsby): allow awaiting API run triggered by createNode action (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanprobst authored and pieh committed May 14, 2019
1 parent 1fe6f9d commit 17a67a5
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ async function processRemoteNode({
// be the owner of File nodes or there'll be conflicts if any other
// File nodes are created through normal usages of
// gatsby-source-filesystem.
createNode(fileNode, { name: `gatsby-source-filesystem` })
await createNode(fileNode, { name: `gatsby-source-filesystem` })

return fileNode
}
Expand Down
1 change: 1 addition & 0 deletions packages/gatsby/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
"react-error-overlay": "^3.0.0",
"react-hot-loader": "^4.6.2",
"redux": "^4.0.0",
"redux-thunk": "^2.3.0",
"semver": "^5.6.0",
"shallow-compare": "^1.2.2",
"sift": "^5.1.0",
Expand Down
51 changes: 28 additions & 23 deletions packages/gatsby/src/redux/__tests__/nodes.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
const Redux = require(`redux`)
const { actions } = require(`../actions`)
const nodeReducer = require(`../reducers/nodes`)
const nodeTouchedReducer = require(`../reducers/nodes-touched`)

jest.mock(`../../db/nodes`)
jest.mock(`../nodes`)

const store = Redux.createStore(
Redux.combineReducers({ nodeReducer, nodeTouchedReducer }),
{}
)
const dispatch = jest.fn()

describe(`Create and update nodes`, () => {
beforeEach(() => {
store.dispatch({
type: `DELETE_CACHE`,
})
dispatch.mockClear()
})

it(`allows creating nodes`, () => {
const action = actions.createNode(
actions.createNode(
{
id: `hi`,
children: [],
Expand All @@ -32,13 +27,14 @@ describe(`Create and update nodes`, () => {
{
name: `tests`,
}
)
)(dispatch)
const action = dispatch.mock.calls[0][0]
expect(action).toMatchSnapshot()
expect(nodeReducer(undefined, action)).toMatchSnapshot()
})

it(`allows updating nodes`, () => {
const action = actions.createNode(
actions.createNode(
{
id: `hi`,
children: [],
Expand All @@ -61,8 +57,10 @@ describe(`Create and update nodes`, () => {
{
name: `tests`,
}
)
const updateAction = actions.createNode(
)(dispatch)
const action = dispatch.mock.calls[0][0]

actions.createNode(
{
id: `hi`,
children: [],
Expand All @@ -82,7 +80,9 @@ describe(`Create and update nodes`, () => {
{
name: `tests`,
}
)
)(dispatch)
const updateAction = dispatch.mock.calls[1][0]

let state = nodeReducer(undefined, action)
state = nodeReducer(state, updateAction)
expect(state.get(`hi`).pickle).toEqual(false)
Expand All @@ -91,7 +91,7 @@ describe(`Create and update nodes`, () => {
})

it(`nodes that are added are also "touched"`, () => {
const action = actions.createNode(
actions.createNode(
{
id: `hi`,
children: [],
Expand All @@ -105,13 +105,15 @@ describe(`Create and update nodes`, () => {
{
name: `tests`,
}
)
)(dispatch)
const action = dispatch.mock.calls[0][0]

let state = nodeTouchedReducer(undefined, action)
expect(state[`hi`]).toBe(true)
})

it(`allows adding fields to nodes`, () => {
const action = actions.createNode(
actions.createNode(
{
id: `hi`,
children: [],
Expand All @@ -125,7 +127,8 @@ describe(`Create and update nodes`, () => {
{
name: `tests`,
}
)
)(dispatch)
const action = dispatch.mock.calls[0][0]
let state = nodeReducer(undefined, action)

const addFieldAction = actions.createNodeField(
Expand All @@ -138,12 +141,13 @@ describe(`Create and update nodes`, () => {
name: `test`,
}
)

state = nodeReducer(state, addFieldAction)
expect(state).toMatchSnapshot()
})

it(`throws error if a field is updated by a plugin not its owner`, () => {
const action = actions.createNode(
actions.createNode(
{
id: `hi`,
children: [],
Expand All @@ -157,7 +161,8 @@ describe(`Create and update nodes`, () => {
{
name: `tests`,
}
)
)(dispatch)
const action = dispatch.mock.calls[0][0]
let state = nodeReducer(undefined, action)

const addFieldAction = actions.createNodeField(
Expand Down Expand Up @@ -202,7 +207,7 @@ describe(`Create and update nodes`, () => {
{
name: `pluginA`,
}
)
)(dispatch)

function callActionCreator() {
actions.createNode(
Expand All @@ -219,7 +224,7 @@ describe(`Create and update nodes`, () => {
{
name: `pluginB`,
}
)
)(dispatch)
}

expect(callActionCreator).toThrowErrorMatchingSnapshot()
Expand All @@ -244,7 +249,7 @@ describe(`Create and update nodes`, () => {
{
name: `pluginA`,
}
)
)(dispatch)
}

expect(callActionCreator).toThrowErrorMatchingSnapshot()
Expand Down
25 changes: 24 additions & 1 deletion packages/gatsby/src/redux/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const { store } = require(`./index`)
const fileExistsSync = require(`fs-exists-cached`).sync
const joiSchemas = require(`../joi-schemas/joi`)
const { generateComponentChunkName } = require(`../utils/js-chunk-names`)
const apiRunnerNode = require(`../utils/api-runner-node`)

const actions = {}

Expand Down Expand Up @@ -527,6 +528,8 @@ const typeOwners = {}
* readable description of what this node represent / its source. It will
* be displayed when type conflicts are found, making it easier to find
* and correct type conflicts.
* @returns {Promise} The returned Promise resolves when all cascading
* `onCreateNode` API calls triggered by `createNode` have finished.
* @example
* createNode({
* // Data for the node.
Expand All @@ -551,7 +554,7 @@ const typeOwners = {}
* }
* })
*/
actions.createNode = (
const createNode = (
node: any,
plugin?: Plugin,
actionOptions?: ActionOptions = {}
Expand Down Expand Up @@ -716,6 +719,26 @@ actions.createNode = (
}
}

actions.createNode = (...args) => dispatch => {
const actions = createNode(...args)
dispatch(actions)
const createNodeAction = (Array.isArray(actions) ? actions : [actions]).find(
action => action.type === `CREATE_NODE`
)

if (!createNodeAction) {
return undefined
}

const { payload: node, traceId, parentSpan } = createNodeAction
return apiRunnerNode(`onCreateNode`, {
node,
traceId,
parentSpan,
traceTags: { nodeId: node.id, nodeType: node.internal.type },
})
}

/**
* "Touch" a node. Tells Gatsby a node still exists and shouldn't
* be garbage collected. Primarily useful for source plugins fetching
Expand Down
47 changes: 24 additions & 23 deletions packages/gatsby/src/redux/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ const Redux = require(`redux`)
const _ = require(`lodash`)

const mitt = require(`mitt`)
const thunk = require(`redux-thunk`).default
const reducers = require(`./reducers`)
const { writeToCache, readFromCache } = require(`./persist`)

// Create event emitter for actions
const emitter = mitt()

// Reducers
const reducers = require(`./reducers`)
const { writeToCache, readFromCache } = require(`./persist`)

// Read old node data from cache.
const readState = () => {
try {
Expand All @@ -32,21 +31,23 @@ const readState = () => {
return {}
}

exports.readState = readState
/**
* Redux middleware handling array of actions
*/
const multi = ({ dispatch }) => next => action =>
Array.isArray(action) ? action.filter(Boolean).map(dispatch) : next(action)

const store = Redux.createStore(
Redux.combineReducers({ ...reducers }),
readState(),
Redux.applyMiddleware(function multi({ dispatch }) {
return next => action =>
Array.isArray(action)
? action.filter(Boolean).map(dispatch)
: next(action)
})
)
const configureStore = initialState =>
Redux.createStore(
Redux.combineReducers({ ...reducers }),
initialState,
Redux.applyMiddleware(thunk, multi)
)

const store = configureStore(readState())

// Persist state.
function saveState() {
const saveState = () => {
if (process.env.DANGEROUSLY_DISABLE_OOM) {
return Promise.resolve()
}
Expand All @@ -64,15 +65,15 @@ function saveState() {
return writeToCache(pickedState)
}

exports.saveState = saveState

store.subscribe(() => {
const lastAction = store.getState().lastAction
emitter.emit(lastAction.type, lastAction)
})

/** Event emitter */
exports.emitter = emitter

/** Redux store */
exports.store = store
module.exports = {
emitter,
store,
configureStore,
readState,
saveState,
}
12 changes: 0 additions & 12 deletions packages/gatsby/src/redux/plugin-runner.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
// Invoke plugins for certain actions.

const { emitter } = require(`./index`)
const { getNode } = require(`../db/nodes`)
const apiRunnerNode = require(`../utils/api-runner-node`)

emitter.on(`CREATE_NODE`, action => {
const node = getNode(action.payload.id)
const traceTags = { nodeId: node.id, nodeType: node.internal.type }
apiRunnerNode(`onCreateNode`, {
node,
traceId: action.traceId,
parentSpan: action.parentSpan,
traceTags,
})
})

emitter.on(`CREATE_PAGE`, action => {
const page = action.payload
apiRunnerNode(
Expand Down
5 changes: 3 additions & 2 deletions packages/gatsby/src/utils/api-runner-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ const doubleBind = (boundActionCreators, api, plugin, actionOptions) => {
// Let action callers override who the plugin is. Shouldn't be
// used that often.
if (args.length === 1) {
boundActionCreator(args[0], plugin, actionOptions)
return boundActionCreator(args[0], plugin, actionOptions)
} else if (args.length === 2) {
boundActionCreator(args[0], args[1], actionOptions)
return boundActionCreator(args[0], args[1], actionOptions)
}
return undefined
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -17742,6 +17742,11 @@ reduce@^1.0.1:
dependencies:
object-keys "~1.0.0"

redux-thunk@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==

redux@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5"
Expand Down

0 comments on commit 17a67a5

Please sign in to comment.