libeufin

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

KvDAO.kt (3200B)


      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.nexus.db
     21 
     22 import tech.libeufin.common.*
     23 import tech.libeufin.common.db.*
     24 import tech.libeufin.nexus.iso20022.IncomingPayment
     25 import tech.libeufin.nexus.iso20022.OutgoingPayment
     26 import java.sql.*
     27 import java.time.Instant
     28 import kotlinx.serialization.Contextual
     29 import kotlinx.serialization.KSerializer
     30 import kotlinx.serialization.Serializable
     31 import kotlinx.serialization.descriptors.PrimitiveKind
     32 import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
     33 import kotlinx.serialization.descriptors.SerialDescriptor
     34 import kotlinx.serialization.encodeToString
     35 import kotlinx.serialization.encoding.Decoder
     36 import kotlinx.serialization.encoding.Encoder
     37 import kotlinx.serialization.json.Json
     38 import kotlinx.serialization.modules.SerializersModule
     39 
     40 object InstantSerialize : KSerializer<Instant> {
     41     override val descriptor: SerialDescriptor =
     42         PrimitiveSerialDescriptor("Instant", PrimitiveKind.LONG)
     43     override fun serialize(encoder: Encoder, value: Instant) =
     44         encoder.encodeLong(value.micros())
     45     override fun deserialize(decoder: Decoder): Instant =
     46         decoder.decodeLong().asInstant()
     47 }
     48 
     49 val JSON = Json {
     50     this.serializersModule = SerializersModule {
     51         contextual(Instant::class) { InstantSerialize }
     52     }
     53 }
     54 
     55 inline fun <reified T> ResultSet.getJson(name: String): T? {
     56     val value = this.getString(name)
     57     if (value == null) {
     58         return value
     59     }
     60     return JSON.decodeFromString(value)
     61 }
     62 
     63 /** Data access logic for key value */
     64 class KvDAO( val db: Database) {
     65     /** Get current value for [key] */
     66     suspend inline fun <reified T> get(key: String): T? = db.serializable(
     67         "SELECT value FROM kv WHERE key=?"
     68     ) {
     69         bind(key)
     70         oneOrNull {
     71             it.getJson("value")
     72         }
     73     }
     74 
     75     /** Update a TaskStatus timestamp */
     76     suspend fun updateTaskStatus(key: String, timestamp: Instant, success: Boolean) = db.serializable(
     77         if (success) {
     78             "INSERT INTO kv (key, value) VALUES (?, jsonb_build_object('last_successfull', ?, 'last_trial', ?)) ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value"
     79         } else {
     80             "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(?))"
     81         }
     82     ) {
     83         bind(key)
     84         bind(timestamp)
     85         bind(timestamp)
     86         executeUpdate()
     87     }
     88 }