diff options
author | Robert Jaros <rjaros@finn.pl> | 2020-03-11 16:18:17 +0100 |
---|---|---|
committer | Robert Jaros <rjaros@finn.pl> | 2020-03-11 16:18:17 +0100 |
commit | 9e243a469ae6544e8cf523ad09b959f541c3f565 (patch) | |
tree | be9c7447e221af49180c9e98434df7f988b940b8 /kvision-modules/kvision-server-spring-boot/src/main/kotlin | |
parent | ec6084c42c13a621e17b17bd40d90b5c7879f0ec (diff) | |
download | kvision-9e243a469ae6544e8cf523ad09b959f541c3f565.tar.gz kvision-9e243a469ae6544e8cf523ad09b959f541c3f565.tar.bz2 kvision-9e243a469ae6544e8cf523ad09b959f541c3f565.zip |
Upgrade to Kotlin 1.3.70 + other dependencies (Coroutinse, Serialization, Spring Boot)
Major refactoring of build architecture.
Diffstat (limited to 'kvision-modules/kvision-server-spring-boot/src/main/kotlin')
8 files changed, 0 insertions, 1161 deletions
diff --git a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/Annotations.kt b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/Annotations.kt deleted file mode 100644 index 7de82631..00000000 --- a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/Annotations.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2017-present Robert Jaros - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package pl.treksoft.kvision.remote - -import org.springframework.data.annotation.Id -import org.springframework.data.annotation.Transient - -actual typealias Id = Id - -actual typealias Transient = Transient diff --git a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVRouterConfiguration.kt b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVRouterConfiguration.kt deleted file mode 100644 index 42c4107d..00000000 --- a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVRouterConfiguration.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2017-present Robert Jaros - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package pl.treksoft.kvision.remote - -import org.springframework.beans.factory.annotation.Value -import org.springframework.context.ApplicationContext -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.context.support.GenericApplicationContext -import org.springframework.core.io.Resource -import org.springframework.http.MediaType.TEXT_HTML -import org.springframework.stereotype.Component -import org.springframework.web.reactive.function.server.ServerRequest -import org.springframework.web.reactive.function.server.ServerResponse -import org.springframework.web.reactive.function.server.buildAndAwait -import org.springframework.web.reactive.function.server.coRouter -import org.springframework.web.reactive.function.server.router - -@Configuration -open class KVRouterConfiguration { - @Value("classpath:/public/index.html") - private lateinit var indexHtml: Resource - - @Bean - open fun kvRoutes(kvHandler: KVHandler) = coRouter { - GET("/kv/**", kvHandler::handle) - POST("/kv/**", kvHandler::handle) - PUT("/kv/**", kvHandler::handle) - DELETE("/kv/**", kvHandler::handle) - OPTIONS("/kv/**", kvHandler::handle) - } - - @Bean - open fun indexRouter() = router { - GET("/") { - ok().contentType(TEXT_HTML).bodyValue(indexHtml) - } - } -} - -@Component -open class KVHandler(var services: List<KVServiceManager<*>>, var applicationContext: ApplicationContext) { - - open suspend fun handle(request: ServerRequest): ServerResponse { - val routeUrl = request.path() - val handler = services.mapNotNull { - when (request.method()?.name) { - "GET" -> it.getRequests[routeUrl] - "POST" -> it.postRequests[routeUrl] - "PUT" -> it.putRequests[routeUrl] - "DELETE" -> it.deleteRequests[routeUrl] - "OPTIONS" -> it.optionsRequests[routeUrl] - else -> null - } - }.firstOrNull() - return if (handler != null) { - handler(request, applicationContext as GenericApplicationContext) - } else { - ServerResponse.notFound().buildAndAwait() - } - } -} diff --git a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt deleted file mode 100644 index 2ee939ef..00000000 --- a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt +++ /dev/null @@ -1,586 +0,0 @@ -/* - * Copyright (c) 2017-present Robert Jaros - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package pl.treksoft.kvision.remote - -import com.fasterxml.jackson.databind.module.SimpleModule -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.channels.ReceiveChannel -import kotlinx.coroutines.channels.SendChannel -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.reactive.awaitSingle -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.context.ApplicationContext -import org.springframework.web.reactive.function.server.ServerRequest -import org.springframework.web.reactive.function.server.ServerResponse -import org.springframework.web.reactive.function.server.awaitBody -import org.springframework.web.reactive.function.server.bodyValueAndAwait -import org.springframework.web.reactive.function.server.json -import org.springframework.web.reactive.socket.WebSocketSession -import pl.treksoft.kvision.types.* -import java.math.BigDecimal -import java.time.LocalDate -import java.time.LocalDateTime -import java.time.LocalTime -import java.time.OffsetDateTime -import java.time.OffsetTime -import kotlin.reflect.KClass - -/** - * Multiplatform service manager for Spring Boot. - */ -@UseExperimental(ExperimentalCoroutinesApi::class) -@Suppress("LargeClass", "TooManyFunctions") -actual open class KVServiceManager<T : Any> actual constructor(val serviceClass: KClass<T>) { - - companion object { - val LOG: Logger = LoggerFactory.getLogger(KVServiceManager::class.java.name) - } - - val getRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> = - mutableMapOf() - val postRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> = - mutableMapOf() - val putRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> = - mutableMapOf() - val deleteRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> = - mutableMapOf() - val optionsRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> = - mutableMapOf() - val webSocketsRequests: MutableMap<String, suspend ( - WebSocketSession, ApplicationContext, ReceiveChannel<String>, SendChannel<String> - ) -> Unit> = - mutableMapOf() - - val mapper = jacksonObjectMapper().apply { - val module = SimpleModule() - module.addSerializer(LocalDateTime::class.java, LocalDateTimeSerializer()) - module.addSerializer(LocalDate::class.java, LocalDateSerializer()) - module.addSerializer(LocalTime::class.java, LocalTimeSerializer()) - module.addSerializer(OffsetDateTime::class.java, OffsetDateTimeSerializer()) - module.addSerializer(OffsetTime::class.java, OffsetTimeSerializer()) - module.addSerializer(BigDecimal::class.java, BigDecimalSerializer()) - module.addDeserializer(LocalDateTime::class.java, LocalDateTimeDeserializer()) - module.addDeserializer(LocalDate::class.java, LocalDateDeserializer()) - module.addDeserializer(LocalTime::class.java, LocalTimeDeserializer()) - module.addDeserializer(OffsetDateTime::class.java, OffsetDateTimeDeserializer()) - module.addDeserializer(OffsetTime::class.java, OffsetTimeDeserializer()) - module.addDeserializer(BigDecimal::class.java, BigDecimalDeserializer()) - this.registerModule(module) - } - var counter: Int = 0 - - /** - * @suppress internal function - */ - suspend fun initializeService(service: T, req: ServerRequest) { - if (service is WithRequest) { - service.serverRequest = req - } - if (service is WithWebSession) { - val session = req.session().awaitSingle() - service.webSession = session - } - if (service is WithPrincipal) { - val principal = req.principal().awaitSingle() - service.principal = principal - } - } - - /** - * Binds a given route with a function of the receiver. - * @param function a function of the receiver - * @param method a HTTP method - * @param route a route - */ - @Suppress("TooGenericExceptionCaught") - protected actual inline fun <reified RET> bind( - noinline function: suspend T.() -> RET, - method: HttpMethod, route: String? - ) { - val routeDef = route ?: "route${this::class.simpleName}${counter++}" - addRoute(method, "/kv/$routeDef") { req, ctx -> - val service = ctx.getBean(serviceClass.java) - initializeService(service, req) - val jsonRpcRequest = if (method == HttpMethod.GET) { - JsonRpcRequest(req.queryParam("id").orElse(null)?.toInt() ?: 0, "", listOf()) - } else { - req.awaitBody() - } - try { - val result = function.invoke(service) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - result = mapper.writeValueAsString(result) - ) - ) - ) - } catch (e: Exception) { - if (e !is ServiceException) LOG.error(e.message, e) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = e.message ?: "Error", - exceptionType = e.javaClass.canonicalName - ) - ) - ) - } - } - } - - /** - * Binds a given route with a function of the receiver. - * @param function a function of the receiver - * @param method a HTTP method - * @param route a route - */ - @Suppress("TooGenericExceptionCaught") - protected actual inline fun <reified PAR, reified RET> bind( - noinline function: suspend T.(PAR) -> RET, - method: HttpMethod, route: String? - ) { - if (method == HttpMethod.GET) - throw UnsupportedOperationException("GET method is only supported for methods without parameters") - val routeDef = route ?: "route${this::class.simpleName}${counter++}" - addRoute(method, "/kv/$routeDef") { req, ctx -> - val service = ctx.getBean(serviceClass.java) - initializeService(service, req) - val jsonRpcRequest = req.awaitBody<JsonRpcRequest>() - if (jsonRpcRequest.params.size == 1) { - val param = getParameter<PAR>(jsonRpcRequest.params[0]) - try { - val result = function.invoke(service, param) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - result = mapper.writeValueAsString(result) - ) - ) - ) - } catch (e: Exception) { - if (e !is ServiceException) LOG.error(e.message, e) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = e.message ?: "Error", - exceptionType = e.javaClass.canonicalName - ) - ) - ) - } - } else { - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = "Invalid parameters" - ) - ) - ) - } - } - } - - /** - * Binds a given route with a function of the receiver. - * @param function a function of the receiver - * @param method a HTTP method - * @param route a route - */ - @Suppress("TooGenericExceptionCaught") - protected actual inline fun <reified PAR1, reified PAR2, reified RET> bind( - noinline function: suspend T.(PAR1, PAR2) -> RET, - method: HttpMethod, route: String? - ) { - if (method == HttpMethod.GET) - throw UnsupportedOperationException("GET method is only supported for methods without parameters") - val routeDef = route ?: "route${this::class.simpleName}${counter++}" - addRoute(method, "/kv/$routeDef") { req, ctx -> - val service = ctx.getBean(serviceClass.java) - initializeService(service, req) - val jsonRpcRequest = req.awaitBody<JsonRpcRequest>() - if (jsonRpcRequest.params.size == 2) { - val param1 = getParameter<PAR1>(jsonRpcRequest.params[0]) - val param2 = getParameter<PAR2>(jsonRpcRequest.params[1]) - try { - val result = function.invoke(service, param1, param2) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - result = mapper.writeValueAsString(result) - ) - ) - ) - } catch (e: Exception) { - if (e !is ServiceException) LOG.error(e.message, e) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = e.message ?: "Error", - exceptionType = e.javaClass.canonicalName - ) - ) - ) - } - } else { - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = "Invalid parameters" - ) - ) - ) - } - } - } - - /** - * Binds a given route with a function of the receiver. - * @param function a function of the receiver - * @param method a HTTP method - * @param route a route - */ - @Suppress("TooGenericExceptionCaught") - protected actual inline fun <reified PAR1, reified PAR2, reified PAR3, reified RET> bind( - noinline function: suspend T.(PAR1, PAR2, PAR3) -> RET, - method: HttpMethod, route: String? - ) { - if (method == HttpMethod.GET) - throw UnsupportedOperationException("GET method is only supported for methods without parameters") - val routeDef = route ?: "route${this::class.simpleName}${counter++}" - addRoute(method, "/kv/$routeDef") { req, ctx -> - val service = ctx.getBean(serviceClass.java) - initializeService(service, req) - val jsonRpcRequest = req.awaitBody<JsonRpcRequest>() - @Suppress("MagicNumber") - if (jsonRpcRequest.params.size == 3) { - val param1 = getParameter<PAR1>(jsonRpcRequest.params[0]) - val param2 = getParameter<PAR2>(jsonRpcRequest.params[1]) - val param3 = getParameter<PAR3>(jsonRpcRequest.params[2]) - try { - val result = function.invoke(service, param1, param2, param3) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - result = mapper.writeValueAsString(result) - ) - ) - ) - } catch (e: Exception) { - if (e !is ServiceException) LOG.error(e.message, e) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = e.message ?: "Error", - exceptionType = e.javaClass.canonicalName - ) - ) - ) - } - } else { - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = "Invalid parameters" - ) - ) - ) - } - } - } - - /** - * Binds a given route with a function of the receiver. - * @param function a function of the receiver - * @param method a HTTP method - * @param route a route - */ - @Suppress("TooGenericExceptionCaught") - protected actual inline fun <reified PAR1, reified PAR2, reified PAR3, reified PAR4, reified RET> bind( - noinline function: suspend T.(PAR1, PAR2, PAR3, PAR4) -> RET, - method: HttpMethod, route: String? - ) { - if (method == HttpMethod.GET) - throw UnsupportedOperationException("GET method is only supported for methods without parameters") - val routeDef = route ?: "route${this::class.simpleName}${counter++}" - addRoute(method, "/kv/$routeDef") { req, ctx -> - val service = ctx.getBean(serviceClass.java) - initializeService(service, req) - val jsonRpcRequest = req.awaitBody<JsonRpcRequest>() - @Suppress("MagicNumber") - if (jsonRpcRequest.params.size == 4) { - val param1 = getParameter<PAR1>(jsonRpcRequest.params[0]) - val param2 = getParameter<PAR2>(jsonRpcRequest.params[1]) - val param3 = getParameter<PAR3>(jsonRpcRequest.params[2]) - val param4 = getParameter<PAR4>(jsonRpcRequest.params[3]) - try { - val result = function.invoke(service, param1, param2, param3, param4) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - result = mapper.writeValueAsString(result) - ) - ) - ) - } catch (e: Exception) { - if (e !is ServiceException) LOG.error(e.message, e) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = e.message ?: "Error", - exceptionType = e.javaClass.canonicalName - ) - ) - ) - } - } else { - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = "Invalid parameters" - ) - ) - ) - } - } - } - - /** - * Binds a given route with a function of the receiver. - * @param function a function of the receiver - * @param method a HTTP method - * @param route a route - */ - @Suppress("TooGenericExceptionCaught") - protected actual inline fun <reified PAR1, reified PAR2, reified PAR3, - reified PAR4, reified PAR5, reified RET> bind( - noinline function: suspend T.(PAR1, PAR2, PAR3, PAR4, PAR5) -> RET, - method: HttpMethod, route: String? - ) { - if (method == HttpMethod.GET) - throw UnsupportedOperationException("GET method is only supported for methods without parameters") - val routeDef = route ?: "route${this::class.simpleName}${counter++}" - addRoute(method, "/kv/$routeDef") { req, ctx -> - val service = ctx.getBean(serviceClass.java) - initializeService(service, req) - val jsonRpcRequest = req.awaitBody<JsonRpcRequest>() - @Suppress("MagicNumber") - if (jsonRpcRequest.params.size == 5) { - val param1 = getParameter<PAR1>(jsonRpcRequest.params[0]) - val param2 = getParameter<PAR2>(jsonRpcRequest.params[1]) - val param3 = getParameter<PAR3>(jsonRpcRequest.params[2]) - val param4 = getParameter<PAR4>(jsonRpcRequest.params[3]) - val param5 = getParameter<PAR5>(jsonRpcRequest.params[4]) - try { - val result = function.invoke(service, param1, param2, param3, param4, param5) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - result = mapper.writeValueAsString(result) - ) - ) - ) - } catch (e: Exception) { - if (e !is ServiceException) LOG.error(e.message, e) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = e.message ?: "Error", - exceptionType = e.javaClass.canonicalName - ) - ) - ) - } - } else { - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = "Invalid parameters" - ) - ) - ) - } - } - } - - /** - * Binds a given web socket connetion with a function of the receiver. - * @param function a function of the receiver - * @param route a route - */ - protected actual inline fun <reified PAR1 : Any, reified PAR2 : Any> bind( - noinline function: suspend T.(ReceiveChannel<PAR1>, SendChannel<PAR2>) -> Unit, - route: String? - ) { - val routeDef = "route${this::class.simpleName}${counter++}" - webSocketsRequests[routeDef] = { webSocketSession, ctx, incoming, outgoing -> - val service = ctx.getBean(serviceClass.java) - if (service is WithWebSocketSession) { - service.webSocketSession = webSocketSession - } - if (service is WithPrincipal) { - val principal = webSocketSession.handshakeInfo.principal.awaitSingle() - service.principal = principal - } - val requestChannel = Channel<PAR1>() - val responseChannel = Channel<PAR2>() - coroutineScope { - launch { - for (p in incoming) { - val jsonRpcRequest = getParameter<JsonRpcRequest>(p) - if (jsonRpcRequest.params.size == 1) { - val par = getParameter<PAR1>(jsonRpcRequest.params[0]) - requestChannel.send(par) - } - } - requestChannel.close() - } - launch { - for (p in responseChannel) { - val text = mapper.writeValueAsString( - JsonRpcResponse( - id = 0, - result = mapper.writeValueAsString(p) - ) - ) - outgoing.send(text) - } - if (!incoming.isClosedForReceive) incoming.cancel() - } - launch(start = CoroutineStart.UNDISPATCHED) { - function.invoke(service, requestChannel, responseChannel) - if (!responseChannel.isClosedForReceive) responseChannel.close() - } - } - } - } - - /** - * Binds a given function of the receiver as a tabulator component source - * @param function a function of the receiver - */ - @Suppress("TooGenericExceptionCaught") - protected actual inline fun <reified RET> bindTabulatorRemote( - noinline function: suspend T.(Int?, Int?, List<RemoteFilter>?, List<RemoteSorter>?, String?) -> RemoteData<RET> - ) { - val routeDef = "route${this::class.simpleName}${counter++}" - addRoute(HttpMethod.POST, "/kv/$routeDef") { req, ctx -> - val service = ctx.getBean(serviceClass.java) - initializeService(service, req) - val jsonRpcRequest = req.awaitBody<JsonRpcRequest>() - @Suppress("MagicNumber") - if (jsonRpcRequest.params.size == 5) { - val param1 = getParameter<Int?>(jsonRpcRequest.params[0]) - val param2 = getParameter<Int?>(jsonRpcRequest.params[1]) - val param3 = getParameter<List<RemoteFilter>?>(jsonRpcRequest.params[2]) - @Suppress("MagicNumber") - val param4 = getParameter<List<RemoteSorter>?>(jsonRpcRequest.params[3]) - @Suppress("MagicNumber") - val param5 = getParameter<String?>(jsonRpcRequest.params[4]) - try { - val result = function.invoke(service, param1, param2, param3, param4, param5) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - result = mapper.writeValueAsString(result) - ) - ) - ) - } catch (e: Exception) { - if (e !is ServiceException) LOG.error(e.message, e) - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = e.message ?: "Error", - exceptionType = e.javaClass.canonicalName - ) - ) - ) - } - } else { - ServerResponse.ok().json().bodyValueAndAwait( - mapper.writeValueAsString( - JsonRpcResponse( - id = jsonRpcRequest.id, - error = "Invalid parameters" - ) - ) - ) - } - } - } - - /** - * @suppress internal function - */ - fun addRoute( - method: HttpMethod, - path: String, - handler: suspend (ServerRequest, ApplicationContext) -> ServerResponse - ) { - when (method) { - HttpMethod.GET -> getRequests[path] = handler - HttpMethod.POST -> postRequests[path] = handler - HttpMethod.PUT -> putRequests[path] = handler - HttpMethod.DELETE -> deleteRequests[path] = handler - HttpMethod.OPTIONS -> optionsRequests[path] = handler - } - } - - /** - * @suppress internal function - */ - protected inline fun <reified T> getParameter(str: String?): T { - return str?.let { - if (T::class == String::class) { - str as T - } else { - mapper.readValue(str) - } - } ?: null as T - } -} diff --git a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVWebSocketConfig.kt b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVWebSocketConfig.kt deleted file mode 100644 index 2c74e611..00000000 --- a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVWebSocketConfig.kt +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2017-present Robert Jaros - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package pl.treksoft.kvision.remote - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.async -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.channels.ReceiveChannel -import kotlinx.coroutines.channels.SendChannel -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.consumeAsFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.reactive.asFlow -import kotlinx.coroutines.reactor.asFlux -import kotlinx.coroutines.reactor.asMono -import org.springframework.context.ApplicationContext -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.web.reactive.HandlerMapping -import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping -import org.springframework.web.reactive.socket.WebSocketHandler -import org.springframework.web.reactive.socket.WebSocketSession -import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter -import reactor.core.publisher.Mono -import kotlin.coroutines.EmptyCoroutineContext - -class KVWebSocketHandler( - private val services: List<KVServiceManager<*>>, - private val applicationContext: ApplicationContext -) : WebSocketHandler, CoroutineScope by CoroutineScope(Dispatchers.Default) { - - private fun getHandler(session: WebSocketSession): (suspend ( - WebSocketSession, ApplicationContext, - ReceiveChannel<String>, SendChannel<String> - ) -> Unit) { - val uri = session.handshakeInfo.uri.toString() - val route = uri.substring(uri.lastIndexOf('/') + 1) - return services.mapNotNull { - it.webSocketsRequests[route] - }.first() - } - - @UseExperimental(ExperimentalCoroutinesApi::class, FlowPreview::class) - override fun handle(session: WebSocketSession): Mono<Void> { - val handler = getHandler(session) - val responseChannel = Channel<String>() - val requestChannel = Channel<String>() - val output = session.send(responseChannel.consumeAsFlow().asFlux().map(session::textMessage)) - val input = async { - coroutineScope { - launch { - session.receive().map { - it.payloadAsText - }.asFlow().collect { - requestChannel.send(it) - } - requestChannel.close() - } - launch { - handler.invoke(session, applicationContext, requestChannel, responseChannel) - if (!responseChannel.isClosedForReceive) responseChannel.close() - session.close() - } - } - }.asMono(EmptyCoroutineContext).then() - return Mono.zip(input, output).then() - } -} - -@Configuration -open class KVWebSocketConfig( - private var services: List<KVServiceManager<*>>, - private var applicationContext: ApplicationContext -) { - - @Bean - open fun handlerMapping(): HandlerMapping { - val map = mapOf("/kvws/*" to KVWebSocketHandler(services, applicationContext)) - val order = -1 - return SimpleUrlHandlerMapping(map, order) - } - - @Bean - open fun handlerAdapter() = WebSocketHandlerAdapter() -} diff --git a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/Security.kt b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/Security.kt deleted file mode 100644 index 7a9084dc..00000000 --- a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/Security.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2017-present Robert Jaros - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package pl.treksoft.kvision.remote - -import org.springframework.http.HttpMethod -import org.springframework.security.config.web.server.ServerHttpSecurity -import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher -import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers - -/** - * A function to gather paths for spring security matchers. - */ -@Suppress("SpreadOperator", "MaxLineLength") -fun ServerHttpSecurity.AuthorizeExchangeSpec.serviceMatchers(vararg services: KVServiceManager<*>): ServerHttpSecurity.AuthorizeExchangeSpec.Access { - return this.matchers(*getServerWebExchangeMatcher(*services)) -} - -/** - * A function to gather paths for spring security matchers. - */ -@Suppress("SpreadOperator") -fun serviceMatchers(vararg services: KVServiceManager<*>): ServerWebExchangeMatcher { - return ServerWebExchangeMatchers.matchers(*getServerWebExchangeMatcher(*services)) -} - -/** - * A function to gather paths for spring security matchers. - */ -@Suppress("SpreadOperator") -fun getServerWebExchangeMatcher(vararg services: KVServiceManager<*>): Array<ServerWebExchangeMatcher> { - val matchers = mutableListOf<ServerWebExchangeMatcher>() - val getPaths = services.flatMap { it.getRequests.keys }.toTypedArray() - if (getPaths.isNotEmpty()) matchers.add(ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, *getPaths)) - val postPaths = services.flatMap { it.postRequests.keys }.toTypedArray() - if (postPaths.isNotEmpty()) matchers.add(ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, *postPaths)) - val putPaths = services.flatMap { it.putRequests.keys }.toTypedArray() - if (putPaths.isNotEmpty()) matchers.add(ServerWebExchangeMatchers.pathMatchers(HttpMethod.PUT, *putPaths)) - val deletePaths = services.flatMap { it.deleteRequests.keys }.toTypedArray() - if (deletePaths.isNotEmpty()) matchers.add(ServerWebExchangeMatchers.pathMatchers(HttpMethod.DELETE, *deletePaths)) - val optionsPaths = services.flatMap { it.optionsRequests.keys }.toTypedArray() - if (optionsPaths.isNotEmpty()) matchers.add( - ServerWebExchangeMatchers.pathMatchers( - HttpMethod.OPTIONS, - *optionsPaths - ) - ) - return matchers.toTypedArray() -} diff --git a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/SessionInterfaces.kt b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/SessionInterfaces.kt deleted file mode 100644 index 63c5a9d1..00000000 --- a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/SessionInterfaces.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2017-present Robert Jaros - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package pl.treksoft.kvision.remote - -import org.springframework.web.reactive.function.server.ServerRequest -import org.springframework.web.reactive.socket.WebSocketSession -import org.springframework.web.server.WebSession -import java.security.Principal - -interface WithRequest { - var serverRequest: ServerRequest -} - -interface WithWebSession { - var webSession: WebSession -} - -interface WithPrincipal { - var principal: Principal -} - -interface WithWebSocketSession { - var webSocketSession: WebSocketSession -} diff --git a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/types/Date.kt b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/types/Date.kt deleted file mode 100644 index 61f8ba58..00000000 --- a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/types/Date.kt +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2017-present Robert Jaros - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package pl.treksoft.kvision.types - -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.JsonDeserializer -import com.fasterxml.jackson.databind.JsonSerializer -import com.fasterxml.jackson.databind.SerializerProvider -import java.io.IOException -import java.time.LocalDate -import java.time.LocalDateTime -import java.time.LocalTime -import java.time.OffsetDateTime -import java.time.OffsetTime -import java.time.format.DateTimeFormatter -import java.time.format.DateTimeParseException - -actual typealias LocalDateTime = LocalDateTime - -actual typealias LocalDate = LocalDate - -actual typealias LocalTime = LocalTime - -actual typealias OffsetDateTime = OffsetDateTime - -actual typealias OffsetTime = OffsetTime - -fun String.toDateTimeF(): LocalDateTime = LocalDateTime.parse(this) - -fun String.toDateF(): LocalDate = LocalDate.parse(this) - -fun String.toTimeF(): LocalTime = LocalTime.parse(this) - -fun String.toOffsetDateTimeF(): OffsetDateTime = OffsetDateTime.parse(this) - -fun String.toOffsetTimeF(): OffsetTime = OffsetTime.parse(this) - -fun LocalDateTime.toStringF(): String = this.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) - -fun LocalDate.toStringF(): String = this.format(DateTimeFormatter.ISO_LOCAL_DATE) - -fun LocalTime.toStringF(): String = this.format(DateTimeFormatter.ISO_LOCAL_TIME) - -fun OffsetDateTime.toStringF(): String = this.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) - -fun OffsetTime.toStringF(): String = this.format(DateTimeFormatter.ISO_OFFSET_TIME) - -class LocalDateTimeSerializer : JsonSerializer<LocalDateTime>() { - @Throws(IOException::class) - override fun serialize(value: LocalDateTime, gen: JsonGenerator, provider: SerializerProvider) { - try { - val s = value.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) - gen.writeString(s) - } catch (e: DateTimeParseException) { - System.err.println(e) - gen.writeString("") - } - } -} - -class LocalDateTimeDeserializer : JsonDeserializer<LocalDateTime>() { - @Throws(IOException::class) - override fun deserialize(p: JsonParser, ctx: DeserializationContext): LocalDateTime? { - val str = p.text - try { - @Suppress("MagicNumber") - return LocalDateTime.parse(str.dropLast(6), DateTimeFormatter.ISO_LOCAL_DATE_TIME) - } catch (e: DateTimeParseException) { - System.err.println(e) - return null - } - } -} - -class LocalDateSerializer : JsonSerializer<LocalDate>() { - @Throws(IOException::class) - override fun serialize(value: LocalDate, gen: JsonGenerator, provider: SerializerProvider) { - try { - val s = value.atStartOfDay().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) - gen.writeString(s) - } catch (e: DateTimeParseException) { - System.err.println(e) - gen.writeString("") - } - } -} - -class LocalDateDeserializer : JsonDeserializer<LocalDate>() { - @Throws(IOException::class) - override fun deserialize(p: JsonParser, ctx: DeserializationContext): LocalDate? { - val str = p.text - try { - @Suppress("MagicNumber") - return LocalDate.parse(str.dropLast(6), DateTimeFormatter.ISO_LOCAL_DATE_TIME) - } catch (e: DateTimeParseException) { - System.err.println(e) - return null - } - } -} - -class LocalTimeSerializer : JsonSerializer<LocalTime>() { - @Throws(IOException::class) - override fun serialize(value: LocalTime, gen: JsonGenerator, provider: SerializerProvider) { - try { - val s = value.atDate(LocalDate.now()).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) - gen.writeString(s) - } catch (e: DateTimeParseException) { - System.err.println(e) - gen.writeString("") - } - } -} - -class LocalTimeDeserializer : JsonDeserializer<LocalTime>() { - @Throws(IOException::class) - override fun deserialize(p: JsonParser, ctx: DeserializationContext): LocalTime? { - val str = p.text - try { - @Suppress("MagicNumber") - return LocalTime.parse(str.dropLast(6), DateTimeFormatter.ISO_LOCAL_DATE_TIME) - } catch (e: DateTimeParseException) { - System.err.println(e) - return null - } - } -} - -class OffsetDateTimeSerializer : JsonSerializer<OffsetDateTime>() { - @Throws(IOException::class) - override fun serialize(value: OffsetDateTime, gen: JsonGenerator, provider: SerializerProvider) { - try { - val s = value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) - gen.writeString(s) - } catch (e: DateTimeParseException) { - System.err.println(e) - gen.writeString("") - } - } -} - -class OffsetDateTimeDeserializer : JsonDeserializer<OffsetDateTime>() { - @Throws(IOException::class) - override fun deserialize(p: JsonParser, ctx: DeserializationContext): OffsetDateTime? { - val str = p.text - return try { - OffsetDateTime.parse(str, DateTimeFormatter.ISO_OFFSET_DATE_TIME) - } catch (e: DateTimeParseException) { - System.err.println(e) - null - } - } -} - -class OffsetTimeSerializer : JsonSerializer<OffsetTime>() { - @Throws(IOException::class) - override fun serialize(value: OffsetTime, gen: JsonGenerator, provider: SerializerProvider) { - try { - val s = value.atDate(LocalDate.now()).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) - gen.writeString(s) - } catch (e: DateTimeParseException) { - System.err.println(e) - gen.writeString("") - } - } -} - -class OffsetTimeDeserializer : JsonDeserializer<OffsetTime>() { - @Throws(IOException::class) - override fun deserialize(p: JsonParser, ctx: DeserializationContext): OffsetTime? { - val str = p.text - return try { - OffsetTime.parse(str, DateTimeFormatter.ISO_OFFSET_DATE_TIME) - } catch (e: DateTimeParseException) { - System.err.println(e) - null - } - } -} diff --git a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/types/Decimal.kt b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/types/Decimal.kt deleted file mode 100644 index 1d37ae51..00000000 --- a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/types/Decimal.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2017-present Robert Jaros - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package pl.treksoft.kvision.types - -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.JsonDeserializer -import com.fasterxml.jackson.databind.JsonSerializer -import com.fasterxml.jackson.databind.SerializerProvider -import java.io.IOException -import java.math.BigDecimal - -actual typealias Decimal = BigDecimal - -class BigDecimalSerializer : JsonSerializer<BigDecimal>() { - @Throws(IOException::class) - override fun serialize(value: BigDecimal, gen: JsonGenerator, provider: SerializerProvider) { - gen.writeNumber(value.toDouble()) - } -} - -class BigDecimalDeserializer : JsonDeserializer<BigDecimal>() { - @Throws(IOException::class) - override fun deserialize(p: JsonParser, ctx: DeserializationContext): BigDecimal? { - return p.doubleValue.toBigDecimal() - } -} |