1С-Битрикс Разработчикам – REST-интерфейсы к своей сущности в коробочном Битрикс24

1с-битрикс разработчикам – rest-интерфейсы к своей сущности в коробочном битрикс24

День добрый.

Продолжаем учиться правильно кастомизировать Битрикс24

:)  

В этот раз рассмотрим тему – создание REST-интерфейсов к своей сущности в коробке Битрикс24

Нередко возникает задача обмена данными между Битрикс24 и другими системами.  Что делать если обмениваться данными нужно по сущности, которую мы добавили в Битрикс24 сами? Создавать велосипед?  Так уже все придумано до нас :)

Слово коллегам из . В изучении этой темы вам поможет
Вебинар по этой теме к сожалению не состоялся.
[spoiler]

Введение

Одна из самых частых задач доработки Битрикс24 — интеграция с внешними системами. Как правило, возникает необходимость получить доступ к данным в Б24 извне.

Эту задачу можно решать разными способами. В коробочной версии никто не запрещает создать свои скрипты, которые будут отдавать или изменять данные в Б24. При всей простоте идеи, реализация, все же, содержит «подводные камни»: вам придется позаботиться о безопасности (разграничение доступа к скриптам), проектировать и реализовывать инфраструктуру веб-службы, защищаться от «спама» запросами и пр. Это уже становится похожим на «велосипед».

Правильный подход — воспользоваться возможностями стандартного модуля REST API (который теперь добавлен и в БУС!), где эти задачи решены. В этой статье мы рассмотрим его не со стороны потребителя веб-служб, а со стороны разработчика.

Вы научитесь:

  • создавать свои REST-методы;
  • REST-события;
  • места для встраивания приложений.

В качестве бонуса рассмотрим использование REST API «внутри коробки», а также разработку JS-приложений, в частности, клиентскую шаблонизацию с использованием Mustache как расширения UI-библиотеки.

Большую часть задач мы будем выполнять на основе сущности «торговая точка», которую мы реализовали ранее. Говоря конкретнее, мы разработаем REST-интерфейс данной сущности, который будет включать методы, события и места для встраивания.

Рекомендуем ознакомиться с предыдущими материалами по разработке сущности «торговая точка:»


Добавление REST-методов

Описание и реализация методов

Для добавления своих методов в REST API Битрикс24 необходимо подписаться на событие OnRestServiceBuildDescription. Обработчик должен вернуть массив следующего вида.

Все методы REST API относятся к своим scope. Scope нужны для того, чтобы разграничивать доступ к методам для приложений. Когда вы создаете веб-хук или приложение, вы должны отметить флажками права доступа. Права — это и есть scope.

В качестве идентификатора scope хорошим решением будет выбирать код модуля. В нашем случае, код модуля — academy.crmstores.

Внутри scope должны быть названия методов, как они будут доступны в REST API и соответствующие им обработчики — методы, которые будут вызваны при вызове REST-методов. Как и в случае с обработчиками событий, указывайте в качестве callable следует использовать название функции или массив с названиями класса и метода.

В REST-интерфейс торговых точек добавим четыре метода для всех CRUD-операций.

В качестве обработчиков выступают методы нового класса AcademyCrmStoresRestRestApi. Классы, содержащие методы-обработчики, должны быть унаследованы от класса IRestService. Рассмотрим методы-обработчики подробнее.

Каждый метод-обработчик получает на вход три аргумента.

Аргумент Тип данных Описание
$params array Значения параметров, с которыми вызван REST-метод, кроме параметра start.
$start int Значение параметра start, используется для постраничной навигации в списочных методах.
$server CRestServer Объект, содержащий служебную информацию о REST-вызове, например, авторизацию и пр.



Реализация списочного метода crm.store.list

REST-метод crm.store.list позволяет получить список торговых точек, при этом пользователь может задать фильтр, сортировку и поля, которые должны попасть в результирующий набор. Такой метод должен быть списочным, то есть возвращать строки результирующего набора страницами по 50 элементов.

Пример вызова REST-метода:

Или (то же самое) с помощью BX24.js:

Начнем с реализации без разбиения на страницы. Будем использовать метод StoreTable::getList.

Как видите, реализация достаточно проста. Обратите внимание на то, что в случае ошибок метод выбрасывает исключение RestException. Такое исключение обрабатывается модулем REST API и выводится в виде объекта-ошибки. Например, если бы модуль (в самом начале метода) не подключился, пользователь получил бы следующий ответ:

