integration.rst 48.6 KB
Newer Older
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Способы интеграции
==================

Существует два основных подхода для интеграции с Synergy:

* Прямая интеграция  интеграционные модули разрабатываются
  с использованием API Synergy и интегрируемых систем.
  Синхронизация данных между системами и координация обмена между ними
  остаётся за разработчиком интеграционного модуля

* Событийная интеграция  когда какая-либо из подсистем Synergy
  генерирует различные события, связанные с какими-либо данными.
  Обработчики этих событий (на стороне Synergy) при необходимости
  преобразовывают данные событий и передают их интегрируемой системе
  через какой-либо транспортный уровень

Прямая интеграция
-----------------

ARTA Synergy предоставляет API для доступа к своим функциям
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
21 22
с помощью rest сервисов. Описание методов REST API можно
найти в `данном разделе`_.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
23 24 25 26 27 28
Авторизация для всех методов API  ``Basic HTTP``.

Событийная интеграция
---------------------

Под «событием» мы будем подразумевать сообщение о каком-либо изменении
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
29
в ARTA Synergy, содержащее тип события
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
30 31 32 33 34 35
и минимально необходимые для получения связанной с событием информации
либо воздействия на Synergy данные. Обработчик события (или событий) 
программный модуль, читающий сообщения о событиях из ``JMS Queue`` или ``JMS Topic``
и осуществляющий, при необходимости, доступ к экземпляру Synergy,
сгенерировавшему сообщение, с помощью API Synergy.

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
36
Обработчик событий является отдельным от ARTA Synergy приложением,
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
37
которое может работать как на том же сервере приложений,
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
38
что и ARTA Synergy, так и на удалённом.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

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

Обработчик событий может обрабатывать как конкретное событие
(например, ``event.registers.formdata.add``),
так и класс событий (например, ``event.registers.*``).

Обработка события может происходить в 3 этапа:

1. Получение события
2. Получение и преобразование необходимых обработчику данных
3. Передача сформированного пакета данных далее (опционально)

ARTA Synergy генерирует событие в случае,
если для этого события настроены обработчики.
Обработчики событий настраиваются в конфигурационном файле
``${jboss.home}/standalone/configuration/arta/api-observation-configuration.xml``.

Сообщение, помещаемое в очередь ``JMS``,
представляет собой экземпляр ``javax.jms.TextMessage``.
Тело сообщения зависит от типа события,
его описание можно посмотреть ниже среди описаний типов событий.
Каждое событие содержит свойство ``api_event``, указывающее на тип события,
вызвавшего его (содержимое тега ``<event>event.registers.formdata.add</event>`` в конфигурационном файле).

Например:

.. code-block:: xml

    <configuration>
        <listener>
            <queue>java:jboss/queues/Synergy/UsersQueue</queue>
            <event>event.users.*</event>
        </listener>
        <listener>
            <queue>java:jboss/queues/Synergy/RegisterCreateDocQueue</queue>
            <event>event.registers.formdata.add</event>
        </listener>
    </configuration>

В этом примере настроены обработчики:

1. ``java:jboss/queues/Synergy/UsersQueue`` для всех событий класса
   ``event.users.*``, т.е. всех событий, связанных с пользователями:
   ``event.users.account.change``,
   ``event.users.formdata.change``,
   ``event.users.account.add`` и т.д.
2. ``java:jboss/queues/Synergy/RegisterCreateDocQueue``
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
88
   для события добавления записи реестра ``event.registers.formdata.add``.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
89 90 91 92 93 94 95 96 97 98 99 100 101 102

Рассмотрим, например, код обработчика очереди ``UsersQueue``:

.. code-block:: java

    public class UsersMessagesListener implements MessageListener {

        public void onMessage(Message message) {
            //Получаем идентификатор пользователя, для которого
            //сгенерировано событие
            String userID = ((TextMessage) message).getText();
            //Получаем тип события
            String eventType = message.getStringProperty("api_event");

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
103
            //Выполнение действия по получению дополнительных данных через API
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
            //и прочих операций, зависящих от условий решаемой задачи
        }
    }

