Merge pull request #13 from TouchInstinct/version-spring-web

add version-spring-web
This commit is contained in:
Alexander Buntakov 2021-06-08 15:36:03 +03:00 committed by GitHub
commit 8a4c5158ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 176 additions and 0 deletions

View File

@ -79,3 +79,7 @@ Interceptor для логирования запросов/ответов.
## exception-handler-logger-spring-web
Добавляет логирование в обработку ошибок
## version-spring-web
Добавляет возможность задавать версию апи через `properties` без необходимости явно указывать в каждом маппинге

View File

@ -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")
}
}

View File

@ -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")

View File

@ -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")
}

View File

@ -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

View File

@ -0,0 +1,6 @@
package ru.touchin.version.annotations
@Target(allowedTargets = [AnnotationTarget.CLASS])
internal annotation class Versioned(
val value: String = ""
)

View File

@ -0,0 +1,5 @@
package ru.touchin.version.annotations
@Versioned("/api/v\${api.version}")
@Target(allowedTargets = [AnnotationTarget.CLASS])
annotation class VersionedApi

View File

@ -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()
}
}

View File

@ -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.Versioned
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, Versioned::class.java)
?: return mappingResult
val versionPath = resolveEmbeddedValuesInPatterns(
arrayOf(versionedAnnotation.value)
)
return RequestMappingInfo.paths(*versionPath).build().combine(mappingResult)
}
}

View File

@ -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()
}
}

View File

@ -0,0 +1,6 @@
package ru.touchin.version
import org.springframework.boot.autoconfigure.SpringBootApplication
@SpringBootApplication
class TestApplication

View File

@ -0,0 +1,75 @@
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.VersionedApi
@VersionedApi
@RestController
@RequestMapping("/app")
class VersionController {
@GetMapping("/version")
fun version(): Map<String, String> {
return mapOf("version" to "yes")
}
}
@RestController
@RequestMapping("/app")
class NoVersionController {
@GetMapping("/no-version")
fun noVersion(): Map<String, String> {
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")))
}
}

View File

@ -0,0 +1,2 @@
# suppress inspection "SpringBootApplicationProperties"
api.version=6