Теперь реализуем разделение на страницы. В классе IRestService (от которого мы унаследовали класс RestApi) есть метод getNavData, генерирующий данные для постраничной навигации списочных методов. Он принимает на вход два аргумента: $start — значение параметра вызова метода start и флаг bORM. Если этот флаг установлен в значение true, метод возвратит массив с ключами limit и offset (подходят в качестве параметров getList ядра D7), в противном случае — массив с параметрами nPageSize и iNumPage (подходят для старого ядра).

С этого момента метод возвращает порции по 50 элементов, начиная со start. Однако пользователь все еще не может определить, существует ли следующая/предыдущая страница. Как следствие, не работают методы more, next и total объекта-результата библиотеки BX24.js. Чтобы исправить эту ситуацию, необходимо вместе с результирующим набором вернуть еще два параметра: total и next.

Для этого так же существует метод в классе IRestService, однако он работает только для старого ядра. Поэтому нам придется написать свой метод, возвращающий эти два значения. Создадим класс AcademyCrmStoresUtilRestUtil и метод prepareNavResult в нем.

Метод принимает на вход класс DataManager конкретной сущности и параметры метода getList, с которыми выполняли выборку. Обратите внимание, что ключ next присутствует только в том случае, если следующая страница существует.

Воспользуемся этим методом при выводе результата.

Здесь есть неочевидный момент. Наш метод-обработчик должен вернуть результат следующего вида:

То есть массив, содержащий и числовые, и строковые ключи. Модуль REST API, впоследствии, удалит ключи total и next, а их значения перенесет в другую часть ответа (как мы и привыкли).

Реализация остальных методов

Следующие три метода довольно просты в реализации и похожи друг на друга. Рассмотрим только метод добавления торговой точки crm.store.add.

Пример вызова метода:

Код обработчика (RestApi::add):

public static function add($params, $start, $server)
{
  if (!Loader::includeModule('academy.crmstores')) {
    throw new RestException(Loc::getMessage('CRMSTORES_NO_MODULE'), 'no_module');
  }

  $result = StoreTable::add($params['fields']);

  if (!$result->isSuccess()) {
    throw new RestException(implode(', ', $result->getErrorMessages()));
  }

  if (Loader::includeModule('bizproc')) {
    CBPDocument::AutoStartWorkflows(
        StoreDocument::getComplexDocumentType(),
        CBPDocumentEventType::Create,
        StoreDocument::getComplexDocumentId($result->getId()),
        array(),
        $errors
        );
  }

  return $result->getId();
}

Для добавления самой торговой точки используется метод StoreTable::add. Поскольку мы реализовали поддержку бизнес-процессов на предыдущем вебинаре, мы должны позаботиться и об автоматическом запуске БП при добавлении документа. Метод CBPDocument::AutoStartWorkflows мы уже рассматривали в предыдущей статье.

Аналогично реализован метод crm.store.update. В методе crm.store.delete нам не нужно останавливать и удалять бизнес-процессы, т. к. это уже реализовано на уровне DataManager’а — StoreTable.

Вывод названия scope и создание веб-хука

На данном этапе методы уже доступны для приложений, в том числе и во входящих веб-хуков. Перейдите на страницу создания входящего веб-хука. В списке прав доступа можно увидеть название добавленного scope.

image4.png

Однако данный scope не имеет локализованного названия.

Шаблон компонента создания веб-хука выводит названия прав доступа из языковых констант. Согласно этой логике, в файле языковых констант этого шаблона должно быть название нашего scope. Естественно, мы не можем изменить этот файл, так как это является изменением ядра.

Однако есть другой способ. Зная, что при загрузке языковых констант, они сливаются в один большой массив, не имеет значения, где именно объявлена та или иная константа. То есть достаточно сделать так, чтобы до вызова шаблона компонента языковая константа была загружена.

Обработчик события OnRestServiceBuildDescription как раз подходит для этого. Шаблон компонента будет использовать константу, имя которой строится следующим образом: REST_SCOPE_<SCOPE В ВЕРХНЕМ РЕГИСТРЕ>. Учитывайте также, что языковые константы Битрикс загружает «ленивым» способом. То есть, чтобы константа точно была загружена, к ней необходимо хотя бы один раз обратиться.

Теперь на странице создания веб-хука выводится правильное название права доступа.

image9.png

Создадим веб-хук, которому разрешено вызывать методы торговых точек.

Тестирование методов с помощью REST-клиента Postman

Теперь можно приступить к тестированию созданного REST API. Для этого мы будем использовать созданный входящий веб-хук и REST-клиент Postman. Вы можете использовать и другой клиент.

После установки Postman вам понадобится зарегистрировать учетную запись, чтобы активировать все возможности программы.

Создайте рабочее пространство (это в верхней центральной части): нажмите на стрелку, затем Create New.

