Написание ролевого приложения

Введение

Ролевым приложением называют web-приложение, которое можно установить в домен системы R, создав объект типа roleapp и загрузив в него файл архива.
Архив будет распакован системой и отображён на веб-сервере по определённому пути, частично заданному администратором домена.

Для связи с сервером ролевое веб-приложение должно использовать http и/или websocket API открытое на том же самом сервере, что и загруженное ролевое приложение.
Одним из способов расширения функциональности ролевых приложений является возможность запускать служебный сценарий в системе по запросу web-приложения.
Если такой сценарий существует (создан и настроен администратором домена), то привязка сценария происходит путём указания его кода в roleapp entity в поле svcscriptcode.
Запуск связанного служебного сценария происходит через выполнение запроса /exec ролевым приложением.

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

Файл ролевого приложения

Ролевое приложение представляет из себя zip-архив, который будет распакован на сервере при установке.
При этом архив должен содержать файл roleapp.json на верхнем уровне.

├── assets
│   └── img
│   └── desktop.svg
├── css
│   └── jsoneditor.min.css
├── index.html
├── js
│   ├── aes-js.js
│   ├── base64.js
│   ├── jquery-3.1.1.min.js
│   └── jsoneditor.min.js
├── monitor.js
└── roleapp.json

Файл roleapp.json в корне архива содержит описание приложения. Как правило все файлы и папки приложения находятся также в корне архива (index.html и другие).

roleapp.json

Должен быть валидным json файлом, размер которого не превышает 10Kb. Все поля опциональные. Минимальный json может быть пустым объектом ({}).

{
  "name": "Roleapp Sample", (1)
  "description": "Monitor (old) as RoleApp description", (2)
  "order": 5000, (3)
  "fa-icon": "fa-paw" (4)
  "icon": "assets/img/desktop.svg" (5)
}
1 name – название ролевого приложения. Значение поля будет использовано для названия ролевого приложения в списке если администратор оставил поле "title" объекта ролевого приложения пустым. Тип поля строка, иначе значение будет проигнорированно.
2 description – описание ролевого приложения, идёт напрямую в /rest/v1/iam/sessions/current. Тип поля строка, иначе значение будет проигнорированно.
3 order – порядок ролевого приложения при сортировке для отображения в списке. Администратор может принудительно его задать в поле ext.order объекта roleapp, чтобы заменить значение из roleapp.json.
4 fa-icon – имя класса иконки Font Awesome.
5 icon – путь до иконки для списка приложений относительно папки ролевого приложения.

Есть возможность указать подпапку внутри архива в качестве папки с web-документами (по-умолчанию будет использован весь архив от корня). Если roleapp.json содержит поле "folder", то в результате по url’у установленного приложения будет доступен не корень архива, а эта подпапка (в url не появляется).
Таким образом, разницу между режимом по-умолчанию и режимом с подпапкой можно проследить на примере roleapp.json: в режиме по-умолчанию файл становится доступным по http: /app/ccoper/roleapp.json но в режиме с подпапкой он оказывается не виден.

Пример:

├── dist
│   └── index.html
└── roleapp.json

{
  "name": "Def roleapp",
  "order": 5000,
  "fa-icon": "fa-folder-open",
  "folder": "dist"
}

Установка ролевого приложения

После установки ролевого приложения оно появляется в списке приложений если проходит все этапы фильтрации. Фильтруются приложения по следующему правилу:

  • пользователь должен иметь хотя бы одну роль доступа из списка ролей приложения (поле "roles" объекта roleapp) - задаются администратором.

Результат установки

Приложение становится доступным по URL указанном в выдаче /rest/v1/iam/sessions/current, например /app/ccoper/.

{
  ...,
  "webapps": [
    ...,
    {
      "description": "OktellSutdio Contact Center Operator web application",
      "fa-icon": "fa-headphones",
      "icon": "/app/ccoper/logo192.png",
      "name": "CC Operator",
      "order": 5000,
      "roles": [
        "admin",
        "ccoperator"
      ],
      "url": "/app/ccoper/"
    }
  ]
}

Первая часть пути URL /app определяется системой, а вторая часть /app/ccoper (ccoper) определяется администратором в поле folder объекта roleapp.
Веб-сервер системы настроен таким образом, что при запросе на адрес, указанный в поле "url", будет отдаваться содержимое index.html (только корень приложения).
Все ресурсы приложения (другие файлы кроме index.html) будут доступны в этой подпапке, например, /app/ccoper/css/main.css

Особенности реализации ролевого приложения

Здесь нужно понимать, что URL установки не известен автору приложения заранее, ведь folder определяется администратором в момент установки.
Более того, администратор имеет возможность установить, например, разные версии ролевого приложения в разные папки и пользоваться ими одновременно.

Требования платформы

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

  • приложение будет загружено из произвольной папки на сервере (напр. /app/ccoper/) и должно считаться корнем приложения.

  • приложение может также быть загружено с явным указанием файла внутри приложения (напр. /app/ccoper/index.html) но данный вариант на практике не используется по эстетическим соображениям.

  • все остальные запросы в пределах папки приложений обслуживаются статическим веб-сервером без выдачи индексов подкаталогов и без подмены их содержимым index.html (как это происходит с корнем приложения).

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

    • например, приложение совершит переход на виртуальный адрес /app/ccoper/main при отображении главной страницы. Перезагрузка страницы выдаст 404 Not Found.

Примеры реализации требований

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>R.API Monitor</title>
<link rel="stylesheet" href="css/jsoneditor.min.css">
<script src="js/jsoneditor.min.js"></script>
<script src="js/jquery-3.1.1.min.js"></script>
<script src="js/aes-js.js"></script>
<script src="js/base64.js"></script>
<script src="monitor.js"></script>

Где ресурсы будут загружаться из папок /app/ccoper/css/, /app/ccoper/js/ и т.д. или как /app/ccoper/monitor.js из папки ролевого приложения, что является стандартной работой браузера.

Типичным вариантом при сборке приложений React/др. является указание префикса "./" означающего текущую папку в качестве папки установи приложения, напр. PUBLIC_URL=..

С другой стороны, применяя javascript очень легко динамически узнать путь установки ролевого приложения при загрузке страницы (из переменной window.location.pathname), например:

<html>
<body>
<h1>Role Application "Def2".<h1>
<div id="abc"></div>
<script>
console.log('1');
(function() {
var path = window.location.pathname;
console.log('path = ', path);
document.getElementById('abc').innerHTML = path;
})();
console.log('2');
</script>
</body>
</html>

После загрузки страницы и ресурсов в процессе работы приложение может использовать адрес URL в строке браузера как хранилище части своего состояния (т.н. Routing).
Чтобы пользователь мог в любой момент перезагрузить страницу приложения и продолжить свою работу с ним, необходимо избегать модификации адреса URL в части path.
Этого легко достичь, используя в качестве хранилища состояния идентификатор фрагмента вместо пути. В приложениях React это достигается использованием класса HashRouter вместо BrowserRouter.
/app/ccoper/main/app/ccoper/#/main