66
Spring REST Docs Documenting RESTful services

Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

  • Upload
    others

  • View
    18

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Spring REST DocsDocumenting RESTful services

Page 2: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

About me

@jeroenvschagen

• Senior developer at 42.nl • Open source • Lead product developer • Academic domain: VU, WUR

Page 3: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

API docs

Page 4: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Needs to be done..

Page 5: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Accuracy

• Otherwise nobody will read it

Page 6: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Efficiency

• Use a tool designed for writing

• Generate when possible

• No duplication

• Cross cutting concerns

Page 7: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Swagger

Page 8: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Swagger

• Describes the RESTful API: /apidocs

• Analysing the implementation

• Spring MVC

• Jersey

Page 9: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Swagger@RestController @RequestMapping(value = "/books") public class BooksController { @RequestMapping(value = "/{id}", method = RequestMethod.GET) public Book findById(@PathVariable Long id) { return bookService.findById(id); } @RequestMapping(method = RequestMethod.POST) public Book save(@RequestBody Book book) { return bookService.save(book); } @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) public Book delete(@PathVariable Long id) { return bookService.delete(id); } ... }

Page 10: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Swagger UI

Page 11: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey
Page 12: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Not quite there yet...

Page 13: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Hypermedia

Page 14: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

http://martinfowler.com/articles/richardsonMaturityModel.html

Page 15: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Spring HATEAOS{ "_embedded" : { "product" : [ { "name" : "My product", "_links" : { "self" : { "href" : "http://localhost:8080/products/1" },

"vendors": { "href" : "http://localhost:8080/products/1/vendors" }

} } ] }, "_links" : { "self" : { "href" : "http://localhost:8080/products" } }}

http://localhost:8080/products

Hypermedia

Data

Page 16: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey
Page 17: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

URI vs Resource based

Page 18: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

URI vs Resource based

Page 19: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey
Page 20: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Buggy

Frameworks are complicated

Interceptor, Filter, ControllerAdvice, ArgumentResolver,

ResultHandler, Converters

Page 21: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Customisation @ApiOperation(value = "find-all", notes = "Retrieves all system users.") @ApiResponses({ @ApiResponse(code = 200, message = "Everything goes ok."), @ApiResponse(code = 403, message = "Not allowed to retrieve data."), @ApiResponse(code = 500, message = "Unexpected server error.") }) @ResponseBody @RequestMapping(method = GET) public Iterable<User> findAll(Principal principal) { return userService.findAll(Users.getUserName(principal)); }

Annotation hell

Duplication (code, annotation)

@ApiModel(value = "CreateUserForm", description = "Form for creating a user") public class CreateUserForm { @NotNull @ApiModelProperty(value = "email", required = true) private String email; }

Page 22: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Cross cutting concerns

• HTTP verbs, status codes

• Authentication, versioning

• Error handling

• Duplication...

Page 23: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Production

• Security risk

• Only for authorised users

• Disable /api-docs

• Framework size

Page 24: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Instant try

Page 25: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Alternatives?

Page 26: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Swagger2Markup

Page 27: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Spring REST Docs

Page 28: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Spring project

• Documenting RESTful services

• Andy Wilkinson (Pivotal)

• 1.0 release in October 20151.1 released May 31, 2016

• Webinar: Documenting RESTful APIshttps://www.youtube.com/watch?v=knH5ihPNiUs

Page 29: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Write tests

Generate snippets

Add handwritten content

User guide

(Frequently changing)

(Rarely changing)

Page 30: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Test driven documentation

• Encourages testing

• No annotations / duplication in code

• Realistic examples

• Documentation is accurate, or the test fails

Page 31: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey
Page 32: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Generate snippetsSpring test

ResultHandler (org.springframework.restdocs.mockmvc.MockMvcRestDocumentation)

this.webClient.perform(get("/users/1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.email").value("[email protected]")) .andDo(document("user-find-by-id"));

Page 33: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Generate snippets

Page 34: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Generate snippetscurl-request.adoc

http-request.adoc

request-fields.adoc

http-response.adoc

response-fields.adoc

links.adoc

Page 35: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Generate snippets

[source,http]----HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Content-Length: 40

{ "id" : 2, "email" : "[email protected]"}----

http-response.adoc

Page 36: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Start writingAsciidoctor

Page 37: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Include contentAsciidoctor

Page 38: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Document

Page 39: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Alternatives (1.1.0+)

• JUnit / TestNG

