UNPKG

kentico-cloud-delivery

Version:

Official Kentico Cloud Delivery SDK

406 lines 22.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var kentico_cloud_core_1 = require("kentico-cloud-core"); var config_1 = require("../config"); var elements_1 = require("../elements"); var models_1 = require("../models"); var resolvers_1 = require("../resolvers"); var ElementMapper = /** @class */ (function () { function ElementMapper(config, richTextHtmlParser) { this.config = config; this.richTextHtmlParser = richTextHtmlParser; this.defaultLinkedItemWrapperTag = 'p'; this.defaultLinkedItemWrapperClasses = ['kc-linked-item-wrapper']; } /** * Maps all element in given content item and returns strongly typed content item based on the resolver specified * in DeliveryClientConfig */ ElementMapper.prototype.mapElements = function (data) { var _this = this; // return processed item if possible (to avoid infinite recursion) var processedItem = this.getProcessedItem(data.item.system.codename, data.processedItems); if (processedItem) { // item was already resolved, return it return { item: processedItem, processedItems: data.processedItems, preparedItems: data.preparedItems, processingStartedForCodenames: data.processingStartedForCodenames }; } var elementCodenames = Object.getOwnPropertyNames(data.item.elements); var itemInstance = resolvers_1.stronglyTypedResolver.createItemInstance(data.item.system.type, { item: data.item, modularContent: data.modularContent }, this.config.typeResolvers || []); if (!data.preparedItems) { data.preparedItems = {}; } // add to prepared items data.preparedItems[data.item.system.codename] = itemInstance; elementCodenames.forEach(function (elementCodename) { if (!itemInstance) { throw Error("Item instance was not initiazed correctly."); } var elementMap = _this.resolveElementMap(itemInstance, elementCodename); var elementWrapper = { contentItemSystem: data.item.system, rawElement: data.item.elements[elementCodename], propertyName: elementMap.resolvedName, }; if (elementMap.shouldMapElement) { var mappedElement = _this.mapElement({ elementWrapper: elementWrapper, item: itemInstance, modularContent: data.modularContent, preparedItems: data.preparedItems, processingStartedForCodenames: data.processingStartedForCodenames, processedItems: data.processedItems, queryConfig: data.queryConfig }); // set mapped element to item instance itemInstance[elementMap.resolvedName] = mappedElement; } }); return { item: itemInstance, processedItems: data.processedItems, preparedItems: data.preparedItems, processingStartedForCodenames: data.processingStartedForCodenames }; }; ElementMapper.prototype.mapElement = function (data) { var elementType = kentico_cloud_core_1.enumHelper.getEnumFromValue(elements_1.ElementType, data.elementWrapper.rawElement.type); if (elementType) { if (elementType === elements_1.ElementType.ModularContent) { return this.mapLinkedItemsElement({ elementWrapper: data.elementWrapper, modularContent: data.modularContent, preparedItems: data.preparedItems, processingStartedForCodenames: data.processingStartedForCodenames, processedItems: data.processedItems, queryConfig: data.queryConfig }); } if (elementType === elements_1.ElementType.Text) { return this.mapTextElement(data.elementWrapper); } if (elementType === elements_1.ElementType.Asset) { return this.mapAssetsElement(data.elementWrapper); } if (elementType === elements_1.ElementType.Number) { return this.mapNumberElement(data.elementWrapper); } if (elementType === elements_1.ElementType.MultipleChoice) { return this.mapMultipleChoiceElement(data.elementWrapper); } if (elementType === elements_1.ElementType.DateTime) { return this.mapDateTimeElement(data.elementWrapper); } if (elementType === elements_1.ElementType.RichText) { return this.mapRichTextElement(data.item, data.elementWrapper, data.modularContent, data.queryConfig, data.processedItems, data.processingStartedForCodenames, data.preparedItems); } if (elementType === elements_1.ElementType.UrlSlug) { return this.mapUrlSlugElement(data.elementWrapper, data.item, data.queryConfig); } if (elementType === elements_1.ElementType.Taxonomy) { return this.mapTaxonomyElement(data.elementWrapper); } if (elementType === elements_1.ElementType.Custom) { return this.mapCustomElement(data.elementWrapper); } } console.warn("Could not map element '" + data.elementWrapper.rawElement.name + "' of type '" + data.elementWrapper.rawElement.type + "'. Returning unknown element instead."); return this.mapUnknowElement(data.elementWrapper); }; ElementMapper.prototype.mapRichTextElement = function (item, elementWrapper, modularContent, queryConfig, processedItems, processingStartedForCodenames, preparedItems) { var _this = this; // get all linked items nested in rich text var richTextLinkedItems = []; var rawElement = elementWrapper.rawElement; if (rawElement.modular_content) { if (Array.isArray(rawElement.modular_content)) { rawElement.modular_content.forEach(function (codename) { // get linked item and check if it exists (it might not be included in response due to 'Depth' parameter) var rawItem = modularContent[codename]; // first try to get existing item var existingLinkedItem = _this.getOrSaveLinkedItemForElement(codename, rawElement, queryConfig, modularContent, processedItems, processingStartedForCodenames, preparedItems); if (existingLinkedItem) { // item was found, add it to linked items richTextLinkedItems.push(existingLinkedItem); } else { var throwErrorForMissingLinkedItems = false; // check if errors should be thrown for missing linked items if (queryConfig.throwErrorForMissingLinkedItems === false || queryConfig.throwErrorForMissingLinkedItems === true) { // variable is a boolean throwErrorForMissingLinkedItems = queryConfig.throwErrorForMissingLinkedItems; } // throw error if raw item is not available and errors are not skipped if (!rawItem) { var msg = "Mapping RichTextElement element '" + rawElement.name + "' failed because referenced linked item with codename '" + codename + "' could not be found in Delivery response.\n Increasing 'depth' parameter may solve this issue as it will include nested items. Alternatively you may disable 'throwErrorForMissingLinkedItems' in your query"; if (throwErrorForMissingLinkedItems) { throw Error(msg); } } // item was not found or not yet resolved if (rawItem) { var mappedLinkedItemResult = _this.mapElements({ item: rawItem, modularContent: modularContent, preparedItems: preparedItems, processingStartedForCodenames: processingStartedForCodenames, processedItems: processedItems, queryConfig: queryConfig }); // add mapped linked item to result if (mappedLinkedItemResult) { richTextLinkedItems.push(mappedLinkedItemResult.item); } } } }); } } // extract and map links & images var links = this.mapRichTextLinks(rawElement.links); var images = this.mapRichTextImages(rawElement.images); return new elements_1.Elements.RichTextElement(elementWrapper, rawElement.modular_content, { links: links, resolveRichTextFunc: function () { return resolvers_1.richTextResolver.resolveData(item.system.codename, rawElement.value, elementWrapper.propertyName, { enableAdvancedLogging: _this.config.isDeveloperMode ? _this.config.isDeveloperMode : false, images: images, richTextHtmlParser: _this.richTextHtmlParser, getLinkedItem: function (codename) { return _this.getOrSaveLinkedItemForElement(codename, rawElement, queryConfig, modularContent, processedItems, processingStartedForCodenames, preparedItems); }, links: links, queryConfig: queryConfig, linkedItemWrapperTag: _this.config.linkedItemResolver && _this.config.linkedItemResolver.linkedItemWrapperTag ? _this.config.linkedItemResolver.linkedItemWrapperTag : _this.defaultLinkedItemWrapperTag, linkedItemWrapperClasses: _this.config.linkedItemResolver && _this.config.linkedItemResolver.linkedItemWrapperClasses ? _this.config.linkedItemResolver.linkedItemWrapperClasses : _this.defaultLinkedItemWrapperClasses, }); }, images: images }); }; ElementMapper.prototype.mapDateTimeElement = function (elementWrapper) { return new elements_1.Elements.DateTimeElement(elementWrapper); }; ElementMapper.prototype.mapMultipleChoiceElement = function (elementWrapper) { return new elements_1.Elements.MultipleChoiceElement(elementWrapper); }; ElementMapper.prototype.mapNumberElement = function (elementWrapper) { return new elements_1.Elements.NumberElement(elementWrapper); }; ElementMapper.prototype.mapTextElement = function (elementWrapper) { return new elements_1.Elements.TextElement(elementWrapper); }; ElementMapper.prototype.mapAssetsElement = function (elementWrapper) { return new elements_1.Elements.AssetsElement(elementWrapper); }; ElementMapper.prototype.mapTaxonomyElement = function (elementWrapper) { return new elements_1.Elements.TaxonomyElement(elementWrapper); }; ElementMapper.prototype.mapUnknowElement = function (elementWrapper) { return new elements_1.Elements.UnknownElement(elementWrapper); }; ElementMapper.prototype.mapCustomElement = function (elementWrapper) { // try to find element resolver if (this.config.elementResolver) { var customElementClass = this.config.elementResolver(elementWrapper); if (customElementClass) { return customElementClass; } } return new elements_1.Elements.DefaultCustomElement(elementWrapper); }; ElementMapper.prototype.mapUrlSlugElement = function (elementWrapper, item, queryConfig) { var _this = this; var resolver = this.getUrlSlugResolverForElement(item, elementWrapper, queryConfig); return new elements_1.Elements.UrlSlugElement(elementWrapper, { resolveLinkFunc: function () { return resolvers_1.urlSlugResolver.resolveUrl({ elementName: elementWrapper.propertyName, elementValue: elementWrapper.rawElement.value, item: item, enableAdvancedLogging: _this.config.isDeveloperMode ? _this.config.isDeveloperMode : false, resolver: resolver }).url || ''; } }); }; ElementMapper.prototype.mapLinkedItemsElement = function (data) { var _this = this; // prepare linked items var linkedItems = []; // value = array of item codenames var linkedItemCodenames = data.elementWrapper.rawElement.value; linkedItemCodenames.forEach(function (codename) { var linkedItem = _this.getOrSaveLinkedItemForElement(codename, data.elementWrapper.rawElement, data.queryConfig, data.modularContent, data.processedItems, data.processingStartedForCodenames, data.preparedItems); if (linkedItem) { // add item to result linkedItems.push(linkedItem); } else { // item was not found if (_this.config.isDeveloperMode) { // tslint:disable-next-line:max-line-length console.warn("Linked item with codename '" + codename + "' in linked items element '" + data.elementWrapper.rawElement.name + "' of '" + data.elementWrapper.rawElement.type + "' type could not be found. If you require this item, consider increasing 'depth' of your query. This warning can be turned off by disabling 'enableAdvancedLogging' option."); } } }); return new elements_1.Elements.LinkedItemsElement(data.elementWrapper, linkedItems); }; ElementMapper.prototype.getUrlSlugResolverForElement = function (item, elementWrapper, queryConfig) { // query `urlSlugResolver` has priority over global resolver if (queryConfig.urlSlugResolver) { return queryConfig.urlSlugResolver; } if (item._config && item._config.urlSlugResolver) { return item._config.urlSlugResolver; } // resolve default link value return function () { return elementWrapper.rawElement.value; }; }; ElementMapper.prototype.getProcessedItem = function (codename, processedItems) { return processedItems[codename]; }; ElementMapper.prototype.getPreparedItem = function (codename, preparedItems) { return preparedItems[codename]; }; ElementMapper.prototype.getOrSaveLinkedItemForElement = function (codename, element, queryConfig, modularContent, processedItems, mappingStartedForCodenames, preparedItems) { // first check if item was already resolved and return it if it was var processedItem = this.getProcessedItem(codename, processedItems); if (processedItem) { // item was already resolved return processedItem; } if (mappingStartedForCodenames.find(function (m) { return m === codename; })) { // item was already processed, but may not have yet been resolved (e.g. when child references parent) // return reference to prepared item return preparedItems[codename]; } mappingStartedForCodenames.push(codename); // try getting item from modular content var rawItem = modularContent[codename]; // if not found item might not be a linked item, but can still be in standard response // (e.g. when linked item references item in standard response) if (!rawItem) { var preparedItem = this.getPreparedItem(codename, preparedItems); if (preparedItem) { return preparedItem; } } // by default errors are not thrown var throwErrorForMissingLinkedItems = false; // check if errors should be thrown for missing linked items if (queryConfig.throwErrorForMissingLinkedItems === false || queryConfig.throwErrorForMissingLinkedItems === true) { // variable is a boolean throwErrorForMissingLinkedItems = queryConfig.throwErrorForMissingLinkedItems; } // throw error if item is not in response and errors are not skipped if (!rawItem) { if (throwErrorForMissingLinkedItems) { throw Error("Linked item with codename '" + codename + "' could not be found in Delivery response.\n This linked item was requested by '" + element.name + "' element of '" + element.type + "'.\n Error can usually be solved by increasing 'Depth' parameter of your query.\n Alternatively, you may prevent this error by disabling 'throwErrorForMissingLinkedItems' in query configuration."); } return undefined; } var mappedLinkedItem; // try resolving item using custom item resolver if its set if (queryConfig.itemResolver) { var customMappedItem = queryConfig.itemResolver(element, rawItem, modularContent, queryConfig); if (customMappedItem) { // if user used custom mapping, make sure to add 'system' and 'elements' properties to result customMappedItem.system = resolvers_1.stronglyTypedResolver.mapSystemAttributes(rawItem.system); customMappedItem.elements = rawItem.elements; mappedLinkedItem = customMappedItem; } } // original resolving if item is still undefined if (!mappedLinkedItem) { var mappedLinkedItemResult = this.mapElements({ item: rawItem, modularContent: modularContent, preparedItems: preparedItems, processingStartedForCodenames: mappingStartedForCodenames, processedItems: processedItems, queryConfig: queryConfig }); mappedLinkedItem = mappedLinkedItemResult.item; } // add to processed items processedItems[codename] = mappedLinkedItem; return mappedLinkedItem; }; ElementMapper.prototype.mapRichTextLinks = function (linksJson) { var links = []; for (var _i = 0, _a = Object.keys(linksJson); _i < _a.length; _i++) { var linkId = _a[_i]; var linkRaw = linksJson[linkId]; links.push(new models_1.Link({ codename: linkRaw.codename, linkId: linkId, urlSlug: linkRaw.url_slug, type: linkRaw.type, })); } return links; }; ElementMapper.prototype.mapRichTextImages = function (imagesJson) { var images = []; for (var _i = 0, _a = Object.keys(imagesJson); _i < _a.length; _i++) { var imageId = _a[_i]; var imageRaw = imagesJson[imageId]; images.push(new models_1.RichTextImage({ description: imageRaw.description, imageId: imageRaw.image_id, url: imageRaw.url, height: imageRaw.height, width: imageRaw.width })); } return images; }; ElementMapper.prototype.resolveElementMap = function (item, originalElementCodename) { var resolvedElementPropertyName = undefined; // resolve using property resolver if (item._config && item._config.propertyResolver) { resolvedElementPropertyName = item._config.propertyResolver(originalElementCodename); } // if property hasn't been resolved, try getting name using decorator if (resolvedElementPropertyName === originalElementCodename || !resolvedElementPropertyName) { resolvedElementPropertyName = elements_1.ElementDecorators.getPropertyName(item, originalElementCodename); } if (!resolvedElementPropertyName) { // use original element codename resolvedElementPropertyName = originalElementCodename; } // check for collissions if (this.collidesWithAnotherProperty(resolvedElementPropertyName, item)) { // try to resolve collission using dedicated resolver var collisionResolver = this.getCollisionResolver(); resolvedElementPropertyName = collisionResolver(resolvedElementPropertyName); // verify again if the new element collides if (this.collidesWithAnotherProperty(resolvedElementPropertyName, item)) { console.warn("Element '" + resolvedElementPropertyName + "' collides with another element in same type. Element mapping is skipped. Source item: '" + item.system.codename + "'"); return { shouldMapElement: false, resolvedName: '' }; } } return { resolvedName: resolvedElementPropertyName, shouldMapElement: true }; }; ElementMapper.prototype.getCollisionResolver = function () { return this.config.collisionResolver ? this.config.collisionResolver : config_1.defaultCollissionResolver; }; ElementMapper.prototype.collidesWithAnotherProperty = function (elementName, item) { return item[elementName] ? true : false; }; return ElementMapper; }()); exports.ElementMapper = ElementMapper; //# sourceMappingURL=element.mapper.js.map