Ниже описаны типы событий, которые могут быть сгенерированы ARTA Synergy.

Для события ``[event.orgstructure.department.formdata.change]`` -
идентификатор подразделения, для события
``[event.orgstructure.position.formdata.change]`` - идентификатор
должности, для события ``[event.users.formdata.change]`` - идентификатор
пользователя будет передаваться как основной параметр, остальные как
свойства. Получить их можно следующим образом:

.. code-block:: java

    public void onMessage(Message message) {
        //Получение идентификатора пользователя/должности/подразделения (В зависимости от события на которое подписаны)
        String userID = ((TextMessage) message).getText();
        //Получаем идентификатор формы
        String formUUID = message.getStringProperty("formUUID");
    }
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
125 126 127 128 129 130

.. toctree::
  :maxdepth: 2
  :numbered:

  integration/events
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
131 132 133 134 135 136 137 138

Блокирующий процесс
-------------------

Блокирующий процесс предназначен для того,
чтобы предоставить возможность
в маршрут активации/изменения/удаления реестра
вставить асинхронный вызов внешнего модуля.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
139
Основное отличие блокирующего процесса от событий реестра
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
заключается в том, что:

* при использовании блокирующего процесса маршрут реестра
  дожидается ответа о результате выполнения операции внешним модулем
* блокирующий процесс может завершиться положительно или отрицательно,
  что повлияет на дальнейшую работу маршрута
  (Если блокирующий процесс завершится отрицательно 
  процесс остановится, если положительно  то продолжит работу дальше)

Модуль, реализующий блокирующий процесс, должен представлять собой отдельное приложение,
задеплоенное на jboss в соответствии с правилами,
описанными в разделе `Как задеплоить интеграционное приложение`_.

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

Конфигурация блокирующего процесса
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

1. Добавить процесс с в маршрут реестра в конфигураторе:

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
165
      .. figure:: _static/img/integration/blocking-add.png
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
         :scale: 68%

   Название процесса должно начинаться с ``event.blocking.``
   и далее строка, характеризующая суть блокирующего процесса.

2. Создать очередь JMS для блокирующего процесса.
   Для этого необходимо в конфигурационный файл
   (в стандартной установке это ``/opt/synergy/jboss/standalone/configuration/standalone-onesynergy.xml``)
   в секцию ``<subsystem xmlns="urn:jboss:domain:messaging:1.2">`` добавить:

   .. code-block:: xml

      <jms-queue name="ExampleQueue">
          <entry name="java:jboss/queues/Integration/ExampleQueue"/>
          <durable>true</durable>
      </jms-queue>


3. Связать очередь и процесс через конфигурационный файл
   ``{$jboss.home}/standalone/configuration/arta/api-observation-configuration.xml``,
   добавив в него следующее:

   .. code-block:: xml

      <listener>
           <queue>java:jboss/queues/Integration/ExampleQueue</queue>
           <event>event.blocking.example</event>
      </listener>

Обратите внимание, что название блокирующего процесса,
указанное в маршруте в конфигураторе должно быть
равно значению тега в конфигурационном файле
``api-observation-configuration.xml`` (в данном примере: ``event.blocking.example``)
и название очереди должно совпадать со значением тега
``queue`` конфигурационного файла
``api-observation-configuration.xml`` (в данном примере: ``java:jboss/queues/Integration/ExampleQueue``)

Сообщение, передаваемое в очередь, является экземпляром ``TextMessage``.
Содержимым сообщения является объект JSON с полями:

    1. dataUUID  идентификатор данных по форме записи реестра
    2. executionID  идентификатор блокирующего процесса
    3. documentID  идентификатор документа реестра

После того, как модуль обратится к внешней системе
и выполнит необходимые действия, он должен вызвать метод API Synergy
для того, чтобы возвратить результат выполнения процесса и продолжить работу маршрута.
Для того, чтобы это сделать, необходимо вызвать метод API
``rest/api/processes/signal``.

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

В примере кода ниже разблокировка маршрута осуществляется в методе ``onMessage``.
Если время выполнения действия значительно или зависит от внешних факторов
(например, доступность интегрируемой системы,
или необходимость ввода пользователем данных в интегрируемой системе),
то разблокировка маршрута может произойти позже,
в любой другой момент времени из другого метода,
а сам метод onMessage должен завершиться без ошибок, «запомнив» переданные параметры.