• MockMVC / REST Assured

• Asciidoctor / Markdown

Page 40: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Let's code

Page 41: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Spring boot<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId></dependency>

Page 42: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Application

@SpringBootApplicationpublic class SampleApplication { public static void main(String[] args) throws Exception { SpringApplication.run(SampleApplication.class, args); }

}

Page 43: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

JPA Entity

@Entitypublic class User extends BaseEntity { private String email;

public String getEmail() { return email; } public void setEmail(String email) { this.email = email; }

}

Page 44: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Controller

@RestController@RequestMapping("/users")public class UserController { private final UserRepository userRepository; @Autowired public UserController(UserRepository userRepository) { this.userRepository = userRepository; }

@RequestMapping(method = RequestMethod.GET) public Iterable<User> findAll() { return userRepository.findAll(); } ... }

Page 45: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

+ Spring REST docs

Page 46: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Add dependency

<dependency> <groupId>org.springframework.restdocs</groupId> <artifactId>spring-restdocs-mockmvc</artifactId> <scope>test</scope></dependency>

Page 47: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Spring test@WebAppConfigurationpublic abstract class AbstractWebIntegrationTest extends AbstractIntegrationTest { @Rule public final JunitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets"); @Autowired private WebApplicationContext webApplicationContext; protected MockMvc webClient; @Before public void initWebClient() { this.webClient = MockMvcBuilders.webAppContextSetup(webApplicationContext) .apply(MockMvcRestDocumentation.documentationConfiguration(this.restDocumentation)) .alwaysDo(print()) .build(); }

}

MockMvcConfigurer (org.springframework.restdocs.mockmvc.RestDocumentationMockMvcConfigurer)

Page 48: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Spring test

public class UserControllerTest extends AbstractWebIntegrationTest {

@Autowired private UserRepository userRepository; @Test public void testFindById() throws Exception { final User user = new User(); user.setEmail("[email protected]"); userRepository.save(user);

this.webClient.perform(get("/users/" + user.getId())) .andExpect(status().isOk()) .andExpect(jsonPath("email").value("[email protected]")) .andDo(document("user-find-by-id", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), responseFields( fieldWithPath("id").description("The user's identifier."), fieldWithPath("email").description("The user's email address.")))); } }

Page 49: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Spring test

[source,http]----HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Content-Length: 40

{ "id" : 2, "email" : "[email protected]"}----

[source,bash]----$ curl 'http://localhost:8080/users/2' -i----

|===|Path|Type|Description

|id|Number|The user's identifier.

|email|String|The user's email address.

|===

curl-request.adoc

http-response.adoc

response-fields.adoc

target/generated-snippets

Page 50: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Asciidoctor plugin<plugin> <groupId>org.asciidoctor</groupId> <artifactId>asciidoctor-maven-plugin</artifactId> <version>1.5.2</version> <executions> <execution> <id>generate-docs</id> <phase>package</phase> <goals> <goal>process-asciidoc</goal> </goals> <configuration> <backend>html</backend> <doctype>book</doctype> <sourceDocumentName>index.adoc</sourceDocumentName> <attributes> <generated>${project.build.directory}/generated-doc</generated> </attributes> </configuration> </execution> </executions></plugin>

Page 51: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Manual docs

:toc: left

= Sample REST APIJeroen van Schagen

[[abstract]]

Generated documentation using Spring REST Docs.

include::chapters/general.adoc[]include::chapters/resources.adoc[]

:snippets: ../../../../../target/generated-snippets

== Users

Users are used to authenticate with the system.

=== Get one user

Retrieves a user based on identifier.

Request

include::{snippets}/user-find-by-id/curl-request.adoc[]

Response

include::{snippets}/user-find-by-id/http-response.adoc[]include::{snippets}/user-find-by-id/response-fields.adoc[]

Page 52: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey
Page 53: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Publish

Github pages

https://github.com/github/maven-plugins

Nexus assembly

Page 54: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Github

https://developer.github.com/v3/

Page 55: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

More requests..

Page 56: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

GET List

@Test public void testFindAll() throws Exception { final User user = new User(); user.setEmail("[email protected]"); userRepository.save(user);

this.webClient.perform(get("/users")) .andExpect(status().isOk()) .andExpect(jsonPath("$[0].email").value("[email protected]")) .andDo(document("user-find-all", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), responseFields( fieldWithPath("[].id").description("The user's identifier."), fieldWithPath("[].email").description("The user's email address.")))); }

