Взаимодействие компонента Gant с бэкенд-сервисами
Компонент Gant поддерживает следующие способы взаимодействия с бэкенд-сервисами:
- REST API;
- веб-сокеты.
REST API
По умолчанию используется взаимодействие через REST API.
В свойстве endpointData, вложенном в свойство GraphSettings, должен быть описан метод fetch по следующему образцу:
const fetch = async ({ requestType, params }) => {
        return sendDataSet(requestType, params, { showWaitingContainer: false });
      };
requestType описывает тип запроса и принимает значения, описанные в следующей таблице.
Контракты взаимодействия с бэкенд-сервисами
| Тип запроса | Выполняемое действие | 
|---|---|
| getCount(): number | Возвращает количество работ | 
| getIds(skip: number, take: number): INodeIdWithVersion[] | Возвращает массив работ в диапазоне строк с идентификаторами и версиями | 
| getData(nodes: INodeIdWithVersion[], columns: ColumnId[]): IDataResult[] | Возвращает значения в указанных столбцах по указанным работам | 
| getFilterState(nodes: INodeIdWithVersion[], filters: Filters): IFilterState[] | Возвращает признак того, удовлетворяет ли работа параметрам фильтра | 
| getGraph(nodes: INodeIdWithVersion[]): IDataResult[] | Возвращает полосы диаграммы Ганта | 
| getRelations(nodes: INodeIdWithVersion[]): IRelationsResult[] | Возвращает стрелки диаграммы Ганта | 
| getMetrics(nodes: INodeIdWithVersion[], columns: ColumnId[], scale: CalendarScale, from: string, to: string): IDataResult[] | Возвращает значения в календарной сетке, сгруппированные по датам | 
| getRowsUpdateSince(skip: number, take: number, since: number): GetRowsUpdateSinceResult | Возвращает список работ (с идентификаторами и версиями), изм енившихся с определенного момента | 
Данные для компонента Gant могут иметь объем в сотни тысяч записей в базе данных. При использовании взаимодействия с бэкенд-ервисами через REST API объем передаваемых данных ограничен. Для преодоления этого ограничения данные компоненту передаются частями размером chunkSize и отправляются на сервер в количестве не более requestsLimit запросов одновременно.
Следующий фрагмент кода демонстрирует свойства компонента для управления передачей данных при взаимодействии через REST API с бэкенд-сервисами. В примере показаны значения по умолчанию.
chunkSize: 10000,
requestsLimit: 2,
Следующий фрагмент кода экранной формы демонстрирует взаимодействие через REST API:
<Container
  Name="GantRest"
  ContainerType="Page"
  Scripts={[
    async () => {
      const viewModel = await sendDataSet('getViewModel');
      const { mainGridId, tables, filterOperations } = viewModel.model;
      const fetch = async ({ requestType, params }) => {
        return sendDataSet(requestType, params, { showWaitingContainer: false });
      };
      const settings = {};
      settings.groupingVisible = true;
      settings.headers = tables[mainGridId].columns;
      // максимально возможная глубина вложенности, приходит с бэкенда.
      // чем она меньше - тем выше контрастность между цветами уровней
      // однако можно задать индивидуальные цвета для каждого уровня
      // с помощью свойств getLevelBGColor и getLevelTextColor.
      // наличие этого свойства также важно для правильного выделения цветом кликнутой строки
      settings.maxDeepLevel = tables[mainGridId].maxDeepLevel + 1;
      settings.endpointData = { fetch };
      // во viewModel должны прийти поддерживаемые бэкендом операции фильтрации,
      // но можно просто передать {}, тогда будут использоваться дефолтные
      // более подробное описание в контракте
      settings.filterOperations = filterOperations;
      // поиск пока реализован только по строковым колонкам
      settings.searchEnabled = true;
      // отображение контрола разворота узлов до определенного уровня
      settings.wbsEnabled = true;
      const graphSettings = {};
      graphSettings[mainGridId] = settings;
      const pallete = {};
      // ширина смещения уровня в пикселях
      pallete.stripeWidth = 15;
      // задание индивидуальных цветов уровням
      // pallete.getLevelBGColor = (level, maxLevel) => {
      //   const levels = {
      //     0: '#BBBBBB',
      //     4: 'red',
      //     20: 'rgba(1, 1, 1, 0.5)',
      //   };
      //   return levels[level];
      // };
      // pallete.getLevelTextColor = (level, maxLevel) => level > maxLevel / 2 ? 'red' : 'black';
      setState({ mainGridId, graphSettings, pallete });
    },
  ]}
>
  <Gant
    Visible-var="mainGridId"
    Pallete-var="pallete"
    MainGridId-var="mainGridId"
    GraphSettings-var="graphSettings"
  />
