Polishing.
This commit is contained in:
@@ -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"));
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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<CollectionModel<EntityModel<Employee>>> 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<EntityModel<Employee>> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,5 +22,4 @@ import org.springframework.data.repository.CrudRepository;
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
interface EmployeeRepository extends CrudRepository<Employee, Long> {
|
||||
}
|
||||
interface EmployeeRepository extends CrudRepository<Employee, Long> {}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user