diff --git a/spring-boot/argumentresolver/gradle/wrapper/gradle-wrapper.jar b/spring-boot/argumentresolver/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..29953ea Binary files /dev/null and b/spring-boot/argumentresolver/gradle/wrapper/gradle-wrapper.jar differ diff --git a/spring-boot/argumentresolver/gradle/wrapper/gradle-wrapper.properties b/spring-boot/argumentresolver/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..9492014 --- /dev/null +++ b/spring-boot/argumentresolver/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/ErrorHandler.java b/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/ErrorHandler.java index f0b6419..08152e6 100644 --- a/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/ErrorHandler.java +++ b/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/ErrorHandler.java @@ -3,13 +3,12 @@ package io.reflectoring.argumentresolver; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.client.HttpStatusCodeException; @ControllerAdvice class ErrorHandler { - @ExceptionHandler(HttpStatusCodeException.class) - ResponseEntity handleHttpStatusCodeException(HttpStatusCodeException e) { + @ExceptionHandler(NotFoundException.class) + ResponseEntity handleHttpStatusCodeException(NotFoundException e) { return ResponseEntity.status(e.getStatusCode()).build(); } diff --git a/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/Repository.java b/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/Repository.java index f9abb4d..9852896 100644 --- a/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/Repository.java +++ b/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/Repository.java @@ -4,7 +4,7 @@ package io.reflectoring.argumentresolver; import lombok.Value; @Value -public class Repository { +class Repository { private final Long id; private final String slug; diff --git a/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/RepositoryArgumentResolver.java b/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/RepositoryArgumentResolver.java index 331ec5d..67ad9be 100644 --- a/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/RepositoryArgumentResolver.java +++ b/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/RepositoryArgumentResolver.java @@ -1,7 +1,6 @@ package io.reflectoring.argumentresolver; import java.util.Optional; -import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; import org.springframework.core.MethodParameter; @@ -14,8 +13,6 @@ import org.springframework.web.method.support.ModelAndViewContainer; @RequiredArgsConstructor class RepositoryArgumentResolver implements HandlerMethodArgumentResolver { - private static final Pattern SLUG_PATTERN = Pattern.compile("^/([^/]*).*$"); - private final RepositoryFinder repositoryFinder; @Override @@ -32,20 +29,9 @@ class RepositoryArgumentResolver implements HandlerMethodArgumentResolver { String requestPath = ((ServletWebRequest) webRequest).getRequest().getPathInfo(); - Matcher matcher = SLUG_PATTERN.matcher(requestPath); - - if (!matcher.matches()) { - 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)); - } + String slug = requestPath + .substring(0, requestPath.indexOf("/", 1)) + .replaceAll("^/", ""); Optional repository = repositoryFinder.findBySlug(slug); diff --git a/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/RepositoryId.java b/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/RepositoryId.java new file mode 100644 index 0000000..f55ef68 --- /dev/null +++ b/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/RepositoryId.java @@ -0,0 +1,10 @@ +package io.reflectoring.argumentresolver; + +import lombok.Value; + +@Value +class RepositoryId { + + private final long value; + +} diff --git a/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/RepositoryIdConverter.java b/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/RepositoryIdConverter.java new file mode 100644 index 0000000..bc054a9 --- /dev/null +++ b/spring-boot/argumentresolver/src/main/java/io/reflectoring/argumentresolver/RepositoryIdConverter.java @@ -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 { + + @Override + public RepositoryId convert(String source) { + return new RepositoryId(Long.parseLong(source)); + } +} diff --git a/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryArgumentResolverTest.java b/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryArgumentResolverTest.java index ce14a4a..74d6fe8 100644 --- a/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryArgumentResolverTest.java +++ b/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryArgumentResolverTest.java @@ -11,7 +11,7 @@ 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 = TestController.class) +@WebMvcTest(controllers = RepositoryArgumentResolverTestController.class) class RepositoryArgumentResolverTest { @Autowired @@ -26,7 +26,7 @@ class RepositoryArgumentResolverTest { given(repositoryFinder.findBySlug("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()); } @@ -36,7 +36,7 @@ class RepositoryArgumentResolverTest { given(repositoryFinder.findBySlug("unknownSlug")) .willReturn(Optional.empty()); - mockMvc.perform(get("/unknownSlug/foo")) + mockMvc.perform(get("/unknownSlug/listContributors")) .andExpect(status().isNotFound()); } diff --git a/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/TestController.java b/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryArgumentResolverTestController.java similarity index 56% rename from spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/TestController.java rename to spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryArgumentResolverTestController.java index 4ed049a..39deda6 100644 --- a/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/TestController.java +++ b/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryArgumentResolverTestController.java @@ -3,13 +3,15 @@ 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.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController -class TestController { +@RequestMapping(path = "/{repositorySlug}") +class RepositoryArgumentResolverTestController { - @GetMapping("/{slug}/foo") - String getSomething(Repository repository) { + @GetMapping("/listContributors") + String listContributors(Repository repository) { assertThat(repository.getId()).isEqualTo(1L); return "test"; } diff --git a/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryIdConverterTest.java b/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryIdConverterTest.java new file mode 100644 index 0000000..13545ca --- /dev/null +++ b/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryIdConverterTest.java @@ -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()); + } + +} \ No newline at end of file diff --git a/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryIdConverterTestController.java b/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryIdConverterTestController.java new file mode 100644 index 0000000..fdf7ff8 --- /dev/null +++ b/spring-boot/argumentresolver/src/test/java/io/reflectoring/argumentresolver/RepositoryIdConverterTestController.java @@ -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"; + } + +}