4.5.15. Примеры версионирования модели данных
- Атрибут сущности переименован
-
Предположим, атрибут
oldNumber
сущностиsales$Order
был переименован вnewNumber
, а атрибутdate
был переименован вdeliveryDate
. В этом случае конфигурация трансформации будет выглядеть следующим образом:<?xml version="1.0"?> <transformations xmlns="http://schemas.haulmont.com/cuba/rest-json-transformations.xsd"> <transformation modelVersion="1.0" currentEntityName="sales$Order"> <renameAttribute oldName="oldNumber" currentName="newNumber"/> <renameAttribute oldName="date" currentName="deliveryDate"/> </transformation> ... </transformations>
Если клиентскому приложению необходимо работать со старой версии сущности
sales$Order
, то приложение должно передать значение версии модели данных в параметре URLmodelVersion
:http://localhost:8080/app/rest/v2/entities/sales$Order/c838be0a-96d0-4ef4-a7c0-dff348347f93?modelVersion=1.0
Будет возвращен следующий результат:
{ "_entityName": "sales$Order", "_instanceName": "00001", "id": "46322d73-2374-1d65-a5f2-160461da22bf", "date": "2016-10-31", "description": "Vacation order", "oldNumber": "00001" }
Видим, что ответ содержит атрибуты
oldNumber
иdate
, хотя сущность в последней версии приложения уже имеет переименованные атрибутыnewNumber
иdeliveryDate
. - Имя сущности изменено
-
Предположим, что в одном из следующих релизов приложения имя сущности
sales$Order
также было изменено. Новое имя сущности теперьsales$NewOrder
.Конфиг трансформации для версии
1.1
выглядит так:<?xml version="1.0"?> <transformations xmlns="http://schemas.haulmont.com/cuba/rest-json-transformations.xsd"> <transformation modelVersion="1.1" oldEntityName="sales$Order" currentEntityName="sales$NewOrder"> <renameAttribute oldName="oldNumber" currentName="newNumber"/> </transformation> ... </transformations>
В дополнение к конфигу из предыдущего примера здесь появился атрибут
oldEntityName
. Он описывает имя сущности, действительное для версии модели данных1.1
. АтрибутcurrentEntityName
описывает текущее имя сущности.Хотя сущность с именем
sales$Order
более не существует, следующий запрос будет работать:http://localhost:8080/app/rest/v2/entities/sales$Order/c838be0a-96d0-4ef4-a7c0-dff348347f93?modelVersion=1.1
Контроллер REST API поймет, что поиск должен быть осуществлен среди экземпляров сущности
sales$NewOrder
, и после того, как сущность с заданным ID будет найдена, имя сущности и имя атрибутаnewNumber
будут заменены в JSON:{ "_entityName": "sales$Order", "_instanceName": "00001", "id": "46322d73-2374-1d65-a5f2-160461da22bf", "date": "2016-10-31", "description": "Vacation order", "oldNumber": "00001" }
Клиентское приложение также может использовать старую версию модели данных для создания или изменения сущности.
Этот POST запрос использует старое имя сущности и имеет JSON в старом формате в теле запроса, однако, запрос будет работать:
http://localhost:8080/app/rest/v2/entities/sales$Order
{ "_entityName": "sales$Order", "_instanceName": "00001", "id": "46322d73-2374-1d65-a5f2-160461da22bf", "date": "2016-10-31", "description": "Vacation order", "oldNumber": "00001" }
- Атрибут должен быть удален из JSON
-
Иногда может возникнуть ситуация, когда в сущность был добавлен новый атрибут, но клиент, работающий со старой версией модели данных, не должен получать этот атрибут при запросе к сущности. В этом случае стандартный JSON трансформер может удалить атрибут из результата.
Конфиг трансформации для данного случае выглядит примерно так:
<?xml version="1.0"?> <transformations xmlns="http://schemas.haulmont.com/cuba/rest-json-transformations.xsd"> <transformation modelVersion="1.5" currentEntityName="sales$Order"> <toVersion> <removeAttribute name="discount"/> </toVersion> </transformation> ... </transformations>
Описание трансформации здесь содержит тег
toVersion
с вложенной командойremoveAttribute
. Это значит, что при выполнении трансформации из текущей версии модели данных к определенной версии (например, при запросе списка сущностей) атрибутdiscount
будет удален из JSON.Если выполнить запрос к REST API без атрибута
modelVersion
, то атрибутdiscount
будет возвращен.http://localhost:8080/app/rest/v2/entities/sales$Order/c838be0a-96d0-4ef4-a7c0-dff348347f93
{ "_entityName": "sales$Order", "_instanceName": "00001", "id": "46322d73-2374-1d65-a5f2-160461da22bf", "deliveryDate": "2016-10-31", "description": "Vacation order", "number": "00001", "discount": 50 }
Если указать
modelVersion
в запросе, то атрибутdiscount
будет удален:http://localhost:8080/app/rest/v2/entities/sales$Order/c838be0a-96d0-4ef4-a7c0-dff348347f93?modelVersion=1.1
{ "_entityName": "sales$Order", "_instanceName": "00001", "id": "46322d73-2374-1d65-a5f2-160461da22bf", "deliveryDate": "2016-10-31", "description": "Vacation order", "oldNumber": "00001" }
- Использование кастомных трансформеров
-
Вы также можете создать и зарегистрировать свой собственный трансформер JSON. В качестве примера рассмотрим следующую ситуацию.
Сущность
sales$OldOrder
была переименована вsales$NewOrder
. В сущности имеется полеorderDate
с типом дата. В старой версии сущности это поле содержало часть со временем, в новой версии сущности часть со временем в поле отсутствует. Клиент REST API, запрашивающий сущность со старой версией модели данных1.0
ожидает, что дата будет иметь часть со временем. Получается, что JSON трансформер должен изменить значение в JSON.Так будет выглядеть конфигурация трансформации для данного случая:
<?xml version="1.0"?> <transformations xmlns="http://schemas.haulmont.com/cuba/rest-json-transformations.xsd"> <transformation modelVersion="1.0" oldEntityName="sales$OldOrder" currentEntityName="sales$NewOrder"> <custom> <fromVersion transformerBeanRef="sales_OrderJsonTransformerFromVersion"/> <toVersion transformerBeanRef="sales_OrderJsonTransformerToVersion"/> </custom> </transformation> ... </transformations>
Конфигурация содержит элемент
custom
с вложенными элементамиtoVersion
иfromVersion
. Эти элементы содержат ссылки на бины, т.е. кастомный трансформер должен быть зарегистрирован как Spring bean.Важная деталь: в кастомном трансформере возможно потребуется использовать бин платформы
RestTransformations
(он предоставляет доступ к трансформерам для других сущностей). Но бинRestTransformations
зарегистрирован в Spring контексте сервлета REST API, а не в главном контексте веб-приложения. Это значит, что бин кастомного трансформера также должен быть зарегистрирован в контексте REST API.Как это сделать. Во-первых, создайте файл
rest-dispatcher-spring.xml
в модуле web или portal (например в пакетеcom.company.test
).Затем зарегистрируйте этот файл в файле свойств
app.properties
соответствующего модуля.cuba.restSpringContextConfig = +com/company/test/rest-dispatcher-spring.xml
Файл
rest-dispatcher-spring.xml
должен содержать определения бинов для кастомных трансформеров:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <bean name="sales_OrderJsonTransformerFromVersion" class="com.company.test.transformer.OrderJsonTransformerFromVersion"/> <bean name="sales_OrderJsonTransformerToVersion" class="com.company.test.transformer.OrderJsonTransformerToVersion"/> </beans>
Исходный код бина
sales_OrderJsonTransformerToVersion
:package com.company.test.transformer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.base.Strings; import com.haulmont.restapi.transform.AbstractEntityJsonTransformer; import com.haulmont.restapi.transform.JsonTransformationDirection; public class OrderJsonTransformerToVersion extends AbstractEntityJsonTransformer { public OrderJsonTransformerToVersion() { super("sales$NewOrder", "sales$OldOrder", "1.0", JsonTransformationDirection.TO_VERSION); } @Override protected void doCustomTransformations(ObjectNode rootObjectNode, ObjectMapper objectMapper) { JsonNode orderDateNode = rootObjectNode.get("orderDate"); if (orderDateNode != null) { String orderDateNodeValue = orderDateNode.asText(); if (!Strings.isNullOrEmpty(orderDateNodeValue)) rootObjectNode.put("orderDate", orderDateNodeValue + " 00:00:00.000"); } } }
Данный трансформер находит элемент
orderDate
в JSON и изменяет значение элемента, добавляя к нему часть со временем.Когда сущность
sales$Order
будет запрошена с версией модели данных1.0
, то JSON результат будет содержать сущность, полеorderDate
которой содержит часть со временем, хотя текущий тип поля сущности - дата без времени.Несколько слов о кастомных трансформерах. Они должны реализовывать интерфейс
EntityJsonTransformer
. Вы также можете унаследоваться от классаAbstractEntityJsonTransformer
и переопределить его методdoCustomTransformations
. КлассAbstractEntityJsonTransformer
содержит функциональность стандартных трансформеров.