Что делать если я тестирую API, и для одного и того же по смыслу предмета от одного эндпоинта приходит одна модель данных, а от другого другая?
На примере - представим APIшку магазина китайского чая🫖
У APIшки есть два эндпоинта:
1. ➕эндпоинт для добавления нового сорта чая в ассортимент магазина
POST /tea
в теле запроса отправляется json с данными о добавляемом сорте чая, типа такого:
{
"newTeaName": "Жоу Гуй",
"type": "улун"
}2. ☕️эндпоинт для получения рекомендуемого чая сезона
GET /teaOfTheDay
а в теле ответа возвращается объект:
{
"id":"2"
"teaName":"Жоу Гуй"
"type":"улун"
}Чертовски похожие объекты, что там чай, что там чай, ну окей. Смоделировать json-объект отправляемого чая (1) и возвращаемого чая (2) можно такими нехитрыми ДТОшками с применением аннотации @Data (lombok):
@Data
public class Tea {
private String newTeaName;
private String type;
}
@Data
public class ShopTea {
private int id;
private String teaName;
private String type;
}
Мы видим что речь идет о практически идентичных объектах. Но их сложно сравнивать друг с другом по следующим причинам:
1. Tea & ShopTea - объекты разного типа
2. имена/состав полей не соответствуют друг другу или соответствуют не полностью (к примеру поля teaName & newTeaName хранят по сути одинаковую информацию но называются по разному)
А еще возможно что у вас уже написано немало методов-утилит для работы с классом Tea, но вот объект ShopTea туда уже не передать.
Задача перед нами - превратить объект типа ShopTea в объект типа Shop. Для таких превращений выделяют специальные классы-мапперы (mapper):
public class OldTeaMapper {
Tea shopTeaToTea(ShopTea shopTea) {
Tea tea = new Tea();
tea.setNewTeaName(shopTea.getTeaName());
tea.setType(shopTea.getType());
return tea;
}
}Вроде и неплохо, но логику преобразования пришлось написать ручками. А самое главное - в жизни в json от сервисов - намного больше полей (десятки), и рукописного труда оказалось бы дофига. Тут и врывается MapStruct - для генерации таких классов-мапперов. Код для маппера с применением MapStruct будет выглядеть вот так:
@Mapper
public interface MapStructTeaMapper {
@Mapping(source = "teaName", target = "newTeaName")
Tea shopTeaToTea(ShopTea shopTea);
}
Причем логику преобразования совпадающих по имени-типу полей можно вообще не описывать - через @Mapping нам пришлось описать только особый случай, когда поля не совпадали по имени. Ну а если поле у первого объекта совпадает с полем второго и по типу и по имени - никаких аннотаций писать не надо.
Для работы этой библиотеки надо:
1. указать ее как зависимость
2. настроить ее работу с плагином компилятора
3. применить в коде
И все это, с пылу-с жару подготовленное для вас - находится здесь:
https://gitlab.com/RevRay/mapstruct-demo
Пункты 1 и 2 выполняются в pom.xml для Maven-проекта, а пункт 3 - код для разобранного выше примера - есть в классе MapStructTest.
Изучайте пример, читайте документацию (https://mapstruct.org/), пейте вкусный чай и пишите мапперы как преисполнившиеся в своем познании люди🤓