image19.png

Задайте имя и нажмите Create Workspace.

Далее создайте окружение. Считайте окружение набором переменных, которые можно подставлять в запросы. В окружение мы запишем адрес созданного веб-хука, чтобы использовать его во всех запросах. Нажмите на шестеренку в правом верхнем углу.

image21.png

В открывшемся окне нажмите кнопку Add, введите название окружения (лучше всего указать адрес сайта, на который будут отправляться запросы) и задайте переменную webhook.

image18.png

Нажмите кнопку Add и закройте окно настройки окружений. В выпадающем меню в правом верхнем углу выберите созданное окружение.

image24.png

Создайте коллекцию запросов CRUD. В выпадающем меню New выберите Collection в левом верхнем углу экрана.

image17.png

Задайте название коллекции — CRUD. Других настроек совершать не требуется. Нажмите кнопку Create.

В том же выпадающем меню New выберите Request. Укажите название запроса. Мы будем использовать название метода, который собираемся вызвать. Чтобы увидеть, что добавленные REST-методы доступны, вызовем methods. Соответственно, назовем запрос Methods. Также укажите коллекцию (CRUD), в которую будет сохранен метод.

В строке запроса укажите {{webhook}}/methods. Вместо {{webhook}} Postman подставит адрес, который мы задали в переменных окружения, получится корректный адрес для вызова метода.

image2.png

Этот метод не имеет параметров, поэтому других настроек не требуется. Нажмите Send, чтобы отправить запрос.

В правой части окна (или в нижней, в зависимости от настроек внешнего вида) вы увидите ответ сервера.

image26.png

Обратите внимание на конец списка методов. В нем присутствуют добавленные нами crm.store.*, это значит, что их можно вызвать. Остальные методы являются системными. Они присутствуют всегда.

Приступим к тестированию наших методов. Создайте запрос CRM Store List и настройте его как показано на скриншоте.

image22.png

Нажмите кнопку Send. В ответе должны присутствовать все торговые точки. Обратите внимание, что ключ total находится не внутри result.

Поскольку у нас мало торговых точек (меньше 50), мы не видим работу постраничной навигации. Модуль REST API позволяет менять количество элементов на странице (вплоть до метода). Это количество задается константой LIST_LIMIT класса IRestService. Переопределим эту константу в классе обработчиков REST-методов.

И снова сделаем запрос. На этот раз мы видим две торговые точки и ключ next в ответе.

image12.png

Теперь если подставить значение next в параметр start запроса, метод вернет следующую страницу.

Постраничная навигация работает. Переопределение константы можно убрать.

Последующие методы протестируйте аналогичным образом самостоятельно.

Форматирование данных

Помните, что модуль REST API регламентирует форматы некоторых типов данных. Например, дата и время должны быть в формате ISO-8601, файлы могут передаваться как BASE64-закодированная строка или массив из двух элементов: имя файла и содержимое, закодированное алгоритмом BASE64.

Для преобразования даты и времени используйте следующие методы класса CRestUtil:

  • CRestUtil::ConvertDate
  • CRestUtil::ConvertDateTime
  • CRestUtil::unConvertDate
  • CRestUtil::unConvertDateTime

Для обработки файлов используйте метод CRestUtil::saveFile. Этот метод сохраняет файл в файловой системе и возвращает массив, описывающий этот файл (CFile::MakeFileArray). Далее вы можете сохранить его в БД (CFile::SaveFile).

Добавление REST-событий

Описание REST-событий

События позволяют внешним приложениям реагировать на то, что происходит в Б24. Добавим для торговых точек три события: создание, изменение, удаление.

Для добавления своих REST-событий, добавьте их в описание, которое возвращает обработчик события OnRestServiceBuildDescription.

Как видите, события описываются наряду с REST-методами. Только вместо названия метода указывается ключ CRestUtil::EVENTS, значением которого является массив, описывающий события. Ключами этого массива являются названия событий, как они будут доступны приложениям, а значениями — массивы, описывающие привязку к обычным событиям Bitrix Framework.

Массив, описывающий REST-событие состоит из трех элементов:

  • код модуля, который отправляет событие Bitrix Framework,
  • название события,
  • функция, выполняющая подготовку данных события для REST-обработчика.

Проще говоря, нужно задать отображение событий Bitrix Framework в REST-события.

Поскольку API торговых точек представлено классом StoreTable, будем использовать его стандартные события. Названия этих событий строятся по определенным правилам, которые не очень удобно запоминать. Вместо этого названия событий можно сгенерировать с помощью класса BitrixMainEntityEvent.