.. code-block:: java

    package kz.arta.synergy.samples.processes.blocking;

    import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
    import org.codehaus.jackson.JsonFactory;
    import org.codehaus.jackson.JsonParser;
    import org.codehaus.jackson.JsonToken;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    import javax.jms.Message;
    import javax.jms.MessageListener;
    import javax.jms.TextMessage;
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;

    /**
     * <p><Пример блокирующего процесса</p>
     */
    public class BlockingQueueListener implements MessageListener {

        public void onMessage(Message message) {

            String dataUUID = null;
            String executionID = null;
            String documentID = null;

            if (!(message instanceof TextMessage)){
                return;
            }

            try {

                JsonFactory factory = new JsonFactory();
                JsonParser parser = factory.createJsonParser(((TextMessage) message).getText());
                JsonToken token = null;

                while ((token = parser.nextToken()) != null) {
                    if (token == JsonToken.FIELD_NAME) {
                        String fieldName = parser.getText();
                        parser.nextToken();
                        String value = parser.getText();
                        if (fieldName.equals("dataUUID")){
                            dataUUID = value;
                        } else if (fieldName.equals("executionID")){
                            executionID = value;
                        } else if (fieldName.equals("documentID")){
                            documentID = value;
                        }
                    }
                }

             //Выполнение каких-либо действий
             .......

                //Разблокировка маршрута

                String address = "http://127.0.0.1:8080/Synergy";
                String login = "1";
                String password = "1";
                String signal = "got_agree";
                boolean isSuccess = false;

                try {
                    URL url = new URL(address + "/rest/api/processes/signal?signal=" + signal + "&executionID=" + executionID + "&param1=resolution&value1=signal_is_" + signal);

                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setRequestProperty("Accept", "application/json; charset=utf-8");

                    String encoded = Base64.encode((login + ":" + password).getBytes());
                    conn.setRequestProperty("Authorization", "Basic " + encoded);

                    String output;
                    StringBuffer result = new StringBuffer();

                    BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));

                    while ((output = br.readLine()) != null) {
                        result.append(output);
                    }

                    conn.disconnect();

                    JsonFactory factory = new JsonFactory();
                    JsonParser parser = factory.createJsonParser(result.toString());
                    JsonToken token = null;

                    while ((token = parser.nextToken()) != null) {
                        if (token == JsonToken.FIELD_NAME) {
                            String fieldName = parser.getText();
                            token = parser.nextToken();
                            if (fieldName.equals("errorCode") && parser.getText().equals("0")){
                                isSuccess = true;
                            }
                        }
                    }
                } catch (Exception exc){
                    logger.error(exc.getMessage(), exc);
                }

            } catch (Exception exc){
                logger.error(exc.getMessage(), exc);
            }
        }
    }
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
339

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
Дополнительный обработчик для стандартного процесса
---------------------------------------------------

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

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

Обработчик может быть применён к процессам:

*   «работа» (`assignment-single`)
*   «согласование» (`agreement-single`)
*   «утверждение» (`approval-single`)
*   «ознакомление» (`acquaintance-single`)
*   «отправка документа» (`send-document`)
*   «общий процесс при запуске по формам» (`common-process-by-form`)
*   «отправка документа по форме» (`send-document-by-form`)

Обработчик представляет собой Java-класс, реализующий интерфейс

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
365
``kz.arta.synergy.integration.api.bp.StartHandlerIF``
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
366 367 368 369 370 371 372 373 374 375 376 377

Данный интерфейс находится в библиотеке ``integration-api.jar``,
которую можно найти в установленном экземпляре ARTA Synergy в директории
``/opt/synergy/jboss/standalone/deployments/Synergy.ear/lib``.

Интерфейс содержит два метода:

*   ``makeDecision()``  проверяет возможно ли выполнение процесса
*   ``getResolution()``  возвращает текст, который должен быть записан в ход
    исполнения

