diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nipB7Blossom/BlossomServerListState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nipB7Blossom/BlossomServerListState.kt index c3f8f69ef..de9d963e0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nipB7Blossom/BlossomServerListState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nipB7Blossom/BlossomServerListState.kt @@ -74,7 +74,7 @@ class BlossomServerListState( return if (serverList != null && serverList.tags.isNotEmpty()) { BlossomServersEvent.updateRelayList( earlierVersion = serverList, - relays = servers, + servers = servers, signer = signer, ) } else { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/cashu/melt/MeltProcessor.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/cashu/melt/MeltProcessor.kt index c08ec6308..dfcac00c4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/cashu/melt/MeltProcessor.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/cashu/melt/MeltProcessor.kt @@ -27,6 +27,7 @@ import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.service.cashu.CashuToken import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver import com.vitorpamplona.amethyst.ui.stringRes +import com.vitorpamplona.quartz.utils.asTextOrNull import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import okhttp3.MediaType.Companion.toMediaType @@ -127,7 +128,7 @@ class MeltProcessor { val msg = tree ?.get("detail") - ?.asText() + ?.asTextOrNull() ?.split('.') ?.getOrNull(0) ?.ifBlank { null } @@ -203,7 +204,7 @@ class MeltProcessor { val msg = tree ?.get("detail") - ?.asText() + ?.asTextOrNull() ?.split('.') ?.getOrNull(0) ?.ifBlank { null } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomServersViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomServersViewModel.kt index 4c223bf1b..cde4dc856 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomServersViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomServersViewModel.kt @@ -102,7 +102,7 @@ class BlossomServersViewModel : ViewModel() { serverUrl: String, ) { viewModelScope.launch { - val serverName = if (name.isNotBlank()) name else Rfc3986.host(serverUrl) + val serverName = name.ifBlank { Rfc3986.host(serverUrl) } _fileServers.update { it.minus( ServerName(serverName, serverUrl, ServerType.Blossom), @@ -127,5 +127,5 @@ class BlossomServersViewModel : ViewModel() { } } - private fun obtainFileServers(): List? = account.blossomServers.flow.value + private fun obtainFileServers(): List = account.blossomServers.flow.value } diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/JsonMapperNip55.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/JsonMapperNip55.kt index eedede150..f75cc16d0 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/JsonMapperNip55.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/JsonMapperNip55.kt @@ -27,6 +27,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.databind.node.ArrayNode import com.fasterxml.jackson.databind.node.ObjectNode import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue import com.vitorpamplona.quartz.nip01Core.jackson.InliningTagArrayPrettyPrinter import com.vitorpamplona.quartz.nip55AndroidSigner.api.foreground.intents.results.IntentResult import com.vitorpamplona.quartz.nip55AndroidSigner.api.foreground.intents.results.IntentResultJsonDeserializer @@ -50,9 +51,9 @@ object JsonMapperNip55 { .addSerializer(Permission::class.java, PermissionSerializer()), ) - inline fun fromJsonTo(json: String): T = defaultMapper.readValue(json, T::class.java) + inline fun fromJsonTo(json: String): T = defaultMapper.readValue(json) - inline fun fromJsonTo(json: InputStream): T = defaultMapper.readValue(json, T::class.java) + inline fun fromJsonTo(json: InputStream): T = defaultMapper.readValue(json) fun toJson(event: ArrayNode): String = defaultMapper.writeValueAsString(event) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/DecryptZapResponse.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/DecryptZapResponse.kt index fec2ca5ad..6848dca3c 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/DecryptZapResponse.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/DecryptZapResponse.kt @@ -34,7 +34,7 @@ class DecryptZapResponse { ) fun parse(intent: IntentResult): SignerResult.RequestAddressed { - if (intent.rejected) { + if (intent.rejected == true) { return SignerResult.RequestAddressed.ManuallyRejected() } val eventJson = intent.result diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/DeriveKeyResponse.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/DeriveKeyResponse.kt index c1353a1e6..4254c061f 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/DeriveKeyResponse.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/DeriveKeyResponse.kt @@ -33,7 +33,7 @@ class DeriveKeyResponse { ) fun parse(intent: IntentResult): SignerResult.RequestAddressed { - if (intent.rejected) { + if (intent.rejected == true) { return SignerResult.RequestAddressed.ManuallyRejected() } val newPrivateKey = intent.result diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip04DecryptResponse.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip04DecryptResponse.kt index ee94be069..79f9e923b 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip04DecryptResponse.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip04DecryptResponse.kt @@ -32,7 +32,7 @@ class Nip04DecryptResponse { ) fun parse(intent: IntentResult): SignerResult.RequestAddressed { - if (intent.rejected) { + if (intent.rejected == true) { return SignerResult.RequestAddressed.ManuallyRejected() } val plaintext = intent.result diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip04EncryptResponse.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip04EncryptResponse.kt index 874f782e0..5057be7ce 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip04EncryptResponse.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip04EncryptResponse.kt @@ -32,7 +32,7 @@ class Nip04EncryptResponse { ) fun parse(intent: IntentResult): SignerResult.RequestAddressed { - if (intent.rejected) { + if (intent.rejected == true) { return SignerResult.RequestAddressed.ManuallyRejected() } diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip44DecryptResponse.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip44DecryptResponse.kt index 2f609f0e9..c360ad2e3 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip44DecryptResponse.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip44DecryptResponse.kt @@ -32,7 +32,7 @@ class Nip44DecryptResponse { ) fun parse(intent: IntentResult): SignerResult.RequestAddressed { - if (intent.rejected) { + if (intent.rejected == true) { return SignerResult.RequestAddressed.ManuallyRejected() } val plaintext = intent.result diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip44EncryptResponse.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip44EncryptResponse.kt index fc5615442..9c2e42172 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip44EncryptResponse.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/Nip44EncryptResponse.kt @@ -32,7 +32,7 @@ class Nip44EncryptResponse { ) fun parse(intent: IntentResult): SignerResult.RequestAddressed { - if (intent.rejected) { + if (intent.rejected == true) { return SignerResult.RequestAddressed.ManuallyRejected() } val ciphertext = intent.result diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/SignResponse.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/SignResponse.kt index 74a8c8375..c9da8a175 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/SignResponse.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/responses/SignResponse.kt @@ -39,7 +39,7 @@ class SignResponse { intent: IntentResult, unsignedEvent: Event, ): SignerResult.RequestAddressed { - if (intent.rejected) { + if (intent.rejected == true) { return SignerResult.RequestAddressed.ManuallyRejected() } diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/results/IntentResult.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/results/IntentResult.kt index 86c612afa..a5d9a8930 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/results/IntentResult.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/results/IntentResult.kt @@ -31,16 +31,17 @@ data class IntentResult( val result: String? = null, val event: String? = null, val id: String? = null, - val rejected: Boolean = false, + val rejected: Boolean? = false, ) : OptimizedSerializable { fun toJson(): String = JsonMapperNip55.toJson(this) fun toIntent(): Intent { val intent = Intent() - intent.putExtra("id", id) - intent.putExtra("result", result) - intent.putExtra("event", event) - intent.putExtra("package", `package`) + if (id != null) intent.putExtra("id", id) + if (result != null) intent.putExtra("result", result) + if (event != null) intent.putExtra("event", event) + if (`package` != null) intent.putExtra("package", `package`) + if (rejected != null) intent.putExtra("rejected", rejected) return intent } @@ -51,7 +52,12 @@ data class IntentResult( result = data.getStringExtra("result"), event = data.getStringExtra("event"), `package` = data.getStringExtra("package"), - rejected = data.extras?.containsKey("rejected") == true, + rejected = + if (data.extras?.containsKey("rejected") == true) { + data.getBooleanExtra("rejected", false) + } else { + null + }, ) fun fromJson(json: String): IntentResult = JsonMapperNip55.fromJsonTo(json) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/results/IntentResultJsonDeserializer.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/results/IntentResultJsonDeserializer.kt index 0631da0d9..785c09fd4 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/results/IntentResultJsonDeserializer.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/results/IntentResultJsonDeserializer.kt @@ -24,6 +24,8 @@ import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.vitorpamplona.quartz.utils.asBooleanOrNull +import com.vitorpamplona.quartz.utils.asTextOrNull class IntentResultJsonDeserializer : StdDeserializer(IntentResult::class.java) { override fun deserialize( @@ -31,12 +33,13 @@ class IntentResultJsonDeserializer : StdDeserializer(IntentResult: ctxt: DeserializationContext, ): IntentResult { val jsonObject: JsonNode = jp.codec.readTree(jp) + return IntentResult( - `package` = jsonObject.get("package")?.asText()?.intern(), - result = jsonObject.get("result")?.asText(), - event = jsonObject.get("event")?.asText(), - id = jsonObject.get("id")?.asText()?.intern(), - rejected = jsonObject.get("rejected")?.asBoolean() ?: false, + `package` = jsonObject.get("package")?.asTextOrNull()?.intern(), + result = jsonObject.get("result")?.asTextOrNull(), + event = jsonObject.get("event")?.asTextOrNull(), + id = jsonObject.get("id")?.asTextOrNull()?.intern(), + rejected = jsonObject.get("rejected")?.asBooleanOrNull(), ) } } diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/results/IntentResultJsonSerializer.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/results/IntentResultJsonSerializer.kt index d8e5f9226..7fa8cb123 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/results/IntentResultJsonSerializer.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/api/foreground/intents/results/IntentResultJsonSerializer.kt @@ -35,7 +35,7 @@ class IntentResultJsonSerializer : StdSerializer(IntentResult::cla result.result?.let { gen.writeStringField("result", it) } result.event?.let { gen.writeStringField("event", it) } result.id?.let { gen.writeStringField("id", it) } - result.rejected.let { gen.writeBooleanField("rejected", it) } + result.rejected?.let { gen.writeBooleanField("rejected", it) } gen.writeEndObject() } } diff --git a/quartz/src/androidUnitTest/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/foreground/intents/results/IntentResultSerializerTest.kt b/quartz/src/androidUnitTest/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/foreground/intents/results/IntentResultSerializerTest.kt new file mode 100644 index 000000000..e98e49596 --- /dev/null +++ b/quartz/src/androidUnitTest/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/foreground/intents/results/IntentResultSerializerTest.kt @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2025 Vitor Pamplona + * + * 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 com.vitorpamplona.quartz.nip55AndroidSigner.foreground.intents.results + +import com.vitorpamplona.quartz.nip55AndroidSigner.api.foreground.intents.results.IntentResult +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class IntentResultSerializerTest { + val example = + """ +[ + { + "package": null, + "signature": "336590c3f90dc6b3e709090f48b63cc82db01f4a53702d3a9802c7647d43b8a946964fd8178a5439a655e6a2a9af1143573c64d5828f526b2bf9b24bfbde61dd", + "result": "336590c3f90dc6b3e709090f48b63cc82db01f4a53702d3a9802c7647d43b8a946964fd8178a5439a655e6a2a9af1143573c64d5828f526b2bf9b24bfbde61dd", + "rejected": null, + "id": "z6AkVNy2jAH4vcUcUaYIZHrOhi6oWROj" + }, + { + "package": null, + "signature": "2560b238cabffd3c4b02b3f9f131fceb96c388f68e4a60382b833b74871ef5474baa409e6278768a47330e8b1be041a3980c242fb05014d451603e86e6240683", + "result": "2560b238cabffd3c4b02b3f9f131fceb96c388f68e4a60382b833b74871ef5474baa409e6278768a47330e8b1be041a3980c242fb05014d451603e86e6240683", + "rejected": null, + "id": "ZQGMSmJbSbqCBRxc7elKskWHEPldIJ2j" + }, + { + "package": null, + "signature": "3577ce8638367ed0569e1953bc8379ec252d66903edf6e607bbc0c081039b3ca8bee82df1956981e598a7ea172d84671d66b052a138ce41c5f43f96aed05f536", + "result": "3577ce8638367ed0569e1953bc8379ec252d66903edf6e607bbc0c081039b3ca8bee82df1956981e598a7ea172d84671d66b052a138ce41c5f43f96aed05f536", + "rejected": null, + "id": "yuIGh0hwUN5vN3mYTJoMAP1kE7EjedEu" + }, + { + "package": null, + "signature": "2c39187a337083f473e0b3b31867efbc5399e5d64e8085295eb6f5cd94149f7a2563be90a38ca26519b2e4943137d08ebd743ae7c65d11715c9ff8b99d676c57", + "result": "2c39187a337083f473e0b3b31867efbc5399e5d64e8085295eb6f5cd94149f7a2563be90a38ca26519b2e4943137d08ebd743ae7c65d11715c9ff8b99d676c57", + "rejected": null, + "id": "iAGHGc9OKEkSze36Zqhtep0cGi26oWZp" + } +] + """.trimIndent() + + @Test + fun testDeserializer() { + val results = IntentResult.fromJsonArray(example) + + println("${results.get(0).javaClass.simpleName}") + assertEquals(4, results.size) + + assertEquals("z6AkVNy2jAH4vcUcUaYIZHrOhi6oWROj", results[0].id) + assertNull(results[0].`package`) + assertEquals("336590c3f90dc6b3e709090f48b63cc82db01f4a53702d3a9802c7647d43b8a946964fd8178a5439a655e6a2a9af1143573c64d5828f526b2bf9b24bfbde61dd", results[0].result) + assertNull(results[0].rejected) + + assertEquals("ZQGMSmJbSbqCBRxc7elKskWHEPldIJ2j", results[1].id) + assertNull(results[1].`package`) + assertEquals("2560b238cabffd3c4b02b3f9f131fceb96c388f68e4a60382b833b74871ef5474baa409e6278768a47330e8b1be041a3980c242fb05014d451603e86e6240683", results[1].result) + assertNull(results[1].rejected) + + assertEquals("yuIGh0hwUN5vN3mYTJoMAP1kE7EjedEu", results[2].id) + assertNull(results[2].`package`) + assertEquals("3577ce8638367ed0569e1953bc8379ec252d66903edf6e607bbc0c081039b3ca8bee82df1956981e598a7ea172d84671d66b052a138ce41c5f43f96aed05f536", results[2].result) + assertNull(results[2].rejected) + + assertEquals("iAGHGc9OKEkSze36Zqhtep0cGi26oWZp", results[3].id) + assertNull(results[3].`package`) + assertEquals("2c39187a337083f473e0b3b31867efbc5399e5d64e8085295eb6f5cd94149f7a2563be90a38ca26519b2e4943137d08ebd743ae7c65d11715c9ff8b99d676c57", results[3].result) + assertNull(results[3].rejected) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipB7Blossom/BlossomServersEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipB7Blossom/BlossomServersEvent.kt index bbdaef7ee..aaa98ae93 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipB7Blossom/BlossomServersEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipB7Blossom/BlossomServersEvent.kt @@ -66,18 +66,15 @@ class BlossomServersEvent( suspend fun updateRelayList( earlierVersion: BlossomServersEvent, - relays: List, + servers: List, signer: NostrSigner, createdAt: Long = TimeUtils.now(), ): BlossomServersEvent { val tags = earlierVersion.tags .filter { it[0] != "server" } - .plus( - relays.map { - arrayOf("server", it) - }, - ).toTypedArray() + .plus(servers.map { arrayOf("server", it) }) + .toTypedArray() return signer.sign(createdAt, KIND, tags, earlierVersion.content) } diff --git a/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip01Core/relay/filters/FilterDeserializer.kt b/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip01Core/relay/filters/FilterDeserializer.kt index 273a1da62..7b7cbeeca 100644 --- a/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip01Core/relay/filters/FilterDeserializer.kt +++ b/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip01Core/relay/filters/FilterDeserializer.kt @@ -24,6 +24,9 @@ import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.fasterxml.jackson.databind.node.ObjectNode +import com.vitorpamplona.quartz.utils.asIntOrNull +import com.vitorpamplona.quartz.utils.asLongOrNull +import com.vitorpamplona.quartz.utils.asTextOrNull class FilterDeserializer : StdDeserializer(Filter::class.java) { override fun deserialize( @@ -43,14 +46,14 @@ class ManualFilterDeserializer { } return Filter( - ids = jsonObject.get("ids").map { it.asText() }, - authors = jsonObject.get("authors").map { it.asText() }, - kinds = jsonObject.get("kinds").map { it.asInt() }, - tags = tags.associateWith { jsonObject.get(it).map { it.asText() } }, - since = jsonObject.get("since").asLong(), - until = jsonObject.get("until").asLong(), - limit = jsonObject.get("limit").asInt(), - search = jsonObject.get("search").asText(), + ids = jsonObject.get("ids").mapNotNull { it.asTextOrNull() }, + authors = jsonObject.get("authors").mapNotNull { it.asTextOrNull() }, + kinds = jsonObject.get("kinds").mapNotNull { it.asIntOrNull() }, + tags = tags.associateWith { jsonObject.get(it).mapNotNull { it.asTextOrNull() } }, + since = jsonObject.get("since").asLongOrNull(), + until = jsonObject.get("until").asLongOrNull(), + limit = jsonObject.get("limit").asIntOrNull(), + search = jsonObject.get("search").asTextOrNull(), ) } } diff --git a/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/jackson/BunkerResponseDeserializer.kt b/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/jackson/BunkerResponseDeserializer.kt index 3cfc25a46..c1f29e679 100644 --- a/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/jackson/BunkerResponseDeserializer.kt +++ b/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/jackson/BunkerResponseDeserializer.kt @@ -26,6 +26,7 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerResponse import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerResponseAck +import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerResponseError import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerResponseGetRelays import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerResponsePong import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerResponsePublicKey @@ -43,8 +44,7 @@ class BunkerResponseDeserializer : StdDeserializer(BunkerRespons val error = jsonObject.get("error")?.asText() if (error != null) { - return com.vitorpamplona.quartz.nip46RemoteSigner.BunkerResponseError - .parse(id, result, error) + return BunkerResponseError.parse(id, result, error) } if (result != null) { diff --git a/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/jackson/RequestDeserializer.kt b/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/jackson/RequestDeserializer.kt index 8dccd3219..df11129c9 100644 --- a/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/jackson/RequestDeserializer.kt +++ b/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/jackson/RequestDeserializer.kt @@ -26,6 +26,7 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.vitorpamplona.quartz.nip47WalletConnect.PayInvoiceMethod import com.vitorpamplona.quartz.nip47WalletConnect.Request +import com.vitorpamplona.quartz.utils.asTextOrNull class RequestDeserializer : StdDeserializer(Request::class.java) { override fun deserialize( @@ -33,7 +34,7 @@ class RequestDeserializer : StdDeserializer(Request::class.java) { ctxt: DeserializationContext, ): Request? { val jsonObject: JsonNode = jp.codec.readTree(jp) - val method = jsonObject.get("method")?.asText() + val method = jsonObject.get("method")?.asTextOrNull() if (method == "pay_invoice") { return jp.codec.treeToValue(jsonObject, PayInvoiceMethod::class.java) diff --git a/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/jackson/ResponseDeserializer.kt b/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/jackson/ResponseDeserializer.kt index 492558854..2be6b46fa 100644 --- a/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/jackson/ResponseDeserializer.kt +++ b/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip47WalletConnect/jackson/ResponseDeserializer.kt @@ -27,6 +27,7 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.vitorpamplona.quartz.nip47WalletConnect.PayInvoiceErrorResponse import com.vitorpamplona.quartz.nip47WalletConnect.PayInvoiceSuccessResponse import com.vitorpamplona.quartz.nip47WalletConnect.Response +import com.vitorpamplona.quartz.utils.asTextOrNull class ResponseDeserializer : StdDeserializer(Response::class.java) { override fun deserialize( @@ -34,7 +35,7 @@ class ResponseDeserializer : StdDeserializer(Response::class.java) { ctxt: DeserializationContext, ): Response? { val jsonObject: JsonNode = jp.codec.readTree(jp) - val resultType = jsonObject.get("result_type")?.asText() + val resultType = jsonObject.get("result_type")?.asTextOrNull() if (resultType == "pay_invoice") { val result = jsonObject.get("result") diff --git a/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/rumors/jackson/RumorDeserializer.kt b/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/rumors/jackson/RumorDeserializer.kt index cfccb3fc8..ff5c67c19 100644 --- a/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/rumors/jackson/RumorDeserializer.kt +++ b/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/nip59Giftwrap/rumors/jackson/RumorDeserializer.kt @@ -26,6 +26,9 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.vitorpamplona.quartz.nip01Core.jackson.TagArrayManualDeserializer import com.vitorpamplona.quartz.nip59Giftwrap.rumors.Rumor +import com.vitorpamplona.quartz.utils.asIntOrNull +import com.vitorpamplona.quartz.utils.asLongOrNull +import com.vitorpamplona.quartz.utils.asTextOrNull class RumorDeserializer : StdDeserializer(Rumor::class.java) { override fun deserialize( @@ -34,12 +37,12 @@ class RumorDeserializer : StdDeserializer(Rumor::class.java) { ): Rumor { val jsonObject: JsonNode = jp.codec.readTree(jp) return Rumor( - id = jsonObject.get("id")?.asText()?.intern(), - pubKey = jsonObject.get("pubkey")?.asText()?.intern(), - createdAt = jsonObject.get("created_at")?.asLong(), - kind = jsonObject.get("kind")?.asInt(), + id = jsonObject.get("id")?.asTextOrNull()?.intern(), + pubKey = jsonObject.get("pubkey")?.asTextOrNull()?.intern(), + createdAt = jsonObject.get("created_at")?.asLongOrNull(), + kind = jsonObject.get("kind")?.asIntOrNull(), tags = TagArrayManualDeserializer.fromJson(jsonObject.get("tags")), - content = jsonObject.get("content")?.asText(), + content = jsonObject.get("content")?.asTextOrNull(), ) } } diff --git a/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/utils/JacksonExt.kt b/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/utils/JacksonExt.kt new file mode 100644 index 000000000..eae64ec8b --- /dev/null +++ b/quartz/src/jvmAndroid/kotlin/com/vitorpamplona/quartz/utils/JacksonExt.kt @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2025 Vitor Pamplona + * + * 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 com.vitorpamplona.quartz.utils + +import com.fasterxml.jackson.databind.JsonNode + +fun JsonNode.asBooleanOrNull() = if (!this.isNull && this.isBoolean) this.booleanValue() else null + +fun JsonNode.asTextOrNull() = if (!this.isNull && this.isTextual) this.textValue() else null + +fun JsonNode.asLongOrNull() = if (!this.isNull && this.isNumber) this.longValue() else null + +fun JsonNode.asIntOrNull() = if (!this.isNull && this.isNumber) this.intValue() else null