libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

KvDAO.kt (3097B)


      1 /*
      2  * This file is part of LibEuFin.
      3  * Copyright (C) 2024-2025 Taler Systems S.A.
      4 
      5  * LibEuFin is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU Affero General Public License as
      7  * published by the Free Software Foundation; either version 3, or
      8  * (at your option) any later version.
      9 
     10  * LibEuFin is distributed in the hope that it will be useful, but
     11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General
     13  * Public License for more details.
     14 
     15  * You should have received a copy of the GNU Affero General Public
     16  * License along with LibEuFin; see the file COPYING.  If not, see
     17  * <http://www.gnu.org/licenses/>
     18  */
     19 
     20 package tech.libeufin.ebisync.db
     21 
     22 import tech.libeufin.common.*
     23 import tech.libeufin.common.db.*
     24 import java.sql.*
     25 import java.time.Instant
     26 import kotlinx.serialization.Contextual
     27 import kotlinx.serialization.KSerializer
     28 import kotlinx.serialization.Serializable
     29 import kotlinx.serialization.descriptors.PrimitiveKind
     30 import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
     31 import kotlinx.serialization.descriptors.SerialDescriptor
     32 import kotlinx.serialization.encodeToString
     33 import kotlinx.serialization.encoding.Decoder
     34 import kotlinx.serialization.encoding.Encoder
     35 import kotlinx.serialization.json.Json
     36 import kotlinx.serialization.modules.SerializersModule
     37 
     38 object InstantSerialize : KSerializer<Instant> {
     39     override val descriptor: SerialDescriptor =
     40         PrimitiveSerialDescriptor("Instant", PrimitiveKind.LONG)
     41     override fun serialize(encoder: Encoder, value: Instant) =
     42         encoder.encodeLong(value.micros())
     43     override fun deserialize(decoder: Decoder): Instant =
     44         decoder.decodeLong().asInstant()
     45 }
     46 
     47 val JSON = Json {
     48     this.serializersModule = SerializersModule {
     49         contextual(Instant::class) { InstantSerialize }
     50     }
     51 }
     52 
     53 inline fun <reified T> ResultSet.getJson(name: String): T? {
     54     val value = this.getString(name)
     55     if (value == null) {
     56         return value
     57     }
     58     return JSON.decodeFromString(value)
     59 }
     60 
     61 /** Data access logic for key value */
     62 class KvDAO(val db: Database) {
     63     /** Get current value for [key] */
     64     suspend inline fun <reified T> get(key: String): T? = db.serializable(
     65         "SELECT value FROM kv WHERE key=?"
     66     ) {
     67         bind(key)
     68         oneOrNull {
     69             it.getJson("value")
     70         }
     71     }
     72 
     73     /** Update a TaskStatus timestamp */
     74     suspend fun updateTaskStatus(key: String, timestamp: Instant, success: Boolean) = db.serializable(
     75         if (success) {
     76             "INSERT INTO kv (key, value) VALUES (?, jsonb_build_object('last_successfull', ?, 'last_trial', ?)) ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value"
     77         } else {
     78             "INSERT INTO kv (key, value) VALUES (?, jsonb_build_object('last_trial', ?)) ON CONFLICT (key) DO UPDATE SET value=jsonb_set(EXCLUDED.value, '{last_trial}'::text[], to_jsonb(?))"
     79         }
     80     ) {
     81         bind(key)
     82         bind(timestamp)
     83         bind(timestamp)
     84         executeUpdate()
     85     }
     86 }