В обработчике события OnRestServiceBuildDescription создадим объекты Event для всех событий, которые будут доступны через REST API.

$eventOnAdd = new Event(
    StoreTable::getEntity(),
    StoreTable::EVENT_ON_AFTER_ADD,
    array(),
    true
);
$eventOnUpdate = new Event(
    StoreTable::getEntity(),
    StoreTable::EVENT_ON_AFTER_UPDATE,
    array(),
    true
);
$eventOnDelete = new Event(
    StoreTable::getEntity(),
    StoreTable::EVENT_ON_AFTER_DELETE,
    array(),
    true
);

Теперь, чтобы получить правильное название события, на которое можно подписаться (в том числе, и через EventManager), нужно вызвать метод getEventType у любого из этих объектов. Опишем REST-события.

return array(
  ...,
  CRestUtil::EVENTS => array(
    'onCrmStoreAdd' => array(
      'academy.crmstores',
      $eventOnAdd->getEventType(),
      array(RestApi::class, 'prepareEventData')
    ),
    'onCrmStoreUpdate' => array(
      'academy.crmstores',
      $eventOnUpdate->getEventType(),
      array(RestApi::class, 'prepareEventData')
    ),
    'onCrmStoreDelete' => array(
      'academy.crmstores',
      $eventOnDelete->getEventType(),
      array(RestApi::class, 'prepareEventData')
    ),
  ),
);

В качестве фильтра используется метод RestApi::prepareEventData. На вход этот метод получит аргументы, которые передаются в обработчик события Bitrix Framework (например, для события OnAfterIBlockElementAdd это будет массив полей элемента ИБ), а также описание REST-обработчика, которому эти данные будут отправлены. Этот метод должен вернуть данные, которые следует передать в REST-обработчик. Реализуем этот метод.

В данном случае мы просто передаем в REST-обработчик все данные торговой точки, которые есть в объекте Event.

Тестирование REST-событий

Регистрация обработчиков REST-событий разрешена только для приложений (не веб-хуков). Создайте тестовое приложение со следующими настройками:

ПараметрЗначение
Название приложенияЛюбое на ваш выбор.
Приложение использует только APIДа.
Права доступаТорговые точки CRM
Укажите ссылкуУкажите любой адрес, на него во время тестирования не будут идти запросы. Можно указать какой-нибудь каталог на сервере с КП.
Укажите ссылку-callback для
события установки (необязательно)
Оставьте пустым.

Роль приложения будет играть REST-клиент Postman. Через него мы будем получать код доступа и отправлять запросы от имени приложения.

Обратите внимание, что для создания приложения вам необходим КП с активной лицензией и доступом через интернет, т. к. код и ключ приложения выпускает OAuth-сервер компании 1С-Битрикс, а для работы событий необходим доступ к вашему порталу с сервера очередей компании 1С-Битрикс.

В Postman добавьте новую переменную endpoint в окружение.

image23.png

Создайте коллекцию Events. В настройках коллекции, на вкладке Authorization выберите тип авторизации OAuth 2.0, Add auth data to установите равным Request URL.

image10.png

Нажмите на кнопку Get New Access Token. Заполните параметры следующим образом.

Если на вашем портале не установлен валидный SSL-сертификат, то в поле Auth URL допускается указание адреса с протоколом HTTP.

При обмене временного кода на Access token происходит запрос к Access Token URL. На момент написания статьи OAuth-сервер компании 1С-Битрикс поддерживает передачу параметров только методом GET, а Postman отправляет данные методом POST. Поэтому параметры запроса продублированы. Ситуация может измениться в будущем.

Последний параметр (code) оставьте незаполненным. Нам понадобится заполнить его вручную, когда Postman получит код.

Откройте консоль Postman (View ’ Show Postman Console), затем нажмите Request Token. Откроется окно авторизации Б24. Введите логин и пароль администратора (для тестирования получим полный доступ). После того, как вы нажмете «Войти», окно закроется, а в консоли появится неуспешный запрос к OAuth-серверу.

Разверните Request Body и скопируйте параметр code. Затем вернитесь в окно Request New Access Token и допишите это значение в Access Token URL. Затем еще раз нажмите Request Token. На эту операцию у вас есть 30 секунд.

image15.png

Если все сделано правильно, вы увидите окно с информацией об Access Token, а также Refresh Token. Нажмите в этом окне кнопку Use Token.

Теперь можно выполнять запросы от имени приложения. Поскольку авторизация настроена на уровне коллекции, она будет наследоваться всеми запросами в ней.

Создайте запрос Events и настройте его следующим образом.

image20.png

Нажмите Send. Ответом будет список событий, на которые можно подписать REST-обработчики.

image1.png