Arrays

Page 57: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

POST

@Test public void testSave() throws Exception { final User user = new User(); user.setEmail("[email protected]"); userRepository.save(user);

this.webClient.perform(post("/users") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(user))) .andExpect(status().isOk()) .andExpect(jsonPath("$.email").value("[email protected]")) .andDo(document("user-save", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestFields( fieldWithPath("id").description("The user's identifier."), fieldWithPath("email").description("The user's email address.")), responseFields( fieldWithPath("id").description("The user's identifier."), fieldWithPath("email").description("The user's email address.")))); }

Page 58: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

DELETE

@Test public void testDelete() throws Exception { final User user = new User(); user.setEmail("[email protected]"); userRepository.save(user);

this.webClient.perform(delete("/users/" + user.getId())) .andExpect(status().isOk()) .andDo(document("user-delete", preprocessResponse(prettyPrint()))); }

Page 59: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Error handling

Page 60: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

@ControllerAdvicepublic class ControllerExceptionAdvice {

private static final Logger LOGGER = LoggerFactory.getLogger(ControllerExceptionAdvice.class);

@ExceptionHandler({ Exception.class }) public ModelAndView handleOther(HttpServletResponse response, Object handler, Exception ex) { LOGGER.error("Handling request, for [" + handler + "], resulted in the following exception.", ex); response.setStatus(INTERNAL_SERVER_ERROR.value()); return new ModelAndView(new MappingJackson2JsonView(), "error", new ExceptionJsonBody(ex)); }

public static class ExceptionJsonBody { private final Class<?> type; private final String message;

public ExceptionJsonBody(Exception ex) { this.type = ex.getClass(); this.message = ex.getMessage(); }

public Class<?> getType() { return type; } public String getMessage() { return message; }

}

}

Page 61: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Error handling

@Test public void testDeleteUnknown() throws Exception { this.webClient.perform(delete("/users/42")) .andExpect(status().isInternalServerError()) .andExpect(jsonPath("$.error.message").value("User with id 42 does not exist.")) .andDo(document("user-delete-unknown", preprocessResponse(prettyPrint()), responseFields( fieldWithPath("error.type").description("The type of error."), fieldWithPath("error.message").description("The error message.")))); }

Page 62: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Spring Data REST@RepositoryRestResource(collectionResourceRel = "product", path = "products")public interface ProductRepository extends CrudRepository<Product, Long> { }

{ "_embedded" : { "product" : [ { "name" : "My product", "_links" : { "self" : { "href" : "http://localhost:8080/products/1" }, "product" : { "href" : "http://localhost:8080/products/1" } } } ] }, "_links" : { "self" : { "href" : "http://localhost:8080/products" }, "profile" : { "href" : "http://localhost:8080/profile/products" } }}

/products

Hypermedia

Page 63: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Spring Data REST @Test public void testFindAll() throws Exception { final Product product = new Product(); product.setName("My product"); productRepository.save(product);

this.webClient.perform(get("/products")) .andExpect(status().isOk()) .andExpect(jsonPath("_embedded.product[0].name").value("My product")) .andExpect(jsonPath("_links.self", is(notNullValue()))) .andExpect(jsonPath("_links.profile", is(notNullValue()))) .andDo(document("product-find-all", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), responseFields( fieldWithPath("_embedded.product[].name").description("The product name."), fieldWithPath("_embedded.product[]._links").description("Links related to this product."), fieldWithPath("_links").description("Links related to products in general.")), links( linkWithRel("self").description("Reference to self."), linkWithRel("profile").description("Shows the product profile.")))); }

Hypermedia

Page 64: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Reusing snippets (1.1.0+) @Test public void testFindAll() throws Exception { final Product product = new Product(); product.setName("My product"); productRepository.save(product);

this.webClient.perform(get("/products")) .andExpect(status().isOk()) .andExpect(jsonPath("_embedded.product[0].name").value("My product")) .andExpect(jsonPath("_links.self", is(notNullValue()))) .andExpect(jsonPath("_links.profile", is(notNullValue()))) .andDo(document("product-find-all", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), responseFields( fieldWithPath("_embedded.product[].name").description("The product name."), fieldWithPath("_embedded.product[]._links").description("Links related to this product."), fieldWithPath("_links").description("Links related to products in general.")), links( this.pageLinks.and( linkWithRel("profile").description("Shows the product profile.") )))); }

Reused

Page 65: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Demo

Page 66: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey

Questions?