Стейт-машина СМЭВ QL ^^^^^^^^^^^^^^^^^^^^^ СМЭВ QL содержит встроенную машину состояний для изменения объектов модели внутри витрин данных. Одновременно с этим Стейт машина может, в качестве подтверждения перехода состояния, использовать внешний источник (например ИС Электронной очереди). Конфигурирование Стейт-машины ################################## Карта состояний и переходов описывается в формате YAML-файла ``state.yaml`` располагаемого в папке ``states/<имя-модели>/<х.х версия модели>`` инстанса СМЭВ QL Сервера. Описание формата и правил карты состояний: .. code-block:: yaml model: slot # имя модели states: # массив состояний объекта - state: available # название состояния available attributes: # массив атрибутов, описывающих состояние - name: status # состояние определятся значением атрибута status value: AVAILABLE # значение атрибута для описываемого состояния initial: true - state: booked attributes: - name: status value: RECORDED - state: reserved attributes: - name: status value: RESERVED - state: cancelled attributes: - name: status value: CANCELED - state: blocked attributes: - name: status value: BLOCKED events: # список событий изменения состояний, из них создаются методы API - event: book # создает метод POST /states/slot/book from: # массив состояний из которых возможен вызов события - available - reserved to: booked # в какое состояние переводится объект после события hooks: # массив связанных событий - model: book # после перевода надо вызвать событие init для модели book event: init confirm: source: rmis_rest # названия источника endpoint: /booking/book method: post body: payload # что включать в тело запроса (full|state|conditions|payload|credentials) accept: # условие принятия jsonpath: $.status.statusCode eq: 0 # ожидаем, что statusCode будет равен 0 - event: reserve from: available to: reserved - event: block from: available to: blocked - event: cancel from: - available - reserved - booked - blocked to: cancelled hooks: - model: book event: cancel Удаление записи через Стейт-машину ################################### Статус удаления отмечается флагом ``delete: true``, соответствует удалению записи по conditions. Значения атрибутов для такого статуса не применимы, при наличи атрибутов необходимо выдать предупреждение в лог и проигнорировать их. Статус с признаком ``delete`` конечный по смыслу в графе состояний, он не может использоваться в разделе ``from`` любых событий. Проверок на эту тему не делаем, даже если кто то и пропишет из него переход, то условие никогда не выполнится. Передача данных без изменения статуса (статичный ивент) ############################################################ Можно передавать данные к связанному источнику из блока ``confirm`` с использованием флага статичного запроса при создании стейта: ``static: true``. Созданный с указанием стейта с флагом ``static: true`` ивент не меняет статус модели, а только передает указанную в описании ивента часть запроса в блоке confirm к связанному источнику. Пример state ивента: .. code-block:: yaml model: slot # имя модели states: # массив состояний объекта - state: static static: true events: # список событий изменения состояний, из них создаются методы API - event: state from: - available - reserved - booked - blocked - cancelled - deleted to: static # в какое состояние переводится объект после события confirm: source: rmis_rest # названия источника endpoint: /booking/book method: post body: payload # что включать в тело запроса (full|state|conditions|payload|credentials) accept: # условие принятия jsonpath: $.status.statusCode eq: 0 # ожидаем, что statusCode будет равен 0 Обновление объектов через Стейт-машину ###################################### Через Стейт-машину можно обновлять записи в витрине. Для этого при конфигурировании карты состояний необходимо задать значение у event ``updatable: true``. При этом если требуется дать возможность обновлять только часть атрибутов, то ограничить этот список можно перечислив атрибуты в массиве ``updatable_attribute``s. .. code-block:: yaml - event: reserve from: available to: reserved updatable: true // по умолчанию false для всех, кроме init, возможность изменять запись при переводе статуса updatable_attributes: [] // массив атрибутов, которые можно обновлять, пустой — можно все Данные для обновления будут браться из блока ``payload`` запроса на смену состояния. Для события ``init`` такое конфигурирование не требуется — по умолчанию обновления разрешены для всех атрибутов из ``payload``. Обогащение payload запроса дополнительными атрибутами ##################################################### Можно обогащать ``payload`` запроса к Витрине. Для этого необходимо выставить в блоке ``enrich`` раздела ``confim`` значение ``allow: true``, с указанием ``jsonpath`` для получения атрибутов для обогащения. Пример ивента с блоком enrich: .. code-block:: yaml events: # список событий изменения состояний, из них создаются методы API - event: book # создает метод POST /states/slot/book from: # массив состояний из которых возможен вызов события - available - reserved to: booked # в какое состояние переводится объект после события hooks: # массив связанных событий - model: book # после перевода надо вызвать событие init для модели book event: init confirm: source: rmis_rest # названия источника endpoint: /booking/book method: post body: payload # что включать в тело запроса (full|state|conditions|payload|credentials) accept: # условие принятия jsonpath: $.status.statusCode eq: 0 # ожидаем, что statusCode будет равен 0 enrich: allow: true // по умолчанию false attributes: [] // пустой массив — все, а если перечислены, то только эти jsonpath: $.update.payload // ожидается что по этому адресу объект с атрибутами и значениями При выставлении атрибута ``allow: true`` в блоке ``enrich``, при выполнении запроса блока ``confirm`` происходит обращение к объекту, указанному в переменной jsonpath с возвратом атрибутов, перечисленных в массиве ``atttributes`` (пустой массив означает отсутствие ограничений). Извлеченные атрибуты возвращаются в ``payload`` ответа ``confirm`` запроса. Пример возможного ``confirm`` ответа от ИС приведен ниже: .. code-block:: yaml { "status": "OK", "update": { "payload": { "position": "Тест1", "rvr_cv_id": "8888888-AEA2-4EF7-95C4-B29A4A5E990", "rvr_candidate_id": "99999999-AEA2-4EF7-95C4-B29A4A5E990" } } } Атрибуты ``payload`` ответа ``confirm`` запроса обогащают ``payload`` основного запроса. Обновление данных в витрине в случае обогащения payload дополнительными атрибутами происходит независимо от значения ``updatable``. При отсутствии объекта по пути jsonpath в confirm запросе возвращается ошибка ``501 "Отсутствует объект при обращении по jsonpath"``, выполнение запроса прерывается. При несоответствии атрибутов в блоке ``attributes`` и в ``jsonpath`` - в ``payload`` вернутся только те атрибуты, по которым есть соответствия с фиксацией в логах событий уровня WARN о несоответствующих атрибутах: ``"Отсутствует атрибут *attribute_name" в $.update.payload"``. В случае, если в ``payload`` присутствуют атрибуты, аналогичные тем, что возвращаются при выполнении блока ``enrich``, обновляются данные в соответствии со значениями вернувшимися при выполнении блока enrich. В этом случае в логах фиксируется запись ``"Изменение значений атрибутов: *attribute_name" payload в соответствии со значениями блока enrich"``. Методы API Стейт-машины ########################## При наличии заполненных состояний машины СМЭВ QL Сервер генерирует API c набором HTTP-методов, отвечающих за изменение и просмотр состояний объектов: 1. ``GET /states`` — получить карту переходов; 2. ``GET /states/`` — получить карту переходов конкретной модели; 3. ``POST /states//`` — выполнить переход состояний для модели. Запрос выполнения перехода: .. code-block:: yaml POST /states/slot/book { "state": { "conditions": { "id": "d9e70331-b4c0-4e96-96b6-322ac75e5188" # slot_id }, "payload": { "bookId":"82dcac12-0a29-4fff-b9a7-8dfc84f7853d", "patient_Id":"23453456", "booking_type":"APPOINTMENT", "caseNumber":"73367196", "preliminaryReservation": false, "email":"email@gmail.com", "mobilePhone":"89150000102", "referral_id":"102111", "cards_id":"102" } }, "credentials": { "system": { "mnemonic": "117bed7f-1c07-4079-a446-1161588db4e5", "instance_id": "ccb4a88f-f44b-43e7-8a97-3e45c8345e90", "user_id": "5ed38461-0907-486a-930a-7b443482932c" }, "request": { "id": "df5a0073-c6be-4d8c-8eb2-9b2f4188a429", "sub_id": "0cdb59ce-224b-4118-8da1-c5ea08a5d955", "name": "request_name", "purpose_id": "ed1170f1-3caa-4985-aa38-c9c5a190b770", "audit": false, "audit_id": "fc1048fe-323d-4eeb-92df-5710b3d1d100", "audit_token": false } } } Спецификация интерфейса Стейт-машины ##################################### .. code-block:: yaml openapi: 3.0.0 x-stoplight: id: 5i2oag6m5eq3v info: title: SmevQLStateMachine version: '1.0' description: '' servers: - url: 'http://localhost:3000' paths: /states: parameters: [] get: summary: Get models tags: [] responses: '200': description: '' content: application/x-yaml: schema: $ref: '#/components/schemas/Models' examples: {} application/xml: schema: type: object properties: {} multipart/form-data: schema: type: object properties: {} text/html: schema: type: object properties: {} operationId: get-models description: Retrieve the information of models '/states/{model}': parameters: - schema: type: string name: model in: path required: true get: summary: Get model tags: [] responses: '200': description: Model Found content: application/x-yaml: schema: $ref: '#/components/schemas/Model' examples: {} '400': description: Bad Request '404': description: Model Not Found operationId: get-model description: Retrieve the information of model parameters: [] '/states/{model}/{event}': post: summary: State change operationId: post-state responses: '200': description: State Updated content: plain/text: schema: type: string examples: {} '400': description: Bad request '404': description: Not Found requestBody: content: application/json: schema: $ref: '#/components/schemas/StateUpdate' examples: {} description: Post the necessary fields for the API to create a new user. description: Update state parameters: [] parameters: - schema: type: string name: model in: path required: true - schema: type: string name: event in: path required: true components: schemas: State: type: object x-stoplight: id: 89f55561cae04 properties: state: type: string attributes: type: array minItems: 1 items: $ref: '#/components/schemas/Attribute' initial: type: boolean Model: type: object x-stoplight: id: b96a73db1e1b2 properties: model: type: string states: type: array minItems: 1 items: $ref: '#/components/schemas/State' events: type: array items: $ref: '#/components/schemas/Event' Events: title: Events x-stoplight: id: qdvbkmgs9xkli type: array items: $ref: '#/components/schemas/Event' States: $ref: '#/components/schemas/State' x-stoplight: id: wab1w6ro30nrl Event: title: Event x-stoplight: id: sr024y2v7khum type: object properties: event: type: string from: type: string to: type: string Models: title: ModelSet x-stoplight: id: e7de590d788a7 type: array items: $ref: '#/components/schemas/ModelInstance' ModelInstance: type: object properties: model: type: string states: $ref: '#/components/schemas/States' events: $ref: '#/components/schemas/Events' ModelSet: type: array items: $ref: '#/components/schemas/Model' Attribute: title: Attribute x-stoplight: id: 3kiqu047734tc type: object properties: name: type: string value: type: string description: '' StateUpdate: title: StateUpdate x-stoplight: id: 2iyfifo0q1dt2 type: object properties: state: type: object properties: conditions: type: object payload: type: object credentials: type: object properties: system: type: object properties: mnemonic: type: string instance_id: type: string user_id: type: string request: type: object properties: id: type: string format: uuid sub_id: type: string format: uuid name: type: string purpose_id: type: string format: uuid audit: type: boolean audit_id: type: string format: uuid audit_token: type: string **Пример реализации: Спецификация интерфейса Стейт-машины РМИС (OpenAPI)** .. code-block:: yaml openapi: 3.0.0 x-stoplight: id: 5i2oag6m5eq3v info: title: SmevQLStateMachine version: '1.0' description: '' servers: - url: 'http://localhost:3000' paths: /states: parameters: [] get: summary: Get models tags: [] responses: '200': description: '' content: application/x-yaml: schema: $ref: '#/components/schemas/Models' examples: {} application/xml: schema: type: object properties: {} multipart/form-data: schema: type: object properties: {} text/html: schema: type: object properties: {} operationId: get-models description: Retrieve the information of models '/states/{model}': parameters: - schema: type: string name: model in: path required: true get: summary: Get model tags: [] responses: '200': description: Model Found content: application/x-yaml: schema: $ref: '#/components/schemas/Model' examples: {} '400': description: Bad Request '404': description: Model Not Found operationId: get-model description: Retrieve the information of model parameters: [] '/states/{model}/{event}': post: summary: State change operationId: post-state responses: '200': description: State Updated content: plain/text: schema: type: string examples: {} '400': description: Bad request '404': description: Not Found requestBody: content: application/json: schema: $ref: '#/components/schemas/StateUpdate' examples: {} description: Post the necessary fields for the API to create a new user. description: Update state parameters: [] parameters: - schema: type: string name: model in: path required: true - schema: type: string name: event in: path required: true components: schemas: State: type: object x-stoplight: id: 89f55561cae04 properties: state: type: string attributes: type: array minItems: 1 items: $ref: '#/components/schemas/Attribute' initial: type: boolean Model: type: object x-stoplight: id: b96a73db1e1b2 properties: model: type: string states: type: array minItems: 1 items: $ref: '#/components/schemas/State' events: type: array items: $ref: '#/components/schemas/Event' Events: title: Events x-stoplight: id: qdvbkmgs9xkli type: array items: $ref: '#/components/schemas/Event' States: $ref: '#/components/schemas/State' x-stoplight: id: wab1w6ro30nrl Event: title: Event x-stoplight: id: sr024y2v7khum type: object properties: event: type: string from: type: string to: type: string Models: title: ModelSet x-stoplight: id: e7de590d788a7 type: array items: $ref: '#/components/schemas/ModelInstance' ModelInstance: type: object properties: model: type: string states: $ref: '#/components/schemas/States' events: $ref: '#/components/schemas/Events' ModelSet: type: array items: $ref: '#/components/schemas/Model' Attribute: title: Attribute x-stoplight: id: 3kiqu047734tc type: object properties: name: type: string value: type: string description: '' StateUpdate: title: StateUpdate x-stoplight: id: 2iyfifo0q1dt2 type: object properties: state: type: object properties: conditions: type: object properties: id: type: string format: uuid payload: type: object properties: book_id: type: string format: uuid patient_id: type: string booking_type: type: string case_number: type: string preliminary_reservation: type: boolean email: type: string mobile_phone: type: string referral_id: type: string cards_id: type: string credentials: type: object properties: system: type: object properties: mnemonic: type: string instance_id: type: string user_id: type: string request: type: object properties: id: type: string format: uuid sub_id: type: string format: uuid name: type: string purpose_id: type: string format: uuid audit: type: boolean audit_id: type: string format: uuid audit_token: type: string Выполнение операций обновления данных в витрине """""""""""""""""""""""""""""""""""""""""""""""" Инсерты в витрину выполняются в порядке поступления запросов. Каждый экземпляр СМЭВ QL сервера ведет нарастающий счетчик инсертов. Каждый экземпляр СМЭВ QL сервера создает один поток управления дельтами, который выполняет: 1. Периодически (период конфигурируемая величина, по умолчанию 60 сек) проверяет значение счетчика числа инсертов, если значение счетчика более 0 - Выполняет открытие и закрытие дельты с флагом immediate, ошибки открытия и закрытия дельты игнорируются. Попытка закрытия дельты выполняется независимо от успешности открытия дельты. - Обнуляет значение счетчика. 2. Периодически (период конфигурируемая величина, по умолчанию 30 мин) - Выполняет открытие и закрытие дельты с флагом immediate, ошибки открытия и закрытия дельты игнорируются. Попытка закрытия дельты выполняется независимо от успешности открытия дельты. Чтение данных сервером СМЭВQL выполняется с применением AS OF . Обновление объектов через Стейт-машину """"""""""""""""""""""""""""""""""""""""" Через Стейт-машину можно обновлять записи в витрине. Для этого при конфигурировании карты состояний необходимо задать значение у **event** ``updatable: true``. При этом если требуется дать возможность обновлять только часть атрибутов, то ограничить этот список можно перечислив атрибуты в массиве ``updatable_attributes``. .. code-block:: yaml - event: reserve from: available to: reserved updatable: true // по умолчанию false для всех, кроме init, возможность изменять запись при переводе статуса updatable_attributes: [] // массив атрибутов, которые можно обновлять, пустой — можно все Данные для обновления будут браться из блока **payload** запроса на смену состояния. Для события init такое конфигурирование не требуется - по умолчанию обновления разрешены для всех атрибутов из **payload**.