Так же как и в случае с методами, в списке присутствуют системные события, а в конце добавленные нами.

Создадим тестовый обработчик события в виде PHP-скрипта в каталоге приложения. Скрипт просто будет сохранять все запросы к нему в текстовый файл. В нашем случае скрипт будет расположен по пути /app/eventhandler.php.

Зарегистрируем этот скрипт в качестве обработчика события создания торговой точки. Создайте метод Event Bind.

image14.png

Нажмите Send. Вы увидите “result”: true, если обработчик успешно зарегистрирован.

Что произошло на самом деле? Фактически, модуль REST API подписался на событие модуля торговых точек. В этом можно убедиться, посмотрев в таблицу b_module_to_module.

image16.png

Теперь при создании торговой точки модуль REST API передаст событие на внешний сервер очередей, а тот, в свою очередь, зарегистрированному обработчику (скрипту). Если все настроено правильно, то появится текстовый файл с журналом запросов.

В контекстном меню запроса Event Bind выберите Duplicate. Назовите скопированный запрос Event Unbind и укажите вызов метода event.unbind, параметры оставьте те же. Выполните запрос. В ответе вы увидите, что был удален один обработчик.

Посмотрите в таблицу b_module_to_module. Обработчик модуля REST не удалился. Этот обработчик удалится при первом наступлении события, но REST-больше вызываться не будет.

Добавление мест встраивания приложений

Описание мест встраивания

Встраивание приложений позволяет разрабатывать расширяемые интерфейсы. В качестве примера добавим возможность приложениям создавать вкладки в карточке торговой точки.

Места встраивания описываются в обработчике события OnRestServiceBuildDescription.

Аналогично описанию событий, используется ключ CRestUtil::PLACEMENTS, значением является массив, в котором ключ — код места встраивания, как оно будет доступно в REST API, а значение — массив параметров. На текущий момент доступен лишь один параметр — private (если равен true, то REST-приложения не смогут зарегистрировать обработчик для этого места).

Для модуля торговых точек описание будет следующим:

Регистрация обработчиков мест встраивания

Убедимся, что CRM_STORE_DETAILS доступен в списке мест встраивания.

Создайте запрос Placement List в коллекции Events.

image5.png

Создадим тестовый обработчик места встраивания. Он просто будет выводить на экран все параметры запроса. В нашем случае он будет расположен по пути /app/placementhandler.php.

Создайте запрос Placement Bind со следующими параметрами и выполните его.

image8.png

Обработчик зарегистрирован, можно приступать к его запуску со страниц КП.

Запуск обработчиков мест встраивания на страницах КП

Сначала рассмотрим API встраивания в целом.

Подавляющее большинство задач решают следующие элементы API встраивания:

  • метод, возвращающий список зарегистрированных обработчиков для места встраивания,
  • компонент, запускающий один обработчик,
  • метод, запускающий один обработчик в слайдере.

Очевидно, что перед тем, как запускать обработчики, нужно узнать, какие из них зарегистрированы для конкретного места встраивания. Эту информацию можно получить с помощью метода PlacementTable::getHandlersList. Метод принимает на вход один аргумент — код места встраивания (тот, что мы указали при описании мест встраивания). Метод возвращает все необходимые данные для запуска обработчика.

Вызовем этот метод на отдельной странице и распечатаем результат.

В списке видим зарегистрированный нами обработчик.

image13.png

Далее вы можете либо вызвать специальный компонент и обработчик сразу появится на странице, либо вывести кнопки/пункты меню/что угодно, запускающие обработчики либо в окне, либо с помощью компонента через AJAX… В общем, как требуется для вашей задачи.

В качестве примера выведем кнопки запуска обработчиков. Кнопки создадим с помощью расширения ui.buttons модуля UI-библиотека. Вызывать обработчики будем с помощью JS-метода BX.rest.AppLayout.openApplication. Этот метод принимает на вход три аргумента:

  • ID приложения, которое зарегистрировало обработчик;
  • произвольные данные, которые необходимо передать в обработчик (например, если обработчик вызывается в карточке сущности, уместно передать все данные этой сущности);
  • массив параметров компонента, запускающего обработчик, параметров всего два: код места встраивания и ID обработчика.

Теперь для каждого зарегистрированного обработчика выводится кнопка.

image3.png

При нажатии на кнопку обработчик места встраивания запускается в слайдере (наш обработчик, напомним, выводит на экран все параметры запроса).

image6.png

Обратите внимание, что значение второго аргумента доступно в параметре запроса PLACEMENT_OPTIONS.

Приведем также пример вызова компонента bitrix:app.layout, с помощью которого можно запустить обработчик прямо на странице. Этот компонент принимает на вход те же самые параметры:

