Merge branch 'skoiloth-master'
This commit is contained in:
commit
fb6d5f2a67
|
@ -21,6 +21,7 @@
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
|
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -44,16 +45,53 @@
|
||||||
<version>1.0.1.RELEASE</version>
|
<version>1.0.1.RELEASE</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.jayway.jsonpath</groupId>
|
||||||
|
<artifactId>json-path</artifactId>
|
||||||
|
<version>2.0.0</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
<plugin>
|
||||||
</build>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<includes>
|
||||||
|
<include>**/*Documentation.java</include>
|
||||||
|
</includes>
|
||||||
|
</configuration>
|
||||||
|
</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>
|
||||||
|
<attributes>
|
||||||
|
<snippets>${snippetsDirectory}</snippets>
|
||||||
|
</attributes>
|
||||||
|
<sourceDirectory>src/docs/asciidocs</sourceDirectory>
|
||||||
|
<outputDirectory>target/generated-docs</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -0,0 +1,203 @@
|
||||||
|
= RESTful Notes API Guide
|
||||||
|
Baeldung;
|
||||||
|
:doctype: book
|
||||||
|
:icons: font
|
||||||
|
:source-highlighter: highlightjs
|
||||||
|
:toc: left
|
||||||
|
:toclevels: 4
|
||||||
|
:sectlinks:
|
||||||
|
|
||||||
|
[[overview]]
|
||||||
|
= Overview
|
||||||
|
|
||||||
|
[[overview-http-verbs]]
|
||||||
|
== HTTP verbs
|
||||||
|
|
||||||
|
RESTful notes tries to adhere as closely as possible to standard HTTP and REST conventions in its
|
||||||
|
use of HTTP verbs.
|
||||||
|
|
||||||
|
|===
|
||||||
|
| Verb | Usage
|
||||||
|
|
||||||
|
| `GET`
|
||||||
|
| Used to retrieve a resource
|
||||||
|
|
||||||
|
| `POST`
|
||||||
|
| Used to create a new resource
|
||||||
|
|
||||||
|
| `PATCH`
|
||||||
|
| Used to update an existing resource, including partial updates
|
||||||
|
|
||||||
|
| `DELETE`
|
||||||
|
| Used to delete an existing resource
|
||||||
|
|===
|
||||||
|
|
||||||
|
RESTful notes tries to adhere as closely as possible to standard HTTP and REST conventions in its
|
||||||
|
use of HTTP status codes.
|
||||||
|
|
||||||
|
|===
|
||||||
|
| Status code | Usage
|
||||||
|
|
||||||
|
| `200 OK`
|
||||||
|
| The request completed successfully
|
||||||
|
|
||||||
|
| `201 Created`
|
||||||
|
| A new resource has been created successfully. The resource's URI is available from the response's
|
||||||
|
`Location` header
|
||||||
|
|
||||||
|
| `204 No Content`
|
||||||
|
| An update to an existing resource has been applied successfully
|
||||||
|
|
||||||
|
| `400 Bad Request`
|
||||||
|
| The request was malformed. The response body will include an error providing further information
|
||||||
|
|
||||||
|
| `404 Not Found`
|
||||||
|
| The requested resource did not exist
|
||||||
|
|===
|
||||||
|
|
||||||
|
[[overview-headers]]
|
||||||
|
== Headers
|
||||||
|
|
||||||
|
Every response has the following header(s):
|
||||||
|
|
||||||
|
include::{snippets}/headers-example/response-headers.adoc[]
|
||||||
|
|
||||||
|
[[overview-hypermedia]]
|
||||||
|
== Hypermedia
|
||||||
|
|
||||||
|
RESTful Notes uses hypermedia and resources include links to other resources in their
|
||||||
|
responses. Responses are in http://stateless.co/hal_specification.html[Hypertext Application
|
||||||
|
from resource to resource.
|
||||||
|
Language (HAL)] format. Links can be found beneath the `_links` key. Users of the API should
|
||||||
|
not create URIs themselves, instead they should use the above-described links to navigate
|
||||||
|
|
||||||
|
[[resources]]
|
||||||
|
= Resources
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[resources-index]]
|
||||||
|
== Index
|
||||||
|
|
||||||
|
The index provides the entry point into the service.
|
||||||
|
|
||||||
|
[[resources-index-access]]
|
||||||
|
=== Accessing the index
|
||||||
|
|
||||||
|
A `GET` request is used to access the index
|
||||||
|
|
||||||
|
==== Response structure
|
||||||
|
|
||||||
|
include::{snippets}/index-example/http-response.adoc[]
|
||||||
|
|
||||||
|
==== Example response
|
||||||
|
|
||||||
|
include::{snippets}/index-example/http-response.adoc[]
|
||||||
|
|
||||||
|
==== Example request
|
||||||
|
|
||||||
|
include::{snippets}/index-example/http-request.adoc[]
|
||||||
|
|
||||||
|
==== CURL request
|
||||||
|
|
||||||
|
include::{snippets}/index-example/curl-request.adoc[]
|
||||||
|
|
||||||
|
[[resources-index-links]]
|
||||||
|
==== Links
|
||||||
|
|
||||||
|
include::{snippets}/index-example/links.adoc[]
|
||||||
|
|
||||||
|
|
||||||
|
[[resources-CRUD]]
|
||||||
|
== CRUD REST Service
|
||||||
|
|
||||||
|
The CRUD provides the entry point into the service.
|
||||||
|
|
||||||
|
[[resources-crud-access]]
|
||||||
|
=== Accessing the crud GET
|
||||||
|
|
||||||
|
A `GET` request is used to access the CRUD read
|
||||||
|
|
||||||
|
==== Response structure
|
||||||
|
|
||||||
|
include::{snippets}/crud-get-example/http-request.adoc[]
|
||||||
|
|
||||||
|
==== Example response
|
||||||
|
|
||||||
|
include::{snippets}/crud-get-example/http-response.adoc[]
|
||||||
|
|
||||||
|
==== CURL request
|
||||||
|
|
||||||
|
include::{snippets}/crud-get-example/curl-request.adoc[]
|
||||||
|
|
||||||
|
[[resources-crud-access]]
|
||||||
|
=== Accessing the crud POST
|
||||||
|
|
||||||
|
A `POST` request is used to access the CRUD create
|
||||||
|
|
||||||
|
==== Response structure
|
||||||
|
|
||||||
|
include::{snippets}/crud-create-example/http-request.adoc[]
|
||||||
|
|
||||||
|
==== Example response
|
||||||
|
|
||||||
|
include::{snippets}/crud-create-example/http-response.adoc[]
|
||||||
|
|
||||||
|
==== CURL request
|
||||||
|
|
||||||
|
include::{snippets}/crud-create-example/curl-request.adoc[]
|
||||||
|
|
||||||
|
[[resources-crud-access]]
|
||||||
|
=== Accessing the crud DELETE
|
||||||
|
|
||||||
|
A `DELETE` request is used to access the CRUD create
|
||||||
|
|
||||||
|
==== Response structure
|
||||||
|
|
||||||
|
include::{snippets}/crud-delete-example/http-request.adoc[]
|
||||||
|
|
||||||
|
==== Example response
|
||||||
|
|
||||||
|
include::{snippets}/crud-delete-example/http-response.adoc[]
|
||||||
|
|
||||||
|
==== CURL request
|
||||||
|
|
||||||
|
include::{snippets}/crud-delete-example/curl-request.adoc[]
|
||||||
|
|
||||||
|
[[resources-crud-access]]
|
||||||
|
=== Accessing the crud PATCH
|
||||||
|
|
||||||
|
A `PATCH` request is used to access the CRUD create
|
||||||
|
|
||||||
|
==== Response structure
|
||||||
|
|
||||||
|
include::{snippets}/crud-patch-example/http-request.adoc[]
|
||||||
|
|
||||||
|
==== Example response
|
||||||
|
|
||||||
|
include::{snippets}/crud-patch-example/http-response.adoc[]
|
||||||
|
|
||||||
|
==== CURL request
|
||||||
|
|
||||||
|
include::{snippets}/crud-patch-example/curl-request.adoc[]
|
||||||
|
|
||||||
|
[[resources-crud-access]]
|
||||||
|
=== Accessing the crud PUT
|
||||||
|
|
||||||
|
A `PUT` request is used to access the CRUD create
|
||||||
|
|
||||||
|
==== Response structure
|
||||||
|
|
||||||
|
include::{snippets}/crud-put-example/http-request.adoc[]
|
||||||
|
|
||||||
|
==== Example response
|
||||||
|
|
||||||
|
include::{snippets}/crud-put-example/http-response.adoc[]
|
||||||
|
|
||||||
|
==== CURL request
|
||||||
|
|
||||||
|
include::{snippets}/crud-put-example/curl-request.adoc[]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/crud")
|
||||||
|
public class CRUDController {
|
||||||
|
|
||||||
|
@RequestMapping(method=RequestMethod.GET)
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
public List<CrudInput> read(@RequestBody CrudInput crudInput) {
|
||||||
|
List<CrudInput> returnList=new ArrayList<CrudInput>();
|
||||||
|
returnList.add(crudInput);
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResponseStatus(HttpStatus.CREATED)
|
||||||
|
@RequestMapping(method=RequestMethod.POST)
|
||||||
|
public HttpHeaders save(@RequestBody CrudInput crudInput) {
|
||||||
|
HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
|
httpHeaders.setLocation(linkTo(CRUDController.class).slash(crudInput.getTitle()).toUri());
|
||||||
|
return httpHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
HttpHeaders delete(@RequestBody CrudInput crudInput) {
|
||||||
|
HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
|
return httpHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
|
||||||
|
@ResponseStatus(HttpStatus.ACCEPTED)
|
||||||
|
void put(@PathVariable("id") long id, @RequestBody CrudInput crudInput) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/{id}", method = RequestMethod.PATCH)
|
||||||
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
|
void patch(@PathVariable("id") long id, @RequestBody CrudInput crudInput) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.validator.constraints.NotBlank;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public class CrudInput {
|
||||||
|
|
||||||
|
//@NotBlank
|
||||||
|
private final String title;
|
||||||
|
|
||||||
|
private final String body;
|
||||||
|
|
||||||
|
private final List<URI> tagUris;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public CrudInput(@JsonProperty("title") String title,
|
||||||
|
@JsonProperty("body") String body, @JsonProperty("tags") List<URI> tagUris) {
|
||||||
|
this.title = title;
|
||||||
|
this.body = body;
|
||||||
|
this.tagUris = tagUris == null ? Collections.<URI>emptyList() : tagUris;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("tags")
|
||||||
|
public List<URI> getTagUris() {
|
||||||
|
return this.tagUris;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,23 +1,22 @@
|
||||||
package com.example;
|
package com.example;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||||
|
|
||||||
import org.springframework.hateoas.ResourceSupport;
|
import org.springframework.hateoas.ResourceSupport;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api")
|
@RequestMapping("/")
|
||||||
public class IndexController {
|
public class IndexController {
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET)
|
@RequestMapping(method=RequestMethod.GET)
|
||||||
public ResourceSupport index() {
|
public ResourceSupport index() {
|
||||||
ResourceSupport index = new ResourceSupport();
|
ResourceSupport index = new ResourceSupport();
|
||||||
index.add(linkTo(MyRestController.class).withRel("notes"));
|
index.add(linkTo(CRUDController.class).withRel("crud"));
|
||||||
index.add(linkTo(MyRestController.class).withRel("tags"));
|
return index;
|
||||||
return index;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,16 +0,0 @@
|
||||||
package com.example;
|
|
||||||
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/rest/api")
|
|
||||||
public class MyRestController {
|
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET)
|
|
||||||
public String index() {
|
|
||||||
return "Hello";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class SpringRestDocsApplication {
|
public class SpringRestDocsApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(SpringRestDocsApplication.class, args);
|
SpringApplication.run(SpringRestDocsApplication.class, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,39 @@
|
||||||
package com.example;
|
package com.example;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||||
|
import org.springframework.hateoas.MediaTypes;
|
||||||
import org.springframework.restdocs.RestDocumentation;
|
import org.springframework.restdocs.RestDocumentation;
|
||||||
|
import org.springframework.restdocs.constraints.ConstraintDescriptions;
|
||||||
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
|
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
|
||||||
|
import org.springframework.restdocs.payload.FieldDescriptor;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
import org.springframework.test.context.web.WebAppConfiguration;
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
|
||||||
|
import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
|
||||||
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
|
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
|
||||||
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
|
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
|
||||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
|
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
|
||||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
|
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
|
||||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
|
import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
import static org.springframework.util.StringUtils.collectionToDelimitedString;
|
||||||
|
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@SpringApplicationConfiguration(classes = SpringRestDocsApplication.class)
|
@SpringApplicationConfiguration(classes = SpringRestDocsApplication.class)
|
||||||
|
@ -35,6 +46,9 @@ public class ApiDocumentation {
|
||||||
@Autowired
|
@Autowired
|
||||||
private WebApplicationContext context;
|
private WebApplicationContext context;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
private RestDocumentationResultHandler document;
|
private RestDocumentationResultHandler document;
|
||||||
|
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
@ -42,25 +56,179 @@ public class ApiDocumentation {
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
this.document = document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()));
|
this.document = document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()));
|
||||||
|
|
||||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||||
.apply(documentationConfiguration(this.restDocumentation))
|
.apply(documentationConfiguration(this.restDocumentation))
|
||||||
.alwaysDo(this.document).build();
|
.alwaysDo(this.document)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void headersExample() throws Exception {
|
||||||
|
this.document.snippets(responseHeaders(headerWithName("Content-Type")
|
||||||
|
.description("The Content-Type of the payload, e.g. `application/hal+json`")));
|
||||||
|
this.mockMvc.perform(get("/"))
|
||||||
|
.andExpect(status().isOk());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void indexExample() throws Exception {
|
public void indexExample() throws Exception {
|
||||||
this.document.snippets(
|
this.document.snippets(
|
||||||
links(
|
links(linkWithRel("crud").description("The <<resources-tags,Tags resource>>")),
|
||||||
linkWithRel("notes").description("The <<Simple description about the REST Service >>"),
|
responseFields(fieldWithPath("_links").description("<<resources-index-links,Links>> to other resources"))
|
||||||
linkWithRel("tags").description("The <<resources-tags,Tags resource>>")
|
);
|
||||||
),
|
this.mockMvc.perform(get("/"))
|
||||||
responseFields(fieldWithPath("_links").description("<<resources-index-links,Links>> to other resources")));
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
this.mockMvc.perform(get("/api")).andExpect(status().isOk());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void crudGetExample() throws Exception {
|
||||||
|
|
||||||
|
Map<String, String> tag = new HashMap<>();
|
||||||
|
tag.put("name", "GET");
|
||||||
|
|
||||||
|
String tagLocation = this.mockMvc.perform(get("/crud")
|
||||||
|
.contentType(MediaTypes.HAL_JSON)
|
||||||
|
.content(this.objectMapper.writeValueAsString(tag)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andReturn()
|
||||||
|
.getResponse()
|
||||||
|
.getHeader("Location");
|
||||||
|
|
||||||
|
Map<String, Object> crud = new HashMap<>();
|
||||||
|
crud.put("title", "Sample Model");
|
||||||
|
crud.put("body", "http://www.baeldung.com/");
|
||||||
|
crud.put("tags", singletonList(tagLocation));
|
||||||
|
|
||||||
|
this.mockMvc.perform(get("/crud")
|
||||||
|
.contentType(MediaTypes.HAL_JSON)
|
||||||
|
.content(this.objectMapper.writeValueAsString(crud)))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void crudCreateExample() throws Exception {
|
||||||
|
Map<String, String> tag = new HashMap<>();
|
||||||
|
tag.put("name", "CREATE");
|
||||||
|
|
||||||
|
String tagLocation = this.mockMvc.perform(post("/crud")
|
||||||
|
.contentType(MediaTypes.HAL_JSON)
|
||||||
|
.content(this.objectMapper.writeValueAsString(tag)))
|
||||||
|
.andExpect(status().isCreated())
|
||||||
|
.andReturn()
|
||||||
|
.getResponse()
|
||||||
|
.getHeader("Location");
|
||||||
|
|
||||||
|
Map<String, Object> crud = new HashMap<>();
|
||||||
|
crud.put("title", "Sample Model");
|
||||||
|
crud.put("body", "http://www.baeldung.com/");
|
||||||
|
crud.put("tags", singletonList(tagLocation));
|
||||||
|
|
||||||
|
ConstrainedFields fields = new ConstrainedFields(CrudInput.class);
|
||||||
|
this.document.snippets(requestFields(
|
||||||
|
fields.withPath("title").description("The title of the note"),
|
||||||
|
fields.withPath("body").description("The body of the note"),
|
||||||
|
fields.withPath("tags").description("An array of tag resource URIs")));
|
||||||
|
this.mockMvc.perform(post("/crud").contentType(MediaTypes.HAL_JSON).content(this.objectMapper.writeValueAsString(crud))).andExpect(status().isCreated());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void crudDeleteExample() throws Exception {
|
||||||
|
|
||||||
|
Map<String, String> tag = new HashMap<>();
|
||||||
|
tag.put("name", "DELETE");
|
||||||
|
|
||||||
|
String tagLocation = this.mockMvc.perform(delete("/crud/10")
|
||||||
|
.contentType(MediaTypes.HAL_JSON)
|
||||||
|
.content(this.objectMapper.writeValueAsString(tag)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andReturn()
|
||||||
|
.getResponse()
|
||||||
|
.getHeader("Location");
|
||||||
|
|
||||||
|
Map<String, Object> crud = new HashMap<>();
|
||||||
|
crud.put("title", "Sample Model");
|
||||||
|
crud.put("body", "http://www.baeldung.com/");
|
||||||
|
crud.put("tags", singletonList(tagLocation));
|
||||||
|
|
||||||
|
this.mockMvc.perform(delete("/crud/10")
|
||||||
|
.contentType(MediaTypes.HAL_JSON)
|
||||||
|
.content(this.objectMapper.writeValueAsString(crud)))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void crudPatchExample() throws Exception {
|
||||||
|
|
||||||
|
Map<String, String> tag = new HashMap<>();
|
||||||
|
tag.put("name", "PATCH");
|
||||||
|
|
||||||
|
String tagLocation = this.mockMvc.perform(patch("/crud/10")
|
||||||
|
.contentType(MediaTypes.HAL_JSON)
|
||||||
|
.content(this.objectMapper.writeValueAsString(tag)))
|
||||||
|
.andExpect(status().isNoContent())
|
||||||
|
.andReturn()
|
||||||
|
.getResponse()
|
||||||
|
.getHeader("Location");
|
||||||
|
|
||||||
|
Map<String, Object> crud = new HashMap<>();
|
||||||
|
crud.put("title", "Sample Model");
|
||||||
|
crud.put("body", "http://www.baeldung.com/");
|
||||||
|
crud.put("tags", singletonList(tagLocation));
|
||||||
|
|
||||||
|
this.mockMvc.perform(patch("/crud/10")
|
||||||
|
.contentType(MediaTypes.HAL_JSON)
|
||||||
|
.content(this.objectMapper.writeValueAsString(crud)))
|
||||||
|
.andExpect(status().isNoContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void crudPutExample() throws Exception {
|
||||||
|
Map<String, String> tag = new HashMap<>();
|
||||||
|
tag.put("name", "PUT");
|
||||||
|
|
||||||
|
String tagLocation = this.mockMvc.perform(put("/crud/10")
|
||||||
|
.contentType(MediaTypes.HAL_JSON)
|
||||||
|
.content(this.objectMapper.writeValueAsString(tag)))
|
||||||
|
.andExpect(status().isAccepted())
|
||||||
|
.andReturn()
|
||||||
|
.getResponse()
|
||||||
|
.getHeader("Location");
|
||||||
|
|
||||||
|
Map<String, Object> crud = new HashMap<>();
|
||||||
|
crud.put("title", "Sample Model");
|
||||||
|
crud.put("body", "http://www.baeldung.com/");
|
||||||
|
crud.put("tags", singletonList(tagLocation));
|
||||||
|
|
||||||
|
this.mockMvc.perform(put("/crud/10")
|
||||||
|
.contentType(MediaTypes.HAL_JSON)
|
||||||
|
.content(this.objectMapper.writeValueAsString(crud)))
|
||||||
|
.andExpect(status().isAccepted());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void contextLoads() {
|
public void contextLoads() {
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private static class ConstrainedFields {
|
||||||
|
|
||||||
|
private final ConstraintDescriptions constraintDescriptions;
|
||||||
|
|
||||||
|
ConstrainedFields(Class<?> input) {
|
||||||
|
this.constraintDescriptions = new ConstraintDescriptions(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FieldDescriptor withPath(String path) {
|
||||||
|
return fieldWithPath(path)
|
||||||
|
.attributes(key("constraints")
|
||||||
|
.value(collectionToDelimitedString(this.constraintDescriptions.descriptionsForProperty(path), ". ")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.jayway.jsonpath.JsonPath;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||||
|
import org.springframework.hateoas.MediaTypes;
|
||||||
|
import org.springframework.restdocs.RestDocumentation;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||||
|
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
|
||||||
|
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
|
||||||
|
import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@SpringApplicationConfiguration(classes = SpringRestDocsApplication.class)
|
||||||
|
@WebAppConfiguration
|
||||||
|
public class GettingStartedDocumentation {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WebApplicationContext context;
|
||||||
|
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||||
|
.apply(documentationConfiguration(this.restDocumentation))
|
||||||
|
.alwaysDo(document("{method-name}/{step}/",
|
||||||
|
preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void index() throws Exception {
|
||||||
|
this.mockMvc.perform(get("/").accept(MediaTypes.HAL_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("_links.crud", is(notNullValue())))
|
||||||
|
.andExpect(jsonPath("_links.crud", is(notNullValue())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// String createNote() throws Exception {
|
||||||
|
// Map<String, String> note = new HashMap<String, String>();
|
||||||
|
// note.put("title", "Note creation with cURL");
|
||||||
|
// note.put("body", "An example of how to create a note using curl");
|
||||||
|
// String noteLocation = this.mockMvc.perform(post("/crud")
|
||||||
|
// .contentType(MediaTypes.HAL_JSON)
|
||||||
|
// .content(objectMapper.writeValueAsString(note)))
|
||||||
|
// .andExpect(status().isCreated())
|
||||||
|
// .andExpect(header().string("Location", notNullValue()))
|
||||||
|
// .andReturn()
|
||||||
|
// .getResponse()
|
||||||
|
// .getHeader("Location");
|
||||||
|
// return noteLocation;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// MvcResult getNote(String noteLocation) throws Exception {
|
||||||
|
// return this.mockMvc.perform(get(noteLocation))
|
||||||
|
// .andExpect(status().isOk())
|
||||||
|
// .andExpect(jsonPath("title", is(notNullValue())))
|
||||||
|
// .andExpect(jsonPath("body", is(notNullValue())))
|
||||||
|
// .andExpect(jsonPath("_links.crud", is(notNullValue())))
|
||||||
|
// .andReturn();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// String createTag() throws Exception, JsonProcessingException {
|
||||||
|
// Map<String, String> tag = new HashMap<String, String>();
|
||||||
|
// tag.put("name", "getting-started");
|
||||||
|
// String tagLocation = this.mockMvc.perform(post("/crud")
|
||||||
|
// .contentType(MediaTypes.HAL_JSON)
|
||||||
|
// .content(objectMapper.writeValueAsString(tag)))
|
||||||
|
// .andExpect(status().isCreated())
|
||||||
|
// .andExpect(header().string("Location", notNullValue()))
|
||||||
|
// .andReturn()
|
||||||
|
// .getResponse()
|
||||||
|
// .getHeader("Location");
|
||||||
|
// return tagLocation;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void getTag(String tagLocation) throws Exception {
|
||||||
|
// this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk())
|
||||||
|
// .andExpect(jsonPath("name", is(notNullValue())))
|
||||||
|
// .andExpect(jsonPath("_links.tagged-notes", is(notNullValue())));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// String createTaggedNote(String tag) throws Exception {
|
||||||
|
// Map<String, Object> note = new HashMap<String, Object>();
|
||||||
|
// note.put("title", "Tagged note creation with cURL");
|
||||||
|
// note.put("body", "An example of how to create a tagged note using cURL");
|
||||||
|
// note.put("tags", Arrays.asList(tag));
|
||||||
|
//
|
||||||
|
// String noteLocation = this.mockMvc.perform(post("/notes")
|
||||||
|
// .contentType(MediaTypes.HAL_JSON)
|
||||||
|
// .content(objectMapper.writeValueAsString(note)))
|
||||||
|
// .andExpect(status().isCreated())
|
||||||
|
// .andExpect(header().string("Location", notNullValue()))
|
||||||
|
// .andReturn()
|
||||||
|
// .getResponse()
|
||||||
|
// .getHeader("Location");
|
||||||
|
// return noteLocation;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void getTags(String noteTagsLocation) throws Exception {
|
||||||
|
// this.mockMvc.perform(get(noteTagsLocation))
|
||||||
|
// .andExpect(status().isOk())
|
||||||
|
// .andExpect(jsonPath("_embedded.tags", hasSize(1)));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void tagExistingNote(String noteLocation, String tagLocation) throws Exception {
|
||||||
|
// Map<String, Object> update = new HashMap<String, Object>();
|
||||||
|
// update.put("tags", Arrays.asList(tagLocation));
|
||||||
|
// this.mockMvc.perform(patch(noteLocation)
|
||||||
|
// .contentType(MediaTypes.HAL_JSON)
|
||||||
|
// .content(objectMapper.writeValueAsString(update)))
|
||||||
|
// .andExpect(status().isNoContent());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// MvcResult getTaggedExistingNote(String noteLocation) throws Exception {
|
||||||
|
// return this.mockMvc.perform(get(noteLocation)).andExpect(status().isOk()).andReturn();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void getTagsForExistingNote(String noteTagsLocation) throws Exception {
|
||||||
|
// this.mockMvc.perform(get(noteTagsLocation))
|
||||||
|
// .andExpect(status().isOk()).andExpect(jsonPath("_embedded.tags", hasSize(1)));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private String getLink(MvcResult result, String rel)
|
||||||
|
// throws UnsupportedEncodingException {
|
||||||
|
// return JsonPath.parse(result.getResponse().getContentAsString()).read("_links." + rel + ".href");
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue