Introducing Spring Auto REST Docs Florian Benz @flbenz
@spring_io #springio17 DR REST DOCS OR: HOW I LEARNED TO STOP WORRYING AND LOVE DOCUMENTATION
@spring_io #springio17 Florian Benz Software Engineer @flbenz
@spring_io #springio17 Scalable Capital • Europe’s fastest growing Digital Wealth Manager • Authorized financial institute in Germany and the UK • From scratch with Spring Boot • Joined effort with Juraj Misur @juraj_misur
@spring_io #springio17 Spring Auto REST Docs Scalable Capital founded Dec 2014 Proof of concept Jul 2015 First article Nov 2015
@spring_io #springio17 Spring Auto REST Docs Open source & First release Dec 2016 DZone article Jan 2017 Two releases with fixes and features Feb & Mar 2017
@spring_io #springio17 Our Story
@spring_io #springio17 Manual Documentation DocumentationCode
@spring_io #springio17 A single big document
@spring_io #springio17 Specification-Driven Documentation DocumentationCode Specification
@spring_io #springio17 RAML Specification /weather: get: queryParameters: city: description: Name of a city in the given country. responses: 200: body: application/json: schema: | { "$schema": "http://json-schema.org/schema", "type": "object", "description": "Weather information", "properties": { "temperature": { "type": "number" } } }
@spring_io #springio17 Swagger / OpenAPI @GetMapping("weatherParam") @ApiOperation("weather") @ApiImplicitParams({ @ApiImplicitParam(name = "country", value = "Country code", required = true, dataType = "string", paramType = "query"), @ApiImplicitParam(name = "city", value = "City", required = true, dataType = "string", paramType = "query") }) @ApiResponses({ @ApiResponse(code = 200, message = "Success", response = WeatherResponse.class) }) public WeatherResponse weatherParam(@RequestParam @IsoCountryCode String country, @RequestParam String city) { return new WeatherResponse(20); }
@spring_io #springio17 Swagger / OpenAPI
@spring_io #springio17 Postman
@spring_io #springio17 Test-Driven Documentation DocumentationCode Tests
@spring_io #springio17 Spring REST Docs Generated snippets Tests Hand-written documentation Documentation
@spring_io #springio17 Spring MVC Test @Test public void shouldReturnWeatherForBarcelona() throws Exception { mockMvc.perform( post("/weather") .contentType(MediaType.APPLICATION_JSON) .content("{"country": "ES", "city": "Barcelona"}") ) .andExpect(status().isOk()) .andExpect(jsonPath("$.temperature", is(20))); }
@spring_io #springio17 Spring REST Docs @Test public void shouldReturnWeatherForBarcelona() throws Exception { mockMvc.perform( post("/weather") .contentType(MediaType.APPLICATION_JSON) .content("{"country": "ES", "city": "Barcelona"}") ) .andExpect(status().isOk()) .andExpect(jsonPath("$.temperature", is(20))); .andDo(document("weather", requestFields( fieldWithPath("country").description("Country code"), fieldWithPath("city").description("City name")))); }
@spring_io #springio17 Generated snippet |=== |Path|Type|Optional|Description |country |String |false |Country Code. |city |false |true |City name. |===
@spring_io #springio17 AsciiDoc [[resources-weather]] = Weather for your city `POST /weather` Up-to-date temperature for the given city == Response structure include::{snippets}/weather/response-fields.adoc[] == Example request/response include::{snippets}/weather/curl-request.adoc[] include::{snippets}/weather/http-response.adoc[]
@spring_io #springio17 Spring REST Docs
@spring_io #springio17 Spring REST Docs
@spring_io #springio17 Spring REST Docs Controller POJO Response Entity Jackson HTTP response Documented
@spring_io #springio17 Extending Spring REST Docs
@spring_io #springio17 Motivation .andDo(document("weather", requestFields( fieldWithPath("country").description("Country code"), fieldWithPath("city").description("Name of a city")))); We are lazy
@spring_io #springio17 Proof of concept
@spring_io #springio17 Spring Auto REST Docs Controller POJO Response Entity Jackson HTTP response Javadoc Introspection
@spring_io #springio17 Spring REST Docs @Test public void shouldReturnWeatherForBarcelona() throws Exception { mockMvc.perform( post("/weather") .contentType(MediaType.APPLICATION_JSON) .content("{"country": "ES", "city": "Barcelona"}") ) .andExpect(status().isOk()) .andExpect(jsonPath("$.temperature", is(20))); .andDo(document("weather", requestFields( fieldWithPath("country").optional().description("Country code"), fieldWithPath("city").optional().description("City name")))); }
@spring_io #springio17 Spring Auto REST Docs @Test public void shouldReturnWeatherForBarcelona() throws Exception { mockMvc.perform( post("/weather") .contentType(MediaType.APPLICATION_JSON) .content("{"country": "ES", "city": "Barcelona"}") ) .andExpect(status().isOk()) .andExpect(jsonPath("$.temperature", is(20))); .andDo(document("weather")); }
@spring_io #springio17 Javadoc class WeatherRequest { /** * Country code. */ private String country; /** * City name. */ private String city; } Path Type Optional Description country String true Country code. city String true City name.
@spring_io #springio17 Constraints class WeatherRequest { /** * Country code, e.g. ES, DE, US. */ @NotNull @IsoCountryCode private String country; /** * City name. */ @NotBlank private String city; } Path Type Optional Description country String false Country code. Must be an ISO country code. city String false City name.
@spring_io #springio17 Constraints package.OneOf.description=Must be one of ${value} package.IsoCountryCode.description=Must be an ISO country code ConstraintDesciptions.properties
@spring_io #springio17 Constraints class WeatherRequest { /** * Country code, e.g. ES, DE, US. */ @NotNull @IsoCountryCode(groups = Iso.class) @CountryName(groups = Plain.class) private String country; } Path Type Optional Description country String false Country code. ISO: Must be an ISO country code. Plain: Must be a country name.
@spring_io #springio17 Enums class WeatherRequest { /** * Country code, e.g. ES, DE, US. */ @NotNull private Country country; /** * City name. */ @NotBlank private String city; } Path Type Optional Description country String false Country code. Must be one of [DE, ES, FR, PT, US]. city String false City name.
@spring_io #springio17 Original: hand-written 2.8. Weather POST /weather Up-to-date weather data for cities around the globe. [[resources-weather]] = Weather for your city `POST /weather` Up-to-date temperature for the given city
@spring_io #springio17 Extension: Javadoc on method /** * Up-to-date weather data for cities around the globe. */ @PostMapping("weather") public WeatherResponse weather( @RequestBody @Valid WeatherRequest weatherRequest) { return new WeatherResponse(20); } 2.8. Weather POST /weather Up-to-date weather data for cities around the globe.
@spring_io #springio17 Original: Path Parameters .andDo(document("weather", pathParameters( parameterWithName("country").description("Country code"), parameterWithName("city").description("City name"))));
@spring_io #springio17 Extension: Path Parameters /** * Up-to-date weather data for cities around the globe. * * @param country Country code. * @param city City name. */ @GetMapping("weather/{country}/{city}") public WeatherResponse weatherPath( @PathVariable @IsoCountryCode String country, @PathVariable String city) { return new WeatherResponse(20); }
@spring_io #springio17 Path Parameters
@spring_io #springio17 Path Parameters
@spring_io #springio17 Original: Query Parameters .andDo(document("weather", requestParameters( parameterWithName("country").description("Country code"), parameterWithName("city").description("City name"))));
@spring_io #springio17 Extension: Query Parameters /** * Up-to-date weather data for cities around the globe. * * @param country Country code. * @param city City name. */ @GetMapping("weatherParam") public WeatherResponse weatherParam( @RequestParam @IsoCountryCode String country, @RequestParam String city) { return new WeatherResponse(20); }
@spring_io #springio17 Query Parameters
@spring_io #springio17 Query Parameters
@spring_io #springio17 Section Snippet [[resources-weather]] === Weather for your city `POST /weather` Up-to-date temperature for the given city ===== Request structure include::{snippets}/weather/request-fields.adoc[] ===== Response structure include::{snippets}/weather/response-fields.adoc[] ===== Example request/response include::{snippets}/weather/curl-request.adoc[] include::{snippets}/weather/http-response.adoc[] include::{snippets}/weather/section.adoc[] Documentation path Spring MVC Controller Javadoc Method name
@spring_io #springio17 Authorization snippet
@spring_io #springio17 Content Modifiers [ 1, 2, 3, 4, 5 ] [ 1, 2, 3 ] array shortener
@spring_io #springio17 Content Modifiers %PDF-1.5 %���� 12 0 obj << /Length 3654 /Filter /FlateDecode >> <binary> binary replacement
@spring_io #springio17 Benefits Less to write Code review Maintainability Happier developers DRY Accurate
@spring_io #springio17 Sounds good! Issues?
@spring_io #springio17 Issues
@spring_io #springio17 Issues
@spring_io #springio17 Issues
@spring_io #springio17 It’s an extension Authorization snippet Javadoc/introspection snippets Content modifiers
@spring_io #springio17 Spring Auto REST Docs at Scalable Capital
@spring_io #springio17 Spring Auto REST Docs at Scalable Capital
@spring_io #springio17 Thank you
@spring_io #springio17 Q&A @flbenz

Introducing Spring Auto REST Docs - Spring IO 2017