├── .circleci └── config.yml ├── README.md ├── pom.xml ├── renovate.json └── src ├── main ├── java │ └── pl │ │ └── piomin │ │ └── services │ │ └── versioning │ │ ├── VersioningApplication.java │ │ ├── controller │ │ ├── PersonController.java │ │ └── PersonControllerWithHeaders.java │ │ ├── mapper │ │ └── PersonMapper.java │ │ ├── model │ │ ├── Gender.java │ │ ├── Person.java │ │ ├── PersonCurrent.java │ │ └── PersonOld.java │ │ └── repository │ │ └── PersonRepository.java └── resources │ └── application.yml └── test └── java └── pl └── piomin └── services └── versioning ├── PersonControllerTests.java └── PersonControllerWithHeadersTests.java /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | jobs: 4 | build: 5 | docker: 6 | - image: 'cimg/openjdk:21.0.6' 7 | steps: 8 | - checkout 9 | - run: 10 | name: Analyze on SonarCloud 11 | command: mvn verify sonar:sonar 12 | 13 | executors: 14 | jdk: 15 | docker: 16 | - image: 'cimg/openjdk:21.0.6' 17 | 18 | orbs: 19 | maven: circleci/maven@2.0.0 20 | 21 | workflows: 22 | maven_test: 23 | jobs: 24 | - maven/test: 25 | executor: jdk 26 | - build: 27 | context: SonarCloud -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Versioning REST API with Spring Boot and Swagger [![Twitter](https://img.shields.io/twitter/follow/piotr_minkowski.svg?style=social&logo=twitter&label=Follow%20Me)](https://twitter.com/piotr_minkowski) 2 | 3 | [![CircleCI](https://circleci.com/gh/piomin/sample-api-versioning.svg?style=svg)](https://circleci.com/gh/piomin/sample-api-versioning) 4 | 5 | [![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-black.svg)](https://sonarcloud.io/dashboard?id=piomin_sample-api-versioning) 6 | [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=piomin_sample-api-versioning&metric=bugs)](https://sonarcloud.io/dashboard?id=piomin_sample-api-versioning) 7 | [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=piomin_sample-api-versioning&metric=coverage)](https://sonarcloud.io/dashboard?id=piomin_sample-api-versioning) 8 | [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=piomin_sample-api-versioning&metric=ncloc)](https://sonarcloud.io/dashboard?id=piomin_sample-api-versioning) 9 | 10 | Detailed description can be found here: [Versioning REST API with Spring Boot and Swagger](https://piotrminkowski.com/2018/02/19/versioning-rest-api-with-spring-boot-and-swagger/) 11 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | pl.piomin.services 5 | sample-api-versioning 6 | 1.0-SNAPSHOT 7 | 8 | 9 | 21 10 | piomin_sample-api-versioning 11 | piomin 12 | https://sonarcloud.io 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-parent 18 | 3.5.0 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | org.springdoc 28 | springdoc-openapi-starter-webmvc-ui 29 | 2.8.8 30 | 31 | 32 | com.fasterxml.jackson.datatype 33 | jackson-datatype-jsr310 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | 39 | 40 | org.instancio 41 | instancio-junit 42 | 5.4.1 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-maven-plugin 51 | 52 | 53 | 54 | build-info 55 | 56 | 57 | 58 | 59 | 60 | org.jacoco 61 | jacoco-maven-plugin 62 | 0.8.13 63 | 64 | 65 | 66 | prepare-agent 67 | 68 | 69 | 70 | report 71 | test 72 | 73 | report 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base",":dependencyDashboard" 5 | ], 6 | "packageRules": [ 7 | { 8 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"], 9 | "automerge": true 10 | } 11 | ], 12 | "prCreation": "not-pending" 13 | } -------------------------------------------------------------------------------- /src/main/java/pl/piomin/services/versioning/VersioningApplication.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.versioning; 2 | 3 | import org.springdoc.core.models.GroupedOpenApi; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.Bean; 7 | import pl.piomin.services.versioning.model.Gender; 8 | import pl.piomin.services.versioning.model.PersonOld; 9 | import pl.piomin.services.versioning.repository.PersonRepository; 10 | 11 | import java.time.LocalDate; 12 | 13 | @SpringBootApplication 14 | public class VersioningApplication { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(VersioningApplication.class, args); 18 | } 19 | 20 | @Bean 21 | PersonRepository repository() { 22 | PersonRepository repository = new PersonRepository(); 23 | repository.add(new PersonOld(1L, "John Smith", Gender.MALE, LocalDate.parse("1977-01-20"))); 24 | repository.add(new PersonOld(2L, "Lawrence Crawford", Gender.MALE, LocalDate.parse("1987-01-20"))); 25 | repository.add(new PersonOld(3L, "Adam Blair", Gender.MALE, LocalDate.parse("1982-01-20"))); 26 | repository.add(new PersonOld(4L, "Laura Saint", Gender.FEMALE, LocalDate.parse("1965-01-20"))); 27 | return repository; 28 | } 29 | 30 | @Bean 31 | public GroupedOpenApi personApiViaHeaders() { 32 | return GroupedOpenApi.builder() 33 | .group("person-via-headers") 34 | .pathsToMatch("/persons-via-headers/**") 35 | .build(); 36 | } 37 | 38 | @Bean 39 | public GroupedOpenApi personApi10() { 40 | return GroupedOpenApi.builder() 41 | .group("person-api-1.0") 42 | .pathsToMatch("/person/v1.0/**") 43 | .build(); 44 | } 45 | 46 | @Bean 47 | public GroupedOpenApi personApi11() { 48 | return GroupedOpenApi.builder() 49 | .group("person-api-1.1") 50 | .pathsToMatch("/person/v1.1/**") 51 | .build(); 52 | } 53 | 54 | @Bean 55 | public GroupedOpenApi personApi12() { 56 | return GroupedOpenApi.builder() 57 | .group("person-api-1.2") 58 | .pathsToMatch("/person/v1.2/**") 59 | .build(); 60 | } 61 | 62 | // @Bean 63 | // public Docket swaggerPersonApi10() { 64 | // return new Docket(DocumentationType.SWAGGER_2) 65 | // .groupName("person-api-1.0") 66 | // .select() 67 | // .apis(RequestHandlerSelectors.basePackage("pl.piomin.services.versioning.controller")) 68 | // .paths(regex("/person/v1.0.*")) 69 | // .build() 70 | // .apiInfo(new ApiInfoBuilder().version("1.0").title("Person API").description("Documentation Person API v1.0").build()); 71 | // } 72 | // 73 | // @Bean 74 | // public Docket swaggerPersonApi11() { 75 | // return new Docket(DocumentationType.SWAGGER_2) 76 | // .groupName("person-api-1.1") 77 | // .select() 78 | // .apis(RequestHandlerSelectors.basePackage("pl.piomin.services.versioning.controller")) 79 | // .paths(regex("/person/v1.1.*")) 80 | // .build() 81 | // .apiInfo(new ApiInfoBuilder().version("1.1").title("Person API").description("Documentation Person API v1.1").build()); 82 | // } 83 | // 84 | // @Bean 85 | // public Docket swaggerPersonApi12() { 86 | // return new Docket(DocumentationType.SWAGGER_2) 87 | // .groupName("person-api-1.2") 88 | // .select() 89 | // .apis(RequestHandlerSelectors.basePackage("pl.piomin.services.versioning.controller")) 90 | // .paths(regex("/person/v1.2.*")) 91 | // .build() 92 | // .apiInfo(new ApiInfoBuilder().version("1.2").title("Person API").description("Documentation Person API v1.2").build()); 93 | // } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/services/versioning/controller/PersonController.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.versioning.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.DeleteMapping; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.PutMapping; 9 | import org.springframework.web.bind.annotation.RequestBody; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | import pl.piomin.services.versioning.mapper.PersonMapper; 14 | import pl.piomin.services.versioning.model.PersonCurrent; 15 | import pl.piomin.services.versioning.model.PersonOld; 16 | import pl.piomin.services.versioning.repository.PersonRepository; 17 | 18 | @RestController 19 | @RequestMapping("/person") 20 | public class PersonController { 21 | 22 | @Autowired 23 | PersonMapper mapper; 24 | @Autowired 25 | PersonRepository repository; 26 | 27 | @PostMapping({"/v1.0", "/v1.1"}) 28 | public PersonOld add(@RequestBody PersonOld person) { 29 | return (PersonOld) repository.add(person); 30 | } 31 | 32 | @PostMapping("/v1.2") 33 | public PersonCurrent add(@RequestBody PersonCurrent person) { 34 | // return mapper.map((PersonOld) repository.add(person)); 35 | return (PersonCurrent) repository.add(person); 36 | } 37 | 38 | @PutMapping("/v1.0") 39 | @Deprecated 40 | public PersonOld update(@RequestBody PersonOld person) { 41 | return (PersonOld) repository.update(person); 42 | } 43 | 44 | @PutMapping("/v1.1/{id}") 45 | public PersonOld update(@PathVariable("id") Long id, @RequestBody PersonOld person) { 46 | return (PersonOld) repository.update(person); 47 | } 48 | 49 | @PutMapping("/v1.2/{id}") 50 | public PersonCurrent update(@PathVariable("id") Long id, @RequestBody PersonCurrent person) { 51 | return mapper.map((PersonOld) repository.update(person)); 52 | } 53 | 54 | @GetMapping({"/v1.0/{id}", "/v1.1/{id}"}) 55 | public PersonOld findByIdOld(@PathVariable("id") Long id) { 56 | return (PersonOld) repository.findById(id); 57 | } 58 | 59 | @GetMapping("/v1.2/{id}") 60 | public PersonCurrent findById(@PathVariable("id") Long id) { 61 | return mapper.map((PersonOld) repository.findById(id)); 62 | } 63 | 64 | @DeleteMapping({"/v1.0/{id}", "/v1.1/{id}", "/v1.2/{id}"}) 65 | public void delete(@PathVariable("id") Long id) { 66 | repository.delete(id); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/services/versioning/controller/PersonControllerWithHeaders.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.versioning.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.*; 5 | import pl.piomin.services.versioning.mapper.PersonMapper; 6 | import pl.piomin.services.versioning.model.PersonCurrent; 7 | import pl.piomin.services.versioning.model.PersonOld; 8 | import pl.piomin.services.versioning.repository.PersonRepository; 9 | 10 | @RestController 11 | @RequestMapping("/persons-via-headers") 12 | public class PersonControllerWithHeaders { 13 | 14 | @Autowired 15 | PersonMapper mapper; 16 | @Autowired 17 | PersonRepository repository; 18 | 19 | @PostMapping(headers = "X-VERSION=v1.0") 20 | public PersonOld add(@RequestBody PersonOld person) { 21 | return (PersonOld) repository.add(person); 22 | } 23 | 24 | @PostMapping(headers = "X-VERSION=v1.2") 25 | public PersonCurrent add(@RequestBody PersonCurrent person) { 26 | return (PersonCurrent) repository.add(person); 27 | } 28 | 29 | @PutMapping(headers = "X-VERSION=v1.0") 30 | @Deprecated 31 | public PersonOld update(@RequestBody PersonOld person) { 32 | return (PersonOld) repository.update(person); 33 | } 34 | 35 | @PutMapping(value = "/{id}", headers = "X-VERSION=v1.1") 36 | public PersonOld update(@PathVariable("id") Long id, @RequestBody PersonOld person) { 37 | return (PersonOld) repository.update(person); 38 | } 39 | 40 | @PutMapping(value = "/{id}", headers = "X-VERSION=v1.2") 41 | public PersonCurrent update(@PathVariable("id") Long id, @RequestBody PersonCurrent person) { 42 | return mapper.map((PersonOld) repository.update(person)); 43 | } 44 | 45 | @GetMapping(value = "/{id}", headers = "X-VERSION=v1.0") 46 | public PersonOld findByIdOld(@PathVariable("id") Long id) { 47 | return (PersonOld) repository.findById(id); 48 | } 49 | 50 | @GetMapping(value = "/{id}", headers = "X-VERSION=v1.2") 51 | public PersonCurrent findById(@PathVariable("id") Long id) { 52 | return mapper.map((PersonOld) repository.findById(id)); 53 | } 54 | 55 | @DeleteMapping("/{id}") 56 | public void delete(@PathVariable("id") Long id) { 57 | repository.delete(id); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/services/versioning/mapper/PersonMapper.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.versioning.mapper; 2 | 3 | import java.time.LocalDate; 4 | import java.time.Period; 5 | 6 | import org.springframework.stereotype.Component; 7 | 8 | import pl.piomin.services.versioning.model.PersonCurrent; 9 | import pl.piomin.services.versioning.model.PersonOld; 10 | 11 | @Component 12 | public class PersonMapper { 13 | 14 | public PersonCurrent map(PersonOld person) { 15 | int age = Period.between(person.getBirthDate(), LocalDate.now()).getYears(); 16 | return new PersonCurrent(person.getId(), person.getName(), person.getGender(), age); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/services/versioning/model/Gender.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.versioning.model; 2 | 3 | public enum Gender { 4 | 5 | MALE, FEMALE; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/services/versioning/model/Person.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.versioning.model; 2 | 3 | public abstract class Person { 4 | 5 | private Long id; 6 | private String name; 7 | private Gender gender; 8 | 9 | public Person() { 10 | 11 | } 12 | 13 | public Person(Long id, String name, Gender gender) { 14 | this.id = id; 15 | this.name = name; 16 | this.gender = gender; 17 | } 18 | 19 | public Long getId() { 20 | return id; 21 | } 22 | 23 | public void setId(Long id) { 24 | this.id = id; 25 | } 26 | 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | public void setName(String name) { 32 | this.name = name; 33 | } 34 | 35 | public Gender getGender() { 36 | return gender; 37 | } 38 | 39 | public void setGender(Gender gender) { 40 | this.gender = gender; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/services/versioning/model/PersonCurrent.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.versioning.model; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | 5 | @Schema(name = "Person") 6 | public class PersonCurrent extends Person { 7 | 8 | private int age; 9 | 10 | public PersonCurrent() { 11 | 12 | } 13 | 14 | public PersonCurrent(Long id, String name, Gender gender, int age) { 15 | super(id, name, gender); 16 | this.age = age; 17 | } 18 | 19 | public int getAge() { 20 | return age; 21 | } 22 | 23 | public void setAge(int age) { 24 | this.age = age; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/services/versioning/model/PersonOld.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.versioning.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | 6 | import java.time.LocalDate; 7 | 8 | @Schema(name = "Person") 9 | public class PersonOld extends Person { 10 | 11 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") 12 | private LocalDate birthDate; 13 | 14 | public PersonOld() { 15 | 16 | } 17 | 18 | public PersonOld(Long id, String name, Gender gender, LocalDate birthDate) { 19 | super(id, name, gender); 20 | this.birthDate = birthDate; 21 | } 22 | 23 | public LocalDate getBirthDate() { 24 | return birthDate; 25 | } 26 | 27 | public void setBirthDate(LocalDate birthDate) { 28 | this.birthDate = birthDate; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/pl/piomin/services/versioning/repository/PersonRepository.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.versioning.repository; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Optional; 6 | 7 | import pl.piomin.services.versioning.model.Person; 8 | 9 | public class PersonRepository { 10 | 11 | private List persons = new ArrayList<>(); 12 | 13 | public Person add(Person person) { 14 | person.setId((long) (persons.size()+1)); 15 | persons.add(person); 16 | return person; 17 | } 18 | 19 | public Person update(Person person) { 20 | persons.set(person.getId().intValue() - 1, person); 21 | return person; 22 | } 23 | 24 | public Person update(Long id, Person person) { 25 | persons.set(id.intValue() - 1, person); 26 | return person; 27 | } 28 | 29 | public Person findById(Long id) { 30 | Optional person = persons.stream().filter(a -> a.getId().equals(id)).findFirst(); 31 | if (person.isPresent()) 32 | return person.get(); 33 | else 34 | return null; 35 | } 36 | 37 | public void delete(Long id) { 38 | persons.remove(id.intValue()); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8090 -------------------------------------------------------------------------------- /src/test/java/pl/piomin/services/versioning/PersonControllerTests.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.versioning; 2 | 3 | import org.instancio.Instancio; 4 | import org.junit.jupiter.api.MethodOrderer; 5 | import org.junit.jupiter.api.Order; 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.api.TestMethodOrder; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.boot.test.web.client.TestRestTemplate; 11 | import pl.piomin.services.versioning.model.PersonCurrent; 12 | import pl.piomin.services.versioning.model.PersonOld; 13 | 14 | import static org.junit.jupiter.api.Assertions.*; 15 | 16 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 17 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 18 | public class PersonControllerTests { 19 | 20 | @Autowired 21 | TestRestTemplate restTemplate; 22 | 23 | @Test 24 | @Order(1) 25 | void addV0() { 26 | PersonOld p = restTemplate.postForObject("/person/v1.1", Instancio.create(PersonOld.class), PersonOld.class); 27 | assertNotNull(p); 28 | assertNotNull(p.getId()); 29 | } 30 | 31 | @Test 32 | @Order(2) 33 | void addV2() { 34 | PersonCurrent p = restTemplate.postForObject("/person/v1.2", Instancio.create(PersonCurrent.class), PersonCurrent.class); 35 | assertNotNull(p); 36 | assertNotNull(p.getId()); 37 | assertTrue(p.getAge() > 0); 38 | } 39 | 40 | @Test 41 | @Order(3) 42 | void findByIdV0() { 43 | PersonOld p = restTemplate.getForObject("/person/v1.0/{id}", PersonOld.class, 1); 44 | assertNotNull(p); 45 | assertNotNull(p.getId()); 46 | } 47 | 48 | @Test 49 | @Order(3) 50 | void findByIdV2() { 51 | PersonCurrent p = restTemplate.getForObject("/person/v1.2/{id}", PersonCurrent.class, 2); 52 | assertNotNull(p); 53 | assertNotNull(p.getId()); 54 | assertTrue(p.getAge() > 0); 55 | } 56 | 57 | @Test 58 | @Order(3) 59 | void findByIdV2ToV1Compability() { 60 | PersonCurrent p = restTemplate.getForObject("/person/v1.2/{id}", PersonCurrent.class, 1); 61 | assertNotNull(p); 62 | assertNotNull(p.getId()); 63 | assertTrue(p.getAge() > 0); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/pl/piomin/services/versioning/PersonControllerWithHeadersTests.java: -------------------------------------------------------------------------------- 1 | package pl.piomin.services.versioning; 2 | 3 | import org.instancio.Instancio; 4 | import org.junit.jupiter.api.MethodOrderer; 5 | import org.junit.jupiter.api.Order; 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.api.TestMethodOrder; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.boot.test.web.client.TestRestTemplate; 11 | import org.springframework.http.HttpEntity; 12 | import org.springframework.http.HttpHeaders; 13 | import org.springframework.http.HttpMethod; 14 | import org.springframework.http.ResponseEntity; 15 | import pl.piomin.services.versioning.model.PersonCurrent; 16 | import pl.piomin.services.versioning.model.PersonOld; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertNotNull; 19 | import static org.junit.jupiter.api.Assertions.assertTrue; 20 | 21 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 22 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 23 | public class PersonControllerWithHeadersTests { 24 | 25 | @Autowired 26 | TestRestTemplate restTemplate; 27 | 28 | @Test 29 | @Order(1) 30 | void addV0() { 31 | HttpHeaders headers = new HttpHeaders(); 32 | headers.add("X-VERSION", "v1.0"); 33 | HttpEntity httpEntity = new HttpEntity<>(Instancio.create(PersonOld.class), headers); 34 | ResponseEntity p = restTemplate.exchange("/persons-via-headers", HttpMethod.POST, httpEntity, PersonOld.class); 35 | assertTrue(p.getStatusCode().is2xxSuccessful()); 36 | assertNotNull(p.getBody()); 37 | assertNotNull(p.getBody().getId()); 38 | } 39 | 40 | @Test 41 | @Order(2) 42 | void addV2() { 43 | HttpHeaders headers = new HttpHeaders(); 44 | headers.add("X-VERSION", "v1.2"); 45 | HttpEntity httpEntity = new HttpEntity<>(Instancio.create(PersonCurrent.class), headers); 46 | ResponseEntity p = restTemplate.exchange("/persons-via-headers", HttpMethod.POST, httpEntity, PersonCurrent.class); 47 | assertTrue(p.getStatusCode().is2xxSuccessful()); 48 | assertNotNull(p.getBody()); 49 | assertNotNull(p.getBody().getId()); 50 | assertTrue(p.getBody().getAge() > 0); 51 | } 52 | 53 | @Test 54 | @Order(3) 55 | void findByIdV0() { 56 | HttpHeaders headers = new HttpHeaders(); 57 | headers.add("X-VERSION", "v1.0"); 58 | HttpEntity httpEntity = new HttpEntity<>(headers); 59 | ResponseEntity p = restTemplate.exchange("/persons-via-headers/{id}", HttpMethod.GET, httpEntity, PersonOld.class, 1); 60 | assertTrue(p.getStatusCode().is2xxSuccessful()); 61 | assertNotNull(p.getBody()); 62 | assertNotNull(p.getBody().getId()); 63 | } 64 | 65 | @Test 66 | @Order(3) 67 | void findByIdV2() { 68 | HttpHeaders headers = new HttpHeaders(); 69 | headers.add("X-VERSION", "v1.2"); 70 | HttpEntity httpEntity = new HttpEntity<>(headers); 71 | ResponseEntity p = restTemplate.exchange("/persons-via-headers/{id}", HttpMethod.GET, httpEntity, PersonCurrent.class, 2); 72 | assertTrue(p.getStatusCode().is2xxSuccessful()); 73 | assertNotNull(p.getBody()); 74 | assertNotNull(p.getBody().getId()); 75 | assertTrue(p.getBody().getAge() > 0); 76 | } 77 | 78 | @Test 79 | @Order(3) 80 | void findByIdV2ToV1Compability() { 81 | HttpHeaders headers = new HttpHeaders(); 82 | headers.add("X-VERSION", "v1.2"); 83 | HttpEntity httpEntity = new HttpEntity<>(headers); 84 | ResponseEntity p = restTemplate.exchange("/persons-via-headers/{id}", HttpMethod.GET, httpEntity, PersonCurrent.class, 1); 85 | assertTrue(p.getStatusCode().is2xxSuccessful()); 86 | assertNotNull(p.getBody()); 87 | assertNotNull(p.getBody().getId()); 88 | assertTrue(p.getBody().getAge() > 0); 89 | } 90 | } 91 | --------------------------------------------------------------------------------