Параметр компонента Описание
PLACEMENT Код места встраивания.
PLACEMENT_OPTIONS Произвольные данные для передачи в обработчик.
ID ID приложения, зарегистрировавшего обработчик.
PLACEMENT_ID ID обработчика.

Пример вызова:

На странице это выглядит так:

image27.png

Разумеется, каждый обработчик вызывается в iframe, что обеспечивает необходимую степень изоляции кода внешнего приложения от Б24.

Теперь, зная API встраивания, сделаем вывод обработчиков мест встраивания в карточке торговой точки в виде вкладок в нижней части страницы.

image11.png

Карточка торговой точки выводится компонентом academy.crmstores:store.show. В классе компонента добавим метод, возвращающий список обработчиков места встраивания, передадим этот список в шаблон в $arResult.

Вывод обработчиков удобно сделать с помощью компонента app.layout. Однако внешние приложения могут зарегистрировать неограниченное количество обработчиков места встраивания. Во-первых, загрузка содержимого для всех вкладок с обработчиками займет неопределенное количество времени, что, очевидно, отрицательно скажется на времени полной загрузки страницы, во-вторых, пользователю почти никогда не нужны все вкладки сразу.

Поэтому будем использовать функцию «ленивой» загрузки содержимого вкладок компонента crm.interface.form. Благодаря этой функции содержимое вкладки загружается с помощью AJAX только в тот момент, когда пользователь откроет эту вкладку. Такое поведение реализовано для всех вкладок карточек сущностей CRM.

Чтобы воспользоваться этой функцией, необходимо вывести все вкладки с пустыми div’ами, а также вызвать метод BX.CrmFormTabLazyLoader.create, который возьмет на себя задачу привязки обработчиков событий к элементам DOM-дерева и загрузки содержимого. Чтобы загрузка произошла корректно, требуется специальный скрипт обработчика вкладки. К счастью, в компоненте app.layout такой скрипт уже есть.

Итак, начнем вывод вкладок с ленивой загрузкой в файле template.php. Сначала инициализируем несколько переменных, которые понадобятся далее.

Переменная Описание
$tabId Идентификатор вкладки для компонента crm.interface.form.
$wrapperId Идентификатор div’а, в который будет загружаться содержимое вкладки.
$serviceUrl Путь к скрипту, который будет формировать содержимое вкладки.
$loaderId Идентификатор загрузчика вкладки. Нам не придется с ним взаимодействовать, можно придумать на свое усмотрение. Обычно в идентификатор включают идентификаторы формы и вкладки.
$componentData Данные для вызова компонента, передаются как параметры запроса при вызове $serviceUrl. Скрипт вызывает определенный компонент (app.layout), поэтому передаем только шаблон и параметры этого компонента.

Далее сформируем код описание вкладки для компонента crm.interface.form (ниже продолжается код тела цикла).

Вкладка включает единственное поле с параметром colspan = true, это значит, что внутри вкладки не будет двухколоночной таблицы типа «название поля-значение», а просто будет выведено произвольное содержимое на всю ширину вкладки.

Наконец, сформируем скрипт, активирующий «ленивую» загрузку.

Теперь, как только пользователь открывает вкладку обработчика места встраивания, происходит загрузка содержимого вкладки — iframe, который, в свою очередь, запускает сам обработчик. В качестве параметров обработчика (PLACEMENT_OPTIONS) мы передали идентификатор торговой точки, по которому, впоследствии, можно получить все данные с помощью REST-метода crm.store.list.

image7.png

«Локальный» REST-клиент

Использование REST API в рамках портала

Еще одна замечательная функция модуля REST API специально для коробочной версии КП — возможность использовать REST API без создания приложения. В этом случае КП сам выступает в роли потребителя своего же REST API. При этом нет никаких сложностей с получением кода доступа, его продления и т. д. Все вызовы REST-методов выполняются от имени текущего авторизованного пользователя.

На данный момент предусмотрен вызов REST-методов со стороны клиента, для этого разработаны несколько JS-классов. Чтобы ими воспользоваться, нужно подключить CJSCore-расширение restclient на странице.

JS-API практически идентично JS-библиотеке для внешних приложений. Для вызова REST-методов используйте:

  • BX.rest.callMethod
  • BX.rest.callBatch

Попробуем вызвать метод profile в консоли браузера.

Когда выполнение метода завершится, в консоли появится результат. Обратите внимание, что в обработчик передается объект, такой же как и во внешних приложениях: у него доступны методы data, more, next, total…

Синхронизация вызовов с помощью BX.Promise

