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 }