argumentresolver example

This commit is contained in:
thombergs
2020-03-01 09:20:29 +11:00
committed by akuksin
parent 16bdac5240
commit b7dc19cb8d
11 changed files with 87 additions and 27 deletions

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -3,13 +3,12 @@ package io.reflectoring.argumentresolver;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.client.HttpStatusCodeException;
@ControllerAdvice @ControllerAdvice
class ErrorHandler { class ErrorHandler {
@ExceptionHandler(HttpStatusCodeException.class) @ExceptionHandler(NotFoundException.class)
ResponseEntity<?> handleHttpStatusCodeException(HttpStatusCodeException e) { ResponseEntity<?> handleHttpStatusCodeException(NotFoundException e) {
return ResponseEntity.status(e.getStatusCode()).build(); return ResponseEntity.status(e.getStatusCode()).build();
} }

View File

@@ -4,7 +4,7 @@ package io.reflectoring.argumentresolver;
import lombok.Value; import lombok.Value;
@Value @Value
public class Repository { class Repository {
private final Long id; private final Long id;
private final String slug; private final String slug;

View File

@@ -1,7 +1,6 @@
package io.reflectoring.argumentresolver; package io.reflectoring.argumentresolver;
import java.util.Optional; import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
@@ -14,8 +13,6 @@ import org.springframework.web.method.support.ModelAndViewContainer;
@RequiredArgsConstructor @RequiredArgsConstructor
class RepositoryArgumentResolver implements HandlerMethodArgumentResolver { class RepositoryArgumentResolver implements HandlerMethodArgumentResolver {
private static final Pattern SLUG_PATTERN = Pattern.compile("^/([^/]*).*$");
private final RepositoryFinder repositoryFinder; private final RepositoryFinder repositoryFinder;
@Override @Override
@@ -32,20 +29,9 @@ class RepositoryArgumentResolver implements HandlerMethodArgumentResolver {
String requestPath = ((ServletWebRequest) webRequest).getRequest().getPathInfo(); String requestPath = ((ServletWebRequest) webRequest).getRequest().getPathInfo();
Matcher matcher = SLUG_PATTERN.matcher(requestPath); String slug = requestPath
.substring(0, requestPath.indexOf("/", 1))
if (!matcher.matches()) { .replaceAll("^/", "");
throw new IllegalArgumentException(String.format(
"Cannot resolve argument of type Site. Expecting the slug to be the first part of the request path (%s).",
requestPath));
}
String slug = matcher.group(1);
if (slug == null || slug.isBlank()) {
throw new IllegalArgumentException(String.format(
"Cannot resolve argument of type Site. Slug is empty (request path: %s).",
requestPath));
}
Optional<Repository> repository = repositoryFinder.findBySlug(slug); Optional<Repository> repository = repositoryFinder.findBySlug(slug);

View File

@@ -0,0 +1,10 @@
package io.reflectoring.argumentresolver;
import lombok.Value;
@Value
class RepositoryId {
private final long value;
}

View File

@@ -0,0 +1,13 @@
package io.reflectoring.argumentresolver;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
@Component
class RepositoryIdConverter implements Converter<String, RepositoryId> {
@Override
public RepositoryId convert(String source) {
return new RepositoryId(Long.parseLong(source));
}
}

View File

@@ -11,7 +11,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
@WebMvcTest(controllers = TestController.class) @WebMvcTest(controllers = RepositoryArgumentResolverTestController.class)
class RepositoryArgumentResolverTest { class RepositoryArgumentResolverTest {
@Autowired @Autowired
@@ -26,7 +26,7 @@ class RepositoryArgumentResolverTest {
given(repositoryFinder.findBySlug("my-repo")) given(repositoryFinder.findBySlug("my-repo"))
.willReturn(Optional.of(new Repository(1L, "my-repo"))); .willReturn(Optional.of(new Repository(1L, "my-repo")));
mockMvc.perform(get("/my-repo/foo")) mockMvc.perform(get("/my-repo/listContributors"))
.andExpect(status().isOk()); .andExpect(status().isOk());
} }
@@ -36,7 +36,7 @@ class RepositoryArgumentResolverTest {
given(repositoryFinder.findBySlug("unknownSlug")) given(repositoryFinder.findBySlug("unknownSlug"))
.willReturn(Optional.empty()); .willReturn(Optional.empty());
mockMvc.perform(get("/unknownSlug/foo")) mockMvc.perform(get("/unknownSlug/listContributors"))
.andExpect(status().isNotFound()); .andExpect(status().isNotFound());
} }

View File

@@ -3,13 +3,15 @@ package io.reflectoring.argumentresolver;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
class TestController { @RequestMapping(path = "/{repositorySlug}")
class RepositoryArgumentResolverTestController {
@GetMapping("/{slug}/foo") @GetMapping("/listContributors")
String getSomething(Repository repository) { String listContributors(Repository repository) {
assertThat(repository.getId()).isEqualTo(1L); assertThat(repository.getId()).isEqualTo(1L);
return "test"; return "test";
} }

View File

@@ -0,0 +1,27 @@
package io.reflectoring.argumentresolver;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
@WebMvcTest(controllers = RepositoryIdConverterTestController.class)
class RepositoryIdConverterTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private RepositoryFinder repositoryFinder;
@Test
void resolvesRepositoryId() throws Exception {
mockMvc.perform(get("/repositories/42"))
.andExpect(status().isOk());
}
}

View File

@@ -0,0 +1,18 @@
package io.reflectoring.argumentresolver;
import static org.assertj.core.api.Assertions.assertThat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
class RepositoryIdConverterTestController {
@GetMapping("/repositories/{repositoryId}")
String getRepository(@PathVariable("repositoryId") RepositoryId repositoryId) {
assertThat(repositoryId).isNotNull();
return "test";
}
}