UNPKG

gatsby-source-strapi

Version:

Gatsby source plugin for building websites using Strapi as a data source

151 lines (121 loc) 4.12 kB
import { fetchStrapiContentTypes, fetchEntities, fetchEntity } from "./fetch"; import { downloadMediaFiles } from "./download-media-files"; import { buildMapFromNodes, buildNodesToRemoveMap, getEndpoints } from "./helpers"; import { createNodes } from "./normalize"; import { createAxiosInstance } from "./axios-instance"; const LAST_FETCHED_KEY = "timestamp"; export const onPreInit = () => console.log("Loaded gatsby-source-strapi-plugin"); export const sourceNodes = async ( { actions, createContentDigest, createNodeId, reporter, getCache, store, cache, getNodes, getNode, }, strapiConfig, ) => { reporter.info(`gatsby-source-strapi is using Strapi version ${strapiConfig.version || 5}`); // Cast singleTypes and collectionTypes to empty arrays if they're not defined if (!Array.isArray(strapiConfig.singleTypes)) { strapiConfig.singleTypes = []; } if (!Array.isArray(strapiConfig.collectionTypes)) { strapiConfig.collectionTypes = []; } const axiosInstance = createAxiosInstance(strapiConfig); const { schemas } = await fetchStrapiContentTypes(axiosInstance); const { deleteNode, touchNode } = actions; const context = { strapiConfig, axiosInstance, actions, schemas, createContentDigest, createNodeId, reporter, getCache, getNode, getNodes, store, cache, }; const { createNode } = actions; const existingNodes = getNodes().filter( (n) => n.internal.owner === `gatsby-source-strapi` || n.internal.type === "File", ); for (const n of existingNodes) { touchNode(n); } const endpoints = getEndpoints(strapiConfig, schemas); const lastFetched = await cache.get(LAST_FETCHED_KEY); const allResults = await Promise.all( endpoints.map(({ kind, ...config }) => { if (kind === "singleType") { return fetchEntity(config, context); } return fetchEntities(config, context); }), ); let newOrExistingEntries; // Fetch only the updated data between run if (lastFetched) { // Add the updatedAt filter const deltaEndpoints = endpoints.map((endpoint) => { return { ...endpoint, queryParams: { ...endpoint.queryParams, filters: { ...endpoint.queryParams.filters, updatedAt: { $gt: lastFetched }, }, }, }; }); newOrExistingEntries = await Promise.all( deltaEndpoints.map(({ kind, ...config }) => { if (kind === "singleType") { return fetchEntity(config, context); } return fetchEntities(config, context); }), ); } const data = newOrExistingEntries || allResults; // Build a map of all nodes with the gatsby id and the strapi_id const existingNodesMap = buildMapFromNodes(existingNodes); // Build a map of all the parent nodes that should be removed // This should also delete all the created nodes for markdown, relations, dz... // When fetching only one content type and populating its relations it might cause some issues // as the relation nodes will never be deleted // it's best to fetch the content type and its relations separately and to populate // only one level of relation const nodesToRemoveMap = buildNodesToRemoveMap(existingNodesMap, endpoints, allResults); // Delete all nodes that should be deleted for (const [nodeName, nodesToDelete] of Object.entries(nodesToRemoveMap)) { if (nodesToDelete.length > 0) { reporter.info(`Strapi: ${nodeName} deleting ${nodesToDelete.length}`); for (const { id } of nodesToDelete) { const node = getNode(id); touchNode(node); deleteNode(node); } } } await cache.set(LAST_FETCHED_KEY, Date.now()); for (const [index, { uid }] of endpoints.entries()) { if (!strapiConfig.skipFileDownloads) { await downloadMediaFiles(data[index], context, uid); } for (let entity of data[index]) { const nodes = createNodes(entity, context, uid); await Promise.all(nodes.map((n) => createNode(n))); } } return; };