diff --git a/affordances/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java b/affordances/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java index 9583481..2bad50a 100644 --- a/affordances/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java +++ b/affordances/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java @@ -28,13 +28,14 @@ import org.springframework.stereotype.Component; class DatabaseLoader { /** - * Use Spring to inject a {@link EmployeeRepository} that can then load data. Since this will run - * only after the app is operational, the database will be up. + * Use Spring to inject a {@link EmployeeRepository} that can then load data. Since this will run only after the app + * is operational, the database will be up. * * @param repository */ @Bean CommandLineRunner init(EmployeeRepository repository) { + return args -> { repository.save(new Employee("Frodo", "Baggins", "ring bearer")); repository.save(new Employee("Bilbo", "Baggins", "burglar")); diff --git a/affordances/src/main/java/org/springframework/hateoas/examples/Employee.java b/affordances/src/main/java/org/springframework/hateoas/examples/Employee.java index 03b63ce..a046d5d 100644 --- a/affordances/src/main/java/org/springframework/hateoas/examples/Employee.java +++ b/affordances/src/main/java/org/springframework/hateoas/examples/Employee.java @@ -25,18 +25,14 @@ import javax.persistence.GeneratedValue; import javax.persistence.Id; /** - * Domain object representing a company employee. Project Lombok keeps actual code at a minimum. - * - * {@code @Data} - Generates getters, setters, toString, hash, and equals functions - * {@code @Entity} - JPA annotation to flag this class for DB persistence - * {@code @NoArgsConstructor} - Create a constructor with no args to support JPA + * Domain object representing a company employee. Project Lombok keeps actual code at a minimum. {@code @Data} - + * Generates getters, setters, toString, hash, and equals functions {@code @Entity} - JPA annotation to flag this class + * for DB persistence {@code @NoArgsConstructor} - Create a constructor with no args to support JPA * {@code @AllArgsConstructor} - Create a constructor with all args to support testing - * - * {@code @JsonIgnoreProperties(ignoreUnknow=true)} - * When converting JSON to Java, ignore any unrecognized attributes. This is critical for REST because it - * encourages adding new fields in later versions that won't break. It also allows things like _links to be - * ignore as well, meaning HAL documents can be fetched and later posted to the server without adjustment. - * + * {@code @JsonIgnoreProperties(ignoreUnknow=true)} When converting JSON to Java, ignore any unrecognized attributes. + * This is critical for REST because it encourages adding new fields in later versions that won't break. It also allows + * things like _links to be ignore as well, meaning HAL documents can be fetched and later posted to the server without + * adjustment. * * @author Greg Turnquist */ @@ -46,8 +42,7 @@ import javax.persistence.Id; @AllArgsConstructor class Employee { - @Id @GeneratedValue - private Long id; + @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String role; diff --git a/affordances/src/main/java/org/springframework/hateoas/examples/EmployeeController.java b/affordances/src/main/java/org/springframework/hateoas/examples/EmployeeController.java index 1391cf1..3ef75dc 100644 --- a/affordances/src/main/java/org/springframework/hateoas/examples/EmployeeController.java +++ b/affordances/src/main/java/org/springframework/hateoas/examples/EmployeeController.java @@ -23,10 +23,10 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.IanaLinkRelations; import org.springframework.hateoas.Link; -import org.springframework.hateoas.EntityModel; -import org.springframework.hateoas.CollectionModel; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -45,7 +45,6 @@ class EmployeeController { private final EmployeeRepository repository; EmployeeController(EmployeeRepository repository) { - this.repository = repository; } @@ -53,17 +52,17 @@ class EmployeeController { ResponseEntity>> findAll() { List> employeeResources = StreamSupport.stream(repository.findAll().spliterator(), false) - .map(employee -> new EntityModel<>(employee, - linkTo(methodOn(EmployeeController.class).findOne(employee.getId())).withSelfRel() - .andAffordance(afford(methodOn(EmployeeController.class).updateEmployee(null, employee.getId()))) - .andAffordance(afford(methodOn(EmployeeController.class).deleteEmployee(employee.getId()))), - linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees") - )) - .collect(Collectors.toList()); + .map(employee -> new EntityModel<>(employee, + linkTo(methodOn(EmployeeController.class).findOne(employee.getId())).withSelfRel() + .andAffordance(afford(methodOn(EmployeeController.class).updateEmployee(null, employee.getId()))) + .andAffordance(afford(methodOn(EmployeeController.class).deleteEmployee(employee.getId()))), + linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees"))) + .collect(Collectors.toList()); - return ResponseEntity.ok(new CollectionModel<>(employeeResources, - linkTo(methodOn(EmployeeController.class).findAll()).withSelfRel() - .andAffordance(afford(methodOn(EmployeeController.class).newEmployee(null))))); + return ResponseEntity.ok(new CollectionModel<>( // + employeeResources, // + linkTo(methodOn(EmployeeController.class).findAll()).withSelfRel() + .andAffordance(afford(methodOn(EmployeeController.class).newEmployee(null))))); } @PostMapping("/employees") @@ -72,35 +71,33 @@ class EmployeeController { Employee savedEmployee = repository.save(employee); return new EntityModel<>(savedEmployee, - linkTo(methodOn(EmployeeController.class).findOne(savedEmployee.getId())).withSelfRel() - .andAffordance(afford(methodOn(EmployeeController.class).updateEmployee(null, savedEmployee.getId()))) - .andAffordance(afford(methodOn(EmployeeController.class).deleteEmployee(savedEmployee.getId()))), - linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees") - ).getLink(IanaLinkRelations.SELF) - .map(Link::getHref) - .map(href -> { - try { - return new URI(href); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - }) - .map(uri -> ResponseEntity.noContent().location(uri).build()) - .orElse(ResponseEntity.badRequest().body("Unable to create " + employee)); + linkTo(methodOn(EmployeeController.class).findOne(savedEmployee.getId())).withSelfRel() + .andAffordance(afford(methodOn(EmployeeController.class).updateEmployee(null, savedEmployee.getId()))) + .andAffordance(afford(methodOn(EmployeeController.class).deleteEmployee(savedEmployee.getId()))), + linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees")).getLink(IanaLinkRelations.SELF) + .map(Link::getHref) // + .map(href -> { + try { + return new URI(href); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + }) // + .map(uri -> ResponseEntity.noContent().location(uri).build()) + .orElse(ResponseEntity.badRequest().body("Unable to create " + employee)); } @GetMapping("/employees/{id}") ResponseEntity> findOne(@PathVariable long id) { return repository.findById(id) - .map(employee -> new EntityModel<>(employee, - linkTo(methodOn(EmployeeController.class).findOne(employee.getId())).withSelfRel() - .andAffordance(afford(methodOn(EmployeeController.class).updateEmployee(null, employee.getId()))) - .andAffordance(afford(methodOn(EmployeeController.class).deleteEmployee(employee.getId()))), - linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees") - )) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + .map(employee -> new EntityModel<>(employee, + linkTo(methodOn(EmployeeController.class).findOne(employee.getId())).withSelfRel() + .andAffordance(afford(methodOn(EmployeeController.class).updateEmployee(null, employee.getId()))) + .andAffordance(afford(methodOn(EmployeeController.class).deleteEmployee(employee.getId()))), + linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees"))) + .map(ResponseEntity::ok) // + .orElse(ResponseEntity.notFound().build()); } @PutMapping("/employees/{id}") @@ -112,21 +109,19 @@ class EmployeeController { Employee updatedEmployee = repository.save(employeeToUpdate); return new EntityModel<>(updatedEmployee, - linkTo(methodOn(EmployeeController.class).findOne(updatedEmployee.getId())).withSelfRel() - .andAffordance(afford(methodOn(EmployeeController.class).updateEmployee(null, updatedEmployee.getId()))) - .andAffordance(afford(methodOn(EmployeeController.class).deleteEmployee(updatedEmployee.getId()))), - linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees") - ).getLink(IanaLinkRelations.SELF) - .map(Link::getHref) - .map(href -> { - try { - return new URI(href); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - }) - .map(uri -> ResponseEntity.noContent().location(uri).build()) - .orElse(ResponseEntity.badRequest().body("Unable to update " + employeeToUpdate)); + linkTo(methodOn(EmployeeController.class).findOne(updatedEmployee.getId())).withSelfRel() + .andAffordance(afford(methodOn(EmployeeController.class).updateEmployee(null, updatedEmployee.getId()))) + .andAffordance(afford(methodOn(EmployeeController.class).deleteEmployee(updatedEmployee.getId()))), + linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees")).getLink(IanaLinkRelations.SELF) + .map(Link::getHref).map(href -> { + try { + return new URI(href); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + }) // + .map(uri -> ResponseEntity.noContent().location(uri).build()) // + .orElse(ResponseEntity.badRequest().body("Unable to update " + employeeToUpdate)); } @DeleteMapping("/employees/{id}") diff --git a/affordances/src/test/java/org/springframework/hateoas/examples/EmployeeControllerTests.java b/affordances/src/test/java/org/springframework/hateoas/examples/EmployeeControllerTests.java index d34ba2a..a205c13 100644 --- a/affordances/src/test/java/org/springframework/hateoas/examples/EmployeeControllerTests.java +++ b/affordances/src/test/java/org/springframework/hateoas/examples/EmployeeControllerTests.java @@ -41,100 +41,96 @@ import org.springframework.test.web.servlet.MockMvc; */ @RunWith(SpringRunner.class) @WebMvcTest(EmployeeController.class) -@Import({HypermediaConfiguration.class}) +@Import({ HypermediaConfiguration.class }) public class EmployeeControllerTests { - @Autowired - private MockMvc mvc; + @Autowired private MockMvc mvc; - @MockBean - private EmployeeRepository repository; + @MockBean private EmployeeRepository repository; @Test public void getAllShouldFetchAHalFormsEmbeddedDocument() throws Exception { - given(repository.findAll()).willReturn( - Arrays.asList( - new Employee(1L, "Frodo", "Baggins", "ring bearer"), + given(repository.findAll()).willReturn(Arrays.asList( // + new Employee(1L, "Frodo", "Baggins", "ring bearer"), // new Employee(2L, "Bilbo", "Baggins", "burglar"))); - mvc.perform(get("/employees").accept(MediaTypes.HAL_FORMS_JSON_VALUE)) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaTypes.HAL_FORMS_JSON_VALUE + ";charset=UTF-8")) + mvc.perform(get("/employees").accept(MediaTypes.HAL_FORMS_JSON_VALUE)) // + .andDo(print()) // + .andExpect(status().isOk()) // + .andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaTypes.HAL_FORMS_JSON_VALUE + ";charset=UTF-8")) - .andExpect(jsonPath("$._embedded.employees[0].id", is(1))) - .andExpect(jsonPath("$._embedded.employees[0].firstName", is("Frodo"))) - .andExpect(jsonPath("$._embedded.employees[0].lastName", is("Baggins"))) - .andExpect(jsonPath("$._embedded.employees[0].role", is("ring bearer"))) - .andExpect(jsonPath("$._embedded.employees[0]._templates.default.method", is("put"))) - .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[0].name", is("firstName"))) - .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[0].required", is(true))) - .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[1].name", is("id"))) - .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[1].required", is(true))) - .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[2].name", is("lastName"))) - .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[2].required", is(true))) - .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[3].name", is("role"))) - .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[3].required", is(true))) - .andExpect(jsonPath("$._embedded.employees[0]._links.self.href", is("http://localhost/employees/1"))) - .andExpect(jsonPath("$._embedded.employees[0]._links.employees.href", is("http://localhost/employees"))) + .andExpect(jsonPath("$._embedded.employees[0].id", is(1))) + .andExpect(jsonPath("$._embedded.employees[0].firstName", is("Frodo"))) + .andExpect(jsonPath("$._embedded.employees[0].lastName", is("Baggins"))) + .andExpect(jsonPath("$._embedded.employees[0].role", is("ring bearer"))) + .andExpect(jsonPath("$._embedded.employees[0]._templates.default.method", is("put"))) + .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[0].name", is("firstName"))) + .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[0].required", is(true))) + .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[1].name", is("id"))) + .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[1].required", is(true))) + .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[2].name", is("lastName"))) + .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[2].required", is(true))) + .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[3].name", is("role"))) + .andExpect(jsonPath("$._embedded.employees[0]._templates.default.properties[3].required", is(true))) + .andExpect(jsonPath("$._embedded.employees[0]._links.self.href", is("http://localhost/employees/1"))) + .andExpect(jsonPath("$._embedded.employees[0]._links.employees.href", is("http://localhost/employees"))) - .andExpect(jsonPath("$._embedded.employees[1].id", is(2))) - .andExpect(jsonPath("$._embedded.employees[1].firstName", is("Bilbo"))) - .andExpect(jsonPath("$._embedded.employees[1].lastName", is("Baggins"))) - .andExpect(jsonPath("$._embedded.employees[1].role", is("burglar"))) - .andExpect(jsonPath("$._embedded.employees[1]._templates.default.method", is("put"))) - .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[0].name", is("firstName"))) - .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[0].required", is(true))) - .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[1].name", is("id"))) - .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[1].required", is(true))) - .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[2].name", is("lastName"))) - .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[2].required", is(true))) - .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[3].name", is("role"))) - .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[3].required", is(true))) - .andExpect(jsonPath("$._embedded.employees[1]._links.self.href", is("http://localhost/employees/2"))) - .andExpect(jsonPath("$._embedded.employees[1]._links.employees.href", is("http://localhost/employees"))) + .andExpect(jsonPath("$._embedded.employees[1].id", is(2))) + .andExpect(jsonPath("$._embedded.employees[1].firstName", is("Bilbo"))) + .andExpect(jsonPath("$._embedded.employees[1].lastName", is("Baggins"))) + .andExpect(jsonPath("$._embedded.employees[1].role", is("burglar"))) + .andExpect(jsonPath("$._embedded.employees[1]._templates.default.method", is("put"))) + .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[0].name", is("firstName"))) + .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[0].required", is(true))) + .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[1].name", is("id"))) + .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[1].required", is(true))) + .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[2].name", is("lastName"))) + .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[2].required", is(true))) + .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[3].name", is("role"))) + .andExpect(jsonPath("$._embedded.employees[1]._templates.default.properties[3].required", is(true))) + .andExpect(jsonPath("$._embedded.employees[1]._links.self.href", is("http://localhost/employees/2"))) + .andExpect(jsonPath("$._embedded.employees[1]._links.employees.href", is("http://localhost/employees"))) - .andExpect(jsonPath("$._templates.default.method", is("post"))) - .andExpect(jsonPath("$._templates.default.properties[0].name", is("firstName"))) - .andExpect(jsonPath("$._templates.default.properties[0].required", is(true))) - .andExpect(jsonPath("$._templates.default.properties[1].name", is("id"))) - .andExpect(jsonPath("$._templates.default.properties[1].required", is(true))) - .andExpect(jsonPath("$._templates.default.properties[2].name", is("lastName"))) - .andExpect(jsonPath("$._templates.default.properties[2].required", is(true))) - .andExpect(jsonPath("$._templates.default.properties[3].name", is("role"))) - .andExpect(jsonPath("$._templates.default.properties[3].required", is(true))) + .andExpect(jsonPath("$._templates.default.method", is("post"))) + .andExpect(jsonPath("$._templates.default.properties[0].name", is("firstName"))) + .andExpect(jsonPath("$._templates.default.properties[0].required", is(true))) + .andExpect(jsonPath("$._templates.default.properties[1].name", is("id"))) + .andExpect(jsonPath("$._templates.default.properties[1].required", is(true))) + .andExpect(jsonPath("$._templates.default.properties[2].name", is("lastName"))) + .andExpect(jsonPath("$._templates.default.properties[2].required", is(true))) + .andExpect(jsonPath("$._templates.default.properties[3].name", is("role"))) + .andExpect(jsonPath("$._templates.default.properties[3].required", is(true))) - .andExpect(jsonPath("$._links.self.href", is("http://localhost/employees"))); + .andExpect(jsonPath("$._links.self.href", is("http://localhost/employees"))); } @Test public void getOneShouldFetchASingleHalFormsDocument() throws Exception { - given(repository.findById(any())).willReturn( - Optional.of(new Employee(1L, "Frodo", "Baggins", "ring bearer"))); + given(repository.findById(any())).willReturn(Optional.of(new Employee(1L, "Frodo", "Baggins", "ring bearer"))); - mvc.perform(get("/employees/1").accept(MediaTypes.HAL_FORMS_JSON_VALUE)) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaTypes.HAL_FORMS_JSON_VALUE + ";charset=UTF-8")) + mvc.perform(get("/employees/1").accept(MediaTypes.HAL_FORMS_JSON_VALUE)) // + .andDo(print()) // + .andExpect(status().isOk()) // + .andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaTypes.HAL_FORMS_JSON_VALUE + ";charset=UTF-8")) - .andExpect(jsonPath("$.id", is(1))) - .andExpect(jsonPath("$.firstName", is("Frodo"))) - .andExpect(jsonPath("$.lastName", is("Baggins"))) - .andExpect(jsonPath("$.role", is("ring bearer"))) + .andExpect(jsonPath("$.id", is(1))) // + .andExpect(jsonPath("$.firstName", is("Frodo"))) // + .andExpect(jsonPath("$.lastName", is("Baggins"))) // + .andExpect(jsonPath("$.role", is("ring bearer"))) - .andExpect(jsonPath("$._templates.default.method", is("put"))) - .andExpect(jsonPath("$._templates.default.properties[0].name", is("firstName"))) - .andExpect(jsonPath("$._templates.default.properties[0].required", is(true))) - .andExpect(jsonPath("$._templates.default.properties[1].name", is("id"))) - .andExpect(jsonPath("$._templates.default.properties[1].required", is(true))) - .andExpect(jsonPath("$._templates.default.properties[2].name", is("lastName"))) - .andExpect(jsonPath("$._templates.default.properties[2].required", is(true))) - .andExpect(jsonPath("$._templates.default.properties[3].name", is("role"))) - .andExpect(jsonPath("$._templates.default.properties[3].required", is(true))) - - .andExpect(jsonPath("$._links.self.href", is("http://localhost/employees/1"))) - .andExpect(jsonPath("$._links.employees.href", is("http://localhost/employees"))); + .andExpect(jsonPath("$._templates.default.method", is("put"))) + .andExpect(jsonPath("$._templates.default.properties[0].name", is("firstName"))) + .andExpect(jsonPath("$._templates.default.properties[0].required", is(true))) + .andExpect(jsonPath("$._templates.default.properties[1].name", is("id"))) + .andExpect(jsonPath("$._templates.default.properties[1].required", is(true))) + .andExpect(jsonPath("$._templates.default.properties[2].name", is("lastName"))) + .andExpect(jsonPath("$._templates.default.properties[2].required", is(true))) + .andExpect(jsonPath("$._templates.default.properties[3].name", is("role"))) + .andExpect(jsonPath("$._templates.default.properties[3].required", is(true))) + + .andExpect(jsonPath("$._links.self.href", is("http://localhost/employees/1"))) + .andExpect(jsonPath("$._links.employees.href", is("http://localhost/employees"))); } } diff --git a/api-evolution/new-client/src/main/java/org/springframework/hateoas/examples/Employee.java b/api-evolution/new-client/src/main/java/org/springframework/hateoas/examples/Employee.java index bdbf505..2750189 100644 --- a/api-evolution/new-client/src/main/java/org/springframework/hateoas/examples/Employee.java +++ b/api-evolution/new-client/src/main/java/org/springframework/hateoas/examples/Employee.java @@ -19,8 +19,8 @@ import lombok.Data; import lombok.NoArgsConstructor; /** - * An updated domain object on the client side. It doesn't need all the backward compatible bits that the new - * server needs (unless this becomes a service of its own). + * An updated domain object on the client side. It doesn't need all the backward compatible bits that the new server + * needs (unless this becomes a service of its own). * * @author Greg Turnquist */ diff --git a/api-evolution/new-client/src/main/java/org/springframework/hateoas/examples/HomeController.java b/api-evolution/new-client/src/main/java/org/springframework/hateoas/examples/HomeController.java index e3febd0..7fb1ce0 100644 --- a/api-evolution/new-client/src/main/java/org/springframework/hateoas/examples/HomeController.java +++ b/api-evolution/new-client/src/main/java/org/springframework/hateoas/examples/HomeController.java @@ -48,11 +48,9 @@ public class HomeController { } /** - * Get a listing of ALL {@link Employee}s by querying the remote services' root URI, and then - * "hopping" to the {@literal employees} rel. - * - * NOTE: Also create a form-backed {@link Employee} object to allow creating a new entry with - * the Thymeleaf template. + * Get a listing of ALL {@link Employee}s by querying the remote services' root URI, and then "hopping" to the + * {@literal employees} rel. NOTE: Also create a form-backed {@link Employee} object to allow creating a new entry + * with the Thymeleaf template. * * @param model * @return @@ -62,9 +60,10 @@ public class HomeController { public String index(Model model) throws URISyntaxException { Traverson client = new Traverson(new URI(REMOTE_SERVICE_ROOT_URI), MediaTypes.HAL_JSON); - CollectionModel> employees = client - .follow("employees") - .toObject(new CollectionModelType>(){}); + + CollectionModel> employees = client // + .follow("employees") // + .toObject(new CollectionModelType>() {}); model.addAttribute("employee", new Employee()); model.addAttribute("employees", employees); @@ -73,11 +72,9 @@ public class HomeController { } /** - * Instead of putting the creation link from the remote service in the template (a security concern), - * have a local route for {@literal POST} requests. Gather up the information, and form a remote call, - * using {@link Traverson} to fetch the {@literal employees} {@link Link}. - * - * Once a new employee is created, redirect back to the root URL. + * Instead of putting the creation link from the remote service in the template (a security concern), have a local + * route for {@literal POST} requests. Gather up the information, and form a remote call, using {@link Traverson} to + * fetch the {@literal employees} {@link Link}. Once a new employee is created, redirect back to the root URL. * * @param employee * @return @@ -87,9 +84,7 @@ public class HomeController { public String newEmployee(@ModelAttribute Employee employee) throws URISyntaxException { Traverson client = new Traverson(new URI(REMOTE_SERVICE_ROOT_URI), MediaTypes.HAL_JSON); - Link employeesLink = client - .follow("employees") - .asLink(); + Link employeesLink = client.follow("employees").asLink(); this.rest.postForEntity(employeesLink.expand().getHref(), employee, Employee.class); diff --git a/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/Employee.java b/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/Employee.java index 4f2b1cc..aab7fd7 100644 --- a/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/Employee.java +++ b/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/Employee.java @@ -28,8 +28,8 @@ import javax.persistence.Id; import org.springframework.util.StringUtils; /** - * An updated domain object where {@literal name} has been replaced by {@literal firstName} and {@literal} lastName. - * To easy migration, we need to support the old {@literal name} field with a getter and a setter. + * An updated domain object where {@literal name} has been replaced by {@literal firstName} and {@literal} lastName. To + * easy migration, we need to support the old {@literal name} field with a getter and a setter. * * @author Greg Turnquist */ @@ -38,8 +38,7 @@ import org.springframework.util.StringUtils; @Entity class Employee { - @Id @GeneratedValue - private Long id; + @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String role; diff --git a/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/EmployeeController.java b/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/EmployeeController.java index a455934..a3b329d 100644 --- a/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/EmployeeController.java +++ b/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/EmployeeController.java @@ -17,9 +17,9 @@ package org.springframework.hateoas.examples; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; +import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.RepresentationModel; -import org.springframework.hateoas.CollectionModel; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -46,10 +46,10 @@ class EmployeeController { public RepresentationModel root() { RepresentationModel rootResource = new RepresentationModel(); - - rootResource.add( - linkTo(methodOn(EmployeeController.class).root()).withSelfRel(), - linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees")); + + rootResource.add( // + linkTo(methodOn(EmployeeController.class).root()).withSelfRel(), // + linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees")); return rootResource; } @@ -64,20 +64,19 @@ class EmployeeController { Employee savedEmployee = repository.save(employee); - return savedEmployee.getId() - .map(id -> ResponseEntity - .created(linkTo(methodOn(EmployeeController.class).findOne(id)).toUri()) - .body(assembler.toModel(savedEmployee))) - .orElse(ResponseEntity.notFound().build()); + return savedEmployee.getId() // + .map(id -> ResponseEntity.created( // + linkTo(methodOn(EmployeeController.class).findOne(id)).toUri()).body(assembler.toModel(savedEmployee))) + .orElse(ResponseEntity.notFound().build()); } @GetMapping("/employees/{id}") public ResponseEntity> findOne(@PathVariable Long id) { - return repository.findById(id) - .map(assembler::toModel) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + return repository.findById(id) // + .map(assembler::toModel) // + .map(ResponseEntity::ok) // + .orElse(ResponseEntity.notFound().build()); } } diff --git a/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java b/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java index 688dfe7..5d0d119 100644 --- a/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java +++ b/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java @@ -1,4 +1,4 @@ -package org.springframework.hateoas.examples;/* +/* * Copyright 2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,6 +13,7 @@ package org.springframework.hateoas.examples;/* * See the License for the specific language governing permissions and * limitations under the License. */ +package org.springframework.hateoas.examples; import org.springframework.data.repository.CrudRepository; diff --git a/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/InitDatabase.java b/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/InitDatabase.java index 7bebc44..a99c427 100644 --- a/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/InitDatabase.java +++ b/api-evolution/new-server/src/main/java/org/springframework/hateoas/examples/InitDatabase.java @@ -33,6 +33,7 @@ class InitDatabase { @Bean CommandLineRunner loadEmployees() { + return args -> { repository.save(new Employee("Frodo", "Baggins", "ring bearer")); repository.save(new Employee("Bilbo", "Baggins", "burglar")); diff --git a/api-evolution/original-client/src/main/java/org/springframework/hateoas/examples/HomeController.java b/api-evolution/original-client/src/main/java/org/springframework/hateoas/examples/HomeController.java index e3febd0..7b51700 100644 --- a/api-evolution/original-client/src/main/java/org/springframework/hateoas/examples/HomeController.java +++ b/api-evolution/original-client/src/main/java/org/springframework/hateoas/examples/HomeController.java @@ -48,11 +48,9 @@ public class HomeController { } /** - * Get a listing of ALL {@link Employee}s by querying the remote services' root URI, and then - * "hopping" to the {@literal employees} rel. - * - * NOTE: Also create a form-backed {@link Employee} object to allow creating a new entry with - * the Thymeleaf template. + * Get a listing of ALL {@link Employee}s by querying the remote services' root URI, and then "hopping" to the + * {@literal employees} rel. NOTE: Also create a form-backed {@link Employee} object to allow creating a new entry + * with the Thymeleaf template. * * @param model * @return @@ -62,9 +60,9 @@ public class HomeController { public String index(Model model) throws URISyntaxException { Traverson client = new Traverson(new URI(REMOTE_SERVICE_ROOT_URI), MediaTypes.HAL_JSON); - CollectionModel> employees = client - .follow("employees") - .toObject(new CollectionModelType>(){}); + CollectionModel> employees = client // + .follow("employees") // + .toObject(new CollectionModelType>() {}); model.addAttribute("employee", new Employee()); model.addAttribute("employees", employees); @@ -73,11 +71,9 @@ public class HomeController { } /** - * Instead of putting the creation link from the remote service in the template (a security concern), - * have a local route for {@literal POST} requests. Gather up the information, and form a remote call, - * using {@link Traverson} to fetch the {@literal employees} {@link Link}. - * - * Once a new employee is created, redirect back to the root URL. + * Instead of putting the creation link from the remote service in the template (a security concern), have a local + * route for {@literal POST} requests. Gather up the information, and form a remote call, using {@link Traverson} to + * fetch the {@literal employees} {@link Link}. Once a new employee is created, redirect back to the root URL. * * @param employee * @return @@ -87,9 +83,9 @@ public class HomeController { public String newEmployee(@ModelAttribute Employee employee) throws URISyntaxException { Traverson client = new Traverson(new URI(REMOTE_SERVICE_ROOT_URI), MediaTypes.HAL_JSON); - Link employeesLink = client - .follow("employees") - .asLink(); + Link employeesLink = client // + .follow("employees") // + .asLink(); this.rest.postForEntity(employeesLink.expand().getHref(), employee, Employee.class); diff --git a/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/Employee.java b/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/Employee.java index be85585..bf6066f 100644 --- a/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/Employee.java +++ b/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/Employee.java @@ -33,13 +33,12 @@ import javax.persistence.Id; @Entity class Employee { - @Id @GeneratedValue - private Long id; + @Id @GeneratedValue private Long id; private String name; private String role; Employee(String name, String role) { - + this.name = name; this.role = role; } diff --git a/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/EmployeeController.java b/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/EmployeeController.java index 0ba9f0c..fe9a733 100644 --- a/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/EmployeeController.java +++ b/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/EmployeeController.java @@ -17,9 +17,9 @@ package org.springframework.hateoas.examples; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; +import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.RepresentationModel; -import org.springframework.hateoas.CollectionModel; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -47,9 +47,9 @@ class EmployeeController { RepresentationModel rootResource = new RepresentationModel(); - rootResource.add( - linkTo(methodOn(EmployeeController.class).root()).withSelfRel(), - linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees")); + rootResource.add( // + linkTo(methodOn(EmployeeController.class).root()).withSelfRel(), // + linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees")); return rootResource; } @@ -64,18 +64,18 @@ class EmployeeController { Employee savedEmployee = repository.save(employee); - return ResponseEntity - .created(savedEmployee.getId() - .map(id -> linkTo(methodOn(EmployeeController.class).findOne(id)).toUri()) - .orElseThrow(() -> new RuntimeException("Failed to create for some reason"))) - .body(assembler.toModel(savedEmployee)); + return ResponseEntity // + .created(savedEmployee.getId() // + .map(id -> linkTo(methodOn(EmployeeController.class).findOne(id)).toUri()) // + .orElseThrow(() -> new RuntimeException("Failed to create for some reason"))) // + .body(assembler.toModel(savedEmployee)); } @GetMapping("/employees/{id}") public EntityModel findOne(@PathVariable Long id) { - return repository.findById(id) - .map(assembler::toModel) - .orElseThrow(() -> new RuntimeException("No employee '" + id + "' found")); + return repository.findById(id) // + .map(assembler::toModel) // + .orElseThrow(() -> new RuntimeException("No employee '" + id + "' found")); } } diff --git a/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java b/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java index 688dfe7..5d0d119 100644 --- a/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java +++ b/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java @@ -1,4 +1,4 @@ -package org.springframework.hateoas.examples;/* +/* * Copyright 2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,6 +13,7 @@ package org.springframework.hateoas.examples;/* * See the License for the specific language governing permissions and * limitations under the License. */ +package org.springframework.hateoas.examples; import org.springframework.data.repository.CrudRepository; diff --git a/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/InitDatabase.java b/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/InitDatabase.java index f9ecb39..1583e2b 100644 --- a/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/InitDatabase.java +++ b/api-evolution/original-server/src/main/java/org/springframework/hateoas/examples/InitDatabase.java @@ -33,6 +33,7 @@ class InitDatabase { @Bean CommandLineRunner loadEmployees() { + return args -> { repository.save(new Employee("Frodo", "ring bearer")); repository.save(new Employee("Bilbo", "burglar")); diff --git a/basics/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java b/basics/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java index 9583481..2bad50a 100644 --- a/basics/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java +++ b/basics/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java @@ -28,13 +28,14 @@ import org.springframework.stereotype.Component; class DatabaseLoader { /** - * Use Spring to inject a {@link EmployeeRepository} that can then load data. Since this will run - * only after the app is operational, the database will be up. + * Use Spring to inject a {@link EmployeeRepository} that can then load data. Since this will run only after the app + * is operational, the database will be up. * * @param repository */ @Bean CommandLineRunner init(EmployeeRepository repository) { + return args -> { repository.save(new Employee("Frodo", "Baggins", "ring bearer")); repository.save(new Employee("Bilbo", "Baggins", "burglar")); diff --git a/basics/src/main/java/org/springframework/hateoas/examples/Employee.java b/basics/src/main/java/org/springframework/hateoas/examples/Employee.java index ab9091e..fab35a7 100644 --- a/basics/src/main/java/org/springframework/hateoas/examples/Employee.java +++ b/basics/src/main/java/org/springframework/hateoas/examples/Employee.java @@ -29,18 +29,14 @@ import javax.persistence.Id; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; /** - * Domain object representing a company employee. Project Lombok keeps actual code at a minimum. - * - * {@code @Data} - Generates getters, setters, toString, hash, and equals functions - * {@code @Entity} - JPA annotation to flag this class for DB persistence - * {@code @NoArgsConstructor} - Create a constructor with no args to support JPA + * Domain object representing a company employee. Project Lombok keeps actual code at a minimum. {@code @Data} - + * Generates getters, setters, toString, hash, and equals functions {@code @Entity} - JPA annotation to flag this class + * for DB persistence {@code @NoArgsConstructor} - Create a constructor with no args to support JPA * {@code @AllArgsConstructor} - Create a constructor with all args to support testing - * - * {@code @JsonIgnoreProperties(ignoreUnknow=true)} - * When converting JSON to Java, ignore any unrecognized attributes. This is critical for REST because it - * encourages adding new fields in later versions that won't break. It also allows things like _links to be - * ignore as well, meaning HAL documents can be fetched and later posted to the server without adjustment. - * + * {@code @JsonIgnoreProperties(ignoreUnknow=true)} When converting JSON to Java, ignore any unrecognized attributes. + * This is critical for REST because it encourages adding new fields in later versions that won't break. It also allows + * things like _links to be ignore as well, meaning HAL documents can be fetched and later posted to the server without + * adjustment. * * @author Greg Turnquist */ @@ -51,13 +47,9 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) class Employee { - @Id @GeneratedValue - private Long id; - + @Id @GeneratedValue private Long id; private String firstName; - private String lastName; - private String role; /** @@ -79,14 +71,10 @@ class Employee { } /** - * This method will create another piece of data in the REST resource representation. These types - * of methods are key in supporting backward compatibility. - * - * By NOT removing old fields, and instead replacing them with methods like this, an API can evolve - * without breaking old clients. - * - * Because of {@code @JsonIgnoreProperties} settings above, this attribute will be ignore if sent back - * to the server, allowing API evolution. + * This method will create another piece of data in the REST resource representation. These types of methods are key + * in supporting backward compatibility. By NOT removing old fields, and instead replacing them with methods like + * this, an API can evolve without breaking old clients. Because of {@code @JsonIgnoreProperties} settings above, this + * attribute will be ignore if sent back to the server, allowing API evolution. * * @return */ diff --git a/basics/src/main/java/org/springframework/hateoas/examples/EmployeeController.java b/basics/src/main/java/org/springframework/hateoas/examples/EmployeeController.java index cb02a3e..da061c9 100644 --- a/basics/src/main/java/org/springframework/hateoas/examples/EmployeeController.java +++ b/basics/src/main/java/org/springframework/hateoas/examples/EmployeeController.java @@ -15,18 +15,17 @@ */ package org.springframework.hateoas.examples; -import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; /** - * Spring Web {@link RestController} used to generate a REST API. - * - * Works by injecting an {@link EmployeeRepository} and an {@link EmployeeRepresentationModelAssembler} in the constructor, both - * of which are used to retrieve data from the database, and assemble a REST resource. + * Spring Web {@link RestController} used to generate a REST API. Works by injecting an {@link EmployeeRepository} and + * an {@link EmployeeRepresentationModelAssembler} in the constructor, both of which are used to retrieve data from the + * database, and assemble a REST resource. * * @author Greg Turnquist */ @@ -37,38 +36,38 @@ class EmployeeController { private final EmployeeRepresentationModelAssembler assembler; EmployeeController(EmployeeRepository repository, EmployeeRepresentationModelAssembler assembler) { - + this.repository = repository; this.assembler = assembler; } /** * Look up all employees, and transform them into a REST collection resource using - * {@link EmployeeRepresentationModelAssembler#toCollectionModel(Iterable)}. Then return them through - * Spring Web's {@link ResponseEntity} fluent API. + * {@link EmployeeRepresentationModelAssembler#toCollectionModel(Iterable)}. Then return them through Spring Web's + * {@link ResponseEntity} fluent API. */ @GetMapping("/employees") public ResponseEntity>> findAll() { - - return ResponseEntity.ok( - this.assembler.toCollectionModel(this.repository.findAll())); + + return ResponseEntity.ok( // + this.assembler.toCollectionModel(this.repository.findAll())); } /** * Look up a single {@link Employee} and transform it into a REST resource using - * {@link EmployeeRepresentationModelAssembler#toModel(Object)}. Then return it through - * Spring Web's {@link ResponseEntity} fluent API. + * {@link EmployeeRepresentationModelAssembler#toModel(Object)}. Then return it through Spring Web's + * {@link ResponseEntity} fluent API. * * @param id */ @GetMapping("/employees/{id}") public ResponseEntity> findOne(@PathVariable long id) { - return this.repository.findById(id) - .map(this.assembler::toModel) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + return this.repository.findById(id) // + .map(this.assembler::toModel) // + .map(ResponseEntity::ok) // + .orElse(ResponseEntity.notFound().build()); } } diff --git a/basics/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java b/basics/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java index 61ec4c8..be1e22a 100644 --- a/basics/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java +++ b/basics/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java @@ -22,5 +22,4 @@ import org.springframework.data.repository.CrudRepository; * * @author Greg Turnquist */ -interface EmployeeRepository extends CrudRepository { -} +interface EmployeeRepository extends CrudRepository {} diff --git a/basics/src/main/java/org/springframework/hateoas/examples/EmployeeRepresentationModelAssembler.java b/basics/src/main/java/org/springframework/hateoas/examples/EmployeeRepresentationModelAssembler.java index b943dce..f38c085 100644 --- a/basics/src/main/java/org/springframework/hateoas/examples/EmployeeRepresentationModelAssembler.java +++ b/basics/src/main/java/org/springframework/hateoas/examples/EmployeeRepresentationModelAssembler.java @@ -26,8 +26,8 @@ class EmployeeRepresentationModelAssembler extends SimpleIdentifiableRepresentat /** * Link the {@link Employee} domain type to the {@link EmployeeController} using this - * {@link SimpleIdentifiableRepresentationModelAssembler} in order to generate both {@link org.springframework.hateoas.Resource} - * and {@link org.springframework.hateoas.CollectionModel}. + * {@link SimpleIdentifiableRepresentationModelAssembler} in order to generate both + * {@link org.springframework.hateoas.Resource} and {@link org.springframework.hateoas.CollectionModel}. */ EmployeeRepresentationModelAssembler() { super(EmployeeController.class); diff --git a/basics/src/test/java/org/springframework/hateoas/examples/EmployeeControllerTests.java b/basics/src/test/java/org/springframework/hateoas/examples/EmployeeControllerTests.java index 9465461..1cbfafd 100644 --- a/basics/src/test/java/org/springframework/hateoas/examples/EmployeeControllerTests.java +++ b/basics/src/test/java/org/springframework/hateoas/examples/EmployeeControllerTests.java @@ -25,7 +25,6 @@ import java.util.Arrays; import org.junit.Test; import org.junit.runner.RunWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -42,41 +41,39 @@ import org.springframework.test.web.servlet.MockMvc; */ @RunWith(SpringRunner.class) @WebMvcTest(EmployeeController.class) -@Import({EmployeeRepresentationModelAssembler.class}) +@Import({ EmployeeRepresentationModelAssembler.class }) public class EmployeeControllerTests { - @Autowired - private MockMvc mvc; + @Autowired private MockMvc mvc; - @MockBean - private EmployeeRepository repository; + @MockBean private EmployeeRepository repository; @Test public void getShouldFetchAHalDocument() throws Exception { - given(repository.findAll()).willReturn( - Arrays.asList( - new Employee(1L,"Frodo", "Baggins", "ring bearer"), - new Employee(2L,"Bilbo", "Baggins", "burglar"))); + given(repository.findAll()).willReturn( // + Arrays.asList( // + new Employee(1L, "Frodo", "Baggins", "ring bearer"), // + new Employee(2L, "Bilbo", "Baggins", "burglar"))); - mvc.perform(get("/employees").accept(MediaTypes.HAL_JSON_VALUE)) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaTypes.HAL_JSON_VALUE + ";charset=UTF-8")) - .andExpect(jsonPath("$._embedded.employees[0].id", is(1))) - .andExpect(jsonPath("$._embedded.employees[0].firstName", is("Frodo"))) - .andExpect(jsonPath("$._embedded.employees[0].lastName", is("Baggins"))) - .andExpect(jsonPath("$._embedded.employees[0].role", is("ring bearer"))) - .andExpect(jsonPath("$._embedded.employees[0]._links.self.href", is("http://localhost/employees/1"))) - .andExpect(jsonPath("$._embedded.employees[0]._links.employees.href", is("http://localhost/employees"))) - .andExpect(jsonPath("$._embedded.employees[1].id", is(2))) - .andExpect(jsonPath("$._embedded.employees[1].firstName", is("Bilbo"))) - .andExpect(jsonPath("$._embedded.employees[1].lastName", is("Baggins"))) - .andExpect(jsonPath("$._embedded.employees[1].role", is("burglar"))) - .andExpect(jsonPath("$._embedded.employees[1]._links.self.href", is("http://localhost/employees/2"))) - .andExpect(jsonPath("$._embedded.employees[1]._links.employees.href", is("http://localhost/employees"))) - .andExpect(jsonPath("$._links.self.href", is("http://localhost/employees"))) - .andReturn(); + mvc.perform(get("/employees").accept(MediaTypes.HAL_JSON_VALUE)) // + .andDo(print()) // + .andExpect(status().isOk()) // + .andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaTypes.HAL_JSON_VALUE + ";charset=UTF-8")) + .andExpect(jsonPath("$._embedded.employees[0].id", is(1))) + .andExpect(jsonPath("$._embedded.employees[0].firstName", is("Frodo"))) + .andExpect(jsonPath("$._embedded.employees[0].lastName", is("Baggins"))) + .andExpect(jsonPath("$._embedded.employees[0].role", is("ring bearer"))) + .andExpect(jsonPath("$._embedded.employees[0]._links.self.href", is("http://localhost/employees/1"))) + .andExpect(jsonPath("$._embedded.employees[0]._links.employees.href", is("http://localhost/employees"))) + .andExpect(jsonPath("$._embedded.employees[1].id", is(2))) + .andExpect(jsonPath("$._embedded.employees[1].firstName", is("Bilbo"))) + .andExpect(jsonPath("$._embedded.employees[1].lastName", is("Baggins"))) + .andExpect(jsonPath("$._embedded.employees[1].role", is("burglar"))) + .andExpect(jsonPath("$._embedded.employees[1]._links.self.href", is("http://localhost/employees/2"))) + .andExpect(jsonPath("$._embedded.employees[1]._links.employees.href", is("http://localhost/employees"))) + .andExpect(jsonPath("$._links.self.href", is("http://localhost/employees"))) // + .andReturn(); } } diff --git a/commons/src/main/java/org/springframework/hateoas/SimpleIdentifiableRepresentationModelAssembler.java b/commons/src/main/java/org/springframework/hateoas/SimpleIdentifiableRepresentationModelAssembler.java index 894542e..a3662e0 100644 --- a/commons/src/main/java/org/springframework/hateoas/SimpleIdentifiableRepresentationModelAssembler.java +++ b/commons/src/main/java/org/springframework/hateoas/SimpleIdentifiableRepresentationModelAssembler.java @@ -31,15 +31,15 @@ import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; import org.springframework.util.ReflectionUtils; /** - * A {@link SimpleRepresentationModelAssembler} that mixes together a Spring web controller and a {@link RelProvider} to build links - * upon a certain strategy. - * + * A {@link SimpleRepresentationModelAssembler} that mixes together a Spring web controller and a {@link RelProvider} to + * build links upon a certain strategy. + * * @author Greg Turnquist */ public class SimpleIdentifiableRepresentationModelAssembler implements SimpleRepresentationModelAssembler { /** - * The Spring MVC class for the {@link Identifiable} from which links will be built. + * The Spring MVC class for the object from which links will be built. */ private final Class controllerClass; @@ -49,7 +49,7 @@ public class SimpleIdentifiableRepresentationModelAssembler implements Simple @Getter private final RelProvider relProvider; /** - * A {@link Class} depicting the {@link Identifiable}'s type. + * A {@link Class} depicting the object's type. */ @Getter private final Class resourceType; @@ -63,7 +63,6 @@ public class SimpleIdentifiableRepresentationModelAssembler implements Simple * of information, resources can be defined. * * @see #setBasePath(String) to adjust base path to something like "/api"/ - * * @param controllerClass - Spring MVC controller to base links off of * @param relProvider */ @@ -72,13 +71,15 @@ public class SimpleIdentifiableRepresentationModelAssembler implements Simple this.controllerClass = controllerClass; this.relProvider = relProvider; - // Find the "T" type contained in "T extends Identifiable", e.g. SimpleIdentifiableRepresentationModelAssembler -> User - this.resourceType = GenericTypeResolver.resolveTypeArgument(this.getClass(), SimpleIdentifiableRepresentationModelAssembler.class); + // Find the "T" type contained in "T extends Identifiable", e.g. + // SimpleIdentifiableRepresentationModelAssembler -> User + this.resourceType = GenericTypeResolver.resolveTypeArgument(this.getClass(), + SimpleIdentifiableRepresentationModelAssembler.class); } /** * Alternate constructor that falls back to {@link EvoInflectorRelProvider}. - * + * * @param controllerClass */ public SimpleIdentifiableRepresentationModelAssembler(Class controllerClass) { @@ -86,8 +87,8 @@ public class SimpleIdentifiableRepresentationModelAssembler implements Simple } /** - * Add single item self link based on {@link Identifiable} and link back to aggregate root of the {@literal T} domain - * type using {@link RelProvider#getCollectionResourceRelFor(Class)}}. + * Add single item self link based on the object and link back to aggregate root of the {@literal T} domain type using + * {@link RelProvider#getCollectionResourceRelFor(Class)}}. * * @param resource */ @@ -115,15 +116,12 @@ public class SimpleIdentifiableRepresentationModelAssembler implements Simple } /** - * Build up a URI for the collection using the Spring web controller followed by the resource type transformed - * by the {@link RelProvider}. - * - * Assumption is that an {@literal EmployeeController} serving up {@literal Employee} - * objects will be serving resources at {@code /employees} and {@code /employees/1}. - * - * If this is not the case, simply override this method in your concrete instance, or resort to - * overriding {@link #addLinks(EntityModel)} and {@link #addLinks(CollectionModel)} where you have full control over exactly - * what links are put in the individual and collection resources. + * Build up a URI for the collection using the Spring web controller followed by the resource type transformed by the + * {@link RelProvider}. Assumption is that an {@literal EmployeeController} serving up {@literal Employee} objects + * will be serving resources at {@code /employees} and {@code /employees/1}. If this is not the case, simply override + * this method in your concrete instance, or resort to overriding {@link #addLinks(EntityModel)} and + * {@link #addLinks(CollectionModel)} where you have full control over exactly what links are put in the individual + * and collection resources. * * @return */ @@ -131,7 +129,8 @@ public class SimpleIdentifiableRepresentationModelAssembler implements Simple WebMvcLinkBuilder linkBuilder = linkTo(this.controllerClass); - for (String pathComponent : (getPrefix() + this.relProvider.getCollectionResourceRelFor(this.resourceType)).split("/")) { + for (String pathComponent : (getPrefix() + this.relProvider.getCollectionResourceRelFor(this.resourceType)) + .split("/")) { if (!pathComponent.isEmpty()) { linkBuilder = linkBuilder.slash(pathComponent); } diff --git a/hypermedia/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java b/hypermedia/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java index c815056..407a2fd 100644 --- a/hypermedia/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java +++ b/hypermedia/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java @@ -30,6 +30,7 @@ class DatabaseLoader { @Bean CommandLineRunner initDatabase(EmployeeRepository employeeRepository, ManagerRepository managerRepository) { return args -> { + /* * Gather Gandalf's team */ diff --git a/hypermedia/src/main/java/org/springframework/hateoas/examples/Employee.java b/hypermedia/src/main/java/org/springframework/hateoas/examples/Employee.java index d2167f2..71b4ae1 100644 --- a/hypermedia/src/main/java/org/springframework/hateoas/examples/Employee.java +++ b/hypermedia/src/main/java/org/springframework/hateoas/examples/Employee.java @@ -35,17 +35,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore; @NoArgsConstructor class Employee { - @Id @GeneratedValue - private Long id; + @Id @GeneratedValue private Long id; private String name; private String role; /** * To break the recursive, bi-directional relationship, don't serialize {@literal manager}. */ - @JsonIgnore - @OneToOne - private Manager manager; + @JsonIgnore @OneToOne private Manager manager; Employee(String name, String role, Manager manager) { diff --git a/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeController.java b/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeController.java index eaf448a..2242f3e 100644 --- a/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeController.java +++ b/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeController.java @@ -18,8 +18,8 @@ package org.springframework.hateoas.examples; import java.util.stream.Collectors; import java.util.stream.StreamSupport; -import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -36,7 +36,7 @@ class EmployeeController { private final EmployeeWithManagerResourceAssembler employeeWithManagerResourceAssembler; EmployeeController(EmployeeRepository repository, EmployeeRepresentationModelAssembler assembler, - EmployeeWithManagerResourceAssembler employeeWithManagerResourceAssembler) { + EmployeeWithManagerResourceAssembler employeeWithManagerResourceAssembler) { this.repository = repository; this.assembler = assembler; @@ -45,30 +45,30 @@ class EmployeeController { /** * Look up all employees, and transform them into a REST collection resource using - * {@link EmployeeRepresentationModelAssembler#toCollectionModel(Iterable)}. Then return them through - * Spring Web's {@link ResponseEntity} fluent API. + * {@link EmployeeRepresentationModelAssembler#toCollectionModel(Iterable)}. Then return them through Spring Web's + * {@link ResponseEntity} fluent API. */ @GetMapping("/employees") public ResponseEntity>> findAll() { - return ResponseEntity.ok( - assembler.toCollectionModel(repository.findAll())); + + return ResponseEntity.ok(assembler.toCollectionModel(repository.findAll())); } /** * Look up a single {@link Employee} and transform it into a REST resource using - * {@link EmployeeRepresentationModelAssembler#toModel(Object)}. Then return it through - * Spring Web's {@link ResponseEntity} fluent API. + * {@link EmployeeRepresentationModelAssembler#toModel(Object)}. Then return it through Spring Web's + * {@link ResponseEntity} fluent API. * * @param id */ @GetMapping("/employees/{id}") public ResponseEntity> findOne(@PathVariable long id) { - return repository.findById(id) - .map(assembler::toModel) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + return repository.findById(id) // + .map(assembler::toModel) // + .map(ResponseEntity::ok) // + .orElse(ResponseEntity.notFound().build()); } /** @@ -79,27 +79,27 @@ class EmployeeController { */ @GetMapping("/managers/{id}/employees") public ResponseEntity>> findEmployees(@PathVariable long id) { - return ResponseEntity.ok( - assembler.toCollectionModel(repository.findByManagerId(id))); + + return ResponseEntity.ok(assembler.toCollectionModel(repository.findByManagerId(id))); } @GetMapping("/employees/detailed") public ResponseEntity>> findAllDetailedEmployees() { - return ResponseEntity.ok( - employeeWithManagerResourceAssembler.toCollectionModel( - StreamSupport.stream(repository.findAll().spliterator(), false) - .map(EmployeeWithManager::new) - .collect(Collectors.toList()))); + return ResponseEntity.ok( // + employeeWithManagerResourceAssembler.toCollectionModel( // + StreamSupport.stream(repository.findAll().spliterator(), false) // + .map(EmployeeWithManager::new) // + .collect(Collectors.toList()))); } @GetMapping("/employees/{id}/detailed") public ResponseEntity> findDetailedEmployee(@PathVariable Long id) { - return repository.findById(id) - .map(EmployeeWithManager::new) - .map(employeeWithManagerResourceAssembler::toModel) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + return repository.findById(id) // + .map(EmployeeWithManager::new) // + .map(employeeWithManagerResourceAssembler::toModel) // + .map(ResponseEntity::ok) // + .orElse(ResponseEntity.notFound().build()); } } diff --git a/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeRepresentationModelAssembler.java b/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeRepresentationModelAssembler.java index dc0bced..4386bde 100644 --- a/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeRepresentationModelAssembler.java +++ b/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeRepresentationModelAssembler.java @@ -17,8 +17,8 @@ package org.springframework.hateoas.examples; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; -import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.SimpleIdentifiableRepresentationModelAssembler; import org.springframework.stereotype.Component; @@ -41,21 +41,21 @@ class EmployeeRepresentationModelAssembler extends SimpleIdentifiableRepresentat public void addLinks(EntityModel resource) { /** - * Add some custom links to the default ones provided. - * - * NOTE: To replace default links, don't invoke {@literal super.addLinks()}. + * Add some custom links to the default ones provided. NOTE: To replace default links, don't invoke + * {@literal super.addLinks()}. */ super.addLinks(resource); - resource.getContent().getId() - .ifPresent(id -> { - // Add additional links - resource.add(linkTo(methodOn(ManagerController.class).findManager(id)).withRel("manager")); - resource.add(linkTo(methodOn(EmployeeController.class).findDetailedEmployee(id)).withRel("detailed")); + resource.getContent().getId() // + .ifPresent(id -> { // + // Add additional links + resource.add(linkTo(methodOn(ManagerController.class).findManager(id)).withRel("manager")); + resource.add(linkTo(methodOn(EmployeeController.class).findDetailedEmployee(id)).withRel("detailed")); - // Maintain a legacy link to support older clients not yet adjusted to the switch from "supervisor" to "manager". - resource.add(linkTo(methodOn(SupervisorController.class).findOne(id)).withRel("supervisor")); - }); + // Maintain a legacy link to support older clients not yet adjusted to the switch from "supervisor" to + // "manager". + resource.add(linkTo(methodOn(SupervisorController.class).findOne(id)).withRel("supervisor")); + }); } /** @@ -65,7 +65,7 @@ class EmployeeRepresentationModelAssembler extends SimpleIdentifiableRepresentat */ @Override public void addLinks(CollectionModel> resources) { - + super.addLinks(resources); resources.add(linkTo(methodOn(EmployeeController.class).findAllDetailedEmployees()).withRel("detailedEmployees")); diff --git a/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeWithManager.java b/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeWithManager.java index 386e6e5..a18ac68 100644 --- a/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeWithManager.java +++ b/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeWithManager.java @@ -26,16 +26,15 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; * @author Greg Turnquist */ @Value -@JsonPropertyOrder({"id", "name", "role", "manager"}) +@JsonPropertyOrder({ "id", "name", "role", "manager" }) public class EmployeeWithManager { - @JsonIgnore - private final Employee employee; - + @JsonIgnore private final Employee employee; + public Long getId() { - - return this.employee.getId() - .orElseThrow(() -> new RuntimeException("Couldn't find anything.")); + + return this.employee.getId() // + .orElseThrow(() -> new RuntimeException("Couldn't find anything.")); } public String getName() { diff --git a/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeWithManagerResourceAssembler.java b/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeWithManagerResourceAssembler.java index 9f8d899..d8affa5 100644 --- a/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeWithManagerResourceAssembler.java +++ b/hypermedia/src/main/java/org/springframework/hateoas/examples/EmployeeWithManagerResourceAssembler.java @@ -36,7 +36,8 @@ class EmployeeWithManagerResourceAssembler implements SimpleRepresentationModelA @Override public void addLinks(EntityModel resource) { - resource.add(linkTo(methodOn(EmployeeController.class).findDetailedEmployee(resource.getContent().getId())).withSelfRel()); + resource.add( + linkTo(methodOn(EmployeeController.class).findDetailedEmployee(resource.getContent().getId())).withSelfRel()); resource.add(linkTo(methodOn(EmployeeController.class).findOne(resource.getContent().getId())).withRel("summary")); resource.add(linkTo(methodOn(EmployeeController.class).findAllDetailedEmployees()).withRel("detailedEmployees")); } diff --git a/hypermedia/src/main/java/org/springframework/hateoas/examples/Manager.java b/hypermedia/src/main/java/org/springframework/hateoas/examples/Manager.java index 1fe6994..a89e6ae 100644 --- a/hypermedia/src/main/java/org/springframework/hateoas/examples/Manager.java +++ b/hypermedia/src/main/java/org/springframework/hateoas/examples/Manager.java @@ -37,15 +37,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore; @NoArgsConstructor class Manager { - @Id @GeneratedValue - private Long id; + @Id @GeneratedValue private Long id; private String name; /** * To break the recursive, bi-directional interface, don't serialize {@literal employees}. */ - @JsonIgnore - @OneToMany(mappedBy = "manager") + @JsonIgnore // + @OneToMany(mappedBy = "manager") // private List employees = new ArrayList<>(); Manager(String name) { diff --git a/hypermedia/src/main/java/org/springframework/hateoas/examples/ManagerController.java b/hypermedia/src/main/java/org/springframework/hateoas/examples/ManagerController.java index fdd3a72..70002dd 100644 --- a/hypermedia/src/main/java/org/springframework/hateoas/examples/ManagerController.java +++ b/hypermedia/src/main/java/org/springframework/hateoas/examples/ManagerController.java @@ -15,8 +15,8 @@ */ package org.springframework.hateoas.examples; -import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -39,30 +39,31 @@ class ManagerController { /** * Look up all managers, and transform them into a REST collection resource using - * {@link ManagerRepresentationModelAssembler#toCollectionModel(Iterable)}. Then return them through - * Spring Web's {@link ResponseEntity} fluent API. + * {@link ManagerRepresentationModelAssembler#toCollectionModel(Iterable)}. Then return them through Spring Web's + * {@link ResponseEntity} fluent API. */ @GetMapping("/managers") ResponseEntity>> findAll() { - return ResponseEntity.ok( - assembler.toCollectionModel(repository.findAll())); + + return ResponseEntity.ok( // + assembler.toCollectionModel(repository.findAll())); } /** * Look up a single {@link Manager} and transform it into a REST resource using - * {@link ManagerRepresentationModelAssembler#toModel(Object)}. Then return it through - * Spring Web's {@link ResponseEntity} fluent API. + * {@link ManagerRepresentationModelAssembler#toModel(Object)}. Then return it through Spring Web's + * {@link ResponseEntity} fluent API. * * @param id */ @GetMapping("/managers/{id}") ResponseEntity> findOne(@PathVariable long id) { - return repository.findById(id) - .map(assembler::toModel) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + return repository.findById(id) // + .map(assembler::toModel) // + .map(ResponseEntity::ok) // + .orElse(ResponseEntity.notFound().build()); } /** @@ -73,7 +74,8 @@ class ManagerController { */ @GetMapping("/employees/{id}/manager") ResponseEntity> findManager(@PathVariable long id) { - return ResponseEntity.ok( - assembler.toModel(repository.findByEmployeesId(id))); + + return ResponseEntity.ok( // + assembler.toModel(repository.findByEmployeesId(id))); } } diff --git a/hypermedia/src/main/java/org/springframework/hateoas/examples/ManagerRepresentationModelAssembler.java b/hypermedia/src/main/java/org/springframework/hateoas/examples/ManagerRepresentationModelAssembler.java index b548c29..683f832 100644 --- a/hypermedia/src/main/java/org/springframework/hateoas/examples/ManagerRepresentationModelAssembler.java +++ b/hypermedia/src/main/java/org/springframework/hateoas/examples/ManagerRepresentationModelAssembler.java @@ -17,8 +17,8 @@ package org.springframework.hateoas.examples; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; -import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.SimpleIdentifiableRepresentationModelAssembler; import org.springframework.stereotype.Component; @@ -33,7 +33,8 @@ class ManagerRepresentationModelAssembler extends SimpleIdentifiableRepresentati } /** - * Retain default links provided by {@link SimpleIdentifiableRepresentationModelAssembler}, but add extra ones to each {@link Manager}. + * Retain default links provided by {@link SimpleIdentifiableRepresentationModelAssembler}, but add extra ones to each + * {@link Manager}. * * @param resource */ @@ -44,11 +45,11 @@ class ManagerRepresentationModelAssembler extends SimpleIdentifiableRepresentati */ super.addLinks(resource); - resource.getContent().getId() - .ifPresent(id -> { - // Add custom link to find all managed employees - resource.add(linkTo(methodOn(EmployeeController.class).findEmployees(id)).withRel("employees")); - }); + resource.getContent().getId() // + .ifPresent(id -> { // + // Add custom link to find all managed employees + resource.add(linkTo(methodOn(EmployeeController.class).findEmployees(id)).withRel("employees")); + }); } /** diff --git a/hypermedia/src/main/java/org/springframework/hateoas/examples/RootController.java b/hypermedia/src/main/java/org/springframework/hateoas/examples/RootController.java index 170646f..1f8daaf 100644 --- a/hypermedia/src/main/java/org/springframework/hateoas/examples/RootController.java +++ b/hypermedia/src/main/java/org/springframework/hateoas/examples/RootController.java @@ -35,7 +35,8 @@ class RootController { resourceSupport.add(linkTo(methodOn(RootController.class).root()).withSelfRel()); resourceSupport.add(linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees")); - resourceSupport.add(linkTo(methodOn(EmployeeController.class).findAllDetailedEmployees()).withRel("detailedEmployees")); + resourceSupport + .add(linkTo(methodOn(EmployeeController.class).findAllDetailedEmployees()).withRel("detailedEmployees")); resourceSupport.add(linkTo(methodOn(ManagerController.class).findAll()).withRel("managers")); return ResponseEntity.ok(resourceSupport); diff --git a/hypermedia/src/main/java/org/springframework/hateoas/examples/Supervisor.java b/hypermedia/src/main/java/org/springframework/hateoas/examples/Supervisor.java index 099e4bb..44cade9 100644 --- a/hypermedia/src/main/java/org/springframework/hateoas/examples/Supervisor.java +++ b/hypermedia/src/main/java/org/springframework/hateoas/examples/Supervisor.java @@ -30,16 +30,15 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; * @author Greg Turnquist */ @Value -@JsonPropertyOrder({"id", "name", "employees"}) +@JsonPropertyOrder({ "id", "name", "employees" }) class Supervisor { - @JsonIgnore - private final Manager manager; + @JsonIgnore private final Manager manager; public Long getId() { - return this.manager.getId() - .orElseThrow(() -> new RuntimeException("Couldn't find anything")); + return this.manager.getId() // + .orElseThrow(() -> new RuntimeException("Couldn't find anything")); } public String getName() { @@ -47,8 +46,9 @@ class Supervisor { } public List getEmployees() { - return manager.getEmployees().stream() - .map(employee -> employee.getName() + "::" + employee.getRole()) - .collect(Collectors.toList()); + + return manager.getEmployees().stream() // + .map(employee -> employee.getName() + "::" + employee.getRole()) // + .collect(Collectors.toList()); } } diff --git a/hypermedia/src/main/java/org/springframework/hateoas/examples/SupervisorController.java b/hypermedia/src/main/java/org/springframework/hateoas/examples/SupervisorController.java index f3a7ceb..1442343 100644 --- a/hypermedia/src/main/java/org/springframework/hateoas/examples/SupervisorController.java +++ b/hypermedia/src/main/java/org/springframework/hateoas/examples/SupervisorController.java @@ -22,8 +22,8 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; /** - * Represent an older controller that has since been replaced with {@link ManagerController}. - * This controller is used to provide legacy routes, i.e. backwards compatibility. + * Represent an older controller that has since been replaced with {@link ManagerController}. This controller is used to + * provide legacy routes, i.e. backwards compatibility. * * @author Greg Turnquist */ @@ -40,9 +40,10 @@ public class SupervisorController { public ResponseEntity> findOne(@PathVariable Long id) { EntityModel managerResource = controller.findOne(id).getBody(); - EntityModel supervisorResource = new EntityModel<>( - new Supervisor(managerResource.getContent()), - managerResource.getLinks()); + + EntityModel supervisorResource = new EntityModel<>( // + new Supervisor(managerResource.getContent()), // + managerResource.getLinks()); return ResponseEntity.ok(supervisorResource); } diff --git a/pom.xml b/pom.xml index bfb32c9..dda7997 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,20 @@ 2017-2019 + + + Greg L. Turnquist + Pivotal + https://spring.io + + Project Lead + + -6 + https://spring.io/team/gturnquist + gturnquist (at) pivotal.io + + + Apache License, Version 2.0 @@ -145,6 +159,17 @@ + + + + maven-surefire-plugin + + false + + + + + spring-libs-snapshot diff --git a/simplified/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java b/simplified/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java index 9583481..2bad50a 100644 --- a/simplified/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java +++ b/simplified/src/main/java/org/springframework/hateoas/examples/DatabaseLoader.java @@ -28,13 +28,14 @@ import org.springframework.stereotype.Component; class DatabaseLoader { /** - * Use Spring to inject a {@link EmployeeRepository} that can then load data. Since this will run - * only after the app is operational, the database will be up. + * Use Spring to inject a {@link EmployeeRepository} that can then load data. Since this will run only after the app + * is operational, the database will be up. * * @param repository */ @Bean CommandLineRunner init(EmployeeRepository repository) { + return args -> { repository.save(new Employee("Frodo", "Baggins", "ring bearer")); repository.save(new Employee("Bilbo", "Baggins", "burglar")); diff --git a/simplified/src/main/java/org/springframework/hateoas/examples/Employee.java b/simplified/src/main/java/org/springframework/hateoas/examples/Employee.java index e63ee24..a83a5ee 100644 --- a/simplified/src/main/java/org/springframework/hateoas/examples/Employee.java +++ b/simplified/src/main/java/org/springframework/hateoas/examples/Employee.java @@ -25,18 +25,14 @@ import javax.persistence.GeneratedValue; import javax.persistence.Id; /** - * Domain object representing a company employee. Project Lombok keeps actual code at a minimum. - * - * {@code @Data} - Generates getters, setters, toString, hash, and equals functions - * {@code @Entity} - JPA annotation to flag this class for DB persistence - * {@code @NoArgsConstructor} - Create a constructor with no args to support JPA + * Domain object representing a company employee. Project Lombok keeps actual code at a minimum. {@code @Data} - + * Generates getters, setters, toString, hash, and equals functions {@code @Entity} - JPA annotation to flag this class + * for DB persistence {@code @NoArgsConstructor} - Create a constructor with no args to support JPA * {@code @AllArgsConstructor} - Create a constructor with all args to support testing - * - * {@code @JsonIgnoreProperties(ignoreUnknow=true)} - * When converting JSON to Java, ignore any unrecognized attributes. This is critical for REST because it - * encourages adding new fields in later versions that won't break. It also allows things like _links to be - * ignore as well, meaning HAL documents can be fetched and later posted to the server without adjustment. - * + * {@code @JsonIgnoreProperties(ignoreUnknow=true)} When converting JSON to Java, ignore any unrecognized attributes. + * This is critical for REST because it encourages adding new fields in later versions that won't break. It also allows + * things like _links to be ignore as well, meaning HAL documents can be fetched and later posted to the server without + * adjustment. * * @author Greg Turnquist */ @@ -46,8 +42,7 @@ import javax.persistence.Id; @AllArgsConstructor class Employee { - @Id @GeneratedValue - private Long id; + @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String role; diff --git a/simplified/src/main/java/org/springframework/hateoas/examples/EmployeeController.java b/simplified/src/main/java/org/springframework/hateoas/examples/EmployeeController.java index 402e101..88834c0 100644 --- a/simplified/src/main/java/org/springframework/hateoas/examples/EmployeeController.java +++ b/simplified/src/main/java/org/springframework/hateoas/examples/EmployeeController.java @@ -23,10 +23,10 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.IanaLinkRelations; import org.springframework.hateoas.Link; -import org.springframework.hateoas.EntityModel; -import org.springframework.hateoas.CollectionModel; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -50,21 +50,21 @@ class EmployeeController { } /** - * Look up all employees, and transform them into a REST collection resource. - * Then return them through Spring Web's {@link ResponseEntity} fluent API. + * Look up all employees, and transform them into a REST collection resource. Then return them through Spring Web's + * {@link ResponseEntity} fluent API. */ @GetMapping("/employees") ResponseEntity>> findAll() { List> employees = StreamSupport.stream(repository.findAll().spliterator(), false) - .map(employee -> new EntityModel<>(employee, - linkTo(methodOn(EmployeeController.class).findOne(employee.getId())).withSelfRel(), - linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees"))) - .collect(Collectors.toList()); + .map(employee -> new EntityModel<>(employee, // + linkTo(methodOn(EmployeeController.class).findOne(employee.getId())).withSelfRel(), // + linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees"))) // + .collect(Collectors.toList()); - return ResponseEntity.ok( - new CollectionModel<>(employees, - linkTo(methodOn(EmployeeController.class).findAll()).withSelfRel())); + return ResponseEntity.ok( // + new CollectionModel<>(employees, // + linkTo(methodOn(EmployeeController.class).findAll()).withSelfRel())); } @PostMapping("/employees") @@ -73,32 +73,32 @@ class EmployeeController { try { Employee savedEmployee = repository.save(employee); - EntityModel employeeResource = new EntityModel<>(savedEmployee, - linkTo(methodOn(EmployeeController.class).findOne(savedEmployee.getId())).withSelfRel()); + EntityModel employeeResource = new EntityModel<>(savedEmployee, // + linkTo(methodOn(EmployeeController.class).findOne(savedEmployee.getId())).withSelfRel()); - return ResponseEntity - .created(new URI(employeeResource.getRequiredLink(IanaLinkRelations.SELF).getHref())) - .body(employeeResource); + return ResponseEntity // + .created(new URI(employeeResource.getRequiredLink(IanaLinkRelations.SELF).getHref())) // + .body(employeeResource); } catch (URISyntaxException e) { return ResponseEntity.badRequest().body("Unable to create " + employee); } } /** - * Look up a single {@link Employee} and transform it into a REST resource. Then return it through - * Spring Web's {@link ResponseEntity} fluent API. + * Look up a single {@link Employee} and transform it into a REST resource. Then return it through Spring Web's + * {@link ResponseEntity} fluent API. * * @param id */ @GetMapping("/employees/{id}") ResponseEntity> findOne(@PathVariable long id) { - return repository.findById(id) - .map(employee -> new EntityModel<>(employee, - linkTo(methodOn(EmployeeController.class).findOne(employee.getId())).withSelfRel(), - linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees"))) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + return repository.findById(id) // + .map(employee -> new EntityModel<>(employee, // + linkTo(methodOn(EmployeeController.class).findOne(employee.getId())).withSelfRel(), // + linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees"))) // + .map(ResponseEntity::ok) // + .orElse(ResponseEntity.notFound().build()); } /** @@ -118,9 +118,7 @@ class EmployeeController { Link newlyCreatedLink = linkTo(methodOn(EmployeeController.class).findOne(id)).withSelfRel(); try { - return ResponseEntity.noContent() - .location(new URI(newlyCreatedLink.getHref())) - .build(); + return ResponseEntity.noContent().location(new URI(newlyCreatedLink.getHref())).build(); } catch (URISyntaxException e) { return ResponseEntity.badRequest().body("Unable to update " + employeeToUpdate); } diff --git a/simplified/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java b/simplified/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java index 61ec4c8..be1e22a 100644 --- a/simplified/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java +++ b/simplified/src/main/java/org/springframework/hateoas/examples/EmployeeRepository.java @@ -22,5 +22,4 @@ import org.springframework.data.repository.CrudRepository; * * @author Greg Turnquist */ -interface EmployeeRepository extends CrudRepository { -} +interface EmployeeRepository extends CrudRepository {} diff --git a/simplified/src/test/java/org/springframework/hateoas/examples/EmployeeControllerTests.java b/simplified/src/test/java/org/springframework/hateoas/examples/EmployeeControllerTests.java index 7292335..49576a2 100644 --- a/simplified/src/test/java/org/springframework/hateoas/examples/EmployeeControllerTests.java +++ b/simplified/src/test/java/org/springframework/hateoas/examples/EmployeeControllerTests.java @@ -42,37 +42,35 @@ import org.springframework.test.web.servlet.MockMvc; @WebMvcTest(EmployeeController.class) public class EmployeeControllerTests { - @Autowired - private MockMvc mvc; + @Autowired private MockMvc mvc; - @MockBean - private EmployeeRepository repository; + @MockBean private EmployeeRepository repository; @Test public void getShouldFetchAHalDocument() throws Exception { - given(repository.findAll()).willReturn( - Arrays.asList( - new Employee(1L,"Frodo", "Baggins", "ring bearer"), - new Employee(2L,"Bilbo", "Baggins", "burglar"))); + given(repository.findAll()).willReturn( // + Arrays.asList( // + new Employee(1L, "Frodo", "Baggins", "ring bearer"), // + new Employee(2L, "Bilbo", "Baggins", "burglar"))); - mvc.perform(get("/employees").accept(MediaTypes.HAL_JSON_VALUE)) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaTypes.HAL_JSON_UTF8_VALUE)) - .andExpect(jsonPath("$._embedded.employees[0].id", is(1))) - .andExpect(jsonPath("$._embedded.employees[0].firstName", is("Frodo"))) - .andExpect(jsonPath("$._embedded.employees[0].lastName", is("Baggins"))) - .andExpect(jsonPath("$._embedded.employees[0].role", is("ring bearer"))) - .andExpect(jsonPath("$._embedded.employees[0]._links.self.href", is("http://localhost/employees/1"))) - .andExpect(jsonPath("$._embedded.employees[0]._links.employees.href", is("http://localhost/employees"))) - .andExpect(jsonPath("$._embedded.employees[1].id", is(2))) - .andExpect(jsonPath("$._embedded.employees[1].firstName", is("Bilbo"))) - .andExpect(jsonPath("$._embedded.employees[1].lastName", is("Baggins"))) - .andExpect(jsonPath("$._embedded.employees[1].role", is("burglar"))) - .andExpect(jsonPath("$._embedded.employees[1]._links.self.href", is("http://localhost/employees/2"))) - .andExpect(jsonPath("$._embedded.employees[1]._links.employees.href", is("http://localhost/employees"))) - .andExpect(jsonPath("$._links.self.href", is("http://localhost/employees"))) - .andReturn(); + mvc.perform(get("/employees").accept(MediaTypes.HAL_JSON_VALUE)) // + .andDo(print()) // + .andExpect(status().isOk()) // + .andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaTypes.HAL_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$._embedded.employees[0].id", is(1))) + .andExpect(jsonPath("$._embedded.employees[0].firstName", is("Frodo"))) + .andExpect(jsonPath("$._embedded.employees[0].lastName", is("Baggins"))) + .andExpect(jsonPath("$._embedded.employees[0].role", is("ring bearer"))) + .andExpect(jsonPath("$._embedded.employees[0]._links.self.href", is("http://localhost/employees/1"))) + .andExpect(jsonPath("$._embedded.employees[0]._links.employees.href", is("http://localhost/employees"))) + .andExpect(jsonPath("$._embedded.employees[1].id", is(2))) + .andExpect(jsonPath("$._embedded.employees[1].firstName", is("Bilbo"))) + .andExpect(jsonPath("$._embedded.employees[1].lastName", is("Baggins"))) + .andExpect(jsonPath("$._embedded.employees[1].role", is("burglar"))) + .andExpect(jsonPath("$._embedded.employees[1]._links.self.href", is("http://localhost/employees/2"))) + .andExpect(jsonPath("$._embedded.employees[1]._links.employees.href", is("http://localhost/employees"))) + .andExpect(jsonPath("$._links.self.href", is("http://localhost/employees"))) // + .andReturn(); } }