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 }