-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
♻️ Refactor swagger/openapi v3 support (#497)
- Loading branch information
Showing
25 changed files
with
765 additions
and
1,008 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
starter | ||
|
||
plugins { | ||
`java-library` | ||
} | ||
|
||
description = "Starter for building gRPC server which with HTTP and gRPC Transcoding in Sisyphus Framework" | ||
|
||
dependencies { | ||
implementation(projects.starter.sisyphusGrpcTranscodingStarter) | ||
api(libs.swagger) | ||
api(projects.starter.sisyphusWebfluxStarter) | ||
} |
111 changes: 111 additions & 0 deletions
111
...penapi-starter/src/main/kotlin/com/bybutter/sisyphus/starter/grpc/openapi/ApiDocConfig.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
package com.bybutter.sisyphus.starter.grpc.openapi | ||
|
||
import com.bybutter.sisyphus.starter.grpc.ServiceRegistrar | ||
import com.bybutter.sisyphus.starter.grpc.transcoding.EnableHttpToGrpcTranscoding | ||
import io.grpc.Server | ||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory | ||
import org.springframework.beans.factory.support.BeanDefinitionBuilder | ||
import org.springframework.beans.factory.support.BeanDefinitionRegistry | ||
import org.springframework.boot.context.properties.bind.Binder | ||
import org.springframework.context.EnvironmentAware | ||
import org.springframework.context.annotation.Configuration | ||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar | ||
import org.springframework.core.env.Environment | ||
import org.springframework.core.type.AnnotationMetadata | ||
import org.springframework.http.HttpMethod | ||
import org.springframework.web.cors.CorsConfiguration | ||
import org.springframework.web.cors.reactive.CorsConfigurationSource | ||
import org.springframework.web.cors.reactive.CorsWebFilter | ||
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource | ||
import org.springframework.web.reactive.function.server.RouterFunction | ||
|
||
/** | ||
* Config of gRPC transcoding, it will register webflux router and CORS filter based on the gRPC server. | ||
* | ||
* It imported by [EnableHttpToGrpcTranscoding] annotation, and it will found all services registered by | ||
* [ServiceRegistrar], create [TranscodingRouterFunction] and register into spring context for handling | ||
* HTTP requests. | ||
* | ||
* Also, CORS requests have been supported too, it will analyze service and register [CorsWebFilter] based | ||
* on [TranscodingCorsConfigurationSource] into spring context. | ||
*/ | ||
@Configuration | ||
class ApiDocConfig : ImportBeanDefinitionRegistrar, EnvironmentAware { | ||
private lateinit var environment: Environment | ||
|
||
override fun setEnvironment(environment: Environment) { | ||
this.environment = environment | ||
} | ||
|
||
private val swaggerProperty by lazy { | ||
Binder.get(environment).bind("openapi", ApiDocProperty::class.java).orElse(null) ?: ApiDocProperty() | ||
} | ||
|
||
override fun registerBeanDefinitions(importingClassMetadata: AnnotationMetadata, registry: BeanDefinitionRegistry) { | ||
// Find the [EnableHttpToGrpcTranscoding] annotation. | ||
val enableAnnotation = | ||
importingClassMetadata.getAnnotationAttributes(EnableHttpToGrpcTranscoding::class.java.name) ?: return | ||
// Get the enabled transcoding service in [EnableHttpToGrpcTranscoding] annotation. | ||
val enableServices = | ||
(enableAnnotation[EnableHttpToGrpcTranscoding::services.name] as? Array<String>)?.asList() ?: listOf() | ||
registerSwaggerRouterFunction(registry, enableServices) | ||
registerSwaggerCorsConfigSource(registry) | ||
} | ||
|
||
/** | ||
* Register swagger router function bean definition to spring context. | ||
*/ | ||
private fun registerSwaggerRouterFunction(registry: BeanDefinitionRegistry, enableServices: Collection<String>) { | ||
val definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(RouterFunction::class.java) { | ||
val server = | ||
(registry as ConfigurableListableBeanFactory).getBean(ServiceRegistrar.QUALIFIER_AUTO_CONFIGURED_GRPC_SERVER) as Server | ||
ApiDocRouterFunction( | ||
server, | ||
enableServices, | ||
swaggerProperty, | ||
(registry as ConfigurableListableBeanFactory).getBeansOfType(ApiDocRequestInterceptor::class.java).values.toList(), | ||
(registry as ConfigurableListableBeanFactory).getBeansOfType(ApiDocInterceptor::class.java).values.toList() | ||
) | ||
} | ||
registry.registerBeanDefinition( | ||
QUALIFIER_AUTO_CONFIGURED_GRPC_OPENAPI_ROUTER_FUNCTION, | ||
definitionBuilder.beanDefinition | ||
) | ||
} | ||
|
||
/** | ||
* Register gRPC swagger CORS config source bean definition to spring context. | ||
*/ | ||
private fun registerSwaggerCorsConfigSource(registry: BeanDefinitionRegistry) { | ||
val definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(CorsConfigurationSource::class.java) { | ||
UrlBasedCorsConfigurationSource().apply { | ||
registerCorsConfiguration( | ||
swaggerProperty.path, | ||
CorsConfiguration().apply { | ||
addAllowedHeader(CorsConfiguration.ALL) | ||
addAllowedOrigin(CorsConfiguration.ALL) | ||
addAllowedMethod(HttpMethod.OPTIONS) | ||
addAllowedMethod(HttpMethod.HEAD) | ||
addAllowedMethod(HttpMethod.GET) | ||
} | ||
) | ||
} | ||
} | ||
registry.registerBeanDefinition( | ||
QUALIFIER_AUTO_CONFIGURED_GRPC_OPENAPI_CORS_CONFIG, | ||
definitionBuilder.beanDefinition | ||
) | ||
} | ||
|
||
companion object { | ||
/** | ||
* Bean name for registered swagger router function, you can use it to refer it. | ||
*/ | ||
const val QUALIFIER_AUTO_CONFIGURED_GRPC_OPENAPI_ROUTER_FUNCTION = "sisyphus:grpc:openapi-router" | ||
|
||
/** | ||
* Bean name for registered transcoding CORS filter, you can use it to refer it. | ||
*/ | ||
const val QUALIFIER_AUTO_CONFIGURED_GRPC_OPENAPI_CORS_CONFIG = "sisyphus:grpc:openapi-cors" | ||
} | ||
} |
6 changes: 3 additions & 3 deletions
6
.../SwaggerConfigArtifactPropertyExporter.kt → ...i/ApiDocConfigArtifactPropertyExporter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
package com.bybutter.sisyphus.starter.grpc.transcoding.support.swagger | ||
package com.bybutter.sisyphus.starter.grpc.openapi | ||
|
||
import com.bybutter.sisyphus.middleware.configuration.ConfigFormatFilePropertyExporter | ||
|
||
/** | ||
* The configuration of swagger uses 'swagger/config' by default. | ||
* This configuration can be overridden in the application. | ||
* */ | ||
object SwaggerConfigArtifactPropertyExporter : ConfigFormatFilePropertyExporter() { | ||
override val names: Collection<String> = listOf("swagger/config") | ||
object ApiDocConfigArtifactPropertyExporter : ConfigFormatFilePropertyExporter() { | ||
override val names: Collection<String> = listOf("openapi/config") | ||
} |
8 changes: 8 additions & 0 deletions
8
...i-starter/src/main/kotlin/com/bybutter/sisyphus/starter/grpc/openapi/ApiDocInterceptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.bybutter.sisyphus.starter.grpc.openapi | ||
|
||
import io.swagger.v3.oas.models.OpenAPI | ||
import org.springframework.web.reactive.function.server.ServerRequest | ||
|
||
interface ApiDocInterceptor { | ||
fun intercept(request: ServerRequest, openAPI: OpenAPI) | ||
} |
5 changes: 5 additions & 0 deletions
5
...napi-starter/src/main/kotlin/com/bybutter/sisyphus/starter/grpc/openapi/ApiDocProperty.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.bybutter.sisyphus.starter.grpc.openapi | ||
|
||
data class ApiDocProperty( | ||
var path: String = "/api-docs" | ||
) |
7 changes: 7 additions & 0 deletions
7
...er/src/main/kotlin/com/bybutter/sisyphus/starter/grpc/openapi/ApiDocRequestInterceptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.bybutter.sisyphus.starter.grpc.openapi | ||
|
||
import org.springframework.web.reactive.function.server.ServerRequest | ||
|
||
interface ApiDocRequestInterceptor { | ||
fun intercept(request: ServerRequest) | ||
} |
64 changes: 64 additions & 0 deletions
64
...tarter/src/main/kotlin/com/bybutter/sisyphus/starter/grpc/openapi/ApiDocRouterFunction.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package com.bybutter.sisyphus.starter.grpc.openapi | ||
|
||
import com.bybutter.sisyphus.starter.webflux.EmptyRouterFunction | ||
import io.grpc.Server | ||
import io.grpc.ServerServiceDefinition | ||
import io.swagger.v3.core.util.Json | ||
import org.springframework.web.reactive.function.server.HandlerFunction | ||
import org.springframework.web.reactive.function.server.RouterFunction | ||
import org.springframework.web.reactive.function.server.ServerRequest | ||
import org.springframework.web.reactive.function.server.ServerResponse | ||
import reactor.core.publisher.Mono | ||
|
||
class ApiDocRouterFunction private constructor( | ||
private val services: List<ServerServiceDefinition>, | ||
private val apiDocProperty: ApiDocProperty, | ||
private val requestInterceptors: List<ApiDocRequestInterceptor>, | ||
private val interceptors: List<ApiDocInterceptor> | ||
) : RouterFunction<ServerResponse>, HandlerFunction<ServerResponse> { | ||
|
||
override fun route(request: ServerRequest): Mono<HandlerFunction<ServerResponse>> { | ||
return if (request.path() != apiDocProperty.path) { | ||
Mono.empty() | ||
} else { | ||
Mono.just(this) | ||
} | ||
} | ||
|
||
override fun handle(request: ServerRequest): Mono<ServerResponse> { | ||
requestInterceptors.forEach { | ||
it.intercept(request) | ||
} | ||
val openApi = openApi { | ||
for (service in services) { | ||
addService(service) | ||
} | ||
}.apply { | ||
interceptors.forEach { | ||
it.intercept(request, this) | ||
} | ||
} | ||
return ServerResponse.ok().bodyValue(Json.mapper().writeValueAsString(openApi)) | ||
} | ||
|
||
companion object { | ||
const val COMPONENTS_SCHEMAS_PREFIX = "#/components/schemas/" | ||
operator fun invoke( | ||
server: Server, | ||
enableServices: Collection<String> = listOf(), | ||
apiDocProperty: ApiDocProperty, | ||
requestInterceptors: List<ApiDocRequestInterceptor>, | ||
interceptors: List<ApiDocInterceptor> | ||
): RouterFunction<ServerResponse> { | ||
val enableServicesSet = enableServices.toSet() | ||
val enableServicesDefinition = mutableListOf<ServerServiceDefinition>() | ||
server.services.forEach { | ||
if (enableServicesSet.isEmpty() || enableServicesSet.contains(it.serviceDescriptor.name)) { | ||
enableServicesDefinition.add(it) | ||
} | ||
} | ||
if (enableServicesDefinition.isEmpty()) return EmptyRouterFunction | ||
return ApiDocRouterFunction(enableServicesDefinition, apiDocProperty, requestInterceptors, interceptors) | ||
} | ||
} | ||
} |
Oops, something went wrong.