@kontent-ai/gatsby-source
Version:
Source plugin providing Kontent.ai data from REST Delivery API
267 lines • 15.6 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleIncomingWebhook = void 0;
const client = __importStar(require("./client"));
const sourceNodes_items_1 = require("./sourceNodes.items");
const naming_1 = require("./naming");
const lodash_1 = __importDefault(require("lodash"));
const parseKontentWebhookBody = (api) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
if (((_b = (_a = api.webhookBody) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.api_name) === "delivery_preview" || ((_d = (_c = api.webhookBody) === null || _c === void 0 ? void 0 : _c.message) === null || _d === void 0 ? void 0 : _d.api_name) === "delivery_production") {
const parsedBody = api.webhookBody;
const isCorrectStructure = ((_f = (_e = parsedBody === null || parsedBody === void 0 ? void 0 : parsedBody.data) === null || _e === void 0 ? void 0 : _e.items) === null || _f === void 0 ? void 0 : _f.every(item => item.language && item.id))
&& ((_g = parsedBody === null || parsedBody === void 0 ? void 0 : parsedBody.message) === null || _g === void 0 ? void 0 : _g.api_name)
&& ((_h = parsedBody === null || parsedBody === void 0 ? void 0 : parsedBody.message) === null || _h === void 0 ? void 0 : _h.project_id)
&& ((_j = parsedBody === null || parsedBody === void 0 ? void 0 : parsedBody.message) === null || _j === void 0 ? void 0 : _j.operation) !== null;
if (isCorrectStructure) {
return parsedBody;
}
}
else if (((_l = (_k = api.webhookBody) === null || _k === void 0 ? void 0 : _k.message) === null || _l === void 0 ? void 0 : _l.api_name) === "content_management") {
const parsedBody = api.webhookBody;
const isCorrectStructure = ((_o = (_m = parsedBody === null || parsedBody === void 0 ? void 0 : parsedBody.data) === null || _m === void 0 ? void 0 : _m.items) === null || _o === void 0 ? void 0 : _o.every(item => item.language && item.transition_from && item.transition_to && item.item.id))
&& ((_p = parsedBody === null || parsedBody === void 0 ? void 0 : parsedBody.message) === null || _p === void 0 ? void 0 : _p.api_name)
&& ((_q = parsedBody === null || parsedBody === void 0 ? void 0 : parsedBody.message) === null || _q === void 0 ? void 0 : _q.project_id)
&& ((_r = parsedBody === null || parsedBody === void 0 ? void 0 : parsedBody.message) === null || _r === void 0 ? void 0 : _r.operation) !== null;
if (isCorrectStructure) {
return parsedBody;
}
}
return null;
};
const isKontentSupportedWebhook = (message, pluginConfig) => {
var _a;
const isCorrectProject = message.project_id === pluginConfig.projectId;
const isPreviewWebhook = 'delivery_preview' === message.api_name
&& ['upsert', 'archive', 'restore'].includes(message.operation);
const isBuildWebhook = 'delivery_production' === message.api_name
&& ['publish', 'unpublish'].includes(message.operation);
const isWorkflowWebhook = 'content_management' === message.api_name
&& message.operation === 'change_workflow_step';
const isCorrectMessageType = message.type == 'content_item_variant';
return isCorrectProject
&& (isPreviewWebhook || isBuildWebhook || (isWorkflowWebhook && ((_a = pluginConfig === null || pluginConfig === void 0 ? void 0 : pluginConfig.experimental) === null || _a === void 0 ? void 0 : _a.managementApiTriggersUpdate)))
&& isCorrectMessageType;
};
const createNodeFromRawKontentItem = (api, rawKontentItem, includeRawContent, preferredLanguage) => {
(0, sourceNodes_items_1.addPreferredLanguageProperty)([rawKontentItem], preferredLanguage);
(0, sourceNodes_items_1.alterRichTextElements)([rawKontentItem]);
const nodeData = (0, sourceNodes_items_1.getKontentItemLanguageVariantArtifact)(api, rawKontentItem, includeRawContent);
api.actions.createNode(nodeData);
return nodeData.id;
};
const isContentComponent = (data) => {
var _a;
// Components have substring 01 in its id starting at position 14.
// xxxxxxxx-xxxx-01xx-xxxx-xxxxxxxxxxxx
const id = (_a = data === null || data === void 0 ? void 0 : data.system) === null || _a === void 0 ? void 0 : _a.id;
return id !== null && id.substring(14, 16) === "01";
};
const handleUpsertItem = async (api, pluginConfig, itemId, itemLanguage) => {
// TODO - language codename is not provided for management call
if (itemLanguage && !pluginConfig.languageCodenames.includes(itemLanguage)) {
api.reporter.verbose(`Cant find specified language ${itemLanguage} in plugin configuration`);
return [];
}
// TODO could be optimized to by checking the fallback structure and save some requests
// not recreate the ones that has different system.language
// be careful on fallback language - verify cz->de->en fallbacks
const createdItemsIds = [];
for (const lang of pluginConfig.languageCodenames) {
const { item: kontentItem, modularKontent } = await client.loadKontentItem(itemId, lang, pluginConfig, true);
if (kontentItem === undefined) {
api.reporter.verbose(`Item (${itemId}) language variant (${lang}) not found on the kontent.ai delivery API for update`);
continue;
}
const nodeId = createNodeFromRawKontentItem(api, kontentItem, pluginConfig.includeRawContent, lang);
createdItemsIds.push(nodeId);
for (const key in modularKontent) {
if (Object.prototype.hasOwnProperty.call(modularKontent, key)) {
const modularKontentItem = modularKontent[key];
const nodeId = createNodeFromRawKontentItem(api, modularKontentItem, pluginConfig.includeRawContent, lang);
createdItemsIds.push(nodeId);
}
}
}
return createdItemsIds;
};
const handleDeleteItem = async (api, pluginConfig) => {
var _a, _b, _c;
const itemInfo = (_a = api.webhookBody) === null || _a === void 0 ? void 0 : _a.data.items[0];
if (!pluginConfig.languageCodenames.includes(itemInfo.language)) {
api.reporter.verbose(`Cant find specified language ${itemInfo.language} in plugin configuration`);
return [];
}
// TODO could be optimized to by checking the fallback structure and save some requests
// not recreate the ones that has different system.language
// be careful on fallback language - verify cz->de->en fallbacks
const touchedItemsIds = [];
for (const lang of pluginConfig.languageCodenames) {
const { item: kontentItem, modularKontent } = await client.loadKontentItem(itemInfo.id, lang, pluginConfig, true);
if (kontentItem === undefined) { //item was deleted (with content components)
const idString = (0, naming_1.getKontentItemNodeStringForId)(itemInfo.id, lang);
const node = api.getNode(api.createNodeId(idString));
if (!node) {
api.reporter.warn(`Node with ${idString} not found - skipping`);
continue;
}
// Remove content components
const kontentItemNodes = api.getNodes()
.filter((node) => node.internal.type.startsWith((0, naming_1.getKontentItemInterfaceName)()))
.map(node => node);
const modularItemCodenames = lodash_1.default.flatMap(Object.values(node.elements)
.filter(element => element.type === naming_1.RICH_TEXT_ELEMENT_TYPE_NAME)
.map(richTextElement => richTextElement.modular_content));
modularItemCodenames.forEach(modularItemCodename => {
const candidate = kontentItemNodes.find((candidateNode) => {
var _a;
return ((_a = candidateNode === null || candidateNode === void 0 ? void 0 : candidateNode.system) === null || _a === void 0 ? void 0 : _a.codename) === modularItemCodename
&& candidateNode[naming_1.PREFERRED_LANGUAGE_IDENTIFIER] === node[naming_1.PREFERRED_LANGUAGE_IDENTIFIER];
});
if (candidate && isContentComponent(candidate)) {
touchedItemsIds.push(candidate.id);
api.actions.deleteNode(candidate);
}
});
if (node) {
touchedItemsIds.push(node.id);
api.actions.deleteNode(node);
}
continue;
}
else { // fallback version still available
const nodeId = createNodeFromRawKontentItem(api, kontentItem, pluginConfig.includeRawContent, lang);
touchedItemsIds.push(nodeId);
for (const key in modularKontent) {
if (Object.prototype.hasOwnProperty.call(modularKontent, key)) {
const modularKontentItem = modularKontent[key];
const nodeId = createNodeFromRawKontentItem(api, modularKontentItem, pluginConfig.includeRawContent, lang);
touchedItemsIds.push(nodeId);
}
}
}
}
if (((_b = api.webhookBody) === null || _b === void 0 ? void 0 : _b.data.items.length) > 1) {
api.reporter.verbose(`Webhook contains more than one item for un-publish operation - upserting all (all but first in webhook) related items.`);
for (const relatedItem of (_c = api.webhookBody) === null || _c === void 0 ? void 0 : _c.data.items.slice(1)) {
api.reporter.verbose(`Upserting item ${relatedItem.codename} (${relatedItem.codename}) - ${relatedItem.language}`);
const processedIds = await handleUpsertItem(api, pluginConfig, relatedItem.id, relatedItem.language);
touchedItemsIds.push(...processedIds);
}
}
return touchedItemsIds;
};
const handleIncomingWebhook = async (api, pluginConfig, itemTypes) => {
var _a;
const webhook = parseKontentWebhookBody(api);
if (webhook === null) {
api.reporter.verbose('Webhook ignored - webhook does not come from Kontent.ai');
return;
}
if (!isKontentSupportedWebhook(webhook.message, pluginConfig)) {
api.reporter.verbose('This Kontent.ai webhook is not handled by the Gatsby source kontent source plugin');
return;
}
api.reporter.verbose(`Handling ${webhook.message.operation} from ${webhook.message.api_name} API`);
if (webhook.data.items.length > 1) {
if (webhook.message.operation != 'unpublish') {
api.reporter.warn(`Webhook contains more than one item! - contains (${webhook.data.items.length})`);
}
else {
api.reporter.verbose(`Unpublish webhook contains more than one item (${webhook.data.items.length})`);
}
}
const processedItemIds = [];
if (webhook.message.api_name === 'delivery_preview') {
const item = webhook.data.items[0];
// TODO: Webhook header signature (once headers are available)
// use signatureHelper '@kontent-ai/kontent-webhook-helper'
// https://github.com/gatsbyjs/gatsby/issues/23593
if (webhook.message.operation === "upsert" || webhook.message.operation === "restore") {
const processedIds = await handleUpsertItem(api, pluginConfig, item.id, item.language);
processedItemIds.push(...processedIds);
}
if (webhook.message.operation === "archive") {
const processedIds = await handleDeleteItem(api, pluginConfig);
processedItemIds.push(...processedIds);
}
}
else if (webhook.message.api_name === 'delivery_production') {
const item = webhook.data.items[0];
// TODO: Webhook header signature (once headers are available)
// use signatureHelper '@kontent-ai/kontent-webhook-helper'
// https://github.com/gatsbyjs/gatsby/issues/23593
if (webhook.message.operation === "publish") {
const processedIds = await handleUpsertItem(api, pluginConfig, item.id, item.language);
processedItemIds.push(...processedIds);
}
if (webhook.message.operation === "unpublish") {
const processedIds = await handleDeleteItem(api, pluginConfig);
processedItemIds.push(...processedIds);
}
}
else if (((_a = pluginConfig === null || pluginConfig === void 0 ? void 0 : pluginConfig.experimental) === null || _a === void 0 ? void 0 : _a.managementApiTriggersUpdate) && webhook.message.api_name === 'content_management') {
const item = webhook.data.items[0];
// TODO: Webhook header signature (once headers are available)
// use signatureHelper '@kontent-ai/kontent-webhook-helper'
// https://github.com/gatsbyjs/gatsby/issues/23593
if (webhook.message.operation === "change_workflow_step") {
const processedIds = await handleUpsertItem(api, pluginConfig, item.item.id);
processedItemIds.push(...processedIds);
}
else {
api.reporter.verbose(`Operation '${webhook.message.operation}' is not supported yet!`);
}
}
else {
api.reporter.verbose(`Webhook is not supported yet!`);
api.reporter.verbose(JSON.stringify(webhook, null, 2));
return;
}
for (const itemType of itemTypes) {
const itemsToTouch = api.getNodesByType(itemType);
itemsToTouch
.filter(item => !processedItemIds.includes(item.id))
.forEach(itemToTouch => api.actions.touchNode(itemToTouch));
}
if (pluginConfig.includeTaxonomies) {
const taxonomies = api.getNodesByType((0, naming_1.getKontentTaxonomyTypeName)());
for (const taxonomy of taxonomies) {
api.actions.touchNode(taxonomy);
}
}
if (pluginConfig.includeTypes) {
const types = api.getNodesByType((0, naming_1.getKontentTypeTypeName)());
for (const type of types) {
api.actions.touchNode(type);
}
}
};
exports.handleIncomingWebhook = handleIncomingWebhook;
//# sourceMappingURL=webhookProcessor.js.map