All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cosium.hal_mock_mvc.HalMockMvc Maven / Gradle / Ivy

The newest version!
package com.cosium.hal_mock_mvc;

import static com.cosium.hal_mock_mvc.HalCompatibleContentTypeMatcher.contentTypeIsCompatibleWithHal;
import static java.util.Objects.requireNonNull;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.client.LinkDiscoverer;
import org.springframework.hateoas.mediatype.hal.forms.HalFormsLinkDiscoverer;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

/**
 *
 *
 * 

HAL links browsing

* *
{@code
 * HalMockMvc.builder(mockMvc).baseUri(linkTo(methodOn(MyController.class).get()).toUri())
 *   .build()
 *   .follow("collection")
 *   .get()
 *   .andExpect(status().isOk())
 *   .andExpect(jsonPath("$._embedded.content.length()").value(1))
 *   .andExpect(jsonPath("$._embedded.content[0].name").value("foo"));
 * }
* *

HAL forms submission

* *
{@code
 * HalMockMvc.builder(mockMvc).baseUri(linkTo(methodOn(MyController.class).list()).toUri())
 * .build()
 * .follow()
 * .templates()
 * .byKey("create")
 * .submit(
 *   JSON.std
 *       .composeString()
 *       .startObject()
 *       .put("name", "john")
 *       .end()
 *       .finish()
 * )
 * .andExpect(status().isCreated());
 * }
* * @author Réda Housni Alaoui */ public class HalMockMvc { public static final String DEFAULT_BASE_URI = "/"; private final MockMvc mockMvc; private final String baseUri; private final List requestPostProcessors; private final MultiValueMap headers; private HalMockMvc( MockMvc mockMvc, String baseUri, List requestPostProcessors, MultiValueMap headers) { this.mockMvc = requireNonNull(mockMvc); this.baseUri = requireNonNull(baseUri); this.requestPostProcessors = List.copyOf(requestPostProcessors); this.headers = CollectionUtils.unmodifiableMultiValueMap(new LinkedMultiValueMap<>(headers)); } public static Builder builder(MockMvc mockMvc) { return new Builder(mockMvc); } public Builder toBuilder() { return new Builder(this); } public String baseUri() { return baseUri; } public TraversalBuilder follow(String... relations) { return new TraversalBuilder(mockMvc, baseUri, requestPostProcessors, new HttpHeaders(headers)) .follow(relations); } public TraversalBuilder follow(Hop relation) { return new TraversalBuilder(mockMvc, baseUri, requestPostProcessors, new HttpHeaders(headers)) .follow(relation); } /** * The traversal builder allows to navigate to a final endpoint through a sequence of relations */ public static class TraversalBuilder { private final RequestExecutor requestExecutor; private final String baseUri; private final LinkDiscoverer halLinkDiscoverer = new HalFormsLinkDiscoverer(); private final List hops = new ArrayList<>(); private TraversalBuilder( MockMvc mockMvc, String baseUri, List postProcessors, HttpHeaders httpHeaders) { this.requestExecutor = new RequestExecutor(mockMvc, postProcessors, httpHeaders); this.baseUri = baseUri; } public TraversalBuilder follow(String... relation) { hops.addAll(Stream.of(relation).map(Hop::relation).toList()); return this; } public TraversalBuilder follow(Hop relation) { hops.add(relation); return this; } /** * @return The HAL-FORMS templates provided by the final endpoint. */ public Templates templates() throws Exception { return new Templates(requestExecutor, get()); } /** * Begins the creation of a multipart request targeting the final endpoint * * @see MockMvcRequestBuilders#multipart(URI) */ public MultipartRequest multipartRequest() throws Exception { return new MultipartRequest(requestExecutor, fetchTargetUri()); } /** * Begins the creation of a request targeting the final endpoint * * @see MockMvcRequestBuilders#request(String, URI) */ public Request request() throws Exception { return new Request(requestExecutor, fetchTargetUri()); } /** * Begins the creation of a GET request targeting the final endpoint * * @see MockMvcRequestBuilders#get(URI) */ public ResultActions get() throws Exception { return request().get(); } /** * Begins the creation of a POST request targeting the final endpoint * * @see MockMvcRequestBuilders#post(URI) */ public ResultActions post() throws Exception { return request().post(); } /** * Begins the creation of a POST request with a JSON payload targeting the final endpoint * * @see MockMvcRequestBuilders#post(URI) */ public ResultActions post(String jsonContent) throws Exception { return request().jsonContent(jsonContent).post(); } /** * Begins the creation of a PUT request with a JSON payload targeting the final endpoint * * @see MockMvcRequestBuilders#put(URI) */ public ResultActions put(String jsonContent) throws Exception { return request().jsonContent(jsonContent).put(); } /** * Begins the creation of a PUT request targeting the final endpoint * * @see MockMvcRequestBuilders#put(URI) */ public ResultActions put() throws Exception { return request().put(); } /** * Begins the creation of a PATCH request with a JSON payload targeting the final endpoint * * @see MockMvcRequestBuilders#patch(URI) */ public ResultActions patch(String jsonContent) throws Exception { return request().jsonContent(jsonContent).patch(); } /** * Begins the creation of a PATCH request targeting the final endpoint * * @see MockMvcRequestBuilders#patch(URI) */ public ResultActions patch() throws Exception { return request().patch(); } /** * Begins the creation of a DELETE request targeting the final endpoint * * @see MockMvcRequestBuilders#patch(URI) */ public ResultActions delete() throws Exception { return request().delete(); } private URI fetchTargetUri() throws Exception { URI targetUri = URI.create(baseUri); for (Hop hop : hops) { ResultActions requestResult = requestExecutor.execute(MockMvcRequestBuilders.get(targetUri)); requestResult.andExpect(contentTypeIsCompatibleWithHal()); MockHttpServletResponse response = requestResult.andReturn().getResponse(); String body = response.getContentAsString(); int responseStatus = response.getStatus(); if (responseStatus < 200 || responseStatus >= 400) { throw new IllegalStateException( "GET on " + targetUri + " failed with code " + responseStatus + " and body '" + body + "'"); } Link link = halLinkDiscoverer.findLinkWithRel(hop.relationName(), body).orElse(null); if (link == null) { throw new IllegalArgumentException( "Could not find relation " + hop + " at URI " + requestResult.andReturn().getRequest().getRequestURI()); } targetUri = link.expand(hop.parameters()).toUri(); } return targetUri; } } public static class Builder { private final MockMvc mockMvc; private String baseUri; private final List requestPostProcessors; private final MultiValueMap headers; private Builder(MockMvc mockMvc) { this(mockMvc, DEFAULT_BASE_URI, List.of(), new LinkedMultiValueMap<>()); } private Builder(HalMockMvc halMockMvc) { this( halMockMvc.mockMvc, halMockMvc.baseUri, halMockMvc.requestPostProcessors, halMockMvc.headers); } private Builder( MockMvc mockMvc, String baseUri, List requestPostProcessors, MultiValueMap headers) { this.mockMvc = mockMvc; this.baseUri = baseUri; this.requestPostProcessors = new ArrayList<>(requestPostProcessors); this.headers = new LinkedMultiValueMap<>(headers); } /** * @param baseUri The URI from which the traversal will begin */ public Builder baseUri(String baseUri) { this.baseUri = baseUri; return this; } /** * @param baseUri The URI from which the traversal will begin */ public Builder baseUri(URI baseUri) { this.baseUri = baseUri.toString(); return this; } public Builder addRequestPostProcessor(RequestPostProcessor requestPostProcessor) { requestPostProcessors.add(requestPostProcessor); return this; } public Builder requestPostProcessors(List requestPostProcessors) { this.requestPostProcessors.clear(); this.requestPostProcessors.addAll(requestPostProcessors); return this; } /** * @param name The header name * @param values The header values */ public Builder header(String name, String... values) { headers.put(name, List.of(values)); return this; } public Builder headers(MultiValueMap headers) { this.headers.clear(); this.headers.addAll(headers); return this; } public Builder headers(HttpHeaders headers) { this.headers.clear(); this.headers.addAll(headers); return this; } public HalMockMvc build() { return new HalMockMvc(mockMvc, baseUri, requestPostProcessors, headers); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy