UNPKG

@kontent-ai/delivery-sdk

Version:
182 lines (161 loc) 6.97 kB
import { codenameHelper } from '../utilities'; import { IDeliveryClientConfig } from '../config'; import { Contracts } from '../contracts'; import { IContentItem, IContentItemsContainer, IContentItemWithRawDataContainer, IContentItemWithRawElements } from '../models'; import { ElementMapper } from './element.mapper'; export interface IMapItemResult< TContentItem extends IContentItem = IContentItem, TLinkedItemType extends IContentItem = IContentItem > { item: TContentItem; processedItems: IContentItemsContainer<TLinkedItemType>; preparedItems: IContentItemWithRawDataContainer; processingStartedForCodenames: string[]; } export interface IMultipleItemsMapResult< TContentItem extends IContentItem = IContentItem, TLinkedItemType extends IContentItem = IContentItem > { items: TContentItem[]; linkedItems: IContentItemsContainer<TLinkedItemType>; } export interface ISingleItemMapResult< TContentItem extends IContentItem = IContentItem, TLinkedItemType extends IContentItem = IContentItem > { item: TContentItem; linkedItems: IContentItemsContainer<TLinkedItemType>; } export class ItemMapper<TContentItemType extends IContentItem> { private readonly elementMapper: ElementMapper<TContentItemType>; constructor(readonly config: IDeliveryClientConfig) { this.elementMapper = new ElementMapper(config); } /** * Maps single item to its proper strongly typed model from the given response * @param response Response used to map the item * @param queryConfig Query configuration */ mapSingleItemFromResponse<TContentItem extends TContentItemType = TContentItemType>( response: Contracts.IViewContentItemContract ): ISingleItemMapResult<TContentItem, TContentItemType> { const mapResult = this.mapItems<TContentItem>({ mainItems: [response.item], linkedItems: Object.values(response.modular_content) }); return { item: mapResult.items[0], linkedItems: mapResult.linkedItems }; } /** * Maps multiple items to their strongly typed model from the given response * @param response Response used to map the item * @param queryConfig Query configuration */ mapMultipleItemsFromResponse<TContentItem extends TContentItemType = TContentItemType>( response: Contracts.IItemsWithModularContentContract ): IMultipleItemsMapResult<TContentItem, TContentItemType> { const mapResult = this.mapItems<TContentItem>({ mainItems: response.items, linkedItems: Object.values(response.modular_content) }); return mapResult; } /** * Maps item contracts to full models */ mapItems<TContentItem extends TContentItemType = TContentItemType>(data: { mainItems: Contracts.IContentItemContract[]; linkedItems: Contracts.IContentItemContract[]; }): IMultipleItemsMapResult<TContentItem, TContentItemType> { const processedItems: IContentItemsContainer<TContentItem> = {}; const preparedItems: IContentItemWithRawDataContainer = {}; const processingStartedForCodenames: string[] = []; const mappedMainItems: TContentItem[] = []; const mappedLinkedItems: IContentItemsContainer<TContentItem> = {}; const itemsToResolve: Contracts.IContentItemContract[] = [...data.mainItems, ...data.linkedItems]; // first prepare reference for all items for (const item of itemsToResolve) { preparedItems[codenameHelper.escapeCodenameInCodenameIndexer(item.system.codename)] = { item: this.createContentItem(item), rawItem: item }; } // then resolve main items for (const item of data.mainItems) { const itemResult = this.mapItem<TContentItem>({ item: preparedItems[codenameHelper.escapeCodenameInCodenameIndexer(item.system.codename)], processedItems: processedItems, preparedItems: preparedItems, processingStartedForCodenames: processingStartedForCodenames }); mappedMainItems.push(itemResult.item); } // and linked items for (const item of data.linkedItems) { const itemResult = this.mapItem<TContentItem>({ item: preparedItems[codenameHelper.escapeCodenameInCodenameIndexer(item.system.codename)], processedItems: processedItems, preparedItems: preparedItems, processingStartedForCodenames: processingStartedForCodenames }); mappedLinkedItems[item.system.codename] = itemResult.item; } return { items: mappedMainItems, linkedItems: mappedLinkedItems }; } /** * Maps item contract to full model */ private mapItem<TContentItem extends TContentItemType = TContentItemType>(data: { item: IContentItemWithRawElements; processedItems: IContentItemsContainer<TContentItem>; processingStartedForCodenames: string[]; preparedItems: IContentItemWithRawDataContainer; }): IMapItemResult<TContentItem, TContentItemType> { if (!data.item) { throw Error(`Could not map item because its undefined`); } const result = this.elementMapper.mapElements<TContentItem>({ dataToMap: data.item, preparedItems: data.preparedItems, processingStartedForCodenames: [], processedItems: data.processedItems }); if (!result) { throw Error(`Mapping of content item '${data.item.item.system.codename}' failed`); } return { item: result.item, processedItems: result.processedItems, preparedItems: result.preparedItems, processingStartedForCodenames: result.processingStartedForCodenames }; } private createContentItem(item: Contracts.IContentItemContract): IContentItem { const contentItem: IContentItem = { elements: {}, system: { codename: item.system.codename, collection: item.system.collection, id: item.system.id, language: item.system.language, lastModified: item.system.last_modified, name: item.system.name, sitemapLocations: item.system.sitemap_locations, type: item.system.type, workflowStep: item.system.workflow_step ?? null, workflow: item.system.workflow ?? null } }; return contentItem; } }