Однако, в отличие от JS-библиотеки для внешних приложений, методы callMethod и callBatch «локального» REST-клиента поддерживают синхронизацию вызовов с помощью BX.Promise. Чтобы использовать этот способ синхронизации требуется не указывать третий аргумент — callback-функцию, тогда метод вернет объект класса BX.Promise.

Рассмотрим общие принципы работы с BX.Promise. Если требуется синхронизировать код, создайте один объект BX.Promise для описания цепочки вызовов, и по одному объекту этого же класса для каждого асинхронного участка. Используйте методы then — чтобы задать последовательность асинхронных вызовов, fulfill и reject — для возврата результата работы асинхронного участка кода.

С помощью then в примере выше мы задали последовательность функций. Эти функции не будут вызваны до тех пор, пока у объекта promise не будет вызван метод fulfill. Обратите внимание, что then каждый раз возвращает новый объект, то есть если несколько раз вызвать then у объекта promise, будет не то же самое.

Каждая функция, переданная в then принимает на вход один аргумент — некоторое значение. В функцию передается то значение, которое вернула функция из предыдущего then, только если она не вернула объект типа BX.Promise. В последнем случае выполнение цепочки приостанавливается до тех пор, пока у возвращенного BX.Promise не будет вызван метод fulfill. А в следующую функцию будет передано значение, которое было указано при вызове fulfill. В самую первую функцию в цепочке передается значение, которое было указано при вызове fulfill для объекта promise (когда мы запускали цепочку).

Этот способ синхронизации отлично работает с «промисифицированными» асинхронными функциями, которых в Б24 уже немало. Метод callMethod как раз относится к таким. Обратите внимание как просто выглядит его вызов во второй функции в цепочке. Объект-результат передается в третью функцию цепочки.

Очевидно, что для «промисификации» своих функций достаточно сделать так, чтобы они возвращали объект BX.Promise и вызывали у него fulfill или reject когда-нибудь.

Пример JS-приложения: вывод данных пользователя

Структура приложения, получение данных, запуск

Возможность использовать REST-методы без внешнего приложения позволяет, потенциально, отказаться от генерации верстки на сервере, что снижает нагрузку на back-end. Например, в модуле UI-библиотека уже есть клиентский шаблонизатор Mustache.js.

В качестве примера рассмотрим простейшее приложение, которое выводит данные профиля пользователя. Этот пример не связан с торговыми точками.

Важное замечание. На текущий момент в Битриксе еще нет полноценной инфраструктуры для создания клиентской части приложения на JS. Но, наверняка, все появится, поскольку этот способ разработки приложений сегодня очень популярен.

Приложение будет состоять из одного JS-файла, зарегистрируем его в CJSCore в файле init.php.

Код приложения:

Запуск этого приложения будем выполнять на пустой странице /localrest.php.

В коде приложения опущены получение исходного кода шаблона и запуск шаблонизатора. Рассмотрим их отдельно.

Разработка шаблонов, доставка их на клиент и запуск шаблонизатора

Синтаксис шаблонов Mustache довольно прост в первом приближении. Например:

Считайте, что в двойных фигурных скобках записано название ключа из как-будто бы $arResult. Также есть специальные блоки для повторений, условий, фрагментов…

Для профиля сделаем следующий шаблон.

Теперь, чтобы получить HTML-код, нужно вызвать шаблонизатор следующим образом (считайте, что в profileTemplate находится текст шаблона, а в profile — данные, которые вернул REST-метод profile):

Таким образом, для запуска шаблонизатора достаточно вызвать метод Mustache.render. Первый аргумент — текст шаблона, второй — наш мнимый $arResult.

Перед тем как перейти к доставке шаблона на клиент давайте обратим внимание на то, что в коде шаблона присутствуют константы, которые могут меняться в зависимости от языка в системе. Локализация — важный момент.

Ранее, при регистрации файлов в CJSCore мы указали и файл с языковыми константами. Это значит, что на клиенте можно использовать метод BX.message для их получения. Как их использовать в шаблоне? Самый простой вариант — передать их вместе с данными профиля. Сами константы хранятся как поля функции (а функция в JS — это объект) BX.message. Поэтому можно сделать так:

var html = Mustache.render(
profileTemplate,
{
Loc: BX.message,
profile: profile
}
);

И изменить шаблон следующим образом:

Но будьте осторожны! В Mustache заложена определенная логика, связанная с функциями, передаваемыми в шаблон.

Теперь рассмотрим способы передачи шаблонов на сторону клиента. Нам видится два приемлемых способа это сделать:

  • загрузить код шаблона с помощью AJAX;
  • вывести код шаблона на странице заранее.

