-
Notifications
You must be signed in to change notification settings - Fork 21
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
Adds gatsby-source-swiftype
to theme
#185
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
const fs = require('fs'); | ||
const createRelatedResourceNode = require('./src/createRelatedResourceNode'); | ||
const getRelatedResources = require('./src/getRelatedResources'); | ||
|
||
const writeableData = {}; | ||
|
||
exports.onPreBootstrap = (_, pluginOptions) => { | ||
const { file } = pluginOptions; | ||
|
||
if (!fs.existsSync(file)) { | ||
fs.writeFileSync(file, '{}'); | ||
} | ||
}; | ||
|
||
exports.onCreateNode = async ( | ||
{ actions, node, getNodesByType, createNodeId, createContentDigest }, | ||
pluginOptions | ||
) => { | ||
const { createNode, createParentChildLink } = actions; | ||
const { filterNode = () => false, getPath } = pluginOptions; | ||
|
||
if (node.internal.type !== 'Mdx' || !filterNode({ node })) { | ||
return; | ||
} | ||
|
||
const [ | ||
{ | ||
siteMetadata: { siteUrl }, | ||
}, | ||
] = getNodesByType('Site'); | ||
|
||
const pathname = getPath({ node }); | ||
const resources = await getRelatedResources({ node, siteUrl }, pluginOptions); | ||
|
||
writeableData[pathname] = resources; | ||
|
||
resources.forEach((resource) => { | ||
const child = createRelatedResourceNode({ | ||
parent: node.id, | ||
resource, | ||
createContentDigest, | ||
createNode, | ||
createNodeId, | ||
}); | ||
|
||
createParentChildLink({ parent: node, child: child }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm guessing that in order to get the related resources we'll query for the children of the node? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The PR is just copying what we had before. I didn't write the original code, but I believe we need to establish a relationship so that we can query related content for a specific page. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. This creates a |
||
}); | ||
}; | ||
|
||
exports.createSchemaCustomization = ({ actions }) => { | ||
const { createTypes } = actions; | ||
|
||
const typeDefs = ` | ||
type RelatedResource implements Node { | ||
id: ID! | ||
title: String! | ||
url: String! | ||
} | ||
`; | ||
|
||
createTypes(typeDefs); | ||
}; | ||
|
||
exports.createResolvers = ({ createResolvers }) => { | ||
createResolvers({ | ||
Mdx: { | ||
relatedResources: { | ||
args: { | ||
limit: { | ||
type: 'Int', | ||
defaultValue: 5, | ||
}, | ||
}, | ||
type: ['RelatedResource!'], | ||
resolve: (source, args, context) => { | ||
const { limit } = args; | ||
|
||
return context.nodeModel | ||
.getNodesByIds({ ids: source.children }) | ||
.slice(0, Math.max(limit, 0)); | ||
}, | ||
}, | ||
}, | ||
}); | ||
}; | ||
|
||
exports.onPostBootstrap = (_, pluginOptions) => { | ||
const { refetch, file } = pluginOptions; | ||
|
||
if (refetch) { | ||
fs.writeFileSync(file, JSON.stringify(writeableData, null, 2)); | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
module.exports = ({ | ||
createNode, | ||
createNodeId, | ||
createContentDigest, | ||
resource, | ||
parent, | ||
}) => { | ||
const node = { | ||
id: createNodeId(`RelatedResource-${resource.url}`), | ||
title: resource.title, | ||
url: resource.url, | ||
parent, | ||
children: [], | ||
plugin: 'gatsby-source-swiftype', | ||
internal: { | ||
type: 'RelatedResource', | ||
content: JSON.stringify(resource), | ||
contentDigest: createContentDigest(resource), | ||
}, | ||
}; | ||
|
||
createNode(node); | ||
|
||
return node; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
const fs = require('fs'); | ||
const search = require('./search'); | ||
|
||
module.exports = async ({ node, siteUrl }, pluginOptions) => { | ||
const { | ||
refetch, | ||
engineKey, | ||
limit, | ||
file, | ||
getParams = () => ({}), | ||
getPath, | ||
} = pluginOptions; | ||
|
||
const pathname = getPath({ node }); | ||
|
||
if (refetch) { | ||
return search(siteUrl + pathname, getParams({ node }), { | ||
engineKey, | ||
limit, | ||
}); | ||
} | ||
|
||
const data = JSON.parse(fs.readFileSync(file)); | ||
|
||
return data[pathname] || []; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
const fetch = require('node-fetch'); | ||
const { appendTrailingSlash, stripTrailingSlash } = require('./utils/url'); | ||
|
||
const normalizeUrl = (url) => { | ||
const prefix = url.startsWith('!') ? '!' : ''; | ||
const plainUrl = url.replace(/^!/, ''); | ||
|
||
return [ | ||
prefix + appendTrailingSlash(plainUrl), | ||
prefix + stripTrailingSlash(plainUrl), | ||
]; | ||
}; | ||
|
||
const uniq = (arr) => [...new Set(arr)]; | ||
|
||
module.exports = async (url, params = {}, { engineKey, limit }) => { | ||
const { page: pageFilters = {} } = params.filters || {}; | ||
|
||
const res = await fetch( | ||
'https://search-api.swiftype.com/api/v1/public/engines/search.json', | ||
{ | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ | ||
...params, | ||
engine_key: engineKey, | ||
per_page: limit, | ||
filters: { | ||
...params.filters, | ||
page: { | ||
...pageFilters, | ||
url: uniq([ | ||
...normalizeUrl(`!${url}`), | ||
...(pageFilters.url || []).flatMap(normalizeUrl), | ||
]), | ||
}, | ||
}, | ||
}), | ||
} | ||
); | ||
|
||
const { records } = await res.json(); | ||
|
||
return records.page; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
const hasQueryParams = (urlString) => { | ||
const url = new URL(urlString); | ||
|
||
return Boolean(url.search); | ||
}; | ||
|
||
exports.appendTrailingSlash = (url) => { | ||
if (hasQueryParams(url)) { | ||
return url; | ||
} | ||
|
||
return url.endsWith('/') ? url : `${url}/`; | ||
}; | ||
|
||
exports.stripTrailingSlash = (url) => { | ||
if (hasQueryParams(url)) { | ||
return url; | ||
} | ||
|
||
return url.endsWith('/') ? url.replace(/\/$/, '') : url; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does this
filterNode
function do? how does this inline function workThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this is being used as a default value in case
filterNode
isn't specified. If we use this plugin withoutfilterNode
, it will just be a function that always returnsfalse
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason that
filterNode
returnsfalse
rather thantrue
is that we want to avoid executing searches on Swiftype for pages that aren't explicitly set as needing related resources. This helps us avoid the cost of executing a query against Swiftype for pages that we don't use this for.