introducing spring auto rest docs - spring community meetup munich
TRANSCRIPT
Florian Benz
IntroducingSpring Auto REST Docs
Florian Benz
Software Engineer
@flbenz
Spring Auto REST Docs
Scalable Capital founded
Dec 2014
Proof of concept
Jul 2015
First article
Nov 2015
Spring Auto REST Docs
Open source&
First release
Dec 2016
DZone article
Jan 2017
Four releases with fixes and
features
Feb - Jul 2017
DR REST DOCS
OR: HOW I LEARNED TO STOP WORRYINGAND LOVE DOCUMENTATION
Manual Documentation
DocumentationCode
A single big document
Specification-Driven Documentation
DocumentationCode
Specification
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" } } }
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);}
Swagger / OpenAPI
Postman
Test-Driven Documentation
DocumentationCode
Tests
Spring REST Docs
Generatedsnippets
Tests
Hand-written documentation
Documentation
Spring MVC Test@Testpublic 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 REST Docs@Testpublic 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"))));}
Generated snippet|===|Path|Type|Optional|Description
|country|String|false|Country Code.
|city|false|true|City name.
|===
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 REST Docs
Spring REST Docs
Spring REST Docs
Controller
POJO
Response Entity
Jackson
HTTP response Documented
ExtendingSpring REST Docs
Motivation: We are lazy
.andDo(document("weather", requestFields( fieldWithPath("country").description("Country code"), fieldWithPath("city").description("Name of a city"))));
Proof of concept
Spring Auto REST Docs
Controller
POJO
Response Entity
Jackson
HTTP response
Javadoc
Introspection
Spring REST Docs@Testpublic 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 Auto REST Docs@Testpublic 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"));}
Javadocclass 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.
Constraintsclass 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.
Constraints
package.OneOf.description=Must be one of ${value}package.IsoCountryCode.description=Must be an ISO country code
ConstraintDesciptions.properties
Constraintsclass 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.
Enumsclass 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.
Original: hand-written[[resources-weather]]= Weather for your city
`POST /weather`
Up-to-date temperature for the given city
2.8. WeatherPOST /weather
Up-to-date weather data for cities around the globe.
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. WeatherPOST /weather
Up-to-date weather data for cities around the globe.
Original: Path Parameters
.andDo(document("weather", pathParameters( parameterWithName("country").description("Country code"), parameterWithName("city").description("City name"))));
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);}
Path Parameters
Path Parameters
Original: Query Parameters
.andDo(document("weather", requestParameters( parameterWithName("country").description("Country code"), parameterWithName("city").description("City name"))));
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);}
Query Parameters
Query Parameters
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
Authorization snippet
Content Modifiers
[ 1, 2, 3, 4, 5]
[ 1, 2, 3]
array shortener
Content Modifiers
%PDF-1.5%����12 0 obj<</Length 3654 /Filter /FlateDecode>>
<binary>
binary replacement
Benefits
Less to write
Code review Maintainability
Happier developers
DRY
Accurate
Sounds good!
Issues?
Issues
Issues
It’s an extension
Authorization snippet
Javadoc/introspection snippets
Content modifiers
Spring Auto REST Docsat Scalable Capital
Spring Auto REST Docsat Scalable Capital
Thank you
Q&A
@flbenz