-
Notifications
You must be signed in to change notification settings - Fork 0
resource query (resource migration 1/3) #71
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
Changes from all commits
37fd2cf
5e1c540
75fbc17
9f43b2b
a345633
478c946
e0fd5b2
cc336ee
6653ea5
eb6b692
3e89540
211c55a
ee27bd4
00d073e
2bdcb98
879d577
1a50470
1e1ee25
079639a
f9283a8
be3ef5c
971039c
c12a1aa
72038d2
fc2b31f
f793be6
89d19e2
a517a79
666a21a
137179e
0c37800
8d88a8d
8264972
0d4765e
2b14e68
2c25a81
2127cba
8411c40
8d3b7b9
fbe2502
23a5c69
1a233ec
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 |
|---|---|---|
|
|
@@ -7,8 +7,14 @@ const { | |
| resolveToArray, | ||
| resolveSlug, | ||
| resolveSoftwareTools, | ||
| resourceQuery, | ||
| } = require("./gatsby/utils/gatsby-resolver-utils"); | ||
| const { DATASET_PATH } = require("./gatsby/constants"); | ||
| const { | ||
| DATASET_PATH, | ||
| RESOURCES_GATSBY_NODE_KEY, | ||
| MARKDOWN_REMARK_GATSBY_NODE_KEY, | ||
| TEMPLATE_KEY_TO_TYPE, | ||
| } = require("./gatsby/constants"); | ||
|
|
||
| const read = (p) => fs.readFileSync(path.join(__dirname, p), "utf8"); | ||
|
|
||
|
|
@@ -18,13 +24,10 @@ const read = (p) => fs.readFileSync(path.join(__dirname, p), "utf8"); | |
| * They serve as single source of truth, can be added/edited via CMS, | ||
| * and are referenced by other markdown files. | ||
| */ | ||
| const DATA_ONLY_PAGES = [ | ||
| "software", | ||
| "dataset", | ||
| "allenite", | ||
| "program", | ||
| "resource", | ||
| ]; | ||
|
|
||
| // TODO: use constants here when this gets revised when we migrate old resources | ||
| // to new collection | ||
| const DATA_ONLY_PAGES = ["software", "dataset", "allenite", "program"]; | ||
|
|
||
| exports.createSchemaCustomization = ({ actions }) => { | ||
| const { createTypes } = actions; | ||
|
|
@@ -39,9 +42,9 @@ exports.createSchemaCustomization = ({ actions }) => { | |
| software: [SoftwareTool!]! | ||
| } | ||
|
|
||
| type ProtocolItem { | ||
| protocol: String! | ||
| } | ||
| type ProtocolItem { | ||
| protocol: String! | ||
| } | ||
|
|
||
| type CellLineItem { | ||
| name: String! | ||
|
|
@@ -60,13 +63,14 @@ exports.createSchemaCustomization = ({ actions }) => { | |
| caption: String | ||
| } | ||
|
|
||
| """ | ||
| Software tool reference with optional custom description. | ||
| """ | ||
| type SoftwareTool { | ||
| softwareTool: MarkdownRemark @link(by: "fields.slug") | ||
| customDescription: String | ||
| }`, | ||
| """ | ||
| Software tool reference with optional custom description. | ||
| """ | ||
| type SoftwareTool { | ||
| softwareTool: MarkdownRemark @link(by: "fields.slug") | ||
| customDescription: String | ||
| } | ||
| `, | ||
| ]; | ||
| createTypes(read("gatsby/schema/base.gql")); | ||
| createTypes(typeDefs); | ||
|
|
@@ -77,7 +81,7 @@ exports.createSchemaCustomization = ({ actions }) => { | |
| * custom resolution logic. Takes the place of downstream data unpacking | ||
| * functions where possible | ||
| */ | ||
| exports.createResolvers = ({ createResolvers }) => { | ||
| exports.createResolvers = ({ reporter, createResolvers }) => { | ||
| createResolvers({ | ||
| MarkdownRemark: { | ||
| fields: { | ||
|
|
@@ -101,6 +105,23 @@ exports.createResolvers = ({ createResolvers }) => { | |
| resolve: (source) => | ||
| stringWithDefault(source.title, "No title provided."), | ||
| }, | ||
| resources: { | ||
| resolve: async (source, _args, context) => { | ||
| const names = resolveToArray(source.resources); | ||
| const results = await Promise.all( | ||
| names.map((name) => | ||
| context.nodeModel.findOne(resourceQuery(name)), | ||
| ), | ||
|
Comment on lines
+112
to
+114
This comment was marked as outdated.
Sorry, something went wrong. |
||
| ); | ||
| results.forEach((result, i) => { | ||
| if (!result) { | ||
| const msg = `Resource "${names[i]}" not found for idea "${source.title}". Check for typos and ensure the resource file exists with the correct templateKey.`; | ||
| reporter.error(msg, new Error(msg)); | ||
| } | ||
| }); | ||
| return results.filter(Boolean); | ||
| }, | ||
| }, | ||
|
Comment on lines
+108
to
+124
Contributor
Author
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. A different way to handle the resolution, using Do we really need error handling here? We have some now. |
||
| materialsAndMethods: { | ||
| resolve: (source) => { | ||
| const raw = source.materialsAndMethods; | ||
|
|
@@ -160,7 +181,46 @@ exports.createResolvers = ({ createResolvers }) => { | |
| exports.createPages = ({ actions, graphql }) => { | ||
| const { createPage } = actions; | ||
|
|
||
| return graphql(` | ||
| // Create pages for any markdown files that are configured to have their | ||
| // own node type (e.g. Resource) based on their templateKey. | ||
| const typedNodePages = Object.keys(TEMPLATE_KEY_TO_TYPE).map( | ||
| (templateKey) => { | ||
| const nodeKey = TEMPLATE_KEY_TO_TYPE[templateKey]; | ||
| const allKeyString = `all${nodeKey}`; | ||
| return graphql(` | ||
| { | ||
| ${allKeyString} { | ||
| nodes { | ||
| id | ||
| slug | ||
| } | ||
| } | ||
| } | ||
| `).then((result) => { | ||
| if (result.errors) { | ||
| result.errors.forEach((e) => console.error(e.toString())); | ||
| return Promise.reject(result.errors); | ||
| } | ||
|
|
||
| result.data[allKeyString].nodes.forEach((node) => { | ||
| createPage({ | ||
| path: node.slug, | ||
| component: path.resolve( | ||
| `src/templates/${templateKey}.tsx`, | ||
| ), | ||
| context: { id: node.id }, | ||
| }); | ||
| }); | ||
| }); | ||
| }, | ||
| ); | ||
|
|
||
| /** | ||
| * We make pages from all markdown files that are consumed by gatsby-transformer-remark, | ||
| * unless they are specified in DATA_ONLY_PAGES, or TEMPLATE_KEY_TO_TYPE. | ||
| * In practice this block makes pages for idea posts and tags. | ||
| */ | ||
| const markdownPages = graphql(` | ||
| { | ||
| allMarkdownRemark(limit: 1000) { | ||
| edges { | ||
|
|
@@ -191,7 +251,10 @@ exports.createPages = ({ actions, graphql }) => { | |
| const templateKey = edge.node.frontmatter.templateKey; | ||
|
|
||
| // Skip creating pages for data-only pages (software, dataset, etc.) | ||
| if (DATA_ONLY_PAGES.includes(templateKey)) { | ||
| if ( | ||
| DATA_ONLY_PAGES.includes(templateKey) || | ||
| templateKey in TEMPLATE_KEY_TO_TYPE | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -203,7 +266,6 @@ exports.createPages = ({ actions, graphql }) => { | |
|
|
||
| createPage({ | ||
| path: edge.node.fields.slug, | ||
| tags: edge.node.frontmatter.tags, | ||
| component: path.resolve( | ||
| `src/templates/${String(templateKey)}.tsx`, | ||
| ), | ||
|
|
@@ -238,17 +300,63 @@ exports.createPages = ({ actions, graphql }) => { | |
| }); | ||
| }); | ||
| }); | ||
|
|
||
| return Promise.all([...typedNodePages, markdownPages]); | ||
| }; | ||
|
|
||
| exports.onCreateNode = ({ node, actions, getNode }) => { | ||
| const { createNodeField } = actions; | ||
| exports.onCreateNode = ({ | ||
| node, | ||
| actions, | ||
| getNode, | ||
| createNodeId, | ||
| createContentDigest, | ||
| }) => { | ||
| const { createNodeField, createNode } = actions; | ||
|
|
||
| if (node.internal.type === `MarkdownRemark`) { | ||
| const value = createFilePath({ node, getNode }); | ||
| // By default all markdown files are transformed into MarkdownRemark nodes by gatsby-transformer-remark. | ||
| // We add a slug field to these nodes. | ||
| if (node.internal.type === MARKDOWN_REMARK_GATSBY_NODE_KEY) { | ||
| const slug = createFilePath({ node, getNode }); | ||
| createNodeField({ | ||
| name: `slug`, | ||
| node, | ||
| value, | ||
| value: slug, | ||
| }); | ||
|
|
||
| // Here me make nodes for any types defined in TEMPLATE_KEY_TO_TYPE | ||
| // Once these nodes are in the data layer, we can query them directly by their type (e.g. allResource) | ||
| // This type of query is used when mapping over the same TEMPLATE_KEY_TO_TYPE object | ||
| // in the createPages. | ||
| if ( | ||
| node.frontmatter?.templateKey && | ||
| Object.keys(TEMPLATE_KEY_TO_TYPE).includes( | ||
| node.frontmatter?.templateKey, | ||
| ) | ||
| ) { | ||
| const nodeType = TEMPLATE_KEY_TO_TYPE[node.frontmatter.templateKey]; | ||
|
|
||
| let fields = { ...node.frontmatter }; | ||
|
|
||
| // The structure of our variable type widget leads to a nested field | ||
| // that we can flatten out here. | ||
| if (nodeType === RESOURCES_GATSBY_NODE_KEY) { | ||
| fields = { | ||
| ...node.frontmatter, | ||
| ...node.frontmatter.resourceDetails, | ||
| }; | ||
| delete fields.resourceDetails; // avoid duplication in GraphQL node | ||
| } | ||
| createNode({ | ||
| ...fields, | ||
| id: createNodeId(`${node.id}-${nodeType}`), | ||
| parent: node.id, | ||
| children: [], | ||
| slug, | ||
|
Comment on lines
+350
to
+354
This comment was marked as resolved.
Sorry, something went wrong.
Contributor
Author
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 think how we are handling it is fine because there are not parent child relationships here. |
||
| internal: { | ||
| type: nodeType, | ||
| contentDigest: createContentDigest(node.frontmatter || {}), | ||
| }, | ||
| }); | ||
| } | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,26 @@ | ||
| const DATASET_PATH = `dataset`; | ||
| const SOFTWARE_PATH = `software`; | ||
|
|
||
| const RESOURCES_TEMPLATE_KEY = `resource`; | ||
|
|
||
| const RESOURCES_GATSBY_NODE_KEY = `Resource`; | ||
| const MARKDOWN_REMARK_GATSBY_NODE_KEY = `MarkdownRemark`; | ||
|
|
||
| /** | ||
| * Maps frontmatter templateKey → a distinct GraphQL node type. | ||
| * gatsby-transformer-remark collapses every .md file into one flat MarkdownRemark | ||
| * type regardless of which collection it lives in. Adding entries here causes | ||
| * onCreateNode to emit a second, collection-specific node so you can query | ||
| * allAllenite, allIdeaPost, etc. with their own inferred schemas. | ||
| */ | ||
| const TEMPLATE_KEY_TO_TYPE = { | ||
| [RESOURCES_TEMPLATE_KEY]: RESOURCES_GATSBY_NODE_KEY, | ||
| }; | ||
| module.exports = { | ||
| DATASET_PATH, | ||
| SOFTWARE_PATH, | ||
| RESOURCES_TEMPLATE_KEY, | ||
| MARKDOWN_REMARK_GATSBY_NODE_KEY, | ||
| TEMPLATE_KEY_TO_TYPE, | ||
| RESOURCES_GATSBY_NODE_KEY, | ||
| }; |
Uh oh!
There was an error while loading. Please reload this page.
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.
Another optional clarifying question: any rhyme or reason to what gets CommonJS-style vs ESM-style imports? I guess these and the files in
gatsby/are either config or server-side...?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.
Historical anachronism from gatsby's age and the template this repo was started from.
I think webpack handles
srcandgatsby-browser.jsand Node directly handles the other gatsby configuration files. I think we would need to migrate everything at once and it's just been low on my list, but it is starting to get annoying. Maybe I should do that next. Gatsby supports ESM now, it didn't always.https://www.gatsbyjs.com/docs/how-to/custom-configuration/es-modules/