3.9.11.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содержит функциональность стандартных трансформеров.