Merge branch 'master' into master
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
= 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-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
|
||||
|
||||
==== Request structure
|
||||
|
||||
include::{snippets}/index-example/http-request.adoc[]
|
||||
|
||||
==== Example response
|
||||
|
||||
include::{snippets}/index-example/http-response.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-get]]
|
||||
=== Accessing the crud GET
|
||||
|
||||
A `GET` request is used to access the CRUD read.
|
||||
|
||||
==== Request 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-post]]
|
||||
=== Accessing the crud POST
|
||||
|
||||
A `POST` request is used to access the CRUD create.
|
||||
|
||||
==== Request 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-delete]]
|
||||
=== Accessing the crud DELETE
|
||||
|
||||
A `DELETE` request is used to access the CRUD delete.
|
||||
|
||||
==== Request structure
|
||||
|
||||
include::{snippets}/crud-delete-example/http-request.adoc[]
|
||||
|
||||
==== Path Parameters
|
||||
include::{snippets}/crud-delete-example/path-parameters.adoc[]
|
||||
|
||||
==== Example response
|
||||
|
||||
include::{snippets}/crud-delete-example/http-response.adoc[]
|
||||
|
||||
==== CURL request
|
||||
|
||||
include::{snippets}/crud-delete-example/curl-request.adoc[]
|
||||
|
||||
[[resources-crud-patch]]
|
||||
=== Accessing the crud PATCH
|
||||
|
||||
A `PATCH` request is used to access the CRUD update.
|
||||
|
||||
==== Request 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-put]]
|
||||
=== Accessing the crud PUT
|
||||
|
||||
A `PUT` request is used to access the CRUD update.
|
||||
|
||||
==== Request 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[]
|
||||
|
||||
|
||||
|
||||
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
package com.baeldung.functional;
|
||||
|
||||
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
|
||||
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
|
||||
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
|
||||
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
|
||||
public class ExploreSpring5URLPatternUsingRouterFunctions {
|
||||
|
||||
private RouterFunction<ServerResponse> routingFunction() {
|
||||
|
||||
return route(GET("/p?ths"), serverRequest -> ok().body(fromObject("/p?ths"))).andRoute(GET("/test/{*id}"), serverRequest -> ok().body(fromObject(serverRequest.pathVariable("id"))))
|
||||
.andRoute(GET("/*card"), serverRequest -> ok().body(fromObject("/*card path was accessed")))
|
||||
.andRoute(GET("/{var1}_{var2}"), serverRequest -> ok().body(fromObject(serverRequest.pathVariable("var1") + " , " + serverRequest.pathVariable("var2"))))
|
||||
.andRoute(GET("/{baeldung:[a-z]+}"), serverRequest -> ok().body(fromObject("/{baeldung:[a-z]+} was accessed and baeldung=" + serverRequest.pathVariable("baeldung"))))
|
||||
.and(RouterFunctions.resources("/files/{*filepaths}", new ClassPathResource("files/")));
|
||||
}
|
||||
|
||||
WebServer start() throws Exception {
|
||||
WebHandler webHandler = (WebHandler) toHttpHandler(routingFunction());
|
||||
HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(webHandler)
|
||||
.filter(new IndexRewriteFilter())
|
||||
.build();
|
||||
|
||||
Tomcat tomcat = new Tomcat();
|
||||
tomcat.setHostname("localhost");
|
||||
tomcat.setPort(9090);
|
||||
Context rootContext = tomcat.addContext("", System.getProperty("java.io.tmpdir"));
|
||||
ServletHttpHandlerAdapter servlet = new ServletHttpHandlerAdapter(httpHandler);
|
||||
Tomcat.addServlet(rootContext, "httpHandlerServlet", servlet);
|
||||
rootContext.addServletMappingDecoded("/", "httpHandlerServlet");
|
||||
|
||||
TomcatWebServer server = new TomcatWebServer(tomcat);
|
||||
server.start();
|
||||
return server;
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
new FunctionalWebApplication().start();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.baeldung.restdocs;
|
||||
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PatchMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/crud")
|
||||
public class CRUDController {
|
||||
|
||||
@GetMapping
|
||||
public List<CrudInput> read(@RequestBody @Valid CrudInput crudInput) {
|
||||
List<CrudInput> returnList = new ArrayList<>();
|
||||
returnList.add(crudInput);
|
||||
return returnList;
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
@PostMapping
|
||||
public HttpHeaders save(@RequestBody @Valid CrudInput crudInput) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setLocation(linkTo(CRUDController.class).slash(crudInput.getId()).toUri());
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
HttpHeaders delete(@PathVariable("id") long id) {
|
||||
return new HttpHeaders();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@ResponseStatus(HttpStatus.ACCEPTED)
|
||||
void put(@PathVariable("id") long id, @RequestBody CrudInput crudInput) {
|
||||
|
||||
}
|
||||
|
||||
@PatchMapping("/{id}")
|
||||
public List<CrudInput> patch(@PathVariable("id") long id, @RequestBody CrudInput crudInput) {
|
||||
List<CrudInput> returnList = new ArrayList<CrudInput>();
|
||||
crudInput.setId(id);
|
||||
returnList.add(crudInput);
|
||||
return returnList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.baeldung.restdocs;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class CrudInput {
|
||||
|
||||
@NotNull
|
||||
private long id;
|
||||
|
||||
@NotBlank
|
||||
private String title;
|
||||
|
||||
private String body;
|
||||
|
||||
private List<URI> tagUris;
|
||||
|
||||
@JsonCreator
|
||||
public CrudInput(@JsonProperty("id") long id, @JsonProperty("title") String title, @JsonProperty("body") String body, @JsonProperty("tags") List<URI> tagUris) {
|
||||
this.id=id;
|
||||
this.title = title;
|
||||
this.body = body;
|
||||
this.tagUris = tagUris == null ? Collections.<URI> emptyList() : tagUris;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public void setBody(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public void setTagUris(List<URI> tagUris) {
|
||||
this.tagUris = tagUris;
|
||||
}
|
||||
|
||||
@JsonProperty("tags")
|
||||
public List<URI> getTagUris() {
|
||||
return this.tagUris;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.baeldung.restdocs;
|
||||
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/")
|
||||
public class IndexController {
|
||||
|
||||
@GetMapping
|
||||
public ResourceSupport index() {
|
||||
ResourceSupport index = new ResourceSupport();
|
||||
index.add(linkTo(CRUDController.class).withRel("crud"));
|
||||
return index;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.baeldung.restdocs;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SpringRestDocsApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringRestDocsApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.baeldung.web;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class PathPatternController {
|
||||
|
||||
@GetMapping("/spring5/{*id}")
|
||||
public String URIVariableHandler(@PathVariable String id) {
|
||||
return id;
|
||||
}
|
||||
|
||||
@GetMapping("/s?ring5")
|
||||
public String wildcardTakingExactlyOneChar() {
|
||||
return "/s?ring5";
|
||||
}
|
||||
|
||||
@GetMapping("/spring5/*id")
|
||||
public String wildcardTakingZeroOrMoreChar() {
|
||||
return "/spring5/*id";
|
||||
}
|
||||
|
||||
@GetMapping("/resources/**")
|
||||
public String wildcardTakingZeroOrMorePathSegments() {
|
||||
return "/resources/**";
|
||||
}
|
||||
|
||||
@GetMapping("/{baeldung:[a-z]+}")
|
||||
public String regexInPathVariable(@PathVariable String baeldung) {
|
||||
return baeldung;
|
||||
}
|
||||
|
||||
@GetMapping("/{var1}_{var2}")
|
||||
public String multiplePathVariablesInSameSegment(@PathVariable String var1, @PathVariable String var2) {
|
||||
return "Two variables are var1=" + var1 + " and var2=" + var2;
|
||||
}
|
||||
}
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
package com.baeldung.functional;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
public class ExploreSpring5URLPatternUsingRouterFunctionsTest {
|
||||
|
||||
private static WebTestClient client;
|
||||
private static WebServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() throws Exception {
|
||||
server = new ExploreSpring5URLPatternUsingRouterFunctions().start();
|
||||
client = WebTestClient.bindToServer()
|
||||
.baseUrl("http://localhost:" + server.getPort())
|
||||
.build();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void destroy() {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRouter_whenGetPathWithSingleCharWildcard_thenGotPathPattern() throws Exception {
|
||||
client.get()
|
||||
.uri("/paths")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("/p?ths");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRouter_whenMultipleURIVariablePattern_thenGotPathVariable() throws Exception {
|
||||
client.get()
|
||||
.uri("/test/ab/cd")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("/ab/cd");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRouter_whenGetMultipleCharWildcard_thenGotPathPattern() throws Exception {
|
||||
|
||||
client.get()
|
||||
.uri("/wildcard")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("/*card path was accessed");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRouter_whenGetMultiplePathVaribleInSameSegment_thenGotPathVariables() throws Exception {
|
||||
|
||||
client.get()
|
||||
.uri("/baeldung_tutorial")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("baeldung , tutorial");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRouter_whenGetRegexInPathVarible_thenGotPathVariable() throws Exception {
|
||||
|
||||
client.get()
|
||||
.uri("/abcd")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("/{baeldung:[a-z]+} was accessed and baeldung=abcd");
|
||||
|
||||
client.get()
|
||||
.uri("/1234")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is4xxClientError();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenResources_whenAccess_thenGot() throws Exception {
|
||||
client.get()
|
||||
.uri("/files/test/test.txt")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("test");
|
||||
|
||||
client.get()
|
||||
.uri("/files/hello.txt")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("hello");
|
||||
}
|
||||
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
package com.baeldung.restdocs;
|
||||
|
||||
import com.baeldung.restdocs.CrudInput;
|
||||
import com.baeldung.restdocs.SpringRestDocsApplication;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
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.context.SpringBootTest;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.restdocs.JUnitRestDocumentation;
|
||||
import org.springframework.restdocs.constraints.ConstraintDescriptions;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
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.links;
|
||||
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.preprocessRequest;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.util.StringUtils.collectionToDelimitedString;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = SpringRestDocsApplication.class)
|
||||
public class ApiDocumentationJUnit4IntegrationTest {
|
||||
|
||||
@Rule
|
||||
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||
.apply(documentationConfiguration(this.restDocumentation))
|
||||
.alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexExample() throws Exception {
|
||||
this.mockMvc.perform(get("/"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("index-example", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), links(linkWithRel("crud").description("The CRUD resource")), responseFields(subsectionWithPath("_links").description("Links to other resources")),
|
||||
responseHeaders(headerWithName("Content-Type").description("The Content-Type of the payload, e.g. `application/hal+json`"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crudGetExample() throws Exception {
|
||||
|
||||
Map<String, Object> crud = new HashMap<>();
|
||||
crud.put("id", 1L);
|
||||
crud.put("title", "Sample Model");
|
||||
crud.put("body", "http://www.baeldung.com/");
|
||||
|
||||
String tagLocation = this.mockMvc.perform(get("/crud").contentType(MediaTypes.HAL_JSON)
|
||||
.content(this.objectMapper.writeValueAsString(crud)))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn()
|
||||
.getResponse()
|
||||
.getHeader("Location");
|
||||
|
||||
crud.put("tags", singletonList(tagLocation));
|
||||
|
||||
ConstraintDescriptions desc = new ConstraintDescriptions(CrudInput.class);
|
||||
|
||||
this.mockMvc.perform(get("/crud").contentType(MediaTypes.HAL_JSON)
|
||||
.content(this.objectMapper.writeValueAsString(crud)))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("crud-get-example", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestFields(fieldWithPath("id").description("The id of the input" + collectionToDelimitedString(desc.descriptionsForProperty("id"), ". ")),
|
||||
fieldWithPath("title").description("The title of the input"), fieldWithPath("body").description("The body of the input"), fieldWithPath("tags").description("An array of tag resource URIs"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crudCreateExample() throws Exception {
|
||||
Map<String, Object> crud = new HashMap<>();
|
||||
crud.put("id", 2L);
|
||||
crud.put("title", "Sample Model");
|
||||
crud.put("body", "http://www.baeldung.com/");
|
||||
|
||||
String tagLocation = this.mockMvc.perform(post("/crud").contentType(MediaTypes.HAL_JSON)
|
||||
.content(this.objectMapper.writeValueAsString(crud)))
|
||||
.andExpect(status().isCreated())
|
||||
.andReturn()
|
||||
.getResponse()
|
||||
.getHeader("Location");
|
||||
|
||||
crud.put("tags", singletonList(tagLocation));
|
||||
|
||||
this.mockMvc.perform(post("/crud").contentType(MediaTypes.HAL_JSON)
|
||||
.content(this.objectMapper.writeValueAsString(crud)))
|
||||
.andExpect(status().isCreated())
|
||||
.andDo(document("crud-create-example", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestFields(fieldWithPath("id").description("The id of the input"), fieldWithPath("title").description("The title of the input"),
|
||||
fieldWithPath("body").description("The body of the input"), fieldWithPath("tags").description("An array of tag resource URIs"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crudDeleteExample() throws Exception {
|
||||
this.mockMvc.perform(delete("/crud/{id}", 10))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("crud-delete-example", pathParameters(parameterWithName("id").description("The id of the input to delete"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crudPatchExample() throws Exception {
|
||||
|
||||
Map<String, String> tag = new HashMap<>();
|
||||
tag.put("name", "PATCH");
|
||||
|
||||
String tagLocation = this.mockMvc.perform(patch("/crud/{id}", 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 Patch");
|
||||
crud.put("body", "http://www.baeldung.com/");
|
||||
crud.put("tags", singletonList(tagLocation));
|
||||
|
||||
this.mockMvc.perform(patch("/crud/{id}", 10).contentType(MediaTypes.HAL_JSON)
|
||||
.content(this.objectMapper.writeValueAsString(crud)))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crudPutExample() throws Exception {
|
||||
Map<String, String> tag = new HashMap<>();
|
||||
tag.put("name", "PUT");
|
||||
|
||||
String tagLocation = this.mockMvc.perform(put("/crud/{id}", 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/{id}", 10).contentType(MediaTypes.HAL_JSON)
|
||||
.content(this.objectMapper.writeValueAsString(crud)))
|
||||
.andExpect(status().isAccepted());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
package com.baeldung.restdocs;
|
||||
|
||||
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.links;
|
||||
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.preprocessRequest;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.util.StringUtils.collectionToDelimitedString;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.restdocs.RestDocumentationContextProvider;
|
||||
import org.springframework.restdocs.RestDocumentationExtension;
|
||||
import org.springframework.restdocs.constraints.ConstraintDescriptions;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import com.baeldung.restdocs.CrudInput;
|
||||
import com.baeldung.restdocs.SpringRestDocsApplication;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@ExtendWith({ RestDocumentationExtension.class, SpringExtension.class })
|
||||
@SpringBootTest(classes = SpringRestDocsApplication.class)
|
||||
public class ApiDocumentationJUnit5IntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
|
||||
.apply(documentationConfiguration(restDocumentation))
|
||||
.alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexExample() throws Exception {
|
||||
this.mockMvc.perform(get("/"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("index-example", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), links(linkWithRel("crud").description("The CRUD resource")), responseFields(subsectionWithPath("_links").description("Links to other resources")),
|
||||
responseHeaders(headerWithName("Content-Type").description("The Content-Type of the payload, e.g. `application/hal+json`"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crudGetExample() throws Exception {
|
||||
|
||||
Map<String, Object> crud = new HashMap<>();
|
||||
crud.put("id", 1L);
|
||||
crud.put("title", "Sample Model");
|
||||
crud.put("body", "http://www.baeldung.com/");
|
||||
|
||||
String tagLocation = this.mockMvc.perform(get("/crud").contentType(MediaTypes.HAL_JSON)
|
||||
.content(this.objectMapper.writeValueAsString(crud)))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn()
|
||||
.getResponse()
|
||||
.getHeader("Location");
|
||||
|
||||
crud.put("tags", singletonList(tagLocation));
|
||||
|
||||
ConstraintDescriptions desc = new ConstraintDescriptions(CrudInput.class);
|
||||
|
||||
this.mockMvc.perform(get("/crud").contentType(MediaTypes.HAL_JSON)
|
||||
.content(this.objectMapper.writeValueAsString(crud)))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("crud-get-example", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestFields(fieldWithPath("id").description("The id of the input" + collectionToDelimitedString(desc.descriptionsForProperty("id"), ". ")),
|
||||
fieldWithPath("title").description("The title of the input"), fieldWithPath("body").description("The body of the input"), fieldWithPath("tags").description("An array of tag resource URIs"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crudCreateExample() throws Exception {
|
||||
Map<String, Object> crud = new HashMap<>();
|
||||
crud.put("id", 2L);
|
||||
crud.put("title", "Sample Model");
|
||||
crud.put("body", "http://www.baeldung.com/");
|
||||
|
||||
String tagLocation = this.mockMvc.perform(post("/crud").contentType(MediaTypes.HAL_JSON)
|
||||
.content(this.objectMapper.writeValueAsString(crud)))
|
||||
.andExpect(status().isCreated())
|
||||
.andReturn()
|
||||
.getResponse()
|
||||
.getHeader("Location");
|
||||
|
||||
crud.put("tags", singletonList(tagLocation));
|
||||
|
||||
this.mockMvc.perform(post("/crud").contentType(MediaTypes.HAL_JSON)
|
||||
.content(this.objectMapper.writeValueAsString(crud)))
|
||||
.andExpect(status().isCreated())
|
||||
.andDo(document("crud-create-example", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestFields(fieldWithPath("id").description("The id of the input"), fieldWithPath("title").description("The title of the input"),
|
||||
fieldWithPath("body").description("The body of the input"), fieldWithPath("tags").description("An array of tag resource URIs"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crudDeleteExample() throws Exception {
|
||||
this.mockMvc.perform(delete("/crud/{id}", 10))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("crud-delete-example", pathParameters(parameterWithName("id").description("The id of the input to delete"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crudPatchExample() throws Exception {
|
||||
|
||||
Map<String, String> tag = new HashMap<>();
|
||||
tag.put("name", "PATCH");
|
||||
|
||||
String tagLocation = this.mockMvc.perform(patch("/crud/{id}", 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 Patch");
|
||||
crud.put("body", "http://www.baeldung.com/");
|
||||
crud.put("tags", singletonList(tagLocation));
|
||||
|
||||
this.mockMvc.perform(patch("/crud/{id}", 10).contentType(MediaTypes.HAL_JSON)
|
||||
.content(this.objectMapper.writeValueAsString(crud)))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crudPutExample() throws Exception {
|
||||
Map<String, String> tag = new HashMap<>();
|
||||
tag.put("name", "PUT");
|
||||
|
||||
String tagLocation = this.mockMvc.perform(put("/crud/{id}", 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/{id}", 10).contentType(MediaTypes.HAL_JSON)
|
||||
.content(this.objectMapper.writeValueAsString(crud)))
|
||||
.andExpect(status().isAccepted());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
package com.baeldung.web;
|
||||
|
||||
import com.baeldung.Spring5Application;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = Spring5Application.class)
|
||||
public class PathPatternsUsingHandlerMethodIntegrationTest {
|
||||
|
||||
private static WebTestClient client;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() {
|
||||
client = WebTestClient.bindToController(new PathPatternController())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenHandlerMethod_whenMultipleURIVariablePattern_then200() {
|
||||
|
||||
client.get()
|
||||
.uri("/spring5/ab/cd")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.equals("/ab/cd");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenHandlerMethod_whenURLWithWildcardTakingZeroOrMoreChar_then200() {
|
||||
|
||||
client.get()
|
||||
.uri("/spring5/userid")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.equals("/spring5/*id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenHandlerMethod_whenURLWithWildcardTakingExactlyOneChar_then200() {
|
||||
|
||||
client.get()
|
||||
.uri("/string5")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.equals("/s?ring5");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenHandlerMethod_whenURLWithWildcardTakingZeroOrMorePathSegments_then200() {
|
||||
|
||||
client.get()
|
||||
.uri("/resources/baeldung")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.equals("/resources/**");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenHandlerMethod_whenURLWithRegexInPathVariable_thenExpectedOutput() {
|
||||
|
||||
client.get()
|
||||
.uri("/abc")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.equals("abc");
|
||||
|
||||
client.get()
|
||||
.uri("/123")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is4xxClientError();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenHandlerMethod_whenURLWithMultiplePathVariablesInSameSegment_then200() {
|
||||
|
||||
client.get()
|
||||
.uri("/baeldung_tutorial")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is2xxSuccessful()
|
||||
.expectBody()
|
||||
.equals("Two variables are var1=baeldung and var2=tutorial");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user