kentico-cloud-delivery
Version:
Official Kentico Cloud Delivery SDK
406 lines • 22.4 kB
JavaScript
"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