Более подробную информацию о полях методов можно посмотреть в javadoc
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
378
к этим методам, которые доступны в ``integration-api.jar`` (библиотека
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
содержит и скомпилированные классы, и исходный код).

Установка обработчика для процесса осуществляется с помощью
конфигурационного файла
``${jboss.server.config.dir}/arta/process-handlers-configuration.xml``,
имеющего следующий формат:

.. code-block:: xml

    <?xml version="1.0" encoding="UTF-8"?>
    <process-handlers-configuration
      xmlns="http://www.arta.kz/xml/ns/ai"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.arta.kz/xml/ns/arta
      http://www.arta.kz/xml/ns/ai/process-handlers-configuration .xsd">
     <handlers>
         <handler>
             <process>assignment-single</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
         <handler>
             <process>agreement-single</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
         <handler>
             <process>approval-single</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
         <handler>
             <process>acquaintance-single</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
         <handler>
             <process>send-document</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
     </handlers>
    </process-handlers-configuration>


Обработчики выполняются последовательно до тех пор, пока метод
``makeDecision()`` одного из них не вернет ``false``, после этого процесс
прерывается.

Библиотеку, содержащую обработчик необходимо скопировать в папку
``/opt/synergy/jboss/standalone/deployments/Synergy.ear/lib``.

После копирования библиотеки обработчика и изменения файла
``process-handlers-configuration.xml`` необходимо перезапустить JBoss.

.. attention:: Процесс ``common-process-by-form`` запускает процессы
               ``agreement-single``, ``approval-single``, ``acquaintance-single``,
               ``assignment-single`` (подпроцессы). Поэтому, если обработчик будет
               запрещать выполнение подпроцесса и при этом разрешать выполнение процесса
               ``common-process-by-form``, то подпроцессы все равно будут прерваны.
               Аналогично, если выполнение ``common-process-by-form`` разрешено, а
               выполнение подпроцесса запрещено, подпроцессы будут прерваны.

Пример использования
~~~~~~~~~~~~~~~~~~~~

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

Для установки внешнего модуля из репозитория необходимо установить пакет
``arta-synergy-ext-sendcontrol``.

Далее на остановленном JBoss в конфигурационном файле
``${jboss.server.config.dir}/arta/process-handlers-configuration.xml``
необходимо прописать следующие обработчики процесса:

.. code-block:: xml

    <?xml version="1.0" encoding="UTF-8"?>
    <process-handlers-configuration
      xmlns="http://www.arta.kz/xml/ns/ai"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.arta.kz/xml/ns/arta
      http://www.arta.kz/xml/ns/ai/process-handlers-configuration .xsd">
     <handlers>
         <handler>
             <process>assignment-single</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
         <handler>
             <process>agreement-single</process>
             <handler-providers>kz.arta.synergy.ext.blocking.SendControlHandler</handler-providers>
         </handler>
     </handlers>
    </process-handlers-configuration>

Установка групп (каким группам пользователей Synergy) разрешать либо
блокировать процессы осуществляется с помощью конфигурационного файла
``${jboss.server.config.dir}/arta/ext/send-control.xml``. Пример настройки:

.. code-block:: xml

    <?xml version="1.0" encoding="UTF-8"?>
    <send-control>

        <!--
            Идентификаторы групп, членам которых разрешено отправлять
            что-либо из блоков `deny`
        -->
        <from>
            <allow>35</allow>
        </from>

        <!--
            Идентификаторы групп, членам которых могут отправлять что-либо
            только пользователи групп из блоков `allow`, если таковые есть.
            В противном случае, никто ничего этим пользователям отправить
            не сможет.
        -->
        <to>
            <deny comment="Вы не можете отправлять что-либо на согласование данному пользователю">111</deny>
        </to>
    </send-control>
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
498 499 500 501 502

Способы авторизации в ARTA Synergy
----------------------------------

REST API ARTA Synergy доступно только авторизованным пользователям.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
503
Тип авторизации  ``BASIC HTTP``.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
Методы API выполняются от имени того пользователя,
который авторизован. Имеются следующие типы авторизации:

Авторизация по логину и паролю
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Авторизация пользователя по его логину и паролю приемлема в тех случаях,
когда приложение может знать текущий логин и пароль пользователя, например:

* Приложение предоставляет альтернативный интерфейс
  к некоторым модулям Synergy
  (мобильное приложение, десктопный клиент для хранилища)
* Приложение представляет собой `server-side` утилиту для синхронизации,
  для которого создан выделенный пользователь,
  и его логин и пароль хранятся в конфигурационном файле на сервере.

Для реализации данного типа авторизации надо передать в запросе заголовок
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
521
``Authorization`` со значением:
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
522

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
523
    ``"Basic " + Base64("login" + ":" + "password")``
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
524 525 526 527 528 529 530 531 532 533 534

Например:

+--------------------+--------------------------------------+
| Логин              | `Administrator`                      |
+--------------------+--------------------------------------+
| Пароль             | `123456`                             |
+--------------------+--------------------------------------+
| Значение заголовка | `Basic QWRtaW5pc3RyYXRvcjoxMjM0NTY=` |
+--------------------+--------------------------------------+

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
535 536 537 538 539 540 541 542 543 544 545 546
.. code-block::
    :emphasize-lines: 3

    POST /Synergy/rest/api/report/do?reportID=daa64ab2-1a79-4ea2-9c24-372e35d9da3e&amp;fileName=report.pdf HTTP/1.1
    Host: demo.arta.kz
    Authorization: Basic QWRtaW5pc3RyYXRvcjoxMjM0NTY=
    Cache-Control: no-cache
    Content-Type: multipart/form-data

Пример кода на JAVA:

.. code-block:: java
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
547
    :emphasize-lines: 5
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
548

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
549 550 551 552 553
    String login = "Administrator";
    String password = "123456";

    HttpResponse<String> response = Unirest.get("http://demo.arta.kz/Synergy/rest/api/admin/db/current_version")
    .header("authorization", Base64.getEncoder().encodeToString((login + ":" + password).getBytes("UTF‌​-8")))
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
554 555 556 557 558
    .asString();

Пример кода на JavaScript (jQuery):

.. code-block:: js
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
559 560 561 562
    :emphasize-lines: 10

    var login = "Administrator";
    var password = "123456";
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
563 564 565 566

    var settings = {
      "async": true,
      "crossDomain": true,
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
567 568
      "url": "http://demo.arta.kz/Synergy/rest/api/admin/db/current_version",
      "method": "GET",
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
569
      "headers": {
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
570
        "authorization": ("Basic " + btoa(login + ":" + password))
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
571 572 573 574 575 576 577 578 579 580
      }
    }

    $.ajax(settings).done(function (response) {
      console.log(response);
    });

Пример кода на PHP:

.. code-block:: php
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
581
    :emphasize-lines: 11
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
582 583 584

    <?php

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
585
    $login = "Administrator";
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
586
    $password = "123456";
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
587

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
588 589 590
    $request = new HttpRequest();
    $request->setUrl('http://demo.arta.kz/Synergy/rest/api/admin/db/current_version');
    $request->setMethod(HTTP_METH_GET);
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
591 592

    $request->setHeaders(array(
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
593
      'authorization' => "Basic " . base64_encode("$login:$password")
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
594 595 596 597 598 599 600 601 602 603
    ));

    try {
      $response = $request->send();

      echo $response->getBody();
    } catch (HttpException $ex) {
      echo $ex;
    }

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
604 605 606 607
Сессионная авторизация
~~~~~~~~~~~~~~~~~~~~~~

Сессионная авторизации используется для встроенных WEB-модулей.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
608
При cессионной авторизации также используется тип  ``BASIC HTTP``,
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
609
но в качестве логина пользователя необходимо использовать
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
610 611
значение ``$session`` и в качестве пароля 
полученное значение ``sso_hash``.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
612

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
613
Таким образом заголовок ``Authorization`` должен иметь значение:
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
614

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
615
    ``"Basic " + Base64("$session" + ":" + "sso_hash")``
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
616 617 618