В первом случае следует сохранить код шаблона в отдельном файле. Мы разместим его в /local/js/profile.mustache (только для примера). Далее воспользуемся методом BX.ajax.promise для загрузки.

Поскольку этот шаблон не меняется со временем, нет смысла загружать его всякий раз, когда он понадобится. Реализуем в приложении примитивный кеш шаблонов. В конструкторе объявим поле для хранения текстов шаблонов. Ключом будет выступать URL файла шаблона, значением — сам текст.

Добавим метод, который проверяет наличие шаблона в кеше и загружает шаблон с помощью AJAX, если его в кеше еще нет. Сделаем этот метод сразу «промисифицированным».

В методе _displayProfile вызовем _getTemplate (приведен только тот then, в котором загрузка была опущена):

Наконец, можно запустить приложение.

Этот же шаблон можно разместить на странице localrest.php (где мы вызываем приложение). Для этого понадобится добавить тег script (как советуют авторы Mustache.js).

Добавим в класс приложения метод для получения шаблона со страницы.

Метод возвращает код шаблона по ID (в примере выше profileTemplate). Этот метод использовать вместо _getTemplate, так как функция, указанная в then может возвращать любое значение, оно будет передано в следующую.

Важно! Не смешивайте PHP-код с кодом шаблонов Mustache, даже ради локализации. Во-первых, получится хаос, во-вторых, получится странная цепочка: PHP-шаблон формирует Mustache-шаблон, который формирует HTML.

Заключение

Модуль REST API обладает большими возможностями. Технологии REST-методы, события и встраивание приложений позволяют решить многие задачи интеграции с внешними системами. А «локальный» REST-клиент и клиентский шаблонизатор позволяют разрабатывать современные приложения с явным разделением на клиент и сервер, что положительно сказывается на архитектуре программ.

С помощью модуля REST API создание веб-служб в Bitrix Framework еще никогда не было такой простой задачей.

——
Спасибо за внимание :)
Материалы:  
Автор статьи: , компания .  

Examples

Problem: get the list of all REST methods available at my.bitrix24.com.

Problem: obtain the ID’s of all task a current user planned for today.

  • To use the Tasks module, a registered application must be granted the appropriate permission (scope).
       	"CLIENT_ID" => 'Tasks_APP',
    	"CLIENT_SECRET" => 'very_secret_key',
    	"TITLE" => 'Task test app',
    	"REDIRECT_URI" => ' http://test.com/bitrix/oauth/oauth_test.php',
    	"SCOPE" => array('task', 'user')

    Here:

    • SCOPE – specifies permissions required by the application;
  • Get the first key (request token). Send a GET request to:

    http://my.bitrix24.com/oauth/authorize/?client_id=Tasks_APP&response_type=code&redirect_uri=http://test.com/bitrix/oauth/oauth_test.php

  • To return the result, the server will call a script at REDIRECT_URI:

    http://test.com/bitrix/oauth/oauth_test.php?code=xxxxxxxxxxxxxxxxxxxxxxxxxxx

  • The script reads the code parameter value and asks for the second key (access token) by sending another GET request:

    http://my.bitrix24.com/oauth/token/?client_id=Tasks_APP&grant_type=authorization_code&client_secret=very_secret_key&redirect_uri=http://test.com/bitrix/oauth/oauth_test.php&code= xxxxxxxxxxxxxxxxxxxxxxxxxxx&scope=user,task

  • Again, the server replies with the result by calling REDIRECT_URI:

    http://test.com/bitrix/oauth/oauth_test.php?access_token=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy&expires_in=3600&refresh_token=zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

    Here:

    • expires_in – the access token lifetime, seconds;
    • refresh_token – the key to refresh the access token when lifetime has expired.
  • Now that we have access_token we can call REST API:

    http://my.bitrix24.com/rest/task.planner.getlist?auth=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

  • In response, we get the task ID’s:
    [result] => Array ( [0] => 3 [1] => 4 )

    The access token can be used as many times as needed as long as it is alive. Once the access token has expired, the server will return an expired_token error. To get a new key, send the refresh token to the server:

    http://my.bitrix24.com/oauth/token/?client_id=Tasks_APP&grant_type=refresh_token&client_secret=very_secret_key&redirect_uri=http://test.com/bitrix/oauth/oauth_test.php&refresh_token=zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

    As before, the server will response by calling the script with the “access_token” parameter:

    http://test.com/bitrix/oauth/oauth_test.php?access_token=wwwwwwwwwwwwwwwwwwwwwwwwww&expires_in=3600

  • Похожее:  Simple lightweight NTLM in PHP | Playing on the frontier

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *