@quantumart/qa-engine-page-structure
    TypeScript icon, indicating that this package has built-in type declarations

    0.1.1 • Public • Published

    @quantumart/qa-engine-page-structure

    Данная библиотека предоставляет набор инструментов для построения фронтенда для сайта на базе QP

    Demo site

    Описание

    Необходимый бэкенд для построения сайта:

    • База данных на основе qp
    • API на .net core с методом получения структуры сайта (дерево UniversalAbstractItem)

    Типы данных

    interface UniversalAbstractItem {
      readonly type: string;
      readonly id: number;
      readonly alias?: string;
      readonly title?: string;
      isPage: boolean;
      readonly sortOrder: number;
      readonly regionIds?: number[];
      readonly cultureId?: number;
      childItems?: UniversalAbstractItem[];
      readonly untypedFields: { [key: string]: any };
    }

    Интерфейс UniversalAbstractItem соответствующий UniversalAbstractItem из бэкенд апи. Описывает элемент структуры сайта (виджет или страницу).

    class AbstractItem
    

    Для дальнейшей работы со структурой сайта надо сконвертировать элементы структуры сайта в экземпляры классов AbstractItem (или их наследников). Класс AbstractItem предоставляет возможность подниматься по дереву (есть свойство parent), предоставляет свойства по получению полей из untypedFields, а также свойство для получения относительного пути к странице.

    interface BaseItemModel {
      readonly type: string;
      readonly id: number;
      readonly title: string;
      readonly alias: string;
      readonly sortOrder: number;
      readonly regionIds?: number[];
      readonly cultureId?: number;
      readonly isPage: boolean;
    }
    
    interface BaseWidgetModel extends BaseItemModel {
      zoneName: string;
      children?: BaseWidgetModel[];
    }
    
    interface BasePageModel extends BaseItemModel {
      widgets?: BaseWidgetModel[];
    }
    

    Интерфейсы BaseItemModel, BaseWidgetModel и BasePageModel используются уже непосрественно для компонентов вашего сайта.

    Функции

    Использование Для построения страницы с использованием библиотеки нужно сначала получить структуру сайта (дерево элементов, соответствующих интерфейсу UniversalAbstractItem), сконвертировать его в дерево экземпляров класса AbstractItem (или его наследников).

    Далее нужно определить стартовую страницу по хосту.

    Далее можно найти Path (содержит AbstractItem и остаточный путь) соответствующий текущему пути, при этом можно использовать TargetingFilter для структуры сайта.

    Из полученного Path можно уже построить модель данных для страницы.

    import {
      AbstractItem,
      DefaultWidgetFilter,
      findPath,
      getPageStructureContextScript,
      getStartPage,
      ServerPageStructureContextProvider,
    } from '@quantumart/qa-engine-page-structure';
    
    
    // ....
    
    const rawSiteStructure = await apiService.getSiteStructure();
    const siteStructure = new AbstractItem(rawSiteStructure, type => type === PageType.StartPage);
    
    const startPage = getStartPage(siteStructure, req.headers.host || '');
    if (!startPage) {
      res.status(NOT_FOUND).send('Page not found');
      return;
    }
    const siteStructureFilter = new SiteStructureFilter();
    const path = findPath(startPage, req.path, siteStructureFilter);
    const widgetFilter = new DefaultWidgetFilter(req.path);
    
    const modelFactory = new ModelFactory(widgetFilter);
    const pageModel = await modelFactory.buildPageModel(path);
    
    

    Модель строится исходя из типа найденного AbstractItem и остаточного пути. Пример реализации:

    interface PageModelResult {
      notFound?: boolean;
      redirectTo?: string;
      permanentRedirect?: boolean;
      pageModel?: BasePageModel;
    }
    
    export class ModelFactory {
      private readonly _widgetFilter?: TargetingFilter;
      constructor(widgetFilter?: TargetingFilter) {
        this._widgetFilter = widgetFilter;
      }
    
      async buildPageModel(pathData?: PathData): Promise<PageModelResult> {
        if (!pathData) {
          return { notFound: true };
        }
        const page = pathData.abstractItem as AbstractItem;
        const pageType = page.type as PageType;
        switch (pageType) {
          case PageType.StartPage: {
            // ...
          }
          case PageType.TextPage: {
            if (pathData.remainingPath && pathData.remainingPath !== '/') {
              return { notFound: true };
            }
            const result: TextPageModel = {
              ...(await this.mapBasePage(page)),
              text: page.getField('TEXT', ''),
              hideTitle: page.getField('HIDETITLE', false),
            };
            return { pageModel: result };
          }
          case PageType.RedirectPage: {
            return { redirectTo: page.getField('REDIRECTTO', ''), permanentRedirect: true };
          }
          default:
            return { notFound: true };
        }
      }
    
    
      private isMatchingWidget(item: AbstractItem): boolean {
        return !item.isPage && (!this._widgetFilter || this._widgetFilter.match(item));
      }
    
      private async mapBaseWidget(widget: AbstractItem): Promise<BaseWidgetModel> {
        const childrenPromises = widget.childItems?.filter(x => this.isMatchingWidget(x)).map(x => this.mapWidget(x));
        const children = childrenPromises ? await Promise.all(childrenPromises) : [];
        return {
          id: widget.id,
          title: widget.title || '',
          type: widget.type,
          alias: widget.alias || '',
          zoneName: widget.zoneName,
          cultureId: widget.cultureId,
          regionIds: widget.regionIds,
          sortOrder: widget.sortOrder,
          isPage: false,
          children,
        };
      }
    
      private async mapBasePage(page: AbstractItem): Promise<BasePageModel> {
        const widgetPromises = getPageWidgets(page, this._widgetFilter)?.map(x => this.mapWidget(x));
        const widgets = widgetPromises ? await Promise.all(widgetPromises) : [];
        return {
          id: page.id,
          title: page.title || '',
          type: page.type,
          alias: page.alias || '',
          cultureId: page.cultureId,
          regionIds: page.regionIds,
          sortOrder: page.sortOrder,
          isPage: true,
          widgets,
        };
      }
    
      private async mapWidget(widget: AbstractItem): Promise<BaseWidgetModel> {
        const type = widget.type as WidgetType;
        switch (type) {
          case WidgetType.FaqWidget: {
            const result: FaqWidgetModel = {
              ...(await this.mapBaseWidget(widget)),
              header: widget.getField('HEADER', ''),
              questions: await apiService.getFaqQuestions(widget.getField<number[]>('QUESTIONS', [])),
            };
            return result;
          }
          case WidgetType.HtmlWidget: {
            const result: HtmlWidgetModel = {
              ...(await this.mapBaseWidget(widget)),
              html: widget.getField('HTML', ''),
            };
            return result;
          }
          default:
            console.error(`unknown widget type: ${type}`);
            return this.mapBaseWidget(widget);
        }
      }
    }
    

    Keywords

    none

    Install

    npm i @quantumart/qa-engine-page-structure

    DownloadsWeekly Downloads

    2

    Version

    0.1.1

    License

    MIT

    Unpacked Size

    121 kB

    Total Files

    42

    Last publish

    Collaborators

    • v.sarenkov
    • akuznetsova
    • celutp
    • russian_dragon
    • thorgnir