From 7e802166a36ff5ddb4d9d08ea7f6fee29df8512a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bazyli=20Brz=C3=B3ska?= Date: Mon, 17 Aug 2026 03:40:27 -0700 Subject: [PATCH] fix(json): expose data message metadata (#2104) --- .../asamk/signal/json/JsonDataMessage.java | 12 +++ .../signal/json/JsonGroupCallUpdate.java | 13 ++++ .../signal/json/JsonDataMessageTest.java | 77 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 src/main/java/org/asamk/signal/json/JsonGroupCallUpdate.java create mode 100644 src/test/java/org/asamk/signal/json/JsonDataMessageTest.java diff --git a/src/main/java/org/asamk/signal/json/JsonDataMessage.java b/src/main/java/org/asamk/signal/json/JsonDataMessage.java index 3c7f2998..9fd20781 100644 --- a/src/main/java/org/asamk/signal/json/JsonDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonDataMessage.java @@ -16,6 +16,10 @@ record JsonDataMessage( Integer expiresInSeconds, @JsonInclude(JsonInclude.Include.NON_NULL) Boolean isExpirationUpdate, @JsonInclude(JsonInclude.Include.NON_NULL) Boolean viewOnce, + @JsonInclude(JsonInclude.Include.NON_NULL) JsonGroupCallUpdate groupCallUpdate, + @JsonInclude(JsonInclude.Include.NON_NULL) Boolean isEndSession, + @JsonInclude(JsonInclude.Include.NON_NULL) Boolean isProfileKeyUpdate, + @JsonInclude(JsonInclude.Include.NON_NULL) Boolean hasProfileKey, @JsonInclude(JsonInclude.Include.NON_NULL) JsonReaction reaction, @JsonInclude(JsonInclude.Include.NON_NULL) JsonQuote quote, @JsonInclude(JsonInclude.Include.NON_NULL) JsonPayment payment, @@ -47,6 +51,10 @@ record JsonDataMessage( final var expiresInSeconds = dataMessage.expiresInSeconds(); final var isExpirationUpdate = dataMessage.isExpirationUpdate(); final var viewOnce = dataMessage.isViewOnce(); + final var groupCallUpdate = dataMessage.groupCallUpdate().map(JsonGroupCallUpdate::from).orElse(null); + final var isEndSession = dataMessage.isEndSession(); + final var isProfileKeyUpdate = dataMessage.isProfileKeyUpdate(); + final var hasProfileKey = dataMessage.hasProfileKey(); final var reaction = dataMessage.reaction().map(JsonReaction::from).orElse(null); final var quote = dataMessage.quote().isPresent() ? JsonQuote.from(dataMessage.quote().get()) : null; final var payment = dataMessage.payment().isPresent() ? JsonPayment.from(dataMessage.payment().get()) : null; @@ -86,6 +94,10 @@ record JsonDataMessage( expiresInSeconds, isExpirationUpdate, viewOnce, + groupCallUpdate, + isEndSession, + isProfileKeyUpdate, + hasProfileKey, reaction, quote, payment, diff --git a/src/main/java/org/asamk/signal/json/JsonGroupCallUpdate.java b/src/main/java/org/asamk/signal/json/JsonGroupCallUpdate.java new file mode 100644 index 00000000..500a9f80 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonGroupCallUpdate.java @@ -0,0 +1,13 @@ +package org.asamk.signal.json; + +import org.asamk.signal.manager.api.MessageEnvelope; + +import io.micronaut.jsonschema.JsonSchema; + +@JsonSchema(title = "GroupCallUpdate") +record JsonGroupCallUpdate(String eraId) { + + static JsonGroupCallUpdate from(MessageEnvelope.Data.GroupCallUpdate groupCallUpdate) { + return new JsonGroupCallUpdate(groupCallUpdate.eraId()); + } +} diff --git a/src/test/java/org/asamk/signal/json/JsonDataMessageTest.java b/src/test/java/org/asamk/signal/json/JsonDataMessageTest.java new file mode 100644 index 00000000..faac30ae --- /dev/null +++ b/src/test/java/org/asamk/signal/json/JsonDataMessageTest.java @@ -0,0 +1,77 @@ +package org.asamk.signal.json; + +import org.asamk.signal.manager.api.MessageEnvelope; +import org.asamk.signal.util.Util; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class JsonDataMessageTest { + + @Test + void serializesProtocolMetadata() { + final var dataMessage = dataMessage(Optional.of(new MessageEnvelope.Data.GroupCallUpdate("era-id")), + true, + true, + true); + + final var json = Util.createJsonObjectMapper().valueToTree(JsonDataMessage.from(dataMessage, null)); + + assertEquals("era-id", json.get("groupCallUpdate").get("eraId").textValue()); + assertTrue(json.get("isEndSession").booleanValue()); + assertTrue(json.get("isProfileKeyUpdate").booleanValue()); + assertTrue(json.get("hasProfileKey").booleanValue()); + } + + @Test + void omitsAbsentGroupCallUpdateAndSerializesFalseFlags() { + final var dataMessage = dataMessage(Optional.empty(), false, false, false); + + final var json = Util.createJsonObjectMapper().valueToTree(JsonDataMessage.from(dataMessage, null)); + + assertFalse(json.has("groupCallUpdate")); + assertFalse(json.get("isEndSession").booleanValue()); + assertFalse(json.get("isProfileKeyUpdate").booleanValue()); + assertFalse(json.get("hasProfileKey").booleanValue()); + } + + private static MessageEnvelope.Data dataMessage( + final Optional groupCallUpdate, + final boolean isEndSession, + final boolean isProfileKeyUpdate, + final boolean hasProfileKey + ) { + return new MessageEnvelope.Data(1L, + Optional.empty(), + Optional.empty(), + groupCallUpdate, + Optional.empty(), + 0, + false, + false, + isEndSession, + isProfileKeyUpdate, + hasProfileKey, + Optional.empty(), + Optional.empty(), + Optional.empty(), + List.of(), + Optional.empty(), + Optional.empty(), + List.of(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + List.of(), + List.of(), + List.of(), + Optional.empty(), + Optional.empty(), + Optional.empty()); + } +}