diff --git a/README.md b/README.md index 345462b40..60bb75f9a 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,12 @@ To use the library in your project, add the following dependency to your pom.xml ``` -I recommend having a look at the [nostr-example](https://github.com/tcheeric/nostr-java/tree/main/nostr-java-examples) module and the [nostr-client](https://github.com/tcheeric/nostr-client/) project for a simple example on how to use the library. +I recommend having a look at: + - [nostr-example](https://github.com/tcheeric/nostr-java/tree/main/nostr-java-examples) module + - [nostr-client](https://github.com/tcheeric/nostr-client/) project + - [SuperConductor](https://github.com/avlo/superconductor) nostr relay + +for simple examples on how to use the library. ## Supported NIPs The following NIPs are supported by the API out-of-the-box: diff --git a/nostr-java-event/src/main/java/nostr/event/impl/GenericTag.java b/nostr-java-event/src/main/java/nostr/event/impl/GenericTag.java index 8f0d4e1a3..f66e09ce1 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/GenericTag.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/GenericTag.java @@ -41,18 +41,16 @@ public void addAttribute(ElementAttribute attribute) { } public static GenericTag create(String code, Integer nip, String... params) { + return create(code, nip, List.of(params)); + } + + public static GenericTag create(String code, Integer nip, List params) { List attributes = new ArrayList<>(); - for (int i = 0; i < params.length; i++) { + for (int i = 0; i < params.size(); i++) { String name = "param" + i; - var p = params[i]; + var p = params.get(i); attributes.add(i, ElementAttribute.builder().name(name).value(p).build()); } return new GenericTag(code, nip, attributes); } - - public static GenericTag create(String code, Integer nip, String param) { - List attributes = new ArrayList<>(); - attributes.add(0, ElementAttribute.builder().name("param0").value(param).build()); - return new GenericTag(code, nip, attributes); - } -} +} \ No newline at end of file diff --git a/nostr-java-event/src/main/java/nostr/event/json/deserializer/TagDeserializer.java b/nostr-java-event/src/main/java/nostr/event/json/deserializer/TagDeserializer.java index 2e15a377d..8c5ddd5ba 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/deserializer/TagDeserializer.java +++ b/nostr-java-event/src/main/java/nostr/event/json/deserializer/TagDeserializer.java @@ -4,23 +4,25 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; - -import java.io.IOException; -import nostr.base.PublicKey; import nostr.event.BaseTag; -import nostr.event.Marker; import nostr.event.json.codec.GenericTagDecoder; import nostr.event.tag.EventTag; +import nostr.event.tag.GeohashTag; +import nostr.event.tag.HashtagTag; import nostr.event.tag.NonceTag; +import nostr.event.tag.PriceTag; import nostr.event.tag.PubKeyTag; +import nostr.event.tag.RelaysTag; import nostr.event.tag.SubjectTag; +import java.io.IOException; + public class TagDeserializer extends JsonDeserializer { @Override public T deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { - JsonNode node = jsonParser.getCodec().readTree(jsonParser); + JsonNode node = jsonParser.getCodec().readTree(jsonParser); // Extract relevant data from the JSON node String code = node.get(0).asText(); @@ -28,78 +30,18 @@ public T deserialize(JsonParser jsonParser, DeserializationContext deserializati throw new IOException("Unknown tag code: " + null); } else // Perform custom deserialization logic based on the concrete class { - switch (code) { - case "p" -> { - // Deserialization logic for ConcreteTag1 - PubKeyTag tag = new PubKeyTag(); - - final JsonNode nodePubKey = node.get(1); - if (nodePubKey != null) { - tag.setPublicKey(new PublicKey(nodePubKey.asText())); - } - - final JsonNode nodeMainUrl = node.get(2); - if (nodeMainUrl != null) { - tag.setMainRelayUrl(nodeMainUrl.asText()); - } - - final JsonNode nodePetName = node.get(3); - if (nodePetName != null) { - tag.setPetName(nodePetName.asText()); - } - - return (T) tag; - } - - case "nonce" -> { - // Deserialization logic for ConcreteTag2 - NonceTag tag = new NonceTag(); - - final JsonNode nodeNonce = node.get(1); - if (nodeNonce != null) { - tag.setNonce(Integer.valueOf(nodeNonce.asText())); - } - - final JsonNode nodeDifficulty = node.get(2); - if (nodeDifficulty != null) { - tag.setDifficulty(Integer.valueOf(nodeDifficulty.asText())); - } - return (T) tag; - } - case "e" -> { - // Deserialization logic for ConcreteTag2 - EventTag tag = new EventTag(); - - final JsonNode nodeIdEvent = node.get(1); - if (nodeIdEvent != null) { - tag.setIdEvent(nodeIdEvent.asText()); - } - - final JsonNode nodeRelay = node.get(2); - if (nodeRelay != null) { - tag.setRecommendedRelayUrl(nodeRelay.asText()); - } - - final JsonNode nodeMarker = node.get(3); - if (nodeMarker != null) { - tag.setMarker(Marker.valueOf(nodeMarker.asText().toUpperCase())); - } - return (T) tag; - } - case "subject" -> { - SubjectTag tag = new SubjectTag(); - - final JsonNode nodeSubject = node.get(1); - if (nodeSubject != null) { - tag.setSubject(nodeSubject.asText()); - } - return (T) tag; - } - default -> { - return (T) new GenericTagDecoder<>().decode(node.toString()); - } - - } + return switch (code) { + case "e" -> EventTag.deserialize(node); + case "g" -> GeohashTag.deserialize(node); + case "p" -> PubKeyTag.deserialize(node); + case "t" -> HashtagTag.deserialize(node); + + case "nonce" -> NonceTag.deserialize(node); + case "price" -> PriceTag.deserialize(node); + case "relays" -> RelaysTag.deserialize(node); + case "subject" -> SubjectTag.deserialize(node); + default -> (T) new GenericTagDecoder<>().decode(node.toString()); + }; } } } diff --git a/nostr-java-event/src/main/java/nostr/event/json/serializer/RelaysTagSerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/RelaysTagSerializer.java index 626e31c49..1a32d0ded 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/serializer/RelaysTagSerializer.java +++ b/nostr-java-event/src/main/java/nostr/event/json/serializer/RelaysTagSerializer.java @@ -14,7 +14,7 @@ public class RelaysTagSerializer extends JsonSerializer { @Override public void serialize(@NonNull RelaysTag relaysTag, @NonNull JsonGenerator jsonGenerator, @NonNull SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeStartArray(); - jsonGenerator.writeFieldName("relays"); + jsonGenerator.writeString("relays"); relaysTag.getRelays().forEach(json -> writeString(jsonGenerator, json.getUri())); jsonGenerator.writeEndArray(); } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/EventTag.java b/nostr-java-event/src/main/java/nostr/event/tag/EventTag.java index 106b3e174..220dfa939 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/EventTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/EventTag.java @@ -1,53 +1,75 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package nostr.event.tag; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import nostr.base.annotation.Key; -import nostr.base.annotation.Tag; -import nostr.event.BaseTag; -import nostr.event.Marker; - -/** - * - * @author squirrel - */ -@Builder -@Data -@EqualsAndHashCode(callSuper = true) -@AllArgsConstructor -@Tag(code = "e", name = "event") -@JsonPropertyOrder({"idEvent", "recommendedRelayUrl", "marker"}) -@NoArgsConstructor -public class EventTag extends BaseTag { - - @Key - @JsonProperty("idEvent") - private String idEvent; - - @Key - @JsonProperty("recommendedRelayUrl") - @JsonInclude(JsonInclude.Include.NON_NULL) - private String recommendedRelayUrl; - - @Key(nip = 10) - @JsonProperty("marker") - @JsonInclude(JsonInclude.Include.NON_NULL) - private Marker marker; - - public EventTag(String idEvent) { - this.recommendedRelayUrl = null; - this.idEvent = idEvent; - this.marker = this.idEvent == null ? Marker.ROOT : Marker.REPLY; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package nostr.event.tag; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import nostr.base.annotation.Key; +import nostr.base.annotation.Tag; +import nostr.event.BaseTag; +import nostr.event.Marker; + +/** + * + * @author squirrel + */ +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@Tag(code = "e", name = "event") +@JsonPropertyOrder({"idEvent", "recommendedRelayUrl", "marker"}) +@NoArgsConstructor +public class EventTag extends BaseTag { + + @Key + @JsonProperty("idEvent") + private String idEvent; + + @Key + @JsonProperty("recommendedRelayUrl") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String recommendedRelayUrl; + + @Key(nip = 10) + @JsonProperty("marker") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Marker marker; + + public EventTag(String idEvent) { + this.recommendedRelayUrl = null; + this.idEvent = idEvent; + this.marker = this.idEvent == null ? Marker.ROOT : Marker.REPLY; + } + + public static T deserialize(@NonNull JsonNode node) { + EventTag tag = new EventTag(); + + final JsonNode nodeIdEvent = node.get(1); + if (nodeIdEvent != null) { + tag.setIdEvent(nodeIdEvent.asText()); + } + + final JsonNode nodeRelay = node.get(2); + if (nodeRelay != null) { + tag.setRecommendedRelayUrl(nodeRelay.asText()); + } + + final JsonNode nodeMarker = node.get(3); + if (nodeMarker != null) { + tag.setMarker(Marker.valueOf(nodeMarker.asText().toUpperCase())); + } + return (T) tag; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/tag/GeohashTag.java b/nostr-java-event/src/main/java/nostr/event/tag/GeohashTag.java index 83fca49ce..96302d378 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/GeohashTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/GeohashTag.java @@ -1,11 +1,13 @@ package nostr.event.tag; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.NonNull; import nostr.base.annotation.Key; import nostr.base.annotation.Tag; import nostr.event.BaseTag; @@ -25,4 +27,15 @@ public class GeohashTag extends BaseTag { @Key @JsonProperty("g") private String location; + + public static T deserialize(@NonNull JsonNode node) { + GeohashTag tag = new GeohashTag(); + + final JsonNode nodePubKey = node.get(1); + if (nodePubKey != null) { + tag.setLocation(nodePubKey.asText()); + } + + return (T) tag; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/HashtagTag.java b/nostr-java-event/src/main/java/nostr/event/tag/HashtagTag.java index 4f65ad3d0..da33f509b 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/HashtagTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/HashtagTag.java @@ -1,11 +1,13 @@ package nostr.event.tag; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.NonNull; import nostr.base.annotation.Key; import nostr.base.annotation.Tag; import nostr.event.BaseTag; @@ -26,4 +28,14 @@ public class HashtagTag extends BaseTag { @JsonProperty("t") private String hashTag; + public static T deserialize(@NonNull JsonNode node) { + HashtagTag tag = new HashtagTag(); + + final JsonNode nodePubKey = node.get(1); + if (nodePubKey != null) { + tag.setHashTag(nodePubKey.asText()); + } + + return (T) tag; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/NonceTag.java b/nostr-java-event/src/main/java/nostr/event/tag/NonceTag.java index 814443b6a..cec19b4dc 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/NonceTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/NonceTag.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.JsonNode; import nostr.base.annotation.Key; import nostr.event.BaseTag; import lombok.Builder; @@ -36,4 +37,19 @@ public NonceTag(@NonNull Integer nonce, @NonNull Integer difficulty) { this.nonce = nonce; this.difficulty = difficulty; } + + public static T deserialize(@NonNull JsonNode node) { + NonceTag tag = new NonceTag(); + + final JsonNode nodeNonce = node.get(1); + if (nodeNonce != null) { + tag.setNonce(Integer.valueOf(nodeNonce.asText())); + } + + final JsonNode nodeDifficulty = node.get(2); + if (nodeDifficulty != null) { + tag.setDifficulty(Integer.valueOf(nodeDifficulty.asText())); + } + return (T) tag; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/PriceTag.java b/nostr-java-event/src/main/java/nostr/event/tag/PriceTag.java index 3725174e7..54e162922 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/PriceTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/PriceTag.java @@ -1,29 +1,52 @@ package nostr.event.tag; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NonNull; import lombok.RequiredArgsConstructor; +import nostr.base.annotation.Key; import nostr.base.annotation.Tag; import nostr.event.BaseTag; import java.math.BigDecimal; +import java.util.Optional; @Builder @Data @EqualsAndHashCode(callSuper = true) @Tag(code = "price", nip = 99) @RequiredArgsConstructor +@AllArgsConstructor @JsonPropertyOrder({"number", "currency", "frequency"}) public class PriceTag extends BaseTag { + + @Key @JsonProperty - private final BigDecimal number; + @JsonFormat(shape = JsonFormat.Shape.STRING) + private BigDecimal number; + @Key @JsonProperty - private final String currency; + private String currency; + @Key @JsonProperty - private final String frequency; + private String frequency; + + public static T deserialize(@NonNull JsonNode node) { + String text = Optional.ofNullable(node.get(1)).orElseThrow().asText(); + final BigDecimal number = new BigDecimal(text); + final String currency = Optional.ofNullable(node.get(2)).orElseThrow().asText(); + + PriceTag tag = PriceTag.builder().number(number).currency(currency).build(); + Optional.ofNullable(node.get(3)).ifPresent(jsonNode1 -> tag.setFrequency(jsonNode1.asText())); + + return (T) tag; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/PubKeyTag.java b/nostr-java-event/src/main/java/nostr/event/tag/PubKeyTag.java index 014d5cd28..60749556a 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/PubKeyTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/PubKeyTag.java @@ -1,57 +1,78 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package nostr.event.tag; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.NonNull; -import nostr.base.PublicKey; -import nostr.base.annotation.Key; -import nostr.base.annotation.Tag; -import nostr.event.BaseTag; - -/** - * - * @author squirrel - */ -@JsonPropertyOrder({"pubKey", "mainRelayUrl", "petName"}) -@Builder -@Data -@EqualsAndHashCode(callSuper = true) -@Tag(code = "p") -@NoArgsConstructor -public class PubKeyTag extends BaseTag { - - @Key - @JsonProperty("publicKey") - private PublicKey publicKey; - - @Key - @JsonProperty("mainRelayUrl") - @JsonInclude(JsonInclude.Include.NON_NULL) - private String mainRelayUrl; - - @Key(nip = 2) - @JsonProperty("petName") - @JsonInclude(JsonInclude.Include.NON_NULL) - private String petName; - - public PubKeyTag(@NonNull PublicKey publicKey) { - this.publicKey = publicKey; - } - - public PubKeyTag(@NonNull PublicKey publicKey, String mainRelayUrl, String petName) { - this.publicKey = publicKey; - this.mainRelayUrl = mainRelayUrl; - this.petName = petName; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package nostr.event.tag; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import nostr.base.PublicKey; +import nostr.base.annotation.Key; +import nostr.base.annotation.Tag; +import nostr.event.BaseTag; + +/** + * + * @author squirrel + */ +@JsonPropertyOrder({"pubKey", "mainRelayUrl", "petName"}) +@Builder +@Data +@EqualsAndHashCode(callSuper = true) +@Tag(code = "p") +@NoArgsConstructor +public class PubKeyTag extends BaseTag { + + @Key + @JsonProperty("publicKey") + private PublicKey publicKey; + + @Key + @JsonProperty("mainRelayUrl") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String mainRelayUrl; + + @Key(nip = 2) + @JsonProperty("petName") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String petName; + + public PubKeyTag(@NonNull PublicKey publicKey) { + this.publicKey = publicKey; + } + + public PubKeyTag(@NonNull PublicKey publicKey, String mainRelayUrl, String petName) { + this.publicKey = publicKey; + this.mainRelayUrl = mainRelayUrl; + this.petName = petName; + } + + public static T deserialize(@NonNull JsonNode node) { + PubKeyTag tag = new PubKeyTag(); + + final JsonNode nodePubKey = node.get(1); + if (nodePubKey != null) { + tag.setPublicKey(new PublicKey(nodePubKey.asText())); + } + + final JsonNode nodeMainUrl = node.get(2); + if (nodeMainUrl != null) { + tag.setMainRelayUrl(nodeMainUrl.asText()); + } + + final JsonNode nodePetName = node.get(3); + if (nodePetName != null) { + tag.setPetName(nodePetName.asText()); + } + + return (T) tag; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/tag/RelaysTag.java b/nostr-java-event/src/main/java/nostr/event/tag/RelaysTag.java index 10fd01baf..62926f6e6 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/RelaysTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/RelaysTag.java @@ -1,5 +1,6 @@ package nostr.event.tag; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.Builder; import lombok.Data; @@ -11,6 +12,7 @@ import nostr.event.json.serializer.RelaysTagSerializer; import java.util.List; +import java.util.Optional; @Builder @Data @@ -27,4 +29,8 @@ public RelaysTag(@NonNull List relays) { public RelaysTag(@NonNull Relay... relays) { this(List.of(relays)); } + + public static T deserialize(@NonNull JsonNode node) { + return (T) new RelaysTag(Optional.of(node).stream().map(jsonNode -> new Relay(jsonNode.get(1).asText())).toList()); + } } \ No newline at end of file diff --git a/nostr-java-event/src/main/java/nostr/event/tag/SubjectTag.java b/nostr-java-event/src/main/java/nostr/event/tag/SubjectTag.java index 8fd7466b8..533493b59 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/SubjectTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/SubjectTag.java @@ -1,31 +1,43 @@ - -package nostr.event.tag; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import nostr.base.annotation.Key; -import nostr.base.annotation.Tag; -import nostr.event.BaseTag; - -/** - * - * @author squirrel - */ -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@EqualsAndHashCode(callSuper = true) -@Tag(code = "subject", nip = 14) -@JsonPropertyOrder({"subject"}) -public final class SubjectTag extends BaseTag { - - @Key - @JsonProperty("subject") - private String subject; -} + +package nostr.event.tag; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import nostr.base.annotation.Key; +import nostr.base.annotation.Tag; +import nostr.event.BaseTag; + +/** + * + * @author squirrel + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +@Tag(code = "subject", nip = 14) +@JsonPropertyOrder({"subject"}) +public final class SubjectTag extends BaseTag { + + @Key + @JsonProperty("subject") + private String subject; + + public static T deserialize(@NonNull JsonNode node) { + SubjectTag tag = new SubjectTag(); + + final JsonNode nodeSubject = node.get(1); + if (nodeSubject != null) { + tag.setSubject(nodeSubject.asText()); + } + return (T) tag; + } +} diff --git a/nostr-java-test/src/test/java/nostr/test/event/EventTagTest.java b/nostr-java-test/src/test/java/nostr/test/event/EventTagTest.java new file mode 100644 index 000000000..3774f529f --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/event/EventTagTest.java @@ -0,0 +1,46 @@ +package nostr.test.event; + +import nostr.event.Marker; +import nostr.event.tag.EventTag; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class EventTagTest { + @Test + void getSupportedFields() { + String eventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; + String recommendedRelayUrl = "ws://localhost:5555"; + + EventTag eventTag = new EventTag(eventId); + eventTag.setMarker(Marker.REPLY); + eventTag.setRecommendedRelayUrl(recommendedRelayUrl); + + assertDoesNotThrow(() -> { + List fields = eventTag.getSupportedFields(); + assertTrue(fields.stream().anyMatch(field -> field.getName().equals("idEvent"))); + assertTrue(fields.stream().anyMatch(field -> field.getName().equals("recommendedRelayUrl"))); + assertTrue(fields.stream().anyMatch(field -> field.getName().equals("marker"))); + + assertTrue(fields.stream().map(field -> getFieldValue(field, eventTag)).anyMatch(fieldValue -> fieldValue.equals(eventId))); + assertTrue(fields.stream().map(field -> getFieldValue(field, eventTag)).anyMatch(fieldValue -> fieldValue.equalsIgnoreCase(Marker.REPLY.getValue()))); + assertTrue(fields.stream().map(field -> getFieldValue(field, eventTag)).anyMatch(fieldValue -> fieldValue.equals(recommendedRelayUrl))); + + assertFalse(fields.stream().anyMatch(field -> field.getName().equals("idEventXXX"))); + assertFalse(fields.stream().map(field -> getFieldValue(field, eventTag)).anyMatch(fieldValue -> fieldValue.equals(eventId + "x"))); + }); + } + + private String getFieldValue(Field field, EventTag eventTag) { + final String[] returnVal = new String[1]; + assertDoesNotThrow(() -> { + returnVal[0] = eventTag.getFieldValue(field); + }); + return returnVal[0]; + } +} \ No newline at end of file diff --git a/nostr-java-test/src/test/java/nostr/test/event/PriceTagTest.java b/nostr-java-test/src/test/java/nostr/test/event/PriceTagTest.java new file mode 100644 index 000000000..fa8959e41 --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/event/PriceTagTest.java @@ -0,0 +1,23 @@ +package nostr.test.event; + +import nostr.event.tag.PriceTag; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class PriceTagTest { + @Test + void getSupportedFields() { + PriceTag priceTag = new PriceTag(new BigDecimal(11111), "BTC", "NANOSECONDS"); + assertDoesNotThrow(() -> { + List list = priceTag.getSupportedFields().stream().toList(); + assertTrue(List.of("number", "currency", "frequency").containsAll(list.stream().map(Field::getName).toList())); + assertTrue(List.of("java.math.BigDecimal", "java.lang.String").containsAll(list.stream().map(field -> field.getAnnotatedType().toString()).toList())); + }); + } +} \ No newline at end of file diff --git a/nostr-java-test/src/test/java/nostr/test/event/PubkeyTagTest.java b/nostr-java-test/src/test/java/nostr/test/event/PubkeyTagTest.java new file mode 100644 index 000000000..c7955ddc6 --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/event/PubkeyTagTest.java @@ -0,0 +1,24 @@ +package nostr.test.event; + +import nostr.base.PublicKey; +import nostr.event.tag.PubKeyTag; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PubkeyTagTest { + @Test + void getSupportedFields() { + String sha256 = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f"; + PubKeyTag pubKeyTag = new PubKeyTag(new PublicKey(sha256)); + assertDoesNotThrow(() -> { + Field field = pubKeyTag.getSupportedFields().stream().findFirst().orElseThrow(); + assertEquals("nostr.base.PublicKey", field.getAnnotatedType().toString()); + assertEquals("publicKey", field.getName()); + assertEquals(sha256, pubKeyTag.getFieldValue(field)); + }); + } +} \ No newline at end of file diff --git a/nostr-java-test/src/test/java/nostr/test/event/RelaysTagTest.java b/nostr-java-test/src/test/java/nostr/test/event/RelaysTagTest.java new file mode 100644 index 000000000..f9ec57d7e --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/event/RelaysTagTest.java @@ -0,0 +1,44 @@ +package nostr.test.event; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import nostr.base.Relay; +import nostr.event.BaseTag; +import nostr.event.json.codec.BaseTagEncoder; +import nostr.event.tag.RelaysTag; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class RelaysTagTest { + public final static String RELAYS_KEY = "relays"; + public final static String HOST_VALUE = "ws://localhost:5555"; + public final static String HOST_VALUE2 = "ws://anotherlocalhost:5432"; + ObjectMapper mapper = new ObjectMapper(); + + @Test + void testSerialize() { + final String expected = "[\"relays\",\"ws://localhost:5555\",\"ws://anotherlocalhost:5432\"]"; + RelaysTag relaysTag = new RelaysTag(List.of(new Relay(HOST_VALUE), new Relay(HOST_VALUE2))); + BaseTagEncoder baseTagEncoder = new BaseTagEncoder(relaysTag); + assertDoesNotThrow(() -> { + assertEquals(expected, baseTagEncoder.encode()); + }); + } + + @Test + void testDeserialize() throws JsonProcessingException { + final String EXPECTED = "[\"relays\",\"ws://localhost:5555\"]"; + JsonNode node = new ObjectMapper().readTree(EXPECTED); + + assertDoesNotThrow(() -> { + BaseTag deserialize = RelaysTag.deserialize(node); + assertEquals(RELAYS_KEY, deserialize.getCode()); + assertEquals(HOST_VALUE, ((RelaysTag) deserialize).getRelays().get(0).getUri()); + }); + } +} \ No newline at end of file diff --git a/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java b/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java index d3b176b66..4f9cec3e5 100644 --- a/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java +++ b/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java @@ -25,12 +25,14 @@ import nostr.event.message.EventMessage; import nostr.event.message.ReqMessage; import nostr.event.tag.EventTag; +import nostr.event.tag.PriceTag; import nostr.event.tag.PubKeyTag; import nostr.id.Identity; import nostr.util.NostrException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -192,7 +194,8 @@ public void testClassifiedListingTagSerializer() throws NostrException, JsonProc GenericEvent event = new GenericEventDecoder<>().decode(classifiedListingEventJson); EventMessage message = NIP01.createEventMessage(event, "1"); assertEquals(1, message.getNip()); - assertEquals("{\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\",\"kind\":30402,\"content\":\"content ipsum\",\"pubkey\":\"ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de\",\"created_at\":1687765220,\"tags\":[[\"p\",\"ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de\"],[\"title\",\"title ipsum\"],[\"summary\",\"summary ipsum\"],[\"published_at\",\"1687765220\"],[\"location\",\"location ipsum\"],[\"price\",\"11111\",\"BTC\",\"1\"]],\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"}", new BaseEventEncoder<>((BaseEvent) message.getEvent()).encode()); + String encoded = new BaseEventEncoder<>((BaseEvent) message.getEvent()).encode(); + assertEquals("{\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\",\"kind\":30402,\"content\":\"content ipsum\",\"pubkey\":\"ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de\",\"created_at\":1687765220,\"tags\":[[\"p\",\"ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de\"],[\"title\",\"title ipsum\"],[\"summary\",\"summary ipsum\"],[\"published_at\",\"1687765220\"],[\"location\",\"location ipsum\"],[\"price\",\"11111\",\"BTC\",\"1\"]],\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"}", encoded); assertEquals("28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a", event.getId()); assertEquals(30402, event.getKind()); @@ -201,16 +204,28 @@ public void testClassifiedListingTagSerializer() throws NostrException, JsonProc assertEquals(1687765220L, event.getCreatedAt()); assertEquals("86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546", event.getSignature().toString()); + assertEquals(new BigDecimal("11111"), event.getTags().stream().filter(baseTag -> + baseTag.getCode().equalsIgnoreCase("price")) + .filter(PriceTag.class::isInstance) + .map(PriceTag.class::cast) + .map(PriceTag::getNumber).findFirst().orElseThrow()); + + assertEquals("BTC", event.getTags().stream().filter(baseTag -> + baseTag.getCode().equalsIgnoreCase("price")) + .filter(PriceTag.class::isInstance) + .map(PriceTag.class::cast) + .map(PriceTag::getCurrency).findFirst().orElseThrow()); + + assertEquals("1", event.getTags().stream().filter(baseTag -> + baseTag.getCode().equalsIgnoreCase("price")) + .filter(PriceTag.class::isInstance) + .map(PriceTag.class::cast) + .map(PriceTag::getFrequency).findFirst().orElseThrow()); + List genericTags = event.getTags().stream() .filter(GenericTag.class::isInstance) .map(GenericTag.class::cast).toList(); - List price = genericTags.stream() - .filter(tag -> tag.getCode().equalsIgnoreCase("price")).map(GenericTag::getAttributes).toList().get(0); - assertEquals("11111", price.get(0).getValue()); - assertEquals("BTC", price.get(1).getValue()); - assertEquals("1", price.get(2).getValue()); - assertEquals("title ipsum", genericTags.stream() .filter(tag -> tag.getCode().equalsIgnoreCase("title")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue());