UNPKG

gatsby-source-wordpress

Version:

Source data from WordPress in an efficient and scalable way.

325 lines (316 loc) • 12.8 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.sourcePreviews = exports.sourcePreview = exports.inPreviewMode = void 0; var _getGatsbyApi = require("./../../utils/get-gatsby-api"); var _path = _interopRequireDefault(require("path")); var _fsExtra = _interopRequireDefault(require("fs-extra")); var _chalk = _interopRequireDefault(require("chalk")); var _url = _interopRequireDefault(require("url")); var _pQueue = _interopRequireDefault(require("p-queue")); var _dumper = require("dumper.js"); var _introspectRemoteSchema = require("../ingest-remote-schema/introspect-remote-schema"); var _fetchNodesPaginated = require("../source-nodes/fetch-nodes/fetch-nodes-paginated"); var _fetchGraphql = _interopRequireDefault(require("../../utils/fetch-graphql")); var _store = require("../../store"); var _update = require("../source-nodes/update-nodes/wp-actions/update"); var _formatLogMessage = require("../../utils/format-log-message"); var _fetchNodeUpdates = require("../source-nodes/update-nodes/fetch-node-updates"); var _cleanup = require("./cleanup"); const inDevelopPreview = process.env.NODE_ENV === `development` && !!process.env.ENABLE_GATSBY_REFRESH_ENDPOINT; const inPreviewRunner = process.env.RUNNER_TYPE === `PREVIEW` || process.env.RUNNER_TYPE === `INCREMENTAL_PREVIEWS` || !!process.env.IS_GATSBY_PREVIEW; // this is a function simply because many places in the code expect it to be. // it used to call getStore().getState() and check for some state to determine preview mode const inPreviewMode = () => inDevelopPreview || inPreviewRunner; exports.inPreviewMode = inPreviewMode; let previewQueue; const getPreviewQueue = () => { if (!previewQueue) { const { previewRequestConcurrency } = (0, _store.getStore)().getState().gatsbyApi.pluginOptions.schema; previewQueue = new _pQueue.default({ concurrency: previewRequestConcurrency, carryoverConcurrencyCount: true }); } return previewQueue; }; // This checks wether or not we're already currently processing a preview // for the passed preview id. const previewForIdIsAlreadyBeingProcessed = id => { if (!id) { return false; } const existingCallbacks = (0, _store.getStore)().getState().previewStore.nodePageCreatedCallbacks; const alreadyProcessingThisPreview = !!(existingCallbacks !== null && existingCallbacks !== void 0 && existingCallbacks[id]); return alreadyProcessingThisPreview; }; /** * For previews of draft posts, gatsby develop will throw a bunch of 404 errors * while WPGatsby is trying to read page-data.json * So we can write a dummy page-data.json if one doesn't exist. * that way there will be no 404's and Gatsby will overwrite our dummy file when it * needs to. */ const writeDummyPageDataJsonIfNeeded = async ({ previewData, pageNode }) => { if (!previewData.isDraft) { return; } const pageDataDirectory = _path.default.join(process.cwd(), `public/page-data`, pageNode.path); await _fsExtra.default.ensureDir(pageDataDirectory); const pageDataPath = _path.default.join(pageDataDirectory, `page-data.json`); const pageDataExists = await _fsExtra.default.pathExists(pageDataPath); if (!pageDataExists) { await _fsExtra.default.writeJSON(pageDataPath, { isDraft: previewData.isDraft }); } }; const createPreviewStatusCallback = ({ previewData, reporter }) => async ({ passedNode, pageNode, context, status, graphqlEndpoint, error }) => { var _data$wpGatsbyRemoteP; if (status === `PREVIEW_SUCCESS`) { // we might need to write a dummy page-data.json so that // Gatsby doesn't throw 404 errors when WPGatsby tries to read this file // that maybe doesn't exist yet await writeDummyPageDataJsonIfNeeded({ previewData, pageNode }); } const statusContext = error !== null && error !== void 0 && error.message ? `${context}\n\n${error.message}` : context; const { data } = await (0, _fetchGraphql.default)({ url: graphqlEndpoint, query: /* GraphQL */` mutation MUTATE_PREVIEW_NODE( $input: WpGatsbyRemotePreviewStatusInput! ) { wpGatsbyRemotePreviewStatus(input: $input) { success } } `, variables: { input: { clientMutationId: `sendPreviewStatus`, modified: passedNode === null || passedNode === void 0 ? void 0 : passedNode.modified, pagePath: pageNode === null || pageNode === void 0 ? void 0 : pageNode.path, parentDatabaseId: previewData.parentDatabaseId || previewData.previewDatabaseId, // if the parentDatabaseId is 0 we want to use the previewDatabaseId status, statusContext } }, errorContext: `Error occurred while mutating WordPress Preview node meta.`, forceReportCriticalErrors: true, headers: { WPGatsbyPreview: previewData.token, WPGatsbyPreviewUser: previewData.userDatabaseId } }); if (data !== null && data !== void 0 && (_data$wpGatsbyRemoteP = data.wpGatsbyRemotePreviewStatus) !== null && _data$wpGatsbyRemoteP !== void 0 && _data$wpGatsbyRemoteP.success) { reporter.log((0, _formatLogMessage.formatLogMessage)(`Successfully sent Preview status back to WordPress post ${previewData.id} during ${context}`)); } else { reporter.log((0, _formatLogMessage.formatLogMessage)(`failed to mutate WordPress post ${previewData.id} during Preview ${context}.\nCheck your WP server logs for more information.`)); } }; /** * This is called and passed the result from the ActionMonitor.previewData object along with a JWT token * It sources a single preview and creates the callback that's invoked to send preview status back to WPGatsby. * When the preview status is sent back to Gatsby, the preview action that this * logic is processing is deleted in the WP instance. That's why we call * previewForIdIsAlreadyBeingProcessed to see if another preview webhook * already started processing for this action */ const sourcePreview = async ({ previewData, reporter, actions }) => { var _previewData$manifest; if (previewForIdIsAlreadyBeingProcessed(previewData === null || previewData === void 0 ? void 0 : previewData.id)) { return; } const requiredProperties = [`previewDatabaseId`, `id`, `token`, `remoteUrl`, `parentDatabaseId`, `modified`, `userDatabaseId`]; const missingProperties = requiredProperties.filter(property => !(property in previewData)); if (!previewData || missingProperties.length) { reporter.warn((0, _formatLogMessage.formatLogMessage)(`sourcePreview was called but the required previewData properties weren't provided.`)); reporter.info((0, _formatLogMessage.formatLogMessage)(`Missing properties: \n${JSON.stringify(missingProperties, null, 2)}`)); reporter.log((0, _formatLogMessage.formatLogMessage)(`previewData: \n${JSON.stringify(previewData, null, 2)}`)); return; } await (0, _fetchNodeUpdates.touchValidNodes)(); const sendPreviewStatus = createPreviewStatusCallback({ previewData, reporter }); // this callback will be invoked when the page is created/updated for this node // then it'll send a mutation to WPGraphQL so that WP knows the preview is ready (0, _store.getStore)().dispatch.previewStore.subscribeToPagesCreatedFromNodeById({ nodeId: previewData.id, modified: previewData.modified, sendPreviewStatus }); const { node } = await (0, _update.fetchAndCreateSingleNode)({ actionType: `PREVIEW`, ...previewData, previewParentId: previewData.parentDatabaseId, isPreview: true }); if (previewData !== null && previewData !== void 0 && (_previewData$manifest = previewData.manifestIds) !== null && _previewData$manifest !== void 0 && _previewData$manifest.length && `unstable_createNodeManifest` in actions && node) { previewData.manifestIds.forEach(manifestId => { actions.unstable_createNodeManifest({ manifestId, node }); }); } }; /** * This is called when the /__refresh endpoint is posted to from WP previews. * It should only ever run in Preview mode, which is process.env.ENABLE_GATSBY_REFRESH_ENDPOINT = true * It first sources all pending preview actions, then calls sourcePreview() for each of them. */ exports.sourcePreview = sourcePreview; const sourcePreviews = async helpers => { const { webhookBody, reporter, actions } = helpers; const { debug: { preview: inPreviewDebugModeOption }, url } = (0, _getGatsbyApi.getPluginOptions)(); // some versions of WPGatsby don't send a remoteUrl on every webhook. // if we check this for every webhookBody errors will occur! if (webhookBody.remoteUrl) { // check if we're receiving preview data fromt the right WP backend const { hostname: settingsHostname } = _url.default.parse(url); const { hostname: remoteHostname } = _url.default.parse(webhookBody.remoteUrl); if (settingsHostname !== remoteHostname) { const sendPreviewStatus = createPreviewStatusCallback({ previewData: webhookBody, reporter }); await sendPreviewStatus({ status: `RECEIVED_PREVIEW_DATA_FROM_WRONG_URL`, context: `check that the preview data came from the right URL.`, passedNode: { modified: webhookBody.modified, databaseId: webhookBody.parentDatabaseId }, graphqlEndpoint: webhookBody.remoteUrl }); reporter.warn((0, _formatLogMessage.formatLogMessage)(`Received preview data from a different remote URL than the one specified in plugin options. Preview will not work. Please send preview requests from the WP instance configured in gatsby-config.js.\n\n ${_chalk.default.bold(`Remote URL:`)} ${webhookBody.remoteUrl}\n ${_chalk.default.bold(`Plugin options URL:`)} ${url}\n\n`)); return; } } const inPreviewDebugMode = inPreviewDebugModeOption || process.env.WP_GATSBY_PREVIEW_DEBUG; if (inPreviewDebugMode) { reporter.info(`Sourcing previews for the following webhook:`); (0, _dumper.dump)(webhookBody); } const wpGatsbyPreviewNodeManifestsAreSupported = await (0, _introspectRemoteSchema.remoteSchemaSupportsFieldNameOnTypeName)({ typeName: `GatsbyPreviewData`, fieldName: `manifestIds` }); const previewActions = await (0, _fetchNodesPaginated.paginatedWpNodeFetch)({ contentTypePlural: `actionMonitorActions`, nodeTypeName: `ActionMonitor`, headers: { WPGatsbyPreview: webhookBody.token, WPGatsbyPreviewUser: webhookBody.userDatabaseId }, helpers, query: /* GraphQL */` query PREVIEW_ACTIONS($after: String) { actionMonitorActions( where: { previewStream: true status: PRIVATE orderby: { field: MODIFIED, order: DESC } sinceTimestamp: ${ // only source previews made in the last 60 minutes // We delete every preview action we process so this accounts for very long cold builds between previews. Date.now() - 1000 * 60 * 60} } first: 100 after: $after ) { nodes { previewData { id isDraft modified parentDatabaseId previewDatabaseId remoteUrl singleName userDatabaseId ${wpGatsbyPreviewNodeManifestsAreSupported ? `manifestIds` : ``} } } pageInfo { hasNextPage endCursor } } } ` }); if (!(previewActions !== null && previewActions !== void 0 && previewActions.length)) { if (inPreviewDebugMode) { reporter.info(`Preview for id ${webhookBody === null || webhookBody === void 0 ? void 0 : webhookBody.id} returned no action monitor actions.`); } return; } if (inPreviewDebugMode) { reporter.info(`Preview for id ${webhookBody === null || webhookBody === void 0 ? void 0 : webhookBody.id} returned the following actions:`); (0, _dumper.dump)(previewActions); } const queue = getPreviewQueue(); for (const { previewData } of previewActions) { queue.add(() => sourcePreview({ previewData: { ...previewData, token: webhookBody.token }, reporter, actions })); } await Promise.all([queue.onEmpty(), queue.onIdle()]); // clean up leftover callbacks at the end to clean up anything we didn't catch elsewhere await (0, _cleanup.invokeAndCleanupLeftoverPreviewCallbacks)({ status: `GATSBY_PREVIEW_PROCESS_ERROR`, context: `Starting sourcePreviews` }); }; exports.sourcePreviews = sourcePreviews; //# sourceMappingURL=index.js.map