From b903289b21c7baa66df3306bb951cefb47bba4a5 Mon Sep 17 00:00:00 2001 From: Alexander Buntakov Date: Tue, 8 Jun 2021 14:35:17 +0300 Subject: [PATCH 1/2] add version-spring-web --- README.md | 4 + build.gradle.kts | 2 + settings.gradle.kts | 1 + version-spring-web/build.gradle.kts | 13 ++++ .../ru/touchin/version/EnableSpringVersion.kt | 8 ++ .../version/annotations/VersionedApi.kt | 6 ++ .../annotations/VersionedRestController.kt | 8 ++ .../SpringVersionConfiguration.kt | 17 +++++ .../VersionedRequestMappingHandlerMapping.kt | 25 +++++++ .../mapping/WebMvcVersionRegistrations.kt | 12 +++ .../ru/touchin/version/TestApplication.kt | 6 ++ ...onedRequestMappingHandlerMappingMvcTest.kt | 74 +++++++++++++++++++ .../resources/application-test.properties | 2 + 13 files changed, 178 insertions(+) create mode 100644 version-spring-web/build.gradle.kts create mode 100644 version-spring-web/src/main/kotlin/ru/touchin/version/EnableSpringVersion.kt create mode 100644 version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedApi.kt create mode 100644 version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedRestController.kt create mode 100644 version-spring-web/src/main/kotlin/ru/touchin/version/configurations/SpringVersionConfiguration.kt create mode 100644 version-spring-web/src/main/kotlin/ru/touchin/version/mapping/VersionedRequestMappingHandlerMapping.kt create mode 100644 version-spring-web/src/main/kotlin/ru/touchin/version/mapping/WebMvcVersionRegistrations.kt create mode 100644 version-spring-web/src/test/kotlin/ru/touchin/version/TestApplication.kt create mode 100644 version-spring-web/src/test/kotlin/ru/touchin/version/VersionedRequestMappingHandlerMappingMvcTest.kt create mode 100644 version-spring-web/src/test/resources/application-test.properties diff --git a/README.md b/README.md index 5eba7c5..501ca72 100644 --- a/README.md +++ b/README.md @@ -79,3 +79,7 @@ Interceptor для логирования запросов/ответов. ## exception-handler-logger-spring-web Добавляет логирование в обработку ошибок + +## version-spring-web + +Добавляет возможность задавать версию апи через `properties` без необходимости явно указывать в каждом маппинге diff --git a/build.gradle.kts b/build.gradle.kts index e593edb..87675fa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -59,6 +59,8 @@ subprojects { dependency("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0") dependency("org.mockito:mockito-inline:2.13.0") + + dependency("com.github.zafarkhaja:java-semver:0.9.0") } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 3e016cf..b49f56a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,3 +30,4 @@ include("logger-spring") include("logger-spring-web") include("exception-handler-spring-web") include("exception-handler-logger-spring-web") +include("version-spring-web") diff --git a/version-spring-web/build.gradle.kts b/version-spring-web/build.gradle.kts new file mode 100644 index 0000000..a103ea4 --- /dev/null +++ b/version-spring-web/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("kotlin") + id("kotlin-spring") + id("maven-publish") +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + + implementation("org.springframework.boot:spring-boot-starter-web") + + testImplementation("org.springframework.boot:spring-boot-starter-test") +} diff --git a/version-spring-web/src/main/kotlin/ru/touchin/version/EnableSpringVersion.kt b/version-spring-web/src/main/kotlin/ru/touchin/version/EnableSpringVersion.kt new file mode 100644 index 0000000..6df8704 --- /dev/null +++ b/version-spring-web/src/main/kotlin/ru/touchin/version/EnableSpringVersion.kt @@ -0,0 +1,8 @@ +@file:Suppress("unused") +package ru.touchin.version + +import org.springframework.context.annotation.Import +import ru.touchin.version.configurations.SpringVersionConfiguration + +@Import(value = [SpringVersionConfiguration::class]) +annotation class EnableSpringVersion diff --git a/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedApi.kt b/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedApi.kt new file mode 100644 index 0000000..1938840 --- /dev/null +++ b/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedApi.kt @@ -0,0 +1,6 @@ +package ru.touchin.version.annotations + +@Target(allowedTargets = [AnnotationTarget.CLASS]) +annotation class VersionedApi( + val value: String = "" +) diff --git a/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedRestController.kt b/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedRestController.kt new file mode 100644 index 0000000..70c6473 --- /dev/null +++ b/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedRestController.kt @@ -0,0 +1,8 @@ +package ru.touchin.version.annotations + +import org.springframework.web.bind.annotation.RestController + +@RestController +@VersionedApi("/api/v\${api.version}") +@Target(allowedTargets = [AnnotationTarget.CLASS]) +annotation class VersionedRestController diff --git a/version-spring-web/src/main/kotlin/ru/touchin/version/configurations/SpringVersionConfiguration.kt b/version-spring-web/src/main/kotlin/ru/touchin/version/configurations/SpringVersionConfiguration.kt new file mode 100644 index 0000000..1bd191b --- /dev/null +++ b/version-spring-web/src/main/kotlin/ru/touchin/version/configurations/SpringVersionConfiguration.kt @@ -0,0 +1,17 @@ +package ru.touchin.version.configurations + +import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import ru.touchin.version.mapping.WebMvcVersionRegistrations + +@Suppress("SpringFacetCodeInspection") +@Configuration +class SpringVersionConfiguration { + + @Bean + fun webMvcRegistrations(): WebMvcRegistrations { + return WebMvcVersionRegistrations() + } + +} diff --git a/version-spring-web/src/main/kotlin/ru/touchin/version/mapping/VersionedRequestMappingHandlerMapping.kt b/version-spring-web/src/main/kotlin/ru/touchin/version/mapping/VersionedRequestMappingHandlerMapping.kt new file mode 100644 index 0000000..43c01e7 --- /dev/null +++ b/version-spring-web/src/main/kotlin/ru/touchin/version/mapping/VersionedRequestMappingHandlerMapping.kt @@ -0,0 +1,25 @@ +package ru.touchin.version.mapping + +import org.springframework.core.annotation.AnnotatedElementUtils +import org.springframework.web.servlet.mvc.method.RequestMappingInfo +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping +import ru.touchin.version.annotations.VersionedApi +import java.lang.reflect.Method + +class VersionedRequestMappingHandlerMapping : RequestMappingHandlerMapping() { + + override fun getMappingForMethod(method: Method, handlerType: Class<*>): RequestMappingInfo? { + val mappingResult = super.getMappingForMethod(method, handlerType) + ?: return null + + val versionedAnnotation = AnnotatedElementUtils.findMergedAnnotation(handlerType, VersionedApi::class.java) + ?: return mappingResult + + val versionPath = resolveEmbeddedValuesInPatterns( + arrayOf(versionedAnnotation.value) + ) + + return RequestMappingInfo.paths(*versionPath).build().combine(mappingResult) + } + +} diff --git a/version-spring-web/src/main/kotlin/ru/touchin/version/mapping/WebMvcVersionRegistrations.kt b/version-spring-web/src/main/kotlin/ru/touchin/version/mapping/WebMvcVersionRegistrations.kt new file mode 100644 index 0000000..c04417e --- /dev/null +++ b/version-spring-web/src/main/kotlin/ru/touchin/version/mapping/WebMvcVersionRegistrations.kt @@ -0,0 +1,12 @@ +package ru.touchin.version.mapping + +import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping + +class WebMvcVersionRegistrations : WebMvcRegistrations { + + override fun getRequestMappingHandlerMapping(): RequestMappingHandlerMapping { + return VersionedRequestMappingHandlerMapping() + } + +} diff --git a/version-spring-web/src/test/kotlin/ru/touchin/version/TestApplication.kt b/version-spring-web/src/test/kotlin/ru/touchin/version/TestApplication.kt new file mode 100644 index 0000000..c25afed --- /dev/null +++ b/version-spring-web/src/test/kotlin/ru/touchin/version/TestApplication.kt @@ -0,0 +1,6 @@ +package ru.touchin.version + +import org.springframework.boot.autoconfigure.SpringBootApplication + +@SpringBootApplication +class TestApplication diff --git a/version-spring-web/src/test/kotlin/ru/touchin/version/VersionedRequestMappingHandlerMappingMvcTest.kt b/version-spring-web/src/test/kotlin/ru/touchin/version/VersionedRequestMappingHandlerMappingMvcTest.kt new file mode 100644 index 0000000..1391e79 --- /dev/null +++ b/version-spring-web/src/test/kotlin/ru/touchin/version/VersionedRequestMappingHandlerMappingMvcTest.kt @@ -0,0 +1,74 @@ +package ru.touchin.version + +import org.hamcrest.Matchers.`is` +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.MediaType +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import ru.touchin.version.annotations.VersionedRestController + +@VersionedRestController +@RequestMapping("/app") +class VersionController { + + @GetMapping("/version") + fun version(): Map { + return mapOf("version" to "yes") + } + +} + +@RestController +@RequestMapping("/app") +class NoVersionController { + + @GetMapping("/no-version") + fun noVersion(): Map { + return mapOf("version" to "no") + } + +} + +@ActiveProfiles("test") +@SpringBootTest +@AutoConfigureMockMvc +internal class VersionedRequestMappingHandlerMappingMvcTest { + + @Autowired + private lateinit var mockMvc: MockMvc + + @Test + @DisplayName("К пути должна добавляться версия") + fun shouldBePathWithVersion() { + mockMvc + .perform(get("/api/v6/app/version")) + .andDo(print()) + .andExpect(status().isOk) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.version", `is`("yes"))) + } + + @Test + @DisplayName("К пути не должна добавляться версия") + fun shouldBePathWithoutVersion() { + mockMvc + .perform(get("/app/no-version")) + .andDo(print()) + .andExpect(status().isOk) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.version", `is`("no"))) + } + +} diff --git a/version-spring-web/src/test/resources/application-test.properties b/version-spring-web/src/test/resources/application-test.properties new file mode 100644 index 0000000..771a6a9 --- /dev/null +++ b/version-spring-web/src/test/resources/application-test.properties @@ -0,0 +1,2 @@ +# suppress inspection "SpringBootApplicationProperties" +api.version=6 From 06e692ea9be0bf97f23dbe0dd2d35805a88e3c1b Mon Sep 17 00:00:00 2001 From: Alexander Buntakov Date: Tue, 8 Jun 2021 15:23:16 +0300 Subject: [PATCH 2/2] rename version annotations --- .../kotlin/ru/touchin/version/annotations/Versioned.kt | 6 ++++++ .../kotlin/ru/touchin/version/annotations/VersionedApi.kt | 5 ++--- .../version/annotations/VersionedRestController.kt | 8 -------- .../mapping/VersionedRequestMappingHandlerMapping.kt | 4 ++-- .../VersionedRequestMappingHandlerMappingMvcTest.kt | 5 +++-- 5 files changed, 13 insertions(+), 15 deletions(-) create mode 100644 version-spring-web/src/main/kotlin/ru/touchin/version/annotations/Versioned.kt delete mode 100644 version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedRestController.kt diff --git a/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/Versioned.kt b/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/Versioned.kt new file mode 100644 index 0000000..a138f74 --- /dev/null +++ b/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/Versioned.kt @@ -0,0 +1,6 @@ +package ru.touchin.version.annotations + +@Target(allowedTargets = [AnnotationTarget.CLASS]) +internal annotation class Versioned( + val value: String = "" +) diff --git a/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedApi.kt b/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedApi.kt index 1938840..8fafc7e 100644 --- a/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedApi.kt +++ b/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedApi.kt @@ -1,6 +1,5 @@ package ru.touchin.version.annotations +@Versioned("/api/v\${api.version}") @Target(allowedTargets = [AnnotationTarget.CLASS]) -annotation class VersionedApi( - val value: String = "" -) +annotation class VersionedApi diff --git a/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedRestController.kt b/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedRestController.kt deleted file mode 100644 index 70c6473..0000000 --- a/version-spring-web/src/main/kotlin/ru/touchin/version/annotations/VersionedRestController.kt +++ /dev/null @@ -1,8 +0,0 @@ -package ru.touchin.version.annotations - -import org.springframework.web.bind.annotation.RestController - -@RestController -@VersionedApi("/api/v\${api.version}") -@Target(allowedTargets = [AnnotationTarget.CLASS]) -annotation class VersionedRestController diff --git a/version-spring-web/src/main/kotlin/ru/touchin/version/mapping/VersionedRequestMappingHandlerMapping.kt b/version-spring-web/src/main/kotlin/ru/touchin/version/mapping/VersionedRequestMappingHandlerMapping.kt index 43c01e7..dcdf71a 100644 --- a/version-spring-web/src/main/kotlin/ru/touchin/version/mapping/VersionedRequestMappingHandlerMapping.kt +++ b/version-spring-web/src/main/kotlin/ru/touchin/version/mapping/VersionedRequestMappingHandlerMapping.kt @@ -3,7 +3,7 @@ package ru.touchin.version.mapping import org.springframework.core.annotation.AnnotatedElementUtils import org.springframework.web.servlet.mvc.method.RequestMappingInfo import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping -import ru.touchin.version.annotations.VersionedApi +import ru.touchin.version.annotations.Versioned import java.lang.reflect.Method class VersionedRequestMappingHandlerMapping : RequestMappingHandlerMapping() { @@ -12,7 +12,7 @@ class VersionedRequestMappingHandlerMapping : RequestMappingHandlerMapping() { val mappingResult = super.getMappingForMethod(method, handlerType) ?: return null - val versionedAnnotation = AnnotatedElementUtils.findMergedAnnotation(handlerType, VersionedApi::class.java) + val versionedAnnotation = AnnotatedElementUtils.findMergedAnnotation(handlerType, Versioned::class.java) ?: return mappingResult val versionPath = resolveEmbeddedValuesInPatterns( diff --git a/version-spring-web/src/test/kotlin/ru/touchin/version/VersionedRequestMappingHandlerMappingMvcTest.kt b/version-spring-web/src/test/kotlin/ru/touchin/version/VersionedRequestMappingHandlerMappingMvcTest.kt index 1391e79..9331b7c 100644 --- a/version-spring-web/src/test/kotlin/ru/touchin/version/VersionedRequestMappingHandlerMappingMvcTest.kt +++ b/version-spring-web/src/test/kotlin/ru/touchin/version/VersionedRequestMappingHandlerMappingMvcTest.kt @@ -17,9 +17,10 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -import ru.touchin.version.annotations.VersionedRestController +import ru.touchin.version.annotations.VersionedApi -@VersionedRestController +@VersionedApi +@RestController @RequestMapping("/app") class VersionController {