Например:

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
619 620 621 622 623
+-----------------------+--------------------------------------------------------+
| Значение ``sso_hash`` | ``D3RONfC52dtJO5XgDyn5qUMv``                           |
+-----------------------+--------------------------------------------------------+
| Значение заголовка    | ``Basic JHNlc3Npb246RDNST05mQzUyZHRKTzVYZ0R5bjVxVU12`` |
+-----------------------+--------------------------------------------------------+
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
624

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
625 626
Получить значение ``sso_hash`` авторизованного пользователя можно
следующими способами:
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
627

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
628 629
#.  В случае если приложение представляет собой `Внешний WEB-модуль`_, получить
    значение ``sso_hash`` можно из строки запроса.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
630

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
631
    Пример кода на JavaScript (jQuery):
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
632

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
    .. code-block:: js
        :emphasize-lines: 13

        function getURLParameter(name) {
          return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)')
          .exec(location.search) || [null, ''])[1]
          .replace(/\+/g, '%20')) || null;
        }

        var settings = {
          "async": true,
          "crossDomain": true,
          "url": "http://demo.arta.kz/Synergy/rest/api/admin/db/current_version",
          "method": "GET",
          "headers": {
            "authorization": ("Basic " + btoa("$session" + ":" + getURLParameter('sso_hash')))
          }
        }

        $.ajax(settings).done(function (response) {
          console.log(response);
        });

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
656
#.  С помощью переменной окружения ``$CURRENT_USER`` основного WEB-приложения Synergy,
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
    которая представляет собой JSON-объект следующего вида:

    .. code-block:: json

        {
          "id": "Идентификатор текущего пользователя",
          "sso_hash": "hash-сумма для идентификации пользователя",
          "surname": "Фамилия текущего пользователя",
          "name": "Имя текущего пользователя",
          "patronymic": "Отчество текущего пользователя"
        }

    Пример кода на JavaScript (jQuery):

    .. code-block:: js
        :emphasize-lines: 7

        var settings = {
          "async": true,
          "crossDomain": true,
          "url": "http://demo.arta.kz/Synergy/rest/api/admin/db/current_version",
          "method": "GET",
          "headers": {
            "authorization": ("Basic " + btoa("$session" + ":" + $CURRENT_USER.sso_hash))
          }
        }

        $.ajax(settings).done(function (response) {
          console.log(response);
        });

    .. note:: Данный способ можно использовать только если код исполняется
              в основном приложении Synergy. Например, приложение представляет
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
690
              собой :ref:`extcmp-label` .
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
691

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
692 693 694

Авторизация по ключам
~~~~~~~~~~~~~~~~~~~~~
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
695 696 697 698 699 700 701

Модуль, который хочет авторизоваться от имени какого-либо пользователя таким способом,
должен сгенерировать для него ключевую пару,
обеспечив сохранность закрытого ключа.
Затем модуль сохраняет получивший открытый ключ для пользователя в Synergy,
используя следующий вызов API:

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
702
    ``rest/api/person/generate_auth_key``
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
703 704 705

Этот вызов назначает ключ тому пользователю, от имени которого выполняется.

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
706
Параметр ``user_token_expire_interval`` регулирует интервал устаревания ключей авторизации.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
707 708
Пример настройки интервала:

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
709 710 711
.. code-block:: sql

	   insert into options (id, value) values ('user_token_expire_interval', '5256000'); -- 10 лет
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
712

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
713 714
.. note:: Интервал устаревания ключа указывается в минутах. Значение по умолчанию ``0``, то есть если ранее для
          данного пользователя был сгенерирован другой ключ, то предыдущий автоматически становится недействительным.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
715 716 717

Создать ключ можно только для существующего WEB-модуля,
так как для этого требуется идентификатор приложения.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
718 719 720

.. tip::  Если у вас нет необходимости разрабатывать WEB модуль,
          но есть необходимость в использовании авторизации по ключам,
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
721
          можно добавить внешний модуль и отключить его использование
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
722
          в административном приложении SynergyAdmin для всех элементов оргструктуры.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
723 724