</Container>;
Веб-сокеты
Для переключения на взаимодействие через веб-сокеты, в свойство endpointData, вложенное в свойство GraphSettings, должен быть передан параметр networkServiceType: 'socket'.
В свойстве endpointData, вложенном в свойство GraphSettings, должен быть описан метод stream по следующему образцу:
const stream = p => {
        return {
          stream: connection.stream(p.requestType, p?.params || {}),
          postProcess: data => data,
        };
      };
requestType описывает тип запроса и принимает значения, описанные в следующей таблице.
Контракты взаимодействия с бэкенд-сервисами
| Тип запроса | Выполняемое действие | 
|---|---|
| getData(nodes: Nullable<INodeIdWithVersion[]>, columns: ColumnId[], useZip?: boolean, since?: number): SocketDataResult | Возвращает значения в указанных столбцах по указанным работам | 
| getFilterState(nodes: Nullable<INodeIdWithVersion[]>, filters: Filters): IFilterState[] | Возвращает признак того, удовлетворяет ли работа параметрам фильтра | 
Взаимодействие с бэкенд-сервисами через веб-сокеты происходит быстрее, чем через REST API.
При использовании взаимодействия через веб-сокеты можно включить кэширование данных на клиентской стороне. Для включения кэширования используется свойство cache, вложенное в GraphSettings. Данные сохраняются в хранилище данных внутри браузера — IndexedDB. При обновлении версии компонента Gant содержимое кэша сбрасывается.
Поскольку взаимодействие через веб-сокеты является ненадежным, для контроля целостности данных компо нент Gant периодически запрашивает через REST API все работы, которые изменились с момента последнего обновления кэша. С этой целью компонент Gant отправляет запрос getRowsUpdateSince (см. описание в разделе выше). В связи с большим объемом данных, например, вследствие массового импорта данных из внешней системы, данные отправляются клиенту постранично. Для управления разбивкой на страницы используются параметры skip и take запроса getRowsUpdateSince. Метод возвращает работы, у которых значение параметра since больше переданного значения, и количество таких работ.
Параметр since представляет собой версию моментального снимка базы данных источника данных. Следующая таблица демонстрирует отличие версии работы от версии моментального снимка базы данных источника данных.
| Идентификатор работы | Версия работы | Версия моментального снимка БД | Комментарий | 
|---|---|---|---|
| 1 | 1 | 1 | Работы 1, 2, 3 и 4 появились в БД при генерации данных. Начальные значения версии для этих работ — 1 и значение моментального снимка БД — 1. | 
| 2 | 2 | 2 | Пользователь изменил работу 2, в результате чего работа 2 получила версию 2, версия моментального снимка БД приняла значение 2. | 
| 3 | 2 | 3 | Далее в одной транзакции пользователь изменил работы 3 и 4. Обе работы получили версию 2. | 
| 4 | 2 | 3 | Версия моментального снимка БД приняла значение 3. | 
| 5 | 1 | 4 | Далее в следующей транзакции пользователь создал работу 5. Версия работы 5 — 1, версия моментального снимка БД приняла значение 4. | 
Следующий фрагмент кода демонстрирует настройку параметров для использования метода getRowsUpdateSince с периодичностью timeoutUpdateSince.
timeoutUpdateSince: 60000,              // частота отправки запроса данных в миллисекундах
checkMissedNotificationsEnabled: true,  // при установке в true этого параметра используется тип запроса getRowsUpdateSince
Фрагмент кода экранной формы с использованием взаимодействия через веб-сокеты показан в следующем примере:
<Container
  Name="GantSocket"
  ContainerType="Page"
  Scripts={[
    async () => {
      const viewModel = await sendDataSet('getViewModel');
      const { mainGridId, tables } = viewModel.model;
      const connection = createConnection({
        hubUrl: '',
        eventName: '',
      });
      const stream = p => {
        return {
          stream: connection.stream(p.requestType, p?.params || {}),
          postProcess: data => data,
        };
      };
      const settings = {};
      settings.headers = tables[mainGridId].columns;
      settings.maxDeepLevel = tables[mainGridId].maxDeepLevel + 1;
      settings.endpointData = {};
      settings.endpointData.stream = stream;
      settings.endpointData.onEditTransactionUpdate = () => {};
      settings.endpointData.networkServiceType = 'socket';
      settings.filterOperations = {};
      settings.wbsEnabled = true;
      const graphSettings = {};
      graphSettings[mainGridId] = settings;
      setState({ mainGridId, graphSettings });
    },
  ]}
>
  <Gant
    Visible-var="mainGridId"
    Pallete={{}}
    MainGridId-var="mainGridId"
    GraphSettings-var="graphSettings"
  />
</Container>;