Использование этого ключа для авторизации аналогично использованию сессионного ключа.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
725
Тип авторизации ``Basic HTTP``,
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
726
в качестве логина пользователя надо использовать строку ``$key``,
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
727 728
в качестве пароля  полученный с помощью API ключ.

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
729
Таким образом заголовок ``Authorization`` должен иметь значение:
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
730

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
731
    ``"Basic " + Base64("$key" + ":" + "значение_ключа")``
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
732 733 734

Например:

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
735 736 737 738 739
+---------------------+------------------------------------------------------------------------------------------------------------------------+
| Значение ключа      | ``MS03Y2Q0ZGU3YS0zYjRkLTQ2NjgtYWIyOC0zZDI1YzgxZGNmOGZfMjAxMy0xMC0zMSAxNzo0Mg==``                                       |
+---------------------+------------------------------------------------------------------------------------------------------------------------+
| Значение заголовка  | ``Basic JGtleTpNUzAzWTJRMFpHVTNZUzB6WWpSa0xUUTJOamd0WVdJeU9DMHpaREkxWXpneFpHTm1PR1pmTWpBeE15MHhNQzB6TVNBeE56bzBNZz09`` |
+---------------------+------------------------------------------------------------------------------------------------------------------------+
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
740 741 742

Внешний WEB-модуль
------------------
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779

Web-приложение внешнего модуля открывается
в ``iframe`` в окне основного приложения.
При этом рабочая область внешнего модуля
занимает всю область страницы, кроме панели меню и панели задач:

.. figure:: _static/img/integration/web-module.png
   :scale: 50%

   Внешний WEB-модуль

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

:menuselection:`Конфигуратор --> Настройки системы --> Управление модулями --> Внешние модули`

и нажать на кнопку "Добавить".

.. figure:: _static/img/integration/web-modules.png

   Внешние модули

В открывшемся окне нужно заполнить следующие поля:

* **«Название»** - название модуля в соответствующем интерфейсе.
* **«Код»** - поле должно содержать уникальное значение.
* **«Адрес приложения»** - поле для ввода URL.
* **«Описание модуля»** - поле для описания данного модуля.
* **«Иконка»** - задает иконку модуля в пользовательской подсистеме
  (по умолчанию внешний модуль имеет стандартную иконку). Для того,
  чтобы изменить стандартную иконку, нужно кликнуть по кнопке
  «Выберите файл» и в диалоге выбора файла указать файл формата PNG,
  размер которого не превышает 28х26.

  .. figure:: _static/img/integration/web-module-add.png

     Добавление нового внешнего модуля

Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
780 781
.. hint:: Добавить внешний web-модуль можно с помощью SQL-запроса
          в БД ``synergy.outer_modules``, вставив запись со следующими полями:
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
782 783 784 785 786 787 788 789 790 791

              * ``id``  идентификатор модуля, должен совпадать с идентификатором
                 вашего проекта в репозитории проектов
              * ``nameru``, ``namekz``, ``nameen``  название модуля на русском,
                казахском и английском языках соответственно
              * ``url``  адрес приложения
              * ``description``  описание модуля
              * ``active``  активен ли модуль, 1/0.

Для реализации механизма :abbr:`SSO (Single Sign-On)` приложений,
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
792 793
ARTA Synergy при загрузке внешнего web-модуля будет в строку URL
добавлять три параметра:
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830

#. ``locale``  локаль авторизованного пользователя
#. ``sso_hash``  hash-сумма для идентификации пользователя.
#. ``host``  адрес, с которого загружено приложение Synergy

Например, если URL приложения

    ``http://host:port/plans_module``,

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

    ``http://host:port/plans_module?locale=locale_value&sso_hash=sso_hash_value``

Интегрированный модуль должен будет будет получить из URL
параметр ``sso_hash`` и запросить по REST API у ARTA Synergy
информацию об авторизованном пользователе (идентификатор, имя).
Если метод REST API возвращает информацию о пользователе,
это подтверждает, что данный пользователь
действительно авторизован с данного хоста, в данном браузере.

Далее строка ``sso_hash`` может быть использована для
`Сессионная авторизация`_ и вызова REST API Arta Synergy.

В ARTA Synergy реализована возможность обращения к ее модулям по относительной ссылке.
Такая же возможность существует для внешних web-модулей. Переход по ссылке вида:

``#submodule=outer&outerModuleID='код_модуля'&прочие_параметры_по_желанию_модуля``

активирует в Synergy заданный модуль и передаст ему заданные в url-e параметры
(параметры ``locale``, ``sso_hash``, ``host`` также будут переданы,
несмотря на то, что они отсутствуют в ссылке).

Часто возникает необходимость в этой ссылке передать ссылку на текущий документ.
Для этого можно добавить в ссылку параметр,
значение которого будет равно ``${docID}`` 
эта строка в web-интерфейсе проигрывателя форм Synergy
будет заменена на идентификатор данного документа.
Valentin Skripnikov's avatar
-  
Valentin Skripnikov committed
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933

Ссылки на модули системы и их внутренние элементы
-------------------------------------------------

Ссылки на модули и различные объекты Synergy
можно использовать как внутри основного web-приложения
(в этом случае предпочтительно использовать относительные
ссылки, чтобы не перезагружать страницу), так и во внешних
системах.

Общий вид ссылок:

    http[s]://**host***[:port]*/**Application**?*param1*=*value1*&*param2*=*value2*#*param3*=*value3*&*param4*=*value4*

где

* **host** - доменное имя или ip-адрес сервера Synergy
* *port* - порт
* **Application**:
    * `Synergy` - основное приложение
    * `Configurator` - Конфигуратор
    * `SynergyAdmin` - административное приложение
* *param1*, *param2* - параметры абсолютной ссылки
* *param3*, *param4* - параметры относительной ссылки

Параметры абсолютной ссылки - это, как правило:

* **locale** - локаль загружаемой системы
* **nocache** - специальный параметр, предотвращающий случайное кэширование

остальные параметры можно передавать как параметры относительной ссылки.

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

Ссылка на модуль системы
~~~~~~~~~~~~~~~~~~~~~~~~

``#submodule=module_id``


где ``module_id``:

* **workflow** - Потоки работ
* **calendar** - Ежедневник
* **repository** - Хранилище
* **plans** - Проекты
* **pointers** - Цели и показатели
* **employees** - Сотрудники

При переходе по ссылке откроется указанный модуль.

Ссылка на документ и файл в нём
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``#submodule=common&file_identifier=some_file_id&action=open_document&document_identifier=some_doc_id``

При переходе по такой ссылке откроется указанный документ с основным файлом,
а если указан ``file_identifier`` - то откроется документ с этим файлом.

Ссылка на проект и мероприятие в нем
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``#submodule=plans&action=open_action&action_identifier=some_action_id&project_identifier=some_project_id``

При переходе по такой ссылке откроется указанный проект,
а если указан ``action_identifier`` - то в проекте будет выделено
это мероприятие.

Ссылка на профиль пользователя
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``#submodule=employees&innermodule=structure&action=open_user&user_identifier=some_user_id``

При переходе по такой ссылке будет открыт модуль «Сотрудники»,
а в нем - профиль указанного пользователя

Отключение всего пользовательского клиентского скриптинга
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Если в абсолютной ссылке указать параметр ``noCustomScripting``,
то все пользовательские ВМК, скрипты в формах и пользовательских
компонентах будут отключены. Это можно использовать для отладки
пользовательских компонентов, ВМК и скриптов на форме.

Как задеплоить интеграционное приложение
----------------------------------------

ARTA Synergy работает на сервере приложений JBoss AS7.
Интеграционное приложение может представлять собой
*jar*-файл либо *war*-файл либо их комбинацию.

Если приложение является одиночным файлом, его можно задеплоить,
скопировав в директорию ``${jboss.home}/standalone/deployments``.
Если приложение состоит из нескольких файлов,
необходимо создать `*.ear` приложение.

Если приложение имеет зависимости на внешние библиотеки
и они находятся в модулях JBoss-а (``${jboss.home}/modules``),
необходимо использовать их, прочие зависимости  помещать внутрь приложения.

.. danger:: В целях безопасности работы приложения Synergy и сервера приложений
            категорически запрещается помещать артефакты интеграционного модуля
            в приложение ``Synergy.ear`` и изменять состав модулей (``${jboss.home}/modules``).