From aef0ad4ed0619fd3e74d101d3f76bae2fd1eccae Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sun, 5 Jan 2025 20:32:46 -0500 Subject: [PATCH 01/45] heavy refactor --- .../src/main/java/nostr/api/EventNostr.java | 10 +- .../src/main/java/nostr/api/Nostr.java | 334 ------------------ .../src/main/java/nostr/base/IDecoder.java | 4 +- .../src/main/java/module-info.java | 1 - .../src/main/java/nostr/client/Client.java | 117 ------ .../ReactiveWebSocketClient.java | 2 +- .../SpringWebSocketClient.java | 10 +- .../StandardWebSocketClient.java | 4 +- ...cketClient.java => WebSocketClientIF.java} | 2 +- .../java/nostr/command/CommandHandler.java | 4 +- .../src/main/java/module-info.java | 31 -- .../provider/AbstractCommandHandler.java | 14 + .../command/provider/AuthCommandHandler.java | 19 +- .../provider/ClosedCommandHandler.java | 21 +- .../command/provider/EoseCommandHandler.java | 10 +- .../command/provider/EventCommandHandler.java | 10 +- .../provider/NoticeCommandHandler.java | 11 +- .../command/provider/OkCommandHandler.java | 10 +- nostr-java-connection/pom.xml | 45 --- .../src/main/java/module-info.java | 21 -- .../java/nostr/connection/Connection.java | 17 - .../nostr/connection/impl/ConnectionImpl.java | 103 ------ .../nostr/connection/impl/ConnectionPool.java | 105 ------ .../impl/listeners/CloseListener.java | 40 --- .../impl/listeners/CompositeListener.java | 46 --- .../impl/listeners/ErrorListener.java | 29 -- .../impl/listeners/OpenListener.java | 29 -- .../impl/listeners/TextListener.java | 88 ----- .../impl/ApplicationControllerImpl.java | 5 +- nostr-java-crypto/pom.xml | 2 +- .../nostr/event/impl/CalendarContent.java | 27 +- .../event/impl/CalendarTimeBasedEvent.java | 20 +- .../event/json/codec/BaseMessageDecoder.java | 8 +- .../nostr/event/message/EventMessage.java | 22 +- .../java/nostr/event/message/ReqMessage.java | 6 +- .../java/nostr/test/client/ClientTest.java | 145 -------- .../java/nostr/test/event/ApiEventTest.java | 6 +- ...TestUsingSpringWebSocketClientIFTest.java} | 2 +- .../java/nostr/test/event/NIP52ImplTest.java | 10 +- .../src/main/java/nostr/util/thread/Task.java | 4 +- pom.xml | 14 +- 41 files changed, 153 insertions(+), 1255 deletions(-) delete mode 100644 nostr-java-api/src/main/java/nostr/api/Nostr.java delete mode 100644 nostr-java-client/src/main/java/nostr/client/Client.java rename nostr-java-client/src/main/java/nostr/client/springwebsocket/{WebSocketClient.java => WebSocketClientIF.java} (89%) delete mode 100644 nostr-java-command-provider/src/main/java/module-info.java create mode 100644 nostr-java-command-provider/src/main/java/nostr/command/provider/AbstractCommandHandler.java delete mode 100644 nostr-java-connection/pom.xml delete mode 100644 nostr-java-connection/src/main/java/module-info.java delete mode 100644 nostr-java-connection/src/main/java/nostr/connection/Connection.java delete mode 100644 nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionImpl.java delete mode 100644 nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionPool.java delete mode 100644 nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CloseListener.java delete mode 100644 nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CompositeListener.java delete mode 100644 nostr-java-connection/src/main/java/nostr/connection/impl/listeners/ErrorListener.java delete mode 100644 nostr-java-connection/src/main/java/nostr/connection/impl/listeners/OpenListener.java delete mode 100644 nostr-java-connection/src/main/java/nostr/connection/impl/listeners/TextListener.java delete mode 100644 nostr-java-test/src/test/java/nostr/test/client/ClientTest.java rename nostr-java-test/src/test/java/nostr/test/event/{ApiEventTestUsingSpringWebSocketClientTest.java => ApiEventTestUsingSpringWebSocketClientIFTest.java} (98%) diff --git a/nostr-java-api/src/main/java/nostr/api/EventNostr.java b/nostr-java-api/src/main/java/nostr/api/EventNostr.java index 53d91db10..11a58dbf5 100644 --- a/nostr-java-api/src/main/java/nostr/api/EventNostr.java +++ b/nostr-java-api/src/main/java/nostr/api/EventNostr.java @@ -4,6 +4,7 @@ */ package nostr.api; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.NonNull; @@ -46,13 +47,18 @@ public U send() { return this.send(getRelays()); } - @SuppressWarnings("unchecked") public U send(Map relays) { List messages = super.send(this.event, relays); BaseMessageDecoder decoder = new BaseMessageDecoder(); return messages.stream() - .map(msg -> (U) decoder.decode(msg)) + .map(msg -> { + try { + return (U) decoder.decode(msg); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }) .filter(msg -> msg != null) .findFirst() .orElseThrow(() -> new RuntimeException("No message received")); diff --git a/nostr-java-api/src/main/java/nostr/api/Nostr.java b/nostr-java-api/src/main/java/nostr/api/Nostr.java deleted file mode 100644 index aaf4b488d..000000000 --- a/nostr-java-api/src/main/java/nostr/api/Nostr.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license - * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template - */ -package nostr.api; - -import com.fasterxml.jackson.core.JsonProcessingException; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.NonNull; -import nostr.base.GenericTagQuery; -import nostr.base.IElement; -import nostr.base.IEvent; -import nostr.base.ISignable; -import nostr.base.Relay; -import nostr.client.Client; -import nostr.context.RequestContext; -import nostr.context.impl.DefaultRequestContext; -import nostr.crypto.schnorr.Schnorr; -import nostr.event.BaseEvent; -import nostr.event.BaseMessage; -import nostr.event.BaseTag; -import nostr.event.impl.Filters; -import nostr.event.impl.GenericEvent; -import nostr.event.json.codec.BaseEventEncoder; -import nostr.event.json.codec.BaseMessageDecoder; -import nostr.event.json.codec.BaseTagDecoder; -import nostr.event.json.codec.BaseTagEncoder; -import nostr.event.json.codec.FiltersDecoder; -import nostr.event.json.codec.FiltersListEncoder; -import nostr.event.json.codec.GenericEventDecoder; -import nostr.event.json.codec.GenericTagQueryEncoder; -import nostr.event.message.EventMessage; -import nostr.event.message.ReqMessage; -import nostr.id.Identity; -import nostr.util.NostrUtil; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeoutException; - -/** - * @author eric - */ -@NoArgsConstructor -public class Nostr implements NostrIF { - - private static Nostr INSTANCE; - - private Client client; - @Getter - private Identity sender; - - @Getter - private Map relays; - - public static NostrIF getInstance() { - return (INSTANCE == null) ? new Nostr() : INSTANCE; - } - - public static NostrIF getInstance(@NonNull Identity sender) { - return (INSTANCE == null) ? new Nostr(sender) : INSTANCE; - } - - public Nostr(@NonNull Identity sender) { - this.sender = sender; - } - - @Override - public NostrIF setSender(@NonNull Identity sender) { - this.sender = sender; - - return this; - } - - @Override - public NostrIF setRelays(@NonNull Map relays) { - this.relays = relays; - - return this; - } - - @Override - public void close() { - if (client == null) { - throw new IllegalStateException("Client is not initialized"); - } - try { - this.client.disconnect(); - } catch (TimeoutException e) { - throw new RuntimeException(e); - } - } - - @Override - public List send(@NonNull IEvent event) { - return send(event, getRelays()); - } - - @Override - public List send(@NonNull IEvent event, Map relays) { - var context = new DefaultRequestContext(); - context.setPrivateKey(getSender().getPrivateKey().getRawData()); - context.setRelays(relays); - - return send(new EventMessage(event), context); - } - - - @Override - public List send(@NonNull Filters filters, @NonNull String subscriptionId) { - return send(filters, subscriptionId, getRelays()); - } - - @Override - public List send(@NonNull Filters filters, @NonNull String subscriptionId, Map relays) { - List filtersList = new ArrayList<>(); - filtersList.add(filters); - - return send(filtersList, subscriptionId, relays); - } - - @Override - public List send(@NonNull List filtersList, @NonNull String subscriptionId) { - return send(filtersList, subscriptionId, getRelays()); - } - - @Override - public List send(@NonNull List filtersList, @NonNull String subscriptionId, Map relays) { - - var context = new DefaultRequestContext(); - context.setRelays(relays); - context.setPrivateKey(getSender().getPrivateKey().getRawData()); - var message = new ReqMessage(subscriptionId, filtersList); - - return send(message, context); - } - - @Override - public List send(@NonNull BaseMessage message, @NonNull RequestContext context) { - if (context instanceof DefaultRequestContext) { - try { - Client.getInstance().connect(context).send(message); - } catch (TimeoutException e) { - throw new RuntimeException(e); - } - } - return List.of(); - } - - /** - * @param signable - */ - @Override - public NostrIF sign(@NonNull Identity identity, @NonNull ISignable signable) { - identity.sign(signable); - - return this; - } - - @Override - public boolean verify(@NonNull GenericEvent event) { - if (!event.isSigned()) { - throw new IllegalStateException("The event is not signed"); - } - - var signature = event.getSignature(); - - try { - var message = NostrUtil.sha256(event.get_serializedEvent()); - return Schnorr.verify(message, event.getPubKey().getRawData(), signature.getRawData()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static Map toMapRelays(List relayList) { - Map relays = new HashMap<>(); - relayList.forEach(r -> relays.put(r.getName(), r.getUri())); - return relays; - } - - public static class Json { - - // Events - - /** - * @param event - */ - public static String encode(@NonNull BaseEvent event) { - return Nostr.Json.encode(event, null); - } - - /** - * @param event - * @param relay - * @return - */ - public static String encode(@NonNull BaseEvent event, Relay relay) { - final var enc = new BaseEventEncoder(event); - return enc.encode(); - } - - /** - * @param json - */ - public static GenericEvent decodeEvent(@NonNull String json) { - return new GenericEventDecoder<>().decode(json); - } - - // Messages - - /** - * @param message - * @param relay - */ - public static String encode(@NonNull BaseMessage message, Relay relay) throws JsonProcessingException { - return message.encode(); - } - - /** - * @param message - */ - public static String encode(@NonNull BaseMessage message) throws JsonProcessingException { - return Nostr.Json.encode(message, null); - } - - /** - * @param json - */ - public static BaseMessage decodeMessage(@NonNull String json) { - return new BaseMessageDecoder().decode(json); - } - - // Tags - - /** - * @param tag - * @param relay - */ - public static String encode(@NonNull BaseTag tag, Relay relay) { - final var enc = new BaseTagEncoder(tag, relay); - return enc.encode(); - } - - /** - * @param tag - */ - public static String encode(@NonNull BaseTag tag) { - return Nostr.Json.encode(tag, null); - } - - /** - * @param json - */ - public static BaseTag decodeTag(@NonNull String json) { - return new BaseTagDecoder<>().decode(json); - } - - // Filters - - /** - * @param filtersList - * @param relay - */ - public static String encode(@NonNull List filtersList, Relay relay) { - final var enc = new FiltersListEncoder(filtersList); - return enc.encode(); - } - - /** - * @param filtersList - */ - public static String encode(@NonNull List filtersList) { - return Nostr.Json.encode(filtersList, null); - } - - /** - * @param json - */ - public static Filters decodeFilters(@NonNull String json) { - return new FiltersDecoder<>().decode(json); - } - - // Generic Tag Queries - - /** - * @param gtq - * @param relay - */ - public static String encode(@NonNull GenericTagQuery gtq, Relay relay) { - final var enc = new GenericTagQueryEncoder(gtq, relay); - return enc.encode(); - } - - /** - * @param gtq - */ - public static String encode(@NonNull GenericTagQuery gtq) { - return Nostr.Json.encode(gtq, null); - } - - /** - * @param json - * @param clazz - */ - public static IElement decode(@NonNull String json, @NonNull Class clazz) { - switch (clazz.getName()) { - case "nostr.event.BaseEvent.class" -> { - return decodeEvent(json); - } - case "nostr.event.BaseMessage.class" -> { - return decodeMessage(json); - } - case "nostr.event.BaseTag.class" -> { - return decodeTag(json); - } - default -> throw new AssertionError(); - } - } - - public static Filters decodeFilters(@NonNull String json, @NonNull Class clazz) { - switch (clazz.getName()) { - case "nostr.event.Filters.class" -> { - return decodeFilters(json); - } - default -> throw new AssertionError(); - } - } - - } -} diff --git a/nostr-java-base/src/main/java/nostr/base/IDecoder.java b/nostr-java-base/src/main/java/nostr/base/IDecoder.java index c1dd430be..f94d95120 100644 --- a/nostr-java-base/src/main/java/nostr/base/IDecoder.java +++ b/nostr-java-base/src/main/java/nostr/base/IDecoder.java @@ -1,5 +1,7 @@ package nostr.base; +import com.fasterxml.jackson.core.JsonProcessingException; + /** * * @author eric @@ -7,6 +9,6 @@ */ public interface IDecoder { - T decode(String str); + T decode(String str) throws JsonProcessingException; } diff --git a/nostr-java-client/src/main/java/module-info.java b/nostr-java-client/src/main/java/module-info.java index 75a2fccc2..eb2e20fd9 100644 --- a/nostr-java-client/src/main/java/module-info.java +++ b/nostr-java-client/src/main/java/module-info.java @@ -17,6 +17,5 @@ requires annotations; requires awaitility; - exports nostr.client; exports nostr.client.springwebsocket; } diff --git a/nostr-java-client/src/main/java/nostr/client/Client.java b/nostr-java-client/src/main/java/nostr/client/Client.java deleted file mode 100644 index d1cc550f2..000000000 --- a/nostr-java-client/src/main/java/nostr/client/Client.java +++ /dev/null @@ -1,117 +0,0 @@ -package nostr.client; - -import lombok.AllArgsConstructor; -import lombok.NoArgsConstructor; -import lombok.NonNull; -import lombok.SneakyThrows; -import lombok.extern.java.Log; -import nostr.base.Relay; -import nostr.connection.impl.ConnectionPool; -import nostr.context.Context; -import nostr.context.RequestContext; -import nostr.context.impl.DefaultRequestContext; -import nostr.event.BaseMessage; -import nostr.event.impl.GenericTag; -import nostr.event.message.CanonicalAuthenticationMessage; -import nostr.util.thread.Task; -import nostr.util.thread.ThreadUtil; - -import java.util.concurrent.TimeoutException; -import java.util.logging.Level; - -@Log -@NoArgsConstructor -public class Client { - - private static class Holder { - private static final Client INSTANCE = new Client(); - } - - private RequestContext context; - - private ConnectionPool connectionPool; - - public static Client getInstance() { - return Holder.INSTANCE; - } - - public Client connect(@NonNull RequestContext context) throws TimeoutException { - if (context instanceof DefaultRequestContext defaultRequestContext) { - Holder.INSTANCE.context = context; - connectionPool = ConnectionPool.getInstance(defaultRequestContext); - ThreadUtil.builder().blocking(true).lock(true).task(new RelayConnectionTask(this.connectionPool)).build().run(context); - } - return this; - } - - public void disconnect() throws TimeoutException { - ThreadUtil.builder().blocking(true).task(new RelayDisconnectionTask(this.connectionPool)).build().run(this.context); - } - - public int getOpenConnectionsCount() { - return connectionPool.connectionCount(); - } - - public void send(@NonNull BaseMessage message) throws TimeoutException { - log.log(Level.FINE, "Requesting to send the message {0}...", message); - ThreadUtil.builder().blocking(false).lock(true).task(new SendMessageTask(message, this.connectionPool)).build().run(this.context); - } - - boolean isConnected(@NonNull Relay relay) { - return this.connectionPool.isConnectedTo(relay); - } - - @AllArgsConstructor - private static class RelayConnectionTask implements Task { - - private final ConnectionPool connectionManager; - - @Override - public Void execute(@NonNull Context context) { - connectionManager.connect(); - return null; - } - } - - @AllArgsConstructor - private static class RelayDisconnectionTask implements Task { - - private final ConnectionPool connectionPool; - - @Override - public Void execute(@NonNull Context context) { - connectionPool.disconnect(); - return null; - } - } - - @AllArgsConstructor - private static class SendMessageTask implements Task { - - private final BaseMessage message; - private final ConnectionPool connectionPool; - - @SneakyThrows - @Override - public Void execute(@NonNull Context context) { - // Only send AUTH messages to the relay mentioned in the tag https://github.com/tcheeric/nostr-java/issues/129 - if (message instanceof CanonicalAuthenticationMessage canonicalAuthenticationMessage) { - log.log(Level.FINE, ">>> Authentication message {0}...", canonicalAuthenticationMessage); - var event = canonicalAuthenticationMessage.getEvent(); - var relayTag = event.getTags().stream().filter(t -> t.getCode().equalsIgnoreCase("relay")).findFirst(); - if (relayTag.isPresent()) { - var relayTagValue = ((GenericTag) relayTag.get()).getAttributes().get(0).getValue().toString(); - log.log(Level.FINEST, "**** Relay found in CanonicalAuthenticationMessage: {0}", relayTagValue); - var r = new Relay(relayTagValue); - connectionPool.send(canonicalAuthenticationMessage.encode(), r); - } else { - log.log(Level.SEVERE, "Relay tag not found in CanonicalAuthenticationMessage. Ignoring..."); - } - } else { - log.log(Level.FINER, "+++ message {0}...", message); - connectionPool.send(message.encode()); - } - return null; - } - } -} diff --git a/nostr-java-client/src/main/java/nostr/client/springwebsocket/ReactiveWebSocketClient.java b/nostr-java-client/src/main/java/nostr/client/springwebsocket/ReactiveWebSocketClient.java index e71e9f06d..4f9f72647 100644 --- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/ReactiveWebSocketClient.java +++ b/nostr-java-client/src/main/java/nostr/client/springwebsocket/ReactiveWebSocketClient.java @@ -18,7 +18,7 @@ @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) -public class ReactiveWebSocketClient implements WebSocketClient { +public class ReactiveWebSocketClient implements WebSocketClientIF { private final ReactorNettyWebSocketClient client; private final URI uri; diff --git a/nostr-java-client/src/main/java/nostr/client/springwebsocket/SpringWebSocketClient.java b/nostr-java-client/src/main/java/nostr/client/springwebsocket/SpringWebSocketClient.java index c685f09e2..c1647f99d 100644 --- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/SpringWebSocketClient.java +++ b/nostr-java-client/src/main/java/nostr/client/springwebsocket/SpringWebSocketClient.java @@ -9,27 +9,27 @@ import java.util.List; public class SpringWebSocketClient { - private final WebSocketClient webSocketClient; + private final WebSocketClientIF webSocketClientIF; @Getter private final String relayUrl; public SpringWebSocketClient(@NonNull String relayUrl) { - webSocketClient = new StandardWebSocketClient(relayUrl); + webSocketClientIF = new StandardWebSocketClient(relayUrl); this.relayUrl = relayUrl; } @SneakyThrows public List send(@NonNull BaseMessage eventMessage) { - return webSocketClient.send(eventMessage.encode()); + return webSocketClientIF.send(eventMessage.encode()); } public List send(@NonNull String json) throws IOException { - return webSocketClient.send(json); + return webSocketClientIF.send(json); } public void closeSocket() throws IOException { - webSocketClient.closeSocket(); + webSocketClientIF.closeSocket(); } } diff --git a/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java b/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java index 23328d426..6bc8e0587 100644 --- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java +++ b/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java @@ -22,7 +22,7 @@ @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) -public class StandardWebSocketClient extends TextWebSocketHandler implements WebSocketClient { +public class StandardWebSocketClient extends TextWebSocketHandler implements WebSocketClientIF { private final WebSocketSession clientSession; private List events = new ArrayList<>(); private boolean completed = false; @@ -57,4 +57,4 @@ public List send(String json) throws IOException { public void closeSocket() throws IOException { clientSession.close(); } -} \ No newline at end of file +} diff --git a/nostr-java-client/src/main/java/nostr/client/springwebsocket/WebSocketClient.java b/nostr-java-client/src/main/java/nostr/client/springwebsocket/WebSocketClientIF.java similarity index 89% rename from nostr-java-client/src/main/java/nostr/client/springwebsocket/WebSocketClient.java rename to nostr-java-client/src/main/java/nostr/client/springwebsocket/WebSocketClientIF.java index 600a4284e..75e538201 100644 --- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/WebSocketClient.java +++ b/nostr-java-client/src/main/java/nostr/client/springwebsocket/WebSocketClientIF.java @@ -5,7 +5,7 @@ import java.io.IOException; import java.util.List; -public interface WebSocketClient { +public interface WebSocketClientIF { List send(T eventMessage) throws IOException; List send(String json) throws IOException; void closeSocket() throws IOException; diff --git a/nostr-java-command-interface/src/main/java/nostr/command/CommandHandler.java b/nostr-java-command-interface/src/main/java/nostr/command/CommandHandler.java index ca1ff6e50..ec4115e8f 100644 --- a/nostr-java-command-interface/src/main/java/nostr/command/CommandHandler.java +++ b/nostr-java-command-interface/src/main/java/nostr/command/CommandHandler.java @@ -2,7 +2,9 @@ import nostr.context.CommandContext; +import java.io.IOException; + public interface CommandHandler { - void handle(CommandContext context); + void handle(CommandContext context) throws IOException; } diff --git a/nostr-java-command-provider/src/main/java/module-info.java b/nostr-java-command-provider/src/main/java/module-info.java deleted file mode 100644 index 34f9f62c2..000000000 --- a/nostr-java-command-provider/src/main/java/module-info.java +++ /dev/null @@ -1,31 +0,0 @@ -import nostr.command.provider.AuthCommandHandler; -import nostr.command.provider.ClosedCommandHandler; -import nostr.command.provider.EoseCommandHandler; -import nostr.command.provider.EventCommandHandler; -import nostr.command.provider.NoticeCommandHandler; -import nostr.command.provider.OkCommandHandler; -import nostr.command.CommandHandler; - -module nostr.command.provider { - requires static lombok; - requires java.logging; - - requires nostr.base; - requires nostr.id; - requires nostr.client; - requires nostr.event; - requires nostr.context; - requires nostr.context.impl; - requires nostr.command.handler; - requires com.fasterxml.jackson.core; - - exports nostr.command.provider; - - provides CommandHandler with - OkCommandHandler, - NoticeCommandHandler, - EoseCommandHandler, - AuthCommandHandler, - EventCommandHandler, - ClosedCommandHandler; -} diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/AbstractCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/AbstractCommandHandler.java new file mode 100644 index 000000000..c73ea3692 --- /dev/null +++ b/nostr-java-command-provider/src/main/java/nostr/command/provider/AbstractCommandHandler.java @@ -0,0 +1,14 @@ +package nostr.command.provider; + +import lombok.Getter; +import nostr.client.springwebsocket.WebSocketClientIF; +import nostr.command.CommandHandler; + +@Getter +public abstract class AbstractCommandHandler implements CommandHandler { + private final WebSocketClientIF client; + + public AbstractCommandHandler(WebSocketClientIF client) { + this.client = client; + } +} diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/AuthCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/AuthCommandHandler.java index 001c29f54..3eab74aba 100644 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/AuthCommandHandler.java +++ b/nostr-java-command-provider/src/main/java/nostr/command/provider/AuthCommandHandler.java @@ -1,14 +1,11 @@ package nostr.command.provider; -import lombok.NoArgsConstructor; import lombok.NonNull; -import lombok.SneakyThrows; import lombok.extern.java.Log; import nostr.base.Command; import nostr.base.PrivateKey; import nostr.base.annotation.DefaultHandler; -import nostr.client.Client; -import nostr.command.CommandHandler; +import nostr.client.springwebsocket.WebSocketClientIF; import nostr.context.CommandContext; import nostr.context.impl.DefaultCommandContext; import nostr.event.impl.CanonicalAuthenticationEvent; @@ -16,16 +13,19 @@ import nostr.event.message.RelayAuthenticationMessage; import nostr.id.Identity; +import java.io.IOException; import java.util.logging.Level; @DefaultHandler(command = Command.AUTH) -@NoArgsConstructor @Log -public class AuthCommandHandler implements CommandHandler { +public class AuthCommandHandler extends AbstractCommandHandler { + + public AuthCommandHandler(WebSocketClientIF client) { + super(client); + } - @SneakyThrows @Override - public void handle(@NonNull CommandContext context) { + public void handle(@NonNull CommandContext context) throws IOException { log.log(Level.INFO, "onAuth event - {0}", context); @@ -47,13 +47,12 @@ public void handle(@NonNull CommandContext context) { // Sign the event identity.sign(canonicalAuthenticationEvent); - var client = Client.getInstance(); var canonicalAuthenticationMessage = new CanonicalAuthenticationMessage(canonicalAuthenticationEvent); var encodedMessage = canonicalAuthenticationMessage.encode(); log.log(Level.INFO, "Sending authentication event {0} to the relay {1}", new Object[]{encodedMessage, relay}); // Publish the event to the relay - client.send(canonicalAuthenticationMessage); + getClient().send(canonicalAuthenticationMessage); } } else { throw new IllegalArgumentException("Invalid context type"); diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/ClosedCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/ClosedCommandHandler.java index 20400e981..ca0c2e9d2 100644 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/ClosedCommandHandler.java +++ b/nostr-java-command-provider/src/main/java/nostr/command/provider/ClosedCommandHandler.java @@ -1,13 +1,10 @@ package nostr.command.provider; -import lombok.NoArgsConstructor; -import lombok.SneakyThrows; import lombok.extern.java.Log; import nostr.base.Command; import nostr.base.PrivateKey; import nostr.base.annotation.DefaultHandler; -import nostr.client.Client; -import nostr.command.CommandHandler; +import nostr.client.springwebsocket.WebSocketClientIF; import nostr.context.CommandContext; import nostr.context.impl.DefaultCommandContext; import nostr.event.impl.CanonicalAuthenticationEvent; @@ -15,16 +12,19 @@ import nostr.event.message.ClosedMessage; import nostr.id.Identity; +import java.io.IOException; import java.util.logging.Level; -@DefaultHandler(command = Command.CLOSED) -@NoArgsConstructor @Log -public class ClosedCommandHandler implements CommandHandler { +@DefaultHandler(command = Command.CLOSED) +public class ClosedCommandHandler extends AbstractCommandHandler { + + public ClosedCommandHandler(WebSocketClientIF client) { + super(client); + } - @SneakyThrows @Override - public void handle(CommandContext context) { + public void handle(CommandContext context) throws IOException { log.info("onClosed event {0}" + context); @@ -47,13 +47,12 @@ public void handle(CommandContext context) { // Sign the event identity.sign(canonicalAuthenticationEvent); - var client = Client.getInstance(); // No need to pass the request context here. The client will use the default one var canonicalAuthenticationMessage = new CanonicalAuthenticationMessage(canonicalAuthenticationEvent); var encodedMessage = canonicalAuthenticationMessage.encode(); log.log(Level.INFO, "Sending authentication event {0} to the relay {1}", new Object[]{encodedMessage, relay}); // Publish the event to the relay - client.send(canonicalAuthenticationMessage); + getClient().send(canonicalAuthenticationMessage); } } } else { diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/EoseCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/EoseCommandHandler.java index 3b9df1846..347fc58ad 100644 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/EoseCommandHandler.java +++ b/nostr-java-command-provider/src/main/java/nostr/command/provider/EoseCommandHandler.java @@ -1,18 +1,20 @@ package nostr.command.provider; -import lombok.NoArgsConstructor; import lombok.extern.java.Log; import nostr.base.Command; import nostr.base.annotation.DefaultHandler; -import nostr.command.CommandHandler; +import nostr.client.springwebsocket.WebSocketClientIF; import nostr.context.CommandContext; import java.util.logging.Level; @Log @DefaultHandler(command = Command.EOSE) -@NoArgsConstructor -public class EoseCommandHandler implements CommandHandler { +public class EoseCommandHandler extends AbstractCommandHandler { + + public EoseCommandHandler(WebSocketClientIF client) { + super(client); + } @Override public void handle(CommandContext context) { diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/EventCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/EventCommandHandler.java index ac2fb75fc..a37a59b81 100644 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/EventCommandHandler.java +++ b/nostr-java-command-provider/src/main/java/nostr/command/provider/EventCommandHandler.java @@ -1,18 +1,20 @@ package nostr.command.provider; -import lombok.NoArgsConstructor; import lombok.extern.java.Log; import nostr.base.Command; import nostr.base.annotation.DefaultHandler; -import nostr.command.CommandHandler; +import nostr.client.springwebsocket.WebSocketClientIF; import nostr.context.CommandContext; import java.util.logging.Level; @Log @DefaultHandler(command = Command.EVENT) -@NoArgsConstructor -public class EventCommandHandler implements CommandHandler { +public class EventCommandHandler extends AbstractCommandHandler { + + public EventCommandHandler(WebSocketClientIF client) { + super(client); + } @Override public void handle(CommandContext context) { diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/NoticeCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/NoticeCommandHandler.java index 22d862036..5ccc0aa7d 100644 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/NoticeCommandHandler.java +++ b/nostr-java-command-provider/src/main/java/nostr/command/provider/NoticeCommandHandler.java @@ -1,18 +1,21 @@ package nostr.command.provider; -import lombok.NoArgsConstructor; import lombok.extern.java.Log; import nostr.base.Command; import nostr.base.annotation.DefaultHandler; -import nostr.command.CommandHandler; +import nostr.client.springwebsocket.WebSocketClientIF; import nostr.context.CommandContext; import java.util.logging.Level; @Log @DefaultHandler(command = Command.NOTICE) -@NoArgsConstructor -public class NoticeCommandHandler implements CommandHandler { +public class NoticeCommandHandler extends AbstractCommandHandler { + + public NoticeCommandHandler(WebSocketClientIF client) { + super(client); + } + @Override public void handle(CommandContext context) { log.log(Level.INFO, "onNotice event - {0}", context); diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/OkCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/OkCommandHandler.java index 5f2c1d982..20d44f3a4 100644 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/OkCommandHandler.java +++ b/nostr-java-command-provider/src/main/java/nostr/command/provider/OkCommandHandler.java @@ -1,18 +1,20 @@ package nostr.command.provider; -import lombok.NoArgsConstructor; import lombok.extern.java.Log; import nostr.base.Command; import nostr.base.annotation.DefaultHandler; -import nostr.command.CommandHandler; +import nostr.client.springwebsocket.WebSocketClientIF; import nostr.context.CommandContext; import java.util.logging.Level; @Log @DefaultHandler(command = Command.OK) -@NoArgsConstructor -public class OkCommandHandler implements CommandHandler { +public class OkCommandHandler extends AbstractCommandHandler { + + public OkCommandHandler(WebSocketClientIF client) { + super(client); + } @Override public void handle(CommandContext context) { diff --git a/nostr-java-connection/pom.xml b/nostr-java-connection/pom.xml deleted file mode 100644 index f27b6bfd8..000000000 --- a/nostr-java-connection/pom.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - 4.0.0 - - - xyz.tcheeric - nostr-java - 0.6.3-SNAPSHOT - - - nostr-java-connection - jar - - - - - ${project.groupId} - nostr-java-event - ${project.version} - - - ${project.groupId} - nostr-java-util - ${project.version} - - - ${project.groupId} - nostr-java-controller - ${project.version} - compile - - - com.squareup.okio - okio - ${okio.version} - - - com.squareup.okhttp3 - okhttp - ${okhttp.version} - - - - diff --git a/nostr-java-connection/src/main/java/module-info.java b/nostr-java-connection/src/main/java/module-info.java deleted file mode 100644 index 974504604..000000000 --- a/nostr-java-connection/src/main/java/module-info.java +++ /dev/null @@ -1,21 +0,0 @@ - -module nostr.connection { - - requires static lombok; - - requires okio; - requires okhttp3; - - requires java.logging; - - requires nostr.base; - requires nostr.event; - requires nostr.controller; - requires nostr.context; - requires nostr.context.impl; - requires org.bouncycastle.provider; - - exports nostr.connection; - exports nostr.connection.impl; - exports nostr.connection.impl.listeners; -} diff --git a/nostr-java-connection/src/main/java/nostr/connection/Connection.java b/nostr-java-connection/src/main/java/nostr/connection/Connection.java deleted file mode 100644 index b6c328e95..000000000 --- a/nostr-java-connection/src/main/java/nostr/connection/Connection.java +++ /dev/null @@ -1,17 +0,0 @@ -package nostr.connection; - -import nostr.base.Relay; - -public interface Connection { - - void send(String message); - - void connect(); - - void disconnect(); - - Relay getRelay(); - - boolean isConnected(); - -} diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionImpl.java b/nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionImpl.java deleted file mode 100644 index 61c1640c6..000000000 --- a/nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionImpl.java +++ /dev/null @@ -1,103 +0,0 @@ -package nostr.connection.impl; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NonNull; -import lombok.ToString; -import lombok.extern.java.Log; -import nostr.base.Relay; -import nostr.connection.Connection; -import nostr.connection.impl.listeners.CloseListener; -import nostr.connection.impl.listeners.CompositeListener; -import nostr.connection.impl.listeners.ErrorListener; -import nostr.connection.impl.listeners.OpenListener; -import nostr.connection.impl.listeners.TextListener; -import nostr.context.Context; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.WebSocket; - -import java.util.Arrays; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Level; - -@Log -@ToString(onlyExplicitlyIncluded = true) -@EqualsAndHashCode(onlyExplicitlyIncluded = true) -public class ConnectionImpl implements Connection { - - @Getter - @EqualsAndHashCode.Include - @ToString.Include - private final String id = UUID.randomUUID().toString(); - - @Getter - @EqualsAndHashCode.Include - @ToString.Include - private final Relay relay; - - private final Context context; - - private WebSocket webSocket = null; - - private final AtomicBoolean connected = new AtomicBoolean(false); - - public ConnectionImpl(@NonNull Relay relay, @NonNull Context context) { - this.relay = relay; - this.context = context; - } - - @Override - public void connect() { - var relay = getRelay(); - - try { - if (isConnected()) { - log.log(Level.FINE, "Already connected to {0}. Do nothing...", relay); - return; - } - - log.log(Level.INFO, "Connecting to {0}... httpUrl = {1}", new Object[]{relay, HttpUrl.parse(relay.getHttpUri())}); - var client = new OkHttpClient(); - var compositeListener = new CompositeListener(Arrays.asList(new OpenListener(relay), new TextListener(relay, context), new CloseListener(relay), new ErrorListener(relay))); - - webSocket = client.newWebSocket((new Request.Builder()).url(HttpUrl.parse(relay.getHttpUri())).build(), compositeListener); - //.connectTimeout(Duration.ofMillis(10000)) // TODO - make this configurable and add to the context. - - connected.set(true); - - log.log(Level.INFO, "Connected to {0}", getRelay()); - } catch (Exception e) { - log.log(Level.SEVERE, "Failed to connect to {0} - {1}", new Object[]{relay, e.getMessage()}); - connected.set(false); - } - } - - @Override - public boolean isConnected() { - return connected.get() && webSocket != null; - } - - @Override - public void send(@NonNull String message) { - if (!isConnected()) { - log.log(Level.WARNING, "FAIL - Not properly connected with Relay: {0}", relay); - return; - } - - log.log(Level.INFO, "Sending message: {0} - Relay: {1}", new Object[]{message, relay}); - webSocket.send(message); - } - - @Override - public void disconnect() { - if (isConnected()) { - log.log(Level.INFO, "Disconnecting from {0}", relay); - // NORMAL_CLOSURE is 1000 - webSocket.close(1000, "bye"); // TODO check return result of close - } - connected.set(false); - } -} \ No newline at end of file diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionPool.java b/nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionPool.java deleted file mode 100644 index 44e144813..000000000 --- a/nostr-java-connection/src/main/java/nostr/connection/impl/ConnectionPool.java +++ /dev/null @@ -1,105 +0,0 @@ -package nostr.connection.impl; - -import lombok.Getter; -import lombok.NonNull; -import lombok.extern.java.Log; -import nostr.base.Relay; -import nostr.connection.Connection; -import nostr.context.Context; -import nostr.context.impl.DefaultRequestContext; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Level; - -@Getter -@Log -public class ConnectionPool { - - private static class Holder { - private static final ConnectionPool INSTANCE = new ConnectionPool(); - } - - private final Set connections = Collections.synchronizedSet(new HashSet<>()); - - private ConnectionPool() { - // private constructor to prevent instantiation - } - - public static ConnectionPool getInstance(@NonNull Context context) { - if (Holder.INSTANCE.connections.isEmpty() && context instanceof DefaultRequestContext defaultRequestContext) { - var relays = defaultRequestContext.getRelays(); - relays.values().stream().map(Relay::new).forEach(r -> Holder.INSTANCE.addConnection(new ConnectionImpl(r, context))); - } - return Holder.INSTANCE; - } - - public void connect() { - log.log(Level.INFO, "Connecting to relays"); - connections.forEach(c -> { - c.connect(); - }); - } - - public void disconnect() { - log.log(Level.INFO, "Disconnecting from relays"); - connections.forEach(connection -> { - connection.disconnect(); - }); - } - - public void disconnect(@NonNull Relay relay) { - log.log(Level.INFO, "Disconnecting from {0}...", relay); - var connection = getConnection(relay); - if (connection != null) { - connection.disconnect(); - } else { - log.log(Level.WARNING, "No connection found for {0}. Ignoring...", relay); - } - } - - public Connection getConnection(@NonNull Relay relay) { - return connections.stream().filter(c -> c.getRelay().getUri().equalsIgnoreCase(relay.getUri())).findFirst().orElse(null); - } - - public boolean isConnectedTo(@NonNull Relay relay) { - return connections.stream().filter(c -> c.getRelay().equals(relay)).anyMatch(Connection::isConnected); - } - - public int connectionCount() { - return (int) connections.stream().filter(Connection::isConnected).count(); - } - - public void send(@NonNull String message) { - log.log(Level.FINER, "Connected to {0} relay(s)...", connections.size()); - connections.forEach(conn -> { - conn.send(message); - }); - } - - public void send(@NonNull String message, @NonNull Relay relay) { - log.log(Level.FINER, "ConnectionPool.send({0}, {1}) / {2} connection(s)", new Object[]{message, relay, connections.size()}); - var connection = getConnection(relay); - if (connection != null) { - log.log(Level.FINE, "Trying to send {0} to {1}...", new Object[]{message, relay}); - connection.send(message); - } - else { - log.log(Level.WARNING, "No connection found for {0}", relay); - } - } - - private boolean addConnection(@NonNull Connection connection) { - return connections.add(connection); - } - - private boolean removeConnection(@NonNull Connection connection) { - return connections.remove(connection); - } - - private void removeAllConnections() { - connections.clear(); - } - -} \ No newline at end of file diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CloseListener.java b/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CloseListener.java deleted file mode 100644 index f064bee5c..000000000 --- a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CloseListener.java +++ /dev/null @@ -1,40 +0,0 @@ -package nostr.connection.impl.listeners; - -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.ToString; -import lombok.extern.java.Log; -import nostr.base.Relay; -import nostr.event.Response; - -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Level; - -@AllArgsConstructor -@Log -public class CloseListener extends WebSocketListener { - - @Getter - @EqualsAndHashCode.Include - @ToString.Include - private final Relay relay; - - private final Set responses = Collections.synchronizedSet(new HashSet<>()); - - @Override - public void onClosed(WebSocket webSocket, int statusCode, String reason) { - log.log(Level.INFO, "WebSocket connection to {0} closed: {1}, {2}", new Object[]{relay, statusCode, reason}); - responses.clear(); - } - - @Override - public void onClosing(WebSocket webSocket, int statusCode, String reason) { - log.log(Level.INFO, "WebSocket connection to {0} closing: {1}, {2}", new Object[]{relay, statusCode, reason}); - responses.clear(); - } -} diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CompositeListener.java b/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CompositeListener.java deleted file mode 100644 index a7f696bca..000000000 --- a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/CompositeListener.java +++ /dev/null @@ -1,46 +0,0 @@ -package nostr.connection.impl.listeners; - -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; -import okhttp3.Response; -import okio.ByteString; - -import java.util.List; - -public class CompositeListener extends WebSocketListener { - private final List listeners; - - public CompositeListener(List listeners) { - this.listeners = listeners; - } - - @Override - public void onOpen(WebSocket webSocket, Response response) { - listeners.forEach(listener -> listener.onOpen(webSocket, response)); - } - - @Override - public void onMessage(WebSocket webSocket, String data) { - listeners.forEach(listener -> listener.onMessage(webSocket, data)); - } - - @Override - public void onMessage(WebSocket webSocket, ByteString bytes) { - listeners.forEach(listener -> listener.onMessage(webSocket, bytes)); - } - - @Override - public void onClosing(WebSocket webSocket, int statusCode, String reason) { - listeners.forEach(listener -> listener.onClosing(webSocket, statusCode, reason)); - } - - @Override - public void onClosed(WebSocket webSocket, int statusCode, String reason) { - listeners.forEach(listener -> listener.onClosed(webSocket, statusCode, reason)); - } - - @Override - public void onFailure(WebSocket webSocket, Throwable error, Response response) { - listeners.forEach(listener -> listener.onFailure(webSocket, error, response)); - } -} diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/ErrorListener.java b/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/ErrorListener.java deleted file mode 100644 index 607159813..000000000 --- a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/ErrorListener.java +++ /dev/null @@ -1,29 +0,0 @@ -package nostr.connection.impl.listeners; - -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.ToString; -import lombok.extern.java.Log; -import nostr.base.Relay; - -import okhttp3.WebSocketListener; -import okhttp3.WebSocket; -import okhttp3.Response; -import java.util.logging.Level; - -@Getter -@Log -@AllArgsConstructor -public class ErrorListener extends WebSocketListener { - - @EqualsAndHashCode.Include - @ToString.Include - private final Relay relay; - - @Override - public void onFailure(WebSocket webSocket, Throwable error, Response response) { - log.log(Level.WARNING, "WebSocket error: {0} - Relay {1}", new Object[]{error, relay}); - error.printStackTrace(); - } -} diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/OpenListener.java b/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/OpenListener.java deleted file mode 100644 index fb3e69d74..000000000 --- a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/OpenListener.java +++ /dev/null @@ -1,29 +0,0 @@ -package nostr.connection.impl.listeners; - -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.ToString; -import lombok.extern.java.Log; -import nostr.base.Relay; - -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; -import okhttp3.Response; -import java.util.logging.Level; - -@Getter -@AllArgsConstructor -@Log -public class OpenListener extends WebSocketListener { - - @Getter - @EqualsAndHashCode.Include - @ToString.Include - private final Relay relay; - - @Override - public void onOpen(WebSocket webSocket, Response response) { - log.log(Level.INFO, "WebSocket opened to {0}", relay); - } -} diff --git a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/TextListener.java b/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/TextListener.java deleted file mode 100644 index 7695e8a7e..000000000 --- a/nostr-java-connection/src/main/java/nostr/connection/impl/listeners/TextListener.java +++ /dev/null @@ -1,88 +0,0 @@ -package nostr.connection.impl.listeners; - -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NonNull; -import lombok.ToString; -import lombok.extern.java.Log; -import nostr.base.Relay; -import nostr.context.CommandContext; -import nostr.context.Context; -import nostr.context.impl.DefaultCommandContext; -import nostr.context.impl.DefaultRequestContext; -import nostr.controller.impl.ApplicationControllerImpl; -import nostr.event.BaseMessage; -import nostr.event.json.codec.BaseMessageDecoder; -import nostr.event.message.ClosedMessage; -import nostr.event.message.RelayAuthenticationMessage; - -import okhttp3.WebSocketListener; -import okhttp3.WebSocket; -import okio.ByteString; - -import java.util.logging.Level; - -@AllArgsConstructor -@Log -public class TextListener extends WebSocketListener { - - @Getter - @EqualsAndHashCode.Include - @ToString.Include - private final Relay relay; - - private Context context; - - @Override - public void onMessage(WebSocket webSocket, String message) { - // nocheckin - log.log(Level.INFO, "WebSocket received: " + message + " - Relay: {0}", new Object[]{relay}); - handleReceivedText(message); - } - - @Override - public void onMessage(WebSocket webSocket, ByteString bytes) { - String message = bytes.toString(); - log.log(Level.INFO, "WebSocket received: {0} - Relay: {1}", new Object[]{message, relay}); - handleReceivedText(message); - } - - private void handleReceivedText(@NonNull String message) { - log.log(Level.INFO, "Received message {0} from {1}", new Object[]{message, relay}); - - var msg = new BaseMessageDecoder<>().decode(message); - final String strCommand = msg.getCommand(); - - log.log(Level.FINE, "Creating the command context with message {0}", new Object[]{msg}); - Context commandContext = createCommandContext(msg); - var applicationController = new ApplicationControllerImpl(strCommand); - - applicationController.handleRequest(commandContext); - } - - private CommandContext createCommandContext(@NonNull BaseMessage message) { - var commandContext = new DefaultCommandContext(); - - // Pass on the message - commandContext.setMessage(message); - - // Pass on the private key - commandContext.setPrivateKey(((DefaultRequestContext) this.context).getPrivateKey()); - - // Pass on the first relay - commandContext.setRelay(relay); - - // Set the challenge - if (message instanceof RelayAuthenticationMessage authMessage) { - log.log(Level.FINE, "Setting the challenge {0} for the relay {1}", new Object[]{authMessage.getChallenge(), relay}); - commandContext.setChallenge(authMessage.getChallenge()); - } - - if (message instanceof ClosedMessage) { - commandContext.setChallenge(((DefaultRequestContext) this.context).getChallenge(relay)); - } - - return commandContext; - } -} diff --git a/nostr-java-controller/src/main/java/nostr/controller/impl/ApplicationControllerImpl.java b/nostr-java-controller/src/main/java/nostr/controller/impl/ApplicationControllerImpl.java index 3421c9ff7..523681900 100644 --- a/nostr-java-controller/src/main/java/nostr/controller/impl/ApplicationControllerImpl.java +++ b/nostr-java-controller/src/main/java/nostr/controller/impl/ApplicationControllerImpl.java @@ -12,6 +12,7 @@ import nostr.controller.ApplicationController; import nostr.util.thread.ThreadUtil; +import java.io.IOException; import java.util.Optional; import java.util.ServiceLoader; import java.util.concurrent.TimeoutException; @@ -43,14 +44,14 @@ public void handleRequest(Context requestContext) { } @Override - public Void execute(@NonNull Context context) { + public Void execute(@NonNull Context context) throws IOException { if (context instanceof DefaultCommandContext commandContext) { executeCommand(commandContext); } return null; } - private void executeCommand(@NonNull DefaultCommandContext defaultCommandContext) { + private void executeCommand(@NonNull DefaultCommandContext defaultCommandContext) throws IOException { var commandHandler = getCommandHandler(this.command); diff --git a/nostr-java-crypto/pom.xml b/nostr-java-crypto/pom.xml index bade534fb..ffc0034a1 100644 --- a/nostr-java-crypto/pom.xml +++ b/nostr-java-crypto/pom.xml @@ -37,4 +37,4 @@ ${project.version} - \ No newline at end of file + diff --git a/nostr-java-event/src/main/java/nostr/event/impl/CalendarContent.java b/nostr-java-event/src/main/java/nostr/event/impl/CalendarContent.java index 41204381d..18358fbec 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/CalendarContent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/CalendarContent.java @@ -6,11 +6,8 @@ import lombok.EqualsAndHashCode; import lombok.NonNull; import nostr.event.AbstractEventContent; -import nostr.event.tag.GeohashTag; -import nostr.event.tag.HashtagTag; import nostr.event.tag.IdentifierTag; import nostr.event.tag.PubKeyTag; -import nostr.event.tag.ReferenceTag; import java.util.List; @@ -20,25 +17,25 @@ @EqualsAndHashCode(callSuper = false) public class CalendarContent extends AbstractEventContent { //@JsonProperty - private final String id; + private String id; // below fields mandatory - private final IdentifierTag identifierTag; - private final String title; - private final Long start; + private IdentifierTag identifierTag; + private String title; + private Long start; // below fields optional - private Long end; - private String startTzid; - private String endTzid; +// private Long end; +// private String startTzid; +// private String endTzid; private String summary; - private String image; +// private String image; private String location; - private GeohashTag geohashTag; +// private GeohashTag geohashTag; private List participantPubKeys; - private List labels; - private List hashtagTags; - private List referenceTags; +// private List labels; +// private List hashtagTags; +// private List referenceTags; public static CalendarContentBuilder builder(@NonNull IdentifierTag identifierTag, @NonNull String title, @NonNull Long start) { return new CalendarContentBuilder() diff --git a/nostr-java-event/src/main/java/nostr/event/impl/CalendarTimeBasedEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/CalendarTimeBasedEvent.java index 812f73146..c159416ab 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/CalendarTimeBasedEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/CalendarTimeBasedEvent.java @@ -42,20 +42,24 @@ public CalendarTimeBasedEvent(@NonNull PublicKey sender, @NonNull List } private void mapCustomTags() { +// below, required addStandardTag(calendarContent.getIdentifierTag()); addGenericTag("title", getNip(), calendarContent.getTitle()); addGenericTag("start", getNip(), calendarContent.getStart()); - addGenericTag("end", getNip(), calendarContent.getEnd()); - addGenericTag("start_tzid", getNip(), calendarContent.getStartTzid()); - addGenericTag("end_tzid", getNip(), calendarContent.getEndTzid()); + +// below, optional +// addGenericTag("end", getNip(), calendarContent.getEnd()); +// addGenericTag("start_tzid", getNip(), calendarContent.getStartTzid()); +// addGenericTag("end_tzid", getNip(), calendarContent.getEndTzid()); addGenericTag("summary", getNip(), calendarContent.getSummary()); - addGenericTag("image", getNip(), calendarContent.getImage()); +// addGenericTag("image", getNip(), calendarContent.getImage()); addGenericTag("location", getNip(), calendarContent.getLocation()); - addStandardTag(calendarContent.getGeohashTag()); +// addStandardTag(calendarContent.getGeohashTag()); addStandardTag(calendarContent.getParticipantPubKeys()); - addStringListTag("l", getNip(), calendarContent.getLabels()); - addStandardTag(calendarContent.getHashtagTags()); - addStandardTag(calendarContent.getReferenceTags()); + +// addStringListTag("l", getNip(), calendarContent.getLabels()); +// addStandardTag(calendarContent.getHashtagTags()); +// addStandardTag(calendarContent.getReferenceTags()); } public static class CalendarTimeBasedEventDeserializer extends StdDeserializer { diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java index 40dadbbb7..4a2bd3bc2 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java @@ -1,6 +1,7 @@ package nostr.event.json.codec; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.NonNull; import lombok.SneakyThrows; @@ -25,13 +26,12 @@ public class BaseMessageDecoder implements IDecoder { private final ObjectMapper mapper; public BaseMessageDecoder() { - mapper = new ObjectMapper(); - mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + this.mapper = new ObjectMapper(); + this.mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); } - @SneakyThrows @Override - public T decode(@NonNull String jsonString) { + public T decode(@NonNull String jsonString) throws JsonProcessingException { Object[] msgArr = mapper.readValue(jsonString, Object[].class); final String strCmd = msgArr[0].toString(); final Object arg = msgArr[1]; diff --git a/nostr-java-event/src/main/java/nostr/event/message/EventMessage.java b/nostr-java-event/src/main/java/nostr/event/message/EventMessage.java index 9c81a7fd2..7caf29d7d 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/EventMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/EventMessage.java @@ -51,15 +51,25 @@ public String encode() throws JsonProcessingException { public static T decode(@NonNull Object[] msgArr, ObjectMapper mapper) { var arg = msgArr[1]; if (msgArr.length == 2 && arg instanceof Map map) { - var event = mapper.convertValue(map, new TypeReference() {}); - return (T) new EventMessage(event); - } else if (msgArr.length == 3 && arg instanceof String) { - var subId = arg.toString(); + return (T) new EventMessage( + convertValue(mapper, map) + ); + } + + if (msgArr.length == 3 && arg instanceof String) { if (msgArr[2] instanceof Map map) { - var event = mapper.convertValue(map, new TypeReference() {}); - return (T) new EventMessage(event, subId); + return (T) new EventMessage( + convertValue(mapper, map), + arg.toString() + ); } } + throw new AssertionError("Invalid argument: " + arg); } + + private static GenericEvent convertValue(ObjectMapper mapper, Map map) { + return mapper.convertValue(map, new TypeReference() { + }); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java index b750e34f7..835bc106c 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java @@ -14,6 +14,7 @@ import nostr.event.impl.Filters; import nostr.event.json.codec.FiltersEncoder; +import java.time.temporal.ValueRange; import java.util.ArrayList; import java.util.List; @@ -38,6 +39,9 @@ public ReqMessage(String subscriptionId, Filters filters) { public ReqMessage(String subscriptionId, List incomingFiltersList) { super(Command.REQ.name()); + if (!ValueRange.of(1, 64).isValidIntValue(subscriptionId.length())) { + throw new IllegalArgumentException("subscriptionId must be between 1 and 64 characters"); + } this.subscriptionId = subscriptionId; this.filtersList = new ArrayList<>(); this.filtersList.addAll(incomingFiltersList); @@ -67,6 +71,6 @@ public static T decode(@NonNull Object[] msgArr, ObjectM System.arraycopy(msgArr, 2, filtersArr, 0, len); var filtersList = mapper.convertValue(filtersArr, new TypeReference>() { }); - return (T) new ReqMessage(msgArr[1].toString(), filtersList); + return (T) new ReqMessage(msgArr[1].toString(), filtersList); } } diff --git a/nostr-java-test/src/test/java/nostr/test/client/ClientTest.java b/nostr-java-test/src/test/java/nostr/test/client/ClientTest.java deleted file mode 100644 index 3fdc5a491..000000000 --- a/nostr-java-test/src/test/java/nostr/test/client/ClientTest.java +++ /dev/null @@ -1,145 +0,0 @@ -package nostr.test.client; - -import lombok.extern.java.Log; -import nostr.base.PrivateKey; -import nostr.client.Client; -import nostr.context.impl.DefaultRequestContext; -import nostr.event.BaseMessage; -import nostr.event.message.EventMessage; -import nostr.id.Identity; -import nostr.test.EntityFactory; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.TimeoutException; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * @author squirrel - */ -@Log -class ClientTest { - - private Client client; - private Identity identity; - - public ClientTest() { - } - - @BeforeEach - public void init() { - log.info("init"); - identity = Identity.create(PrivateKey.generateRandomPrivKey()); - - DefaultRequestContext requestContext = new DefaultRequestContext(); - requestContext.setPrivateKey(identity.getPrivateKey().getRawData()); - //requestContext.setRelays(Map.of("My local test relay", "ws://localhost:5555")); - Properties loadedProperties = getRelayProperties(); - requestContext.setRelays(convertPropertiesToMap(loadedProperties)); - try { - client = Client.getInstance().connect(requestContext); - } catch (TimeoutException e) { - throw new RuntimeException(e); - } - } - - //@AfterEach - public void dispose() { - log.info("dispose"); - try { - this.client.disconnect(); - } catch (TimeoutException e) { - throw new RuntimeException(e); - } - this.client = null; - this.identity = null; - } - - @Test - public void testSend() { - log.info("testSend"); - var event = EntityFactory.Events.createTextNoteEvent(identity.getPublicKey()); - identity.sign(event); - BaseMessage msg = new EventMessage(event); - assertDoesNotThrow(() -> { - client.send(msg); - }); - assertTrue(true); - } - - @Test - public void disconnect() { - log.info("disconnect"); - - int relayCount = getRelayCount(); - assertEquals(relayCount, client.getOpenConnectionsCount()); - assertDoesNotThrow(() -> { - client.disconnect(); - }); - assertEquals(0, client.getOpenConnectionsCount()); - - assertDoesNotThrow(() -> { - client.disconnect(); // if all connections are closed, trying to disconnect again wont throw error - }); - } - -/* - @Test - public void testNip42() { - System.out.println("testNip42"); - - var rcpt = Identity.generateRandomIdentity().getPublicKey(); - var sender = identity.getPublicKey(); - var event = EntityFactory.Events.createDirectMessageEvent(sender, rcpt, "Hello, World!"); - identity.sign(event); - BaseMessage msg = new EventMessage(event); - assertDoesNotThrow(() -> { - client.send(msg); - - var filters = Filters.builder().kinds(new KindList(4)).authors(new PublicKeyList(sender)).build(); - msg = new ReqMessage("testNip42_" + sender.toString(), filters); - client.send(msg); - }); - } -*/ - - private Properties getRelayProperties() { - Properties properties = new Properties(); - try (InputStream is = getClass().getClassLoader().getResourceAsStream("relays.properties")) { - if (is != null) { - properties.load(is); - } else { - throw new IOException("Cannot find relays.properties on the classpath."); - } - } catch (IOException e) { - log.severe(e.getMessage()); - } - return properties; - } - - private int getRelayCount() { - return getRelayProperties().size(); - } - - public static Map convertPropertiesToMap(Properties properties) { - Map map = new HashMap<>(); - - // Iterate over the properties and put each entry into the map - Set propertyNames = properties.stringPropertyNames(); - for (String name : propertyNames) { - map.put(name, properties.getProperty(name)); - } - - return map; - } - -} diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java index a146f0d25..cb3ddbe06 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java @@ -320,10 +320,10 @@ public void testNIP52CalendarTimeBasedEventEvent() throws IOException { "Calendar Time-Based Event title", 1716513986268L).build(); - calendarContent.setStartTzid("1687765220"); - calendarContent.setEndTzid("1687765230"); + // calendarContent.setStartTzid("1687765220"); + // calendarContent.setEndTzid("1687765230"); - calendarContent.setLabels(List.of("english", "mycenaean greek")); + // calendarContent.setLabels(List.of("english", "mycenaean greek")); List tags = new ArrayList<>(); tags.add(new PubKeyTag(new PublicKey("2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76985"), diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientIFTest.java similarity index 98% rename from nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientTest.java rename to nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientIFTest.java index a71f2c04c..19ce7f752 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientIFTest.java @@ -21,7 +21,7 @@ import static nostr.test.event.ApiEventTest.createProduct; import static nostr.test.event.ApiEventTest.createStall; -class ApiEventTestUsingSpringWebSocketClientTest { +class ApiEventTestUsingSpringWebSocketClientIFTest { private static Map relays; private SpringWebSocketClient springWebSocketClient; diff --git a/nostr-java-test/src/test/java/nostr/test/event/NIP52ImplTest.java b/nostr-java-test/src/test/java/nostr/test/event/NIP52ImplTest.java index e4400f76f..3e08ad91d 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/NIP52ImplTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/NIP52ImplTest.java @@ -55,12 +55,12 @@ static void setup() { .build(); timeBasedCalendarContent.setParticipantPubKeys(List.of(P_1_TAG, P_2_TAG)); - timeBasedCalendarContent.setGeohashTag(G_TAG); - timeBasedCalendarContent.setHashtagTags(List.of(T_TAG)); - timeBasedCalendarContent.setStartTzid(CALENDAR_TIME_BASED_EVENT_START_TZID); - timeBasedCalendarContent.setEndTzid(START.toString()); +// timeBasedCalendarContent.setGeohashTag(G_TAG); +// timeBasedCalendarContent.setHashtagTags(List.of(T_TAG)); +// timeBasedCalendarContent.setStartTzid(CALENDAR_TIME_BASED_EVENT_START_TZID); +// timeBasedCalendarContent.setEndTzid(START.toString()); Long l = START + 100L; - timeBasedCalendarContent.setEndTzid(l.toString()); +// timeBasedCalendarContent.setEndTzid(l.toString()); timeBasedCalendarContent.setSummary(CALENDAR_TIME_BASED_EVENT_SUMMARY); timeBasedCalendarContent.setLocation(CALENDAR_TIME_BASED_EVENT_LOCATION); timeBasedSender = Identity.generateRandomIdentity(); diff --git a/nostr-java-util/src/main/java/nostr/util/thread/Task.java b/nostr-java-util/src/main/java/nostr/util/thread/Task.java index a82403264..a370fbe49 100644 --- a/nostr-java-util/src/main/java/nostr/util/thread/Task.java +++ b/nostr-java-util/src/main/java/nostr/util/thread/Task.java @@ -3,7 +3,9 @@ import lombok.NonNull; import nostr.context.Context; +import java.io.IOException; + public interface Task { - T execute(@NonNull Context context); + T execute(@NonNull Context context) throws IOException; } diff --git a/pom.xml b/pom.xml index 6b099bf0a..f94b16d0f 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,6 @@ nostr-java-id nostr-java-test nostr-java-util - nostr-java-connection nostr-java-command-interface nostr-java-command-provider nostr-java-client @@ -61,24 +60,25 @@ + ${version} UTF-8 - - 21 - 21 + 21 + ${java.version} + ${java.version} 4.9.3 2.8.0 - 1.18.32 + 1.18.34 1.70 1.78 - 2.14.1 + 2.17.2 - 5.9.1 + 5.10.2 3.23.1 From 2248a8ab54b5819e0832db2f2a6fc504b0a15d12 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sun, 12 Jan 2025 21:23:44 -0500 Subject: [PATCH 02/45] explicit exception handling added for usages of BaseDecoder<>.decode(String) --- nostr-java-client/pom.xml | 8 +----- .../src/main/java/module-info.java | 3 --- .../StandardWebSocketClient.java | 4 +-- .../nostr/event/impl/CalendarContent.java | 27 ++++++++++--------- .../event/impl/CalendarTimeBasedEvent.java | 20 ++++++-------- .../java/nostr/test/event/ApiEventTest.java | 6 ++--- .../nostr/test/event/ApiNIP52RequestTest.java | 10 ++++++- .../nostr/test/event/ApiNIP99RequestTest.java | 10 ++++++- .../java/nostr/test/event/DecodeTest.java | 3 ++- .../java/nostr/test/event/NIP52ImplTest.java | 10 +++---- .../java/nostr/test/json/JsonParseTest.java | 15 ++++++----- 11 files changed, 62 insertions(+), 54 deletions(-) diff --git a/nostr-java-client/pom.xml b/nostr-java-client/pom.xml index 2ad124eea..a605b06da 100644 --- a/nostr-java-client/pom.xml +++ b/nostr-java-client/pom.xml @@ -24,12 +24,6 @@ nostr-java-id ${project.version} - - ${project.groupId} - nostr-java-connection - ${project.version} - compile - org.springframework spring-websocket @@ -64,4 +58,4 @@ 4.2.2 - \ No newline at end of file + diff --git a/nostr-java-client/src/main/java/module-info.java b/nostr-java-client/src/main/java/module-info.java index eb2e20fd9..ff959a1f2 100644 --- a/nostr-java-client/src/main/java/module-info.java +++ b/nostr-java-client/src/main/java/module-info.java @@ -4,9 +4,7 @@ requires java.logging; requires nostr.util; requires nostr.base; - requires nostr.connection; requires nostr.context; - requires nostr.context.impl; requires com.fasterxml.jackson.core; requires reactor.core; requires spring.webflux; @@ -14,7 +12,6 @@ requires spring.beans; requires spring.websocket; requires jakarta.websocket.client; - requires annotations; requires awaitility; exports nostr.client.springwebsocket; diff --git a/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java b/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java index 6bc8e0587..9a9e19b8f 100644 --- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java +++ b/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java @@ -1,9 +1,9 @@ package nostr.client.springwebsocket; import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.NonNull; import lombok.SneakyThrows; import nostr.event.BaseMessage; -import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -33,7 +33,7 @@ public StandardWebSocketClient(@Value("${nostr.relay.uri}") String relayUri) { } @Override - protected void handleTextMessage(@NotNull WebSocketSession session, TextMessage message) { + protected void handleTextMessage(@NonNull WebSocketSession session, TextMessage message) { events.add(message.getPayload()); completed = true; } diff --git a/nostr-java-event/src/main/java/nostr/event/impl/CalendarContent.java b/nostr-java-event/src/main/java/nostr/event/impl/CalendarContent.java index 18358fbec..41204381d 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/CalendarContent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/CalendarContent.java @@ -6,8 +6,11 @@ import lombok.EqualsAndHashCode; import lombok.NonNull; import nostr.event.AbstractEventContent; +import nostr.event.tag.GeohashTag; +import nostr.event.tag.HashtagTag; import nostr.event.tag.IdentifierTag; import nostr.event.tag.PubKeyTag; +import nostr.event.tag.ReferenceTag; import java.util.List; @@ -17,25 +20,25 @@ @EqualsAndHashCode(callSuper = false) public class CalendarContent extends AbstractEventContent { //@JsonProperty - private String id; + private final String id; // below fields mandatory - private IdentifierTag identifierTag; - private String title; - private Long start; + private final IdentifierTag identifierTag; + private final String title; + private final Long start; // below fields optional -// private Long end; -// private String startTzid; -// private String endTzid; + private Long end; + private String startTzid; + private String endTzid; private String summary; -// private String image; + private String image; private String location; -// private GeohashTag geohashTag; + private GeohashTag geohashTag; private List participantPubKeys; -// private List labels; -// private List hashtagTags; -// private List referenceTags; + private List labels; + private List hashtagTags; + private List referenceTags; public static CalendarContentBuilder builder(@NonNull IdentifierTag identifierTag, @NonNull String title, @NonNull Long start) { return new CalendarContentBuilder() diff --git a/nostr-java-event/src/main/java/nostr/event/impl/CalendarTimeBasedEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/CalendarTimeBasedEvent.java index c159416ab..812f73146 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/CalendarTimeBasedEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/CalendarTimeBasedEvent.java @@ -42,24 +42,20 @@ public CalendarTimeBasedEvent(@NonNull PublicKey sender, @NonNull List } private void mapCustomTags() { -// below, required addStandardTag(calendarContent.getIdentifierTag()); addGenericTag("title", getNip(), calendarContent.getTitle()); addGenericTag("start", getNip(), calendarContent.getStart()); - -// below, optional -// addGenericTag("end", getNip(), calendarContent.getEnd()); -// addGenericTag("start_tzid", getNip(), calendarContent.getStartTzid()); -// addGenericTag("end_tzid", getNip(), calendarContent.getEndTzid()); + addGenericTag("end", getNip(), calendarContent.getEnd()); + addGenericTag("start_tzid", getNip(), calendarContent.getStartTzid()); + addGenericTag("end_tzid", getNip(), calendarContent.getEndTzid()); addGenericTag("summary", getNip(), calendarContent.getSummary()); -// addGenericTag("image", getNip(), calendarContent.getImage()); + addGenericTag("image", getNip(), calendarContent.getImage()); addGenericTag("location", getNip(), calendarContent.getLocation()); -// addStandardTag(calendarContent.getGeohashTag()); + addStandardTag(calendarContent.getGeohashTag()); addStandardTag(calendarContent.getParticipantPubKeys()); - -// addStringListTag("l", getNip(), calendarContent.getLabels()); -// addStandardTag(calendarContent.getHashtagTags()); -// addStandardTag(calendarContent.getReferenceTags()); + addStringListTag("l", getNip(), calendarContent.getLabels()); + addStandardTag(calendarContent.getHashtagTags()); + addStandardTag(calendarContent.getReferenceTags()); } public static class CalendarTimeBasedEventDeserializer extends StdDeserializer { diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java index cb3ddbe06..a146f0d25 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java @@ -320,10 +320,10 @@ public void testNIP52CalendarTimeBasedEventEvent() throws IOException { "Calendar Time-Based Event title", 1716513986268L).build(); - // calendarContent.setStartTzid("1687765220"); - // calendarContent.setEndTzid("1687765230"); + calendarContent.setStartTzid("1687765220"); + calendarContent.setEndTzid("1687765230"); - // calendarContent.setLabels(List.of("english", "mycenaean greek")); + calendarContent.setLabels(List.of("english", "mycenaean greek")); List tags = new ArrayList<>(); tags.add(new PubKeyTag(new PublicKey("2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76985"), diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java index 8e78c7e19..7b31376de 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java @@ -7,6 +7,8 @@ import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -155,7 +157,13 @@ void testNIP99CalendarContentPreRequest() throws IOException { private T mapJsonToEvent(List reqResponse, Class clazz) { Optional first = reqResponse .stream() - .map(baseMessage -> new BaseMessageDecoder().decode(baseMessage)) + .map(baseMessage -> { + try { + return new BaseMessageDecoder().decode(baseMessage); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }) .map(eventMessage -> ((GenericEvent) eventMessage.getEvent())) .map(event -> new BaseEventEncoder<>(event).encode()) .map(encode -> new GenericEventDecoder<>(clazz).decode(encode)) diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java index 26e8881c9..dd9f9685c 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java @@ -7,6 +7,8 @@ import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -151,7 +153,13 @@ void testNIP99ClassifiedListingPreRequest() throws IOException { private T mapJsonToEvent(List reqResponse, Class clazz) { Optional first = reqResponse .stream() - .map(baseMessage -> new BaseMessageDecoder().decode(baseMessage)) + .map(baseMessage -> { + try { + return new BaseMessageDecoder().decode(baseMessage); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }) .map(eventMessage -> ((GenericEvent) eventMessage.getEvent())) .map(event -> new BaseEventEncoder<>(event).encode()) .map(encode -> new GenericEventDecoder<>(clazz).decode(encode)) diff --git a/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java b/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java index b75ecdd49..5508422b5 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java @@ -1,5 +1,6 @@ package nostr.test.event; +import com.fasterxml.jackson.core.JsonProcessingException; import nostr.base.PublicKey; import nostr.event.BaseMessage; import nostr.event.BaseTag; @@ -18,7 +19,7 @@ public class DecodeTest { @Test - public void decodeTest() { + public void decodeTest() throws JsonProcessingException { String json = "[" + "\"EVENT\"," diff --git a/nostr-java-test/src/test/java/nostr/test/event/NIP52ImplTest.java b/nostr-java-test/src/test/java/nostr/test/event/NIP52ImplTest.java index 3e08ad91d..e4400f76f 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/NIP52ImplTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/NIP52ImplTest.java @@ -55,12 +55,12 @@ static void setup() { .build(); timeBasedCalendarContent.setParticipantPubKeys(List.of(P_1_TAG, P_2_TAG)); -// timeBasedCalendarContent.setGeohashTag(G_TAG); -// timeBasedCalendarContent.setHashtagTags(List.of(T_TAG)); -// timeBasedCalendarContent.setStartTzid(CALENDAR_TIME_BASED_EVENT_START_TZID); -// timeBasedCalendarContent.setEndTzid(START.toString()); + timeBasedCalendarContent.setGeohashTag(G_TAG); + timeBasedCalendarContent.setHashtagTags(List.of(T_TAG)); + timeBasedCalendarContent.setStartTzid(CALENDAR_TIME_BASED_EVENT_START_TZID); + timeBasedCalendarContent.setEndTzid(START.toString()); Long l = START + 100L; -// timeBasedCalendarContent.setEndTzid(l.toString()); + timeBasedCalendarContent.setEndTzid(l.toString()); timeBasedCalendarContent.setSummary(CALENDAR_TIME_BASED_EVENT_SUMMARY); timeBasedCalendarContent.setLocation(CALENDAR_TIME_BASED_EVENT_LOCATION); timeBasedSender = Identity.generateRandomIdentity(); 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 1b39aa879..4f288f74a 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 @@ -1,5 +1,6 @@ package nostr.test.json; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.java.Log; import nostr.api.NIP01; import nostr.base.Command; @@ -46,7 +47,7 @@ public class JsonParseTest { @Test - public void testBaseMessageDecoder() { + public void testBaseMessageDecoder() throws JsonProcessingException { log.info("testBaseMessageDecoder"); final String parseTarget = @@ -101,7 +102,7 @@ public void testBaseReqMessageDecoder() { } @Test - public void testBaseEventMessageDecoder() { + public void testBaseEventMessageDecoder() throws JsonProcessingException { log.info("testBaseEventMessageDecoder"); final String parseTarget @@ -129,7 +130,7 @@ public void testBaseEventMessageDecoder() { } @Test - public void testBaseEventMessageMarkerDecoder() { + public void testBaseEventMessageMarkerDecoder() throws JsonProcessingException { log.info("testBaseEventMessageMarkerDecoder"); final String json = "[" @@ -342,7 +343,7 @@ public void testReqMessageFiltersListDecoder() { } @Test - public void testReqMessageDeserializer() { + public void testReqMessageDeserializer() throws JsonProcessingException { log.info("testReqMessageDeserializer"); String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; @@ -361,7 +362,7 @@ public void testReqMessageDeserializer() { } @Test - public void testReqMessageFilterListDecoder() { + public void testReqMessageFilterListDecoder() throws JsonProcessingException { log.info("testReqMessageFilterListDecoder"); String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; @@ -381,7 +382,7 @@ public void testReqMessageFilterListDecoder() { } @Test - public void testReqMessagePopulatedFilterDecoder() { + public void testReqMessagePopulatedFilterDecoder() throws JsonProcessingException { log.info("testReqMessagePopulatedFilterDecoder"); String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; @@ -413,7 +414,7 @@ public void testReqMessagePopulatedFilterDecoder() { } @Test - public void testReqMessagePopulatedListOfFiltersListDecoder() { + public void testReqMessagePopulatedListOfFiltersListDecoder() throws JsonProcessingException { log.info("testReqMessagePopulatedListOfFiltersListDecoder"); String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; From e54a60f904c6a441cd0d5ec9fd2e3c88bc0b45b4 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sun, 12 Jan 2025 21:38:59 -0500 Subject: [PATCH 03/45] sneaky throws --- .../src/main/java/nostr/api/EventNostr.java | 10 ++-------- .../event/json/codec/BaseMessageDecoder.java | 4 ++-- .../nostr/test/event/ApiNIP52RequestTest.java | 10 +--------- .../nostr/test/event/ApiNIP99RequestTest.java | 10 +--------- .../test/java/nostr/test/event/DecodeTest.java | 3 +-- .../test/java/nostr/test/json/JsonParseTest.java | 15 +++++++-------- 6 files changed, 14 insertions(+), 38 deletions(-) diff --git a/nostr-java-api/src/main/java/nostr/api/EventNostr.java b/nostr-java-api/src/main/java/nostr/api/EventNostr.java index 11a58dbf5..53d91db10 100644 --- a/nostr-java-api/src/main/java/nostr/api/EventNostr.java +++ b/nostr-java-api/src/main/java/nostr/api/EventNostr.java @@ -4,7 +4,6 @@ */ package nostr.api; -import com.fasterxml.jackson.core.JsonProcessingException; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.NonNull; @@ -47,18 +46,13 @@ public U send() { return this.send(getRelays()); } + @SuppressWarnings("unchecked") public U send(Map relays) { List messages = super.send(this.event, relays); BaseMessageDecoder decoder = new BaseMessageDecoder(); return messages.stream() - .map(msg -> { - try { - return (U) decoder.decode(msg); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - }) + .map(msg -> (U) decoder.decode(msg)) .filter(msg -> msg != null) .findFirst() .orElseThrow(() -> new RuntimeException("No message received")); diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java index 4a2bd3bc2..a61386ff1 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java @@ -1,7 +1,6 @@ package nostr.event.json.codec; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.NonNull; import lombok.SneakyThrows; @@ -30,8 +29,9 @@ public BaseMessageDecoder() { this.mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); } + @SneakyThrows @Override - public T decode(@NonNull String jsonString) throws JsonProcessingException { + public T decode(@NonNull String jsonString) { Object[] msgArr = mapper.readValue(jsonString, Object[].class); final String strCmd = msgArr[0].toString(); final Object arg = msgArr[1]; diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java index 7b31376de..8e78c7e19 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java @@ -7,8 +7,6 @@ import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -157,13 +155,7 @@ void testNIP99CalendarContentPreRequest() throws IOException { private T mapJsonToEvent(List reqResponse, Class clazz) { Optional first = reqResponse .stream() - .map(baseMessage -> { - try { - return new BaseMessageDecoder().decode(baseMessage); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - }) + .map(baseMessage -> new BaseMessageDecoder().decode(baseMessage)) .map(eventMessage -> ((GenericEvent) eventMessage.getEvent())) .map(event -> new BaseEventEncoder<>(event).encode()) .map(encode -> new GenericEventDecoder<>(clazz).decode(encode)) diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java index dd9f9685c..26e8881c9 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java @@ -7,8 +7,6 @@ import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -153,13 +151,7 @@ void testNIP99ClassifiedListingPreRequest() throws IOException { private T mapJsonToEvent(List reqResponse, Class clazz) { Optional first = reqResponse .stream() - .map(baseMessage -> { - try { - return new BaseMessageDecoder().decode(baseMessage); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - }) + .map(baseMessage -> new BaseMessageDecoder().decode(baseMessage)) .map(eventMessage -> ((GenericEvent) eventMessage.getEvent())) .map(event -> new BaseEventEncoder<>(event).encode()) .map(encode -> new GenericEventDecoder<>(clazz).decode(encode)) diff --git a/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java b/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java index 5508422b5..b75ecdd49 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java @@ -1,6 +1,5 @@ package nostr.test.event; -import com.fasterxml.jackson.core.JsonProcessingException; import nostr.base.PublicKey; import nostr.event.BaseMessage; import nostr.event.BaseTag; @@ -19,7 +18,7 @@ public class DecodeTest { @Test - public void decodeTest() throws JsonProcessingException { + public void decodeTest() { String json = "[" + "\"EVENT\"," 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 4f288f74a..1b39aa879 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 @@ -1,6 +1,5 @@ package nostr.test.json; -import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.java.Log; import nostr.api.NIP01; import nostr.base.Command; @@ -47,7 +46,7 @@ public class JsonParseTest { @Test - public void testBaseMessageDecoder() throws JsonProcessingException { + public void testBaseMessageDecoder() { log.info("testBaseMessageDecoder"); final String parseTarget = @@ -102,7 +101,7 @@ public void testBaseReqMessageDecoder() { } @Test - public void testBaseEventMessageDecoder() throws JsonProcessingException { + public void testBaseEventMessageDecoder() { log.info("testBaseEventMessageDecoder"); final String parseTarget @@ -130,7 +129,7 @@ public void testBaseEventMessageDecoder() throws JsonProcessingException { } @Test - public void testBaseEventMessageMarkerDecoder() throws JsonProcessingException { + public void testBaseEventMessageMarkerDecoder() { log.info("testBaseEventMessageMarkerDecoder"); final String json = "[" @@ -343,7 +342,7 @@ public void testReqMessageFiltersListDecoder() { } @Test - public void testReqMessageDeserializer() throws JsonProcessingException { + public void testReqMessageDeserializer() { log.info("testReqMessageDeserializer"); String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; @@ -362,7 +361,7 @@ public void testReqMessageDeserializer() throws JsonProcessingException { } @Test - public void testReqMessageFilterListDecoder() throws JsonProcessingException { + public void testReqMessageFilterListDecoder() { log.info("testReqMessageFilterListDecoder"); String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; @@ -382,7 +381,7 @@ public void testReqMessageFilterListDecoder() throws JsonProcessingException { } @Test - public void testReqMessagePopulatedFilterDecoder() throws JsonProcessingException { + public void testReqMessagePopulatedFilterDecoder() { log.info("testReqMessagePopulatedFilterDecoder"); String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; @@ -414,7 +413,7 @@ public void testReqMessagePopulatedFilterDecoder() throws JsonProcessingExceptio } @Test - public void testReqMessagePopulatedListOfFiltersListDecoder() throws JsonProcessingException { + public void testReqMessagePopulatedListOfFiltersListDecoder() { log.info("testReqMessagePopulatedListOfFiltersListDecoder"); String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; From 3c2dd32d994698cdee7251396e5e1298539d0645 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sun, 19 Jan 2025 17:11:45 -0500 Subject: [PATCH 04/45] replace jetbrain @NotNull with lombok @NonNull, add unit tests --- .../src/main/java/nostr/api/NIP52.java | 7 ++ .../src/main/java/nostr/api/Nostr.java | 4 +- .../nostr/api/factory/impl/NIP52Impl.java | 18 +++ .../src/main/java/nostr/base/IDecoder.java | 4 +- .../StandardWebSocketClient.java | 6 +- .../src/main/java/nostr/event/Kind.java | 1 + .../nostr/event/impl/CalendarRsvpContent.java | 42 +++++++ .../nostr/event/impl/CalendarRsvpEvent.java | 118 ++++++++++++++++++ .../event/json/codec/GenericEventDecoder.java | 12 +- .../test/event/CalendarContentDecodeTest.java | 18 +-- .../nostr/test/event/KindMappingTest.java | 6 + .../java/nostr/test/json/JsonParseTest.java | 3 +- 12 files changed, 212 insertions(+), 27 deletions(-) create mode 100644 nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpContent.java create mode 100644 nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpEvent.java diff --git a/nostr-java-api/src/main/java/nostr/api/NIP52.java b/nostr-java-api/src/main/java/nostr/api/NIP52.java index 8189316a4..c3e6c7528 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP52.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP52.java @@ -1,10 +1,12 @@ package nostr.api; import lombok.NonNull; +import nostr.api.factory.impl.NIP52Impl.CalendarRsvpEventFactory; import nostr.api.factory.impl.NIP52Impl.CalendarTimeBasedEventFactory; import nostr.event.BaseTag; import nostr.event.NIP52Event; import nostr.event.impl.CalendarContent; +import nostr.event.impl.CalendarRsvpContent; import nostr.id.Identity; import java.util.List; @@ -18,4 +20,9 @@ public NIP52 createCalendarTimeBasedEvent(@NonNull List baseTags, @N setEvent((T) new CalendarTimeBasedEventFactory(getSender(), baseTags, content, calendarContent).create()); return this; } + + public NIP52 createCalendarRsvpEvent(@NonNull List baseTags, @NonNull String content, @NonNull CalendarRsvpContent calendarRsvpContent) { + setEvent((T) new CalendarRsvpEventFactory(getSender(), baseTags, content, calendarRsvpContent).create()); + return this; + } } diff --git a/nostr-java-api/src/main/java/nostr/api/Nostr.java b/nostr-java-api/src/main/java/nostr/api/Nostr.java index aaf4b488d..554cc22cd 100644 --- a/nostr-java-api/src/main/java/nostr/api/Nostr.java +++ b/nostr-java-api/src/main/java/nostr/api/Nostr.java @@ -206,7 +206,7 @@ public static String encode(@NonNull BaseEvent event, Relay relay) { /** * @param json */ - public static GenericEvent decodeEvent(@NonNull String json) { + public static GenericEvent decodeEvent(@NonNull String json) throws JsonProcessingException { return new GenericEventDecoder<>().decode(json); } @@ -306,7 +306,7 @@ public static String encode(@NonNull GenericTagQuery gtq) { * @param json * @param clazz */ - public static IElement decode(@NonNull String json, @NonNull Class clazz) { + public static IElement decode(@NonNull String json, @NonNull Class clazz) throws JsonProcessingException { switch (clazz.getName()) { case "nostr.event.BaseEvent.class" -> { return decodeEvent(json); diff --git a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP52Impl.java b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP52Impl.java index b4d6ab07b..9e3a52aad 100644 --- a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP52Impl.java +++ b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP52Impl.java @@ -6,6 +6,8 @@ import nostr.api.factory.EventFactory; import nostr.event.BaseTag; import nostr.event.impl.CalendarContent; +import nostr.event.impl.CalendarRsvpContent; +import nostr.event.impl.CalendarRsvpEvent; import nostr.event.impl.CalendarTimeBasedEvent; import nostr.id.Identity; @@ -27,4 +29,20 @@ public CalendarTimeBasedEvent create() { return new CalendarTimeBasedEvent(getSender(), getTags(), getContent(), calendarContent); } } + + @Data + @EqualsAndHashCode(callSuper = false) + public static class CalendarRsvpEventFactory extends EventFactory { + private final CalendarRsvpContent calendarRsvpContent; + + public CalendarRsvpEventFactory(@NonNull Identity sender, @NonNull List baseTags, @NonNull String content, @NonNull CalendarRsvpContent calendarRsvpContent) { + super(sender, baseTags, content); + this.calendarRsvpContent = calendarRsvpContent; + } + + @Override + public CalendarRsvpEvent create() { + return new CalendarRsvpEvent(getSender(), getTags(), getContent(), calendarRsvpContent); + } + } } diff --git a/nostr-java-base/src/main/java/nostr/base/IDecoder.java b/nostr-java-base/src/main/java/nostr/base/IDecoder.java index c1dd430be..f94d95120 100644 --- a/nostr-java-base/src/main/java/nostr/base/IDecoder.java +++ b/nostr-java-base/src/main/java/nostr/base/IDecoder.java @@ -1,5 +1,7 @@ package nostr.base; +import com.fasterxml.jackson.core.JsonProcessingException; + /** * * @author eric @@ -7,6 +9,6 @@ */ public interface IDecoder { - T decode(String str); + T decode(String str) throws JsonProcessingException; } diff --git a/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java b/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java index 23328d426..e36f2ed45 100644 --- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java +++ b/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java @@ -1,9 +1,9 @@ package nostr.client.springwebsocket; import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.NonNull; import lombok.SneakyThrows; import nostr.event.BaseMessage; -import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -33,7 +33,7 @@ public StandardWebSocketClient(@Value("${nostr.relay.uri}") String relayUri) { } @Override - protected void handleTextMessage(@NotNull WebSocketSession session, TextMessage message) { + protected void handleTextMessage(@NonNull WebSocketSession session, TextMessage message) { events.add(message.getPayload()); completed = true; } @@ -57,4 +57,4 @@ public List send(String json) throws IOException { public void closeSocket() throws IOException { clientSession.close(); } -} \ No newline at end of file +} diff --git a/nostr-java-event/src/main/java/nostr/event/Kind.java b/nostr-java-event/src/main/java/nostr/event/Kind.java index 4b88c6b36..58a3b7bc7 100644 --- a/nostr-java-event/src/main/java/nostr/event/Kind.java +++ b/nostr-java-event/src/main/java/nostr/event/Kind.java @@ -40,6 +40,7 @@ public enum Kind { CLASSIFIED_LISTING_DRAFT(30_403, "classified_listing_draft"), CALENDAR_DATE_BASED_EVENT(31_922, "calendar_date_based_event"), CALENDAR_TIME_BASED_EVENT(31_923, "calendar_time_based_event"), + CALENDAR_RSVP_EVENT(31_925, "calendar_rsvp_event"), UNDEFINED(-1, "undefined"); @JsonValue diff --git a/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpContent.java b/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpContent.java new file mode 100644 index 000000000..0c0145a3b --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpContent.java @@ -0,0 +1,42 @@ +package nostr.event.impl; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import nostr.event.AbstractEventContent; +import nostr.event.impl.CalendarContent.CalendarContentBuilder; +import nostr.event.impl.CalendarRsvpContent.CalendarRsvpContentBuilder; +import nostr.event.tag.AddressTag; +import nostr.event.tag.GeohashTag; +import nostr.event.tag.HashtagTag; +import nostr.event.tag.IdentifierTag; +import nostr.event.tag.PubKeyTag; +import nostr.event.tag.ReferenceTag; + +import java.util.List; + +@Data +@Builder +@JsonDeserialize(builder = CalendarRsvpContentBuilder.class) +@EqualsAndHashCode(callSuper = false) +public class CalendarRsvpContent extends AbstractEventContent { + //@JsonProperty + private final String id; + + // below fields mandatory + private final IdentifierTag identifierTag; + private final AddressTag addressTag; + private final String status; + + // below fields optional + private List participantPubKeys; + + public static CalendarRsvpContentBuilder builder(@NonNull IdentifierTag identifierTag, @NonNull AddressTag addressTag, @NonNull String status) { + return new CalendarRsvpContentBuilder() + .identifierTag(identifierTag) + .addressTag(addressTag) + .status(status); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpEvent.java new file mode 100644 index 000000000..6243aa523 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpEvent.java @@ -0,0 +1,118 @@ +package nostr.event.impl; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import nostr.base.PublicKey; +import nostr.base.Signature; +import nostr.base.annotation.Event; +import nostr.event.BaseTag; +import nostr.event.Kind; +import nostr.event.NIP52Event; +import nostr.event.impl.CalendarRsvpEvent.CalendarRsvpEventDeserializer; +import nostr.event.impl.CalendarTimeBasedEvent.CalendarTimeBasedEventDeserializer; +import nostr.event.tag.AddressTag; +import nostr.event.tag.IdentifierTag; +import nostr.event.tag.PubKeyTag; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.StreamSupport; + +@EqualsAndHashCode(callSuper = false) +@Event(name = "CalendarRsvpEvent", nip = 52) +@JsonDeserialize(using = CalendarRsvpEventDeserializer.class) +public class CalendarRsvpEvent extends NIP52Event { + @Getter + @JsonIgnore + private CalendarRsvpContent calendarRsvpContent; + + public CalendarRsvpEvent(@NonNull PublicKey sender, @NonNull List baseTags, @NonNull String content, @NonNull CalendarRsvpContent calendarRsvpContent) { + super(sender, Kind.CALENDAR_RSVP_EVENT, baseTags, content); + this.calendarRsvpContent = calendarRsvpContent; + mapCustomTags(); + } + + private void mapCustomTags() { + addStandardTag(calendarRsvpContent.getIdentifierTag()); + addStandardTag(calendarRsvpContent.getAddressTag()); + addGenericTag("status", getNip(), calendarRsvpContent.getStatus()); + addStandardTag(calendarRsvpContent.getParticipantPubKeys()); + } + + public static class CalendarRsvpEventDeserializer extends StdDeserializer { + public CalendarRsvpEventDeserializer() { + super(CalendarRsvpEvent.class); + } + +// TODO: below methods needs comprehensive tags assignment completion + @Override + public CalendarRsvpEvent deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { + JsonNode calendarTimeBasedEventNode = jsonParser.getCodec().readTree(jsonParser); + ArrayNode tags = (ArrayNode) calendarTimeBasedEventNode.get("tags"); + + List baseTags = StreamSupport.stream( + tags.spliterator(), false).toList().stream() + .map( + JsonNode::elements) + .map(element -> + new ObjectMapper().convertValue(element, BaseTag.class)).toList(); + + List genericTags = baseTags.stream() + .filter(GenericTag.class::isInstance) + .map(GenericTag.class::cast) + .toList(); + + IdentifierTag identifierTag = getBaseTagCastFromString(baseTags, IdentifierTag.class).getFirst(); + + AddressTag addressTag = getBaseTagCastFromString(baseTags, AddressTag.class).getFirst(); + + CalendarRsvpContent calendarRsvpContent = CalendarRsvpContent.builder( + identifierTag, + addressTag, + getTagValueFromString(genericTags, "status")) + .build(); + + calendarRsvpContent.setParticipantPubKeys(getBaseTagCastFromString(baseTags, PubKeyTag.class)); + + Map generalMap = new HashMap<>(); + calendarTimeBasedEventNode.fields().forEachRemaining(generalTag -> + generalMap.put( + generalTag.getKey(), + generalTag.getValue().asText())); + + + CalendarRsvpEvent calendarTimeBasedEvent = new CalendarRsvpEvent( + new PublicKey(generalMap.get("pubkey")), + baseTags, + generalMap.get("content"), + calendarRsvpContent + ); + calendarTimeBasedEvent.setId(generalMap.get("id")); + calendarTimeBasedEvent.setCreatedAt(Long.valueOf(generalMap.get("created_at"))); + calendarTimeBasedEvent.setSignature(Signature.fromString(generalMap.get("sig"))); + + return calendarTimeBasedEvent; + } + + private String getTagValueFromString(List genericTags, String code) { + return genericTags.stream() + .filter(tag -> tag.getCode().equalsIgnoreCase(code)) + .findFirst().get().getAttributes().get(0).getValue().toString(); + } + + private List getBaseTagCastFromString(List baseTags, Class type) { + return baseTags.stream().filter(type::isInstance).map(type::cast).toList(); + } + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericEventDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericEventDecoder.java index b97ffeca4..533d61e19 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericEventDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericEventDecoder.java @@ -25,13 +25,9 @@ public GenericEventDecoder(Class clazz) { } @Override - public T decode(String jsonEvent) { - try { - var mapper = new ObjectMapper(); - mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); - return mapper.readValue(jsonEvent, clazz); - } catch (JsonProcessingException ex) { - throw new RuntimeException(ex); - } + public T decode(String jsonEvent) throws JsonProcessingException { + var mapper = new ObjectMapper(); + mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + return mapper.readValue(jsonEvent, clazz); } } diff --git a/nostr-java-test/src/test/java/nostr/test/event/CalendarContentDecodeTest.java b/nostr-java-test/src/test/java/nostr/test/event/CalendarContentDecodeTest.java index b375a85cd..7e2dae22b 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/CalendarContentDecodeTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/CalendarContentDecodeTest.java @@ -82,25 +82,19 @@ public class CalendarContentDecodeTest { @Test void testCalendarContentMinimalJsonDecoding() { - assertDoesNotThrow(() -> { - CalendarTimeBasedEvent decode = new GenericEventDecoder<>(CalendarTimeBasedEvent.class).decode(eventMinimalJson); - return decode; - }); + assertDoesNotThrow(() -> + new GenericEventDecoder<>(CalendarTimeBasedEvent.class).decode(eventMinimalJson)); } @Test void testCalendarContentFullJsonDecoding() { - assertDoesNotThrow(() -> { - CalendarTimeBasedEvent decode = new GenericEventDecoder<>(CalendarTimeBasedEvent.class).decode(eventFullJson); - return decode; - }); + assertDoesNotThrow(() -> + new GenericEventDecoder<>(CalendarTimeBasedEvent.class).decode(eventFullJson)); } @Test void testCalendarContentProblemBarchettaJsonDecoding() { - assertDoesNotThrow(() -> { - CalendarTimeBasedEvent decoded = new GenericEventDecoder<>(CalendarTimeBasedEvent.class).decode(eventFullJson); - return decoded; - }); + assertDoesNotThrow(() -> + new GenericEventDecoder<>(CalendarTimeBasedEvent.class).decode(eventFullJson)); } } diff --git a/nostr-java-test/src/test/java/nostr/test/event/KindMappingTest.java b/nostr-java-test/src/test/java/nostr/test/event/KindMappingTest.java index 48b1f5283..cbb4b44b4 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/KindMappingTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/KindMappingTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; public class KindMappingTest { @Test @@ -15,4 +16,9 @@ void testKindValueOf() { void testKindName() { assertEquals("text_note", Kind.valueOf(1).getName()); } + + @Test + void testKindUndefinedName() { + assertThrows(IllegalArgumentException.class, () -> Kind.valueOf(9999999)); + } } 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 c17855037..77e012fd4 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 @@ -1,5 +1,6 @@ package nostr.test.json; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import lombok.extern.java.Log; import nostr.api.NIP01; @@ -176,7 +177,7 @@ public void testGenericTagDecoder() { } @Test - public void testClassifiedListingTagSerializer() { + public void testClassifiedListingTagSerializer() throws JsonProcessingException { log.info("testClassifiedListingSerializer"); final String classifiedListingEventJson = "{" + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\"," From 89c1bd2b3b92a65cf1a23856c0d97dcff65832b6 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sun, 19 Jan 2025 18:44:24 -0500 Subject: [PATCH 05/45] updated tests --- .../nostr/test/event/ApiNIP52RequestTest.java | 42 ++++++------------- .../nostr/test/event/ApiNIP99RequestTest.java | 41 ++++++------------ 2 files changed, 24 insertions(+), 59 deletions(-) diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java index 8e78c7e19..6baba40fa 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java @@ -1,17 +1,6 @@ package nostr.test.event; -import java.io.IOException; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; - import com.fasterxml.jackson.databind.ObjectMapper; - import nostr.api.NIP52; import nostr.base.PublicKey; import nostr.client.springwebsocket.SpringWebSocketClient; @@ -19,9 +8,6 @@ import nostr.event.impl.CalendarContent; import nostr.event.impl.GenericEvent; import nostr.event.impl.GenericTag; -import nostr.event.json.codec.BaseEventEncoder; -import nostr.event.json.codec.BaseMessageDecoder; -import nostr.event.json.codec.GenericEventDecoder; import nostr.event.message.EventMessage; import nostr.event.tag.EventTag; import nostr.event.tag.GeohashTag; @@ -30,7 +16,15 @@ import nostr.event.tag.PubKeyTag; import nostr.event.tag.ReferenceTag; import nostr.id.Identity; -import nostr.test.util.JsonComparator; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertTrue; class ApiNIP52RequestTest { private static final String PRV_KEY_VALUE = "23c011c4c02de9aa98d48c3646c70bb0e7ae30bdae1dfed4d251cbceadaeeb7b"; @@ -103,9 +97,9 @@ void testNIP99CalendarContentPreRequest() throws IOException { tags.add(R_TAG); CalendarContent calendarContent = CalendarContent.builder( - new IdentifierTag(UUID_CALENDAR_TIME_BASED_EVENT_TEST), - TITLE, - Long.valueOf(START)) + new IdentifierTag(UUID_CALENDAR_TIME_BASED_EVENT_TEST), + TITLE, + Long.valueOf(START)) .build(); var nip52 = new NIP52<>(Identity.create(PRV_KEY_VALUE)); @@ -152,18 +146,6 @@ void testNIP99CalendarContentPreRequest() throws IOException { */ } - private T mapJsonToEvent(List reqResponse, Class clazz) { - Optional first = reqResponse - .stream() - .map(baseMessage -> new BaseMessageDecoder().decode(baseMessage)) - .map(eventMessage -> ((GenericEvent) eventMessage.getEvent())) - .map(event -> new BaseEventEncoder<>(event).encode()) - .map(encode -> new GenericEventDecoder<>(clazz).decode(encode)) - .findFirst(); - T t = first.get(); - return t; - } - private String expectedEventResponseJson(String subscriptionId) { return "[\"OK\",\"" + subscriptionId + "\",true,\"success: request processed\"]"; } diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java index 26e8881c9..10b9017b0 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java @@ -1,18 +1,7 @@ package nostr.test.event; -import java.io.IOException; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; - import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; - import nostr.api.NIP99; import nostr.base.PublicKey; import nostr.client.springwebsocket.SpringWebSocketClient; @@ -20,9 +9,6 @@ import nostr.event.impl.ClassifiedListing; import nostr.event.impl.GenericEvent; import nostr.event.impl.GenericTag; -import nostr.event.json.codec.BaseEventEncoder; -import nostr.event.json.codec.BaseMessageDecoder; -import nostr.event.json.codec.GenericEventDecoder; import nostr.event.message.EventMessage; import nostr.event.tag.EventTag; import nostr.event.tag.GeohashTag; @@ -31,8 +17,17 @@ import nostr.event.tag.PubKeyTag; import nostr.event.tag.SubjectTag; import nostr.id.Identity; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + import static nostr.test.event.ClassifiedListingEventTest.LOCATION_CODE; import static nostr.test.event.ClassifiedListingEventTest.PUBLISHED_AT_CODE; +import static org.junit.jupiter.api.Assertions.assertTrue; class ApiNIP99RequestTest { private static final String PRV_KEY_VALUE = "23c011c4c02de9aa98d48c3646c70bb0e7ae30bdae1dfed4d251cbceadaeeb7b"; @@ -85,9 +80,9 @@ void testNIP99ClassifiedListingPreRequest() throws IOException { PriceTag priceTag = new PriceTag(NUMBER, CURRENCY, FREQUENCY); ClassifiedListing classifiedListing = ClassifiedListing.builder( - TITLE, - SUMMARY, - priceTag) + TITLE, + SUMMARY, + priceTag) .build(); var nip99 = new NIP99<>(Identity.create(PRV_KEY_VALUE)); @@ -148,18 +143,6 @@ void testNIP99ClassifiedListingPreRequest() throws IOException { */ } - private T mapJsonToEvent(List reqResponse, Class clazz) { - Optional first = reqResponse - .stream() - .map(baseMessage -> new BaseMessageDecoder().decode(baseMessage)) - .map(eventMessage -> ((GenericEvent) eventMessage.getEvent())) - .map(event -> new BaseEventEncoder<>(event).encode()) - .map(encode -> new GenericEventDecoder<>(clazz).decode(encode)) - .findFirst(); - T t = first.get(); - return t; - } - private String expectedEventResponseJson(String subscriptionId) { return "[\"OK\",\"" + subscriptionId + "\",true,\"success: request processed\"]"; } From 97b215550b2540636451a4a4cbfd391bf46ae7d0 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sat, 25 Jan 2025 13:08:49 -0500 Subject: [PATCH 06/45] replace boolean with AtomicBoolean for message handling synchronization --- .../StandardWebSocketClient.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java b/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java index 23328d426..40bf54786 100644 --- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java +++ b/nostr-java-client/src/main/java/nostr/client/springwebsocket/StandardWebSocketClient.java @@ -1,9 +1,8 @@ package nostr.client.springwebsocket; -import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.NonNull; import lombok.SneakyThrows; import nostr.event.BaseMessage; -import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -17,6 +16,7 @@ import java.net.URI; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import static org.awaitility.Awaitility.await; @@ -25,7 +25,7 @@ public class StandardWebSocketClient extends TextWebSocketHandler implements WebSocketClient { private final WebSocketSession clientSession; private List events = new ArrayList<>(); - private boolean completed = false; + private final AtomicBoolean completed = new AtomicBoolean(false); @SneakyThrows public StandardWebSocketClient(@Value("${nostr.relay.uri}") String relayUri) { @@ -33,23 +33,25 @@ public StandardWebSocketClient(@Value("${nostr.relay.uri}") String relayUri) { } @Override - protected void handleTextMessage(@NotNull WebSocketSession session, TextMessage message) { + protected void handleTextMessage(@NonNull WebSocketSession session, TextMessage message) { events.add(message.getPayload()); - completed = true; + completed.setRelease(true); } @Override - public List send(T eventMessage) throws JsonProcessingException, IOException { + public List send(T eventMessage) throws IOException { return send(eventMessage.encode()); } @Override public List send(String json) throws IOException { clientSession.sendMessage(new TextMessage(json)); - await().until(() -> completed); + await() +// .timeout(66, TimeUnit.MINUTES) + .untilTrue(completed); List eventList = List.copyOf(events); events = new ArrayList<>(); - completed = false; + completed.setRelease(false); return eventList; } @@ -57,4 +59,4 @@ public List send(String json) throws IOException { public void closeSocket() throws IOException { clientSession.close(); } -} \ No newline at end of file +} From ed95b9974a0d87885a5a4dbadae66e5469144d01 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sat, 25 Jan 2025 20:35:10 -0500 Subject: [PATCH 07/45] websocket client update --- .../nostr/api/NostrSpringWebSocketClient.java | 108 +++++++++--------- .../nostr/api/WebSocketClientHandler.java | 48 ++++++++ .../java/nostr/examples/NostrApiExamples.java | 2 +- .../nostr/test/event/APINIP09EventTest.java | 6 +- 4 files changed, 103 insertions(+), 61 deletions(-) create mode 100644 nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java diff --git a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java index 5389e644d..7bd08234b 100644 --- a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java +++ b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java @@ -1,45 +1,35 @@ package nostr.api; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import lombok.Getter; import lombok.NoArgsConstructor; import lombok.NonNull; -import lombok.SneakyThrows; import nostr.base.IEvent; import nostr.base.ISignable; -import nostr.client.springwebsocket.SpringWebSocketClient; import nostr.context.RequestContext; import nostr.crypto.schnorr.Schnorr; import nostr.event.BaseMessage; import nostr.event.impl.Filters; import nostr.event.impl.GenericEvent; -import nostr.event.message.EventMessage; -import nostr.event.message.ReqMessage; import nostr.id.Identity; import nostr.util.NostrUtil; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + @NoArgsConstructor public class NostrSpringWebSocketClient implements NostrIF { - - private static NostrSpringWebSocketClient INSTANCE; - - private List clients = new ArrayList<>(); - + private final Map clientMap = new ConcurrentHashMap<>(); @Getter private Identity sender; - private NostrSpringWebSocketClient(@NonNull Map relays) { - relays.values().forEach(this::createClientForRelay); - } + private static NostrSpringWebSocketClient INSTANCE; - public NostrSpringWebSocketClient(@NonNull Identity sender) { - this.sender = sender; + public NostrSpringWebSocketClient(String relayName, String relayUri) { + setRelays(Map.of(relayName, relayUri)); } public static NostrIF getInstance() { @@ -50,47 +40,39 @@ public static NostrIF getInstance(@NonNull Identity sender) { return (INSTANCE == null) ? new NostrSpringWebSocketClient(sender) : INSTANCE; } - private void createClientForRelay(String relay) { - clients.add(new SpringWebSocketClient(relay)); + public NostrSpringWebSocketClient(@NonNull Identity sender) { + this.sender = sender; } - @Override public NostrIF setSender(@NonNull Identity sender) { this.sender = sender; return this; } - @Override - public NostrIF setRelays(@NonNull Map relays) { - relays.values().stream() - .filter(relay -> - clients.stream() - .map(SpringWebSocketClient::getRelayUrl) - .noneMatch( - relay::equals)) - .forEach(this::addClient); - return this; - } - - @SneakyThrows - private void addClient(String relay) { - clients.add(new SpringWebSocketClient(relay)); + public List send(T event, Map relays) { + return relays.entrySet().stream().map(relayEntry -> + clientMap.get(relayEntry.getKey()) + .sendEvent(event)) + .flatMap(List::stream) + .distinct().toList(); } @Override - public void close() throws IOException { - for (SpringWebSocketClient client : clients) { - client.closeSocket(); - } + public NostrIF setRelays(@NonNull Map relays) { + relays.entrySet().stream().forEach(relayEntry -> + clientMap.putIfAbsent(relayEntry.getKey(), + new WebSocketClientHandler( + relayEntry.getKey(), + relayEntry.getValue()))); + return this; } @Override public List send(@NonNull IEvent event) { - EventMessage message = new EventMessage(event); - return clients.stream().flatMap(client -> send(client, message).stream()).toList(); + return clientMap.values().stream().map(client -> + client.sendEvent(event)).flatMap(List::stream).distinct().toList(); } - @Override public List send(@NonNull IEvent event, Map relays) { setRelays(relays); @@ -99,8 +81,7 @@ public List send(@NonNull IEvent event, Map relays) { @Override public List send(@NonNull Filters filters, @NonNull String subscriptionId) { - ReqMessage reqMessage = new ReqMessage(subscriptionId, filters); - return clients.stream().flatMap(client -> send(client, reqMessage).stream()).toList(); + return clientMap.get(subscriptionId).sendRequest(filters, subscriptionId); } @Override @@ -111,17 +92,18 @@ public List send(@NonNull Filters filters, @NonNull String subscriptionI @Override public List send(@NonNull List filtersList, @NonNull String subscriptionId) { - return filtersList.stream().flatMap(filters -> send(filters, subscriptionId).stream()).toList(); + return filtersList.stream().map(filters -> send( + filters, + subscriptionId + )) + .flatMap(List::stream) + .distinct().toList(); } @Override public List send(@NonNull List filtersList, @NonNull String subscriptionId, Map relays) { - return filtersList.stream().flatMap(filters -> send(filters, subscriptionId, relays).stream()).toList(); - } - - @SneakyThrows - private List send(SpringWebSocketClient client, BaseMessage message) { - return client.send(message); + setRelays(relays); + return send(filtersList, subscriptionId); } @Override @@ -151,9 +133,21 @@ public boolean verify(@NonNull GenericEvent event) { } } + @Override + public Identity getSender() { + return sender; + } + @Override public Map getRelays() { - return clients.stream() - .collect(Collectors.toMap(SpringWebSocketClient::getRelayUrl, SpringWebSocketClient::getRelayUrl, (prev, next) -> next, HashMap::new)); + return clientMap.values().stream() + .collect(Collectors.toMap(WebSocketClientHandler::getRelayName, WebSocketClientHandler::getRelayUri, + (prev, next) -> next, HashMap::new)); + } + + public void close() throws IOException { + for (WebSocketClientHandler client : clientMap.values()) { + client.close(); + } } } diff --git a/nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java b/nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java new file mode 100644 index 000000000..4f4cfa6df --- /dev/null +++ b/nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java @@ -0,0 +1,48 @@ +package nostr.api; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import nostr.base.IEvent; +import nostr.client.springwebsocket.SpringWebSocketClient; +import nostr.event.impl.Filters; +import nostr.event.message.EventMessage; +import nostr.event.message.ReqMessage; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@NoArgsConstructor +public class WebSocketClientHandler { + + private SpringWebSocketClient eventClient; + private Map requestClientMap = new ConcurrentHashMap<>(); + + @Getter + private String relayName; + @Getter + private String relayUri; + + protected WebSocketClientHandler(@NonNull String relayName, @NonNull String relayUri) { + this.relayName = relayName; + this.relayUri = relayUri; + this.eventClient = new SpringWebSocketClient(relayUri); + } + + protected List sendEvent(@NonNull IEvent event) { + return eventClient.send(new EventMessage(event)).stream().toList(); + } + + protected List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId) { + return requestClientMap.get(subscriptionId).send(new ReqMessage(subscriptionId, filters)); + } + + public void close() throws IOException { + eventClient.closeSocket(); + for (SpringWebSocketClient client : requestClientMap.values()) { + client.closeSocket(); + } + } +} diff --git a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java index 0a3117e6a..ad16dfddf 100644 --- a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java +++ b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java @@ -260,7 +260,7 @@ private static void reactionEvent() { var reactionEvent = nip25.createReactionEvent(event.getEvent(), Reaction.LIKE); reactionEvent.signAndSend(RELAYS); nip25.createReactionEvent(event.getEvent(), "💩").signAndSend(); -// Using Custom Emoji as reaction +// Using Custom Emoji as reaction nip25.createReactionEvent( event.getEvent(), NIP30.createCustomEmojiTag( diff --git a/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java b/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java index 0c11089a6..95bd5e462 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java @@ -2,8 +2,8 @@ import nostr.api.NIP01; import nostr.api.NIP09; +import nostr.api.NostrSpringWebSocketClient; import nostr.base.Relay; -import nostr.client.springwebsocket.SpringWebSocketClient; import nostr.event.BaseMessage; import nostr.event.BaseTag; import nostr.event.Kind; @@ -32,10 +32,10 @@ public class APINIP09EventTest { private static final String RELAY_URI = "ws://localhost:5555"; - private final SpringWebSocketClient springWebSocketClient; + private final NostrSpringWebSocketClient nostrSpringWebSocketClient; public APINIP09EventTest() { - springWebSocketClient = new SpringWebSocketClient(RELAY_URI); + nostrSpringWebSocketClient = new NostrSpringWebSocketClient("localhost", RELAY_URI); } @Test From 7ff754580dcf778d80ce5317dcf830a350a8cd4a Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sat, 1 Feb 2025 22:21:29 -0500 Subject: [PATCH 08/45] filters refactor --- .../java/nostr/event/filter/EventFilter.java | 31 +++ .../java/nostr/event/filter/Filterable.java | 12 ++ .../main/java/nostr/event/filter/Filters.java | 178 ++++++++++++++++++ .../java/nostr/event/filter/FiltersCore.java | 23 +++ .../event/filter/GenericTagQueryFilter.java | 32 ++++ .../java/nostr/event/filter/KindFilter.java | 28 +++ .../nostr/event/filter/PublicKeyFilter.java | 31 +++ .../event/filter/ReferencedEventFilter.java | 31 +++ .../filter/ReferencedPublicKeyFilter.java | 31 +++ .../java/nostr/event/filter/SinceFilter.java | 28 +++ .../java/nostr/event/filter/UntilFilter.java | 27 +++ .../main/java/nostr/event/impl/Filters.java | 79 -------- .../java/nostr/test/event/FiltersTest.java | 161 ++++++++++++++++ 13 files changed, 613 insertions(+), 79 deletions(-) create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/Filterable.java create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/Filters.java create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java delete mode 100644 nostr-java-event/src/main/java/nostr/event/impl/Filters.java create mode 100644 nostr-java-test/src/test/java/nostr/test/event/FiltersTest.java diff --git a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java new file mode 100644 index 000000000..3e238447e --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java @@ -0,0 +1,31 @@ +package nostr.event.filter; + +import nostr.event.impl.GenericEvent; + +import java.util.function.BiPredicate; +import java.util.function.Function; + +public class EventFilter implements Filterable { + private final T event; + + public EventFilter(T event) { + this.event = event; + } + @Override + public BiPredicate getBiPredicate() { + return (event, genericEvent) -> event.getId().equals(genericEvent.getId()); + } + @Override + public T getFilterCriterion() { + return event; + } + + @Override + public Function createContainedInstance() { + return eventId -> { + GenericEvent event = new GenericEvent(); + event.setId(eventId); + return (T) event; + }; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java new file mode 100644 index 000000000..dae036c7e --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java @@ -0,0 +1,12 @@ +package nostr.event.filter; + +import nostr.event.impl.GenericEvent; + +import java.util.function.BiPredicate; +import java.util.function.Function; + +public interface Filterable { + BiPredicate getBiPredicate(); + T getFilterCriterion(); + Function createContainedInstance(); +} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java new file mode 100644 index 000000000..81c28c695 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java @@ -0,0 +1,178 @@ +package nostr.event.filter; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; +import lombok.SneakyThrows; +import nostr.base.GenericTagQuery; +import nostr.base.PublicKey; +import nostr.base.annotation.Key; +import nostr.event.Kind; +import nostr.event.impl.GenericEvent; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author squirrel + */ +@Builder +@EqualsAndHashCode(callSuper = false) +@AllArgsConstructor +public class Filters { + @JsonIgnore + private final FiltersCore core; + + public Filters() { + this.core = new FiltersCore(); + } + + @Key + @Getter + @Setter + private Integer limit; + + @JsonProperty("ids") + public List getEvents() { + return getFilterableListByType("ids"); + } + + @JsonProperty("ids") + public void setEvents(@NonNull List events) { + setFilterableListByType("ids", events, EventFilter::new); + } + + @JsonProperty("authors") + public List getAuthors() { +// TODO: may possibly require below variant +// getFilterableByType("authors", PublicKeyFilter.class); + return getFilterableListByType("authors"); + } + + @JsonProperty("authors") + public void setAuthors(@NonNull List authors) { + setFilterableListByType("authors", authors, PublicKeyFilter::new); + } + + @JsonProperty("kinds") + public List getKinds() { + return getFilterableListByType("kinds"); + } + + @JsonProperty("kinds") + public void setKinds(@NonNull List kinds) { + setFilterableListByType("kinds", kinds, KindFilter::new); + } + + @JsonProperty("#e") + public List getReferencedEvents() { + return getFilterableListByType("#e"); + } + + @JsonProperty("#e") + public void setReferencedEvents(@NonNull List events) { + setFilterableListByType("#e", events, EventFilter::new); + } + + @JsonProperty("#p") + public List getReferencePubKeys() { + return getFilterableListByType("#p"); + } + + @JsonProperty("#p") + public void setReferencePubKeys(@NonNull List publicKeys) { + setFilterableListByType("#p", publicKeys, ReferencedPublicKeyFilter::new); + } + + @JsonProperty("since") + public Long getSince() { + return getSinceOrUntil("since"); + } + + @JsonProperty("since") + public void setSince(@NonNull Long since) { + this.core.addFilter("since", new SinceFilter<>(since)); + } + + @JsonProperty("until") + public Long getUntil() { + return getSinceOrUntil("until"); + } + + @JsonProperty("until") + public void setUntil(@NonNull Long until) { + this.core.addFilter("until", new UntilFilter<>(until)); + } + + @Setter(AccessLevel.NONE) + private Map> genericTagQuery; + + public List getReferenceforbelow() { + return getFilterableListByType("ids"); + } + + @JsonAnyGetter + public Map> getGenericTagQuery(@NonNull String key) { + return genericTagQuery; + } + + @JsonAnySetter + public void setGenericTagQuery(@NonNull String key, @NonNull List value) { + this.genericTagQuery = Optional.ofNullable(genericTagQuery).orElse(new HashMap<>()); + this.genericTagQuery.put(key, value); + GenericTagQuery query = new GenericTagQuery(); + query.setTagName(key); + query.setValue(value); + setFilterableListByType(key, List.of(query), GenericTagQueryFilter::new); + } + + public void setReferencedEventsasdf(@NonNull List events) { + setFilterableListByType("#e", events, EventFilter::new); + } + + private List getFilterableListByType(@NonNull String type) { + return Optional + .ofNullable( + this.core.getFilterableByType(type)) + .stream().flatMap(filterables -> + filterables.stream().map(filterable -> + (T) filterable.getFilterCriterion())) + .toList(); + } + + + @SneakyThrows + private void setFilterableListByType( + @NonNull String key, + @NonNull List filterType, + @NonNull Function filterableFunction) { + this.core.addFilterList( + key, + filterType.stream().map(filterableFunction).collect(Collectors.toList())); +// .orElseThrow(() -> +// new IllegalArgumentException( +// String.format("[%s] filter must contain at least one element"))) + } + + private Long getSinceOrUntil(String type) { + return Optional + .ofNullable( + this.core.getFilterableByType(type)) + .stream().flatMap(filterables -> + filterables.stream().map(dateFilter -> + (Long) dateFilter.getFilterCriterion())) + .toList().getFirst(); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java b/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java new file mode 100644 index 000000000..684f0f85b --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java @@ -0,0 +1,23 @@ +package nostr.event.filter; + +import lombok.NonNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class FiltersCore { + Map> filtersMap = new HashMap<>(); + + public void addFilter(@NonNull String key, @NonNull Filterable filterable) { + addFilterList(key, List.of(filterable)); + } + + public void addFilterList(@NonNull String key, @NonNull List filterable) { + filtersMap.put(key, filterable); + } + + public List getFilterableByType(String type) { + return filtersMap.get(type); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java new file mode 100644 index 000000000..3a934b88d --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java @@ -0,0 +1,32 @@ +package nostr.event.filter; + +import lombok.NonNull; +import nostr.base.GenericTagQuery; + +import java.util.function.BiPredicate; +import java.util.function.Function; + +public class GenericTagQueryFilter implements Filterable { + private final T genericTagQuery; + + public GenericTagQueryFilter(@NonNull T genericTagQuery) { + this.genericTagQuery = genericTagQuery; + } + @Override + public BiPredicate getBiPredicate() { + return (genericTagQueryA, genericTagQueryB) -> genericTagQueryA.getTagName().equals(genericTagQueryB.getTagName()); + } + @Override + public T getFilterCriterion() { + return genericTagQuery; + } + + @Override + public Function createContainedInstance() { + return tagName -> { + GenericTagQuery tagQuery = new GenericTagQuery(); + tagQuery.setTagName(tagName); + return (T) tagQuery; + }; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java new file mode 100644 index 000000000..27201695a --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java @@ -0,0 +1,28 @@ +package nostr.event.filter; + +import nostr.event.Kind; +import nostr.event.impl.GenericEvent; + +import java.util.function.BiPredicate; +import java.util.function.Function; + +public class KindFilter implements Filterable { + private final T kind; + + public KindFilter(T kind) { + this.kind = kind; + } + @Override + public BiPredicate getBiPredicate() { + return (publicKey, genericEvent) -> Kind.valueOf(genericEvent.getKind()).equals(kind); + } + @Override + public T getFilterCriterion() { + return kind; + } + + @Override + public Function createContainedInstance() { + return pubkey -> (T) Kind.valueOf(pubkey); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java new file mode 100644 index 000000000..03c57fd42 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java @@ -0,0 +1,31 @@ +package nostr.event.filter; + +import nostr.base.PublicKey; +import nostr.event.impl.GenericEvent; + +import java.util.function.BiPredicate; +import java.util.function.Function; + +public class PublicKeyFilter implements Filterable { + private final T publicKey; + + public PublicKeyFilter(T publicKey) { + this.publicKey = publicKey; + } + @Override + public BiPredicate getBiPredicate() { + return (publicKey, genericEvent) -> publicKey.toString().equals(genericEvent.getPubKey().toString()); + } + @Override + public T getFilterCriterion() { + return publicKey; + } + + @Override + public Function createContainedInstance() { + return pubKeyString -> { + PublicKey pubKey = new PublicKey(pubKeyString); + return (T) pubKey; + }; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java new file mode 100644 index 000000000..5d3ea75ec --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java @@ -0,0 +1,31 @@ +package nostr.event.filter; + +import nostr.event.impl.GenericEvent; + +import java.util.function.BiPredicate; +import java.util.function.Function; + +public class ReferencedEventFilter implements Filterable { + private final T referencedEvent; + + public ReferencedEventFilter(T referencedEvent) { + this.referencedEvent = referencedEvent; + } + @Override + public BiPredicate getBiPredicate() { + return (referencedEvent, genericEvent) -> referencedEvent.getId().equals(genericEvent.getId()); + } + @Override + public T getFilterCriterion() { + return referencedEvent; + } + + @Override + public Function createContainedInstance() { + return eventId -> { + GenericEvent event = new GenericEvent(); + event.setId(eventId); + return (T) event; + }; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java new file mode 100644 index 000000000..38c832006 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java @@ -0,0 +1,31 @@ +package nostr.event.filter; + +import nostr.base.PublicKey; +import nostr.event.impl.GenericEvent; + +import java.util.function.BiPredicate; +import java.util.function.Function; + +public class ReferencedPublicKeyFilter implements Filterable { + private final T publicKey; + + public ReferencedPublicKeyFilter(T publicKey) { + this.publicKey = publicKey; + } + @Override + public BiPredicate getBiPredicate() { + return (publicKey, genericEvent) -> publicKey.toString().equals(genericEvent.getPubKey().toString()); + } + @Override + public T getFilterCriterion() { + return publicKey; + } + + @Override + public Function createContainedInstance() { + return pubKeyString -> { + PublicKey pubKey = new PublicKey(pubKeyString); + return (T) pubKey; + }; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java new file mode 100644 index 000000000..cd6748a45 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java @@ -0,0 +1,28 @@ +package nostr.event.filter; + +import nostr.event.impl.GenericEvent; + +import java.util.function.BiPredicate; +import java.util.function.Function; + +public class SinceFilter implements Filterable { + private final T since; + + public SinceFilter(T since) { + this.since = since; + } + @Override + public BiPredicate getBiPredicate() { + return (since, genericEvent) -> (Long) since > genericEvent.getCreatedAt(); + } + + @Override + public T getFilterCriterion() { + return since; + } + + @Override + public Function createContainedInstance() { + return longValue -> (T) Long.valueOf(longValue); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java new file mode 100644 index 000000000..c51331ac1 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java @@ -0,0 +1,27 @@ +package nostr.event.filter; + +import nostr.event.impl.GenericEvent; + +import java.util.function.BiPredicate; +import java.util.function.Function; + +public class UntilFilter implements Filterable { + private final T until; + + public UntilFilter(T until) { + this.until = until; + } + @Override + public BiPredicate getBiPredicate() { + return (since, genericEvent) -> (Long) since < genericEvent.getCreatedAt(); + } + @Override + public T getFilterCriterion() { + return until; + } + + @Override + public Function createContainedInstance() { + return longValue -> (T) Long.valueOf(longValue); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/Filters.java b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java deleted file mode 100644 index 44272f9f3..000000000 --- a/nostr-java-event/src/main/java/nostr/event/impl/Filters.java +++ /dev/null @@ -1,79 +0,0 @@ -package nostr.event.impl; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.Setter; -import nostr.base.PublicKey; -import nostr.base.annotation.Key; -import nostr.event.Kind; -import nostr.event.json.serializer.CustomIdEventListSerializer; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** - * - * @author squirrel - */ -@Builder -@Data -@EqualsAndHashCode(callSuper = false) -@NoArgsConstructor -@AllArgsConstructor -public class Filters { - - @Key - @JsonProperty("ids") - @JsonSerialize(using=CustomIdEventListSerializer.class) - private List events; - - @Key - @JsonProperty("authors") - private List authors; - - @Key - private List kinds; - - @Key - @JsonProperty("#e") - @JsonSerialize(using=CustomIdEventListSerializer.class) - private List referencedEvents; - - @Key - @JsonProperty("#p") - private List referencePubKeys; - - @Key - private Long since; - - @Key - private Long until; - - @Key - private Integer limit; - - @Key(nip = 12) - @Setter(AccessLevel.NONE) - private Map> genericTagQuery; - - @JsonAnyGetter - public Map> getGenericTagQuery() { - return genericTagQuery; - } - - @JsonAnySetter - public void setGenericTagQuery(String key, List value) { - this.genericTagQuery = Optional.ofNullable(genericTagQuery).orElse(new HashMap<>()); - this.genericTagQuery.put(key, value); - } -} diff --git a/nostr-java-test/src/test/java/nostr/test/event/FiltersTest.java b/nostr-java-test/src/test/java/nostr/test/event/FiltersTest.java new file mode 100644 index 000000000..0a00f8e9a --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/event/FiltersTest.java @@ -0,0 +1,161 @@ +package nostr.test.event; + +import lombok.extern.java.Log; +import nostr.base.GenericTagQuery; +import nostr.base.PublicKey; +import nostr.base.Relay; +import nostr.event.Kind; +import nostr.event.filter.Filters; +import nostr.event.impl.GenericEvent; +import nostr.event.tag.EventTag; +import nostr.event.tag.IdentifierTag; +import nostr.event.tag.PubKeyTag; +import nostr.id.Identity; +import nostr.test.EntityFactory; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Log +public class FiltersTest { + + @Test + public void testEventFilters() { + PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); + GenericEvent instance = EntityFactory.Events.createTextNoteEvent(publicKey); + instance.update(); + + Filters filters = new Filters(); + GenericEvent eventList = new GenericEvent(); + eventList.setId(instance.getId()); + + filters.setEvents(List.of(eventList)); + + List filtersEvents = filters.getEvents(); + assertEquals(filtersEvents.getFirst().getId(), eventList.getId()); + } + + @Test + public void testAuthorFilters() { + PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); + GenericEvent instance = EntityFactory.Events.createTextNoteEvent(publicKey); + instance.update(); + + Filters filters = new Filters(); + filters.setAuthors(List.of(publicKey)); + + List authors = filters.getAuthors(); + assertEquals(authors, List.of(publicKey)); + } + + @Test + public void testKindFilters() { + Filters filters1 = new Filters(); + List kindList = List.of(Kind.CONTACT_LIST, Kind.DELETION); + filters1.setKinds(kindList); + List kinds = filters1.getKinds(); + assertEquals(kinds, kindList); + } + + @Test + public void testReferencedEventsFilters() { + PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); + + GenericEvent eventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); + eventToReference.update(); + + GenericEvent eventContainingEventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); + eventContainingEventToReference.setTags(List.of(new EventTag(eventToReference.getId()))); + eventContainingEventToReference.update(); + + Filters filters = new Filters(); + filters.setReferencedEvents(List.of(eventContainingEventToReference)); + + filters.getReferencedEvents().getFirst().getTags().stream() + .filter(EventTag.class::isInstance) + .map(EventTag.class::cast) + .findFirst() + .ifPresent(eventTag -> + assertEquals(eventTag.getIdEvent(), eventToReference.getId())); + } + + @Test + public void testReferencedPubkeysFilters() { + PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); + + GenericEvent eventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); + eventToReference.update(); + + GenericEvent eventContainingEventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); + eventContainingEventToReference.setTags(List.of(new PubKeyTag(eventToReference.getPubKey()))); + eventContainingEventToReference.update(); + + Filters filters = new Filters(); + filters.setReferencedEvents(List.of(eventContainingEventToReference)); + + filters.getReferencedEvents().getFirst().getTags().stream() + .filter(PubKeyTag.class::isInstance) + .map(PubKeyTag.class::cast) + .findFirst() + .ifPresent(pubkeyTag -> + assertEquals(pubkeyTag.getPublicKey().toHexString(), eventToReference.getPubKey().toHexString())); + } + + @Test + public void testSinceFilters() { + Filters filters = new Filters(); + long startTime = Date.from(Instant.now()).getTime(); + filters.setSince(startTime); + + assertEquals(startTime, filters.getSince()); + } + + @Test + public void testUntilFilters() { + Filters filters = new Filters(); + long startTime = Date.from(Instant.now()).getTime(); + filters.setUntil(startTime); + + assertEquals(startTime, filters.getUntil()); + } + + @Test + public void testGenericQueryTagFilters() { + PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); + GenericTagQuery genericTagQuery = new GenericTagQuery(); + String key = "#a"; + genericTagQuery.setTagName(key); + + String kind = Kind.TEXT_NOTE.toString(); + String hexString = publicKey.toHexString(); + String uuid = new IdentifierTag("uuid").getId(); + String relayUri = new Relay("ws://localhost:5555").getUri(); + + List addressTagValues = List.of( + kind, + hexString, + uuid, + relayUri + ); + genericTagQuery.setValue(addressTagValues); + + Filters filters = new Filters(); + filters.setGenericTagQuery( + genericTagQuery.getTagName(), + addressTagValues); + + Map> genericTagQuery1 = filters.getGenericTagQuery(key); + log.info(genericTagQuery1.entrySet().toString()); + + assertTrue(genericTagQuery1.get(key).contains(kind)); + assertTrue(genericTagQuery1.get(key).contains(hexString)); + assertTrue(genericTagQuery1.get(key).contains(uuid)); + assertTrue(genericTagQuery1.get(key).contains(relayUri)); + } +} From 5ee8e94805d2e46a112d5aff8a2b4fee2d1585cc Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sat, 1 Feb 2025 22:36:49 -0500 Subject: [PATCH 09/45] filters in own test directory --- .../test/event/{ => filter}/FiltersTest.java | 102 ++++++++++++++++-- 1 file changed, 92 insertions(+), 10 deletions(-) rename nostr-java-test/src/test/java/nostr/test/event/{ => filter}/FiltersTest.java (58%) diff --git a/nostr-java-test/src/test/java/nostr/test/event/FiltersTest.java b/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java similarity index 58% rename from nostr-java-test/src/test/java/nostr/test/event/FiltersTest.java rename to nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java index 0a00f8e9a..27b8b7765 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/FiltersTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java @@ -1,4 +1,4 @@ -package nostr.test.event; +package nostr.test.event.filter; import lombok.extern.java.Log; import nostr.base.GenericTagQuery; @@ -56,10 +56,10 @@ public void testAuthorFilters() { @Test public void testKindFilters() { - Filters filters1 = new Filters(); + Filters filters = new Filters(); List kindList = List.of(Kind.CONTACT_LIST, Kind.DELETION); - filters1.setKinds(kindList); - List kinds = filters1.getKinds(); + filters.setKinds(kindList); + List kinds = filters.getKinds(); assertEquals(kinds, kindList); } @@ -110,19 +110,19 @@ public void testReferencedPubkeysFilters() { @Test public void testSinceFilters() { Filters filters = new Filters(); - long startTime = Date.from(Instant.now()).getTime(); - filters.setSince(startTime); + long sinceTime = Date.from(Instant.now()).getTime(); + filters.setSince(sinceTime); - assertEquals(startTime, filters.getSince()); + assertEquals(sinceTime, filters.getSince()); } @Test public void testUntilFilters() { Filters filters = new Filters(); - long startTime = Date.from(Instant.now()).getTime(); - filters.setUntil(startTime); + long untilTime = Date.from(Instant.now()).getTime(); + filters.setUntil(untilTime); - assertEquals(startTime, filters.getUntil()); + assertEquals(untilTime, filters.getUntil()); } @Test @@ -158,4 +158,86 @@ public void testGenericQueryTagFilters() { assertTrue(genericTagQuery1.get(key).contains(uuid)); assertTrue(genericTagQuery1.get(key).contains(relayUri)); } + + @Test + public void testMultipleFilters() { + PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); + GenericEvent instance = EntityFactory.Events.createTextNoteEvent(publicKey); + instance.update(); + + GenericEvent eventList = new GenericEvent(); + eventList.setId(instance.getId()); + + List kindList = List.of(Kind.CONTACT_LIST, Kind.DELETION); + + GenericEvent eventContainingEventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); + eventContainingEventToReference.setTags(List.of(new EventTag(instance.getId()))); + eventContainingEventToReference.setTags(List.of(new PubKeyTag(instance.getPubKey()))); + eventContainingEventToReference.update(); + + long sinceTime = Date.from(Instant.now()).getTime(); + long untilTime = Date.from(Instant.now()).getTime(); + + Filters filters = new Filters(); + filters.setEvents(List.of(eventList)); + filters.setAuthors(List.of(publicKey)); + filters.setKinds(kindList); + filters.setReferencedEvents(List.of(eventContainingEventToReference)); + filters.setSince(sinceTime); + filters.setUntil(untilTime); + + GenericTagQuery genericTagQuery = new GenericTagQuery(); + String key = "#a"; + genericTagQuery.setTagName(key); + + String kind = Kind.TEXT_NOTE.toString(); + String hexString = publicKey.toHexString(); + String uuid = new IdentifierTag("uuid").getId(); + String relayUri = new Relay("ws://localhost:5555").getUri(); + + List addressTagValues = List.of( + kind, + hexString, + uuid, + relayUri + ); + genericTagQuery.setValue(addressTagValues); + + filters.setGenericTagQuery( + genericTagQuery.getTagName(), + addressTagValues); + + List filtersEvents = filters.getEvents(); + assertEquals(filtersEvents.getFirst().getId(), eventList.getId()); + + List authors = filters.getAuthors(); + assertEquals(authors, List.of(publicKey)); + + List kinds = filters.getKinds(); + assertEquals(kinds, kindList); + + filters.getReferencedEvents().getFirst().getTags().stream() + .filter(EventTag.class::isInstance) + .map(EventTag.class::cast) + .findFirst() + .ifPresent(eventTag -> + assertEquals(eventTag.getIdEvent(), instance.getId())); + + filters.getReferencedEvents().getFirst().getTags().stream() + .filter(PubKeyTag.class::isInstance) + .map(PubKeyTag.class::cast) + .findFirst() + .ifPresent(pubkeyTag -> + assertEquals(pubkeyTag.getPublicKey().toHexString(), instance.getPubKey().toHexString())); + + assertEquals(sinceTime, filters.getSince()); + assertEquals(untilTime, filters.getUntil()); + + Map> genericTagQuery1 = filters.getGenericTagQuery(key); + + assertTrue(genericTagQuery1.get(key).contains(kind)); + assertTrue(genericTagQuery1.get(key).contains(hexString)); + assertTrue(genericTagQuery1.get(key).contains(uuid)); + assertTrue(genericTagQuery1.get(key).contains(relayUri)); + } } From 85bbbbff39c05d3cf7eab9cd9f7d8a99b79e3af9 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sun, 2 Feb 2025 01:08:23 -0500 Subject: [PATCH 10/45] additional filter tests --- .../nostr/event/{filter => impl}/Filters.java | 129 ++++++++++++++---- .../nostr/test/event/filter/FiltersTest.java | 54 +++++--- 2 files changed, 141 insertions(+), 42 deletions(-) rename nostr-java-event/src/main/java/nostr/event/{filter => impl}/Filters.java (60%) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java similarity index 60% rename from nostr-java-event/src/main/java/nostr/event/filter/Filters.java rename to nostr-java-event/src/main/java/nostr/event/impl/Filters.java index 81c28c695..072bd7d15 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java @@ -1,4 +1,4 @@ -package nostr.event.filter; +package nostr.event.impl; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; @@ -6,9 +6,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AccessLevel; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.EqualsAndHashCode; -import lombok.Getter; import lombok.NonNull; import lombok.Setter; import lombok.SneakyThrows; @@ -16,8 +14,17 @@ import nostr.base.PublicKey; import nostr.base.annotation.Key; import nostr.event.Kind; -import nostr.event.impl.GenericEvent; +import nostr.event.filter.EventFilter; +import nostr.event.filter.Filterable; +import nostr.event.filter.FiltersCore; +import nostr.event.filter.GenericTagQueryFilter; +import nostr.event.filter.KindFilter; +import nostr.event.filter.PublicKeyFilter; +import nostr.event.filter.ReferencedPublicKeyFilter; +import nostr.event.filter.SinceFilter; +import nostr.event.filter.UntilFilter; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,22 +35,18 @@ /** * @author squirrel */ -@Builder @EqualsAndHashCode(callSuper = false) @AllArgsConstructor public class Filters { @JsonIgnore private final FiltersCore core; + @Key + private Integer limit; public Filters() { this.core = new FiltersCore(); } - @Key - @Getter - @Setter - private Integer limit; - @JsonProperty("ids") public List getEvents() { return getFilterableListByType("ids"); @@ -97,7 +100,7 @@ public void setReferencePubKeys(@NonNull List publicKeys) { } @JsonProperty("since") - public Long getSince() { + public Optional getSince() { return getSinceOrUntil("since"); } @@ -107,7 +110,7 @@ public void setSince(@NonNull Long since) { } @JsonProperty("until") - public Long getUntil() { + public Optional getUntil() { return getSinceOrUntil("until"); } @@ -124,24 +127,31 @@ public List getReferenceforbelow() { } @JsonAnyGetter - public Map> getGenericTagQuery(@NonNull String key) { + public Map> getGenericTagQuery() { + this.genericTagQuery = Optional.ofNullable(genericTagQuery).orElse(new HashMap<>()); return genericTagQuery; } + public List getGenericTagQuery(@NonNull String key) { + return Optional.ofNullable(getGenericTagQuery().get(key)).orElse(Collections.emptyList()); + } + + // TODO: add map content validation? + @JsonAnySetter + public void setGenericTagQuery(@NonNull Map> map) { + map.forEach(this::setGenericTagQuery); + } + + // TODO: add map content validation? @JsonAnySetter public void setGenericTagQuery(@NonNull String key, @NonNull List value) { - this.genericTagQuery = Optional.ofNullable(genericTagQuery).orElse(new HashMap<>()); - this.genericTagQuery.put(key, value); + getGenericTagQuery().put(key, value); GenericTagQuery query = new GenericTagQuery(); query.setTagName(key); query.setValue(value); setFilterableListByType(key, List.of(query), GenericTagQueryFilter::new); } - public void setReferencedEventsasdf(@NonNull List events) { - setFilterableListByType("#e", events, EventFilter::new); - } - private List getFilterableListByType(@NonNull String type) { return Optional .ofNullable( @@ -152,7 +162,6 @@ private List getFilterableListByType(@NonNull String type) { .toList(); } - @SneakyThrows private void setFilterableListByType( @NonNull String key, @@ -166,13 +175,83 @@ private void setFilterableListByType( // String.format("[%s] filter must contain at least one element"))) } - private Long getSinceOrUntil(String type) { + private Optional getSinceOrUntil(String type) { return Optional .ofNullable( this.core.getFilterableByType(type)) - .stream().flatMap(filterables -> - filterables.stream().map(dateFilter -> - (Long) dateFilter.getFilterCriterion())) - .toList().getFirst(); + .map(filterables -> filterables + .getFirst().getFilterCriterion()); + } + + public Optional getLimit() { + return Optional.ofNullable(limit); + } + + public void setLimit(@NonNull Integer limit) { + this.limit = limit; + } + + public static FiltersBuilder builder() { + return new FiltersBuilder(); + } + + public static class FiltersBuilder { + private final Filters filters = new Filters(); + + @JsonProperty("ids") + public FiltersBuilder events(@NonNull List events) { + filters.setEvents(events); + return this; + } + + @JsonProperty("authors") + public FiltersBuilder authors(@NonNull List authors) { + filters.setAuthors(authors); + return this; + } + + @JsonProperty("kinds") + public FiltersBuilder kinds(@NonNull List kinds) { + filters.setKinds(kinds); + return this; + } + + @JsonProperty("#e") + public FiltersBuilder referencedEvents(@NonNull List events) { + filters.setReferencedEvents(events); + return this; + } + + @JsonProperty("#p") + public FiltersBuilder referencePubKeys(@NonNull List publicKeys) { + filters.setReferencePubKeys(publicKeys); + return this; + } + + @JsonProperty("since") + public FiltersBuilder since(@NonNull Long since) { + filters.setSince(since); + return this; + } + + @JsonProperty("until") + public FiltersBuilder until(@NonNull Long until) { + filters.setUntil(until); + return this; + } + + public FiltersBuilder genericTagQuery(@NonNull Map> map) { + filters.setGenericTagQuery(map); + return this; + } + + public FiltersBuilder limit(@NonNull Integer limit) { + filters.setLimit(limit); + return this; + } + + public Filters build() { + return filters; + } } } diff --git a/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java b/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java index 27b8b7765..bc777fd8e 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java @@ -5,7 +5,7 @@ import nostr.base.PublicKey; import nostr.base.Relay; import nostr.event.Kind; -import nostr.event.filter.Filters; +import nostr.event.impl.Filters; import nostr.event.impl.GenericEvent; import nostr.event.tag.EventTag; import nostr.event.tag.IdentifierTag; @@ -17,7 +17,6 @@ import java.time.Instant; import java.util.Date; import java.util.List; -import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -113,7 +112,7 @@ public void testSinceFilters() { long sinceTime = Date.from(Instant.now()).getTime(); filters.setSince(sinceTime); - assertEquals(sinceTime, filters.getSince()); + filters.getSince().ifPresent(aLong -> assertEquals(sinceTime, aLong)); } @Test @@ -122,7 +121,14 @@ public void testUntilFilters() { long untilTime = Date.from(Instant.now()).getTime(); filters.setUntil(untilTime); - assertEquals(untilTime, filters.getUntil()); + filters.getUntil().ifPresent(aLong -> assertEquals(untilTime, aLong)); + } + + @Test + public void testLimit() { + Filters filters = new Filters(); + filters.setLimit(1); + filters.getLimit().ifPresent(integer -> assertEquals(1, integer)); } @Test @@ -150,13 +156,13 @@ public void testGenericQueryTagFilters() { genericTagQuery.getTagName(), addressTagValues); - Map> genericTagQuery1 = filters.getGenericTagQuery(key); - log.info(genericTagQuery1.entrySet().toString()); + List genericTagQueryResult = filters.getGenericTagQuery(key); + genericTagQueryResult.forEach(log::info); - assertTrue(genericTagQuery1.get(key).contains(kind)); - assertTrue(genericTagQuery1.get(key).contains(hexString)); - assertTrue(genericTagQuery1.get(key).contains(uuid)); - assertTrue(genericTagQuery1.get(key).contains(relayUri)); + assertTrue(genericTagQueryResult.contains(kind)); + assertTrue(genericTagQueryResult.contains(hexString)); + assertTrue(genericTagQueryResult.contains(uuid)); + assertTrue(genericTagQueryResult.contains(relayUri)); } @Test @@ -230,14 +236,28 @@ public void testMultipleFilters() { .ifPresent(pubkeyTag -> assertEquals(pubkeyTag.getPublicKey().toHexString(), instance.getPubKey().toHexString())); - assertEquals(sinceTime, filters.getSince()); - assertEquals(untilTime, filters.getUntil()); + filters.getSince().ifPresent(aLong -> assertEquals(sinceTime, aLong)); + filters.getUntil().ifPresent(aLong -> assertEquals(untilTime, aLong)); - Map> genericTagQuery1 = filters.getGenericTagQuery(key); + List genericTagQueryResult = filters.getGenericTagQuery(key); - assertTrue(genericTagQuery1.get(key).contains(kind)); - assertTrue(genericTagQuery1.get(key).contains(hexString)); - assertTrue(genericTagQuery1.get(key).contains(uuid)); - assertTrue(genericTagQuery1.get(key).contains(relayUri)); + assertTrue(genericTagQueryResult.contains(kind)); + assertTrue(genericTagQueryResult.contains(hexString)); + assertTrue(genericTagQueryResult.contains(uuid)); + assertTrue(genericTagQueryResult.contains(relayUri)); + } + + @Test + public void testNonExistingValues() { + Filters filters = new Filters(); + assertTrue(filters.getLimit().isEmpty()); + assertTrue(filters.getAuthors().isEmpty()); + assertTrue(filters.getKinds().isEmpty()); + assertTrue(filters.getReferencedEvents().isEmpty()); + assertTrue(filters.getUntil().isEmpty()); + assertTrue(filters.getEvents().isEmpty()); + assertTrue(filters.getReferencePubKeys().isEmpty()); + assertTrue(filters.getGenericTagQuery().isEmpty()); + assertTrue(filters.getGenericTagQuery("someKey").isEmpty()); } } From 46ba956683ba5648282899f6624586e9fcf7fd46 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sun, 2 Feb 2025 01:09:04 -0500 Subject: [PATCH 11/45] module info --- nostr-java-event/src/main/java/module-info.java | 1 + 1 file changed, 1 insertion(+) diff --git a/nostr-java-event/src/main/java/module-info.java b/nostr-java-event/src/main/java/module-info.java index 607f39c9e..60e80d4b3 100644 --- a/nostr-java-event/src/main/java/module-info.java +++ b/nostr-java-event/src/main/java/module-info.java @@ -18,4 +18,5 @@ exports nostr.event.json.serializer; exports nostr.event.tag; exports nostr.event.util; + exports nostr.event.filter; } From be2dc42fb9c02d6be460c77f6f59782a330465a9 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sun, 2 Feb 2025 01:47:25 -0500 Subject: [PATCH 12/45] test edge/null/empty cases --- .../main/java/nostr/event/impl/Filters.java | 16 +++++++-- .../nostr/test/event/filter/FiltersTest.java | 34 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/impl/Filters.java b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java index 072bd7d15..747c31c8e 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java @@ -139,17 +139,21 @@ public List getGenericTagQuery(@NonNull String key) { // TODO: add map content validation? @JsonAnySetter public void setGenericTagQuery(@NonNull Map> map) { + if (map.isEmpty()) + throw new IllegalArgumentException("generic tag query cannot be empty"); map.forEach(this::setGenericTagQuery); } // TODO: add map content validation? @JsonAnySetter public void setGenericTagQuery(@NonNull String key, @NonNull List value) { - getGenericTagQuery().put(key, value); + if (key.isBlank()) + throw new IllegalArgumentException("key cannot be null"); GenericTagQuery query = new GenericTagQuery(); query.setTagName(key); query.setValue(value); setFilterableListByType(key, List.of(query), GenericTagQueryFilter::new); + getGenericTagQuery().put(key, value); } private List getFilterableListByType(@NonNull String type) { @@ -165,11 +169,17 @@ private List getFilterableListByType(@NonNull String type) { @SneakyThrows private void setFilterableListByType( @NonNull String key, - @NonNull List filterType, + @NonNull List filterTypeList, @NonNull Function filterableFunction) { + + if (filterTypeList.isEmpty()) { + throw new IllegalArgumentException( + String.format("[%s] filter must contain at least one element", key)); + } + this.core.addFilterList( key, - filterType.stream().map(filterableFunction).collect(Collectors.toList())); + filterTypeList.stream().map(filterableFunction).collect(Collectors.toList())); // .orElseThrow(() -> // new IllegalArgumentException( // String.format("[%s] filter must contain at least one element"))) diff --git a/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java b/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java index bc777fd8e..d3ad8457f 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java @@ -17,8 +17,10 @@ import java.time.Instant; import java.util.Date; import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @Log @@ -260,4 +262,36 @@ public void testNonExistingValues() { assertTrue(filters.getGenericTagQuery().isEmpty()); assertTrue(filters.getGenericTagQuery("someKey").isEmpty()); } + + @Test + public void testNonExistingTagTypes() { + Filters filters = new Filters(); + String key = "some-random-key"; + String value = "some-random-value"; + filters.setGenericTagQuery( + key, + List.of(value)); + + assertTrue(filters.getGenericTagQuery(key).contains(value)); + + String anotherValue = "another-random-value"; + filters.setGenericTagQuery( + key, + List.of(value, anotherValue)); + assertTrue(filters.getGenericTagQuery(key).contains(value)); + assertTrue(filters.getGenericTagQuery(key).contains(anotherValue)); + } + + @Test + public void testNullValuesThrowException() { + Filters filters = new Filters(); + assertThrows(NullPointerException.class, () -> filters.setLimit(null)); + assertThrows(IllegalArgumentException.class, () -> filters.setEvents(List.of())); + assertThrows(IllegalArgumentException.class, () -> filters.setAuthors(List.of())); + assertThrows(IllegalArgumentException.class, () -> filters.setKinds(List.of())); + assertThrows(IllegalArgumentException.class, () -> filters.setReferencedEvents(List.of())); + assertThrows(IllegalArgumentException.class, () -> filters.setReferencePubKeys(List.of())); + assertThrows(IllegalArgumentException.class, () -> filters.setGenericTagQuery("", List.of())); + assertThrows(IllegalArgumentException.class, () -> filters.setGenericTagQuery(Map.of())); + } } From 4c8b52dc3742d75ad6704bf3226b976c437f49f7 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Mon, 3 Feb 2025 15:48:51 -0500 Subject: [PATCH 13/45] update using predicates --- .../event/filter/AddressableTagFilter.java | 57 ++++++++++ .../java/nostr/event/filter/EventFilter.java | 15 ++- .../java/nostr/event/filter/Filterable.java | 22 +++- .../java/nostr/event/filter/FiltersCore.java | 17 ++- .../event/filter/GenericTagQueryFilter.java | 29 ++++- .../event/filter/IdentifierTagFilter.java | 39 +++++++ .../java/nostr/event/filter/KindFilter.java | 15 ++- .../nostr/event/filter/PublicKeyFilter.java | 13 ++- .../event/filter/ReferencedEventFilter.java | 15 ++- .../filter/ReferencedPublicKeyFilter.java | 15 ++- .../java/nostr/event/filter/SinceFilter.java | 26 +++-- .../java/nostr/event/filter/UntilFilter.java | 27 +++-- .../main/java/nostr/event/impl/Filters.java | 105 +++++++++++------- 13 files changed, 312 insertions(+), 83 deletions(-) create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java create mode 100644 nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java new file mode 100644 index 000000000..4a1a9f162 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java @@ -0,0 +1,57 @@ +package nostr.event.filter; + +import lombok.NonNull; +import nostr.base.PublicKey; +import nostr.event.impl.GenericEvent; +import nostr.event.tag.AddressTag; +import nostr.event.tag.IdentifierTag; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +public class AddressableTagFilter implements Filterable { + public final static String filterKey = "#a"; + private final T addressableTag; + + public AddressableTagFilter(T addressableTag) { + this.addressableTag = addressableTag; + } + + @Override + public Predicate getPredicate() { + return this::compare; + } + + private boolean compare(@NonNull GenericEvent genericEvent) { + IdentifierTag addressTagIdentifierTag = this.addressableTag.getIdentifierTag(); + String addressTagPublicKey = this.addressableTag.getPublicKey().toHexString(); + Integer addressTagKind = this.addressableTag.getKind(); + + String genericEventPubKey = genericEvent.getPubKey().toHexString(); + Integer genericEventKind = genericEvent.getKind(); + List genericEventIdentifierTags = getIdentifierTags(genericEvent); + + return genericEventPubKey.equals(addressTagPublicKey) && + genericEventKind.equals(addressTagKind) && + genericEventIdentifierTags.stream().anyMatch(identifierTag -> + identifierTag.getId().equals(addressTagIdentifierTag.getId())); + } + + @Override + public T getFilterCriterion() { + return addressableTag; + } + + @Override + public Function createContainedInstance() { + return pubKeyString -> { + PublicKey pubKey = new PublicKey(pubKeyString); + return (T) pubKey; + }; + } + @Override + public String getFilterKey() { + return filterKey; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java index 3e238447e..4dd2c28ab 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java @@ -2,19 +2,23 @@ import nostr.event.impl.GenericEvent; -import java.util.function.BiPredicate; import java.util.function.Function; +import java.util.function.Predicate; public class EventFilter implements Filterable { + public final static String filterKey = "ids"; private final T event; public EventFilter(T event) { this.event = event; } + @Override - public BiPredicate getBiPredicate() { - return (event, genericEvent) -> event.getId().equals(genericEvent.getId()); + public Predicate getPredicate() { + return (genericEvent) -> + this.event.getId().equals(genericEvent.getId()); } + @Override public T getFilterCriterion() { return event; @@ -28,4 +32,9 @@ public Function createContainedInstance() { return (T) event; }; } + + @Override + public String getFilterKey() { + return filterKey; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java index dae036c7e..dc4371ce0 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java @@ -1,12 +1,30 @@ package nostr.event.filter; import nostr.event.impl.GenericEvent; +import nostr.event.impl.GenericTag; +import nostr.event.tag.IdentifierTag; -import java.util.function.BiPredicate; +import java.util.List; import java.util.function.Function; +import java.util.function.Predicate; public interface Filterable { - BiPredicate getBiPredicate(); + Predicate getPredicate(); T getFilterCriterion(); Function createContainedInstance(); + String getFilterKey(); + + default List getGenericTags(GenericEvent genericEvent) { + return genericEvent.getTags().stream() + .filter(GenericTag.class::isInstance) + .map(GenericTag.class::cast) + .toList(); + } + + default List getIdentifierTags(GenericEvent genericEvent) { + return genericEvent.getTags().stream() + .filter(IdentifierTag.class::isInstance) + .map(IdentifierTag.class::cast) + .toList(); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java b/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java index 684f0f85b..c04ffab77 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java @@ -1,23 +1,30 @@ package nostr.event.filter; +import lombok.Getter; import lombok.NonNull; +import lombok.Setter; import java.util.HashMap; import java.util.List; import java.util.Map; +@Getter public class FiltersCore { - Map> filtersMap = new HashMap<>(); + private final Map> filtersMap = new HashMap<>(); - public void addFilter(@NonNull String key, @NonNull Filterable filterable) { - addFilterList(key, List.of(filterable)); +// TODO: make limit configurable + @Setter + private Integer limit = 10; + + public void addFilterable(@NonNull String key, @NonNull Filterable filterable) { + addFilterable(key, List.of(filterable)); } - public void addFilterList(@NonNull String key, @NonNull List filterable) { + public void addFilterable(@NonNull String key, @NonNull List filterable) { filtersMap.put(key, filterable); } - public List getFilterableByType(String type) { + public List getFilterableByType(@NonNull String type) { return filtersMap.get(type); } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java index 3a934b88d..c1a8f07e1 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java @@ -1,21 +1,35 @@ package nostr.event.filter; -import lombok.NonNull; +import nostr.base.ElementAttribute; import nostr.base.GenericTagQuery; +import nostr.event.impl.GenericEvent; -import java.util.function.BiPredicate; +import java.util.HashSet; import java.util.function.Function; +import java.util.function.Predicate; public class GenericTagQueryFilter implements Filterable { + public final static String filterKey = "undefined"; private final T genericTagQuery; - public GenericTagQueryFilter(@NonNull T genericTagQuery) { + public GenericTagQueryFilter(T genericTagQuery) { this.genericTagQuery = genericTagQuery; } + @Override - public BiPredicate getBiPredicate() { - return (genericTagQueryA, genericTagQueryB) -> genericTagQueryA.getTagName().equals(genericTagQueryB.getTagName()); + public Predicate getPredicate() { + return (genericEvent) -> + getGenericTags(genericEvent).stream() + .filter(genericTag -> + genericTag.getCode().equals(this.genericTagQuery.getTagName())) + .anyMatch(genericTag -> + new HashSet<>(genericTag + .getAttributes().stream().map( + ElementAttribute::getValue).toList()) + .containsAll( + this.genericTagQuery.getValue())); } + @Override public T getFilterCriterion() { return genericTagQuery; @@ -29,4 +43,9 @@ public Function createContainedInstance() { return (T) tagQuery; }; } + + @Override + public String getFilterKey() { + return filterKey; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java new file mode 100644 index 000000000..a0123a575 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java @@ -0,0 +1,39 @@ +package nostr.event.filter; + +import nostr.event.impl.GenericEvent; +import nostr.event.tag.IdentifierTag; + +import java.util.function.Function; +import java.util.function.Predicate; + +public class IdentifierTagFilter implements Filterable { + public final static String filterKey = "#d"; + private final T identifierTag; + + public IdentifierTagFilter(T identifierTag) { + this.identifierTag = identifierTag; + } + @Override + public Predicate getPredicate() { + return (genericEvent) -> + (getIdentifierTags(genericEvent).stream().anyMatch(genericEventIdentifiterTag -> + genericEventIdentifiterTag.getId().equals(this.identifierTag.getId()))); + } + @Override + public T getFilterCriterion() { + return identifierTag; + } + + @Override + public Function createContainedInstance() { + return identifier -> { + IdentifierTag identiferTag = new IdentifierTag(identifier); + return (T) identiferTag; + }; + } + + @Override + public String getFilterKey() { + return filterKey; + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java index 27201695a..8c83e6c7b 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java @@ -3,19 +3,23 @@ import nostr.event.Kind; import nostr.event.impl.GenericEvent; -import java.util.function.BiPredicate; import java.util.function.Function; +import java.util.function.Predicate; public class KindFilter implements Filterable { + public final static String filterKey = "kinds"; private final T kind; public KindFilter(T kind) { this.kind = kind; } + @Override - public BiPredicate getBiPredicate() { - return (publicKey, genericEvent) -> Kind.valueOf(genericEvent.getKind()).equals(kind); + public Predicate getPredicate() { + return (genericEvent) -> + genericEvent.getKind().equals(this.kind.getValue()); } + @Override public T getFilterCriterion() { return kind; @@ -25,4 +29,9 @@ public T getFilterCriterion() { public Function createContainedInstance() { return pubkey -> (T) Kind.valueOf(pubkey); } + + @Override + public String getFilterKey() { + return filterKey; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java index 03c57fd42..1c1594687 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java @@ -3,18 +3,20 @@ import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; -import java.util.function.BiPredicate; import java.util.function.Function; +import java.util.function.Predicate; public class PublicKeyFilter implements Filterable { + public final static String filterKey = "authors"; private final T publicKey; public PublicKeyFilter(T publicKey) { this.publicKey = publicKey; } @Override - public BiPredicate getBiPredicate() { - return (publicKey, genericEvent) -> publicKey.toString().equals(genericEvent.getPubKey().toString()); + public Predicate getPredicate() { + return (genericEvent) -> + this.publicKey.toHexString().equals(genericEvent.getPubKey().toHexString()); } @Override public T getFilterCriterion() { @@ -28,4 +30,9 @@ public Function createContainedInstance() { return (T) pubKey; }; } + + @Override + public String getFilterKey() { + return filterKey; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java index 5d3ea75ec..e52567fbb 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java @@ -2,19 +2,23 @@ import nostr.event.impl.GenericEvent; -import java.util.function.BiPredicate; import java.util.function.Function; +import java.util.function.Predicate; public class ReferencedEventFilter implements Filterable { + public final static String filterKey = "#e"; private final T referencedEvent; public ReferencedEventFilter(T referencedEvent) { this.referencedEvent = referencedEvent; } + @Override - public BiPredicate getBiPredicate() { - return (referencedEvent, genericEvent) -> referencedEvent.getId().equals(genericEvent.getId()); + public Predicate getPredicate() { + return (genericEvent) -> + this.referencedEvent.getId().equals(genericEvent.getId()); } + @Override public T getFilterCriterion() { return referencedEvent; @@ -28,4 +32,9 @@ public Function createContainedInstance() { return (T) event; }; } + + @Override + public String getFilterKey() { + return filterKey; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java index 38c832006..dbb030bf8 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java @@ -3,19 +3,23 @@ import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; -import java.util.function.BiPredicate; import java.util.function.Function; +import java.util.function.Predicate; public class ReferencedPublicKeyFilter implements Filterable { + public final static String filterKey = "#p"; private final T publicKey; public ReferencedPublicKeyFilter(T publicKey) { this.publicKey = publicKey; } + @Override - public BiPredicate getBiPredicate() { - return (publicKey, genericEvent) -> publicKey.toString().equals(genericEvent.getPubKey().toString()); + public Predicate getPredicate() { + return (genericEvent) -> + genericEvent.getPubKey().toHexString().equals(this.publicKey.toHexString()); } + @Override public T getFilterCriterion() { return publicKey; @@ -28,4 +32,9 @@ public Function createContainedInstance() { return (T) pubKey; }; } + + @Override + public String getFilterKey() { + return filterKey; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java index cd6748a45..3a6ca45e2 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java @@ -2,27 +2,35 @@ import nostr.event.impl.GenericEvent; -import java.util.function.BiPredicate; import java.util.function.Function; +import java.util.function.Predicate; -public class SinceFilter implements Filterable { - private final T since; +public class SinceFilter implements Filterable { + public final static String filterKey = "since"; + private final Long since; - public SinceFilter(T since) { + public SinceFilter(Long since) { this.since = since; } + @Override - public BiPredicate getBiPredicate() { - return (since, genericEvent) -> (Long) since > genericEvent.getCreatedAt(); + public Predicate getPredicate() { + return (genericEvent) -> + this.since > genericEvent.getCreatedAt(); } @Override - public T getFilterCriterion() { + public Long getFilterCriterion() { return since; } @Override - public Function createContainedInstance() { - return longValue -> (T) Long.valueOf(longValue); + public Function createContainedInstance() { + return longValue -> Long.valueOf(longValue); + } + + @Override + public String getFilterKey() { + return filterKey; } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java index c51331ac1..4d438472e 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java @@ -2,26 +2,35 @@ import nostr.event.impl.GenericEvent; -import java.util.function.BiPredicate; import java.util.function.Function; +import java.util.function.Predicate; -public class UntilFilter implements Filterable { - private final T until; +public class UntilFilter implements Filterable { + public final static String filterKey = "until"; + private final Long until; - public UntilFilter(T until) { + public UntilFilter(Long until) { this.until = until; } + @Override - public BiPredicate getBiPredicate() { - return (since, genericEvent) -> (Long) since < genericEvent.getCreatedAt(); + public Predicate getPredicate() { + return (genericEvent) -> + this.until <= genericEvent.getCreatedAt(); } + @Override - public T getFilterCriterion() { + public Long getFilterCriterion() { return until; } @Override - public Function createContainedInstance() { - return longValue -> (T) Long.valueOf(longValue); + public Function createContainedInstance() { + return longValue -> Long.valueOf(longValue); + } + + @Override + public String getFilterKey() { + return filterKey; } } diff --git a/nostr-java-event/src/main/java/nostr/event/impl/Filters.java b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java index 747c31c8e..1e2f78b6c 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java @@ -14,15 +14,20 @@ import nostr.base.PublicKey; import nostr.base.annotation.Key; import nostr.event.Kind; +import nostr.event.filter.AddressableTagFilter; import nostr.event.filter.EventFilter; import nostr.event.filter.Filterable; import nostr.event.filter.FiltersCore; import nostr.event.filter.GenericTagQueryFilter; +import nostr.event.filter.IdentifierTagFilter; import nostr.event.filter.KindFilter; import nostr.event.filter.PublicKeyFilter; +import nostr.event.filter.ReferencedEventFilter; import nostr.event.filter.ReferencedPublicKeyFilter; import nostr.event.filter.SinceFilter; import nostr.event.filter.UntilFilter; +import nostr.event.tag.AddressTag; +import nostr.event.tag.IdentifierTag; import java.util.Collections; import java.util.HashMap; @@ -47,83 +52,87 @@ public Filters() { this.core = new FiltersCore(); } - @JsonProperty("ids") + public FiltersCore getFiltersCore() { + return this.core; + } + + @JsonProperty(EventFilter.filterKey) public List getEvents() { - return getFilterableListByType("ids"); + return getFilterableListByType(EventFilter.filterKey); } - @JsonProperty("ids") + @JsonProperty(EventFilter.filterKey) public void setEvents(@NonNull List events) { - setFilterableListByType("ids", events, EventFilter::new); + setFilterableListByType(EventFilter.filterKey, events, EventFilter::new); } - @JsonProperty("authors") + @JsonProperty(PublicKeyFilter.filterKey) public List getAuthors() { // TODO: may possibly require below variant -// getFilterableByType("authors", PublicKeyFilter.class); - return getFilterableListByType("authors"); +// getFilterableByType(ReferencedPublicKeyFilter.filterKey, PublicKeyFilter.class); + return getFilterableListByType(PublicKeyFilter.filterKey); } - @JsonProperty("authors") + @JsonProperty(PublicKeyFilter.filterKey) public void setAuthors(@NonNull List authors) { - setFilterableListByType("authors", authors, PublicKeyFilter::new); + setFilterableListByType(PublicKeyFilter.filterKey, authors, PublicKeyFilter::new); } - @JsonProperty("kinds") + @JsonProperty(KindFilter.filterKey) public List getKinds() { - return getFilterableListByType("kinds"); + return getFilterableListByType(KindFilter.filterKey); } - @JsonProperty("kinds") + @JsonProperty(KindFilter.filterKey) public void setKinds(@NonNull List kinds) { - setFilterableListByType("kinds", kinds, KindFilter::new); + setFilterableListByType(KindFilter.filterKey, kinds, KindFilter::new); } - @JsonProperty("#e") + @JsonProperty(ReferencedEventFilter.filterKey) public List getReferencedEvents() { - return getFilterableListByType("#e"); + return getFilterableListByType(ReferencedEventFilter.filterKey); } - @JsonProperty("#e") + @JsonProperty(ReferencedEventFilter.filterKey) public void setReferencedEvents(@NonNull List events) { - setFilterableListByType("#e", events, EventFilter::new); + setFilterableListByType(ReferencedEventFilter.filterKey, events, EventFilter::new); } - @JsonProperty("#p") + @JsonProperty(ReferencedPublicKeyFilter.filterKey) public List getReferencePubKeys() { - return getFilterableListByType("#p"); + return getFilterableListByType(ReferencedPublicKeyFilter.filterKey); } - @JsonProperty("#p") + @JsonProperty(ReferencedPublicKeyFilter.filterKey) public void setReferencePubKeys(@NonNull List publicKeys) { - setFilterableListByType("#p", publicKeys, ReferencedPublicKeyFilter::new); + setFilterableListByType(ReferencedPublicKeyFilter.filterKey, publicKeys, ReferencedPublicKeyFilter::new); } - @JsonProperty("since") + @JsonProperty(SinceFilter.filterKey) public Optional getSince() { - return getSinceOrUntil("since"); + return getSinceOrUntil(SinceFilter.filterKey); } - @JsonProperty("since") + @JsonProperty(SinceFilter.filterKey) public void setSince(@NonNull Long since) { - this.core.addFilter("since", new SinceFilter<>(since)); + this.core.addFilterable(SinceFilter.filterKey, new SinceFilter(since)); } - @JsonProperty("until") + @JsonProperty(UntilFilter.filterKey) public Optional getUntil() { - return getSinceOrUntil("until"); + return getSinceOrUntil(UntilFilter.filterKey); } - @JsonProperty("until") + @JsonProperty(UntilFilter.filterKey) public void setUntil(@NonNull Long until) { - this.core.addFilter("until", new UntilFilter<>(until)); + this.core.addFilterable(UntilFilter.filterKey, new UntilFilter(until)); } @Setter(AccessLevel.NONE) private Map> genericTagQuery; public List getReferenceforbelow() { - return getFilterableListByType("ids"); + return getFilterableListByType("asdfasdf"); } @JsonAnyGetter @@ -156,6 +165,26 @@ public void setGenericTagQuery(@NonNull String key, @NonNull List value) getGenericTagQuery().put(key, value); } + @JsonProperty(AddressableTagFilter.filterKey) + public List getAddressableTags() { + return getFilterableListByType(AddressableTagFilter.filterKey); + } + + @JsonProperty(AddressableTagFilter.filterKey) + public void setAddressableTags(@NonNull List addressTags) { + setFilterableListByType(AddressableTagFilter.filterKey, addressTags, AddressableTagFilter::new); + } + + @JsonProperty(IdentifierTagFilter.filterKey) + public List getIdentifierTags() { + return getFilterableListByType(IdentifierTagFilter.filterKey); + } + + @JsonProperty(IdentifierTagFilter.filterKey) + public void setIdentifierTags(@NonNull List identifierTags) { + setFilterableListByType(IdentifierTagFilter.filterKey, identifierTags, IdentifierTagFilter::new); + } + private List getFilterableListByType(@NonNull String type) { return Optional .ofNullable( @@ -177,7 +206,7 @@ private void setFilterableListByType( String.format("[%s] filter must contain at least one element", key)); } - this.core.addFilterList( + this.core.addFilterable( key, filterTypeList.stream().map(filterableFunction).collect(Collectors.toList())); // .orElseThrow(() -> @@ -208,43 +237,43 @@ public static FiltersBuilder builder() { public static class FiltersBuilder { private final Filters filters = new Filters(); - @JsonProperty("ids") + @JsonProperty(EventFilter.filterKey) public FiltersBuilder events(@NonNull List events) { filters.setEvents(events); return this; } - @JsonProperty("authors") + @JsonProperty(PublicKeyFilter.filterKey) public FiltersBuilder authors(@NonNull List authors) { filters.setAuthors(authors); return this; } - @JsonProperty("kinds") + @JsonProperty(KindFilter.filterKey) public FiltersBuilder kinds(@NonNull List kinds) { filters.setKinds(kinds); return this; } - @JsonProperty("#e") + @JsonProperty(ReferencedEventFilter.filterKey) public FiltersBuilder referencedEvents(@NonNull List events) { filters.setReferencedEvents(events); return this; } - @JsonProperty("#p") + @JsonProperty(ReferencedPublicKeyFilter.filterKey) public FiltersBuilder referencePubKeys(@NonNull List publicKeys) { filters.setReferencePubKeys(publicKeys); return this; } - @JsonProperty("since") + @JsonProperty(SinceFilter.filterKey) public FiltersBuilder since(@NonNull Long since) { filters.setSince(since); return this; } - @JsonProperty("until") + @JsonProperty(UntilFilter.filterKey) public FiltersBuilder until(@NonNull Long until) { filters.setUntil(until); return this; From d27e2f736d287070dc2b51be6840f3021ef0416e Mon Sep 17 00:00:00 2001 From: nick avlo Date: Mon, 3 Feb 2025 18:28:52 -0500 Subject: [PATCH 14/45] bug fixes --- .../event/filter/AddressableTagFilter.java | 22 +++++------- .../java/nostr/event/filter/Filterable.java | 16 +++------ .../event/filter/GenericTagQueryFilter.java | 3 +- .../event/filter/IdentifierTagFilter.java | 4 +-- .../event/filter/ReferencedEventFilter.java | 5 ++- .../filter/ReferencedPublicKeyFilter.java | 5 ++- .../main/java/nostr/event/impl/Filters.java | 2 +- .../main/java/nostr/test/EntityFactory.java | 36 +++++++++---------- 8 files changed, 44 insertions(+), 49 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java index 4a1a9f162..3441f69a0 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java @@ -6,7 +6,6 @@ import nostr.event.tag.AddressTag; import nostr.event.tag.IdentifierTag; -import java.util.List; import java.util.function.Function; import java.util.function.Predicate; @@ -24,18 +23,15 @@ public Predicate getPredicate() { } private boolean compare(@NonNull GenericEvent genericEvent) { - IdentifierTag addressTagIdentifierTag = this.addressableTag.getIdentifierTag(); - String addressTagPublicKey = this.addressableTag.getPublicKey().toHexString(); - Integer addressTagKind = this.addressableTag.getKind(); - - String genericEventPubKey = genericEvent.getPubKey().toHexString(); - Integer genericEventKind = genericEvent.getKind(); - List genericEventIdentifierTags = getIdentifierTags(genericEvent); - - return genericEventPubKey.equals(addressTagPublicKey) && - genericEventKind.equals(addressTagKind) && - genericEventIdentifierTags.stream().anyMatch(identifierTag -> - identifierTag.getId().equals(addressTagIdentifierTag.getId())); + return + !genericEvent.getPubKey().toHexString().equals( + this.addressableTag.getPublicKey().toHexString()) || + !genericEvent.getKind().equals( + this.addressableTag.getKind()) || + getTypeSpecificTags(IdentifierTag.class, genericEvent).stream() + .anyMatch(identifierTag -> + identifierTag.getId().equals( + this.addressableTag.getIdentifierTag().getId())); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java index dc4371ce0..17659f546 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java @@ -1,8 +1,7 @@ package nostr.event.filter; +import nostr.event.BaseTag; import nostr.event.impl.GenericEvent; -import nostr.event.impl.GenericTag; -import nostr.event.tag.IdentifierTag; import java.util.List; import java.util.function.Function; @@ -14,17 +13,10 @@ public interface Filterable { Function createContainedInstance(); String getFilterKey(); - default List getGenericTags(GenericEvent genericEvent) { + default List getTypeSpecificTags(Class tagClass, GenericEvent genericEvent) { return genericEvent.getTags().stream() - .filter(GenericTag.class::isInstance) - .map(GenericTag.class::cast) - .toList(); - } - - default List getIdentifierTags(GenericEvent genericEvent) { - return genericEvent.getTags().stream() - .filter(IdentifierTag.class::isInstance) - .map(IdentifierTag.class::cast) + .filter(tagClass::isInstance) + .map(tagClass::cast) .toList(); } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java index c1a8f07e1..4e041c20f 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java @@ -3,6 +3,7 @@ import nostr.base.ElementAttribute; import nostr.base.GenericTagQuery; import nostr.event.impl.GenericEvent; +import nostr.event.impl.GenericTag; import java.util.HashSet; import java.util.function.Function; @@ -19,7 +20,7 @@ public GenericTagQueryFilter(T genericTagQuery) { @Override public Predicate getPredicate() { return (genericEvent) -> - getGenericTags(genericEvent).stream() + getTypeSpecificTags(GenericTag.class, genericEvent).stream() .filter(genericTag -> genericTag.getCode().equals(this.genericTagQuery.getTagName())) .anyMatch(genericTag -> diff --git a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java index a0123a575..fb4accf7b 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java @@ -16,8 +16,8 @@ public IdentifierTagFilter(T identifierTag) { @Override public Predicate getPredicate() { return (genericEvent) -> - (getIdentifierTags(genericEvent).stream().anyMatch(genericEventIdentifiterTag -> - genericEventIdentifiterTag.getId().equals(this.identifierTag.getId()))); + getTypeSpecificTags(IdentifierTag.class, genericEvent).stream().anyMatch(genericEventIdentifiterTag -> + genericEventIdentifiterTag.getId().equals(this.identifierTag.getId())); } @Override public T getFilterCriterion() { diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java index e52567fbb..faec29839 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java @@ -1,6 +1,7 @@ package nostr.event.filter; import nostr.event.impl.GenericEvent; +import nostr.event.tag.EventTag; import java.util.function.Function; import java.util.function.Predicate; @@ -16,7 +17,9 @@ public ReferencedEventFilter(T referencedEvent) { @Override public Predicate getPredicate() { return (genericEvent) -> - this.referencedEvent.getId().equals(genericEvent.getId()); + getTypeSpecificTags(EventTag.class, genericEvent).stream() + .anyMatch(eventTag -> + eventTag.getIdEvent().equals(referencedEvent.getId())); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java index dbb030bf8..908d08846 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java @@ -2,6 +2,7 @@ import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; +import nostr.event.tag.PubKeyTag; import java.util.function.Function; import java.util.function.Predicate; @@ -17,7 +18,9 @@ public ReferencedPublicKeyFilter(T publicKey) { @Override public Predicate getPredicate() { return (genericEvent) -> - genericEvent.getPubKey().toHexString().equals(this.publicKey.toHexString()); + getTypeSpecificTags(PubKeyTag.class, genericEvent).stream() + .anyMatch(pubKeyTag -> + pubKeyTag.getPublicKey().toHexString().equals(this.publicKey.toHexString())); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/impl/Filters.java b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java index 1e2f78b6c..386080831 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java @@ -95,7 +95,7 @@ public List getReferencedEvents() { @JsonProperty(ReferencedEventFilter.filterKey) public void setReferencedEvents(@NonNull List events) { - setFilterableListByType(ReferencedEventFilter.filterKey, events, EventFilter::new); + setFilterableListByType(ReferencedEventFilter.filterKey, events, ReferencedEventFilter::new); } @JsonProperty(ReferencedPublicKeyFilter.filterKey) diff --git a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java b/nostr-java-test/src/main/java/nostr/test/EntityFactory.java index 67cfca4d6..bd1bf0422 100644 --- a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java +++ b/nostr-java-test/src/main/java/nostr/test/EntityFactory.java @@ -139,24 +139,24 @@ public static GenericTag createGenericTag(PublicKey publicKey, IEvent event, Int return tag; } - public static Filters createFilters(PublicKey publicKey) { - List eventList = new ArrayList<>(); - eventList.add(createTextNoteEvent(publicKey)); - eventList.add(createEphemeralEvent(publicKey)); - - List refEvents = new ArrayList<>(); - refEvents.add(createTextNoteEvent(publicKey)); - - GenericTagQuery genericTagQuery = createGenericTagQuery(); - return Filters.builder() - .events(eventList) - .referencedEvents(refEvents) - .genericTagQuery( - Map.of( - genericTagQuery.getTagName(), - genericTagQuery.getValue())) - .build(); - } +// public static Filters createFilters(PublicKey publicKey) { +// List eventList = new ArrayList<>(); +// eventList.add(createTextNoteEvent(publicKey)); +// eventList.add(createEphemeralEvent(publicKey)); +// +// List refEvents = new ArrayList<>(); +// refEvents.add(createTextNoteEvent(publicKey)); +// +// GenericTagQuery genericTagQuery = createGenericTagQuery(); +// return Filters.builder() +// .events(eventList) +// .referencedEvents(refEvents) +// .genericTagQuery( +// Map.of( +// genericTagQuery.getTagName(), +// genericTagQuery.getValue())) +// .build(); +// } public static GenericTagQuery createGenericTagQuery() { Character c = generateRamdomAlpha(1).charAt(0); From 279d00ca51afa56f56c051df43a103a6b079561c Mon Sep 17 00:00:00 2001 From: nick avlo Date: Mon, 3 Feb 2025 19:31:26 -0500 Subject: [PATCH 15/45] since until --- .../src/main/java/nostr/event/filter/SinceFilter.java | 2 +- .../src/main/java/nostr/event/filter/UntilFilter.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java index 3a6ca45e2..baff811d7 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java @@ -16,7 +16,7 @@ public SinceFilter(Long since) { @Override public Predicate getPredicate() { return (genericEvent) -> - this.since > genericEvent.getCreatedAt(); + this.since >= genericEvent.getCreatedAt(); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java index 4d438472e..22fa8b42a 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java @@ -16,7 +16,7 @@ public UntilFilter(Long until) { @Override public Predicate getPredicate() { return (genericEvent) -> - this.until <= genericEvent.getCreatedAt(); + this.until < genericEvent.getCreatedAt(); } @Override From c2dfb7414adbbf66ddecf7259d7242689e7c4a53 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Wed, 5 Feb 2025 19:31:18 -0500 Subject: [PATCH 16/45] ongoing, initial tests working --- .../main/java/nostr/base/GenericTagQuery.java | 4 +- .../event/filter/AddressableTagFilter.java | 29 ++-- .../java/nostr/event/filter/EventFilter.java | 9 +- .../java/nostr/event/filter/Filterable.java | 2 +- .../java/nostr/event/filter/FiltersCore.java | 16 +- .../event/filter/GenericTagQueryFilter.java | 12 +- .../event/filter/IdentifierTagFilter.java | 8 +- .../java/nostr/event/filter/KindFilter.java | 5 +- .../nostr/event/filter/PublicKeyFilter.java | 8 +- .../event/filter/ReferencedEventFilter.java | 9 +- .../filter/ReferencedPublicKeyFilter.java | 8 +- .../java/nostr/event/filter/SinceFilter.java | 5 +- .../java/nostr/event/filter/UntilFilter.java | 5 +- .../event/json/codec/FiltersEncoderRxR.java | 40 +++++ .../nostr/event/message/ReqMessageRxR.java | 138 ++++++++++++++++++ .../nostr/test/json/JsonParseRxRTest.java | 61 ++++++++ 16 files changed, 295 insertions(+), 64 deletions(-) create mode 100644 nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderRxR.java create mode 100644 nostr-java-event/src/main/java/nostr/event/message/ReqMessageRxR.java create mode 100644 nostr-java-test/src/test/java/nostr/test/json/JsonParseRxRTest.java diff --git a/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java b/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java index 2b2238806..00d2d780b 100644 --- a/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java +++ b/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java @@ -2,6 +2,7 @@ package nostr.base; import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -13,8 +14,9 @@ */ @Data @NoArgsConstructor +@AllArgsConstructor public class GenericTagQuery { - + private String tagName; private List value; diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java index 3441f69a0..7a5e97756 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java @@ -1,13 +1,13 @@ package nostr.event.filter; import lombok.NonNull; -import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; import nostr.event.tag.AddressTag; import nostr.event.tag.IdentifierTag; -import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class AddressableTagFilter implements Filterable { public final static String filterKey = "#a"; @@ -26,12 +26,12 @@ private boolean compare(@NonNull GenericEvent genericEvent) { return !genericEvent.getPubKey().toHexString().equals( this.addressableTag.getPublicKey().toHexString()) || - !genericEvent.getKind().equals( - this.addressableTag.getKind()) || - getTypeSpecificTags(IdentifierTag.class, genericEvent).stream() - .anyMatch(identifierTag -> - identifierTag.getId().equals( - this.addressableTag.getIdentifierTag().getId())); + !genericEvent.getKind().equals( + this.addressableTag.getKind()) || + getTypeSpecificTags(IdentifierTag.class, genericEvent).stream() + .anyMatch(identifierTag -> + identifierTag.getId().equals( + this.addressableTag.getIdentifierTag().getId())); } @Override @@ -40,11 +40,14 @@ public T getFilterCriterion() { } @Override - public Function createContainedInstance() { - return pubKeyString -> { - PublicKey pubKey = new PublicKey(pubKeyString); - return (T) pubKey; - }; + public String toJson() { + Integer kind = addressableTag.getKind(); + String hexString = addressableTag.getPublicKey().toHexString(); + String id = addressableTag.getIdentifierTag().getId(); + String uri = addressableTag.getRelay().getUri(); + + return Stream.of(kind, hexString, id, uri).map(Object::toString) + .collect(Collectors.joining(",")); } @Override public String getFilterKey() { diff --git a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java index 4dd2c28ab..29ac31ef3 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java @@ -1,5 +1,6 @@ package nostr.event.filter; +import com.fasterxml.jackson.databind.JsonNode; import nostr.event.impl.GenericEvent; import java.util.function.Function; @@ -25,12 +26,8 @@ public T getFilterCriterion() { } @Override - public Function createContainedInstance() { - return eventId -> { - GenericEvent event = new GenericEvent(); - event.setId(eventId); - return (T) event; - }; + public String toJson() { + return event.getId(); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java index 17659f546..51cbbef30 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java @@ -10,7 +10,7 @@ public interface Filterable { Predicate getPredicate(); T getFilterCriterion(); - Function createContainedInstance(); + String toJson(); String getFilterKey(); default List getTypeSpecificTags(Class tagClass, GenericEvent genericEvent) { diff --git a/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java b/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java index c04ffab77..22dad052e 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java @@ -3,19 +3,30 @@ import lombok.Getter; import lombok.NonNull; import lombok.Setter; +import nostr.base.annotation.Key; import java.util.HashMap; import java.util.List; import java.util.Map; @Getter +@Setter public class FiltersCore { - private final Map> filtersMap = new HashMap<>(); + @Key + private final Map> filtersMap; -// TODO: make limit configurable + // TODO: make limit configurable @Setter private Integer limit = 10; + public FiltersCore() { + filtersMap = new HashMap<>(); + } + + public FiltersCore(Map> filtersMap) { + this.filtersMap = filtersMap; + } + public void addFilterable(@NonNull String key, @NonNull Filterable filterable) { addFilterable(key, List.of(filterable)); } @@ -28,3 +39,4 @@ public List getFilterableByType(@NonNull String type) { return filtersMap.get(type); } } + diff --git a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java index 4e041c20f..0c36ce927 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java @@ -6,8 +6,8 @@ import nostr.event.impl.GenericTag; import java.util.HashSet; -import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Collectors; public class GenericTagQueryFilter implements Filterable { public final static String filterKey = "undefined"; @@ -37,12 +37,10 @@ public T getFilterCriterion() { } @Override - public Function createContainedInstance() { - return tagName -> { - GenericTagQuery tagQuery = new GenericTagQuery(); - tagQuery.setTagName(tagName); - return (T) tagQuery; - }; + public String toJson() { + return genericTagQuery.getValue().stream().map(s -> + String.format("\"%s\"", s)) + .collect(Collectors.joining(",")); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java index fb4accf7b..6dd31693f 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java @@ -3,7 +3,6 @@ import nostr.event.impl.GenericEvent; import nostr.event.tag.IdentifierTag; -import java.util.function.Function; import java.util.function.Predicate; public class IdentifierTagFilter implements Filterable { @@ -25,11 +24,8 @@ public T getFilterCriterion() { } @Override - public Function createContainedInstance() { - return identifier -> { - IdentifierTag identiferTag = new IdentifierTag(identifier); - return (T) identiferTag; - }; + public String toJson() { + return identifierTag.getId(); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java index 8c83e6c7b..ead17803f 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java @@ -3,7 +3,6 @@ import nostr.event.Kind; import nostr.event.impl.GenericEvent; -import java.util.function.Function; import java.util.function.Predicate; public class KindFilter implements Filterable { @@ -26,8 +25,8 @@ public T getFilterCriterion() { } @Override - public Function createContainedInstance() { - return pubkey -> (T) Kind.valueOf(pubkey); + public String toJson() { + return kind.toString(); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java index 1c1594687..186779488 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java @@ -3,7 +3,6 @@ import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; -import java.util.function.Function; import java.util.function.Predicate; public class PublicKeyFilter implements Filterable { @@ -24,11 +23,8 @@ public T getFilterCriterion() { } @Override - public Function createContainedInstance() { - return pubKeyString -> { - PublicKey pubKey = new PublicKey(pubKeyString); - return (T) pubKey; - }; + public String toJson() { + return publicKey.toHexString(); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java index faec29839..9706a434c 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java @@ -3,7 +3,6 @@ import nostr.event.impl.GenericEvent; import nostr.event.tag.EventTag; -import java.util.function.Function; import java.util.function.Predicate; public class ReferencedEventFilter implements Filterable { @@ -28,12 +27,8 @@ public T getFilterCriterion() { } @Override - public Function createContainedInstance() { - return eventId -> { - GenericEvent event = new GenericEvent(); - event.setId(eventId); - return (T) event; - }; + public String toJson() { + return referencedEvent.getId(); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java index 908d08846..25fd02311 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java @@ -4,7 +4,6 @@ import nostr.event.impl.GenericEvent; import nostr.event.tag.PubKeyTag; -import java.util.function.Function; import java.util.function.Predicate; public class ReferencedPublicKeyFilter implements Filterable { @@ -29,11 +28,8 @@ public T getFilterCriterion() { } @Override - public Function createContainedInstance() { - return pubKeyString -> { - PublicKey pubKey = new PublicKey(pubKeyString); - return (T) pubKey; - }; + public String toJson() { + return publicKey.toHexString(); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java index baff811d7..f3b019c0a 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java @@ -2,7 +2,6 @@ import nostr.event.impl.GenericEvent; -import java.util.function.Function; import java.util.function.Predicate; public class SinceFilter implements Filterable { @@ -25,8 +24,8 @@ public Long getFilterCriterion() { } @Override - public Function createContainedInstance() { - return longValue -> Long.valueOf(longValue); + public String toJson() { + return since.toString(); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java index 22fa8b42a..70ac7ad32 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java @@ -2,7 +2,6 @@ import nostr.event.impl.GenericEvent; -import java.util.function.Function; import java.util.function.Predicate; public class UntilFilter implements Filterable { @@ -25,8 +24,8 @@ public Long getFilterCriterion() { } @Override - public Function createContainedInstance() { - return longValue -> Long.valueOf(longValue); + public String toJson() { + return until.toString(); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderRxR.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderRxR.java new file mode 100644 index 000000000..206d91861 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderRxR.java @@ -0,0 +1,40 @@ +package nostr.event.json.codec; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.SneakyThrows; +import nostr.base.FEncoder; +import nostr.event.filter.Filterable; +import nostr.event.filter.FiltersCore; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author guilhermegps + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class FiltersEncoderRxR implements FEncoder { + private final FiltersCore filtersCore; + + public FiltersEncoderRxR(FiltersCore filtersCore) { + this.filtersCore = filtersCore; + } + + @SneakyThrows + @Override + public String encode() { + Map> result = new HashMap<>(); + filtersCore.getFiltersMap().forEach((key, value) -> + result.put( + key, + value.stream().map( + Filterable::toJson).collect(Collectors.toList()))); + JsonNode jsonNode = MAPPER.valueToTree(result); + return MAPPER.writeValueAsString(jsonNode); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/message/ReqMessageRxR.java b/nostr-java-event/src/main/java/nostr/event/message/ReqMessageRxR.java new file mode 100644 index 000000000..474db934c --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/message/ReqMessageRxR.java @@ -0,0 +1,138 @@ +package nostr.event.message; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.SneakyThrows; +import lombok.ToString; +import nostr.base.Command; +import nostr.base.GenericTagQuery; +import nostr.base.IEncoder; +import nostr.base.PublicKey; +import nostr.event.BaseMessage; +import nostr.event.Kind; +import nostr.event.filter.EventFilter; +import nostr.event.filter.Filterable; +import nostr.event.filter.FiltersCore; +import nostr.event.filter.GenericTagQueryFilter; +import nostr.event.filter.KindFilter; +import nostr.event.filter.PublicKeyFilter; +import nostr.event.filter.ReferencedEventFilter; +import nostr.event.filter.ReferencedPublicKeyFilter; +import nostr.event.filter.SinceFilter; +import nostr.event.filter.UntilFilter; +import nostr.event.impl.GenericEvent; +import nostr.event.json.codec.FiltersEncoderRxR; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.StreamSupport; + +/** + * @author squirrel + */ +@Getter +@EqualsAndHashCode(callSuper = false) +@ToString(callSuper = true) +public class ReqMessageRxR extends BaseMessage { + private final static ObjectMapper mapper = new ObjectMapper(); + + @JsonProperty + private final String subscriptionId; + + @JsonProperty + private final FiltersCore filtersCore; + + public ReqMessageRxR(String subscriptionId, Map> filterPlugins) { + super(Command.REQ.name()); + this.filtersCore = new FiltersCore(filterPlugins); + this.subscriptionId = subscriptionId; + } + + @Override + public String encode() throws JsonProcessingException { + getArrayNode() + .add(getCommand()) + .add(getSubscriptionId()); + + try { + FiltersEncoderRxR filtersEncoder = new FiltersEncoderRxR(filtersCore); + var filterNode = IEncoder.MAPPER.readTree(filtersEncoder.encode()); + getArrayNode().add(filterNode); + } catch (Exception e) { + throw new RuntimeException(e); + } + return IEncoder.MAPPER.writeValueAsString(getArrayNode()); + } + + @SneakyThrows + public static T decode(@NonNull Object subscriptionId, @NonNull String jsonString) { + final Map> filterPluginsMap = new HashMap<>(); + mapper.readTree(jsonString).fields().forEachRemaining(field -> + filterPluginsMap.put( + field.getKey(), + getFilterable( + field.getKey(), + field.getValue()))); + +// TODO: variant 1 stream generating map, almost working +// Iterator elements = mapper.readTree(jsonString).elements(); +// List>> list = Stream.of(elements).map(jsonNodeIterator -> +// { +// JsonNode next = jsonNodeIterator.next(); +// Iterator> fields1 = next.fields(); +// return fields1; +// }).map(entryIterator -> +// Map.of( +// entryIterator.next().getKey(), +// getFilterable( +// entryIterator.next().getKey(), +// entryIterator.next().getValue()))) +// .toList(); + +// TODO: variant 2 stream generating map, left as potentially helpful for variant 1 +// JsonNode jsonNode = mapper.readTree(jsonString); +// Iterator> fields = jsonNode.fields(); +// List>> kindOnly = Stream.of(fields) +// .map(entryIterator -> +// Map.of(entryIterator.next().getKey(), +// getFilterable( +// entryIterator.next().getKey(), +// entryIterator.next().getValue()))).toList(); + + return (T) new ReqMessageRxR(subscriptionId.toString(), filterPluginsMap); + } + +// TODO: below functionals can/should be refactored into their associated filter classes + static List getFilterable(String type, JsonNode node) { + return switch (type) { + case ReferencedPublicKeyFilter.filterKey -> + getFilterable(node, referencedPubKey -> new ReferencedPublicKeyFilter<>(new PublicKey(referencedPubKey.asText()))); + case ReferencedEventFilter.filterKey -> + getFilterable(node, referencedEvent -> new ReferencedEventFilter<>(new GenericEvent(referencedEvent.asText()))); + case PublicKeyFilter.filterKey -> + getFilterable(node, author -> new PublicKeyFilter<>(new PublicKey(author.asText()))); + case EventFilter.filterKey -> getFilterable(node, event -> new EventFilter<>(new GenericEvent(event.asText()))); + case SinceFilter.filterKey -> getFilterable(node, since -> new SinceFilter(since.asLong())); + case UntilFilter.filterKey -> getFilterable(node, until -> new UntilFilter(until.asLong())); + case KindFilter.filterKey -> getFilterable(node, kindNode -> new KindFilter<>(Kind.valueOf(kindNode.asInt()))); +// case AddressableTagFilter.filterKey -> new XYZ<>(getGenericTagQuery(node)); +// case IdentifierTagFilter.filterKey -> new XYZ<>(getGenericTagQuery(node)); + default -> getFilterable(node, kindNode -> + new GenericTagQueryFilter<>( + new GenericTagQuery( + type, + StreamSupport.stream(node.spliterator(), false).map(JsonNode::asText).toList()))); + }; + } + + private static List getFilterable(JsonNode jsonNode, Function filterFunction) { + return StreamSupport.stream(jsonNode.spliterator(), false).map(filterFunction).toList(); + } +} diff --git a/nostr-java-test/src/test/java/nostr/test/json/JsonParseRxRTest.java b/nostr-java-test/src/test/java/nostr/test/json/JsonParseRxRTest.java new file mode 100644 index 000000000..e8b300dd5 --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/json/JsonParseRxRTest.java @@ -0,0 +1,61 @@ +package nostr.test.json; + +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.extern.java.Log; +import nostr.base.PublicKey; +import nostr.event.Kind; +import nostr.event.filter.FiltersCore; +import nostr.event.filter.KindFilter; +import nostr.event.filter.PublicKeyFilter; +import nostr.event.filter.ReferencedEventFilter; +import nostr.event.impl.GenericEvent; +import nostr.event.json.codec.BaseMessageDecoder; +import nostr.event.message.ReqMessageRxR; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Log +public class JsonParseRxRTest { + @Test + public void testReqMessagePopulatedListOfFiltersListDecoder() throws JsonProcessingException { + log.info("testReqMessagePopulatedListOfFiltersListDecoder"); + + String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; + String kind = "1"; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String geohashKey = "#g"; + String geohashValue1 = "2vghde"; + String geohashValue2 = "3abcde"; + String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; + String uuidKey = "#d"; + String uuidValue1 = "UUID-1"; + String uuidValue2 = "UUID-2"; + String reqJsonWithCustomTagQueryFilterToDecode = + "[\"REQ\", " + + "\"" + subscriptionId + "\", " + + "{\"kinds\": [" + kind + "], " + + "\"authors\": [\"" + author + "\"]," + +// "\"" + geohashKey + "\": [\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]," + +// "\"" + uuidKey + "\": [\"" + uuidValue1 + "\",\"" + uuidValue2 + "\"]," + + "\"#e\": [\"" + referencedEventId + "\"]}]"; + + ReqMessageRxR decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); + + FiltersCore expectedFilters = new FiltersCore(); + expectedFilters.addFilterable(KindFilter.filterKey, List.of(new KindFilter<>(Kind.TEXT_NOTE))); + expectedFilters.addFilterable(PublicKeyFilter.filterKey, List.of(new PublicKeyFilter<>(new PublicKey(author)))); + expectedFilters.addFilterable(ReferencedEventFilter.filterKey, List.of(new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); +// List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); +// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); +// List expectedIdentityTagValuesList = List.of(uuidValue1, uuidValue2); +// expectedFilters.setGenericTagQuery(uuidKey, expectedIdentityTagValuesList); + ReqMessageRxR expectedReqMessage = new ReqMessageRxR(subscriptionId, expectedFilters.getFiltersMap()); + String expected = expectedReqMessage.encode(); + String actual = decodedReqMessage.encode(); + assertEquals(expected, actual); + assertEquals(expectedReqMessage, decodedReqMessage); + } +} From 0ea81170c938029aef64b44b8f092358ea9a163c Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sat, 15 Feb 2025 16:09:44 -0800 Subject: [PATCH 17/45] ongoing filters --- .../src/main/java/nostr/api/EventNostr.java | 2 +- .../src/main/java/nostr/api/NIP01.java | 35 +- .../src/main/java/nostr/api/NostrIF.java | 2 +- .../nostr/api/NostrSpringWebSocketClient.java | 2 +- .../nostr/api/factory/impl/NIP01Impl.java | 2 +- .../main/java/nostr/base/GenericTagQuery.java | 18 +- .../src/main/java/nostr/event/Kind.java | 10 +- ...PublicKeyFilter.java => AuthorFilter.java} | 13 +- .../java/nostr/event/filter/EventFilter.java | 9 +- .../java/nostr/event/filter/Filterable.java | 11 +- .../event/filter/FilterableProvider.java | 21 +- .../java/nostr/event/filter/FiltersCore.java | 42 - .../event/filter/GenericTagQueryFilter.java | 15 +- .../java/nostr/event/filter/KindFilter.java | 7 +- .../event/filter/ReferencedEventFilter.java | 7 +- .../filter/ReferencedPublicKeyFilter.java | 17 +- .../java/nostr/event/filter/SinceFilter.java | 7 +- .../java/nostr/event/filter/UntilFilter.java | 7 +- .../main/java/nostr/event/impl/Filters.java | 296 ----- .../event/json/codec/BaseMessageDecoder.java | 53 +- .../event/json/codec/FiltersDecoder.java | 2 +- .../event/json/codec/FiltersEncoder.java | 79 +- .../event/json/codec/FiltersEncoderOld.java | 69 ++ .../event/json/codec/FiltersEncoderRxR.java | 40 - .../event/json/codec/FiltersListEncoder.java | 115 +- .../java/nostr/event/message/ReqMessage.java | 118 +- .../nostr/event/message/ReqMessageRxR.java | 138 --- .../java/nostr/examples/NostrApiExamples.java | 1 - .../main/java/nostr/test/EntityFactory.java | 6 +- .../java/nostr/test/event/ApiEventTest.java | 3 - .../nostr/test/event/filter/FiltersTest.java | 511 ++++---- .../nostr/test/json/JsonParseRxRTest.java | 61 - .../java/nostr/test/json/JsonParseTest.java | 1092 +++++++---------- 33 files changed, 1104 insertions(+), 1707 deletions(-) rename nostr-java-event/src/main/java/nostr/event/filter/{PublicKeyFilter.java => AuthorFilter.java} (65%) delete mode 100644 nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java delete mode 100644 nostr-java-event/src/main/java/nostr/event/impl/Filters.java create mode 100644 nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderOld.java delete mode 100644 nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderRxR.java delete mode 100644 nostr-java-event/src/main/java/nostr/event/message/ReqMessageRxR.java delete mode 100644 nostr-java-test/src/test/java/nostr/test/json/JsonParseRxRTest.java diff --git a/nostr-java-api/src/main/java/nostr/api/EventNostr.java b/nostr-java-api/src/main/java/nostr/api/EventNostr.java index 53d91db10..8a67fa3ca 100644 --- a/nostr-java-api/src/main/java/nostr/api/EventNostr.java +++ b/nostr-java-api/src/main/java/nostr/api/EventNostr.java @@ -49,7 +49,7 @@ public U send() { @SuppressWarnings("unchecked") public U send(Map relays) { List messages = super.send(this.event, relays); - BaseMessageDecoder decoder = new BaseMessageDecoder(); + BaseMessageDecoder decoder = new BaseMessageDecoder<>(); return messages.stream() .map(msg -> (U) decoder.decode(msg)) diff --git a/nostr-java-api/src/main/java/nostr/api/NIP01.java b/nostr-java-api/src/main/java/nostr/api/NIP01.java index c91ccf304..533415028 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP01.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP01.java @@ -28,7 +28,7 @@ import nostr.event.Kind; import nostr.event.Marker; import nostr.event.NIP01Event; -import nostr.event.impl.Filters; +import nostr.event.filter.Filters; import nostr.event.impl.GenericEvent; import nostr.event.message.CloseMessage; import nostr.event.message.EoseMessage; @@ -98,7 +98,7 @@ public NIP01 createMetadataEvent(@NonNull UserProfile profile) { /** * Create a replaceable event - * + * * @param kind the kind (10000 <= kind < 20000 || kind == 0 || kind == 3) * @param content the content */ @@ -111,7 +111,7 @@ public NIP01 createReplaceableEvent(@NonNull Integer kind, String content) { /** * Create a replaceable event - * + * * @param tags the note's tags * @param kind the kind (10000 <= kind < 20000 || kind == 0 || kind == 3) * @param content the note's content @@ -125,7 +125,7 @@ public NIP01 createReplaceableEvent(@NonNull List tags, @NonNull Int /** * Create an ephemeral event - * + * * @param kind the kind (20000 <= n < 30000) * @param content the note's content */ @@ -217,19 +217,20 @@ public static PubKeyTag createPubKeyTag(@NonNull PublicKey publicKey, String mai public static Filters createFilters(List events, List authors, List kinds, List referencedEvents, List referencePubKeys, Long since, Long until, Integer limit, GenericTagQuery genericTagQuery) { - return Filters.builder() - .authors(authors) - .events(events) - .genericTagQuery( - Map.of( - genericTagQuery.getTagName(), - genericTagQuery.getValue())) - .kinds(kinds).limit(limit) - .referencePubKeys(referencePubKeys) - .referencedEvents(referencedEvents) - .since(since) - .until(until) - .build(); +// return Filters.builder() +// .authors(authors) +// .events(events) +// .genericTagQuery( +// Map.of( +// genericTagQuery.getTagName(), +// genericTagQuery.getValue())) +// .kinds(kinds).limit(limit) +// .referencePubKeys(referencePubKeys) +// .referencedEvents(referencedEvents) +// .since(since) +// .until(until) +// .build(); + return null; } /** diff --git a/nostr-java-api/src/main/java/nostr/api/NostrIF.java b/nostr-java-api/src/main/java/nostr/api/NostrIF.java index 0d920f7c9..89450edfd 100644 --- a/nostr-java-api/src/main/java/nostr/api/NostrIF.java +++ b/nostr-java-api/src/main/java/nostr/api/NostrIF.java @@ -5,7 +5,7 @@ import nostr.base.ISignable; import nostr.context.RequestContext; import nostr.event.BaseMessage; -import nostr.event.impl.Filters; +import nostr.event.filter.Filters; import nostr.event.impl.GenericEvent; import nostr.id.Identity; diff --git a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java index 7bd08234b..d49e23b9b 100644 --- a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java +++ b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java @@ -8,7 +8,7 @@ import nostr.context.RequestContext; import nostr.crypto.schnorr.Schnorr; import nostr.event.BaseMessage; -import nostr.event.impl.Filters; +import nostr.event.filter.Filters; import nostr.event.impl.GenericEvent; import nostr.id.Identity; import nostr.util.NostrUtil; diff --git a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP01Impl.java b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP01Impl.java index 94e6211fc..98e9a679b 100644 --- a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP01Impl.java +++ b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP01Impl.java @@ -17,8 +17,8 @@ import nostr.base.UserProfile; import nostr.event.BaseTag; import nostr.event.Marker; +import nostr.event.filter.Filters; import nostr.event.impl.EphemeralEvent; -import nostr.event.impl.Filters; import nostr.event.impl.MetadataEvent; import nostr.event.impl.ParameterizedReplaceableEvent; import nostr.event.impl.ReplaceableEvent; diff --git a/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java b/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java index 00d2d780b..89bccb35e 100644 --- a/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java +++ b/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java @@ -2,14 +2,12 @@ package nostr.base; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import java.util.List; - /** - * * @author squirrel */ @Data @@ -17,11 +15,13 @@ @AllArgsConstructor public class GenericTagQuery { - private String tagName; - private List value; + private String tagName; + + @JsonProperty + private String value; - @JsonIgnore - public Integer getNip() { - return 1; - } + @JsonIgnore + public Integer getNip() { + return 1; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/Kind.java b/nostr-java-event/src/main/java/nostr/event/Kind.java index f51c15658..f1fdc0215 100644 --- a/nostr-java-event/src/main/java/nostr/event/Kind.java +++ b/nostr-java-event/src/main/java/nostr/event/Kind.java @@ -5,6 +5,8 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import java.time.temporal.ValueRange; + /** * * @author squirrel @@ -27,6 +29,8 @@ public enum Kind { MUTE_USER(44, "mute_user"), ENCRYPTED_PAYLOADS(44, "encrypted_payloads"), OTS_EVENT(1040, "ots_event"), + WALLET_UNSPENT_PROOF(7_375, "wallet_unspent_proof"), + WALLET_TX_HISTORY(7_376, "wallet_tx_history"), ZAP_REQUEST(9734, "zap_request"), ZAP_RECEIPT(9735, "zap_receipt"), REPLACEABLE_EVENT(10_000, "replaceable_event"), @@ -38,9 +42,8 @@ public enum Kind { CLASSIFIED_LISTING_DRAFT(30_403, "classified_listing_draft"), CALENDAR_DATE_BASED_EVENT(31_922, "calendar_date_based_event"), CALENDAR_TIME_BASED_EVENT(31_923, "calendar_time_based_event"), + CALENDAR_RSVP_EVENT(31_925, "calendar_rsvp_event"), WALLET(37_375, "wallet"), - WALLET_UNSPENT_PROOF(7_375, "wallet_unspent_proof"), - WALLET_TX_HISTORY(7_376, "wallet_tx_history"), UNDEFINED(-1, "undefined"); @JsonValue @@ -50,6 +53,9 @@ public enum Kind { @JsonCreator public static Kind valueOf(int value) { + if (!ValueRange.of(0, 65535).isValidIntValue(value)) { + throw new IllegalArgumentException(String.format("Kind must be between 0 and 65535 but was [%d]", value)); + } for (Kind k : values()) { if (k.getValue() == value) { return k; diff --git a/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java similarity index 65% rename from nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java rename to nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java index 186779488..7a0e090f2 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/PublicKeyFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java @@ -1,30 +1,35 @@ package nostr.event.filter; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; import java.util.function.Predicate; -public class PublicKeyFilter implements Filterable { +@EqualsAndHashCode +public class AuthorFilter implements Filterable { public final static String filterKey = "authors"; private final T publicKey; - public PublicKeyFilter(T publicKey) { + public AuthorFilter(T publicKey) { this.publicKey = publicKey; } + @Override public Predicate getPredicate() { return (genericEvent) -> this.publicKey.toHexString().equals(genericEvent.getPubKey().toHexString()); } + @Override public T getFilterCriterion() { return publicKey; } @Override - public String toJson() { - return publicKey.toHexString(); + public ArrayNode toArrayNode() { + return mapper.createArrayNode().add(publicKey.toHexString()); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java index 29ac31ef3..08edc7a07 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java @@ -1,11 +1,12 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; -import java.util.function.Function; import java.util.function.Predicate; +@EqualsAndHashCode public class EventFilter implements Filterable { public final static String filterKey = "ids"; private final T event; @@ -26,8 +27,8 @@ public T getFilterCriterion() { } @Override - public String toJson() { - return event.getId(); + public ArrayNode toArrayNode() { + return mapper.createArrayNode().add(event.getId()); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java index 51cbbef30..532f242ca 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java @@ -1,20 +1,23 @@ package nostr.event.filter; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import nostr.event.BaseTag; import nostr.event.impl.GenericEvent; import java.util.List; -import java.util.function.Function; import java.util.function.Predicate; public interface Filterable { + ObjectMapper mapper = new ObjectMapper(); + Predicate getPredicate(); T getFilterCriterion(); - String toJson(); + ArrayNode toArrayNode(); String getFilterKey(); - default List getTypeSpecificTags(Class tagClass, GenericEvent genericEvent) { - return genericEvent.getTags().stream() + default List getTypeSpecificTags(Class tagClass, GenericEvent event) { + return event.getTags().stream() .filter(tagClass::isInstance) .map(tagClass::cast) .toList(); diff --git a/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java b/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java index de84beebd..52f2eba22 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java @@ -7,28 +7,21 @@ import nostr.event.impl.GenericEvent; import java.util.List; -import java.util.stream.StreamSupport; public class FilterableProvider { public static List getFilterable(String type, JsonNode node) { return switch (type) { - case ReferencedPublicKeyFilter.filterKey -> - Filters.getFilterable(node, referencedPubKey -> new ReferencedPublicKeyFilter(new PublicKey(referencedPubKey.asText()))); - case ReferencedEventFilter.filterKey -> - Filters.getFilterable(node, referencedEvent -> new ReferencedEventFilter(new GenericEvent(referencedEvent.asText()))); - case PublicKeyFilter.filterKey -> - Filters.getFilterable(node, author -> new PublicKeyFilter(new PublicKey(author.asText()))); - case EventFilter.filterKey -> Filters.getFilterable(node, event -> new EventFilter(new GenericEvent(event.asText()))); + case ReferencedPublicKeyFilter.filterKey -> Filters.getFilterable(node, referencedPubKey -> new ReferencedPublicKeyFilter<>(new PublicKey(referencedPubKey.asText()))); + case ReferencedEventFilter.filterKey -> Filters.getFilterable(node, referencedEvent -> new ReferencedEventFilter<>(new GenericEvent(referencedEvent.asText()))); + case AuthorFilter.filterKey -> Filters.getFilterable(node, author -> new AuthorFilter<>(new PublicKey(author.asText()))); + case EventFilter.filterKey -> Filters.getFilterable(node, event -> new EventFilter<>(new GenericEvent(event.asText()))); case SinceFilter.filterKey -> Filters.getFilterable(node, since -> new SinceFilter(since.asLong())); case UntilFilter.filterKey -> Filters.getFilterable(node, until -> new UntilFilter(until.asLong())); - case KindFilter.filterKey -> Filters.getFilterable(node, kindNode -> new KindFilter(Kind.valueOf(kindNode.asInt()))); + case KindFilter.filterKey -> Filters.getFilterable(node, kindNode -> new KindFilter<>(Kind.valueOf(kindNode.asInt()))); +// TODO: complete & test below // case AddressableTagFilter.filterKey -> new XYZ<>(getGenericTagQuery(node)); // case IdentifierTagFilter.filterKey -> new XYZ<>(getGenericTagQuery(node)); - default -> Filters.getFilterable(node, kindNode -> - new GenericTagQueryFilter( - new GenericTagQuery( - type, - StreamSupport.stream(node.spliterator(), false).map(JsonNode::asText).toList()))); + default -> Filters.getFilterable(node, genericNode -> new GenericTagQueryFilter<>(new GenericTagQuery(type, genericNode.asText()))); }; } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java b/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java deleted file mode 100644 index 22dad052e..000000000 --- a/nostr-java-event/src/main/java/nostr/event/filter/FiltersCore.java +++ /dev/null @@ -1,42 +0,0 @@ -package nostr.event.filter; - -import lombok.Getter; -import lombok.NonNull; -import lombok.Setter; -import nostr.base.annotation.Key; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Getter -@Setter -public class FiltersCore { - @Key - private final Map> filtersMap; - - // TODO: make limit configurable - @Setter - private Integer limit = 10; - - public FiltersCore() { - filtersMap = new HashMap<>(); - } - - public FiltersCore(Map> filtersMap) { - this.filtersMap = filtersMap; - } - - public void addFilterable(@NonNull String key, @NonNull Filterable filterable) { - addFilterable(key, List.of(filterable)); - } - - public void addFilterable(@NonNull String key, @NonNull List filterable) { - filtersMap.put(key, filterable); - } - - public List getFilterableByType(@NonNull String type) { - return filtersMap.get(type); - } -} - diff --git a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java index 0c36ce927..5986cd39e 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java @@ -1,5 +1,7 @@ package nostr.event.filter; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; import nostr.base.ElementAttribute; import nostr.base.GenericTagQuery; import nostr.event.impl.GenericEvent; @@ -7,10 +9,9 @@ import java.util.HashSet; import java.util.function.Predicate; -import java.util.stream.Collectors; +@EqualsAndHashCode public class GenericTagQueryFilter implements Filterable { - public final static String filterKey = "undefined"; private final T genericTagQuery; public GenericTagQueryFilter(T genericTagQuery) { @@ -27,7 +28,7 @@ public Predicate getPredicate() { new HashSet<>(genericTag .getAttributes().stream().map( ElementAttribute::getValue).toList()) - .containsAll( + .contains( this.genericTagQuery.getValue())); } @@ -37,14 +38,12 @@ public T getFilterCriterion() { } @Override - public String toJson() { - return genericTagQuery.getValue().stream().map(s -> - String.format("\"%s\"", s)) - .collect(Collectors.joining(",")); + public ArrayNode toArrayNode() { + return mapper.createArrayNode().add(genericTagQuery.getValue()); } @Override public String getFilterKey() { - return filterKey; + return genericTagQuery.getTagName(); } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java index ead17803f..e31b404db 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java @@ -1,10 +1,13 @@ package nostr.event.filter; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; import nostr.event.Kind; import nostr.event.impl.GenericEvent; import java.util.function.Predicate; +@EqualsAndHashCode public class KindFilter implements Filterable { public final static String filterKey = "kinds"; private final T kind; @@ -25,8 +28,8 @@ public T getFilterCriterion() { } @Override - public String toJson() { - return kind.toString(); + public ArrayNode toArrayNode() { + return mapper.createArrayNode().add(kind.toString()); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java index 9706a434c..ee2c2b9e5 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java @@ -1,10 +1,13 @@ package nostr.event.filter; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; import nostr.event.tag.EventTag; import java.util.function.Predicate; +@EqualsAndHashCode public class ReferencedEventFilter implements Filterable { public final static String filterKey = "#e"; private final T referencedEvent; @@ -27,8 +30,8 @@ public T getFilterCriterion() { } @Override - public String toJson() { - return referencedEvent.getId(); + public ArrayNode toArrayNode() { + return mapper.createArrayNode().add(referencedEvent.getId()); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java index 25fd02311..7b2d99f8b 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java @@ -1,17 +1,20 @@ package nostr.event.filter; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; import nostr.event.tag.PubKeyTag; import java.util.function.Predicate; +@EqualsAndHashCode public class ReferencedPublicKeyFilter implements Filterable { public final static String filterKey = "#p"; - private final T publicKey; + private final T referencedPublicKey; - public ReferencedPublicKeyFilter(T publicKey) { - this.publicKey = publicKey; + public ReferencedPublicKeyFilter(T referencedPublicKey) { + this.referencedPublicKey = referencedPublicKey; } @Override @@ -19,17 +22,17 @@ public Predicate getPredicate() { return (genericEvent) -> getTypeSpecificTags(PubKeyTag.class, genericEvent).stream() .anyMatch(pubKeyTag -> - pubKeyTag.getPublicKey().toHexString().equals(this.publicKey.toHexString())); + pubKeyTag.getPublicKey().toHexString().equals(this.referencedPublicKey.toHexString())); } @Override public T getFilterCriterion() { - return publicKey; + return referencedPublicKey; } @Override - public String toJson() { - return publicKey.toHexString(); + public ArrayNode toArrayNode() { + return mapper.createArrayNode().add(referencedPublicKey.toHexString()); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java index f3b019c0a..b84460030 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java @@ -1,9 +1,12 @@ package nostr.event.filter; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; import java.util.function.Predicate; +@EqualsAndHashCode public class SinceFilter implements Filterable { public final static String filterKey = "since"; private final Long since; @@ -24,8 +27,8 @@ public Long getFilterCriterion() { } @Override - public String toJson() { - return since.toString(); + public ArrayNode toArrayNode() { + return mapper.createArrayNode().add(since); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java index 70ac7ad32..ddfe2af06 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java @@ -1,9 +1,12 @@ package nostr.event.filter; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; import java.util.function.Predicate; +@EqualsAndHashCode public class UntilFilter implements Filterable { public final static String filterKey = "until"; private final Long until; @@ -24,8 +27,8 @@ public Long getFilterCriterion() { } @Override - public String toJson() { - return until.toString(); + public ArrayNode toArrayNode() { + return mapper.createArrayNode().add(until); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/impl/Filters.java b/nostr-java-event/src/main/java/nostr/event/impl/Filters.java deleted file mode 100644 index 386080831..000000000 --- a/nostr-java-event/src/main/java/nostr/event/impl/Filters.java +++ /dev/null @@ -1,296 +0,0 @@ -package nostr.event.impl; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.NonNull; -import lombok.Setter; -import lombok.SneakyThrows; -import nostr.base.GenericTagQuery; -import nostr.base.PublicKey; -import nostr.base.annotation.Key; -import nostr.event.Kind; -import nostr.event.filter.AddressableTagFilter; -import nostr.event.filter.EventFilter; -import nostr.event.filter.Filterable; -import nostr.event.filter.FiltersCore; -import nostr.event.filter.GenericTagQueryFilter; -import nostr.event.filter.IdentifierTagFilter; -import nostr.event.filter.KindFilter; -import nostr.event.filter.PublicKeyFilter; -import nostr.event.filter.ReferencedEventFilter; -import nostr.event.filter.ReferencedPublicKeyFilter; -import nostr.event.filter.SinceFilter; -import nostr.event.filter.UntilFilter; -import nostr.event.tag.AddressTag; -import nostr.event.tag.IdentifierTag; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; - -/** - * @author squirrel - */ -@EqualsAndHashCode(callSuper = false) -@AllArgsConstructor -public class Filters { - @JsonIgnore - private final FiltersCore core; - @Key - private Integer limit; - - public Filters() { - this.core = new FiltersCore(); - } - - public FiltersCore getFiltersCore() { - return this.core; - } - - @JsonProperty(EventFilter.filterKey) - public List getEvents() { - return getFilterableListByType(EventFilter.filterKey); - } - - @JsonProperty(EventFilter.filterKey) - public void setEvents(@NonNull List events) { - setFilterableListByType(EventFilter.filterKey, events, EventFilter::new); - } - - @JsonProperty(PublicKeyFilter.filterKey) - public List getAuthors() { -// TODO: may possibly require below variant -// getFilterableByType(ReferencedPublicKeyFilter.filterKey, PublicKeyFilter.class); - return getFilterableListByType(PublicKeyFilter.filterKey); - } - - @JsonProperty(PublicKeyFilter.filterKey) - public void setAuthors(@NonNull List authors) { - setFilterableListByType(PublicKeyFilter.filterKey, authors, PublicKeyFilter::new); - } - - @JsonProperty(KindFilter.filterKey) - public List getKinds() { - return getFilterableListByType(KindFilter.filterKey); - } - - @JsonProperty(KindFilter.filterKey) - public void setKinds(@NonNull List kinds) { - setFilterableListByType(KindFilter.filterKey, kinds, KindFilter::new); - } - - @JsonProperty(ReferencedEventFilter.filterKey) - public List getReferencedEvents() { - return getFilterableListByType(ReferencedEventFilter.filterKey); - } - - @JsonProperty(ReferencedEventFilter.filterKey) - public void setReferencedEvents(@NonNull List events) { - setFilterableListByType(ReferencedEventFilter.filterKey, events, ReferencedEventFilter::new); - } - - @JsonProperty(ReferencedPublicKeyFilter.filterKey) - public List getReferencePubKeys() { - return getFilterableListByType(ReferencedPublicKeyFilter.filterKey); - } - - @JsonProperty(ReferencedPublicKeyFilter.filterKey) - public void setReferencePubKeys(@NonNull List publicKeys) { - setFilterableListByType(ReferencedPublicKeyFilter.filterKey, publicKeys, ReferencedPublicKeyFilter::new); - } - - @JsonProperty(SinceFilter.filterKey) - public Optional getSince() { - return getSinceOrUntil(SinceFilter.filterKey); - } - - @JsonProperty(SinceFilter.filterKey) - public void setSince(@NonNull Long since) { - this.core.addFilterable(SinceFilter.filterKey, new SinceFilter(since)); - } - - @JsonProperty(UntilFilter.filterKey) - public Optional getUntil() { - return getSinceOrUntil(UntilFilter.filterKey); - } - - @JsonProperty(UntilFilter.filterKey) - public void setUntil(@NonNull Long until) { - this.core.addFilterable(UntilFilter.filterKey, new UntilFilter(until)); - } - - @Setter(AccessLevel.NONE) - private Map> genericTagQuery; - - public List getReferenceforbelow() { - return getFilterableListByType("asdfasdf"); - } - - @JsonAnyGetter - public Map> getGenericTagQuery() { - this.genericTagQuery = Optional.ofNullable(genericTagQuery).orElse(new HashMap<>()); - return genericTagQuery; - } - - public List getGenericTagQuery(@NonNull String key) { - return Optional.ofNullable(getGenericTagQuery().get(key)).orElse(Collections.emptyList()); - } - - // TODO: add map content validation? - @JsonAnySetter - public void setGenericTagQuery(@NonNull Map> map) { - if (map.isEmpty()) - throw new IllegalArgumentException("generic tag query cannot be empty"); - map.forEach(this::setGenericTagQuery); - } - - // TODO: add map content validation? - @JsonAnySetter - public void setGenericTagQuery(@NonNull String key, @NonNull List value) { - if (key.isBlank()) - throw new IllegalArgumentException("key cannot be null"); - GenericTagQuery query = new GenericTagQuery(); - query.setTagName(key); - query.setValue(value); - setFilterableListByType(key, List.of(query), GenericTagQueryFilter::new); - getGenericTagQuery().put(key, value); - } - - @JsonProperty(AddressableTagFilter.filterKey) - public List getAddressableTags() { - return getFilterableListByType(AddressableTagFilter.filterKey); - } - - @JsonProperty(AddressableTagFilter.filterKey) - public void setAddressableTags(@NonNull List addressTags) { - setFilterableListByType(AddressableTagFilter.filterKey, addressTags, AddressableTagFilter::new); - } - - @JsonProperty(IdentifierTagFilter.filterKey) - public List getIdentifierTags() { - return getFilterableListByType(IdentifierTagFilter.filterKey); - } - - @JsonProperty(IdentifierTagFilter.filterKey) - public void setIdentifierTags(@NonNull List identifierTags) { - setFilterableListByType(IdentifierTagFilter.filterKey, identifierTags, IdentifierTagFilter::new); - } - - private List getFilterableListByType(@NonNull String type) { - return Optional - .ofNullable( - this.core.getFilterableByType(type)) - .stream().flatMap(filterables -> - filterables.stream().map(filterable -> - (T) filterable.getFilterCriterion())) - .toList(); - } - - @SneakyThrows - private void setFilterableListByType( - @NonNull String key, - @NonNull List filterTypeList, - @NonNull Function filterableFunction) { - - if (filterTypeList.isEmpty()) { - throw new IllegalArgumentException( - String.format("[%s] filter must contain at least one element", key)); - } - - this.core.addFilterable( - key, - filterTypeList.stream().map(filterableFunction).collect(Collectors.toList())); -// .orElseThrow(() -> -// new IllegalArgumentException( -// String.format("[%s] filter must contain at least one element"))) - } - - private Optional getSinceOrUntil(String type) { - return Optional - .ofNullable( - this.core.getFilterableByType(type)) - .map(filterables -> filterables - .getFirst().getFilterCriterion()); - } - - public Optional getLimit() { - return Optional.ofNullable(limit); - } - - public void setLimit(@NonNull Integer limit) { - this.limit = limit; - } - - public static FiltersBuilder builder() { - return new FiltersBuilder(); - } - - public static class FiltersBuilder { - private final Filters filters = new Filters(); - - @JsonProperty(EventFilter.filterKey) - public FiltersBuilder events(@NonNull List events) { - filters.setEvents(events); - return this; - } - - @JsonProperty(PublicKeyFilter.filterKey) - public FiltersBuilder authors(@NonNull List authors) { - filters.setAuthors(authors); - return this; - } - - @JsonProperty(KindFilter.filterKey) - public FiltersBuilder kinds(@NonNull List kinds) { - filters.setKinds(kinds); - return this; - } - - @JsonProperty(ReferencedEventFilter.filterKey) - public FiltersBuilder referencedEvents(@NonNull List events) { - filters.setReferencedEvents(events); - return this; - } - - @JsonProperty(ReferencedPublicKeyFilter.filterKey) - public FiltersBuilder referencePubKeys(@NonNull List publicKeys) { - filters.setReferencePubKeys(publicKeys); - return this; - } - - @JsonProperty(SinceFilter.filterKey) - public FiltersBuilder since(@NonNull Long since) { - filters.setSince(since); - return this; - } - - @JsonProperty(UntilFilter.filterKey) - public FiltersBuilder until(@NonNull Long until) { - filters.setUntil(until); - return this; - } - - public FiltersBuilder genericTagQuery(@NonNull Map> map) { - filters.setGenericTagQuery(map); - return this; - } - - public FiltersBuilder limit(@NonNull Integer limit) { - filters.setLimit(limit); - return this; - } - - public Filters build() { - return filters; - } - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java index a61386ff1..701c05570 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java @@ -1,6 +1,7 @@ package nostr.event.json.codec; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.NonNull; import lombok.SneakyThrows; @@ -16,37 +17,63 @@ import nostr.event.message.RelayAuthenticationMessage; import nostr.event.message.ReqMessage; +import java.util.List; import java.util.Map; /** * @author eric */ public class BaseMessageDecoder implements IDecoder { - private final ObjectMapper mapper; + public static final int MAX_JSON_NODE_THRESHOLD = 99; + private final ObjectMapper mapper = new ObjectMapper(); public BaseMessageDecoder() { - this.mapper = new ObjectMapper(); - this.mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); } @SneakyThrows @Override public T decode(@NonNull String jsonString) { - Object[] msgArr = mapper.readValue(jsonString, Object[].class); - final String strCmd = msgArr[0].toString(); - final Object arg = msgArr[1]; + ValidJsonNode validJsonNode = validateJson(jsonString); + String command = validJsonNode.formerly_strCmd(); + Object subscriptionId = validJsonNode.formerly_arg(); // subscriptionId + String filtersJson = validJsonNode.formerly_msgArr(); // filters - return switch (strCmd) { - case "AUTH" -> arg instanceof Map map ? + Object[] msgArr = mapper.readValue(jsonString, Object[].class); // TODO: replace with jsonNode after ReqMessage.decode() is finished + + return switch (command) { + case "AUTH" -> subscriptionId instanceof Map map ? CanonicalAuthenticationMessage.decode(map, mapper) : - RelayAuthenticationMessage.decode(arg); - case "CLOSE" -> CloseMessage.decode(arg); - case "EOSE" -> EoseMessage.decode(arg); + RelayAuthenticationMessage.decode(subscriptionId); + case "CLOSE" -> CloseMessage.decode(subscriptionId); + case "EOSE" -> EoseMessage.decode(subscriptionId); case "EVENT" -> EventMessage.decode(msgArr, mapper); - case "NOTICE" -> NoticeMessage.decode(arg); + case "NOTICE" -> NoticeMessage.decode(subscriptionId); case "OK" -> OkMessage.decode(msgArr); - case "REQ" -> ReqMessage.decode(msgArr, mapper); + case "REQ" -> ReqMessage.decode(subscriptionId, List.of(filtersJson)); default -> GenericMessage.decode(msgArr); }; } + + @SneakyThrows + private ValidJsonNode validateJson(@NonNull String jsonString) { + final JsonNode jsonNode = mapper.readTree(jsonString); + + if (jsonNode.size() > MAX_JSON_NODE_THRESHOLD) + throw new IllegalArgumentException( + String.format("BaseMessageDecoder expected max [%d] JSON nodes but received [%s] instead with contents:\n\n[%s]\n", + MAX_JSON_NODE_THRESHOLD, + jsonNode.size(), + jsonNode.toPrettyString() + )); + + return new ValidJsonNode( + jsonNode.get(0).asText(), + jsonNode.get(1).asText(), + jsonNode.get(2).toString()); + } + + private record ValidJsonNode(@NonNull String formerly_strCmd, @NonNull Object formerly_arg, + @NonNull String formerly_msgArr) { + } } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java index b957d1c00..c1c6b850d 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import lombok.NonNull; -import nostr.event.impl.Filters; +import nostr.event.filter.Filters; /** * diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java index 73f620606..09bdd3246 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java @@ -1,70 +1,37 @@ package nostr.event.json.codec; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.ArrayNode; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.SneakyThrows; import nostr.base.FEncoder; -import nostr.event.impl.Filters; -import nostr.util.NostrException; +import nostr.event.filter.Filterable; +import nostr.event.filter.Filters; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.stream.StreamSupport; +import java.util.HashMap; +import java.util.Map; -/** - * @author guilhermegps - */ @Data @EqualsAndHashCode(callSuper = false) public class FiltersEncoder implements FEncoder { - private final Filters filters; + private final Filters filters; - public FiltersEncoder(Filters filters) { - this.filters = filters; - } + public FiltersEncoder(Filters filters) { + this.filters = filters; + } - protected String toJson() throws NostrException { - try { - JsonNode node = MAPPER.valueToTree(filters); - ObjectNode objNode = (ObjectNode) node; - //var arrayNode = (ArrayNode) node.get("genericTagQuery"); - if (objNode != null && !objNode.isNull()) { - for (JsonNode jn : objNode) { - StreamSupport.stream( - Spliterators.spliteratorUnknownSize(jn.fields(), Spliterator.ORDERED), false) - .forEach(f -> { - if ("genericTagQuery".equals(f.getKey())) { - var mapper = new ObjectMapper(); - try { - mapper.readTree(f.getValue().toString()) - .fields() - .forEachRemaining(g -> objNode.set(g.getKey(), g.getValue())); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } else { - objNode.set(f.getKey(), f.getValue()); - } - }); - } - } - objNode.remove("genericTagQuery"); - - return MAPPER.writeValueAsString(objNode); - } catch (JsonProcessingException | IllegalArgumentException e) { - throw new NostrException(e); - } - } - - @Override - public String encode() { - try { - return toJson(); - } catch (NostrException ex) { - throw new RuntimeException(ex); - } - } + @SneakyThrows + @Override + public String encode() { + Map result = new HashMap<>(); + filters.getFiltersMap().forEach((key, value) -> + value.stream().distinct() + .map(Filterable::toArrayNode) + .reduce(ArrayNode::addAll) + .ifPresent(arrayNode -> + result.put(key, arrayNode))); + JsonNode jsonNode = MAPPER.valueToTree(result); + return MAPPER.writeValueAsString(jsonNode); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderOld.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderOld.java new file mode 100644 index 000000000..e4ce0fa4c --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderOld.java @@ -0,0 +1,69 @@ +//package nostr.event.json.codec; +// +//import com.fasterxml.jackson.core.JsonProcessingException; +//import com.fasterxml.jackson.databind.JsonNode; +//import com.fasterxml.jackson.databind.ObjectMapper; +//import com.fasterxml.jackson.databind.node.ObjectNode; +//import lombok.Data; +//import lombok.EqualsAndHashCode; +//import nostr.base.FEncoder; +//import nostr.util.NostrException; +// +//import java.util.Spliterator; +//import java.util.Spliterators; +//import java.util.stream.StreamSupport; +// +///** +// * @author guilhermegps +// */ +//@Data +//@EqualsAndHashCode(callSuper = false) +//public class FiltersEncoder implements FEncoder { +// private final Filters filters; +// +// public FiltersEncoder(Filters filters) { +// this.filters = filters; +// } +// +// protected String toJson() throws NostrException { +// try { +// JsonNode node = MAPPER.valueToTree(filters); +// ObjectNode objNode = (ObjectNode) node; +// //var arrayNode = (ArrayNode) node.get("genericTagQuery"); +// if (objNode != null && !objNode.isNull()) { +// for (JsonNode jn : objNode) { +// StreamSupport.stream( +// Spliterators.spliteratorUnknownSize(jn.fields(), Spliterator.ORDERED), false) +// .forEach(f -> { +// if ("genericTagQuery".equals(f.getKey())) { +// var mapper = new ObjectMapper(); +// try { +// mapper.readTree(f.getValue().toString()) +// .fields() +// .forEachRemaining(g -> objNode.set(g.getKey(), g.getValue())); +// } catch (JsonProcessingException e) { +// throw new RuntimeException(e); +// } +// } else { +// objNode.set(f.getKey(), f.getValue()); +// } +// }); +// } +// } +// objNode.remove("genericTagQuery"); +// +// return MAPPER.writeValueAsString(objNode); +// } catch (JsonProcessingException | IllegalArgumentException e) { +// throw new NostrException(e); +// } +// } +// +// @Override +// public String encode() { +// try { +// return toJson(); +// } catch (NostrException ex) { +// throw new RuntimeException(ex); +// } +// } +//} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderRxR.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderRxR.java deleted file mode 100644 index 206d91861..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderRxR.java +++ /dev/null @@ -1,40 +0,0 @@ -package nostr.event.json.codec; - -import com.fasterxml.jackson.databind.JsonNode; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.SneakyThrows; -import nostr.base.FEncoder; -import nostr.event.filter.Filterable; -import nostr.event.filter.FiltersCore; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * @author guilhermegps - */ -@Data -@EqualsAndHashCode(callSuper = false) -public class FiltersEncoderRxR implements FEncoder { - private final FiltersCore filtersCore; - - public FiltersEncoderRxR(FiltersCore filtersCore) { - this.filtersCore = filtersCore; - } - - @SneakyThrows - @Override - public String encode() { - Map> result = new HashMap<>(); - filtersCore.getFiltersMap().forEach((key, value) -> - result.put( - key, - value.stream().map( - Filterable::toJson).collect(Collectors.toList()))); - JsonNode jsonNode = MAPPER.valueToTree(result); - return MAPPER.writeValueAsString(jsonNode); - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersListEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersListEncoder.java index 8a43f46d2..e343102a1 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersListEncoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersListEncoder.java @@ -1,58 +1,57 @@ -package nostr.event.json.codec; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import lombok.Data; -import nostr.base.FEncoder; -import nostr.event.impl.Filters; -import nostr.util.NostrException; - -import java.util.List; - -@Data -public class FiltersListEncoder implements FEncoder { - - private final List filtersList; - - public FiltersListEncoder(List filtersList) { - this.filtersList = filtersList; - } - - @Override - public String encode() { - try { - return toJson(); - } catch (NostrException ex) { - throw new RuntimeException(ex); - } - } - - protected String toJson() throws NostrException { - return toJsonCommaSeparated(); - } - - private String toJsonArray() throws NostrException { - try { - StringBuilder sb = new StringBuilder(); - for (Object filter : getFiltersList()) { - if (!sb.isEmpty()) { - sb.append(","); - } - sb.append(MAPPER.writeValueAsString(filter)); - } - return sb.toString(); - } catch (JsonProcessingException | IllegalArgumentException e) { - throw new NostrException(e); - } - } - - private String toJsonCommaSeparated() throws NostrException { - JsonNode node = MAPPER.valueToTree(getFiltersList()); - try { - return MAPPER.writeValueAsString(node); - } catch (JsonProcessingException e) { - throw new NostrException(e); - } - - } -} +//package nostr.event.json.codec; +// +//import com.fasterxml.jackson.core.JsonProcessingException; +//import com.fasterxml.jackson.databind.JsonNode; +//import lombok.Data; +//import nostr.base.FEncoder; +//import nostr.util.NostrException; +// +//import java.util.List; +// +//@Data +//public class FiltersListEncoder implements FEncoder { +// +// private final List filtersList; +// +// public FiltersListEncoder(List filtersList) { +// this.filtersList = filtersList; +// } +// +// @Override +// public String encode() { +// try { +// return toJson(); +// } catch (NostrException ex) { +// throw new RuntimeException(ex); +// } +// } +// +// protected String toJson() throws NostrException { +// return toJsonCommaSeparated(); +// } +// +// private String toJsonArray() throws NostrException { +// try { +// StringBuilder sb = new StringBuilder(); +// for (Object filter : getFiltersList()) { +// if (!sb.isEmpty()) { +// sb.append(","); +// } +// sb.append(MAPPER.writeValueAsString(filter)); +// } +// return sb.toString(); +// } catch (JsonProcessingException | IllegalArgumentException e) { +// throw new NostrException(e); +// } +// } +// +// private String toJsonCommaSeparated() throws NostrException { +// JsonNode node = MAPPER.valueToTree(getFiltersList()); +// try { +// return MAPPER.writeValueAsString(node); +// } catch (JsonProcessingException e) { +// throw new NostrException(e); +// } +// +// } +//} diff --git a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java index aa1d108f3..8f8076d1d 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java @@ -2,21 +2,26 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; +import lombok.SneakyThrows; import lombok.ToString; import nostr.base.Command; import nostr.base.IEncoder; import nostr.event.BaseMessage; -import nostr.event.impl.Filters; +import nostr.event.filter.Filterable; +import nostr.event.filter.FilterableProvider; +import nostr.event.filter.Filters; import nostr.event.json.codec.FiltersEncoder; +import java.util.HashMap; import java.time.temporal.ValueRange; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * @@ -26,51 +31,76 @@ @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) public class ReqMessage extends BaseMessage { + private final static ObjectMapper mapper = new ObjectMapper(); - @JsonProperty - private final String subscriptionId; + @JsonProperty + private final String subscriptionId; - @JsonProperty - private final List filtersList; + @JsonProperty + private final List filtersList; - public ReqMessage(@NonNull String subscriptionId, Filters filters) { - this(subscriptionId, List.of(filters)); - } + public ReqMessage(String subscriptionId, Filters... filtersList) { + this(subscriptionId, List.of(filtersList)); + } - public ReqMessage(@NonNull String subscriptionId, List incomingFiltersList) { - super(Command.REQ.name()); - if (!ValueRange.of(1, 64).isValidIntValue(subscriptionId.length())) { - throw new IllegalArgumentException(String.format("subscriptionId length must be between 1 and 64 characters but was [%d]", subscriptionId.length())); - } - this.subscriptionId = subscriptionId; - this.filtersList = new ArrayList<>(); - this.filtersList.addAll(incomingFiltersList); - } - - @Override - public String encode() throws JsonProcessingException { - getArrayNode() - .add(getCommand()) - .add(getSubscriptionId()); - List localFiltersList = getFiltersList(); - for (Filters f : localFiltersList) { - try { - FiltersEncoder filtersEncoder = new FiltersEncoder(f); - var filterNode = IEncoder.MAPPER.readTree(filtersEncoder.encode()); - getArrayNode().add(filterNode); - } catch (Exception e) { - throw new RuntimeException(e); - } + public ReqMessage(String subscriptionId, List filtersList) { + super(Command.REQ.name()); + if (!ValueRange.of(1, 64).isValidIntValue(subscriptionId.length())) { + throw new IllegalArgumentException(String.format("subscriptionId length must be between 1 and 64 characters but was [%d]", subscriptionId.length())); + } + this.filtersList = filtersList; + this.subscriptionId = subscriptionId; + } + + @SneakyThrows + @Override + public String encode() throws JsonProcessingException { + getArrayNode() + .add(getCommand()) + .add(getSubscriptionId()); + +// filtersList.stream() +// .map(FiltersEncoderRxR::new) +// .map(FiltersEncoderRxR::encode) +// .map(IEncoder.MAPPER::readTree) +// .forEach(jsonNode -> +// getArrayNode().add(jsonNode)); + + + List encodedFilterList = filtersList.stream().map(FiltersEncoder::new).map(FiltersEncoder::encode).toList(); + + List jsonNodesList = encodedFilterList.stream().map( + encode -> { + try { + return IEncoder.MAPPER.readTree(encode); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } } - return IEncoder.MAPPER.writeValueAsString(getArrayNode()); - } - - public static T decode(@NonNull Object[] msgArr, ObjectMapper mapper) { - var len = msgArr.length - 2; - var filtersArr = new Object[len]; - System.arraycopy(msgArr, 2, filtersArr, 0, len); - var filtersList = mapper.convertValue(filtersArr, new TypeReference>() { - }); - return (T) new ReqMessage(msgArr[1].toString(), filtersList); - } + ).toList(); + + jsonNodesList.forEach(jsonNode -> getArrayNode().add(jsonNode)); + + return IEncoder.MAPPER.writeValueAsString(getArrayNode()); + } + + public static T decode(@NonNull Object subscriptionId, @NonNull List jsonFiltersList) { + return (T) new ReqMessage(subscriptionId.toString(), + jsonFiltersList.stream().map( + ReqMessage::createFiltersFromJson).toList()); + } + + @SneakyThrows + private static Filters createFiltersFromJson(String jsonFiltersList) { + final Map> filterPluginsMap = new HashMap<>(); + + mapper.readTree(jsonFiltersList).fields().forEachRemaining(field -> + filterPluginsMap.put( + field.getKey(), + FilterableProvider.getFilterable( + field.getKey(), + field.getValue()))); + + return new Filters(filterPluginsMap); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/message/ReqMessageRxR.java b/nostr-java-event/src/main/java/nostr/event/message/ReqMessageRxR.java deleted file mode 100644 index 474db934c..000000000 --- a/nostr-java-event/src/main/java/nostr/event/message/ReqMessageRxR.java +++ /dev/null @@ -1,138 +0,0 @@ -package nostr.event.message; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NonNull; -import lombok.SneakyThrows; -import lombok.ToString; -import nostr.base.Command; -import nostr.base.GenericTagQuery; -import nostr.base.IEncoder; -import nostr.base.PublicKey; -import nostr.event.BaseMessage; -import nostr.event.Kind; -import nostr.event.filter.EventFilter; -import nostr.event.filter.Filterable; -import nostr.event.filter.FiltersCore; -import nostr.event.filter.GenericTagQueryFilter; -import nostr.event.filter.KindFilter; -import nostr.event.filter.PublicKeyFilter; -import nostr.event.filter.ReferencedEventFilter; -import nostr.event.filter.ReferencedPublicKeyFilter; -import nostr.event.filter.SinceFilter; -import nostr.event.filter.UntilFilter; -import nostr.event.impl.GenericEvent; -import nostr.event.json.codec.FiltersEncoderRxR; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.StreamSupport; - -/** - * @author squirrel - */ -@Getter -@EqualsAndHashCode(callSuper = false) -@ToString(callSuper = true) -public class ReqMessageRxR extends BaseMessage { - private final static ObjectMapper mapper = new ObjectMapper(); - - @JsonProperty - private final String subscriptionId; - - @JsonProperty - private final FiltersCore filtersCore; - - public ReqMessageRxR(String subscriptionId, Map> filterPlugins) { - super(Command.REQ.name()); - this.filtersCore = new FiltersCore(filterPlugins); - this.subscriptionId = subscriptionId; - } - - @Override - public String encode() throws JsonProcessingException { - getArrayNode() - .add(getCommand()) - .add(getSubscriptionId()); - - try { - FiltersEncoderRxR filtersEncoder = new FiltersEncoderRxR(filtersCore); - var filterNode = IEncoder.MAPPER.readTree(filtersEncoder.encode()); - getArrayNode().add(filterNode); - } catch (Exception e) { - throw new RuntimeException(e); - } - return IEncoder.MAPPER.writeValueAsString(getArrayNode()); - } - - @SneakyThrows - public static T decode(@NonNull Object subscriptionId, @NonNull String jsonString) { - final Map> filterPluginsMap = new HashMap<>(); - mapper.readTree(jsonString).fields().forEachRemaining(field -> - filterPluginsMap.put( - field.getKey(), - getFilterable( - field.getKey(), - field.getValue()))); - -// TODO: variant 1 stream generating map, almost working -// Iterator elements = mapper.readTree(jsonString).elements(); -// List>> list = Stream.of(elements).map(jsonNodeIterator -> -// { -// JsonNode next = jsonNodeIterator.next(); -// Iterator> fields1 = next.fields(); -// return fields1; -// }).map(entryIterator -> -// Map.of( -// entryIterator.next().getKey(), -// getFilterable( -// entryIterator.next().getKey(), -// entryIterator.next().getValue()))) -// .toList(); - -// TODO: variant 2 stream generating map, left as potentially helpful for variant 1 -// JsonNode jsonNode = mapper.readTree(jsonString); -// Iterator> fields = jsonNode.fields(); -// List>> kindOnly = Stream.of(fields) -// .map(entryIterator -> -// Map.of(entryIterator.next().getKey(), -// getFilterable( -// entryIterator.next().getKey(), -// entryIterator.next().getValue()))).toList(); - - return (T) new ReqMessageRxR(subscriptionId.toString(), filterPluginsMap); - } - -// TODO: below functionals can/should be refactored into their associated filter classes - static List getFilterable(String type, JsonNode node) { - return switch (type) { - case ReferencedPublicKeyFilter.filterKey -> - getFilterable(node, referencedPubKey -> new ReferencedPublicKeyFilter<>(new PublicKey(referencedPubKey.asText()))); - case ReferencedEventFilter.filterKey -> - getFilterable(node, referencedEvent -> new ReferencedEventFilter<>(new GenericEvent(referencedEvent.asText()))); - case PublicKeyFilter.filterKey -> - getFilterable(node, author -> new PublicKeyFilter<>(new PublicKey(author.asText()))); - case EventFilter.filterKey -> getFilterable(node, event -> new EventFilter<>(new GenericEvent(event.asText()))); - case SinceFilter.filterKey -> getFilterable(node, since -> new SinceFilter(since.asLong())); - case UntilFilter.filterKey -> getFilterable(node, until -> new UntilFilter(until.asLong())); - case KindFilter.filterKey -> getFilterable(node, kindNode -> new KindFilter<>(Kind.valueOf(kindNode.asInt()))); -// case AddressableTagFilter.filterKey -> new XYZ<>(getGenericTagQuery(node)); -// case IdentifierTagFilter.filterKey -> new XYZ<>(getGenericTagQuery(node)); - default -> getFilterable(node, kindNode -> - new GenericTagQueryFilter<>( - new GenericTagQuery( - type, - StreamSupport.stream(node.spliterator(), false).map(JsonNode::asText).toList()))); - }; - } - - private static List getFilterable(JsonNode jsonNode, Function filterFunction) { - return StreamSupport.stream(jsonNode.spliterator(), false).map(filterFunction).toList(); - } -} diff --git a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java index ad16dfddf..2213f7f90 100644 --- a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java +++ b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java @@ -20,7 +20,6 @@ import nostr.event.impl.DeletionEvent; import nostr.event.impl.DirectMessageEvent; import nostr.event.impl.EphemeralEvent; -import nostr.event.impl.Filters; import nostr.event.impl.GenericEvent; import nostr.event.impl.HideMessageEvent; import nostr.event.impl.InternetIdentifierMetadataEvent; diff --git a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java b/nostr-java-test/src/main/java/nostr/test/EntityFactory.java index bd1bf0422..bcdb51614 100644 --- a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java +++ b/nostr-java-test/src/main/java/nostr/test/EntityFactory.java @@ -9,9 +9,9 @@ import nostr.event.BaseTag; import nostr.event.Kind; import nostr.event.Reaction; +import nostr.event.filter.Filters; import nostr.event.impl.DirectMessageEvent; import nostr.event.impl.EphemeralEvent; -import nostr.event.impl.Filters; import nostr.event.impl.GenericEvent; import nostr.event.impl.GenericTag; import nostr.event.impl.InternetIdentifierMetadataEvent; @@ -29,7 +29,6 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Random; /** @@ -60,7 +59,8 @@ public static DirectMessageEvent createDirectMessageEvent(PublicKey senderPublic } public static Filters createFilters(List authors, List kindList, Long since) { - return Filters.builder().authors(authors).kinds(kindList).since(since).build(); +// return Filters.builder().authors(authors).kinds(kindList).since(since).build(); + return null; } public static InternetIdentifierMetadataEvent createInternetIdentifierMetadataEvent(UserProfile profile) { diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java index 26a5a6b35..26e88c8a1 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java @@ -17,13 +17,11 @@ import nostr.crypto.bech32.Bech32; import nostr.crypto.bech32.Bech32Prefix; import nostr.event.BaseTag; -import nostr.event.Kind; import nostr.event.impl.CalendarContent; import nostr.event.impl.CreateOrUpdateStallEvent; import nostr.event.impl.CreateOrUpdateStallEvent.Stall; import nostr.event.impl.DirectMessageEvent; import nostr.event.impl.EncryptedPayloadEvent; -import nostr.event.impl.Filters; import nostr.event.impl.NostrMarketplaceEvent; import nostr.event.impl.NostrMarketplaceEvent.Product.Spec; import nostr.event.impl.TextNoteEvent; @@ -43,7 +41,6 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java b/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java index d3ad8457f..4cfc84e4b 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java @@ -5,7 +5,12 @@ import nostr.base.PublicKey; import nostr.base.Relay; import nostr.event.Kind; -import nostr.event.impl.Filters; +import nostr.event.filter.EventFilter; +import nostr.event.filter.Filterable; +import nostr.event.filter.Filters; +import nostr.event.filter.KindFilter; +import nostr.event.filter.PublicKeyFilter; +import nostr.event.filter.ReferencedPublicKeyFilter; import nostr.event.impl.GenericEvent; import nostr.event.tag.EventTag; import nostr.event.tag.IdentifierTag; @@ -16,6 +21,7 @@ import java.time.Instant; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -32,266 +38,269 @@ public void testEventFilters() { GenericEvent instance = EntityFactory.Events.createTextNoteEvent(publicKey); instance.update(); - Filters filters = new Filters(); - GenericEvent eventList = new GenericEvent(); - eventList.setId(instance.getId()); + GenericEvent event = new GenericEvent(); + event.setId(instance.getId()); - filters.setEvents(List.of(eventList)); + Map> filterablesMap = new HashMap<>(); + filterablesMap.put(EventFilter.filterKey, List.of(new EventFilter<>(event))); - List filtersEvents = filters.getEvents(); - assertEquals(filtersEvents.getFirst().getId(), eventList.getId()); + Filters filters = new Filters(filterablesMap); + List filtersEvents = filters.getFilterableByType(EventFilter.filterKey); + assertEquals(((EventFilter)filtersEvents.getFirst()).getFilterCriterion().getId(), event.getId()); } @Test - public void testAuthorFilters() { + public void testAuthorFiltersRxR() { PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); GenericEvent instance = EntityFactory.Events.createTextNoteEvent(publicKey); instance.update(); - Filters filters = new Filters(); - filters.setAuthors(List.of(publicKey)); + Map> filterablesMap = new HashMap<>(); + PublicKeyFilter authorFilter = new PublicKeyFilter<>(publicKey); + filterablesMap.put(PublicKeyFilter.filterKey, List.of(authorFilter)); - List authors = filters.getAuthors(); - assertEquals(authors, List.of(publicKey)); + Filters filters = new Filters(filterablesMap); + List authors = filters.getFilterableByType(PublicKeyFilter.filterKey); + assertEquals(authors, List.of(authorFilter)); } - @Test - public void testKindFilters() { - Filters filters = new Filters(); - List kindList = List.of(Kind.CONTACT_LIST, Kind.DELETION); - filters.setKinds(kindList); - List kinds = filters.getKinds(); - assertEquals(kinds, kindList); - } - - @Test - public void testReferencedEventsFilters() { - PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); - - GenericEvent eventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); - eventToReference.update(); - - GenericEvent eventContainingEventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); - eventContainingEventToReference.setTags(List.of(new EventTag(eventToReference.getId()))); - eventContainingEventToReference.update(); - - Filters filters = new Filters(); - filters.setReferencedEvents(List.of(eventContainingEventToReference)); - - filters.getReferencedEvents().getFirst().getTags().stream() - .filter(EventTag.class::isInstance) - .map(EventTag.class::cast) - .findFirst() - .ifPresent(eventTag -> - assertEquals(eventTag.getIdEvent(), eventToReference.getId())); - } - - @Test - public void testReferencedPubkeysFilters() { - PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); - - GenericEvent eventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); - eventToReference.update(); - - GenericEvent eventContainingEventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); - eventContainingEventToReference.setTags(List.of(new PubKeyTag(eventToReference.getPubKey()))); - eventContainingEventToReference.update(); - - Filters filters = new Filters(); - filters.setReferencedEvents(List.of(eventContainingEventToReference)); - - filters.getReferencedEvents().getFirst().getTags().stream() - .filter(PubKeyTag.class::isInstance) - .map(PubKeyTag.class::cast) - .findFirst() - .ifPresent(pubkeyTag -> - assertEquals(pubkeyTag.getPublicKey().toHexString(), eventToReference.getPubKey().toHexString())); - } - - @Test - public void testSinceFilters() { - Filters filters = new Filters(); - long sinceTime = Date.from(Instant.now()).getTime(); - filters.setSince(sinceTime); - - filters.getSince().ifPresent(aLong -> assertEquals(sinceTime, aLong)); - } - - @Test - public void testUntilFilters() { - Filters filters = new Filters(); - long untilTime = Date.from(Instant.now()).getTime(); - filters.setUntil(untilTime); - - filters.getUntil().ifPresent(aLong -> assertEquals(untilTime, aLong)); - } - - @Test - public void testLimit() { - Filters filters = new Filters(); - filters.setLimit(1); - filters.getLimit().ifPresent(integer -> assertEquals(1, integer)); - } - - @Test - public void testGenericQueryTagFilters() { - PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); - GenericTagQuery genericTagQuery = new GenericTagQuery(); - String key = "#a"; - genericTagQuery.setTagName(key); - - String kind = Kind.TEXT_NOTE.toString(); - String hexString = publicKey.toHexString(); - String uuid = new IdentifierTag("uuid").getId(); - String relayUri = new Relay("ws://localhost:5555").getUri(); - - List addressTagValues = List.of( - kind, - hexString, - uuid, - relayUri - ); - genericTagQuery.setValue(addressTagValues); - - Filters filters = new Filters(); - filters.setGenericTagQuery( - genericTagQuery.getTagName(), - addressTagValues); - - List genericTagQueryResult = filters.getGenericTagQuery(key); - genericTagQueryResult.forEach(log::info); - - assertTrue(genericTagQueryResult.contains(kind)); - assertTrue(genericTagQueryResult.contains(hexString)); - assertTrue(genericTagQueryResult.contains(uuid)); - assertTrue(genericTagQueryResult.contains(relayUri)); - } - - @Test - public void testMultipleFilters() { - PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); - GenericEvent instance = EntityFactory.Events.createTextNoteEvent(publicKey); - instance.update(); - - GenericEvent eventList = new GenericEvent(); - eventList.setId(instance.getId()); - - List kindList = List.of(Kind.CONTACT_LIST, Kind.DELETION); - - GenericEvent eventContainingEventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); - eventContainingEventToReference.setTags(List.of(new EventTag(instance.getId()))); - eventContainingEventToReference.setTags(List.of(new PubKeyTag(instance.getPubKey()))); - eventContainingEventToReference.update(); - - long sinceTime = Date.from(Instant.now()).getTime(); - long untilTime = Date.from(Instant.now()).getTime(); - - Filters filters = new Filters(); - filters.setEvents(List.of(eventList)); - filters.setAuthors(List.of(publicKey)); - filters.setKinds(kindList); - filters.setReferencedEvents(List.of(eventContainingEventToReference)); - filters.setSince(sinceTime); - filters.setUntil(untilTime); - - GenericTagQuery genericTagQuery = new GenericTagQuery(); - String key = "#a"; - genericTagQuery.setTagName(key); - - String kind = Kind.TEXT_NOTE.toString(); - String hexString = publicKey.toHexString(); - String uuid = new IdentifierTag("uuid").getId(); - String relayUri = new Relay("ws://localhost:5555").getUri(); - - List addressTagValues = List.of( - kind, - hexString, - uuid, - relayUri - ); - genericTagQuery.setValue(addressTagValues); - - filters.setGenericTagQuery( - genericTagQuery.getTagName(), - addressTagValues); - - List filtersEvents = filters.getEvents(); - assertEquals(filtersEvents.getFirst().getId(), eventList.getId()); - - List authors = filters.getAuthors(); - assertEquals(authors, List.of(publicKey)); - - List kinds = filters.getKinds(); - assertEquals(kinds, kindList); - - filters.getReferencedEvents().getFirst().getTags().stream() - .filter(EventTag.class::isInstance) - .map(EventTag.class::cast) - .findFirst() - .ifPresent(eventTag -> - assertEquals(eventTag.getIdEvent(), instance.getId())); - - filters.getReferencedEvents().getFirst().getTags().stream() - .filter(PubKeyTag.class::isInstance) - .map(PubKeyTag.class::cast) - .findFirst() - .ifPresent(pubkeyTag -> - assertEquals(pubkeyTag.getPublicKey().toHexString(), instance.getPubKey().toHexString())); - - filters.getSince().ifPresent(aLong -> assertEquals(sinceTime, aLong)); - filters.getUntil().ifPresent(aLong -> assertEquals(untilTime, aLong)); - - List genericTagQueryResult = filters.getGenericTagQuery(key); - - assertTrue(genericTagQueryResult.contains(kind)); - assertTrue(genericTagQueryResult.contains(hexString)); - assertTrue(genericTagQueryResult.contains(uuid)); - assertTrue(genericTagQueryResult.contains(relayUri)); - } - - @Test - public void testNonExistingValues() { - Filters filters = new Filters(); - assertTrue(filters.getLimit().isEmpty()); - assertTrue(filters.getAuthors().isEmpty()); - assertTrue(filters.getKinds().isEmpty()); - assertTrue(filters.getReferencedEvents().isEmpty()); - assertTrue(filters.getUntil().isEmpty()); - assertTrue(filters.getEvents().isEmpty()); - assertTrue(filters.getReferencePubKeys().isEmpty()); - assertTrue(filters.getGenericTagQuery().isEmpty()); - assertTrue(filters.getGenericTagQuery("someKey").isEmpty()); - } - - @Test - public void testNonExistingTagTypes() { - Filters filters = new Filters(); - String key = "some-random-key"; - String value = "some-random-value"; - filters.setGenericTagQuery( - key, - List.of(value)); - - assertTrue(filters.getGenericTagQuery(key).contains(value)); - - String anotherValue = "another-random-value"; - filters.setGenericTagQuery( - key, - List.of(value, anotherValue)); - assertTrue(filters.getGenericTagQuery(key).contains(value)); - assertTrue(filters.getGenericTagQuery(key).contains(anotherValue)); - } - - @Test - public void testNullValuesThrowException() { - Filters filters = new Filters(); - assertThrows(NullPointerException.class, () -> filters.setLimit(null)); - assertThrows(IllegalArgumentException.class, () -> filters.setEvents(List.of())); - assertThrows(IllegalArgumentException.class, () -> filters.setAuthors(List.of())); - assertThrows(IllegalArgumentException.class, () -> filters.setKinds(List.of())); - assertThrows(IllegalArgumentException.class, () -> filters.setReferencedEvents(List.of())); - assertThrows(IllegalArgumentException.class, () -> filters.setReferencePubKeys(List.of())); - assertThrows(IllegalArgumentException.class, () -> filters.setGenericTagQuery("", List.of())); - assertThrows(IllegalArgumentException.class, () -> filters.setGenericTagQuery(Map.of())); - } +// @Test +// public void testKindFilters() { +// Filters filters = new Filters(); +// List kindList = List.of(Kind.CONTACT_LIST, Kind.DELETION); +// filters.setKinds(kindList); +// List kinds = filters.getKinds(); +// assertEquals(kinds, kindList); +// } +// +// @Test +// public void testReferencedEventsFilters() { +// PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); +// +// GenericEvent eventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); +// eventToReference.update(); +// +// GenericEvent eventContainingEventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); +// eventContainingEventToReference.setTags(List.of(new EventTag(eventToReference.getId()))); +// eventContainingEventToReference.update(); +// +// Filters filters = new Filters(); +// filters.setReferencedEvents(List.of(eventContainingEventToReference)); +// +// filters.getReferencedEvents().getFirst().getTags().stream() +// .filter(EventTag.class::isInstance) +// .map(EventTag.class::cast) +// .findFirst() +// .ifPresent(eventTag -> +// assertEquals(eventTag.getIdEvent(), eventToReference.getId())); +// } +// +// @Test +// public void testReferencedPubkeysFilters() { +// PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); +// +// GenericEvent eventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); +// eventToReference.update(); +// +// GenericEvent eventContainingEventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); +// eventContainingEventToReference.setTags(List.of(new PubKeyTag(eventToReference.getPubKey()))); +// eventContainingEventToReference.update(); +// +// Filters filters = new Filters(); +// filters.setReferencedEvents(List.of(eventContainingEventToReference)); +// +// filters.getReferencedEvents().getFirst().getTags().stream() +// .filter(PubKeyTag.class::isInstance) +// .map(PubKeyTag.class::cast) +// .findFirst() +// .ifPresent(pubkeyTag -> +// assertEquals(pubkeyTag.getPublicKey().toHexString(), eventToReference.getPubKey().toHexString())); +// } +// +// @Test +// public void testSinceFilters() { +// Filters filters = new Filters(); +// long sinceTime = Date.from(Instant.now()).getTime(); +// filters.setSince(sinceTime); +// +// filters.getSince().ifPresent(aLong -> assertEquals(sinceTime, aLong)); +// } +// +// @Test +// public void testUntilFilters() { +// Filters filters = new Filters(); +// long untilTime = Date.from(Instant.now()).getTime(); +// filters.setUntil(untilTime); +// +// filters.getUntil().ifPresent(aLong -> assertEquals(untilTime, aLong)); +// } +// +// @Test +// public void testLimit() { +// Filters filters = new Filters(); +// filters.setLimit(1); +// filters.getLimit().ifPresent(integer -> assertEquals(1, integer)); +// } +// +// @Test +// public void testGenericQueryTagFilters() { +// PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); +// GenericTagQuery genericTagQuery = new GenericTagQuery(); +// String key = "#a"; +// genericTagQuery.setTagName(key); +// +// String kind = Kind.TEXT_NOTE.toString(); +// String hexString = publicKey.toHexString(); +// String uuid = new IdentifierTag("uuid").getId(); +// String relayUri = new Relay("ws://localhost:5555").getUri(); +// +// List addressTagValues = List.of( +// kind, +// hexString, +// uuid, +// relayUri +// ); +// genericTagQuery.setValue(addressTagValues); +// +// Filters filters = new Filters(); +// filters.setGenericTagQuery( +// genericTagQuery.getTagName(), +// addressTagValues); +// +// List genericTagQueryResult = filters.getGenericTagQuery(key); +// genericTagQueryResult.forEach(log::info); +// +// assertTrue(genericTagQueryResult.contains(kind)); +// assertTrue(genericTagQueryResult.contains(hexString)); +// assertTrue(genericTagQueryResult.contains(uuid)); +// assertTrue(genericTagQueryResult.contains(relayUri)); +// } +// +// @Test +// public void testMultipleFilters() { +// PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); +// GenericEvent instance = EntityFactory.Events.createTextNoteEvent(publicKey); +// instance.update(); +// +// GenericEvent eventList = new GenericEvent(); +// eventList.setId(instance.getId()); +// +// List kindList = List.of(Kind.CONTACT_LIST, Kind.DELETION); +// +// GenericEvent eventContainingEventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); +// eventContainingEventToReference.setTags(List.of(new EventTag(instance.getId()))); +// eventContainingEventToReference.setTags(List.of(new PubKeyTag(instance.getPubKey()))); +// eventContainingEventToReference.update(); +// +// long sinceTime = Date.from(Instant.now()).getTime(); +// long untilTime = Date.from(Instant.now()).getTime(); +// +// Filters filters = new Filters(); +// filters.setEvents(List.of(eventList)); +// filters.setAuthors(List.of(publicKey)); +// filters.setKinds(kindList); +// filters.setReferencedEvents(List.of(eventContainingEventToReference)); +// filters.setSince(sinceTime); +// filters.setUntil(untilTime); +// +// GenericTagQuery genericTagQuery = new GenericTagQuery(); +// String key = "#a"; +// genericTagQuery.setTagName(key); +// +// String kind = Kind.TEXT_NOTE.toString(); +// String hexString = publicKey.toHexString(); +// String uuid = new IdentifierTag("uuid").getId(); +// String relayUri = new Relay("ws://localhost:5555").getUri(); +// +// List addressTagValues = List.of( +// kind, +// hexString, +// uuid, +// relayUri +// ); +// genericTagQuery.setValue(addressTagValues); +// +// filters.setGenericTagQuery( +// genericTagQuery.getTagName(), +// addressTagValues); +// +// List filtersEvents = filters.getEvents(); +// assertEquals(filtersEvents.getFirst().getId(), eventList.getId()); +// +// List authors = filters.getAuthors(); +// assertEquals(authors, List.of(publicKey)); +// +// List kinds = filters.getKinds(); +// assertEquals(kinds, kindList); +// +// filters.getReferencedEvents().getFirst().getTags().stream() +// .filter(EventTag.class::isInstance) +// .map(EventTag.class::cast) +// .findFirst() +// .ifPresent(eventTag -> +// assertEquals(eventTag.getIdEvent(), instance.getId())); +// +// filters.getReferencedEvents().getFirst().getTags().stream() +// .filter(PubKeyTag.class::isInstance) +// .map(PubKeyTag.class::cast) +// .findFirst() +// .ifPresent(pubkeyTag -> +// assertEquals(pubkeyTag.getPublicKey().toHexString(), instance.getPubKey().toHexString())); +// +// filters.getSince().ifPresent(aLong -> assertEquals(sinceTime, aLong)); +// filters.getUntil().ifPresent(aLong -> assertEquals(untilTime, aLong)); +// +// List genericTagQueryResult = filters.getGenericTagQuery(key); +// +// assertTrue(genericTagQueryResult.contains(kind)); +// assertTrue(genericTagQueryResult.contains(hexString)); +// assertTrue(genericTagQueryResult.contains(uuid)); +// assertTrue(genericTagQueryResult.contains(relayUri)); +// } +// +// @Test +// public void testNonExistingValues() { +// Filters filters = new Filters(); +// assertTrue(filters.getLimit().isEmpty()); +// assertTrue(filters.getAuthors().isEmpty()); +// assertTrue(filters.getKinds().isEmpty()); +// assertTrue(filters.getReferencedEvents().isEmpty()); +// assertTrue(filters.getUntil().isEmpty()); +// assertTrue(filters.getEvents().isEmpty()); +// assertTrue(filters.getReferencePubKeys().isEmpty()); +// assertTrue(filters.getGenericTagQuery().isEmpty()); +// assertTrue(filters.getGenericTagQuery("someKey").isEmpty()); +// } +// +// @Test +// public void testNonExistingTagTypes() { +// Filters filters = new Filters(); +// String key = "some-random-key"; +// String value = "some-random-value"; +// filters.setGenericTagQuery( +// key, +// List.of(value)); +// +// assertTrue(filters.getGenericTagQuery(key).contains(value)); +// +// String anotherValue = "another-random-value"; +// filters.setGenericTagQuery( +// key, +// List.of(value, anotherValue)); +// assertTrue(filters.getGenericTagQuery(key).contains(value)); +// assertTrue(filters.getGenericTagQuery(key).contains(anotherValue)); +// } +// +// @Test +// public void testNullValuesThrowException() { +// Filters filters = new Filters(); +// assertThrows(NullPointerException.class, () -> filters.setLimit(null)); +// assertThrows(IllegalArgumentException.class, () -> filters.setEvents(List.of())); +// assertThrows(IllegalArgumentException.class, () -> filters.setAuthors(List.of())); +// assertThrows(IllegalArgumentException.class, () -> filters.setKinds(List.of())); +// assertThrows(IllegalArgumentException.class, () -> filters.setReferencedEvents(List.of())); +// assertThrows(IllegalArgumentException.class, () -> filters.setReferencePubKeys(List.of())); +// assertThrows(IllegalArgumentException.class, () -> filters.setGenericTagQuery("", List.of())); +// assertThrows(IllegalArgumentException.class, () -> filters.setGenericTagQuery(Map.of())); +// } } diff --git a/nostr-java-test/src/test/java/nostr/test/json/JsonParseRxRTest.java b/nostr-java-test/src/test/java/nostr/test/json/JsonParseRxRTest.java deleted file mode 100644 index e8b300dd5..000000000 --- a/nostr-java-test/src/test/java/nostr/test/json/JsonParseRxRTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package nostr.test.json; - -import com.fasterxml.jackson.core.JsonProcessingException; -import lombok.extern.java.Log; -import nostr.base.PublicKey; -import nostr.event.Kind; -import nostr.event.filter.FiltersCore; -import nostr.event.filter.KindFilter; -import nostr.event.filter.PublicKeyFilter; -import nostr.event.filter.ReferencedEventFilter; -import nostr.event.impl.GenericEvent; -import nostr.event.json.codec.BaseMessageDecoder; -import nostr.event.message.ReqMessageRxR; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -@Log -public class JsonParseRxRTest { - @Test - public void testReqMessagePopulatedListOfFiltersListDecoder() throws JsonProcessingException { - log.info("testReqMessagePopulatedListOfFiltersListDecoder"); - - String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; - String kind = "1"; - String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; - String geohashKey = "#g"; - String geohashValue1 = "2vghde"; - String geohashValue2 = "3abcde"; - String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; - String uuidKey = "#d"; - String uuidValue1 = "UUID-1"; - String uuidValue2 = "UUID-2"; - String reqJsonWithCustomTagQueryFilterToDecode = - "[\"REQ\", " + - "\"" + subscriptionId + "\", " + - "{\"kinds\": [" + kind + "], " + - "\"authors\": [\"" + author + "\"]," + -// "\"" + geohashKey + "\": [\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]," + -// "\"" + uuidKey + "\": [\"" + uuidValue1 + "\",\"" + uuidValue2 + "\"]," + - "\"#e\": [\"" + referencedEventId + "\"]}]"; - - ReqMessageRxR decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); - - FiltersCore expectedFilters = new FiltersCore(); - expectedFilters.addFilterable(KindFilter.filterKey, List.of(new KindFilter<>(Kind.TEXT_NOTE))); - expectedFilters.addFilterable(PublicKeyFilter.filterKey, List.of(new PublicKeyFilter<>(new PublicKey(author)))); - expectedFilters.addFilterable(ReferencedEventFilter.filterKey, List.of(new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); -// List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); -// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); -// List expectedIdentityTagValuesList = List.of(uuidValue1, uuidValue2); -// expectedFilters.setGenericTagQuery(uuidKey, expectedIdentityTagValuesList); - ReqMessageRxR expectedReqMessage = new ReqMessageRxR(subscriptionId, expectedFilters.getFiltersMap()); - String expected = expectedReqMessage.encode(); - String actual = decodedReqMessage.encode(); - assertEquals(expected, actual); - assertEquals(expectedReqMessage, decodedReqMessage); - } -} 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 77e012fd4..6596e9583 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 @@ -1,619 +1,473 @@ -package nostr.test.json; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import lombok.extern.java.Log; -import nostr.api.NIP01; -import nostr.base.Command; -import nostr.base.ElementAttribute; -import nostr.base.PublicKey; -import nostr.crypto.bech32.Bech32; -import nostr.event.BaseEvent; -import nostr.event.BaseMessage; -import nostr.event.BaseTag; -import nostr.event.Kind; -import nostr.event.Marker; -import nostr.event.impl.Filters; -import nostr.event.impl.GenericEvent; -import nostr.event.impl.GenericTag; -import nostr.event.json.codec.BaseEventEncoder; -import nostr.event.json.codec.BaseMessageDecoder; -import nostr.event.json.codec.BaseTagDecoder; -import nostr.event.json.codec.FiltersDecoder; -import nostr.event.json.codec.FiltersEncoder; -import nostr.event.json.codec.GenericEventDecoder; -import nostr.event.json.codec.GenericTagDecoder; -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.NostrUtil; -import org.junit.jupiter.api.Test; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * @author eric - */ -@Log -public class JsonParseTest { - - @Test - public void testBaseMessageDecoder() { - log.info("testBaseMessageDecoder"); - - final String parseTarget = - "[\"REQ\", " + - "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + - "{\"kinds\": [1], " + - "\"authors\": [\"f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75\"]," + - "\"#e\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]"; - - final var message = new BaseMessageDecoder<>().decode(parseTarget); - - assertEquals(Command.REQ.toString(), message.getCommand()); - assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", ((ReqMessage) message).getSubscriptionId()); - assertEquals(1, ((ReqMessage) message).getFiltersList().size()); - - var filters = ((ReqMessage) message).getFiltersList().getFirst(); - - assertEquals(1, filters.getKinds().size()); - assertEquals(Kind.TEXT_NOTE, filters.getKinds().getFirst()); - - assertEquals(1, filters.getAuthors().size()); - assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", filters.getAuthors().getFirst().toBech32String()); - - assertEquals(1, filters.getReferencedEvents().size()); - assertEquals("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", (filters.getReferencedEvents().getFirst().getId())); - } - - @Test - public void testBaseReqMessageDecoder() { - log.info("testBaseReqMessageDecoder"); - - final var filtersList = new ArrayList(); - var publicKey = Identity.generateRandomIdentity().getPublicKey(); - filtersList.add(Filters.builder().authors(new ArrayList<>(List.of(publicKey))).kinds(new ArrayList<>(List.of(Kind.CONTACT_LIST, Kind.DELETION))).build()); - filtersList.add(Filters.builder().kinds(new ArrayList<>(List.of(Kind.SET_METADATA, Kind.TEXT_NOTE))).build()); - final var reqMessage = new ReqMessage(publicKey.toString(), filtersList); - - assertDoesNotThrow(() -> { - String jsonMessage = reqMessage.encode(); - - String jsonMsg = jsonMessage.substring(1, jsonMessage.length() - 1); - String[] parts = jsonMsg.split(","); - assertEquals("\"REQ\"", parts[0]); - assertEquals("\"" + publicKey.toString() + "\"", parts[1]); - assertFalse(parts[2].startsWith("[")); - assertFalse(parts[parts.length - 1].endsWith("]")); - - BaseMessage message = new BaseMessageDecoder<>().decode(jsonMessage); - - assertEquals(reqMessage, message); - }); - } - - @Test - public void testBaseEventMessageDecoder() { - log.info("testBaseEventMessageDecoder"); - - final String parseTarget - = "[\"EVENT\"," - + "{" - + "\"content\":\"直んないわ。まあええか\"," - + "\"created_at\":1686199583," - + "\"id\":\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"," - + "\"kind\":1," - + "\"pubkey\":\"8c59239319637f97e007dad0d681e65ce35b1ace333b629e2d33f9465c132608\"," - + "\"sig\":\"9584afd231c52fcbcec6ce668a2cc4b6dc9b4d9da20510dcb9005c6844679b4844edb7a2e1e0591958b0295241567c774dbf7d39a73932877542de1a5f963f4b\"," - + "\"tags\":[]" - + "}]"; - - final var message = new BaseMessageDecoder<>().decode(parseTarget); - - assertEquals(Command.EVENT.toString(), message.getCommand()); - - final var event = (GenericEvent) (((EventMessage) message).getEvent()); - assertEquals(1, event.getKind().intValue()); - assertEquals(1686199583, event.getCreatedAt().longValue()); - assertEquals("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", event.getId()); - } - - @Test - public void testBaseEventMessageMarkerDecoder() { - log.info("testBaseEventMessageMarkerDecoder"); - - final String json = "[" - + "\"EVENT\"," - + "{" - + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\"," - + "\"kind\":1," - + "\"pubkey\":\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"," - + "\"created_at\":1687765220," - + "\"content\":\"手順書が間違ってたら作業者は無理だな\"," - + "\"tags\":[" - + "[\"e\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346\",\"\",\"root\"]," - + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"]" - + "]," - + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"" - + "}]"; - - BaseMessage message = new BaseMessageDecoder<>().decode(json); - - final var event = (GenericEvent) (((EventMessage) message).getEvent()); - var tags = event.getTags(); - for (BaseTag t : tags) { - if (t.getCode().equalsIgnoreCase("e")) { - EventTag et = (EventTag) t; - assertEquals(Marker.ROOT, et.getMarker()); - } - } - } - - @Test - public void testGenericTagDecoder() { - log.info("testGenericTagDecoder"); - final String jsonString = "[\"saturn\", \"jetpack\", false]"; - - var tag = new GenericTagDecoder<>().decode(jsonString); - - assertEquals("saturn", tag.getCode()); - assertEquals(2, tag.getAttributes().size()); - assertEquals("jetpack", ((ElementAttribute) (tag.getAttributes().toArray())[0]).getValue()); - assertEquals(false, Boolean.valueOf(((ElementAttribute) (tag.getAttributes().toArray())[1]).getValue().toString())); - } - - @Test - public void testClassifiedListingTagSerializer() throws JsonProcessingException { - log.info("testClassifiedListingSerializer"); - final String classifiedListingEventJson = "{" - + "\"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\"" - + "}]"; - - GenericEvent event = new GenericEventDecoder<>().decode(classifiedListingEventJson); - EventMessage message = NIP01.createEventMessage(event, "1"); - assertEquals(1, message.getNip()); - 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()); - assertEquals("content ipsum", event.getContent()); - assertEquals("ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de", event.getPubKey().toString()); - 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(); - - assertEquals("title ipsum", genericTags.stream() - .filter(tag -> tag.getCode().equalsIgnoreCase("title")).map(GenericTag::getAttributes).toList().getFirst().getFirst().getValue()); - - assertEquals("summary ipsum", genericTags.stream() - .filter(tag -> tag.getCode().equalsIgnoreCase("summary")).map(GenericTag::getAttributes).toList().getFirst().getFirst().getValue()); - - assertEquals("1687765220", genericTags.stream() - .filter(tag -> tag.getCode().equalsIgnoreCase("published_at")).map(GenericTag::getAttributes).toList().getFirst().getFirst().getValue()); - - assertEquals("location ipsum", genericTags.stream() - .filter(tag -> tag.getCode().equalsIgnoreCase("location")).map(GenericTag::getAttributes).toList().getFirst().getFirst().getValue()); - } - - @Test - public void testDeserializeTag() { - log.info("testDeserializeTag"); - - assertDoesNotThrow(() -> { - String npubHex = new PublicKey(NostrUtil.hexToBytes(Bech32.fromBech32("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"))).toString(); - - final String jsonString = "[\"p\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]"; - var tag = new BaseTagDecoder<>().decode(jsonString); - - assertInstanceOf(PubKeyTag.class, tag); - - PubKeyTag pTag = (PubKeyTag) tag; - assertEquals("wss://nostr.java", pTag.getMainRelayUrl()); - assertEquals(npubHex, pTag.getPublicKey().toString()); - assertEquals("alice", pTag.getPetName()); - }); - } - - @Test - public void testDeserializeGenericTag() { - log.info("testDeserializeGenericTag"); - assertDoesNotThrow(() -> { - String npubHex = new PublicKey(Bech32.decode("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9").data).toString(); - final String jsonString = "[\"gt\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]"; - var tag = new BaseTagDecoder<>().decode(jsonString); - - assertInstanceOf(GenericTag.class, tag); - - GenericTag gTag = (GenericTag) tag; - assertEquals("gt", gTag.getCode()); - }); - } - - @Test - public void testFiltersEncoder() { - log.info("testFiltersEncoder"); - - String new_geohash = "2vghde"; - List geohashList = new ArrayList<>(); - geohashList.add(new_geohash); - Filters filters = Filters.builder().genericTagQuery(Map.of("#g", geohashList)).build(); - - FiltersEncoder encoder = new FiltersEncoder(filters); - String jsonMessage = encoder.encode(); - assertEquals("{\"#g\":[\"2vghde\"]}", jsonMessage); - } - - @Test - public void testReqMessageFilterListSerializer() { - log.info("testReqMessageFilterListSerializer"); - - String new_geohash = "2vghde"; - String second_geohash = "3abcde"; - List geohashList = new ArrayList<>(); - geohashList.add(new_geohash); - geohashList.add(second_geohash); - Filters filters = Filters.builder().genericTagQuery(Map.of("#g", geohashList)).build(); - - ReqMessage reqMessage = new ReqMessage("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9", new ArrayList<>(List.of(filters))); - assertDoesNotThrow(() -> { - String jsonMessage = reqMessage.encode(); - - assertEquals("[\"REQ\",\"npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9\",{\"#g\":[\"2vghde\",\"3abcde\"]}]", jsonMessage); - }); - } - - @Test - public void testReqMessageFiltersDecoder() { - log.info("testReqMessageFiltersDecoder"); - - String geohashKey = "#g"; - String geohashValue = "2vghde"; - String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue + "\"]}"; - - Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); - - Filters expectedFilters = new Filters(); - List expectedGeohashValueList = List.of(geohashValue); - expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValueList); - - assertEquals(expectedFilters, decodedFilters); - } - - @Test - public void testReqMessageFiltersListDecoder() { - log.info("testReqMessageFiltersListDecoder"); - - String geohashKey = "#g"; - String geohashValue1 = "2vghde"; - String geohashValue2 = "3abcde"; - String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]}"; - - Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); - - Filters expectedFilters = new Filters(); - List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); - expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); - - assertEquals(expectedFilters, decodedFilters); - } - - @Test - public void testReqMessageDeserializer() { - log.info("testReqMessageDeserializer"); - - String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; - String geohashKey = "#g"; - String geohashValue = "2vghde"; - String reqJsonWithCustomTagQueryFilterToDecode = "[\"REQ\",\"" + subscriptionId + "\",{\"" + geohashKey + "\":[\"" + geohashValue + "\"]}]"; - - ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); - - Filters expectedFilters = new Filters(); - List expectedGeohashValuesList = List.of(geohashValue); - expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); - - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, expectedFilters); - assertEquals(expectedReqMessage, decodedReqMessage); - } - - @Test - public void testReqMessageFilterListDecoder() { - log.info("testReqMessageFilterListDecoder"); - - String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; - String geohashKey = "#g"; - String geohashValue1 = "2vghde"; - String geohashValue2 = "3abcde"; - String reqJsonWithCustomTagQueryFiltersToDecode = "[\"REQ\",\"" + subscriptionId + "\",{\"" + geohashKey + "\":[\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]}]"; - - ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFiltersToDecode); - - Filters expectedFilters = new Filters(); - List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); - expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); - - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, expectedFilters); - assertEquals(expectedReqMessage, decodedReqMessage); - } - - @Test - public void testReqMessagePopulatedFilterDecoder() { - log.info("testReqMessagePopulatedFilterDecoder"); - - String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; - String kind = "1"; - String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; - String geohashKey = "#g"; - String geohashValue1 = "2vghde"; - String geohashValue2 = "3abcde"; - String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; - String reqJsonWithCustomTagQueryFilterToDecode = - "[\"REQ\", " + - "\"" + subscriptionId + "\", " + - "{\"kinds\": [" + kind + "], " + - "\"authors\": [\"" + author + "\"]," + - "\"" + geohashKey + "\": [\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]," + - "\"#e\": [\"" + referencedEventId + "\"]}]"; - - ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); - - Filters expectedFilters = new Filters(); - expectedFilters.setKinds(List.of(Kind.TEXT_NOTE)); - expectedFilters.setAuthors(List.of(new PublicKey(author))); - expectedFilters.setReferencedEvents(List.of(new GenericEvent(referencedEventId))); - List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); - expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); - - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, expectedFilters); - assertEquals(expectedReqMessage, decodedReqMessage); - } - - @Test - public void testReqMessagePopulatedListOfFiltersListDecoder() { - log.info("testReqMessagePopulatedListOfFiltersListDecoder"); - - String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; - String kind = "1"; - String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; - String geohashKey = "#g"; - String geohashValue1 = "2vghde"; - String geohashValue2 = "3abcde"; - String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; - String uuidKey = "#d"; - String uuidValue1 = "UUID-1"; - String uuidValue2 = "UUID-2"; - String reqJsonWithCustomTagQueryFilterToDecode = - "[\"REQ\", " + - "\"" + subscriptionId + "\", " + - "{\"kinds\": [" + kind + "], " + - "\"authors\": [\"" + author + "\"]," + - "\"" + geohashKey + "\": [\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]," + - "\"" + uuidKey + "\": [\"" + uuidValue1 + "\",\"" + uuidValue2 + "\"]," + - "\"#e\": [\"" + referencedEventId + "\"]}]"; - - ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); - - Filters expectedFilters = new Filters(); - expectedFilters.setKinds(List.of(Kind.TEXT_NOTE)); - expectedFilters.setAuthors(List.of(new PublicKey(author))); - expectedFilters.setReferencedEvents(List.of(new GenericEvent(referencedEventId))); - List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); - expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); - List expectedIdentityTagValuesList = List.of(uuidValue1, uuidValue2); - expectedFilters.setGenericTagQuery(uuidKey, expectedIdentityTagValuesList); - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, expectedFilters); - assertEquals(expectedReqMessage, decodedReqMessage); - } - - @Test - public void testReqMessageSubscriptionIdLength() { - log.info("testReqMessageSubscriptionIdLength"); - String id65Chars = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9ab"; - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new ReqMessage(id65Chars, new Filters())) - .getMessage().contains("subscriptionId length must be between 1 and 64 characters but was [65]")); - } - - @Test - public void testReqMessageFilterIdLength() { - log.info("testReqMessageFilterIdLength"); - String id64chars = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; - String reqJsonId64Chars = - "[\"REQ\",\"" + "subscriber_id" + "\",{\"ids\":[\"" + id64chars + "\"]}]"; - assertDoesNotThrow(() -> new BaseMessageDecoder().decode(reqJsonId64Chars)); - - String id65chars = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a7123"; - String reqJsonId65Chars = - "[\"REQ\",\"" + "subscriber_id" + "\",{\"ids\":[\"" + id65chars + "\"]}]"; - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder().decode(reqJsonId65Chars)) - .getMessage().contains("[fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a7123], length: [65], target length: [64]")); - - String id63chars = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a71"; - String reqJsonId63chars = - "[\"REQ\",\"" + "subscriber_id" + "\",{\"ids\":[\"" + id63chars + "\"]}]"; - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder().decode(reqJsonId63chars)) - .getMessage().contains("[fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a71], length: [63], target length: [64]")); - } - - @Test - public void testReqMessageDecoderKind() { - log.info("testReqMessageDecoderKind"); - - Function kindTarget = kind -> "[\"REQ\", " + - "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + - "{\"kinds\": [" + kind + "]" + - "}]"; - - assertDoesNotThrow(() -> new BaseMessageDecoder<>().decode(kindTarget.apply(0))); - assertDoesNotThrow(() -> new BaseMessageDecoder<>().decode(kindTarget.apply(65535))); - - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(kindTarget.apply(-1))) - .getMessage().contains("Kind must be between 0 and 65535 but was [-1]")); - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(kindTarget.apply(65536))) - .getMessage().contains("Kind must be between 0 and 65535 but was [65536]")); - } - - @Test - public void testReqMessageDecoderETag() { - log.info("testReqMessageDecoderETag"); - - String VALID_EVENTID = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f"; - String VALID_EVENTID_ALL_ZEROS = "0000000000000000000000000000000000000000000000000000000000000000"; - String VALID_EVENTID_ALL_FF = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; - String INVALID_EVENTID_NON_HEX_DIGITS = "XYZdf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f"; - String INVALID_EVENTID_LENGTH_TOO_SHORT = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6"; - String INVALID_EVENTID_LENGTH_TOO_LONG = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f666"; - String INVALID_EVENTID_HAS_MULTIPLE_UPPERCASE = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; - String INVALID_EVENTID_HAS_SINGLE_UPPERCASE = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6F"; - - Function eTagTarget = eTag -> "[\"REQ\", " + - "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + - "{\"#e\": [\"" + eTag + "\"]}]"; - - assertDoesNotThrow(() -> new BaseMessageDecoder<>().decode(eTagTarget.apply(VALID_EVENTID))); - assertDoesNotThrow(() -> new BaseMessageDecoder<>().decode(eTagTarget.apply(VALID_EVENTID_ALL_ZEROS))); - assertDoesNotThrow(() -> new BaseMessageDecoder<>().decode(eTagTarget.apply(VALID_EVENTID_ALL_FF))); - - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(eTagTarget.apply(INVALID_EVENTID_NON_HEX_DIGITS))) - .getMessage().contains("has non-hex characters")); - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(eTagTarget.apply(INVALID_EVENTID_LENGTH_TOO_SHORT))) - .getMessage().contains("target length")); - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(eTagTarget.apply(INVALID_EVENTID_LENGTH_TOO_LONG))) - .getMessage().contains("target length")); - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(eTagTarget.apply(INVALID_EVENTID_HAS_MULTIPLE_UPPERCASE))) - .getMessage().contains("has uppcase characters")); - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(eTagTarget.apply(INVALID_EVENTID_HAS_SINGLE_UPPERCASE))) - .getMessage().contains("has uppcase characters")); - } - - @Test - public void testReqMessageDecoderPTag() { - log.info("testReqMessageDecoderPTag"); - - String VALID_HEXPUBKEY = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f"; - String VALID_HEXPUBKEY_ALL_ZEROS = "0000000000000000000000000000000000000000000000000000000000000000"; - String VALID_HEXPUBKEY_ALL_FF = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; - String INVALID_HEXPUBKEY_NON_HEX_DIGITS = "XYZdf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f"; - String INVALID_HEXPUBKEY_LENGTH_TOO_SHORT = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6"; - String INVALID_HEXPUBKEY_LENGTH_TOO_LONG = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f666"; - String INVALID_HEXPUBKEY_HAS_MULTIPLE_UPPERCASE = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; - String INVALID_HEXPUBKEY_HAS_SINGLE_UPPERCASE = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6F"; - - Function eTagTarget = pTag -> "[\"REQ\", " + - "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + - "{\"#p\": [\"" + pTag + "\"]}]"; - - assertDoesNotThrow(() -> new BaseMessageDecoder<>().decode(eTagTarget.apply(VALID_HEXPUBKEY))); - assertDoesNotThrow(() -> new BaseMessageDecoder<>().decode(eTagTarget.apply(VALID_HEXPUBKEY_ALL_ZEROS))); - assertDoesNotThrow(() -> new BaseMessageDecoder<>().decode(eTagTarget.apply(VALID_HEXPUBKEY_ALL_FF))); - - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(eTagTarget.apply(INVALID_HEXPUBKEY_NON_HEX_DIGITS))) - .getMessage().contains("has non-hex characters")); - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(eTagTarget.apply(INVALID_HEXPUBKEY_LENGTH_TOO_SHORT))) - .getMessage().contains("target length")); - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(eTagTarget.apply(INVALID_HEXPUBKEY_LENGTH_TOO_LONG))) - .getMessage().contains("target length")); - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(eTagTarget.apply(INVALID_HEXPUBKEY_HAS_MULTIPLE_UPPERCASE))) - .getMessage().contains("has uppcase characters")); - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(eTagTarget.apply(INVALID_HEXPUBKEY_HAS_SINGLE_UPPERCASE))) - .getMessage().contains("has uppcase characters")); - } - - @Test - public void testReqMessageFilterSince() { - log.info("testReqMessageFilterSince"); - Function sinceTarget = since -> "[\"REQ\", " + - "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + - "{\"since\": " + since + "}]"; - - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(sinceTarget.apply(null))) - .getMessage().contains("since is marked non-null but is null")); - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(sinceTarget.apply("-1"))) - .getMessage().contains("'since' filter cannot be negative")); - assertTrue( - assertThrows(JsonMappingException.class, () -> new BaseMessageDecoder<>().decode(sinceTarget.apply("a"))) - .getMessage().contains("Unrecognized token 'a'")); - } - - @Test - public void testReqMessageFilterUntil() { - log.info("testReqMessageFilterUntil"); - Function untilTarget = until -> "[\"REQ\", " + - "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + - "{\"until\": " + until + "}]"; - - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(untilTarget.apply(null))) - .getMessage().contains("until is marked non-null but is null")); - assertTrue( - assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(untilTarget.apply("-1"))) - .getMessage().contains("'until' filter cannot be negative")); - assertTrue( - assertThrows(JsonMappingException.class, () -> new BaseMessageDecoder<>().decode(untilTarget.apply("a"))) - .getMessage().contains("Unrecognized token 'a'")); - } -} +//package nostr.test.json; +// +//import com.fasterxml.jackson.core.JsonProcessingException; +//import lombok.extern.java.Log; +//import nostr.api.NIP01; +//import nostr.base.Command; +//import nostr.base.ElementAttribute; +//import nostr.base.PublicKey; +//import nostr.crypto.bech32.Bech32; +//import nostr.event.BaseEvent; +//import nostr.event.BaseMessage; +//import nostr.event.BaseTag; +//import nostr.event.Kind; +//import nostr.event.Marker; +//import nostr.event.filter.Filterable; +//import nostr.event.filter.Filters; +//import nostr.event.filter.KindFilter; +//import nostr.event.filter.ReferencedEventFilter; +//import nostr.event.filter.ReferencedPublicKeyFilter; +//import nostr.event.impl.GenericEvent; +//import nostr.event.impl.GenericTag; +//import nostr.event.json.codec.BaseEventEncoder; +//import nostr.event.json.codec.BaseMessageDecoder; +//import nostr.event.json.codec.BaseTagDecoder; +//import nostr.event.json.codec.FiltersDecoder; +//import nostr.event.json.codec.FiltersEncoderRxR; +//import nostr.event.json.codec.GenericEventDecoder; +//import nostr.event.json.codec.GenericTagDecoder; +//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 org.junit.jupiter.api.Test; +// +//import java.math.BigDecimal; +//import java.util.ArrayList; +//import java.util.List; +//import java.util.Map; +// +//import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static org.junit.jupiter.api.Assertions.assertFalse; +//import static org.junit.jupiter.api.Assertions.assertTrue; +// +///** +// * @author eric +// */ +//@Log +//public class JsonParseTest { +// +// @Test +// public void testBaseMessageDecoder() { +// log.info("testBaseMessageDecoder"); +// +// final String parseTarget = +// "[\"REQ\", " + +// "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + +// "{\"kinds\": [1], " + +// "\"authors\": [\"f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75\"]," + +// "\"#e\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]"; +// +// final var message = new BaseMessageDecoder<>().decode(parseTarget); +// +// assertEquals(Command.REQ.toString(), message.getCommand()); +// assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", ((ReqMessage) message).getSubscriptionId()); +// assertEquals(1, ((ReqMessage) message).getFiltersList().size()); +// +// var filters = ((ReqMessage) message).getFiltersList().getFirst(); +// +// List kinds = filters.getFilterableByType(KindFilter.filterKey); +// assertEquals(1, kinds.size()); +// assertEquals(Kind.TEXT_NOTE, kinds.getFirst().getFilterCriterion()); +// +// List authors = filters.getFilterableByType(ReferencedPublicKeyFilter.filterKey); +// assertEquals(1, authors.size()); +// assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", +// ((ReferencedPublicKeyFilter)authors.getFirst()).getFilterCriterion().toHexString()); +// +// List refEvents = filters.getFilterableByType(ReferencedEventFilter.filterKey); +// assertEquals(1, refEvents.size()); +// assertEquals("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", ( +// ((ReferencedEventFilter)refEvents.getFirst()).getFilterCriterion() +// .getId())); +// } +// +// @Test +// public void testBaseReqMessageDecoder() { +// log.info("testBaseReqMessageDecoder"); +// +// final var filtersList = new ArrayList(); +// var publicKey = Identity.generateRandomIdentity().getPublicKey(); +// +// filtersList.add(Filters.builder().authors(new ArrayList<>(List.of(publicKey))).kinds(new ArrayList<>(List.of(Kind.CONTACT_LIST, Kind.DELETION))).build()); +// filtersList.add(Filters.builder().kinds(new ArrayList<>(List.of(Kind.SET_METADATA, Kind.TEXT_NOTE))).build()); +// +// final var reqMessage = new ReqMessage(publicKey.toString(), filtersList); +// +// assertDoesNotThrow(() -> { +// String jsonMessage = reqMessage.encode(); +// +// String jsonMsg = jsonMessage.substring(1, jsonMessage.length() - 1); +// String[] parts = jsonMsg.split(","); +// assertEquals("\"REQ\"", parts[0]); +// assertEquals("\"" + publicKey.toString() + "\"", parts[1]); +// assertFalse(parts[2].startsWith("[")); +// assertFalse(parts[parts.length - 1].endsWith("]")); +// +// BaseMessage message = new BaseMessageDecoder<>().decode(jsonMessage); +// +// assertEquals(reqMessage, message); +// }); +// } +// +// @Test +// public void testBaseEventMessageDecoder() { +// log.info("testBaseEventMessageDecoder"); +// +// final String parseTarget +// = "[\"EVENT\"," +// + "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\"," +// + "{" +// + "\"content\":\"直んないわ。まあええか\"," +// + "\"created_at\":1686199583," +// + "\"id\":\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"," +// + "\"kind\":1," +// + "\"pubkey\":\"8c59239319637f97e007dad0d681e65ce35b1ace333b629e2d33f9465c132608\"," +// + "\"sig\":\"9584afd231c52fcbcec6ce668a2cc4b6dc9b4d9da20510dcb9005c6844679b4844edb7a2e1e0591958b0295241567c774dbf7d39a73932877542de1a5f963f4b\"," +// + "\"tags\":[]" +// + "}]"; +// +// final var message = new BaseMessageDecoder<>().decode(parseTarget); +// +// assertEquals(Command.EVENT.toString(), message.getCommand()); +// +// final var event = (GenericEvent) (((EventMessage) message).getEvent()); +// assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", ((EventMessage) message).getSubscriptionId()); +// assertEquals(1, event.getKind().intValue()); +// assertEquals(1686199583, event.getCreatedAt().longValue()); +// assertEquals("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", event.getId()); +// } +// +// @Test +// public void testBaseEventMessageMarkerDecoder() { +// log.info("testBaseEventMessageMarkerDecoder"); +// +// final String json = "[" +// + "\"EVENT\"," +// + "\"temp20230627\"," +// + "{" +// + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\"," +// + "\"kind\":1," +// + "\"pubkey\":\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"," +// + "\"created_at\":1687765220," +// + "\"content\":\"手順書が間違ってたら作業者は無理だな\"," +// + "\"tags\":[" +// + "[\"e\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346\",\"\",\"root\"]," +// + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"]" +// + "]," +// + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"" +// + "}]"; +// +// BaseMessage message = new BaseMessageDecoder<>().decode(json); +// +// final var event = (GenericEvent) (((EventMessage) message).getEvent()); +// var tags = event.getTags(); +// for (BaseTag t : tags) { +// if (t.getCode().equalsIgnoreCase("e")) { +// EventTag et = (EventTag) t; +// assertEquals(Marker.ROOT, et.getMarker()); +// } +// } +// } +// +// @Test +// public void testGenericTagDecoder() { +// log.info("testGenericTagDecoder"); +// final String jsonString = "[\"saturn\", \"jetpack\", false]"; +// +// var tag = new GenericTagDecoder<>().decode(jsonString); +// +// assertEquals("saturn", tag.getCode()); +// assertEquals(2, tag.getAttributes().size()); +// assertEquals("jetpack", ((ElementAttribute) (tag.getAttributes().toArray())[0]).getValue()); +// assertEquals(false, Boolean.valueOf(((ElementAttribute) (tag.getAttributes().toArray())[1]).getValue().toString())); +// } +// +// @Test +// public void testClassifiedListingTagSerializer() { +// log.info("testClassifiedListingSerializer"); +// final String classifiedListingEventJson = "{" +// + "\"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\"" +// + "}]"; +// +// GenericEvent event = new GenericEventDecoder<>().decode(classifiedListingEventJson); +// EventMessage message = NIP01.createEventMessage(event, "1"); +// assertEquals(1, message.getNip()); +// 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()); +// assertEquals("content ipsum", event.getContent()); +// assertEquals("ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de", event.getPubKey().toString()); +// 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(); +// +// assertEquals("title ipsum", genericTags.stream() +// .filter(tag -> tag.getCode().equalsIgnoreCase("title")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue()); +// +// assertEquals("summary ipsum", genericTags.stream() +// .filter(tag -> tag.getCode().equalsIgnoreCase("summary")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue()); +// +// assertEquals("1687765220", genericTags.stream() +// .filter(tag -> tag.getCode().equalsIgnoreCase("published_at")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue()); +// +// assertEquals("location ipsum", genericTags.stream() +// .filter(tag -> tag.getCode().equalsIgnoreCase("location")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue()); +// } +// +// @Test +// public void testDeserializeTag() { +// log.info("testDeserializeTag"); +// +// assertDoesNotThrow(() -> { +// String npubHex = new PublicKey(Bech32.decode("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9").data).toString(); +// final String jsonString = "[\"p\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]"; +// var tag = new BaseTagDecoder<>().decode(jsonString); +// +// assertTrue(tag instanceof PubKeyTag); +// +// PubKeyTag pTag = (PubKeyTag) tag; +// assertEquals("wss://nostr.java", pTag.getMainRelayUrl()); +// assertEquals(npubHex, pTag.getPublicKey().toString()); +// assertEquals("alice", pTag.getPetName()); +// }); +// } +// +// @Test +// public void testDeserializeGenericTag() { +// log.info("testDeserializeGenericTag"); +// assertDoesNotThrow(() -> { +// String npubHex = new PublicKey(Bech32.decode("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9").data).toString(); +// final String jsonString = "[\"gt\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]"; +// var tag = new BaseTagDecoder<>().decode(jsonString); +// +// assertTrue(tag instanceof GenericTag); +// +// GenericTag gTag = (GenericTag) tag; +// assertEquals("gt", gTag.getCode()); +// }); +// } +// +// @Test +// public void testFiltersEncoder() { +// log.info("testFiltersEncoder"); +// +// String new_geohash = "2vghde"; +// List geohashList = new ArrayList<>(); +// geohashList.add(new_geohash); +// Filters filters = Filters.builder().genericTagQuery(Map.of("#g", geohashList)).build(); +// +// FiltersEncoder encoder = new FiltersEncoder(filters); +// String jsonMessage = encoder.encode(); +// assertEquals("{\"#g\":[\"2vghde\"]}", jsonMessage); +// } +// +// @Test +// public void testReqMessageFilterListSerializer() { +// log.info("testReqMessageFilterListSerializer"); +// +// String new_geohash = "2vghde"; +// String second_geohash = "3abcde"; +// List geohashList = new ArrayList<>(); +// geohashList.add(new_geohash); +// geohashList.add(second_geohash); +// Filters filters = Filters.builder().genericTagQuery(Map.of("#g", geohashList)).build(); +// +// ReqMessage reqMessage = new ReqMessage("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9", new ArrayList(List.of(filters))); +// assertDoesNotThrow(() -> { +// String jsonMessage = reqMessage.encode(); +// +// assertEquals("[\"REQ\",\"npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9\",{\"#g\":[\"2vghde\",\"3abcde\"]}]", jsonMessage); +// }); +// } +// +// @Test +// public void testReqMessageFiltersDecoder() { +// log.info("testReqMessageFiltersDecoder"); +// +// String geohashKey = "#g"; +// String geohashValue = "2vghde"; +// String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue + "\"]}"; +// +// Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); +// +// Filters expectedFilters = new Filters(); +// List expectedGeohashValueList = List.of(geohashValue); +// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValueList); +// +// assertEquals(expectedFilters, decodedFilters); +// } +// +// @Test +// public void testReqMessageFiltersListDecoder() { +// log.info("testReqMessageFiltersListDecoder"); +// +// String geohashKey = "#g"; +// String geohashValue1 = "2vghde"; +// String geohashValue2 = "3abcde"; +// String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]}"; +// +// Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); +// +// Filters expectedFilters = new Filters(); +// List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); +// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); +// +// assertEquals(expectedFilters, decodedFilters); +// } +// +// @Test +// public void testReqMessageDeserializer() { +// log.info("testReqMessageDeserializer"); +// +// String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; +// String geohashKey = "#g"; +// String geohashValue = "2vghde"; +// String reqJsonWithCustomTagQueryFilterToDecode = "[\"REQ\",\"" + subscriptionId + "\",{\"" + geohashKey + "\":[\"" + geohashValue + "\"]}]"; +// +// ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); +// +// Filters expectedFilters = new Filters(); +// List expectedGeohashValuesList = List.of(geohashValue); +// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); +// +// ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, expectedFilters); +// assertEquals(expectedReqMessage, decodedReqMessage); +// } +// +// @Test +// public void testReqMessageFilterListDecoder() { +// log.info("testReqMessageFilterListDecoder"); +// +// String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; +// String geohashKey = "#g"; +// String geohashValue1 = "2vghde"; +// String geohashValue2 = "3abcde"; +// String reqJsonWithCustomTagQueryFiltersToDecode = "[\"REQ\",\"" + subscriptionId + "\",{\"" + geohashKey + "\":[\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]}]"; +// +// ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFiltersToDecode); +// +// Filters expectedFilters = new Filters(); +// List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); +// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); +// +// ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, expectedFilters); +// assertEquals(expectedReqMessage, decodedReqMessage); +// } +// +// @Test +// public void testReqMessagePopulatedFilterDecoder() { +// log.info("testReqMessagePopulatedFilterDecoder"); +// +// String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; +// String kind = "1"; +// String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; +// String geohashKey = "#g"; +// String geohashValue1 = "2vghde"; +// String geohashValue2 = "3abcde"; +// String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; +// String reqJsonWithCustomTagQueryFilterToDecode = +// "[\"REQ\", " + +// "\"" + subscriptionId + "\", " + +// "{\"kinds\": [" + kind + "], " + +// "\"authors\": [\"" + author + "\"]," + +// "\"" + geohashKey + "\": [\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]," + +// "\"#e\": [\"" + referencedEventId + "\"]}]"; +// +// ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); +// +// Filters expectedFilters = new Filters(); +// expectedFilters.setKinds(List.of(Kind.TEXT_NOTE)); +// expectedFilters.setAuthors(List.of(new PublicKey(author))); +// expectedFilters.setReferencedEvents(List.of(new GenericEvent(referencedEventId))); +// List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); +// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); +// +// ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, expectedFilters); +// assertEquals(expectedReqMessage, decodedReqMessage); +// } +// +// @Test +// public void utestReqMessagePopulatedListOfFiltersListDecoder() throws JsonProcessingException { +// log.info("testReqMessagePopulatedListOfFiltersListDecoder"); +// +// String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; +// String kind = "1"; +// String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; +// String geohashKey = "#g"; +// String geohashValue1 = "2vghde"; +// String geohashValue2 = "3abcde"; +// String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; +// String uuidKey = "#d"; +// String uuidValue1 = "UUID-1"; +// String uuidValue2 = "UUID-2"; +// String reqJsonWithCustomTagQueryFilterToDecode = +// "[\"REQ\", " + +// "\"" + subscriptionId + "\", " + +// "{\"kinds\": [" + kind + "], " + +// "\"authors\": [\"" + author + "\"]," + +// "\"" + geohashKey + "\": [\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]," + +// "\"" + uuidKey + "\": [\"" + uuidValue1 + "\",\"" + uuidValue2 + "\"]," + +// "\"#e\": [\"" + referencedEventId + "\"]}]"; +// +// ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); +// +// Filters expectedFilters = new Filters(); +// expectedFilters.setKinds(List.of(Kind.TEXT_NOTE)); +// expectedFilters.setAuthors(List.of(new PublicKey(author))); +// expectedFilters.setReferencedEvents(List.of(new GenericEvent(referencedEventId))); +// List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); +// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); +// List expectedIdentityTagValuesList = List.of(uuidValue1, uuidValue2); +// expectedFilters.setGenericTagQuery(uuidKey, expectedIdentityTagValuesList); +// ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, expectedFilters); +// +// System.out.println("00000000000000000"); +// System.out.println("00000000000000000"); +// System.out.println(expectedReqMessage.encode()); +// System.out.println("------------"); +// System.out.println(decodedReqMessage.encode()); +// System.out.println("00000000000000000"); +// System.out.println("00000000000000000"); +// +// assertEquals(expectedReqMessage, decodedReqMessage); +// } +//} From 595ac713ecfc335cfe4087b1f693c434b00bef1a Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sat, 15 Feb 2025 17:39:52 -0800 Subject: [PATCH 18/45] added identifier tag filter tests --- .../event/filter/FilterableProvider.java | 3 +- .../main/java/nostr/event/filter/Filters.java | 2 + .../event/filter/IdentifierTagFilter.java | 9 +++- .../event/json/codec/BaseMessageDecoder.java | 7 ++-- .../event/json/codec/FiltersDecoder.java | 33 +++++++++------ .../event/json/codec/FiltersEncoder.java | 6 +-- .../java/nostr/event/message/ReqMessage.java | 42 ++++++------------- 7 files changed, 51 insertions(+), 51 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java b/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java index 52f2eba22..4bbf25f01 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java @@ -5,6 +5,7 @@ import nostr.base.PublicKey; import nostr.event.Kind; import nostr.event.impl.GenericEvent; +import nostr.event.tag.IdentifierTag; import java.util.List; @@ -13,6 +14,7 @@ public static List getFilterable(String type, JsonNode node) { return switch (type) { case ReferencedPublicKeyFilter.filterKey -> Filters.getFilterable(node, referencedPubKey -> new ReferencedPublicKeyFilter<>(new PublicKey(referencedPubKey.asText()))); case ReferencedEventFilter.filterKey -> Filters.getFilterable(node, referencedEvent -> new ReferencedEventFilter<>(new GenericEvent(referencedEvent.asText()))); + case IdentifierTagFilter.filterKey -> Filters.getFilterable(node, identifierTag -> new IdentifierTagFilter<>(new IdentifierTag(identifierTag.asText()))); case AuthorFilter.filterKey -> Filters.getFilterable(node, author -> new AuthorFilter<>(new PublicKey(author.asText()))); case EventFilter.filterKey -> Filters.getFilterable(node, event -> new EventFilter<>(new GenericEvent(event.asText()))); case SinceFilter.filterKey -> Filters.getFilterable(node, since -> new SinceFilter(since.asLong())); @@ -20,7 +22,6 @@ public static List getFilterable(String type, JsonNode node) { case KindFilter.filterKey -> Filters.getFilterable(node, kindNode -> new KindFilter<>(Kind.valueOf(kindNode.asInt()))); // TODO: complete & test below // case AddressableTagFilter.filterKey -> new XYZ<>(getGenericTagQuery(node)); -// case IdentifierTagFilter.filterKey -> new XYZ<>(getGenericTagQuery(node)); default -> Filters.getFilterable(node, genericNode -> new GenericTagQueryFilter<>(new GenericTagQuery(type, genericNode.asText()))); }; } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java index 3ee30be8b..77a996e18 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java @@ -1,6 +1,7 @@ package nostr.event.filter; import com.fasterxml.jackson.databind.JsonNode; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; import lombok.Setter; @@ -13,6 +14,7 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; +@EqualsAndHashCode public class Filters { @Getter private final Map> filtersMap; diff --git a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java index 6dd31693f..f54be59de 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java @@ -1,10 +1,13 @@ package nostr.event.filter; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; import nostr.event.tag.IdentifierTag; import java.util.function.Predicate; +@EqualsAndHashCode public class IdentifierTagFilter implements Filterable { public final static String filterKey = "#d"; private final T identifierTag; @@ -12,20 +15,22 @@ public class IdentifierTagFilter implements Filterable public IdentifierTagFilter(T identifierTag) { this.identifierTag = identifierTag; } + @Override public Predicate getPredicate() { return (genericEvent) -> getTypeSpecificTags(IdentifierTag.class, genericEvent).stream().anyMatch(genericEventIdentifiterTag -> genericEventIdentifiterTag.getId().equals(this.identifierTag.getId())); } + @Override public T getFilterCriterion() { return identifierTag; } @Override - public String toJson() { - return identifierTag.getId(); + public ArrayNode toArrayNode() { + return mapper.createArrayNode().add(identifierTag.getId()); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java index 701c05570..e89ab1e57 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java @@ -1,6 +1,7 @@ package nostr.event.json.codec; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.NonNull; @@ -31,9 +32,8 @@ public BaseMessageDecoder() { mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); } - @SneakyThrows @Override - public T decode(@NonNull String jsonString) { + public T decode(@NonNull String jsonString) throws JsonProcessingException { ValidJsonNode validJsonNode = validateJson(jsonString); String command = validJsonNode.formerly_strCmd(); Object subscriptionId = validJsonNode.formerly_arg(); // subscriptionId @@ -55,8 +55,7 @@ public T decode(@NonNull String jsonString) { }; } - @SneakyThrows - private ValidJsonNode validateJson(@NonNull String jsonString) { + private ValidJsonNode validateJson(@NonNull String jsonString) throws JsonProcessingException { final JsonNode jsonNode = mapper.readTree(jsonString); if (jsonNode.size() > MAX_JSON_NODE_THRESHOLD) diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java index c1c6b850d..8a6f790c6 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java @@ -1,26 +1,35 @@ package nostr.event.json.codec; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import lombok.NonNull; +import lombok.SneakyThrows; +import nostr.event.filter.Filterable; +import nostr.event.filter.FilterableProvider; import nostr.event.filter.Filters; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** - * * @author eric */ @Data public class FiltersDecoder implements FDecoder { - private final Class clazz = (Class)Filters.class; + private final static ObjectMapper mapper = new ObjectMapper(); + + @SneakyThrows + public T decode(@NonNull String jsonFiltersList) { + final Map> filterPluginsMap = new HashMap<>(); + + mapper.readTree(jsonFiltersList).fields().forEachRemaining(field -> + filterPluginsMap.put( + field.getKey(), + FilterableProvider.getFilterable( + field.getKey(), + field.getValue()))); - @Override - public T decode(@NonNull String jsonString) { - try { - ObjectMapper mapper = new ObjectMapper(); - return mapper.readValue(jsonString, clazz); - } catch (JsonProcessingException ex) { - throw new RuntimeException(ex); - } - } + return (T) new Filters(filterPluginsMap); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java index 09bdd3246..16ed9c321 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java @@ -1,6 +1,5 @@ package nostr.event.json.codec; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import lombok.Data; import lombok.EqualsAndHashCode; @@ -25,13 +24,14 @@ public FiltersEncoder(Filters filters) { @Override public String encode() { Map result = new HashMap<>(); + filters.getFiltersMap().forEach((key, value) -> value.stream().distinct() .map(Filterable::toArrayNode) .reduce(ArrayNode::addAll) .ifPresent(arrayNode -> result.put(key, arrayNode))); - JsonNode jsonNode = MAPPER.valueToTree(result); - return MAPPER.writeValueAsString(jsonNode); + + return MAPPER.writeValueAsString(MAPPER.valueToTree(result)); } } diff --git a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java index 8f8076d1d..cd537e45c 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java @@ -3,36 +3,28 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; -import lombok.SneakyThrows; import lombok.ToString; import nostr.base.Command; import nostr.base.IEncoder; import nostr.event.BaseMessage; -import nostr.event.filter.Filterable; -import nostr.event.filter.FilterableProvider; import nostr.event.filter.Filters; +import nostr.event.json.codec.FiltersDecoder; import nostr.event.json.codec.FiltersEncoder; -import java.util.HashMap; import java.time.temporal.ValueRange; -import java.util.ArrayList; import java.util.List; -import java.util.Map; /** - * * @author squirrel */ @Getter @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) public class ReqMessage extends BaseMessage { - private final static ObjectMapper mapper = new ObjectMapper(); - @JsonProperty private final String subscriptionId; @@ -45,14 +37,13 @@ public ReqMessage(String subscriptionId, Filters... filtersList) { public ReqMessage(String subscriptionId, List filtersList) { super(Command.REQ.name()); - if (!ValueRange.of(1, 64).isValidIntValue(subscriptionId.length())) { - throw new IllegalArgumentException(String.format("subscriptionId length must be between 1 and 64 characters but was [%d]", subscriptionId.length())); - } + if (!ValueRange.of(1, 64).isValidIntValue(subscriptionId.length())) { + throw new IllegalArgumentException(String.format("subscriptionId length must be between 1 and 64 characters but was [%d]", subscriptionId.length())); + } this.filtersList = filtersList; this.subscriptionId = subscriptionId; } - @SneakyThrows @Override public String encode() throws JsonProcessingException { getArrayNode() @@ -60,13 +51,13 @@ public String encode() throws JsonProcessingException { .add(getSubscriptionId()); // filtersList.stream() -// .map(FiltersEncoderRxR::new) -// .map(FiltersEncoderRxR::encode) +// .map(FiltersEncoder::new) +// .map(FiltersEncoder::encode) // .map(IEncoder.MAPPER::readTree) // .forEach(jsonNode -> // getArrayNode().add(jsonNode)); - +// TODO: remove below once above confirmed working List encodedFilterList = filtersList.stream().map(FiltersEncoder::new).map(FiltersEncoder::encode).toList(); List jsonNodesList = encodedFilterList.stream().map( @@ -79,7 +70,10 @@ public String encode() throws JsonProcessingException { } ).toList(); - jsonNodesList.forEach(jsonNode -> getArrayNode().add(jsonNode)); + jsonNodesList.forEach(jsonNode -> { + ArrayNode arrayNode = getArrayNode(); + arrayNode.add(jsonNode); + }); return IEncoder.MAPPER.writeValueAsString(getArrayNode()); } @@ -90,17 +84,7 @@ public static T decode(@NonNull Object subscriptionId, @ ReqMessage::createFiltersFromJson).toList()); } - @SneakyThrows private static Filters createFiltersFromJson(String jsonFiltersList) { - final Map> filterPluginsMap = new HashMap<>(); - - mapper.readTree(jsonFiltersList).fields().forEachRemaining(field -> - filterPluginsMap.put( - field.getKey(), - FilterableProvider.getFilterable( - field.getKey(), - field.getValue()))); - - return new Filters(filterPluginsMap); + return new FiltersDecoder<>().decode(jsonFiltersList); } } From 77a7f91bf068aa16b03eb4e87a1b95292b9edae3 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Sat, 15 Feb 2025 19:20:28 -0800 Subject: [PATCH 19/45] addressable tag filters tests --- .../event/filter/AddressableTagFilter.java | 30 +- .../event/filter/FilterableProvider.java | 3 +- .../java/nostr/test/json/JsonParseTest.java | 1209 ++++++++++------- 3 files changed, 763 insertions(+), 479 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java index 7a5e97756..56cae233a 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java @@ -1,14 +1,21 @@ package nostr.event.filter; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.EqualsAndHashCode; import lombok.NonNull; +import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; import nostr.event.tag.AddressTag; import nostr.event.tag.IdentifierTag; +import java.util.Arrays; +import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +@EqualsAndHashCode public class AddressableTagFilter implements Filterable { public final static String filterKey = "#a"; private final T addressableTag; @@ -40,17 +47,32 @@ public T getFilterCriterion() { } @Override - public String toJson() { + public ArrayNode toArrayNode() { Integer kind = addressableTag.getKind(); String hexString = addressableTag.getPublicKey().toHexString(); String id = addressableTag.getIdentifierTag().getId(); - String uri = addressableTag.getRelay().getUri(); +// String uri = addressableTag.getRelay().getUri(); - return Stream.of(kind, hexString, id, uri).map(Object::toString) - .collect(Collectors.joining(",")); + String collected = Stream.of(kind, hexString, id).map(Object::toString) + .collect(Collectors.joining(":")); + + return mapper.createArrayNode().add(collected); } + @Override public String getFilterKey() { return filterKey; } + + public static AddressTag createAddressTag(JsonNode addressableTag) { + List list = Arrays.stream(addressableTag.asText().split(":")).toList(); + +// TODO: add validation + AddressTag addressTag = new AddressTag(); + addressTag.setKind(Integer.valueOf(list.getFirst())); + addressTag.setPublicKey(new PublicKey(list.get(1))); + addressTag.setIdentifierTag(new IdentifierTag(list.get(2))); + + return addressTag; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java b/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java index 4bbf25f01..e3497b062 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java @@ -14,14 +14,13 @@ public static List getFilterable(String type, JsonNode node) { return switch (type) { case ReferencedPublicKeyFilter.filterKey -> Filters.getFilterable(node, referencedPubKey -> new ReferencedPublicKeyFilter<>(new PublicKey(referencedPubKey.asText()))); case ReferencedEventFilter.filterKey -> Filters.getFilterable(node, referencedEvent -> new ReferencedEventFilter<>(new GenericEvent(referencedEvent.asText()))); + case AddressableTagFilter.filterKey -> Filters.getFilterable(node, addressableTag -> new AddressableTagFilter<>(AddressableTagFilter.createAddressTag(addressableTag))); case IdentifierTagFilter.filterKey -> Filters.getFilterable(node, identifierTag -> new IdentifierTagFilter<>(new IdentifierTag(identifierTag.asText()))); case AuthorFilter.filterKey -> Filters.getFilterable(node, author -> new AuthorFilter<>(new PublicKey(author.asText()))); case EventFilter.filterKey -> Filters.getFilterable(node, event -> new EventFilter<>(new GenericEvent(event.asText()))); case SinceFilter.filterKey -> Filters.getFilterable(node, since -> new SinceFilter(since.asLong())); case UntilFilter.filterKey -> Filters.getFilterable(node, until -> new UntilFilter(until.asLong())); case KindFilter.filterKey -> Filters.getFilterable(node, kindNode -> new KindFilter<>(Kind.valueOf(kindNode.asInt()))); -// TODO: complete & test below -// case AddressableTagFilter.filterKey -> new XYZ<>(getGenericTagQuery(node)); default -> Filters.getFilterable(node, genericNode -> new GenericTagQueryFilter<>(new GenericTagQuery(type, genericNode.asText()))); }; } 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 6596e9583..b462dfef9 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 @@ -1,473 +1,736 @@ -//package nostr.test.json; -// -//import com.fasterxml.jackson.core.JsonProcessingException; -//import lombok.extern.java.Log; -//import nostr.api.NIP01; -//import nostr.base.Command; -//import nostr.base.ElementAttribute; -//import nostr.base.PublicKey; -//import nostr.crypto.bech32.Bech32; -//import nostr.event.BaseEvent; -//import nostr.event.BaseMessage; -//import nostr.event.BaseTag; -//import nostr.event.Kind; -//import nostr.event.Marker; -//import nostr.event.filter.Filterable; -//import nostr.event.filter.Filters; -//import nostr.event.filter.KindFilter; -//import nostr.event.filter.ReferencedEventFilter; -//import nostr.event.filter.ReferencedPublicKeyFilter; -//import nostr.event.impl.GenericEvent; -//import nostr.event.impl.GenericTag; -//import nostr.event.json.codec.BaseEventEncoder; -//import nostr.event.json.codec.BaseMessageDecoder; -//import nostr.event.json.codec.BaseTagDecoder; -//import nostr.event.json.codec.FiltersDecoder; -//import nostr.event.json.codec.FiltersEncoderRxR; -//import nostr.event.json.codec.GenericEventDecoder; -//import nostr.event.json.codec.GenericTagDecoder; -//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 org.junit.jupiter.api.Test; -// -//import java.math.BigDecimal; -//import java.util.ArrayList; -//import java.util.List; -//import java.util.Map; -// -//import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -//import static org.junit.jupiter.api.Assertions.assertEquals; -//import static org.junit.jupiter.api.Assertions.assertFalse; -//import static org.junit.jupiter.api.Assertions.assertTrue; -// -///** -// * @author eric -// */ -//@Log -//public class JsonParseTest { -// -// @Test -// public void testBaseMessageDecoder() { -// log.info("testBaseMessageDecoder"); -// -// final String parseTarget = -// "[\"REQ\", " + -// "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + -// "{\"kinds\": [1], " + -// "\"authors\": [\"f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75\"]," + -// "\"#e\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]"; -// -// final var message = new BaseMessageDecoder<>().decode(parseTarget); -// -// assertEquals(Command.REQ.toString(), message.getCommand()); -// assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", ((ReqMessage) message).getSubscriptionId()); -// assertEquals(1, ((ReqMessage) message).getFiltersList().size()); -// -// var filters = ((ReqMessage) message).getFiltersList().getFirst(); -// -// List kinds = filters.getFilterableByType(KindFilter.filterKey); -// assertEquals(1, kinds.size()); -// assertEquals(Kind.TEXT_NOTE, kinds.getFirst().getFilterCriterion()); -// -// List authors = filters.getFilterableByType(ReferencedPublicKeyFilter.filterKey); -// assertEquals(1, authors.size()); -// assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", -// ((ReferencedPublicKeyFilter)authors.getFirst()).getFilterCriterion().toHexString()); -// -// List refEvents = filters.getFilterableByType(ReferencedEventFilter.filterKey); -// assertEquals(1, refEvents.size()); -// assertEquals("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", ( -// ((ReferencedEventFilter)refEvents.getFirst()).getFilterCriterion() -// .getId())); -// } -// -// @Test -// public void testBaseReqMessageDecoder() { -// log.info("testBaseReqMessageDecoder"); -// -// final var filtersList = new ArrayList(); -// var publicKey = Identity.generateRandomIdentity().getPublicKey(); -// -// filtersList.add(Filters.builder().authors(new ArrayList<>(List.of(publicKey))).kinds(new ArrayList<>(List.of(Kind.CONTACT_LIST, Kind.DELETION))).build()); -// filtersList.add(Filters.builder().kinds(new ArrayList<>(List.of(Kind.SET_METADATA, Kind.TEXT_NOTE))).build()); -// -// final var reqMessage = new ReqMessage(publicKey.toString(), filtersList); -// -// assertDoesNotThrow(() -> { -// String jsonMessage = reqMessage.encode(); -// -// String jsonMsg = jsonMessage.substring(1, jsonMessage.length() - 1); -// String[] parts = jsonMsg.split(","); -// assertEquals("\"REQ\"", parts[0]); -// assertEquals("\"" + publicKey.toString() + "\"", parts[1]); -// assertFalse(parts[2].startsWith("[")); -// assertFalse(parts[parts.length - 1].endsWith("]")); -// -// BaseMessage message = new BaseMessageDecoder<>().decode(jsonMessage); -// -// assertEquals(reqMessage, message); -// }); -// } -// -// @Test -// public void testBaseEventMessageDecoder() { -// log.info("testBaseEventMessageDecoder"); -// -// final String parseTarget -// = "[\"EVENT\"," -// + "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\"," -// + "{" -// + "\"content\":\"直んないわ。まあええか\"," -// + "\"created_at\":1686199583," -// + "\"id\":\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"," -// + "\"kind\":1," -// + "\"pubkey\":\"8c59239319637f97e007dad0d681e65ce35b1ace333b629e2d33f9465c132608\"," -// + "\"sig\":\"9584afd231c52fcbcec6ce668a2cc4b6dc9b4d9da20510dcb9005c6844679b4844edb7a2e1e0591958b0295241567c774dbf7d39a73932877542de1a5f963f4b\"," -// + "\"tags\":[]" -// + "}]"; -// -// final var message = new BaseMessageDecoder<>().decode(parseTarget); -// -// assertEquals(Command.EVENT.toString(), message.getCommand()); -// -// final var event = (GenericEvent) (((EventMessage) message).getEvent()); -// assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", ((EventMessage) message).getSubscriptionId()); -// assertEquals(1, event.getKind().intValue()); -// assertEquals(1686199583, event.getCreatedAt().longValue()); -// assertEquals("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", event.getId()); -// } -// -// @Test -// public void testBaseEventMessageMarkerDecoder() { -// log.info("testBaseEventMessageMarkerDecoder"); -// -// final String json = "[" -// + "\"EVENT\"," -// + "\"temp20230627\"," -// + "{" -// + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\"," -// + "\"kind\":1," -// + "\"pubkey\":\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"," -// + "\"created_at\":1687765220," -// + "\"content\":\"手順書が間違ってたら作業者は無理だな\"," -// + "\"tags\":[" -// + "[\"e\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346\",\"\",\"root\"]," -// + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"]" -// + "]," -// + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"" -// + "}]"; -// -// BaseMessage message = new BaseMessageDecoder<>().decode(json); -// -// final var event = (GenericEvent) (((EventMessage) message).getEvent()); -// var tags = event.getTags(); -// for (BaseTag t : tags) { -// if (t.getCode().equalsIgnoreCase("e")) { -// EventTag et = (EventTag) t; -// assertEquals(Marker.ROOT, et.getMarker()); -// } -// } -// } -// -// @Test -// public void testGenericTagDecoder() { -// log.info("testGenericTagDecoder"); -// final String jsonString = "[\"saturn\", \"jetpack\", false]"; -// -// var tag = new GenericTagDecoder<>().decode(jsonString); -// -// assertEquals("saturn", tag.getCode()); -// assertEquals(2, tag.getAttributes().size()); -// assertEquals("jetpack", ((ElementAttribute) (tag.getAttributes().toArray())[0]).getValue()); -// assertEquals(false, Boolean.valueOf(((ElementAttribute) (tag.getAttributes().toArray())[1]).getValue().toString())); -// } -// -// @Test -// public void testClassifiedListingTagSerializer() { -// log.info("testClassifiedListingSerializer"); -// final String classifiedListingEventJson = "{" -// + "\"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\"" -// + "}]"; -// -// GenericEvent event = new GenericEventDecoder<>().decode(classifiedListingEventJson); -// EventMessage message = NIP01.createEventMessage(event, "1"); -// assertEquals(1, message.getNip()); -// 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()); -// assertEquals("content ipsum", event.getContent()); -// assertEquals("ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de", event.getPubKey().toString()); -// 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(); -// -// assertEquals("title ipsum", genericTags.stream() -// .filter(tag -> tag.getCode().equalsIgnoreCase("title")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue()); -// -// assertEquals("summary ipsum", genericTags.stream() -// .filter(tag -> tag.getCode().equalsIgnoreCase("summary")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue()); -// -// assertEquals("1687765220", genericTags.stream() -// .filter(tag -> tag.getCode().equalsIgnoreCase("published_at")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue()); -// -// assertEquals("location ipsum", genericTags.stream() -// .filter(tag -> tag.getCode().equalsIgnoreCase("location")).map(GenericTag::getAttributes).toList().get(0).get(0).getValue()); -// } -// -// @Test -// public void testDeserializeTag() { -// log.info("testDeserializeTag"); -// -// assertDoesNotThrow(() -> { -// String npubHex = new PublicKey(Bech32.decode("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9").data).toString(); -// final String jsonString = "[\"p\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]"; -// var tag = new BaseTagDecoder<>().decode(jsonString); -// -// assertTrue(tag instanceof PubKeyTag); -// -// PubKeyTag pTag = (PubKeyTag) tag; -// assertEquals("wss://nostr.java", pTag.getMainRelayUrl()); -// assertEquals(npubHex, pTag.getPublicKey().toString()); -// assertEquals("alice", pTag.getPetName()); -// }); -// } -// -// @Test -// public void testDeserializeGenericTag() { -// log.info("testDeserializeGenericTag"); -// assertDoesNotThrow(() -> { -// String npubHex = new PublicKey(Bech32.decode("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9").data).toString(); -// final String jsonString = "[\"gt\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]"; -// var tag = new BaseTagDecoder<>().decode(jsonString); -// -// assertTrue(tag instanceof GenericTag); -// -// GenericTag gTag = (GenericTag) tag; -// assertEquals("gt", gTag.getCode()); -// }); -// } -// -// @Test -// public void testFiltersEncoder() { -// log.info("testFiltersEncoder"); -// -// String new_geohash = "2vghde"; -// List geohashList = new ArrayList<>(); -// geohashList.add(new_geohash); -// Filters filters = Filters.builder().genericTagQuery(Map.of("#g", geohashList)).build(); -// -// FiltersEncoder encoder = new FiltersEncoder(filters); -// String jsonMessage = encoder.encode(); -// assertEquals("{\"#g\":[\"2vghde\"]}", jsonMessage); -// } -// -// @Test -// public void testReqMessageFilterListSerializer() { -// log.info("testReqMessageFilterListSerializer"); -// -// String new_geohash = "2vghde"; -// String second_geohash = "3abcde"; -// List geohashList = new ArrayList<>(); -// geohashList.add(new_geohash); -// geohashList.add(second_geohash); -// Filters filters = Filters.builder().genericTagQuery(Map.of("#g", geohashList)).build(); -// -// ReqMessage reqMessage = new ReqMessage("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9", new ArrayList(List.of(filters))); -// assertDoesNotThrow(() -> { -// String jsonMessage = reqMessage.encode(); -// -// assertEquals("[\"REQ\",\"npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9\",{\"#g\":[\"2vghde\",\"3abcde\"]}]", jsonMessage); -// }); -// } -// -// @Test -// public void testReqMessageFiltersDecoder() { -// log.info("testReqMessageFiltersDecoder"); -// -// String geohashKey = "#g"; -// String geohashValue = "2vghde"; -// String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue + "\"]}"; -// -// Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); -// -// Filters expectedFilters = new Filters(); -// List expectedGeohashValueList = List.of(geohashValue); -// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValueList); -// -// assertEquals(expectedFilters, decodedFilters); -// } -// -// @Test -// public void testReqMessageFiltersListDecoder() { -// log.info("testReqMessageFiltersListDecoder"); -// -// String geohashKey = "#g"; -// String geohashValue1 = "2vghde"; -// String geohashValue2 = "3abcde"; -// String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]}"; -// -// Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); -// -// Filters expectedFilters = new Filters(); -// List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); -// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); -// -// assertEquals(expectedFilters, decodedFilters); -// } -// -// @Test -// public void testReqMessageDeserializer() { -// log.info("testReqMessageDeserializer"); -// -// String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; -// String geohashKey = "#g"; -// String geohashValue = "2vghde"; -// String reqJsonWithCustomTagQueryFilterToDecode = "[\"REQ\",\"" + subscriptionId + "\",{\"" + geohashKey + "\":[\"" + geohashValue + "\"]}]"; -// -// ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); -// -// Filters expectedFilters = new Filters(); -// List expectedGeohashValuesList = List.of(geohashValue); -// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); -// -// ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, expectedFilters); -// assertEquals(expectedReqMessage, decodedReqMessage); -// } -// -// @Test -// public void testReqMessageFilterListDecoder() { -// log.info("testReqMessageFilterListDecoder"); -// -// String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; -// String geohashKey = "#g"; -// String geohashValue1 = "2vghde"; -// String geohashValue2 = "3abcde"; -// String reqJsonWithCustomTagQueryFiltersToDecode = "[\"REQ\",\"" + subscriptionId + "\",{\"" + geohashKey + "\":[\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]}]"; -// -// ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFiltersToDecode); -// -// Filters expectedFilters = new Filters(); -// List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); -// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); -// -// ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, expectedFilters); -// assertEquals(expectedReqMessage, decodedReqMessage); -// } -// -// @Test -// public void testReqMessagePopulatedFilterDecoder() { -// log.info("testReqMessagePopulatedFilterDecoder"); -// -// String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; -// String kind = "1"; -// String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; -// String geohashKey = "#g"; -// String geohashValue1 = "2vghde"; -// String geohashValue2 = "3abcde"; -// String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; -// String reqJsonWithCustomTagQueryFilterToDecode = -// "[\"REQ\", " + -// "\"" + subscriptionId + "\", " + -// "{\"kinds\": [" + kind + "], " + -// "\"authors\": [\"" + author + "\"]," + -// "\"" + geohashKey + "\": [\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]," + -// "\"#e\": [\"" + referencedEventId + "\"]}]"; -// -// ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); -// -// Filters expectedFilters = new Filters(); -// expectedFilters.setKinds(List.of(Kind.TEXT_NOTE)); -// expectedFilters.setAuthors(List.of(new PublicKey(author))); -// expectedFilters.setReferencedEvents(List.of(new GenericEvent(referencedEventId))); -// List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); -// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); -// -// ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, expectedFilters); -// assertEquals(expectedReqMessage, decodedReqMessage); -// } -// -// @Test -// public void utestReqMessagePopulatedListOfFiltersListDecoder() throws JsonProcessingException { -// log.info("testReqMessagePopulatedListOfFiltersListDecoder"); -// -// String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; -// String kind = "1"; -// String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; -// String geohashKey = "#g"; -// String geohashValue1 = "2vghde"; -// String geohashValue2 = "3abcde"; -// String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; -// String uuidKey = "#d"; -// String uuidValue1 = "UUID-1"; -// String uuidValue2 = "UUID-2"; -// String reqJsonWithCustomTagQueryFilterToDecode = -// "[\"REQ\", " + -// "\"" + subscriptionId + "\", " + -// "{\"kinds\": [" + kind + "], " + -// "\"authors\": [\"" + author + "\"]," + -// "\"" + geohashKey + "\": [\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]," + -// "\"" + uuidKey + "\": [\"" + uuidValue1 + "\",\"" + uuidValue2 + "\"]," + -// "\"#e\": [\"" + referencedEventId + "\"]}]"; -// -// ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); -// -// Filters expectedFilters = new Filters(); -// expectedFilters.setKinds(List.of(Kind.TEXT_NOTE)); -// expectedFilters.setAuthors(List.of(new PublicKey(author))); -// expectedFilters.setReferencedEvents(List.of(new GenericEvent(referencedEventId))); -// List expectedGeohashValuesList = List.of(geohashValue1, geohashValue2); -// expectedFilters.setGenericTagQuery(geohashKey, expectedGeohashValuesList); -// List expectedIdentityTagValuesList = List.of(uuidValue1, uuidValue2); -// expectedFilters.setGenericTagQuery(uuidKey, expectedIdentityTagValuesList); -// ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, expectedFilters); -// -// System.out.println("00000000000000000"); -// System.out.println("00000000000000000"); -// System.out.println(expectedReqMessage.encode()); -// System.out.println("------------"); -// System.out.println(decodedReqMessage.encode()); -// System.out.println("00000000000000000"); -// System.out.println("00000000000000000"); -// -// assertEquals(expectedReqMessage, decodedReqMessage); -// } -//} +package nostr.test.json; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.java.Log; +import nostr.api.NIP01; +import nostr.base.Command; +import nostr.base.ElementAttribute; +import nostr.base.GenericTagQuery; +import nostr.base.PublicKey; +import nostr.crypto.bech32.Bech32; +import nostr.event.BaseEvent; +import nostr.event.BaseMessage; +import nostr.event.BaseTag; +import nostr.event.Kind; +import nostr.event.Marker; +import nostr.event.filter.AddressableTagFilter; +import nostr.event.filter.AuthorFilter; +import nostr.event.filter.Filterable; +import nostr.event.filter.Filters; +import nostr.event.filter.GenericTagQueryFilter; +import nostr.event.filter.IdentifierTagFilter; +import nostr.event.filter.KindFilter; +import nostr.event.filter.ReferencedEventFilter; +import nostr.event.impl.GenericEvent; +import nostr.event.impl.GenericTag; +import nostr.event.json.codec.BaseEventEncoder; +import nostr.event.json.codec.BaseMessageDecoder; +import nostr.event.json.codec.BaseTagDecoder; +import nostr.event.json.codec.FiltersDecoder; +import nostr.event.json.codec.FiltersEncoder; +import nostr.event.json.codec.GenericEventDecoder; +import nostr.event.json.codec.GenericTagDecoder; +import nostr.event.message.EventMessage; +import nostr.event.message.ReqMessage; +import nostr.event.tag.AddressTag; +import nostr.event.tag.EventTag; +import nostr.event.tag.IdentifierTag; +import nostr.event.tag.PriceTag; +import nostr.event.tag.PubKeyTag; +import nostr.id.Identity; +import nostr.test.util.JsonComparator; +import nostr.util.NostrException; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author eric + */ +@Log +public class JsonParseTest { + ObjectMapper mapper = new ObjectMapper(); + + @Test + public void testBaseMessageDecoder() throws JsonProcessingException { + log.info("testBaseMessageDecoder"); + + final String parseTarget = + "[\"REQ\", " + + "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + + "{\"kinds\": [1], " + + "\"authors\": [\"f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75\"]," + + "\"#e\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]"; + + final var message = new BaseMessageDecoder<>().decode(parseTarget); + + assertEquals(Command.REQ.toString(), message.getCommand()); + assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", ((ReqMessage) message).getSubscriptionId()); + assertEquals(1, ((ReqMessage) message).getFiltersList().size()); + + Filters filters = ((ReqMessage) message).getFiltersList().getFirst(); + + List kindFilters = filters.getFilterableByType(KindFilter.filterKey); + assertEquals(1, kindFilters.size()); + assertEquals(new KindFilter<>(Kind.TEXT_NOTE), kindFilters.getFirst()); + + List authorFilters = filters.getFilterableByType(AuthorFilter.filterKey); + assertEquals(1, authorFilters.size()); + assertEquals(new AuthorFilter<>(new PublicKey("f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75")), authorFilters.getFirst()); + + List referencedEventFilters = filters.getFilterableByType(ReferencedEventFilter.filterKey); + assertEquals(1, referencedEventFilters.size()); + assertEquals(new ReferencedEventFilter<>(new GenericEvent("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712")), referencedEventFilters.getFirst()); + } + + @Test + public void testBaseReqMessageDecoder() throws JsonProcessingException { + log.info("testBaseReqMessageDecoder"); + + var publicKey = Identity.generateRandomIdentity().getPublicKey(); + + Map> expectedFilters = new HashMap<>(); + + expectedFilters.put(AuthorFilter.filterKey, + List.of( + new AuthorFilter<>(publicKey))); + + expectedFilters.put(KindFilter.filterKey, + List.of( + new KindFilter<>(Kind.CONTACT_LIST), + new KindFilter<>(Kind.DELETION))); + + expectedFilters.put(KindFilter.filterKey, + List.of( + new KindFilter<>(Kind.SET_METADATA), + new KindFilter<>(Kind.TEXT_NOTE))); + + final var expectedReqMessage = new ReqMessage(publicKey.toString(), new Filters(expectedFilters)); + + String jsonMessage = expectedReqMessage.encode(); + + String jsonMsg = jsonMessage.substring(1, jsonMessage.length() - 1); + String[] parts = jsonMsg.split(","); + assertEquals("\"REQ\"", parts[0]); + assertEquals("\"" + publicKey.toHexString() + "\"", parts[1]); + assertFalse(parts[2].startsWith("[")); + assertFalse(parts[parts.length - 1].endsWith("]")); + + ReqMessage actualMessage = new BaseMessageDecoder().decode(jsonMessage); + + assertEquals(jsonMessage, actualMessage.encode()); + assertEquals(expectedReqMessage, actualMessage); + } + + @Test + public void testBaseEventMessageDecoder() throws JsonProcessingException { + log.info("testBaseEventMessageDecoder"); + + final String parseTarget + = "[\"EVENT\"," + + "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\"," + + "{" + + "\"content\":\"直んないわ。まあええか\"," + + "\"created_at\":1686199583," + + "\"id\":\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"," + + "\"kind\":1," + + "\"pubkey\":\"8c59239319637f97e007dad0d681e65ce35b1ace333b629e2d33f9465c132608\"," + + "\"sig\":\"9584afd231c52fcbcec6ce668a2cc4b6dc9b4d9da20510dcb9005c6844679b4844edb7a2e1e0591958b0295241567c774dbf7d39a73932877542de1a5f963f4b\"," + + "\"tags\":[]" + + "}]"; + + final var message = new BaseMessageDecoder<>().decode(parseTarget); + + assertEquals(Command.EVENT.toString(), message.getCommand()); + + final var event = (GenericEvent) (((EventMessage) message).getEvent()); + assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", ((EventMessage) message).getSubscriptionId()); + assertEquals(1, event.getKind().intValue()); + assertEquals(1686199583, event.getCreatedAt().longValue()); + assertEquals("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", event.getId()); + } + + @Test + public void testBaseEventMessageMarkerDecoder() throws JsonProcessingException { + log.info("testBaseEventMessageMarkerDecoder"); + + final String json = "[" + + "\"EVENT\"," + + "\"temp20230627\"," + + "{" + + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\"," + + "\"kind\":1," + + "\"pubkey\":\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"," + + "\"created_at\":1687765220," + + "\"content\":\"手順書が間違ってたら作業者は無理だな\"," + + "\"tags\":[" + + "[\"e\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346\",\"\",\"root\"]," + + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"]" + + "]," + + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"" + + "}]"; + + BaseMessage message = new BaseMessageDecoder<>().decode(json); + + final var event = (GenericEvent) (((EventMessage) message).getEvent()); + var tags = event.getTags(); + for (BaseTag t : tags) { + if (t.getCode().equalsIgnoreCase("e")) { + EventTag et = (EventTag) t; + assertEquals(Marker.ROOT, et.getMarker()); + } + } + } + + @Test + public void testGenericTagDecoder() { + log.info("testGenericTagDecoder"); + final String jsonString = "[\"saturn\", \"jetpack\", false]"; + + var tag = new GenericTagDecoder<>().decode(jsonString); + + assertEquals("saturn", tag.getCode()); + assertEquals(2, tag.getAttributes().size()); + assertEquals("jetpack", ((ElementAttribute) (tag.getAttributes().toArray())[0]).getValue()); + assertEquals(false, Boolean.valueOf(((ElementAttribute) (tag.getAttributes().toArray())[1]).getValue().toString())); + } + + @Test + public void testClassifiedListingTagSerializer() throws JsonProcessingException { + log.info("testClassifiedListingSerializer"); + final String classifiedListingEventJson = "{" + + "\"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\"" + + "}]"; + + GenericEvent event = new GenericEventDecoder<>().decode(classifiedListingEventJson); + EventMessage message = NIP01.createEventMessage(event, "1"); + assertEquals(1, message.getNip()); + 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()); + assertEquals("content ipsum", event.getContent()); + assertEquals("ec0762fe78b0f0b763d1324452d973a38bef576d1d76662722d2b8ff948af1de", event.getPubKey().toString()); + 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(); + + assertEquals("title ipsum", genericTags.stream() + .filter(tag -> tag.getCode().equalsIgnoreCase("title")).map(GenericTag::getAttributes).toList().getFirst().getFirst().getValue()); + + assertEquals("summary ipsum", genericTags.stream() + .filter(tag -> tag.getCode().equalsIgnoreCase("summary")).map(GenericTag::getAttributes).toList().getFirst().getFirst().getValue()); + + assertEquals("1687765220", genericTags.stream() + .filter(tag -> tag.getCode().equalsIgnoreCase("published_at")).map(GenericTag::getAttributes).toList().getFirst().getFirst().getValue()); + + assertEquals("location ipsum", genericTags.stream() + .filter(tag -> tag.getCode().equalsIgnoreCase("location")).map(GenericTag::getAttributes).toList().getFirst().getFirst().getValue()); + } + + @Test + public void testDeserializeTag() throws NostrException { + log.info("testDeserializeTag"); + + String npubHex = new PublicKey(Bech32.fromBech32("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9")).toString(); + final String jsonString = "[\"p\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]"; + var tag = new BaseTagDecoder<>().decode(jsonString); + + assertInstanceOf(PubKeyTag.class, tag); + + PubKeyTag pTag = (PubKeyTag) tag; + assertEquals("wss://nostr.java", pTag.getMainRelayUrl()); + assertEquals(npubHex, pTag.getPublicKey().toString()); + assertEquals("alice", pTag.getPetName()); + } + + @Test + public void testDeserializeGenericTag() throws NostrException { + log.info("testDeserializeGenericTag"); + String npubHex = new PublicKey(Bech32.fromBech32("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9")).toString(); + final String jsonString = "[\"gt\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]"; + var tag = new BaseTagDecoder<>().decode(jsonString); + + assertInstanceOf(GenericTag.class, tag); + + GenericTag gTag = (GenericTag) tag; + assertEquals("gt", gTag.getCode()); + } + + @Test + public void testFiltersEncoder() { + log.info("testFiltersEncoder"); + + String new_geohash = "2vghde"; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put("#g", + List.of( + new GenericTagQueryFilter<>(new GenericTagQuery("#g", new_geohash)))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals("{\"#g\":[\"2vghde\"]}", jsonMessage); + } + + @Test + public void testReqMessageFilterListSerializer() { + log.info("testReqMessageFilterListSerializer"); + + String new_geohash = "2vghde"; + String second_geohash = "3abcde"; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put("#g", + List.of( + new GenericTagQueryFilter<>(new GenericTagQuery("#g", new_geohash)), + new GenericTagQueryFilter<>(new GenericTagQuery("#g", second_geohash)))); + + ReqMessage reqMessage = new ReqMessage("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9", new Filters(expectedFilters)); + assertDoesNotThrow(() -> { + String jsonMessage = reqMessage.encode(); + String expected = "[\"REQ\",\"npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9\",{\"#g\":[\"2vghde\",\"3abcde\"]}]"; + assertEquals(expected, jsonMessage); + }); + } + + @Test + public void testGenericTagFiltersDecoder() { + log.info("testGenericTagFiltersDecoder"); + + String geohashKey = "#g"; + String geohashValue = "2vghde"; + String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue + "\"]}"; + + Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(geohashKey, List.of(new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue)))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testGenericTagFiltersListDecoder() { + log.info("testGenericTagFiltersListDecoder"); + + String geohashKey = "#g"; + String geohashValue1 = "2vghde"; + String geohashValue2 = "3abcde"; + String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]}"; + + Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(geohashKey, List.of( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testReqMessageDeserializer() throws JsonProcessingException { + log.info("testReqMessageDeserializer"); + + String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; + String geohashKey = "#g"; + String geohashValue = "2vghde"; + String reqJsonWithCustomTagQueryFilterToDecode = "[\"REQ\",\"" + subscriptionId + "\",{\"" + geohashKey + "\":[\"" + geohashValue + "\"]}]"; + + ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put("#g", + List.of( + new GenericTagQueryFilter<>(new GenericTagQuery("#g", geohashValue)))); + + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); + assertEquals(expectedReqMessage, decodedReqMessage); + } + + @Test + public void testReqMessageFilterListDecoder() { + log.info("testReqMessageFilterListDecoder"); + + String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; + String geohashKey = "#g"; + String geohashValue1 = "2vghde"; + String geohashValue2 = "3abcde"; + String reqJsonWithCustomTagQueryFiltersToDecode = "[\"REQ\",\"" + subscriptionId + "\",{\"" + geohashKey + "\":[\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]}]"; + + assertDoesNotThrow(() -> { + ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFiltersToDecode); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(geohashKey, List.of( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); + + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); + assertEquals(reqJsonWithCustomTagQueryFiltersToDecode, decodedReqMessage.encode()); + assertEquals(expectedReqMessage, decodedReqMessage); + }); + } + + @Test + public void testReqMessagePopulatedFilterDecoder() { + log.info("testReqMessagePopulatedFilterDecoder"); + + String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; + String kind = "1"; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String geohashKey = "#g"; + String geohashValue1 = "2vghde"; + String geohashValue2 = "3abcde"; + String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; + String reqJsonWithCustomTagQueryFilterToDecode = + "[\"REQ\", " + + "\"" + subscriptionId + "\", " + + "{\"kinds\": [" + kind + "], " + + "\"authors\": [\"" + author + "\"]," + + "\"" + geohashKey + "\": [\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]," + + "\"#e\": [\"" + referencedEventId + "\"]}]"; + + assertDoesNotThrow(() -> { + ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); + + Map> expectedFilters = new HashMap<>(); + + expectedFilters.put(AuthorFilter.filterKey, + List.of( + new AuthorFilter<>(new PublicKey(author)))); + + expectedFilters.put(KindFilter.filterKey, + List.of( + new KindFilter<>(Kind.TEXT_NOTE))); + + expectedFilters.put(ReferencedEventFilter.filterKey, + List.of(new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); + + expectedFilters.put(geohashKey, List.of( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); + + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); + assertEquals(expectedReqMessage, decodedReqMessage); + }); + } + + + @Test + public void testReqMessagePopulatedListOfFiltersWithIdentityDecoder() throws JsonProcessingException { + log.info("testReqMessagePopulatedListOfFiltersWithIdentityDecoder"); + + String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; + String kind = "1"; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String geohashKey = "#g"; + String geohashValue1 = "2vghde"; + String geohashValue2 = "3abcde"; + String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; + String uuidKey = "#d"; + String uuidValue1 = "UUID-1"; + String uuidValue2 = "UUID-2"; + String reqJsonWithCustomTagQueryFilterToDecode = + "[\"REQ\", " + + "\"" + subscriptionId + "\", " + + "{\"kinds\": [" + kind + "], " + + "\"authors\": [\"" + author + "\"]," + + "\"" + geohashKey + "\": [\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]," + + "\"" + uuidKey + "\": [\"" + uuidValue1 + "\",\"" + uuidValue2 + "\"]," + + "\"#e\": [\"" + referencedEventId + "\"]}]"; + + ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(KindFilter.filterKey, + List.of( + new KindFilter<>(Kind.TEXT_NOTE))); + + expectedFilters.put(AuthorFilter.filterKey, + List.of( + new AuthorFilter<>(new PublicKey(author)))); + + expectedFilters.put(ReferencedEventFilter.filterKey, + List.of( + new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); + + expectedFilters.put(geohashKey, + List.of( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); + + expectedFilters.put(uuidKey, + List.of( + new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)), + new IdentifierTagFilter<>(new IdentifierTag(uuidValue2)))); + + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); + assertEquals(expectedReqMessage, decodedReqMessage); + } + + @Test + public void testReqMessagePopulatedListOfFiltersListDecoder() throws JsonProcessingException { + log.info("testReqMessagePopulatedListOfFiltersListDecoder"); + + String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; + String kind = "1"; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; + String reqJsonWithCustomTagQueryFilterToDecode = + "[\"REQ\", " + + "\"" + subscriptionId + "\", " + + "{\"kinds\": [" + kind + "], " + + "\"authors\": [\"" + author + "\"]," + + "\"#e\": [\"" + referencedEventId + "\"]}]"; + + ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); + + Map> filterablesMap = new HashMap<>(); + filterablesMap.put(KindFilter.filterKey, List.of(new KindFilter<>(Kind.TEXT_NOTE))); + filterablesMap.put(AuthorFilter.filterKey, List.of(new AuthorFilter<>(new PublicKey(author)))); + filterablesMap.put(ReferencedEventFilter.filterKey, List.of(new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(filterablesMap)); + + assertEquals(expectedReqMessage.encode(), decodedReqMessage.encode()); + assertEquals(expectedReqMessage, decodedReqMessage); + } + + @Test + public void testReqMessagePopulatedListOfMultipleTypeFiltersListDecoder() throws JsonProcessingException { + log.info("testReqMessagePopulatedListOfFiltersListDecoder"); + + String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; + String kind = "1"; + String kind2 = "2"; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String author2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; + String reqJsonWithCustomTagQueryFilterToDecode = + "[\"REQ\", " + + "\"" + subscriptionId + "\", " + + "{\"kinds\": [" + kind + ", " + kind2 + "], " + + "\"authors\": [\"" + author + "\",\"" + author2 + "\"]," + + "\"#e\": [\"" + referencedEventId + "\"]" + + "}]"; + + ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(KindFilter.filterKey, + List.of( + new KindFilter<>(Kind.TEXT_NOTE), + new KindFilter<>(Kind.RECOMMEND_SERVER))); + expectedFilters.put(AuthorFilter.filterKey, + List.of( + new AuthorFilter<>(new PublicKey(author)), + new AuthorFilter<>(new PublicKey(author2)))); + expectedFilters.put(ReferencedEventFilter.filterKey, List.of(new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); + + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); + assertEquals(expectedReqMessage.encode(), decodedReqMessage.encode()); + assertEquals(expectedReqMessage, decodedReqMessage); + } + + @Test + public void testGenericTagQueryListDecoder() throws JsonProcessingException { + log.info("testReqMessagePopulatedListOfFiltersListDecoder"); + + String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; + String kind = "1"; + String kind2 = "2"; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String author2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + String geohashKey = "#g"; + String geohashValue1 = "2vghde"; + String geohashValue2 = "3abcde"; + String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; + String uuidKey = "#d"; + String uuidValue1 = "UUID-1"; + String uuidValue2 = "UUID-2"; + String reqJsonWithCustomTagQueryFilterToDecode = + "[\"REQ\", " + + "\"" + subscriptionId + "\", " + + "{\"kinds\": [" + kind + ", " + kind2 + "], " + + "\"authors\": [\"" + author + "\",\"" + author2 + "\"]," + + "\"" + geohashKey + "\": [\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]," + + "\"" + uuidKey + "\": [\"" + uuidValue1 + "\",\"" + uuidValue2 + "\"]," + + "\"#e\": [\"" + referencedEventId + "\"]" + + "}]"; + + ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(KindFilter.filterKey, + List.of( + new KindFilter<>(Kind.TEXT_NOTE), + new KindFilter<>(Kind.RECOMMEND_SERVER))); + + expectedFilters.put(AuthorFilter.filterKey, + List.of( + new AuthorFilter<>(new PublicKey(author)), + new AuthorFilter<>(new PublicKey(author2)))); + + expectedFilters.put(ReferencedEventFilter.filterKey, + List.of( + new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); + + expectedFilters.put(geohashKey, + List.of( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); + + expectedFilters.put(uuidKey, + List.of( + new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)), + new IdentifierTagFilter<>(new IdentifierTag(uuidValue2)))); + + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); + + assertTrue(JsonComparator.isEquivalentJson( + mapper.createArrayNode() + .add(mapper.readTree( + expectedReqMessage.encode())), + mapper.createArrayNode() + .add(mapper.readTree(decodedReqMessage.encode())))); + assertEquals(expectedReqMessage, decodedReqMessage); + } + + @Test + public void testAddressableTagFilterEncoder() { + log.info("testAddressableTagFilterEncoder"); + + Integer kind = 1; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String uuidKey = "#d"; + String uuidValue1 = "UUID-1"; + + String joined = String.join(":", String.valueOf(kind), author, uuidValue1); + + AddressTag addressTag = new AddressTag(); + addressTag.setKind(kind); + addressTag.setPublicKey(new PublicKey(author)); + addressTag.setIdentifierTag(new IdentifierTag(uuidValue1)); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(uuidKey, + List.of( + new AddressableTagFilter<>(addressTag))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals("{\"#d\":[\"" + joined + "\"]}", jsonMessage); + } + + @Test + public void testMultipleAddressableTagFilterEncoder() { + log.info("testMultipleAddressableTagFilterEncoder"); + + Integer kind = 1; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String uuidKey = "#a"; + String uuidValue1 = "UUID-1"; + String uuidValue2 = "UUID-2"; + + String joined1 = String.join(":", String.valueOf(kind), author, uuidValue1); + String joined2 = String.join(":", String.valueOf(kind), author, uuidValue2); + + AddressTag addressTag1 = new AddressTag(); + addressTag1.setKind(kind); + addressTag1.setPublicKey(new PublicKey(author)); + addressTag1.setIdentifierTag(new IdentifierTag(uuidValue1)); + + AddressTag addressTag2 = new AddressTag(); + addressTag2.setKind(kind); + addressTag2.setPublicKey(new PublicKey(author)); + addressTag2.setIdentifierTag(new IdentifierTag(uuidValue2)); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(uuidKey, + List.of( + new AddressableTagFilter<>(addressTag1), + new AddressableTagFilter<>(addressTag2))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + String joinedTags = String.join("\",\"", joined1, joined2); + assertEquals("{\"#a\":[\"" + joinedTags + "\"]}", jsonMessage); + } + + @Test + public void testReqMessageAddressableTagDeserializer() throws JsonProcessingException { + log.info("testReqMessageAddressableTagDeserializer"); + + Integer kind = 1; + String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String uuidKey = "#a"; + String uuidValue1 = "UUID-1"; + String uuidValue2 = "UUID-2"; + + String joined1 = String.join(":", String.valueOf(kind), author, uuidValue1); + + String reqJsonWithCustomTagQueryFilterToDecode = "[\"REQ\",\"" + subscriptionId + "\",{\"" + uuidKey + "\":[\"" + joined1 + "\"]}]"; + + ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); + + AddressTag addressTag1 = new AddressTag(); + addressTag1.setKind(kind); + addressTag1.setPublicKey(new PublicKey(author)); + addressTag1.setIdentifierTag(new IdentifierTag(uuidValue1)); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put("#a", + List.of(new AddressableTagFilter<>(addressTag1))); + + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); + + assertEquals(expectedReqMessage.encode(), decodedReqMessage.encode()); + assertEquals(expectedReqMessage, decodedReqMessage); + } +} From 5d73c921165865d97b77123c6d74061d4af9887f Mon Sep 17 00:00:00 2001 From: nick avlo Date: Mon, 17 Feb 2025 15:45:49 -0800 Subject: [PATCH 20/45] filters encoding ongoing --- .../event/filter/AddressableTagFilter.java | 27 ++++++++++------- .../java/nostr/event/filter/AuthorFilter.java | 11 +++++-- .../java/nostr/event/filter/EventFilter.java | 11 +++++-- .../java/nostr/event/filter/Filterable.java | 29 ++++++++++++++++++- .../main/java/nostr/event/filter/Filters.java | 2 +- .../event/filter/GenericTagQueryFilter.java | 11 +++++-- .../event/filter/IdentifierTagFilter.java | 11 +++++-- .../java/nostr/event/filter/KindFilter.java | 11 +++++-- .../event/filter/ReferencedEventFilter.java | 11 +++++-- .../filter/ReferencedPublicKeyFilter.java | 11 +++++-- .../java/nostr/event/filter/SinceFilter.java | 11 +++++-- .../java/nostr/event/filter/UntilFilter.java | 11 +++++-- .../event/json/codec/FiltersEncoder.java | 27 +++++++++-------- .../java/nostr/event/message/ReqMessage.java | 9 ++++-- 14 files changed, 139 insertions(+), 54 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java index 56cae233a..0a1f6c7bd 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java @@ -1,7 +1,7 @@ package nostr.event.filter; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import lombok.NonNull; import nostr.base.PublicKey; @@ -47,16 +47,8 @@ public T getFilterCriterion() { } @Override - public ArrayNode toArrayNode() { - Integer kind = addressableTag.getKind(); - String hexString = addressableTag.getPublicKey().toHexString(); - String id = addressableTag.getIdentifierTag().getId(); -// String uri = addressableTag.getRelay().getUri(); - - String collected = Stream.of(kind, hexString, id).map(Object::toString) - .collect(Collectors.joining(":")); - - return mapper.createArrayNode().add(collected); + public ObjectNode toObjectNode(ObjectNode objectNode) { + return processArrayNode(objectNode); } @Override @@ -75,4 +67,17 @@ public static AddressTag createAddressTag(JsonNode addressableTag) { return addressTag; } + + @Override + public String getFilterableValue() { + Integer kind = addressableTag.getKind(); + String hexString = addressableTag.getPublicKey().toHexString(); + String id = addressableTag.getIdentifierTag().getId(); +// String uri = addressableTag.getRelay().getUri(); + + String collected = Stream.of(kind, hexString, id).map(Object::toString) + .collect(Collectors.joining(":")); + + return collected; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java index 7a0e090f2..159a80793 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java @@ -1,6 +1,6 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; @@ -28,12 +28,17 @@ public T getFilterCriterion() { } @Override - public ArrayNode toArrayNode() { - return mapper.createArrayNode().add(publicKey.toHexString()); + public ObjectNode toObjectNode(ObjectNode objectNode) { + return processArrayNode(objectNode); } @Override public String getFilterKey() { return filterKey; } + + @Override + public String getFilterableValue() { + return publicKey.toHexString(); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java index 08edc7a07..eb6b29a58 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java @@ -1,6 +1,6 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; @@ -27,12 +27,17 @@ public T getFilterCriterion() { } @Override - public ArrayNode toArrayNode() { - return mapper.createArrayNode().add(event.getId()); + public ObjectNode toObjectNode(ObjectNode objectNode) { + return processArrayNode(objectNode); } @Override public String getFilterKey() { return filterKey; } + + @Override + public String getFilterableValue() { + return event.getId(); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java index 532f242ca..c0f35f10b 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java @@ -1,11 +1,16 @@ package nostr.event.filter; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import nostr.event.BaseTag; import nostr.event.impl.GenericEvent; +import nostr.event.json.codec.ArrayNodeCollector; +import nostr.event.json.codec.Collectors; import java.util.List; +import java.util.Optional; import java.util.function.Predicate; public interface Filterable { @@ -13,7 +18,8 @@ public interface Filterable { Predicate getPredicate(); T getFilterCriterion(); - ArrayNode toArrayNode(); + ObjectNode toObjectNode(ObjectNode objectNode); + String getFilterableValue(); String getFilterKey(); default List getTypeSpecificTags(Class tagClass, GenericEvent event) { @@ -22,4 +28,25 @@ default List getTypeSpecificTags(Class tagClass, Gener .map(tagClass::cast) .toList(); } + + default ObjectNode processArrayNode(ObjectNode objectNode) { + Optional jsonNode1 = Optional.ofNullable(objectNode.get(getFilterKey())); + + ArrayNodeCollector arrayNodeCollector = Collectors.toArrayNode(); + + ArrayNode arrayNode = arrayNodeCollector.supplier().get(); + jsonNode1.ifPresent(jsonNode -> { + jsonNode.elements().forEachRemaining(jsonNode2 -> + arrayNodeCollector.accumulator().accept(arrayNode, jsonNode2)); + }); + + ArrayNode arrayNode1 = mapper.createArrayNode(); + ArrayNode add2 = arrayNode1.add(getFilterableValue()); + + arrayNodeCollector.combiner().apply(arrayNode, add2); + + objectNode.set(getFilterKey(), arrayNode); + + return objectNode; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java index 77a996e18..41a09fa20 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java @@ -19,7 +19,7 @@ public class Filters { @Getter private final Map> filtersMap; - // TODO: make limit configurable + @Getter @Setter private Integer limit = 10; diff --git a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java index 5986cd39e..62dd4aa2c 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java @@ -1,6 +1,6 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.base.ElementAttribute; import nostr.base.GenericTagQuery; @@ -38,12 +38,17 @@ public T getFilterCriterion() { } @Override - public ArrayNode toArrayNode() { - return mapper.createArrayNode().add(genericTagQuery.getValue()); + public ObjectNode toObjectNode(ObjectNode objectNode) { + return processArrayNode(objectNode); } @Override public String getFilterKey() { return genericTagQuery.getTagName(); } + + @Override + public String getFilterableValue() { + return genericTagQuery.getValue(); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java index f54be59de..7a17b2a6f 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java @@ -1,6 +1,6 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; import nostr.event.tag.IdentifierTag; @@ -29,12 +29,17 @@ public T getFilterCriterion() { } @Override - public ArrayNode toArrayNode() { - return mapper.createArrayNode().add(identifierTag.getId()); + public ObjectNode toObjectNode(ObjectNode objectNode) { + return processArrayNode(objectNode); } @Override public String getFilterKey() { return filterKey; } + + @Override + public String getFilterableValue() { + return identifierTag.getId(); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java index e31b404db..d9c74f618 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java @@ -1,6 +1,6 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.event.Kind; import nostr.event.impl.GenericEvent; @@ -28,12 +28,17 @@ public T getFilterCriterion() { } @Override - public ArrayNode toArrayNode() { - return mapper.createArrayNode().add(kind.toString()); + public ObjectNode toObjectNode(ObjectNode objectNode) { + return processArrayNode(objectNode); } @Override public String getFilterKey() { return filterKey; } + + @Override + public String getFilterableValue() { + return kind.toString(); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java index ee2c2b9e5..a0c4f8f38 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java @@ -1,6 +1,6 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; import nostr.event.tag.EventTag; @@ -30,12 +30,17 @@ public T getFilterCriterion() { } @Override - public ArrayNode toArrayNode() { - return mapper.createArrayNode().add(referencedEvent.getId()); + public ObjectNode toObjectNode(ObjectNode objectNode) { + return processArrayNode(objectNode); } @Override public String getFilterKey() { return filterKey; } + + @Override + public String getFilterableValue() { + return referencedEvent.getId(); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java index 7b2d99f8b..83e21e83f 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java @@ -1,6 +1,6 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; @@ -31,12 +31,17 @@ public T getFilterCriterion() { } @Override - public ArrayNode toArrayNode() { - return mapper.createArrayNode().add(referencedPublicKey.toHexString()); + public ObjectNode toObjectNode(ObjectNode objectNode) { + return processArrayNode(objectNode); } @Override public String getFilterKey() { return filterKey; } + + @Override + public String getFilterableValue() { + return referencedPublicKey.toHexString(); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java index b84460030..6d8cd1831 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java @@ -1,6 +1,6 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; @@ -27,12 +27,17 @@ public Long getFilterCriterion() { } @Override - public ArrayNode toArrayNode() { - return mapper.createArrayNode().add(since); + public ObjectNode toObjectNode(ObjectNode objectNode) { + return mapper.createObjectNode().put(filterKey, since); } @Override public String getFilterKey() { return filterKey; } + + @Override + public String getFilterableValue() { + return since.toString(); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java index ddfe2af06..35b61237a 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java @@ -1,6 +1,6 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; @@ -27,12 +27,17 @@ public Long getFilterCriterion() { } @Override - public ArrayNode toArrayNode() { - return mapper.createArrayNode().add(until); + public ObjectNode toObjectNode(ObjectNode objectNode) { + return mapper.createObjectNode().put(filterKey, until); } @Override public String getFilterKey() { return filterKey; } + + @Override + public String getFilterableValue() { + return until.toString(); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java index 16ed9c321..feadf6217 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java @@ -1,15 +1,15 @@ package nostr.event.json.codec; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.SneakyThrows; import nostr.base.FEncoder; -import nostr.event.filter.Filterable; import nostr.event.filter.Filters; -import java.util.HashMap; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; @Data @EqualsAndHashCode(callSuper = false) @@ -23,15 +23,18 @@ public FiltersEncoder(Filters filters) { @SneakyThrows @Override public String encode() { - Map result = new HashMap<>(); + List result = new ArrayList<>(); - filters.getFiltersMap().forEach((key, value) -> - value.stream().distinct() - .map(Filterable::toArrayNode) - .reduce(ArrayNode::addAll) - .ifPresent(arrayNode -> - result.put(key, arrayNode))); + filters.getFiltersMap().forEach((key, filterableList) -> { + final ObjectNode objectNode = MAPPER.createObjectNode(); + ObjectNode list = filterableList.stream().map(filterable -> filterable.toObjectNode(objectNode)).toList().getFirst(); + result.add(list); + }); - return MAPPER.writeValueAsString(MAPPER.valueToTree(result)); + ObjectMapper mapper = new ObjectMapper(); + ObjectNode root = mapper.createObjectNode(); + result.forEach(root::setAll); + System.out.println(root.toPrettyString()); + return root.toString(); } } diff --git a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java index cd537e45c..790e326b8 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java @@ -75,13 +75,18 @@ public String encode() throws JsonProcessingException { arrayNode.add(jsonNode); }); - return IEncoder.MAPPER.writeValueAsString(getArrayNode()); + String s = IEncoder.MAPPER.writeValueAsString(getArrayNode()); + System.out.println(s); + return s; } public static T decode(@NonNull Object subscriptionId, @NonNull List jsonFiltersList) { - return (T) new ReqMessage(subscriptionId.toString(), + + ReqMessage reqMessage = new ReqMessage(subscriptionId.toString(), jsonFiltersList.stream().map( ReqMessage::createFiltersFromJson).toList()); + + return (T) reqMessage; } private static Filters createFiltersFromJson(String jsonFiltersList) { From fc485b397748a585b735ee594c480621b81a0e61 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Mon, 17 Feb 2025 15:46:29 -0800 Subject: [PATCH 21/45] filters encoding ongoing --- .../test/filters/FiltersDecoderTest.java | 54 +++++++ .../test/filters/FiltersEncoderTest.java | 140 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java create mode 100644 nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java new file mode 100644 index 000000000..aaaac2e64 --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java @@ -0,0 +1,54 @@ +package nostr.test.filters; + +import lombok.extern.java.Log; +import nostr.base.GenericTagQuery; +import nostr.event.filter.Filterable; +import nostr.event.filter.Filters; +import nostr.event.filter.GenericTagQueryFilter; +import nostr.event.json.codec.FiltersDecoder; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Log +public class FiltersDecoderTest { + @Test + public void testGenericTagFiltersDecoder() { + log.info("testGenericTagFiltersDecoder"); + + String geohashKey = "#g"; + String geohashValue = "2vghde"; + String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue + "\"]}"; + + Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(geohashKey, List.of(new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue)))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testGenericTagFiltersListDecoder() { + log.info("testGenericTagFiltersListDecoder"); + + String geohashKey = "#g"; + String geohashValue1 = "2vghde"; + String geohashValue2 = "3abcde"; + String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]}"; + + Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(geohashKey, List.of( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + +} diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java new file mode 100644 index 000000000..a79deac90 --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java @@ -0,0 +1,140 @@ +package nostr.test.filters; + +import lombok.extern.java.Log; +import nostr.base.GenericTagQuery; +import nostr.base.PublicKey; +import nostr.event.filter.AddressableTagFilter; +import nostr.event.filter.Filterable; +import nostr.event.filter.Filters; +import nostr.event.filter.GenericTagQueryFilter; +import nostr.event.filter.SinceFilter; +import nostr.event.json.codec.FiltersEncoder; +import nostr.event.tag.AddressTag; +import nostr.event.tag.IdentifierTag; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Log +public class FiltersEncoderTest { + + @Test + public void testAddressableTagFilterEncoder() { + log.info("testAddressableTagFilterEncoder"); + + Integer kind = 1; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String uuidKey = "#d"; + String uuidValue1 = "UUID-1"; + + String joined = String.join(":", String.valueOf(kind), author, uuidValue1); + + AddressTag addressTag = new AddressTag(); + addressTag.setKind(kind); + addressTag.setPublicKey(new PublicKey(author)); + addressTag.setIdentifierTag(new IdentifierTag(uuidValue1)); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(uuidKey, + List.of( + new AddressableTagFilter<>(addressTag))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals("{\"#a\":[\"" + joined + "\"]}", jsonMessage); + } + + @Test + public void testSingleGenericTagQueryFiltersEncoder() { + log.info("testSingleGenericTagQueryFiltersEncoder"); + + String geohashKey = "#g"; + String new_geohash = "2vghde"; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(geohashKey, + List.of( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, new_geohash)))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals("{\"#g\":[\"2vghde\"]}", jsonMessage); + } + + @Test + public void testMultipleGenericTagQueryFiltersEncoder() { + log.info("testMultipleGenericTagQueryFiltersEncoder"); + + String geohashKey = "#g"; + String geohashValue1 = "2vghde"; + String geohashValue2 = "3abcde"; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(geohashKey, + List.of( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals( + "{\"#g\":[\"2vghde\",\"3abcde\"]}" + , jsonMessage); + } + + @Test + public void testMultipleAddressableTagFilterEncoder() { + log.info("testMultipleAddressableTagFilterEncoder"); + + Integer kind = 1; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String uuidKey = "#a"; + String uuidValue1 = "UUID-1"; + String uuidValue2 = "UUID-2"; + + String joined1 = String.join(":", String.valueOf(kind), author, uuidValue1); + String joined2 = String.join(":", String.valueOf(kind), author, uuidValue2); + + AddressTag addressTag1 = new AddressTag(); + addressTag1.setKind(kind); + addressTag1.setPublicKey(new PublicKey(author)); + addressTag1.setIdentifierTag(new IdentifierTag(uuidValue1)); + + AddressTag addressTag2 = new AddressTag(); + addressTag2.setKind(kind); + addressTag2.setPublicKey(new PublicKey(author)); + addressTag2.setIdentifierTag(new IdentifierTag(uuidValue2)); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(uuidKey, + List.of( + new AddressableTagFilter<>(addressTag1), + new AddressableTagFilter<>(addressTag2))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + String joinedTags = String.join("\",\"", joined1, joined2); + assertEquals("{\"#a\":[\"" + joinedTags + "\"]}", jsonMessage); + } + + @Test + public void testSinceFiltersEncoder() { + log.info("testSinceFiltersEncoder"); + + String sinceKey = SinceFilter.filterKey; + Long since = 1111111111L; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(sinceKey, + List.of( + new SinceFilter(since))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals("{\"" + sinceKey + "\":" + since + "}", jsonMessage); + } +} From a45b69eb43a2fd35af5b9abec176edf73fa9b3f4 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Mon, 17 Feb 2025 16:53:34 -0800 Subject: [PATCH 22/45] fixed object filters --- .../java/nostr/event/filter/Filterable.java | 22 +++++++++ .../java/nostr/event/filter/SinceFilter.java | 2 +- .../java/nostr/event/filter/UntilFilter.java | 2 +- .../event/json/codec/ArrayNodeCollector.java | 42 +++++++++++++++++ .../nostr/event/json/codec/Collectors.java | 21 +++++++++ .../event/json/codec/ObjectNodeCollector.java | 45 +++++++++++++++++++ 6 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 nostr-java-event/src/main/java/nostr/event/json/codec/ArrayNodeCollector.java create mode 100644 nostr-java-event/src/main/java/nostr/event/json/codec/Collectors.java create mode 100644 nostr-java-event/src/main/java/nostr/event/json/codec/ObjectNodeCollector.java diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java index c0f35f10b..f75ecb534 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java @@ -8,6 +8,7 @@ import nostr.event.impl.GenericEvent; import nostr.event.json.codec.ArrayNodeCollector; import nostr.event.json.codec.Collectors; +import nostr.event.json.codec.ObjectNodeCollector; import java.util.List; import java.util.Optional; @@ -49,4 +50,25 @@ default ObjectNode processArrayNode(ObjectNode objectNode) { return objectNode; } + + default ObjectNode processObjectNode(String key, ObjectNode objectNode) { + Optional jsonNode1 = Optional.ofNullable(objectNode.get(getFilterKey())); + + ObjectNodeCollector objectNodeCollector = Collectors.toObjectNode(); + + ObjectNode objectNode1 = objectNodeCollector.supplier().get(); + jsonNode1.ifPresent(jsonNode -> { + jsonNode.elements().forEachRemaining(jsonNode2 -> + objectNodeCollector.accumulator().accept(objectNode1, jsonNode2)); + }); + + ObjectNode objectNode2 = mapper.createObjectNode(); + +// TODO: hack string to int, needs fix + ObjectNode add2 = objectNode2.put(key, Integer.valueOf(getFilterableValue())); + + objectNodeCollector.combiner().apply(objectNode1, add2); + + return add2; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java index 6d8cd1831..6c82cbd08 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java @@ -28,7 +28,7 @@ public Long getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return mapper.createObjectNode().put(filterKey, since); + return processObjectNode(filterKey, objectNode); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java index 35b61237a..f2869d1b4 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java @@ -28,7 +28,7 @@ public Long getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return mapper.createObjectNode().put(filterKey, until); + return processObjectNode(filterKey, objectNode); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/ArrayNodeCollector.java b/nostr-java-event/src/main/java/nostr/event/json/codec/ArrayNodeCollector.java new file mode 100644 index 000000000..c987ee49d --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/ArrayNodeCollector.java @@ -0,0 +1,42 @@ +package nostr.event.json.codec; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; + +import java.util.EnumSet; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; + +public class ArrayNodeCollector implements Collector { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + public Supplier supplier() { + return mapper::createArrayNode; + } + + @Override + public BiConsumer accumulator() { + return ArrayNode::add; + } + + @Override + public BinaryOperator combiner() { + return ArrayNode::addAll; + } + + @Override + public Function finisher() { + return accumulator -> accumulator; + } + + @Override + public Set characteristics() { + return EnumSet.of(Characteristics.UNORDERED); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/Collectors.java b/nostr-java-event/src/main/java/nostr/event/json/codec/Collectors.java new file mode 100644 index 000000000..d00458f77 --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/Collectors.java @@ -0,0 +1,21 @@ +package nostr.event.json.codec; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; + +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class Collectors { + public static ArrayNodeCollector toArrayNode() { + return new ArrayNodeCollector(); + } + + public static ObjectNodeCollector toObjectNode() { + return new ObjectNodeCollector(); + } + + public static Stream toStream(ArrayNode arrayNode) { + return StreamSupport.stream(arrayNode.spliterator(), false); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/ObjectNodeCollector.java b/nostr-java-event/src/main/java/nostr/event/json/codec/ObjectNodeCollector.java new file mode 100644 index 000000000..62ece4ceb --- /dev/null +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/ObjectNodeCollector.java @@ -0,0 +1,45 @@ +package nostr.event.json.codec; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.util.EnumSet; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; + +public class ObjectNodeCollector implements Collector { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + public Supplier supplier() { + return mapper::createObjectNode; + } + + @Override + public BiConsumer accumulator() { + return (objectNode, jsonNode) -> { + objectNode.set("someval", jsonNode); + }; + } + + @Override + public BinaryOperator combiner() { +// ArrayNode::addAll; + return ObjectNode::setAll; + } + + @Override + public Function finisher() { + return accumulator -> accumulator; + } + + @Override + public Set characteristics() { + return EnumSet.of(Characteristics.UNORDERED); + } +} From 8464f528d80fc6057de6abbee76654f61d86fdcb Mon Sep 17 00:00:00 2001 From: nick avlo Date: Mon, 17 Feb 2025 20:11:00 -0800 Subject: [PATCH 23/45] ongoing tests --- .../event/filter/AddressableTagFilter.java | 2 +- .../java/nostr/event/filter/AuthorFilter.java | 2 +- .../java/nostr/event/filter/EventFilter.java | 2 +- .../java/nostr/event/filter/Filterable.java | 29 +- .../main/java/nostr/event/filter/Filters.java | 8 - .../event/filter/GenericTagQueryFilter.java | 2 +- .../event/filter/IdentifierTagFilter.java | 2 +- .../java/nostr/event/filter/KindFilter.java | 6 +- .../event/filter/ReferencedEventFilter.java | 2 +- .../filter/ReferencedPublicKeyFilter.java | 2 +- .../event/json/codec/FiltersEncoderOld.java | 69 ---- .../event/json/codec/FiltersListEncoder.java | 57 ---- .../nostr/test/event/filter/FiltersTest.java | 306 ------------------ 13 files changed, 35 insertions(+), 454 deletions(-) delete mode 100644 nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderOld.java delete mode 100644 nostr-java-event/src/main/java/nostr/event/json/codec/FiltersListEncoder.java delete mode 100644 nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java index 0a1f6c7bd..364ffacf2 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java @@ -48,7 +48,7 @@ public T getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNode(objectNode); + return processArrayNodeString(objectNode); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java index 159a80793..8bf977e7e 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java @@ -29,7 +29,7 @@ public T getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNode(objectNode); + return processArrayNodeString(objectNode); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java index eb6b29a58..8cea885ba 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java @@ -28,7 +28,7 @@ public T getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNode(objectNode); + return processArrayNodeString(objectNode); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java index f75ecb534..7b7abf800 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java @@ -20,7 +20,7 @@ public interface Filterable { Predicate getPredicate(); T getFilterCriterion(); ObjectNode toObjectNode(ObjectNode objectNode); - String getFilterableValue(); + Object getFilterableValue(); String getFilterKey(); default List getTypeSpecificTags(Class tagClass, GenericEvent event) { @@ -30,7 +30,7 @@ default List getTypeSpecificTags(Class tagClass, Gener .toList(); } - default ObjectNode processArrayNode(ObjectNode objectNode) { + default ObjectNode processArrayNodeString(ObjectNode objectNode) { Optional jsonNode1 = Optional.ofNullable(objectNode.get(getFilterKey())); ArrayNodeCollector arrayNodeCollector = Collectors.toArrayNode(); @@ -42,7 +42,28 @@ default ObjectNode processArrayNode(ObjectNode objectNode) { }); ArrayNode arrayNode1 = mapper.createArrayNode(); - ArrayNode add2 = arrayNode1.add(getFilterableValue()); + ArrayNode add2 = arrayNode1.add(getFilterableValue().toString()); + + arrayNodeCollector.combiner().apply(arrayNode, add2); + + objectNode.set(getFilterKey(), arrayNode); + + return objectNode; + } + + default ObjectNode processArrayNodeInt(ObjectNode objectNode) { + Optional jsonNode1 = Optional.ofNullable(objectNode.get(getFilterKey())); + + ArrayNodeCollector arrayNodeCollector = Collectors.toArrayNode(); + + ArrayNode arrayNode = arrayNodeCollector.supplier().get(); + jsonNode1.ifPresent(jsonNode -> { + jsonNode.elements().forEachRemaining(jsonNode2 -> + arrayNodeCollector.accumulator().accept(arrayNode, jsonNode2)); + }); + + ArrayNode arrayNode1 = mapper.createArrayNode(); + ArrayNode add2 = arrayNode1.add(Integer.valueOf(getFilterableValue().toString())); arrayNodeCollector.combiner().apply(arrayNode, add2); @@ -65,7 +86,7 @@ default ObjectNode processObjectNode(String key, ObjectNode objectNode) { ObjectNode objectNode2 = mapper.createObjectNode(); // TODO: hack string to int, needs fix - ObjectNode add2 = objectNode2.put(key, Integer.valueOf(getFilterableValue())); + ObjectNode add2 = objectNode2.put(key, Long.valueOf(getFilterableValue().toString())); objectNodeCollector.combiner().apply(objectNode1, add2); diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java index 41a09fa20..ea2f9c67b 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java @@ -71,12 +71,4 @@ public void setFilterableListByType( public static List getFilterable(JsonNode jsonNode, Function filterFunction) { return StreamSupport.stream(jsonNode.spliterator(), false).map(filterFunction).toList(); } - - public Optional getSinceOrUntil(String type) { - return Optional - .ofNullable( - getFilterableByType(type)) - .map(filterables -> filterables - .getFirst().getFilterCriterion()); - } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java index 62dd4aa2c..a90251f6f 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java @@ -39,7 +39,7 @@ public T getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNode(objectNode); + return processArrayNodeString(objectNode); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java index 7a17b2a6f..4f079c159 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java @@ -30,7 +30,7 @@ public T getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNode(objectNode); + return processArrayNodeString(objectNode); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java index d9c74f618..3ac250b9d 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java @@ -29,7 +29,7 @@ public T getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNode(objectNode); + return processArrayNodeInt(objectNode); } @Override @@ -38,7 +38,7 @@ public String getFilterKey() { } @Override - public String getFilterableValue() { - return kind.toString(); + public Integer getFilterableValue() { + return kind.getValue(); } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java index a0c4f8f38..da7e5b42c 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java @@ -31,7 +31,7 @@ public T getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNode(objectNode); + return processArrayNodeString(objectNode); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java index 83e21e83f..641b419e4 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java @@ -32,7 +32,7 @@ public T getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNode(objectNode); + return processArrayNodeString(objectNode); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderOld.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderOld.java deleted file mode 100644 index e4ce0fa4c..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoderOld.java +++ /dev/null @@ -1,69 +0,0 @@ -//package nostr.event.json.codec; -// -//import com.fasterxml.jackson.core.JsonProcessingException; -//import com.fasterxml.jackson.databind.JsonNode; -//import com.fasterxml.jackson.databind.ObjectMapper; -//import com.fasterxml.jackson.databind.node.ObjectNode; -//import lombok.Data; -//import lombok.EqualsAndHashCode; -//import nostr.base.FEncoder; -//import nostr.util.NostrException; -// -//import java.util.Spliterator; -//import java.util.Spliterators; -//import java.util.stream.StreamSupport; -// -///** -// * @author guilhermegps -// */ -//@Data -//@EqualsAndHashCode(callSuper = false) -//public class FiltersEncoder implements FEncoder { -// private final Filters filters; -// -// public FiltersEncoder(Filters filters) { -// this.filters = filters; -// } -// -// protected String toJson() throws NostrException { -// try { -// JsonNode node = MAPPER.valueToTree(filters); -// ObjectNode objNode = (ObjectNode) node; -// //var arrayNode = (ArrayNode) node.get("genericTagQuery"); -// if (objNode != null && !objNode.isNull()) { -// for (JsonNode jn : objNode) { -// StreamSupport.stream( -// Spliterators.spliteratorUnknownSize(jn.fields(), Spliterator.ORDERED), false) -// .forEach(f -> { -// if ("genericTagQuery".equals(f.getKey())) { -// var mapper = new ObjectMapper(); -// try { -// mapper.readTree(f.getValue().toString()) -// .fields() -// .forEachRemaining(g -> objNode.set(g.getKey(), g.getValue())); -// } catch (JsonProcessingException e) { -// throw new RuntimeException(e); -// } -// } else { -// objNode.set(f.getKey(), f.getValue()); -// } -// }); -// } -// } -// objNode.remove("genericTagQuery"); -// -// return MAPPER.writeValueAsString(objNode); -// } catch (JsonProcessingException | IllegalArgumentException e) { -// throw new NostrException(e); -// } -// } -// -// @Override -// public String encode() { -// try { -// return toJson(); -// } catch (NostrException ex) { -// throw new RuntimeException(ex); -// } -// } -//} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersListEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersListEncoder.java deleted file mode 100644 index e343102a1..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersListEncoder.java +++ /dev/null @@ -1,57 +0,0 @@ -//package nostr.event.json.codec; -// -//import com.fasterxml.jackson.core.JsonProcessingException; -//import com.fasterxml.jackson.databind.JsonNode; -//import lombok.Data; -//import nostr.base.FEncoder; -//import nostr.util.NostrException; -// -//import java.util.List; -// -//@Data -//public class FiltersListEncoder implements FEncoder { -// -// private final List filtersList; -// -// public FiltersListEncoder(List filtersList) { -// this.filtersList = filtersList; -// } -// -// @Override -// public String encode() { -// try { -// return toJson(); -// } catch (NostrException ex) { -// throw new RuntimeException(ex); -// } -// } -// -// protected String toJson() throws NostrException { -// return toJsonCommaSeparated(); -// } -// -// private String toJsonArray() throws NostrException { -// try { -// StringBuilder sb = new StringBuilder(); -// for (Object filter : getFiltersList()) { -// if (!sb.isEmpty()) { -// sb.append(","); -// } -// sb.append(MAPPER.writeValueAsString(filter)); -// } -// return sb.toString(); -// } catch (JsonProcessingException | IllegalArgumentException e) { -// throw new NostrException(e); -// } -// } -// -// private String toJsonCommaSeparated() throws NostrException { -// JsonNode node = MAPPER.valueToTree(getFiltersList()); -// try { -// return MAPPER.writeValueAsString(node); -// } catch (JsonProcessingException e) { -// throw new NostrException(e); -// } -// -// } -//} diff --git a/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java b/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java deleted file mode 100644 index 4cfc84e4b..000000000 --- a/nostr-java-test/src/test/java/nostr/test/event/filter/FiltersTest.java +++ /dev/null @@ -1,306 +0,0 @@ -package nostr.test.event.filter; - -import lombok.extern.java.Log; -import nostr.base.GenericTagQuery; -import nostr.base.PublicKey; -import nostr.base.Relay; -import nostr.event.Kind; -import nostr.event.filter.EventFilter; -import nostr.event.filter.Filterable; -import nostr.event.filter.Filters; -import nostr.event.filter.KindFilter; -import nostr.event.filter.PublicKeyFilter; -import nostr.event.filter.ReferencedPublicKeyFilter; -import nostr.event.impl.GenericEvent; -import nostr.event.tag.EventTag; -import nostr.event.tag.IdentifierTag; -import nostr.event.tag.PubKeyTag; -import nostr.id.Identity; -import nostr.test.EntityFactory; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@Log -public class FiltersTest { - - @Test - public void testEventFilters() { - PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); - GenericEvent instance = EntityFactory.Events.createTextNoteEvent(publicKey); - instance.update(); - - GenericEvent event = new GenericEvent(); - event.setId(instance.getId()); - - Map> filterablesMap = new HashMap<>(); - filterablesMap.put(EventFilter.filterKey, List.of(new EventFilter<>(event))); - - Filters filters = new Filters(filterablesMap); - List filtersEvents = filters.getFilterableByType(EventFilter.filterKey); - assertEquals(((EventFilter)filtersEvents.getFirst()).getFilterCriterion().getId(), event.getId()); - } - - @Test - public void testAuthorFiltersRxR() { - PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); - GenericEvent instance = EntityFactory.Events.createTextNoteEvent(publicKey); - instance.update(); - - Map> filterablesMap = new HashMap<>(); - PublicKeyFilter authorFilter = new PublicKeyFilter<>(publicKey); - filterablesMap.put(PublicKeyFilter.filterKey, List.of(authorFilter)); - - Filters filters = new Filters(filterablesMap); - List authors = filters.getFilterableByType(PublicKeyFilter.filterKey); - assertEquals(authors, List.of(authorFilter)); - } - -// @Test -// public void testKindFilters() { -// Filters filters = new Filters(); -// List kindList = List.of(Kind.CONTACT_LIST, Kind.DELETION); -// filters.setKinds(kindList); -// List kinds = filters.getKinds(); -// assertEquals(kinds, kindList); -// } -// -// @Test -// public void testReferencedEventsFilters() { -// PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); -// -// GenericEvent eventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); -// eventToReference.update(); -// -// GenericEvent eventContainingEventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); -// eventContainingEventToReference.setTags(List.of(new EventTag(eventToReference.getId()))); -// eventContainingEventToReference.update(); -// -// Filters filters = new Filters(); -// filters.setReferencedEvents(List.of(eventContainingEventToReference)); -// -// filters.getReferencedEvents().getFirst().getTags().stream() -// .filter(EventTag.class::isInstance) -// .map(EventTag.class::cast) -// .findFirst() -// .ifPresent(eventTag -> -// assertEquals(eventTag.getIdEvent(), eventToReference.getId())); -// } -// -// @Test -// public void testReferencedPubkeysFilters() { -// PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); -// -// GenericEvent eventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); -// eventToReference.update(); -// -// GenericEvent eventContainingEventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); -// eventContainingEventToReference.setTags(List.of(new PubKeyTag(eventToReference.getPubKey()))); -// eventContainingEventToReference.update(); -// -// Filters filters = new Filters(); -// filters.setReferencedEvents(List.of(eventContainingEventToReference)); -// -// filters.getReferencedEvents().getFirst().getTags().stream() -// .filter(PubKeyTag.class::isInstance) -// .map(PubKeyTag.class::cast) -// .findFirst() -// .ifPresent(pubkeyTag -> -// assertEquals(pubkeyTag.getPublicKey().toHexString(), eventToReference.getPubKey().toHexString())); -// } -// -// @Test -// public void testSinceFilters() { -// Filters filters = new Filters(); -// long sinceTime = Date.from(Instant.now()).getTime(); -// filters.setSince(sinceTime); -// -// filters.getSince().ifPresent(aLong -> assertEquals(sinceTime, aLong)); -// } -// -// @Test -// public void testUntilFilters() { -// Filters filters = new Filters(); -// long untilTime = Date.from(Instant.now()).getTime(); -// filters.setUntil(untilTime); -// -// filters.getUntil().ifPresent(aLong -> assertEquals(untilTime, aLong)); -// } -// -// @Test -// public void testLimit() { -// Filters filters = new Filters(); -// filters.setLimit(1); -// filters.getLimit().ifPresent(integer -> assertEquals(1, integer)); -// } -// -// @Test -// public void testGenericQueryTagFilters() { -// PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); -// GenericTagQuery genericTagQuery = new GenericTagQuery(); -// String key = "#a"; -// genericTagQuery.setTagName(key); -// -// String kind = Kind.TEXT_NOTE.toString(); -// String hexString = publicKey.toHexString(); -// String uuid = new IdentifierTag("uuid").getId(); -// String relayUri = new Relay("ws://localhost:5555").getUri(); -// -// List addressTagValues = List.of( -// kind, -// hexString, -// uuid, -// relayUri -// ); -// genericTagQuery.setValue(addressTagValues); -// -// Filters filters = new Filters(); -// filters.setGenericTagQuery( -// genericTagQuery.getTagName(), -// addressTagValues); -// -// List genericTagQueryResult = filters.getGenericTagQuery(key); -// genericTagQueryResult.forEach(log::info); -// -// assertTrue(genericTagQueryResult.contains(kind)); -// assertTrue(genericTagQueryResult.contains(hexString)); -// assertTrue(genericTagQueryResult.contains(uuid)); -// assertTrue(genericTagQueryResult.contains(relayUri)); -// } -// -// @Test -// public void testMultipleFilters() { -// PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); -// GenericEvent instance = EntityFactory.Events.createTextNoteEvent(publicKey); -// instance.update(); -// -// GenericEvent eventList = new GenericEvent(); -// eventList.setId(instance.getId()); -// -// List kindList = List.of(Kind.CONTACT_LIST, Kind.DELETION); -// -// GenericEvent eventContainingEventToReference = EntityFactory.Events.createTextNoteEvent(publicKey); -// eventContainingEventToReference.setTags(List.of(new EventTag(instance.getId()))); -// eventContainingEventToReference.setTags(List.of(new PubKeyTag(instance.getPubKey()))); -// eventContainingEventToReference.update(); -// -// long sinceTime = Date.from(Instant.now()).getTime(); -// long untilTime = Date.from(Instant.now()).getTime(); -// -// Filters filters = new Filters(); -// filters.setEvents(List.of(eventList)); -// filters.setAuthors(List.of(publicKey)); -// filters.setKinds(kindList); -// filters.setReferencedEvents(List.of(eventContainingEventToReference)); -// filters.setSince(sinceTime); -// filters.setUntil(untilTime); -// -// GenericTagQuery genericTagQuery = new GenericTagQuery(); -// String key = "#a"; -// genericTagQuery.setTagName(key); -// -// String kind = Kind.TEXT_NOTE.toString(); -// String hexString = publicKey.toHexString(); -// String uuid = new IdentifierTag("uuid").getId(); -// String relayUri = new Relay("ws://localhost:5555").getUri(); -// -// List addressTagValues = List.of( -// kind, -// hexString, -// uuid, -// relayUri -// ); -// genericTagQuery.setValue(addressTagValues); -// -// filters.setGenericTagQuery( -// genericTagQuery.getTagName(), -// addressTagValues); -// -// List filtersEvents = filters.getEvents(); -// assertEquals(filtersEvents.getFirst().getId(), eventList.getId()); -// -// List authors = filters.getAuthors(); -// assertEquals(authors, List.of(publicKey)); -// -// List kinds = filters.getKinds(); -// assertEquals(kinds, kindList); -// -// filters.getReferencedEvents().getFirst().getTags().stream() -// .filter(EventTag.class::isInstance) -// .map(EventTag.class::cast) -// .findFirst() -// .ifPresent(eventTag -> -// assertEquals(eventTag.getIdEvent(), instance.getId())); -// -// filters.getReferencedEvents().getFirst().getTags().stream() -// .filter(PubKeyTag.class::isInstance) -// .map(PubKeyTag.class::cast) -// .findFirst() -// .ifPresent(pubkeyTag -> -// assertEquals(pubkeyTag.getPublicKey().toHexString(), instance.getPubKey().toHexString())); -// -// filters.getSince().ifPresent(aLong -> assertEquals(sinceTime, aLong)); -// filters.getUntil().ifPresent(aLong -> assertEquals(untilTime, aLong)); -// -// List genericTagQueryResult = filters.getGenericTagQuery(key); -// -// assertTrue(genericTagQueryResult.contains(kind)); -// assertTrue(genericTagQueryResult.contains(hexString)); -// assertTrue(genericTagQueryResult.contains(uuid)); -// assertTrue(genericTagQueryResult.contains(relayUri)); -// } -// -// @Test -// public void testNonExistingValues() { -// Filters filters = new Filters(); -// assertTrue(filters.getLimit().isEmpty()); -// assertTrue(filters.getAuthors().isEmpty()); -// assertTrue(filters.getKinds().isEmpty()); -// assertTrue(filters.getReferencedEvents().isEmpty()); -// assertTrue(filters.getUntil().isEmpty()); -// assertTrue(filters.getEvents().isEmpty()); -// assertTrue(filters.getReferencePubKeys().isEmpty()); -// assertTrue(filters.getGenericTagQuery().isEmpty()); -// assertTrue(filters.getGenericTagQuery("someKey").isEmpty()); -// } -// -// @Test -// public void testNonExistingTagTypes() { -// Filters filters = new Filters(); -// String key = "some-random-key"; -// String value = "some-random-value"; -// filters.setGenericTagQuery( -// key, -// List.of(value)); -// -// assertTrue(filters.getGenericTagQuery(key).contains(value)); -// -// String anotherValue = "another-random-value"; -// filters.setGenericTagQuery( -// key, -// List.of(value, anotherValue)); -// assertTrue(filters.getGenericTagQuery(key).contains(value)); -// assertTrue(filters.getGenericTagQuery(key).contains(anotherValue)); -// } -// -// @Test -// public void testNullValuesThrowException() { -// Filters filters = new Filters(); -// assertThrows(NullPointerException.class, () -> filters.setLimit(null)); -// assertThrows(IllegalArgumentException.class, () -> filters.setEvents(List.of())); -// assertThrows(IllegalArgumentException.class, () -> filters.setAuthors(List.of())); -// assertThrows(IllegalArgumentException.class, () -> filters.setKinds(List.of())); -// assertThrows(IllegalArgumentException.class, () -> filters.setReferencedEvents(List.of())); -// assertThrows(IllegalArgumentException.class, () -> filters.setReferencePubKeys(List.of())); -// assertThrows(IllegalArgumentException.class, () -> filters.setGenericTagQuery("", List.of())); -// assertThrows(IllegalArgumentException.class, () -> filters.setGenericTagQuery(Map.of())); -// } -} From 2c42bdc031453b3437a592b47ba211ac3c432690 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Tue, 18 Feb 2025 12:23:43 -0800 Subject: [PATCH 24/45] ongoing filters --- .../event/filter/AddressableTagFilter.java | 6 -- .../java/nostr/event/filter/AuthorFilter.java | 6 -- .../java/nostr/event/filter/EventFilter.java | 6 -- .../java/nostr/event/filter/Filterable.java | 78 ++++++------------- .../event/filter/FilterableProvider.java | 30 ++++--- .../main/java/nostr/event/filter/Filters.java | 65 +++++++--------- .../event/filter/GenericTagQueryFilter.java | 6 -- .../event/filter/IdentifierTagFilter.java | 6 -- .../java/nostr/event/filter/KindFilter.java | 3 +- .../event/filter/ReferencedEventFilter.java | 6 -- .../filter/ReferencedPublicKeyFilter.java | 6 -- .../java/nostr/event/filter/SinceFilter.java | 2 +- .../java/nostr/event/filter/UntilFilter.java | 2 +- .../event/json/codec/ArrayNodeCollector.java | 42 ---------- .../nostr/event/json/codec/Collectors.java | 21 ----- .../event/json/codec/ObjectNodeCollector.java | 45 ----------- .../test/filters/FiltersListDecoderTest.java | 12 +++ .../test/filters/FiltersListEncoderTest.java | 12 +++ 18 files changed, 97 insertions(+), 257 deletions(-) delete mode 100644 nostr-java-event/src/main/java/nostr/event/json/codec/ArrayNodeCollector.java delete mode 100644 nostr-java-event/src/main/java/nostr/event/json/codec/Collectors.java delete mode 100644 nostr-java-event/src/main/java/nostr/event/json/codec/ObjectNodeCollector.java create mode 100644 nostr-java-test/src/test/java/nostr/test/filters/FiltersListDecoderTest.java create mode 100644 nostr-java-test/src/test/java/nostr/test/filters/FiltersListEncoderTest.java diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java index 364ffacf2..3386d1c85 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java @@ -1,7 +1,6 @@ package nostr.event.filter; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import lombok.NonNull; import nostr.base.PublicKey; @@ -46,11 +45,6 @@ public T getFilterCriterion() { return addressableTag; } - @Override - public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNodeString(objectNode); - } - @Override public String getFilterKey() { return filterKey; diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java index 8bf977e7e..dfd0d185a 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/AuthorFilter.java @@ -1,6 +1,5 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; @@ -27,11 +26,6 @@ public T getFilterCriterion() { return publicKey; } - @Override - public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNodeString(objectNode); - } - @Override public String getFilterKey() { return filterKey; diff --git a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java index 8cea885ba..c6fae8a50 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/EventFilter.java @@ -1,6 +1,5 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; @@ -26,11 +25,6 @@ public T getFilterCriterion() { return event; } - @Override - public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNodeString(objectNode); - } - @Override public String getFilterKey() { return filterKey; diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java index 7b7abf800..8b4e9cee4 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java @@ -1,25 +1,22 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.NonNull; import nostr.event.BaseTag; import nostr.event.impl.GenericEvent; -import nostr.event.json.codec.ArrayNodeCollector; -import nostr.event.json.codec.Collectors; -import nostr.event.json.codec.ObjectNodeCollector; import java.util.List; import java.util.Optional; import java.util.function.Predicate; +import java.util.function.Supplier; public interface Filterable { ObjectMapper mapper = new ObjectMapper(); Predicate getPredicate(); T getFilterCriterion(); - ObjectNode toObjectNode(ObjectNode objectNode); Object getFilterableValue(); String getFilterKey(); @@ -30,66 +27,35 @@ default List getTypeSpecificTags(Class tagClass, Gener .toList(); } - default ObjectNode processArrayNodeString(ObjectNode objectNode) { - Optional jsonNode1 = Optional.ofNullable(objectNode.get(getFilterKey())); - - ArrayNodeCollector arrayNodeCollector = Collectors.toArrayNode(); - - ArrayNode arrayNode = arrayNodeCollector.supplier().get(); - jsonNode1.ifPresent(jsonNode -> { - jsonNode.elements().forEachRemaining(jsonNode2 -> - arrayNodeCollector.accumulator().accept(arrayNode, jsonNode2)); - }); - - ArrayNode arrayNode1 = mapper.createArrayNode(); - ArrayNode add2 = arrayNode1.add(getFilterableValue().toString()); - - arrayNodeCollector.combiner().apply(arrayNode, add2); - - objectNode.set(getFilterKey(), arrayNode); - - return objectNode; + default ObjectNode toObjectNode(ObjectNode objectNode) { + return processArrayNode(objectNode, this::getFilterableValue); } - default ObjectNode processArrayNodeInt(ObjectNode objectNode) { - Optional jsonNode1 = Optional.ofNullable(objectNode.get(getFilterKey())); + default ObjectNode processArrayNode(@NonNull ObjectNode objectNode, Supplier objectSupplier) { + ArrayNode arrayNode = mapper.createArrayNode(); - ArrayNodeCollector arrayNodeCollector = Collectors.toArrayNode(); + Optional.ofNullable(objectNode.get(getFilterKey())) + .ifPresent(jsonNode -> + jsonNode.elements().forEachRemaining(arrayNode::add)); - ArrayNode arrayNode = arrayNodeCollector.supplier().get(); - jsonNode1.ifPresent(jsonNode -> { - jsonNode.elements().forEachRemaining(jsonNode2 -> - arrayNodeCollector.accumulator().accept(arrayNode, jsonNode2)); - }); + arrayNode.addAll( + mapper.createArrayNode().add( + objectSupplier.get().toString())); - ArrayNode arrayNode1 = mapper.createArrayNode(); - ArrayNode add2 = arrayNode1.add(Integer.valueOf(getFilterableValue().toString())); - - arrayNodeCollector.combiner().apply(arrayNode, add2); - - objectNode.set(getFilterKey(), arrayNode); - - return objectNode; + return objectNode.set(getFilterKey(), arrayNode); } - default ObjectNode processObjectNode(String key, ObjectNode objectNode) { - Optional jsonNode1 = Optional.ofNullable(objectNode.get(getFilterKey())); - - ObjectNodeCollector objectNodeCollector = Collectors.toObjectNode(); - - ObjectNode objectNode1 = objectNodeCollector.supplier().get(); - jsonNode1.ifPresent(jsonNode -> { - jsonNode.elements().forEachRemaining(jsonNode2 -> - objectNodeCollector.accumulator().accept(objectNode1, jsonNode2)); - }); - - ObjectNode objectNode2 = mapper.createObjectNode(); + default ObjectNode processArrayNodeIntRxR(@NonNull ObjectNode objectNode, Supplier integerSupplier) { + ArrayNode arrayNode = mapper.createArrayNode(); -// TODO: hack string to int, needs fix - ObjectNode add2 = objectNode2.put(key, Long.valueOf(getFilterableValue().toString())); + Optional.ofNullable(objectNode.get(getFilterKey())) + .ifPresent(jsonNode -> + jsonNode.elements().forEachRemaining(arrayNode::add)); - objectNodeCollector.combiner().apply(objectNode1, add2); + arrayNode.addAll( + mapper.createArrayNode().add( + integerSupplier.get())); - return add2; + return objectNode.set(getFilterKey(), arrayNode); } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java b/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java index e3497b062..a8276ad4a 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java @@ -8,20 +8,30 @@ import nostr.event.tag.IdentifierTag; import java.util.List; +import java.util.function.Function; +import java.util.stream.StreamSupport; public class FilterableProvider { public static List getFilterable(String type, JsonNode node) { return switch (type) { - case ReferencedPublicKeyFilter.filterKey -> Filters.getFilterable(node, referencedPubKey -> new ReferencedPublicKeyFilter<>(new PublicKey(referencedPubKey.asText()))); - case ReferencedEventFilter.filterKey -> Filters.getFilterable(node, referencedEvent -> new ReferencedEventFilter<>(new GenericEvent(referencedEvent.asText()))); - case AddressableTagFilter.filterKey -> Filters.getFilterable(node, addressableTag -> new AddressableTagFilter<>(AddressableTagFilter.createAddressTag(addressableTag))); - case IdentifierTagFilter.filterKey -> Filters.getFilterable(node, identifierTag -> new IdentifierTagFilter<>(new IdentifierTag(identifierTag.asText()))); - case AuthorFilter.filterKey -> Filters.getFilterable(node, author -> new AuthorFilter<>(new PublicKey(author.asText()))); - case EventFilter.filterKey -> Filters.getFilterable(node, event -> new EventFilter<>(new GenericEvent(event.asText()))); - case SinceFilter.filterKey -> Filters.getFilterable(node, since -> new SinceFilter(since.asLong())); - case UntilFilter.filterKey -> Filters.getFilterable(node, until -> new UntilFilter(until.asLong())); - case KindFilter.filterKey -> Filters.getFilterable(node, kindNode -> new KindFilter<>(Kind.valueOf(kindNode.asInt()))); - default -> Filters.getFilterable(node, genericNode -> new GenericTagQueryFilter<>(new GenericTagQuery(type, genericNode.asText()))); + case ReferencedPublicKeyFilter.filterKey -> getFilterable(node, referencedPubKey -> new ReferencedPublicKeyFilter<>(new PublicKey(referencedPubKey.asText()))); + case ReferencedEventFilter.filterKey -> getFilterable(node, referencedEvent -> new ReferencedEventFilter<>(new GenericEvent(referencedEvent.asText()))); + case AddressableTagFilter.filterKey -> getFilterable(node, addressableTag -> new AddressableTagFilter<>(AddressableTagFilter.createAddressTag(addressableTag))); + case IdentifierTagFilter.filterKey -> getFilterable(node, identifierTag -> new IdentifierTagFilter<>(new IdentifierTag(identifierTag.asText()))); + case AuthorFilter.filterKey -> getFilterable(node, author -> new AuthorFilter<>(new PublicKey(author.asText()))); + case EventFilter.filterKey -> getFilterable(node, event -> new EventFilter<>(new GenericEvent(event.asText()))); + case KindFilter.filterKey -> getFilterable(node, kindNode -> new KindFilter<>(Kind.valueOf(kindNode.asInt()))); + case SinceFilter.filterKey -> List.of(new SinceFilter(node.asLong())); + case UntilFilter.filterKey -> List.of(new UntilFilter(node.asLong())); + default -> getFilterable(node, genericNode -> new GenericTagQueryFilter<>(new GenericTagQuery(type, genericNode.asText()))); }; } + /// / .orElseThrow(() -> + /// / new IllegalArgumentException( + /// / String.format("[%s] filter must contain at least one element"))) +// } + + public static List getFilterable(JsonNode jsonNode, Function filterFunction) { + return StreamSupport.stream(jsonNode.spliterator(), false).map(filterFunction).toList(); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java index ea2f9c67b..f78c93324 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java @@ -1,18 +1,12 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.JsonNode; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; import lombok.Setter; -import lombok.SneakyThrows; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; @EqualsAndHashCode public class Filters { @@ -27,6 +21,7 @@ public Filters(Map> filtersMap) { this.filtersMap = filtersMap; } +// TODO: unused public void addFilterable(@NonNull String key, @NonNull Filterable... filterable) { addFilterable(key, List.of(filterable)); } @@ -39,36 +34,30 @@ public List getFilterableByType(@NonNull String type) { return filtersMap.get(type); } - public List getFilterCriterion(@NonNull String type) { - return Optional - .ofNullable( - getFilterableByType(type)) - .stream().flatMap(filterables -> - filterables.stream().map(filterable -> - (T) filterable.getFilterCriterion())) - .toList(); - } - - @SneakyThrows - public void setFilterableListByType( - @NonNull String key, - @NonNull List filterTypeList, - @NonNull Function filterableFunction) { - - if (filterTypeList.isEmpty()) { - throw new IllegalArgumentException( - String.format("[%s] filter must contain at least one element", key)); - } - - addFilterable( - key, - filterTypeList.stream().map(filterableFunction).collect(Collectors.toList())); -// .orElseThrow(() -> -// new IllegalArgumentException( -// String.format("[%s] filter must contain at least one element"))) - } - - public static List getFilterable(JsonNode jsonNode, Function filterFunction) { - return StreamSupport.stream(jsonNode.spliterator(), false).map(filterFunction).toList(); - } + // TODO: no tests currently call below... +// public List getFilterCriterion(@NonNull String type) { +// return Optional +// .ofNullable( +// getFilterableByType(type)) +// .stream().flatMap(filterables -> +// filterables.stream().map(filterable -> +//// TODO: ...which leavesw below uncalled as well. needs testing +// (T) filterable.getFilterCriterion())) +// .toList(); +// } + +// @SneakyThrows +// public void setFilterableListByType( +// @NonNull String key, +// @NonNull List filterTypeList, +// @NonNull Function filterableFunction) { +// +// if (filterTypeList.isEmpty()) { +// throw new IllegalArgumentException( +// String.format("[%s] filter must contain at least one element", key)); +// } +// +// addFilterable( +// key, +// filterTypeList.stream().map(filterableFunction).collect(Collectors.toList())); } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java index a90251f6f..324ba6557 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/GenericTagQueryFilter.java @@ -1,6 +1,5 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.base.ElementAttribute; import nostr.base.GenericTagQuery; @@ -37,11 +36,6 @@ public T getFilterCriterion() { return genericTagQuery; } - @Override - public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNodeString(objectNode); - } - @Override public String getFilterKey() { return genericTagQuery.getTagName(); diff --git a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java index 4f079c159..6efc2b1ff 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/IdentifierTagFilter.java @@ -1,6 +1,5 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; import nostr.event.tag.IdentifierTag; @@ -28,11 +27,6 @@ public T getFilterCriterion() { return identifierTag; } - @Override - public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNodeString(objectNode); - } - @Override public String getFilterKey() { return filterKey; diff --git a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java index 3ac250b9d..aaf2131c2 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java @@ -6,6 +6,7 @@ import nostr.event.impl.GenericEvent; import java.util.function.Predicate; +import java.util.function.Supplier; @EqualsAndHashCode public class KindFilter implements Filterable { @@ -29,7 +30,7 @@ public T getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNodeInt(objectNode); + return processArrayNodeIntRxR(objectNode, () -> Integer.valueOf(getFilterableValue().toString())); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java index da7e5b42c..4ac62b0db 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedEventFilter.java @@ -1,6 +1,5 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.event.impl.GenericEvent; import nostr.event.tag.EventTag; @@ -29,11 +28,6 @@ public T getFilterCriterion() { return referencedEvent; } - @Override - public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNodeString(objectNode); - } - @Override public String getFilterKey() { return filterKey; diff --git a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java index 641b419e4..4dc4eab03 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/ReferencedPublicKeyFilter.java @@ -1,6 +1,5 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.EqualsAndHashCode; import nostr.base.PublicKey; import nostr.event.impl.GenericEvent; @@ -30,11 +29,6 @@ public T getFilterCriterion() { return referencedPublicKey; } - @Override - public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNodeString(objectNode); - } - @Override public String getFilterKey() { return filterKey; diff --git a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java index 6c82cbd08..6d8cd1831 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java @@ -28,7 +28,7 @@ public Long getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return processObjectNode(filterKey, objectNode); + return mapper.createObjectNode().put(filterKey, since); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java index f2869d1b4..35b61237a 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java @@ -28,7 +28,7 @@ public Long getFilterCriterion() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return processObjectNode(filterKey, objectNode); + return mapper.createObjectNode().put(filterKey, until); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/ArrayNodeCollector.java b/nostr-java-event/src/main/java/nostr/event/json/codec/ArrayNodeCollector.java deleted file mode 100644 index c987ee49d..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/ArrayNodeCollector.java +++ /dev/null @@ -1,42 +0,0 @@ -package nostr.event.json.codec; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; - -import java.util.EnumSet; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.BinaryOperator; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collector; - -public class ArrayNodeCollector implements Collector { - private static final ObjectMapper mapper = new ObjectMapper(); - - @Override - public Supplier supplier() { - return mapper::createArrayNode; - } - - @Override - public BiConsumer accumulator() { - return ArrayNode::add; - } - - @Override - public BinaryOperator combiner() { - return ArrayNode::addAll; - } - - @Override - public Function finisher() { - return accumulator -> accumulator; - } - - @Override - public Set characteristics() { - return EnumSet.of(Characteristics.UNORDERED); - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/Collectors.java b/nostr-java-event/src/main/java/nostr/event/json/codec/Collectors.java deleted file mode 100644 index d00458f77..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/Collectors.java +++ /dev/null @@ -1,21 +0,0 @@ -package nostr.event.json.codec; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; - -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -public class Collectors { - public static ArrayNodeCollector toArrayNode() { - return new ArrayNodeCollector(); - } - - public static ObjectNodeCollector toObjectNode() { - return new ObjectNodeCollector(); - } - - public static Stream toStream(ArrayNode arrayNode) { - return StreamSupport.stream(arrayNode.spliterator(), false); - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/ObjectNodeCollector.java b/nostr-java-event/src/main/java/nostr/event/json/codec/ObjectNodeCollector.java deleted file mode 100644 index 62ece4ceb..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/ObjectNodeCollector.java +++ /dev/null @@ -1,45 +0,0 @@ -package nostr.event.json.codec; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import java.util.EnumSet; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.BinaryOperator; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collector; - -public class ObjectNodeCollector implements Collector { - private static final ObjectMapper mapper = new ObjectMapper(); - - @Override - public Supplier supplier() { - return mapper::createObjectNode; - } - - @Override - public BiConsumer accumulator() { - return (objectNode, jsonNode) -> { - objectNode.set("someval", jsonNode); - }; - } - - @Override - public BinaryOperator combiner() { -// ArrayNode::addAll; - return ObjectNode::setAll; - } - - @Override - public Function finisher() { - return accumulator -> accumulator; - } - - @Override - public Set characteristics() { - return EnumSet.of(Characteristics.UNORDERED); - } -} diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersListDecoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersListDecoderTest.java new file mode 100644 index 000000000..884685659 --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersListDecoderTest.java @@ -0,0 +1,12 @@ +//package nostr.test.filters; +// +//import org.junit.jupiter.api.Test; +// +//import static org.junit.jupiter.api.Assertions.fail; +// +//public class FiltersListDecoderTest { +// @Test +// public void testFiltersListDecoder() { +// fail(); +// } +//} diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersListEncoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersListEncoderTest.java new file mode 100644 index 000000000..19a6687eb --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersListEncoderTest.java @@ -0,0 +1,12 @@ +//package nostr.test.filters; +// +//import org.junit.jupiter.api.Test; +// +//import static org.junit.jupiter.api.Assertions.fail; +// +//public class FiltersListEncoderTest { +// @Test +// public void testFiltersListEncoder() { +// fail(); +// } +//} From 2b8ef5bbaff580f2e7adceda4b32fad657781347 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Tue, 18 Feb 2025 12:24:12 -0800 Subject: [PATCH 25/45] ongoing filters --- .../test/filters/FiltersDecoderTest.java | 304 +++++++++++++++++- .../test/filters/FiltersEncoderTest.java | 229 ++++++++++++- .../java/nostr/test/json/JsonParseTest.java | 229 ++++++------- 3 files changed, 633 insertions(+), 129 deletions(-) diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java index aaaac2e64..198c5a381 100644 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java @@ -2,12 +2,27 @@ import lombok.extern.java.Log; import nostr.base.GenericTagQuery; +import nostr.base.PublicKey; +import nostr.event.Kind; +import nostr.event.filter.AddressableTagFilter; +import nostr.event.filter.EventFilter; import nostr.event.filter.Filterable; import nostr.event.filter.Filters; import nostr.event.filter.GenericTagQueryFilter; +import nostr.event.filter.IdentifierTagFilter; +import nostr.event.filter.KindFilter; +import nostr.event.filter.ReferencedEventFilter; +import nostr.event.filter.ReferencedPublicKeyFilter; +import nostr.event.filter.SinceFilter; +import nostr.event.filter.UntilFilter; +import nostr.event.impl.GenericEvent; import nostr.event.json.codec.FiltersDecoder; +import nostr.event.tag.AddressTag; +import nostr.event.tag.IdentifierTag; import org.junit.jupiter.api.Test; +import java.time.Instant; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -16,6 +31,260 @@ @Log public class FiltersDecoderTest { + + @Test + public void testEventFiltersDecoder() { + log.info("testEventFiltersDecoder"); + + String filterKey = EventFilter.filterKey; + String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + + String expected = "{\"" + filterKey + "\":[\"" + eventId + "\"]}"; + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, List.of(new EventFilter<>(new GenericEvent(eventId)))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testMultipleEventFiltersDecoder() { + log.info("testMultipleEventFiltersDecoder"); + + String filterKey = EventFilter.filterKey; + String eventId1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String eventId2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + + String joined = String.join("\",\"", eventId1, eventId2); + + String expected = "{\"" + filterKey + "\":[\"" + joined + "\"]}"; + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, + List.of( + new EventFilter<>(new GenericEvent(eventId1)), + new EventFilter<>(new GenericEvent(eventId2)))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + + @Test + public void testAddressableTagFiltersDecoder() { + log.info("testAddressableTagFiltersDecoder"); + + String filterKey = AddressableTagFilter.filterKey; + Integer kind = 1; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String uuidValue1 = "UUID-1"; + + String joined = String.join(":", String.valueOf(kind), author, uuidValue1); + + AddressTag addressTag = new AddressTag(); + addressTag.setKind(kind); + addressTag.setPublicKey(new PublicKey(author)); + addressTag.setIdentifierTag(new IdentifierTag(uuidValue1)); + + String expected = "{\"#a\":[\"" + joined + "\"]}"; + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, List.of(new AddressableTagFilter<>(addressTag))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testMultipleAddressableTagFiltersDecoder() { + log.info("testMultipleAddressableTagFiltersDecoder"); + + String filterKey = AddressableTagFilter.filterKey; + Integer kind1 = 1; + String author1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String uuidValue1 = "UUID-1"; + + Integer kind2 = 1; + String author2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + String uuidValue2 = "UUID-2"; + + AddressTag addressTag1 = new AddressTag(); + addressTag1.setKind(kind1); + addressTag1.setPublicKey(new PublicKey(author1)); + addressTag1.setIdentifierTag(new IdentifierTag(uuidValue1)); + + AddressTag addressTag2 = new AddressTag(); + addressTag2.setKind(kind2); + addressTag2.setPublicKey(new PublicKey(author2)); + addressTag2.setIdentifierTag(new IdentifierTag(uuidValue2)); + + String joined1 = String.join(":", String.valueOf(kind1), author1, uuidValue1); + String joined2 = String.join(":", String.valueOf(kind2), author2, uuidValue2); + + String joined3 = String.join("\",\"", joined1, joined2); + + String expected = "{\"#a\":[\"" + joined3 + "\"]}"; + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, + List.of( + new AddressableTagFilter<>(addressTag1), + new AddressableTagFilter<>(addressTag2))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testKindFiltersDecoder() { + log.info("testKindFiltersDecoder"); + + String filterKey = KindFilter.filterKey; + Kind kind = Kind.valueOf(1); + + String expected = "{\"" + filterKey + "\":[" + kind.toString() + "]}"; + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, List.of(new KindFilter<>(kind))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testMultipleKindFiltersDecoder() { + log.info("testMultipleKindFiltersDecoder"); + + String filterKey = KindFilter.filterKey; + Kind kind1 = Kind.valueOf(1); + Kind kind2 = Kind.valueOf(2); + + String join = String.join(",", kind1.toString(), kind2.toString()); + + String expected = "{\"" + filterKey + "\":[" + join + "]}"; + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, List.of( + new KindFilter<>(kind1), + new KindFilter<>(kind2))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testIdentifierTagFilterDecoder() { + log.info("testIdentifierTagFilterDecoder"); + + String filterKey = IdentifierTagFilter.filterKey; + String uuidValue1 = "UUID-1"; + + String expected = "{\"#d\":[\"" + uuidValue1 + "\"]}"; + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, List.of(new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testMultipleIdentifierTagFilterDecoder() { + log.info("testMultipleIdentifierTagFilterDecoder"); + + String filterKey = IdentifierTagFilter.filterKey; + String uuidValue1 = "UUID-1"; + String uuidValue2 = "UUID-2"; + + String joined = String.join("\",\"", uuidValue1, uuidValue2); + String expected = "{\"#d\":[\"" + joined + "\"]}"; + + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, + List.of( + new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)), + new IdentifierTagFilter<>(new IdentifierTag(uuidValue2)))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testReferencedEventFilterDecoder() { + log.info("testReferencedEventFilterDecoder"); + + String filterKey = ReferencedEventFilter.filterKey; + String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + + String expected = "{\"#e\":[\"" + eventId + "\"]}"; + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, List.of(new ReferencedEventFilter<>(new GenericEvent(eventId)))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testMultipleReferencedEventFilterDecoder() { + log.info("testMultipleReferencedEventFilterDecoder"); + + String filterKey = ReferencedEventFilter.filterKey; + String eventId1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String eventId2 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + + String joined = String.join("\",\"", eventId1, eventId2); + String expected = "{\"#e\":[\"" + joined + "\"]}"; + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, + List.of( + new ReferencedEventFilter<>(new GenericEvent(eventId1)), + new ReferencedEventFilter<>(new GenericEvent(eventId2)))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testReferencedPublicKeyFilterDecoder() { + log.info("testReferencedPublicKeyFilterDecoder"); + + String filterKey = ReferencedPublicKeyFilter.filterKey; + String pubkeyString = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + + String expected = "{\"#p\":[\"" + pubkeyString + "\"]}"; + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, List.of(new ReferencedPublicKeyFilter<>(new PublicKey(pubkeyString)))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testMultipleReferencedPublicKeyFilterDecoder() { + log.info("testMultipleReferencedPublicKeyFilterDecoder"); + + String filterKey = ReferencedPublicKeyFilter.filterKey; + String pubkeyString1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String pubkeyString2 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + + String joined = String.join("\",\"", pubkeyString1, pubkeyString2); + String expected = "{\"#p\":[\"" + joined + "\"]}"; + + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, List.of( + new ReferencedPublicKeyFilter<>(new PublicKey(pubkeyString1)), + new ReferencedPublicKeyFilter<>(new PublicKey(pubkeyString2)))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + @Test public void testGenericTagFiltersDecoder() { log.info("testGenericTagFiltersDecoder"); @@ -33,8 +302,8 @@ public void testGenericTagFiltersDecoder() { } @Test - public void testGenericTagFiltersListDecoder() { - log.info("testGenericTagFiltersListDecoder"); + public void testMultipleGenericTagFiltersDecoder() { + log.info("testMultipleGenericTagFiltersDecoder"); String geohashKey = "#g"; String geohashValue1 = "2vghde"; @@ -51,4 +320,35 @@ public void testGenericTagFiltersListDecoder() { assertEquals(new Filters(expectedFilters), decodedFilters); } + @Test + public void testSinceFiltersDecoder() { + log.info("testSinceFiltersDecoder"); + + String filterKey = SinceFilter.filterKey; + Long since = Date.from(Instant.now()).getTime(); + + String expected = "{\"since\":" + since + "}"; + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, List.of(new SinceFilter(since))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } + + @Test + public void testUntilFiltersDecoder() { + log.info("testUntilFiltersDecoder"); + + String filterKey = UntilFilter.filterKey; + Long until = Date.from(Instant.now()).getTime(); + + String expected = "{\"until\":" + until + "}"; + Filters decodedFilters = new FiltersDecoder<>().decode(expected); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(filterKey, List.of(new UntilFilter(until))); + + assertEquals(new Filters(expectedFilters), decodedFilters); + } } diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java index a79deac90..5edeb30a5 100644 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java @@ -3,16 +3,27 @@ import lombok.extern.java.Log; import nostr.base.GenericTagQuery; import nostr.base.PublicKey; +import nostr.event.Kind; import nostr.event.filter.AddressableTagFilter; +import nostr.event.filter.AuthorFilter; +import nostr.event.filter.EventFilter; import nostr.event.filter.Filterable; import nostr.event.filter.Filters; import nostr.event.filter.GenericTagQueryFilter; +import nostr.event.filter.IdentifierTagFilter; +import nostr.event.filter.KindFilter; +import nostr.event.filter.ReferencedEventFilter; +import nostr.event.filter.ReferencedPublicKeyFilter; import nostr.event.filter.SinceFilter; +import nostr.event.filter.UntilFilter; +import nostr.event.impl.GenericEvent; import nostr.event.json.codec.FiltersEncoder; import nostr.event.tag.AddressTag; import nostr.event.tag.IdentifierTag; import org.junit.jupiter.api.Test; +import java.time.Instant; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -22,6 +33,97 @@ @Log public class FiltersEncoderTest { + @Test + public void testEventFilterEncoder() { + log.info("testEventFilterEncoder"); + + Map> expectedFilters = new HashMap<>(); + + String filterKey = EventFilter.filterKey; + String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + expectedFilters.put(filterKey, + List.of( + new EventFilter<>(new GenericEvent(eventId)))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals("{\"" + filterKey + "\":[\"" + eventId + "\"]}", jsonMessage); + } + + @Test + public void testKindFiltersEncoder() { + log.info("testKindFiltersEncoder"); + + Map> expectedFilters = new HashMap<>(); + Kind kind = Kind.valueOf(1); + + String filterKey = KindFilter.filterKey; + expectedFilters.put(filterKey, + List.of( + new KindFilter<>(kind))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals("{\"" + filterKey + "\":[" + kind.toString() + "]}", jsonMessage); + } + + @Test + public void testAuthorFilterEncoder() { + log.info("testAuthorFilterEncoder"); + + String pubKeyString = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(AuthorFilter.filterKey, + List.of( + new AuthorFilter<>(new PublicKey(pubKeyString)))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals("{\"authors\":[\"" + pubKeyString + "\"]}", jsonMessage); + } + + @Test + public void testMultipleAuthorFilterEncoder() { + log.info("testMultipleAuthorFilterEncoder"); + + String pubKeyString1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String pubKeyString2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(AuthorFilter.filterKey, + List.of( + new AuthorFilter<>(new PublicKey(pubKeyString1)), + new AuthorFilter<>(new PublicKey(pubKeyString2)))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + + String joined = String.join("\",\"", pubKeyString1, pubKeyString2); + + assertEquals("{\"authors\":[\"" + joined + "\"]}", jsonMessage); + } + + @Test + public void testMultipleKindFiltersEncoder() { + log.info("testMultipleKindFiltersEncoder"); + + Map> expectedFilters = new HashMap<>(); + Kind kind1 = Kind.valueOf(1); + Kind kind2 = Kind.valueOf(2); + + String filterKey = KindFilter.filterKey; + expectedFilters.put(filterKey, + List.of( + new KindFilter<>(kind1), + new KindFilter<>(kind2))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + String join = String.join(",", kind1.toString(), kind2.toString()); + assertEquals("{\"" + filterKey + "\":[" + join + "]}", jsonMessage); + } + @Test public void testAddressableTagFilterEncoder() { log.info("testAddressableTagFilterEncoder"); @@ -45,9 +147,80 @@ public void testAddressableTagFilterEncoder() { FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String jsonMessage = encoder.encode(); +// TODO: make sure below should be #d/#a and not the opposite assertEquals("{\"#a\":[\"" + joined + "\"]}", jsonMessage); } + @Test + public void testIdentifierTagFilterEncoder() { + log.info("testIdentifierTagFilterEncoder"); + + String uuidValue1 = "UUID-1"; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(IdentifierTagFilter.filterKey, + List.of( + new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals("{\"#d\":[\"" + uuidValue1 + "\"]}", jsonMessage); + } + + @Test + public void testMultipleIdentifierTagFilterEncoder() { + log.info("testMultipleIdentifierTagFilterEncoder"); + + String uuidValue1 = "UUID-1"; + String uuidValue2 = "UUID-2"; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(IdentifierTagFilter.filterKey, + List.of( + new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)), + new IdentifierTagFilter<>(new IdentifierTag(uuidValue2)))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + String joined = String.join("\",\"", uuidValue1, uuidValue2); + assertEquals("{\"#d\":[\"" + joined + "\"]}", jsonMessage); + } + + @Test + public void testReferencedEventFilterEncoder() { + log.info("testReferencedEventFilterEncoder"); + + String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(ReferencedEventFilter.filterKey, + List.of( + new ReferencedEventFilter<>(new GenericEvent(eventId)))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals("{\"#e\":[\"" + eventId + "\"]}", jsonMessage); + } + + @Test + public void testMultipleReferencedEventFilterEncoder() { + log.info("testMultipleReferencedEventFilterEncoder"); + + String eventId1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String eventId2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(ReferencedEventFilter.filterKey, + List.of( + new ReferencedEventFilter<>(new GenericEvent(eventId1)), + new ReferencedEventFilter<>(new GenericEvent(eventId2)))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + String joined = String.join("\",\"", eventId1, eventId2); + assertEquals("{\"#e\":[\"" + joined + "\"]}", jsonMessage); + } + @Test public void testSingleGenericTagQueryFiltersEncoder() { log.info("testSingleGenericTagQueryFiltersEncoder"); @@ -65,6 +238,41 @@ public void testSingleGenericTagQueryFiltersEncoder() { assertEquals("{\"#g\":[\"2vghde\"]}", jsonMessage); } + @Test + public void testReferencedPublicKeyFilterEncoder() { + log.info("testReferencedPublicKeyFilterEncoder"); + + String pubKeyString = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(ReferencedPublicKeyFilter.filterKey, + List.of( + new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString)))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals("{\"#p\":[\"" + pubKeyString + "\"]}", jsonMessage); + } + + @Test + public void testMultipleReferencedPublicKeyFilterEncoder() { + log.info("testMultipleReferencedPublicKeyFilterEncoder"); + + String pubKeyString1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String pubKeyString2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(ReferencedPublicKeyFilter.filterKey, + List.of( + new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString1)), + new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString2)))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + String joined = String.join("\",\"", pubKeyString1, pubKeyString2); + assertEquals("{\"#p\":[\"" + joined + "\"]}", jsonMessage); + } + @Test public void testMultipleGenericTagQueryFiltersEncoder() { log.info("testMultipleGenericTagQueryFiltersEncoder"); @@ -126,7 +334,7 @@ public void testSinceFiltersEncoder() { log.info("testSinceFiltersEncoder"); String sinceKey = SinceFilter.filterKey; - Long since = 1111111111L; + Long since = Date.from(Instant.now()).getTime(); Map> expectedFilters = new HashMap<>(); expectedFilters.put(sinceKey, @@ -135,6 +343,23 @@ public void testSinceFiltersEncoder() { FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String jsonMessage = encoder.encode(); - assertEquals("{\"" + sinceKey + "\":" + since + "}", jsonMessage); + assertEquals("{\"since\":" + since + "}", jsonMessage); + } + + @Test + public void testUntilFiltersEncoder() { + log.info("testUntilFiltersEncoder"); + + String untilKey = UntilFilter.filterKey; + Long until = Date.from(Instant.now()).getTime(); + + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(untilKey, + List.of( + new UntilFilter(until))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String jsonMessage = encoder.encode(); + assertEquals("{\"until\":" + until + "}", jsonMessage); } } 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 b462dfef9..762028311 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 @@ -16,19 +16,19 @@ import nostr.event.Marker; import nostr.event.filter.AddressableTagFilter; import nostr.event.filter.AuthorFilter; +import nostr.event.filter.EventFilter; import nostr.event.filter.Filterable; import nostr.event.filter.Filters; import nostr.event.filter.GenericTagQueryFilter; import nostr.event.filter.IdentifierTagFilter; import nostr.event.filter.KindFilter; import nostr.event.filter.ReferencedEventFilter; +import nostr.event.filter.ReferencedPublicKeyFilter; import nostr.event.impl.GenericEvent; import nostr.event.impl.GenericTag; import nostr.event.json.codec.BaseEventEncoder; import nostr.event.json.codec.BaseMessageDecoder; import nostr.event.json.codec.BaseTagDecoder; -import nostr.event.json.codec.FiltersDecoder; -import nostr.event.json.codec.FiltersEncoder; import nostr.event.json.codec.GenericEventDecoder; import nostr.event.json.codec.GenericTagDecoder; import nostr.event.message.EventMessage; @@ -62,8 +62,73 @@ public class JsonParseTest { ObjectMapper mapper = new ObjectMapper(); @Test - public void testBaseMessageDecoder() throws JsonProcessingException { - log.info("testBaseMessageDecoder"); + public void testBaseMessageDecoderEventFilter() throws JsonProcessingException { + log.info("testBaseMessageDecoderEventFilter"); + + String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + final String parseTarget = + "[\"REQ\", " + + "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + + "{\"kinds\": [1], " + + "\"ids\": [\"" + eventId + "\"]," + + "\"#p\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]"; + + final var message = new BaseMessageDecoder<>().decode(parseTarget); + + assertEquals(Command.REQ.toString(), message.getCommand()); + assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", ((ReqMessage) message).getSubscriptionId()); + assertEquals(1, ((ReqMessage) message).getFiltersList().size()); + + Filters filters = ((ReqMessage) message).getFiltersList().getFirst(); + + List kindFilters = filters.getFilterableByType(KindFilter.filterKey); + assertEquals(1, kindFilters.size()); + assertEquals(new KindFilter<>(Kind.TEXT_NOTE), kindFilters.getFirst()); + + List eventFilter = filters.getFilterableByType(EventFilter.filterKey); + assertEquals(1, eventFilter.size()); + assertEquals(new EventFilter<>(new GenericEvent("f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75")), eventFilter.getFirst()); + + List referencedPublicKeyfilter = filters.getFilterableByType(ReferencedPublicKeyFilter.filterKey); + assertEquals(1, referencedPublicKeyfilter.size()); + assertEquals(new ReferencedPublicKeyFilter<>(new PublicKey("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712")), referencedPublicKeyfilter.getFirst()); + } + + @Test + public void testBaseMessageDecoderKindsAuthorsReferencedPublicKey() throws JsonProcessingException { + log.info("testBaseMessageDecoderKindsAuthorsReferencedPublicKey"); + + final String parseTarget = + "[\"REQ\", " + + "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + + "{\"kinds\": [1], " + + "\"authors\": [\"f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75\"]," + + "\"#p\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]"; + + final var message = new BaseMessageDecoder<>().decode(parseTarget); + + assertEquals(Command.REQ.toString(), message.getCommand()); + assertEquals("npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh", ((ReqMessage) message).getSubscriptionId()); + assertEquals(1, ((ReqMessage) message).getFiltersList().size()); + + Filters filters = ((ReqMessage) message).getFiltersList().getFirst(); + + List kindFilters = filters.getFilterableByType(KindFilter.filterKey); + assertEquals(1, kindFilters.size()); + assertEquals(new KindFilter<>(Kind.TEXT_NOTE), kindFilters.getFirst()); + + List authorFilters = filters.getFilterableByType(AuthorFilter.filterKey); + assertEquals(1, authorFilters.size()); + assertEquals(new AuthorFilter<>(new PublicKey("f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75")), authorFilters.getFirst()); + + List referencedPublicKeyfilter = filters.getFilterableByType(ReferencedPublicKeyFilter.filterKey); + assertEquals(1, referencedPublicKeyfilter.size()); + assertEquals(new ReferencedPublicKeyFilter<>(new PublicKey("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712")), referencedPublicKeyfilter.getFirst()); + } + + @Test + public void testBaseMessageDecoderKindsAuthorsReferencedEvents() throws JsonProcessingException { + log.info("testBaseMessageDecoderKindsAuthorsReferencedEvents"); final String parseTarget = "[\"REQ\", " + @@ -120,6 +185,9 @@ public void testBaseReqMessageDecoder() throws JsonProcessingException { String jsonMessage = expectedReqMessage.encode(); String jsonMsg = jsonMessage.substring(1, jsonMessage.length() - 1); + + System.out.println(jsonMessage); + String[] parts = jsonMsg.split(","); assertEquals("\"REQ\"", parts[0]); assertEquals("\"" + publicKey.toHexString() + "\"", parts[1]); @@ -301,22 +369,6 @@ public void testDeserializeGenericTag() throws NostrException { assertEquals("gt", gTag.getCode()); } - @Test - public void testFiltersEncoder() { - log.info("testFiltersEncoder"); - - String new_geohash = "2vghde"; - - Map> expectedFilters = new HashMap<>(); - expectedFilters.put("#g", - List.of( - new GenericTagQueryFilter<>(new GenericTagQuery("#g", new_geohash)))); - - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - assertEquals("{\"#g\":[\"2vghde\"]}", jsonMessage); - } - @Test public void testReqMessageFilterListSerializer() { log.info("testReqMessageFilterListSerializer"); @@ -338,41 +390,6 @@ public void testReqMessageFilterListSerializer() { }); } - @Test - public void testGenericTagFiltersDecoder() { - log.info("testGenericTagFiltersDecoder"); - - String geohashKey = "#g"; - String geohashValue = "2vghde"; - String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue + "\"]}"; - - Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); - - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(geohashKey, List.of(new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue)))); - - assertEquals(new Filters(expectedFilters), decodedFilters); - } - - @Test - public void testGenericTagFiltersListDecoder() { - log.info("testGenericTagFiltersListDecoder"); - - String geohashKey = "#g"; - String geohashValue1 = "2vghde"; - String geohashValue2 = "3abcde"; - String reqJsonWithCustomTagQueryFilterToDecode = "{\"" + geohashKey + "\":[\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]}"; - - Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); - - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(geohashKey, List.of( - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); - - assertEquals(new Filters(expectedFilters), decodedFilters); - } - @Test public void testReqMessageDeserializer() throws JsonProcessingException { log.info("testReqMessageDeserializer"); @@ -434,7 +451,9 @@ public void testReqMessagePopulatedFilterDecoder() { "{\"kinds\": [" + kind + "], " + "\"authors\": [\"" + author + "\"]," + "\"" + geohashKey + "\": [\"" + geohashValue1 + "\",\"" + geohashValue2 + "\"]," + - "\"#e\": [\"" + referencedEventId + "\"]}]"; + "\"#e\": [\"" + referencedEventId + "\"]," + + "\"#p\": [\"" + author + "\"]" + + "}]"; assertDoesNotThrow(() -> { ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); @@ -456,7 +475,12 @@ public void testReqMessagePopulatedFilterDecoder() { new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); + expectedFilters.put(ReferencedPublicKeyFilter.filterKey, + List.of( + new ReferencedPublicKeyFilter<>(new PublicKey(author)))); + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); + assertEquals(expectedReqMessage, decodedReqMessage); }); } @@ -519,23 +543,39 @@ public void testReqMessagePopulatedListOfFiltersListDecoder() throws JsonProcess log.info("testReqMessagePopulatedListOfFiltersListDecoder"); String subscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh"; - String kind = "1"; + Integer kind = 1; String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String referencedEventId = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712"; + String uuidValue1 = "UUID-1"; + + String addressableTag = String.join(":", String.valueOf(kind), author, uuidValue1); + String reqJsonWithCustomTagQueryFilterToDecode = "[\"REQ\", " + "\"" + subscriptionId + "\", " + "{\"kinds\": [" + kind + "], " + "\"authors\": [\"" + author + "\"]," + - "\"#e\": [\"" + referencedEventId + "\"]}]"; + "\"#e\": [\"" + referencedEventId + "\"]," + + "\"#a\": [\"" + addressableTag + "\"]," + + "\"#p\": [\"" + author + "\"]" + + "}]"; ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); - Map> filterablesMap = new HashMap<>(); - filterablesMap.put(KindFilter.filterKey, List.of(new KindFilter<>(Kind.TEXT_NOTE))); - filterablesMap.put(AuthorFilter.filterKey, List.of(new AuthorFilter<>(new PublicKey(author)))); - filterablesMap.put(ReferencedEventFilter.filterKey, List.of(new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(filterablesMap)); + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(KindFilter.filterKey, List.of(new KindFilter<>(Kind.TEXT_NOTE))); + expectedFilters.put(AuthorFilter.filterKey, List.of(new AuthorFilter<>(new PublicKey(author)))); + expectedFilters.put(ReferencedEventFilter.filterKey, List.of(new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); + expectedFilters.put(ReferencedPublicKeyFilter.filterKey, List.of( new ReferencedPublicKeyFilter<>(new PublicKey(author)))); + + AddressTag addressTag1 = new AddressTag(); + addressTag1.setKind(kind); + addressTag1.setPublicKey(new PublicKey(author)); + addressTag1.setIdentifierTag(new IdentifierTag(uuidValue1)); + + expectedFilters.put(AddressableTagFilter.filterKey, List.of(new AddressableTagFilter<>(addressTag1))); + + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); assertEquals(expectedReqMessage.encode(), decodedReqMessage.encode()); assertEquals(expectedReqMessage, decodedReqMessage); @@ -641,67 +681,6 @@ public void testGenericTagQueryListDecoder() throws JsonProcessingException { assertEquals(expectedReqMessage, decodedReqMessage); } - @Test - public void testAddressableTagFilterEncoder() { - log.info("testAddressableTagFilterEncoder"); - - Integer kind = 1; - String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; - String uuidKey = "#d"; - String uuidValue1 = "UUID-1"; - - String joined = String.join(":", String.valueOf(kind), author, uuidValue1); - - AddressTag addressTag = new AddressTag(); - addressTag.setKind(kind); - addressTag.setPublicKey(new PublicKey(author)); - addressTag.setIdentifierTag(new IdentifierTag(uuidValue1)); - - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(uuidKey, - List.of( - new AddressableTagFilter<>(addressTag))); - - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - assertEquals("{\"#d\":[\"" + joined + "\"]}", jsonMessage); - } - - @Test - public void testMultipleAddressableTagFilterEncoder() { - log.info("testMultipleAddressableTagFilterEncoder"); - - Integer kind = 1; - String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; - String uuidKey = "#a"; - String uuidValue1 = "UUID-1"; - String uuidValue2 = "UUID-2"; - - String joined1 = String.join(":", String.valueOf(kind), author, uuidValue1); - String joined2 = String.join(":", String.valueOf(kind), author, uuidValue2); - - AddressTag addressTag1 = new AddressTag(); - addressTag1.setKind(kind); - addressTag1.setPublicKey(new PublicKey(author)); - addressTag1.setIdentifierTag(new IdentifierTag(uuidValue1)); - - AddressTag addressTag2 = new AddressTag(); - addressTag2.setKind(kind); - addressTag2.setPublicKey(new PublicKey(author)); - addressTag2.setIdentifierTag(new IdentifierTag(uuidValue2)); - - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(uuidKey, - List.of( - new AddressableTagFilter<>(addressTag1), - new AddressableTagFilter<>(addressTag2))); - - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - String joinedTags = String.join("\",\"", joined1, joined2); - assertEquals("{\"#a\":[\"" + joinedTags + "\"]}", jsonMessage); - } - @Test public void testReqMessageAddressableTagDeserializer() throws JsonProcessingException { log.info("testReqMessageAddressableTagDeserializer"); @@ -711,7 +690,6 @@ public void testReqMessageAddressableTagDeserializer() throws JsonProcessingExce String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String uuidKey = "#a"; String uuidValue1 = "UUID-1"; - String uuidValue2 = "UUID-2"; String joined1 = String.join(":", String.valueOf(kind), author, uuidValue1); @@ -734,3 +712,4 @@ public void testReqMessageAddressableTagDeserializer() throws JsonProcessingExce assertEquals(expectedReqMessage, decodedReqMessage); } } + From 36f24b6cb514e5a46964cbcb684950d7a6aec26b Mon Sep 17 00:00:00 2001 From: nick avlo Date: Tue, 18 Feb 2025 12:24:39 -0800 Subject: [PATCH 26/45] ongoing filters --- .../java/nostr/test/event/DecodeTest.java | 174 +++++++++--------- 1 file changed, 90 insertions(+), 84 deletions(-) diff --git a/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java b/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java index b75ecdd49..1850e4d01 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java @@ -1,84 +1,90 @@ -package nostr.test.event; - -import nostr.base.PublicKey; -import nostr.event.BaseMessage; -import nostr.event.BaseTag; -import nostr.event.Marker; -import nostr.event.impl.GenericEvent; -import nostr.event.json.codec.BaseMessageDecoder; -import nostr.event.message.EventMessage; -import nostr.event.tag.EventTag; -import nostr.event.tag.PubKeyTag; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -public class DecodeTest { - - @Test - public void decodeTest() { - - String json = "[" - + "\"EVENT\"," - + "\"temp20230627\"," - + "{" - + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\"," - + "\"kind\":1," - + "\"pubkey\":\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"," - + "\"created_at\":1687765220," - + "\"content\":\"手順書が間違ってたら作業者は無理だな\"," - + "\"tags\":[" - + "[\"e\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346\",\"\",\"root\"]," - + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"]" - + "]," - + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"" - + "}]"; - - BaseMessage message = new BaseMessageDecoder<>().decode(json); - - Assertions.assertEquals("EVENT", message.getCommand()); - EventMessage eventMessage = (EventMessage) message; - - Assertions.assertEquals("temp20230627", eventMessage.getSubscriptionId()); - GenericEvent eventImpl = (GenericEvent) eventMessage.getEvent(); - - Assertions.assertEquals("28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a", eventImpl.getId()); - Assertions.assertEquals(1, eventImpl.getKind()); - Assertions.assertEquals("2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984", eventImpl.getPubKey().toString()); - Assertions.assertEquals(1687765220, eventImpl.getCreatedAt()); - Assertions.assertEquals("手順書が間違ってたら作業者は無理だな", eventImpl.getContent()); - Assertions.assertEquals("86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546", - eventImpl.getSignature().toString()); - - List expectedTags = new ArrayList<>(); - EventTag eventTag = new EventTag("494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346"); - eventTag.setRecommendedRelayUrl(""); - eventTag.setMarker(Marker.ROOT); - expectedTags.add(eventTag); - PubKeyTag pubKeyTag = new PubKeyTag(); - pubKeyTag.setPublicKey(new PublicKey("2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984")); - expectedTags.add(pubKeyTag); - - List actualTags = eventImpl.getTags(); - - for (int i = 0; i < expectedTags.size(); i++) { - BaseTag expected = expectedTags.get(i); - if (expected instanceof EventTag expetedEventTag) { - EventTag actualEventTag = (EventTag) actualTags.get(i); - Assertions.assertEquals(expetedEventTag.getIdEvent(), actualEventTag.getIdEvent()); - Assertions.assertEquals(expetedEventTag.getRecommendedRelayUrl(), actualEventTag.getRecommendedRelayUrl()); - Assertions.assertEquals(expetedEventTag.getMarker(), actualEventTag.getMarker()); - } else if (expected instanceof PubKeyTag expectedPublicKeyTag) { - PubKeyTag actualPublicKeyTag = (PubKeyTag) actualTags.get(i); - Assertions.assertEquals(expectedPublicKeyTag.getPublicKey().toString(), actualPublicKeyTag.getPublicKey().toString()); - } else { - Assertions.fail(); - } - - } - - } - -} +package nostr.test.event; + +import com.fasterxml.jackson.core.JsonProcessingException; +import nostr.base.PublicKey; +import nostr.event.BaseMessage; +import nostr.event.BaseTag; +import nostr.event.Marker; +import nostr.event.impl.GenericEvent; +import nostr.event.json.codec.BaseMessageDecoder; +import nostr.event.message.EventMessage; +import nostr.event.tag.EventTag; +import nostr.event.tag.PubKeyTag; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.fail; + +public class DecodeTest { + + @Test + public void decodeTest() throws JsonProcessingException { + + String json = "[" + + "\"EVENT\"," + + "\"temp20230627\"," + + "{" + + "\"id\":\"28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a\"," + + "\"kind\":1," + + "\"pubkey\":\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"," + + "\"created_at\":1687765220," + + "\"content\":\"手順書が間違ってたら作業者は無理だな\"," + + "\"tags\":[" + + "[\"e\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346\",\"\",\"root\"]," + + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984\"]" + + "]," + + "\"sig\":\"86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546\"" + + "}]"; + + BaseMessage message = new BaseMessageDecoder<>().decode(json); + + assertEquals("EVENT", message.getCommand()); + assertInstanceOf(EventMessage.class, message); + + EventMessage eventMessage = (EventMessage) message; + + assertEquals("temp20230627", eventMessage.getSubscriptionId()); + GenericEvent eventImpl = (GenericEvent) eventMessage.getEvent(); + + assertEquals("28f2fc030e335d061f0b9d03ce0e2c7d1253e6fadb15d89bd47379a96b2c861a", eventImpl.getId()); + assertEquals(1, eventImpl.getKind()); + assertEquals("2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984", eventImpl.getPubKey().toString()); + assertEquals(1687765220, eventImpl.getCreatedAt()); + assertEquals("手順書が間違ってたら作業者は無理だな", eventImpl.getContent()); + assertEquals("86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546", + eventImpl.getSignature().toString()); + + List expectedTags = new ArrayList<>(); + EventTag eventTag = new EventTag("494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4346"); + eventTag.setRecommendedRelayUrl(""); + eventTag.setMarker(Marker.ROOT); + expectedTags.add(eventTag); + PubKeyTag pubKeyTag = new PubKeyTag(); + pubKeyTag.setPublicKey(new PublicKey("2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76984")); + expectedTags.add(pubKeyTag); + + List actualTags = eventImpl.getTags(); + + for (int i = 0; i < expectedTags.size(); i++) { + BaseTag expected = expectedTags.get(i); + if (expected instanceof EventTag expetedEventTag) { + EventTag actualEventTag = (EventTag) actualTags.get(i); + assertEquals(expetedEventTag.getIdEvent(), actualEventTag.getIdEvent()); + assertEquals(expetedEventTag.getRecommendedRelayUrl(), actualEventTag.getRecommendedRelayUrl()); + assertEquals(expetedEventTag.getMarker(), actualEventTag.getMarker()); + } else if (expected instanceof PubKeyTag expectedPublicKeyTag) { + PubKeyTag actualPublicKeyTag = (PubKeyTag) actualTags.get(i); + assertEquals(expectedPublicKeyTag.getPublicKey().toString(), actualPublicKeyTag.getPublicKey().toString()); + } else { + fail(); + } + + } + + } + +} From c1a2637e4a2c82bee667a71e7a1cad94b08b5a43 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Tue, 18 Feb 2025 13:12:41 -0800 Subject: [PATCH 27/45] ongoing filters --- .../java/nostr/event/filter/Filterable.java | 22 +++---------------- .../java/nostr/event/filter/KindFilter.java | 8 +++---- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java index 8b4e9cee4..002ded0ba 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filterable.java @@ -3,14 +3,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import lombok.NonNull; import nostr.event.BaseTag; import nostr.event.impl.GenericEvent; import java.util.List; import java.util.Optional; import java.util.function.Predicate; -import java.util.function.Supplier; public interface Filterable { ObjectMapper mapper = new ObjectMapper(); @@ -28,34 +26,20 @@ default List getTypeSpecificTags(Class tagClass, Gener } default ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNode(objectNode, this::getFilterableValue); - } - - default ObjectNode processArrayNode(@NonNull ObjectNode objectNode, Supplier objectSupplier) { ArrayNode arrayNode = mapper.createArrayNode(); Optional.ofNullable(objectNode.get(getFilterKey())) .ifPresent(jsonNode -> jsonNode.elements().forEachRemaining(arrayNode::add)); - arrayNode.addAll( - mapper.createArrayNode().add( - objectSupplier.get().toString())); + addToArrayNode(arrayNode); return objectNode.set(getFilterKey(), arrayNode); } - default ObjectNode processArrayNodeIntRxR(@NonNull ObjectNode objectNode, Supplier integerSupplier) { - ArrayNode arrayNode = mapper.createArrayNode(); - - Optional.ofNullable(objectNode.get(getFilterKey())) - .ifPresent(jsonNode -> - jsonNode.elements().forEachRemaining(arrayNode::add)); - + default void addToArrayNode(ArrayNode arrayNode) { arrayNode.addAll( mapper.createArrayNode().add( - integerSupplier.get())); - - return objectNode.set(getFilterKey(), arrayNode); + getFilterableValue().toString())); } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java index aaf2131c2..500cbd80c 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java @@ -1,12 +1,11 @@ package nostr.event.filter; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.ArrayNode; import lombok.EqualsAndHashCode; import nostr.event.Kind; import nostr.event.impl.GenericEvent; import java.util.function.Predicate; -import java.util.function.Supplier; @EqualsAndHashCode public class KindFilter implements Filterable { @@ -29,8 +28,9 @@ public T getFilterCriterion() { } @Override - public ObjectNode toObjectNode(ObjectNode objectNode) { - return processArrayNodeIntRxR(objectNode, () -> Integer.valueOf(getFilterableValue().toString())); + public void addToArrayNode(ArrayNode arrayNode) { + arrayNode.addAll( + mapper.createArrayNode().add(getFilterableValue())); } @Override From 286b451e1037aa8c35198209cd3ae868ac2ff357 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Tue, 18 Feb 2025 13:28:04 -0800 Subject: [PATCH 28/45] ongoing filters --- .../java/nostr/event/filter/KindFilter.java | 3 +- .../test/filters/FiltersEncoderTest.java | 92 +++++++++---------- 2 files changed, 46 insertions(+), 49 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java index 500cbd80c..d018a913d 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/KindFilter.java @@ -30,7 +30,8 @@ public T getFilterCriterion() { @Override public void addToArrayNode(ArrayNode arrayNode) { arrayNode.addAll( - mapper.createArrayNode().add(getFilterableValue())); + mapper.createArrayNode().add( + getFilterableValue())); } @Override diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java index 5edeb30a5..1eacc4f99 100644 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java @@ -46,8 +46,8 @@ public void testEventFilterEncoder() { new EventFilter<>(new GenericEvent(eventId)))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - assertEquals("{\"" + filterKey + "\":[\"" + eventId + "\"]}", jsonMessage); + String encodedFilters = encoder.encode(); + assertEquals("{\"ids\":[\"" + eventId + "\"]}", encodedFilters); } @Test @@ -63,8 +63,8 @@ public void testKindFiltersEncoder() { new KindFilter<>(kind))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - assertEquals("{\"" + filterKey + "\":[" + kind.toString() + "]}", jsonMessage); + String encodedFilters = encoder.encode(); + assertEquals("{\"kinds\":[" + kind.toString() + "]}", encodedFilters); } @Test @@ -79,8 +79,8 @@ public void testAuthorFilterEncoder() { new AuthorFilter<>(new PublicKey(pubKeyString)))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - assertEquals("{\"authors\":[\"" + pubKeyString + "\"]}", jsonMessage); + String encodedFilters = encoder.encode(); + assertEquals("{\"authors\":[\"" + pubKeyString + "\"]}", encodedFilters); } @Test @@ -97,11 +97,10 @@ public void testMultipleAuthorFilterEncoder() { new AuthorFilter<>(new PublicKey(pubKeyString2)))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); + String encodedFilters = encoder.encode(); + String authorPubKeys = String.join("\",\"", pubKeyString1, pubKeyString2); - String joined = String.join("\",\"", pubKeyString1, pubKeyString2); - - assertEquals("{\"authors\":[\"" + joined + "\"]}", jsonMessage); + assertEquals("{\"authors\":[\"" + authorPubKeys + "\"]}", encodedFilters); } @Test @@ -119,9 +118,9 @@ public void testMultipleKindFiltersEncoder() { new KindFilter<>(kind2))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - String join = String.join(",", kind1.toString(), kind2.toString()); - assertEquals("{\"" + filterKey + "\":[" + join + "]}", jsonMessage); + String encodedFilters = encoder.encode(); + String kinds = String.join(",", kind1.toString(), kind2.toString()); + assertEquals("{\"kinds\":[" + kinds + "]}", encodedFilters); } @Test @@ -133,8 +132,6 @@ public void testAddressableTagFilterEncoder() { String uuidKey = "#d"; String uuidValue1 = "UUID-1"; - String joined = String.join(":", String.valueOf(kind), author, uuidValue1); - AddressTag addressTag = new AddressTag(); addressTag.setKind(kind); addressTag.setPublicKey(new PublicKey(author)); @@ -146,9 +143,10 @@ public void testAddressableTagFilterEncoder() { new AddressableTagFilter<>(addressTag))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); -// TODO: make sure below should be #d/#a and not the opposite - assertEquals("{\"#a\":[\"" + joined + "\"]}", jsonMessage); + String encodedFilters = encoder.encode(); + String addressableTag = String.join(":", String.valueOf(kind), author, uuidValue1); + + assertEquals("{\"#a\":[\"" + addressableTag + "\"]}", encodedFilters); } @Test @@ -163,8 +161,8 @@ public void testIdentifierTagFilterEncoder() { new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - assertEquals("{\"#d\":[\"" + uuidValue1 + "\"]}", jsonMessage); + String encodedFilters = encoder.encode(); + assertEquals("{\"#d\":[\"" + uuidValue1 + "\"]}", encodedFilters); } @Test @@ -181,9 +179,9 @@ public void testMultipleIdentifierTagFilterEncoder() { new IdentifierTagFilter<>(new IdentifierTag(uuidValue2)))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - String joined = String.join("\",\"", uuidValue1, uuidValue2); - assertEquals("{\"#d\":[\"" + joined + "\"]}", jsonMessage); + String encodedFilters = encoder.encode(); + String dTags = String.join("\",\"", uuidValue1, uuidValue2); + assertEquals("{\"#d\":[\"" + dTags + "\"]}", encodedFilters); } @Test @@ -198,8 +196,8 @@ public void testReferencedEventFilterEncoder() { new ReferencedEventFilter<>(new GenericEvent(eventId)))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - assertEquals("{\"#e\":[\"" + eventId + "\"]}", jsonMessage); + String encodedFilters = encoder.encode(); + assertEquals("{\"#e\":[\"" + eventId + "\"]}", encodedFilters); } @Test @@ -216,9 +214,9 @@ public void testMultipleReferencedEventFilterEncoder() { new ReferencedEventFilter<>(new GenericEvent(eventId2)))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - String joined = String.join("\",\"", eventId1, eventId2); - assertEquals("{\"#e\":[\"" + joined + "\"]}", jsonMessage); + String encodedFilters = encoder.encode(); + String eventIds = String.join("\",\"", eventId1, eventId2); + assertEquals("{\"#e\":[\"" + eventIds + "\"]}", encodedFilters); } @Test @@ -234,8 +232,8 @@ public void testSingleGenericTagQueryFiltersEncoder() { new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, new_geohash)))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - assertEquals("{\"#g\":[\"2vghde\"]}", jsonMessage); + String encodedFilters = encoder.encode(); + assertEquals("{\"#g\":[\"2vghde\"]}", encodedFilters); } @Test @@ -250,8 +248,8 @@ public void testReferencedPublicKeyFilterEncoder() { new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString)))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - assertEquals("{\"#p\":[\"" + pubKeyString + "\"]}", jsonMessage); + String encodedFilters = encoder.encode(); + assertEquals("{\"#p\":[\"" + pubKeyString + "\"]}", encodedFilters); } @Test @@ -268,9 +266,9 @@ public void testMultipleReferencedPublicKeyFilterEncoder() { new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString2)))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - String joined = String.join("\",\"", pubKeyString1, pubKeyString2); - assertEquals("{\"#p\":[\"" + joined + "\"]}", jsonMessage); + String encodedFilters = encoder.encode(); + String pubKeyTags = String.join("\",\"", pubKeyString1, pubKeyString2); + assertEquals("{\"#p\":[\"" + pubKeyTags + "\"]}", encodedFilters); } @Test @@ -288,10 +286,8 @@ public void testMultipleGenericTagQueryFiltersEncoder() { new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - assertEquals( - "{\"#g\":[\"2vghde\",\"3abcde\"]}" - , jsonMessage); + String encodedFilters = encoder.encode(); + assertEquals("{\"#g\":[\"2vghde\",\"3abcde\"]}", encodedFilters); } @Test @@ -304,8 +300,8 @@ public void testMultipleAddressableTagFilterEncoder() { String uuidValue1 = "UUID-1"; String uuidValue2 = "UUID-2"; - String joined1 = String.join(":", String.valueOf(kind), author, uuidValue1); - String joined2 = String.join(":", String.valueOf(kind), author, uuidValue2); + String addressableTag1 = String.join(":", String.valueOf(kind), author, uuidValue1); + String addressableTag2 = String.join(":", String.valueOf(kind), author, uuidValue2); AddressTag addressTag1 = new AddressTag(); addressTag1.setKind(kind); @@ -324,9 +320,9 @@ public void testMultipleAddressableTagFilterEncoder() { new AddressableTagFilter<>(addressTag2))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - String joinedTags = String.join("\",\"", joined1, joined2); - assertEquals("{\"#a\":[\"" + joinedTags + "\"]}", jsonMessage); + String encoded = encoder.encode(); + String addressableTags = String.join("\",\"", addressableTag1, addressableTag2); + assertEquals("{\"#a\":[\"" + addressableTags + "\"]}", encoded); } @Test @@ -342,8 +338,8 @@ public void testSinceFiltersEncoder() { new SinceFilter(since))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - assertEquals("{\"since\":" + since + "}", jsonMessage); + String encodedFilters = encoder.encode(); + assertEquals("{\"since\":" + since + "}", encodedFilters); } @Test @@ -359,7 +355,7 @@ public void testUntilFiltersEncoder() { new UntilFilter(until))); FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); - String jsonMessage = encoder.encode(); - assertEquals("{\"until\":" + until + "}", jsonMessage); + String encodedFilters = encoder.encode(); + assertEquals("{\"until\":" + until + "}", encodedFilters); } } From 58275201bd631839d61f62938103ec28879143a0 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Tue, 18 Feb 2025 14:00:47 -0800 Subject: [PATCH 29/45] ongoing filters --- .../nostr/event/json/codec/FiltersEncoder.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java index feadf6217..a74827363 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java @@ -1,16 +1,11 @@ package nostr.event.json.codec; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.SneakyThrows; import nostr.base.FEncoder; import nostr.event.filter.Filters; -import java.util.ArrayList; -import java.util.List; - @Data @EqualsAndHashCode(callSuper = false) public class FiltersEncoder implements FEncoder { @@ -20,21 +15,17 @@ public FiltersEncoder(Filters filters) { this.filters = filters; } - @SneakyThrows @Override public String encode() { - List result = new ArrayList<>(); + ObjectNode root = MAPPER.createObjectNode(); filters.getFiltersMap().forEach((key, filterableList) -> { final ObjectNode objectNode = MAPPER.createObjectNode(); - ObjectNode list = filterableList.stream().map(filterable -> filterable.toObjectNode(objectNode)).toList().getFirst(); - result.add(list); + root.setAll( + filterableList.stream().map( + filterable -> filterable.toObjectNode(objectNode)).findFirst().orElseThrow()); }); - ObjectMapper mapper = new ObjectMapper(); - ObjectNode root = mapper.createObjectNode(); - result.forEach(root::setAll); - System.out.println(root.toPrettyString()); return root.toString(); } } From 3af2cbea7a98709cf23db51110e6c696130433c4 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Tue, 18 Feb 2025 14:41:59 -0800 Subject: [PATCH 30/45] ongoing tests --- .../main/java/nostr/event/json/codec/FiltersEncoder.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java index a74827363..8a439e963 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java @@ -22,8 +22,12 @@ public String encode() { filters.getFiltersMap().forEach((key, filterableList) -> { final ObjectNode objectNode = MAPPER.createObjectNode(); root.setAll( - filterableList.stream().map( - filterable -> filterable.toObjectNode(objectNode)).findFirst().orElseThrow()); + filterableList + .stream() + .map(filterable -> + filterable.toObjectNode(objectNode)) + .toList() + .getFirst()); }); return root.toString(); From 405f78f05d34d7079e2e975d57f8238cd70a2f69 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Tue, 18 Feb 2025 16:04:45 -0800 Subject: [PATCH 31/45] ongoing tests --- .../event/filter/AddressableTagFilter.java | 26 ++++++++++--------- .../event/filter/FilterableProvider.java | 9 +++---- .../test/filters/FiltersDecoderTest.java | 15 +++++++++++ 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java index 3386d1c85..2e013b123 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java @@ -50,16 +50,20 @@ public String getFilterKey() { return filterKey; } - public static AddressTag createAddressTag(JsonNode addressableTag) { - List list = Arrays.stream(addressableTag.asText().split(":")).toList(); + public static AddressTag createAddressTag(@NonNull JsonNode addressableTag) throws IllegalArgumentException { + try { + List list = Arrays.stream(addressableTag.asText().split(":")).toList(); -// TODO: add validation - AddressTag addressTag = new AddressTag(); - addressTag.setKind(Integer.valueOf(list.getFirst())); - addressTag.setPublicKey(new PublicKey(list.get(1))); - addressTag.setIdentifierTag(new IdentifierTag(list.get(2))); + AddressTag addressTag = new AddressTag(); + addressTag.setKind(Integer.valueOf(list.getFirst())); + addressTag.setPublicKey(new PublicKey(list.get(1))); + addressTag.setIdentifierTag(new IdentifierTag(list.get(2))); - return addressTag; + return addressTag; + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + String.format("malformed JsonNode addressable tag: [%s]", addressableTag.asText()), e); + } } @Override @@ -67,11 +71,9 @@ public String getFilterableValue() { Integer kind = addressableTag.getKind(); String hexString = addressableTag.getPublicKey().toHexString(); String id = addressableTag.getIdentifierTag().getId(); -// String uri = addressableTag.getRelay().getUri(); - String collected = Stream.of(kind, hexString, id).map(Object::toString) + return Stream.of(kind, hexString, id) + .map(Object::toString) .collect(Collectors.joining(":")); - - return collected; } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java b/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java index a8276ad4a..c93221b57 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java @@ -20,16 +20,13 @@ public static List getFilterable(String type, JsonNode node) { case IdentifierTagFilter.filterKey -> getFilterable(node, identifierTag -> new IdentifierTagFilter<>(new IdentifierTag(identifierTag.asText()))); case AuthorFilter.filterKey -> getFilterable(node, author -> new AuthorFilter<>(new PublicKey(author.asText()))); case EventFilter.filterKey -> getFilterable(node, event -> new EventFilter<>(new GenericEvent(event.asText()))); - case KindFilter.filterKey -> getFilterable(node, kindNode -> new KindFilter<>(Kind.valueOf(kindNode.asInt()))); + case KindFilter.filterKey -> getFilterable(node, kindNode -> new KindFilter<>(Kind.valueOf(kindNode.asInt()))); case SinceFilter.filterKey -> List.of(new SinceFilter(node.asLong())); case UntilFilter.filterKey -> List.of(new UntilFilter(node.asLong())); - default -> getFilterable(node, genericNode -> new GenericTagQueryFilter<>(new GenericTagQuery(type, genericNode.asText()))); + default -> + getFilterable(node, genericNode -> new GenericTagQueryFilter<>(new GenericTagQuery(type, genericNode.asText()))); }; } - /// / .orElseThrow(() -> - /// / new IllegalArgumentException( - /// / String.format("[%s] filter must contain at least one element"))) -// } public static List getFilterable(JsonNode jsonNode, Function filterFunction) { return StreamSupport.stream(jsonNode.spliterator(), false).map(filterFunction).toList(); diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java index 198c5a381..db085fb99 100644 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java @@ -28,6 +28,7 @@ import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; @Log public class FiltersDecoderTest { @@ -351,4 +352,18 @@ public void testUntilFiltersDecoder() { assertEquals(new Filters(expectedFilters), decodedFilters); } + + @Test + public void testFailedAddressableTagMalformedSeparator() { + log.info("testFailedAddressableTagMalformedSeparator"); + + Integer kind = 1; + String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + String uuidValue1 = "UUID-1"; + + String malformedJoin = String.join(",", String.valueOf(kind), author, uuidValue1); + String expected = "{\"#a\":[\"" + malformedJoin + "\"]}"; + + assertThrows(IllegalArgumentException.class, () -> new FiltersDecoder<>().decode(expected)); + } } From d35e2ee7ad4efe8f69ca87db2266dedfd53b8176 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Tue, 18 Feb 2025 18:10:48 -0800 Subject: [PATCH 32/45] filters exception hanler testing --- .../event/filter/AddressableTagFilter.java | 26 +++---- .../main/java/nostr/event/filter/Filters.java | 24 +++++-- .../java/nostr/event/message/ReqMessage.java | 69 ++++++++----------- .../test/filters/FiltersEncoderTest.java | 34 +++++++++ .../java/nostr/test/json/JsonParseTest.java | 33 ++++++++- 5 files changed, 126 insertions(+), 60 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java index 2e013b123..b6181320b 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/AddressableTagFilter.java @@ -28,18 +28,6 @@ public Predicate getPredicate() { return this::compare; } - private boolean compare(@NonNull GenericEvent genericEvent) { - return - !genericEvent.getPubKey().toHexString().equals( - this.addressableTag.getPublicKey().toHexString()) || - !genericEvent.getKind().equals( - this.addressableTag.getKind()) || - getTypeSpecificTags(IdentifierTag.class, genericEvent).stream() - .anyMatch(identifierTag -> - identifierTag.getId().equals( - this.addressableTag.getIdentifierTag().getId())); - } - @Override public T getFilterCriterion() { return addressableTag; @@ -62,7 +50,7 @@ public static AddressTag createAddressTag(@NonNull JsonNode addressableTag) thro return addressTag; } catch (NumberFormatException e) { throw new IllegalArgumentException( - String.format("malformed JsonNode addressable tag: [%s]", addressableTag.asText()), e); + String.format("Malformed JsonNode addressable tag: [%s]", addressableTag.asText()), e); } } @@ -76,4 +64,16 @@ public String getFilterableValue() { .map(Object::toString) .collect(Collectors.joining(":")); } + + private boolean compare(@NonNull GenericEvent genericEvent) { + return + !genericEvent.getPubKey().toHexString().equals( + this.addressableTag.getPublicKey().toHexString()) || + !genericEvent.getKind().equals( + this.addressableTag.getKind()) || + getTypeSpecificTags(IdentifierTag.class, genericEvent).stream() + .anyMatch(identifierTag -> + identifierTag.getId().equals( + this.addressableTag.getIdentifierTag().getId())); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java index f78c93324..4ad6b9638 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java @@ -17,11 +17,12 @@ public class Filters { @Setter private Integer limit = 10; - public Filters(Map> filtersMap) { + public Filters(@NonNull Map> filtersMap) { + validateFiltersMap(filtersMap); this.filtersMap = filtersMap; } -// TODO: unused + // TODO: unused public void addFilterable(@NonNull String key, @NonNull Filterable... filterable) { addFilterable(key, List.of(filterable)); } @@ -34,14 +35,28 @@ public List getFilterableByType(@NonNull String type) { return filtersMap.get(type); } - // TODO: no tests currently call below... + private static void validateFiltersMap(Map> filtersMap) throws IllegalArgumentException { + filtersMap.values().forEach(filterables -> { + if (filterables.isEmpty()) { + throw new IllegalArgumentException("Filters cannot be empty."); + } + }); + + filtersMap.forEach((key, value) -> { + if (key.isEmpty()) + throw new IllegalArgumentException(String.format("Filter key for filterable [%s] is not defined", value.getFirst().getFilterKey())); + }); + } +} + +// TODO: no tests currently call below... // public List getFilterCriterion(@NonNull String type) { // return Optional // .ofNullable( // getFilterableByType(type)) // .stream().flatMap(filterables -> // filterables.stream().map(filterable -> -//// TODO: ...which leavesw below uncalled as well. needs testing +/// / TODO: ...which leavesw below uncalled as well. needs testing // (T) filterable.getFilterCriterion())) // .toList(); // } @@ -60,4 +75,3 @@ public List getFilterableByType(@NonNull String type) { // addFilterable( // key, // filterTypeList.stream().map(filterableFunction).collect(Collectors.toList())); -} diff --git a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java index 790e326b8..e5fa2222e 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; @@ -31,17 +30,15 @@ public class ReqMessage extends BaseMessage { @JsonProperty private final List filtersList; - public ReqMessage(String subscriptionId, Filters... filtersList) { + public ReqMessage(@NonNull String subscriptionId, Filters... filtersList) { this(subscriptionId, List.of(filtersList)); } - public ReqMessage(String subscriptionId, List filtersList) { + public ReqMessage(@NonNull String subscriptionId, List filtersList) { super(Command.REQ.name()); - if (!ValueRange.of(1, 64).isValidIntValue(subscriptionId.length())) { - throw new IllegalArgumentException(String.format("subscriptionId length must be between 1 and 64 characters but was [%d]", subscriptionId.length())); - } - this.filtersList = filtersList; + validateSubscriptionId(subscriptionId); this.subscriptionId = subscriptionId; + this.filtersList = filtersList; } @Override @@ -50,46 +47,36 @@ public String encode() throws JsonProcessingException { .add(getCommand()) .add(getSubscriptionId()); -// filtersList.stream() -// .map(FiltersEncoder::new) -// .map(FiltersEncoder::encode) -// .map(IEncoder.MAPPER::readTree) -// .forEach(jsonNode -> -// getArrayNode().add(jsonNode)); - -// TODO: remove below once above confirmed working - List encodedFilterList = filtersList.stream().map(FiltersEncoder::new).map(FiltersEncoder::encode).toList(); - - List jsonNodesList = encodedFilterList.stream().map( - encode -> { - try { - return IEncoder.MAPPER.readTree(encode); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - ).toList(); - - jsonNodesList.forEach(jsonNode -> { - ArrayNode arrayNode = getArrayNode(); - arrayNode.add(jsonNode); - }); + filtersList.stream() + .map(FiltersEncoder::new) + .map(FiltersEncoder::encode) + .map(ReqMessage::createJsonNode) + .forEach(jsonNode -> + getArrayNode().add(jsonNode)); - String s = IEncoder.MAPPER.writeValueAsString(getArrayNode()); - System.out.println(s); - return s; + return IEncoder.MAPPER.writeValueAsString(getArrayNode()); } public static T decode(@NonNull Object subscriptionId, @NonNull List jsonFiltersList) { - - ReqMessage reqMessage = new ReqMessage(subscriptionId.toString(), - jsonFiltersList.stream().map( - ReqMessage::createFiltersFromJson).toList()); - + validateSubscriptionId(subscriptionId.toString()); + ReqMessage reqMessage = new ReqMessage( + subscriptionId.toString(), + jsonFiltersList.stream().map(filtersList -> + new FiltersDecoder<>().decode(filtersList)).toList()); return (T) reqMessage; } - private static Filters createFiltersFromJson(String jsonFiltersList) { - return new FiltersDecoder<>().decode(jsonFiltersList); + private static void validateSubscriptionId(String subscriptionId) { + if (!ValueRange.of(1, 64).isValidIntValue(subscriptionId.length())) { + throw new IllegalArgumentException(String.format("SubscriptionId length must be between 1 and 64 characters but was [%d]", subscriptionId.length())); + } + } + + private static JsonNode createJsonNode(String jsonNode) { + try { + return IEncoder.MAPPER.readTree(jsonNode); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException(String.format("Malformed encoding ReqMessage json: [%s]", jsonNode), e); + } } } diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java index 1eacc4f99..6fa8bee57 100644 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java @@ -18,6 +18,7 @@ import nostr.event.filter.UntilFilter; import nostr.event.impl.GenericEvent; import nostr.event.json.codec.FiltersEncoder; +import nostr.event.message.ReqMessage; import nostr.event.tag.AddressTag; import nostr.event.tag.IdentifierTag; import org.junit.jupiter.api.Test; @@ -29,6 +30,7 @@ import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; @Log public class FiltersEncoderTest { @@ -358,4 +360,36 @@ public void testUntilFiltersEncoder() { String encodedFilters = encoder.encode(); assertEquals("{\"until\":" + until + "}", encodedFilters); } + + @Test + public void testReqMessageEmptyFilterMap() { + log.info("testReqMessageEmptyFilterMap"); + String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; + Map> emptyFilters = new HashMap<>(); + emptyFilters.put("", List.of()); + + assertThrows(IllegalArgumentException.class, () -> new ReqMessage(subscriptionId, new Filters(emptyFilters))); + } + + @Test + public void testReqMessageEmptyFilters() { + log.info("testReqMessageEmptyFilters"); + String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; + Map> emptyFilters = new HashMap<>(); + emptyFilters.put("#g", List.of()); + + assertThrows(IllegalArgumentException.class, () -> new ReqMessage(subscriptionId, new Filters(emptyFilters))); + } + + @Test + public void testReqMessageEmptyFilterKey() { + log.info("testReqMessageEmptyFilterKey"); + String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; + Map> emptyFilterKey = new HashMap<>(); + emptyFilterKey.put("", + List.of( + new GenericTagQueryFilter<>(new GenericTagQuery("some-tag", "some-value")))); + + assertThrows(IllegalArgumentException.class, () -> new ReqMessage(subscriptionId, new Filters(emptyFilterKey))); + } } 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 762028311..64ea529f7 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 @@ -52,6 +52,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -566,7 +567,7 @@ public void testReqMessagePopulatedListOfFiltersListDecoder() throws JsonProcess expectedFilters.put(KindFilter.filterKey, List.of(new KindFilter<>(Kind.TEXT_NOTE))); expectedFilters.put(AuthorFilter.filterKey, List.of(new AuthorFilter<>(new PublicKey(author)))); expectedFilters.put(ReferencedEventFilter.filterKey, List.of(new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); - expectedFilters.put(ReferencedPublicKeyFilter.filterKey, List.of( new ReferencedPublicKeyFilter<>(new PublicKey(author)))); + expectedFilters.put(ReferencedPublicKeyFilter.filterKey, List.of(new ReferencedPublicKeyFilter<>(new PublicKey(author)))); AddressTag addressTag1 = new AddressTag(); addressTag1.setKind(kind); @@ -711,5 +712,35 @@ public void testReqMessageAddressableTagDeserializer() throws JsonProcessingExce assertEquals(expectedReqMessage.encode(), decodedReqMessage.encode()); assertEquals(expectedReqMessage, decodedReqMessage); } + + @Test + public void testReqMessageSubscriptionIdTooLong() { + log.info("testReqMessageSubscriptionIdTooLong"); + + String malformedSubscriptionId = "npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujhaa"; + final String parseTarget = + "[\"REQ\", " + + "\"" + malformedSubscriptionId + "\", " + + "{\"kinds\": [1], " + + "\"authors\": [\"f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75\"]," + + "\"#p\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]"; + + assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(parseTarget)); + } + + @Test + public void testReqMessageSubscriptionIdTooShort() { + log.info("testReqMessageSubscriptionIdTooShort"); + + String malformedSubscriptionId = ""; + final String parseTarget = + "[\"REQ\", " + + "\"" + malformedSubscriptionId + "\", " + + "{\"kinds\": [1], " + + "\"authors\": [\"f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75\"]," + + "\"#p\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]"; + + assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(parseTarget)); + } } From 8ed2146a073787b4313071a3378e50d455e13420 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Tue, 18 Feb 2025 19:07:04 -0800 Subject: [PATCH 33/45] since until fixes --- .../src/main/java/nostr/event/filter/SinceFilter.java | 2 +- .../src/main/java/nostr/event/filter/UntilFilter.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java index 6d8cd1831..fb06f049e 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/SinceFilter.java @@ -18,7 +18,7 @@ public SinceFilter(Long since) { @Override public Predicate getPredicate() { return (genericEvent) -> - this.since >= genericEvent.getCreatedAt(); + this.since < genericEvent.getCreatedAt(); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java index 35b61237a..54c9e2a99 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/UntilFilter.java @@ -18,7 +18,7 @@ public UntilFilter(Long until) { @Override public Predicate getPredicate() { return (genericEvent) -> - this.until < genericEvent.getCreatedAt(); + this.until >= genericEvent.getCreatedAt(); } @Override From fdeb9f074f8a7cd8889ae23df207003ad1027c44 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Wed, 19 Feb 2025 12:55:51 -0800 Subject: [PATCH 34/45] removed nostr-java-command-provider, replaced by NostrSpringWebSocketClient removed nostr-java-controller, unused NostrSpringWebSocketClient updates Filters updates APINIP09EventTest use updated Filters mechanism FailableStream introduced for easier exception handling in streams removed deprecated/unused methods from NIP01 --- nostr-java-api/pom.xml | 6 +- nostr-java-api/src/main/java/module-info.java | 8 +- .../src/main/java/nostr/api/EventNostr.java | 19 +++-- .../src/main/java/nostr/api/NIP01.java | 53 +----------- .../src/main/java/nostr/api/NostrIF.java | 14 +-- .../nostr/api/NostrSpringWebSocketClient.java | 67 +++++++++++---- .../nostr/api/WebSocketClientHandler.java | 23 +++-- nostr-java-command-provider/pom.xml | 41 --------- .../provider/AbstractCommandHandler.java | 14 --- .../command/provider/AuthCommandHandler.java | 61 ------------- .../provider/ClosedCommandHandler.java | 62 -------------- .../command/provider/EoseCommandHandler.java | 23 ----- .../command/provider/EventCommandHandler.java | 23 ----- .../provider/NoticeCommandHandler.java | 23 ----- .../command/provider/OkCommandHandler.java | 23 ----- .../services/nostr.command.CommandHandler | 6 -- nostr-java-controller/pom.xml | 46 ---------- .../src/main/java/module-info.java | 18 ---- .../controller/ApplicationController.java | 4 - .../java/nostr/controller/Controller.java | 11 --- .../impl/ApplicationControllerImpl.java | 85 ------------------- .../main/java/nostr/event/filter/Filters.java | 39 +-------- .../event/json/codec/BaseMessageDecoder.java | 42 ++++----- .../java/nostr/event/message/ReqMessage.java | 1 + .../java/nostr/examples/NostrApiExamples.java | 24 ++++-- nostr-java-test/pom.xml | 9 +- .../main/java/nostr/test/EntityFactory.java | 12 +-- .../nostr/test/event/APINIP09EventTest.java | 52 ++++++------ .../event/BaseMessageCommandMapperTest.java | 72 ++++++++++++++++ .../java/nostr/util/thread/ThreadUtil.java | 73 ---------------- pom.xml | 4 +- 31 files changed, 239 insertions(+), 719 deletions(-) delete mode 100644 nostr-java-command-provider/pom.xml delete mode 100644 nostr-java-command-provider/src/main/java/nostr/command/provider/AbstractCommandHandler.java delete mode 100644 nostr-java-command-provider/src/main/java/nostr/command/provider/AuthCommandHandler.java delete mode 100644 nostr-java-command-provider/src/main/java/nostr/command/provider/ClosedCommandHandler.java delete mode 100644 nostr-java-command-provider/src/main/java/nostr/command/provider/EoseCommandHandler.java delete mode 100644 nostr-java-command-provider/src/main/java/nostr/command/provider/EventCommandHandler.java delete mode 100644 nostr-java-command-provider/src/main/java/nostr/command/provider/NoticeCommandHandler.java delete mode 100644 nostr-java-command-provider/src/main/java/nostr/command/provider/OkCommandHandler.java delete mode 100644 nostr-java-command-provider/src/main/resources/META-INF/services/nostr.command.CommandHandler delete mode 100644 nostr-java-controller/pom.xml delete mode 100644 nostr-java-controller/src/main/java/module-info.java delete mode 100644 nostr-java-controller/src/main/java/nostr/controller/ApplicationController.java delete mode 100644 nostr-java-controller/src/main/java/nostr/controller/Controller.java delete mode 100644 nostr-java-controller/src/main/java/nostr/controller/impl/ApplicationControllerImpl.java create mode 100644 nostr-java-test/src/test/java/nostr/test/event/BaseMessageCommandMapperTest.java delete mode 100644 nostr-java-util/src/main/java/nostr/util/thread/ThreadUtil.java diff --git a/nostr-java-api/pom.xml b/nostr-java-api/pom.xml index 3c3ab98db..033a220c3 100644 --- a/nostr-java-api/pom.xml +++ b/nostr-java-api/pom.xml @@ -45,9 +45,9 @@ ${project.version} - ${project.groupId} - nostr-java-command-provider - ${project.version} + org.apache.commons + commons-lang3 + 3.17.0 diff --git a/nostr-java-api/src/main/java/module-info.java b/nostr-java-api/src/main/java/module-info.java index e7b5625f1..14f6eb128 100644 --- a/nostr-java-api/src/main/java/module-info.java +++ b/nostr-java-api/src/main/java/module-info.java @@ -5,16 +5,16 @@ requires nostr.id; requires nostr.client; requires nostr.context; - requires nostr.context.impl; requires nostr.encryption; requires nostr.encryption.nip04dm; requires nostr.encryption.nip44dm; - + requires com.fasterxml.jackson.databind; - + requires lombok; requires java.logging; requires nostr.crypto; + requires org.apache.commons.lang3; - exports nostr.api; + exports nostr.api; } diff --git a/nostr-java-api/src/main/java/nostr/api/EventNostr.java b/nostr-java-api/src/main/java/nostr/api/EventNostr.java index 8a67fa3ca..cd6749123 100644 --- a/nostr-java-api/src/main/java/nostr/api/EventNostr.java +++ b/nostr-java-api/src/main/java/nostr/api/EventNostr.java @@ -10,15 +10,16 @@ import lombok.Setter; import nostr.api.factory.impl.GenericEventFactory; import nostr.base.PublicKey; +import nostr.event.BaseMessage; import nostr.event.BaseTag; import nostr.event.impl.GenericEvent; import nostr.event.json.codec.BaseMessageDecoder; import nostr.id.Identity; +import org.apache.commons.lang3.stream.Streams.FailableStream; import java.util.List; import java.util.Map; - -import nostr.event.BaseMessage; +import java.util.Objects; /** * @author guilhermegps @@ -46,16 +47,16 @@ public U send() { return this.send(getRelays()); } - @SuppressWarnings("unchecked") public U send(Map relays) { - List messages = super.send(this.event, relays); + List messages = super.sendEvent(this.event, relays); BaseMessageDecoder decoder = new BaseMessageDecoder<>(); - return messages.stream() - .map(msg -> (U) decoder.decode(msg)) - .filter(msg -> msg != null) - .findFirst() - .orElseThrow(() -> new RuntimeException("No message received")); + return new FailableStream<>(messages.stream()) + .map(msg -> (U) decoder.decode(msg)) + .filter(Objects::nonNull) + .stream() + .findFirst() + .orElseThrow(() -> new RuntimeException("No message received")); } public U signAndSend() { diff --git a/nostr-java-api/src/main/java/nostr/api/NIP01.java b/nostr-java-api/src/main/java/nostr/api/NIP01.java index 533415028..5bdbe1aee 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP01.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP01.java @@ -19,17 +19,14 @@ import nostr.api.factory.impl.NIP01Impl.ReplaceableEventFactory; import nostr.api.factory.impl.NIP01Impl.ReqMessageFactory; import nostr.api.factory.impl.NIP01Impl.TextNoteEventFactory; -import nostr.base.GenericTagQuery; import nostr.base.IEvent; import nostr.base.PublicKey; import nostr.base.Relay; import nostr.base.UserProfile; import nostr.event.BaseTag; -import nostr.event.Kind; import nostr.event.Marker; import nostr.event.NIP01Event; import nostr.event.filter.Filters; -import nostr.event.impl.GenericEvent; import nostr.event.message.CloseMessage; import nostr.event.message.EoseMessage; import nostr.event.message.EventMessage; @@ -42,7 +39,6 @@ import nostr.id.Identity; import java.util.List; -import java.util.Map; /** * @@ -90,7 +86,7 @@ public NIP01 createTextNoteEvent(@NonNull List tags, @NonNull String public NIP01 createMetadataEvent(@NonNull UserProfile profile) { var sender = getSender(); var event = (sender != null) ? new MetadataEventFactory(sender, profile).create() - : new MetadataEventFactory(profile).create(); + : new MetadataEventFactory(profile).create(); this.setEvent((T) event); return this; @@ -190,49 +186,6 @@ public static PubKeyTag createPubKeyTag(@NonNull PublicKey publicKey, String mai return result; } - /** - * Create a NIP01 filters object (all parameters are optional) - * - * @param events a list of event - * @param authors a list of pubkeys or prefixes, the pubkey of an event - * must - * be one of these - * @param kinds a list of a kind numbers - * @param referencedEvents a list of event ids that are referenced in an "e" - * tag - * @param referencePubKeys a list of pubkeys that are referenced in a "p" - * tag - * @param since an integer unix timestamp in seconds, events must be - * newer - * than this to pass - * @param until an integer unix timestamp in seconds, events must be - * older - * than this to pass - * @param limit maximum number of events to be returned in the - * initial query - * @param genericTagQuery a generic tag query - * @return a filters object - */ - @Deprecated(forRemoval = true) - public static Filters createFilters(List events, List authors, List kinds, - List referencedEvents, List referencePubKeys, Long since, Long until, - Integer limit, GenericTagQuery genericTagQuery) { -// return Filters.builder() -// .authors(authors) -// .events(events) -// .genericTagQuery( -// Map.of( -// genericTagQuery.getTagName(), -// genericTagQuery.getValue())) -// .kinds(kinds).limit(limit) -// .referencePubKeys(referencePubKeys) -// .referencedEvents(referencedEvents) -// .since(since) -// .until(until) -// .build(); - return null; - } - /** * Create an event message to send events requested by clients * @@ -306,7 +259,7 @@ public NIP01 createParameterizedReplaceableEvent(@NonNull Integer kind, Strin * @return */ public NIP01 createParameterizedReplaceableEvent(@NonNull List tags, @NonNull Integer kind, - String comment) { + String comment) { var event = new ParameterizedReplaceableEventFactory(getSender(), tags, kind, comment).create(); this.setEvent((T) event); @@ -331,7 +284,7 @@ public static IdentifierTag createIdentifierTag(@NonNull String id) { * @return */ public static AddressTag createAddressTag(@NonNull Integer kind, @NonNull PublicKey publicKey, - IdentifierTag idTag, Relay relay) { + IdentifierTag idTag, Relay relay) { var result = new AddressTagFactory(publicKey).create(); if(idTag != null) { result.setIdentifierTag(idTag); diff --git a/nostr-java-api/src/main/java/nostr/api/NostrIF.java b/nostr-java-api/src/main/java/nostr/api/NostrIF.java index 89450edfd..aecd93a6a 100644 --- a/nostr-java-api/src/main/java/nostr/api/NostrIF.java +++ b/nostr-java-api/src/main/java/nostr/api/NostrIF.java @@ -16,13 +16,13 @@ public interface NostrIF { NostrIF setSender(@NonNull Identity sender); NostrIF setRelays(@NonNull Map relays); - List send(@NonNull IEvent event); - List send(@NonNull IEvent event, Map relays); - List send(@NonNull Filters filters, @NonNull String subscriptionId); - List send(@NonNull Filters filters, @NonNull String subscriptionId, Map relays); - List send(@NonNull List filtersList, @NonNull String subscriptionId); - List send(@NonNull List filtersList, @NonNull String subscriptionId, Map relays); - List send(@NonNull BaseMessage message, @NonNull RequestContext context); + List sendEvent(@NonNull IEvent event); + List sendEvent(@NonNull IEvent event, Map relays); + List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId); + List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId, Map relays); + List sendRequest(@NonNull List filtersList, @NonNull String subscriptionId); + List sendRequest(@NonNull List filtersList, @NonNull String subscriptionId, Map relays); + List sendRequest(@NonNull BaseMessage message, @NonNull RequestContext context); NostrIF sign(@NonNull Identity identity, @NonNull ISignable signable); boolean verify(@NonNull GenericEvent event); Identity getSender(); diff --git a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java index d49e23b9b..612d4b380 100644 --- a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java +++ b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java @@ -14,9 +14,12 @@ import nostr.util.NostrUtil; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -49,10 +52,10 @@ public NostrIF setSender(@NonNull Identity sender) { return this; } - public List send(T event, Map relays) { - return relays.entrySet().stream().map(relayEntry -> - clientMap.get(relayEntry.getKey()) - .sendEvent(event)) + public List sendEvent(T event, Map relays) { + setRelays(relays); + return relays.keySet().stream().map(s -> + clientMap.get(s).sendEvent(event)) .flatMap(List::stream) .distinct().toList(); } @@ -68,31 +71,30 @@ public NostrIF setRelays(@NonNull Map relays) { } @Override - public List send(@NonNull IEvent event) { + public List sendEvent(@NonNull IEvent event) { return clientMap.values().stream().map(client -> client.sendEvent(event)).flatMap(List::stream).distinct().toList(); } @Override - public List send(@NonNull IEvent event, Map relays) { + public List sendEvent(@NonNull IEvent event, Map relays) { setRelays(relays); - return send(event); + return sendEvent(event); } @Override - public List send(@NonNull Filters filters, @NonNull String subscriptionId) { - return clientMap.get(subscriptionId).sendRequest(filters, subscriptionId); + public List sendRequest(@NonNull List filtersList, @NonNull String subscriptionId, Map relays) { + return sendRequest(filtersList, subscriptionId); } @Override - public List send(@NonNull Filters filters, @NonNull String subscriptionId, Map relays) { - setRelays(relays); - return send(filters, subscriptionId); + public List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId, Map relays) { + return sendRequest(filters, subscriptionId); } @Override - public List send(@NonNull List filtersList, @NonNull String subscriptionId) { - return filtersList.stream().map(filters -> send( + public List sendRequest(@NonNull List filtersList, @NonNull String subscriptionId) { + return filtersList.stream().map(filters -> sendRequest( filters, subscriptionId )) @@ -101,13 +103,42 @@ public List send(@NonNull List filtersList, @NonNull String sub } @Override - public List send(@NonNull List filtersList, @NonNull String subscriptionId, Map relays) { - setRelays(relays); - return send(filtersList, subscriptionId); + public List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId2) { + String subscriptionId = "-" + subscriptionId2; + Set> entrySet = clientMap.entrySet(); + + if (entrySet.stream().noneMatch(entry -> + { + String relayName = entry.getValue().getRelayName(); + String targetRelayName = entry.getKey() + subscriptionId; + boolean equals = relayName.equals(targetRelayName); + return equals; + })) { + clientMap.keySet().forEach(clientMapKey -> + entrySet.stream().map(entry -> + { + String relayName = entry.getKey() + subscriptionId; + String relayUri = entry.getValue().getRelayUri(); + return new WebSocketClientHandler(relayName, relayUri); + }).toList().forEach(webSocketClientHandler -> + clientMap.put(clientMapKey, webSocketClientHandler))); + } + + List list = entrySet.stream().filter(entry -> + { + String relayName = entry.getValue().getRelayName(); + String targetRelayName = entry.getKey() + subscriptionId; + return relayName.equals(targetRelayName); + }) + .map(Entry::getValue) + .map(webSocketClientHandler -> webSocketClientHandler.sendRequest(filters, webSocketClientHandler.getRelayName())) + .flatMap(List::stream).distinct().toList(); + + return list; } @Override - public List send(@NonNull BaseMessage message, @NonNull RequestContext context) { + public List sendRequest(@NonNull BaseMessage message, @NonNull RequestContext context) { return List.of(); } diff --git a/nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java b/nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java index 4f4cfa6df..be0954b6f 100644 --- a/nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java +++ b/nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java @@ -1,24 +1,23 @@ package nostr.api; import lombok.Getter; -import lombok.NoArgsConstructor; import lombok.NonNull; import nostr.base.IEvent; import nostr.client.springwebsocket.SpringWebSocketClient; -import nostr.event.impl.Filters; +import nostr.event.filter.Filters; import nostr.event.message.EventMessage; import nostr.event.message.ReqMessage; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -@NoArgsConstructor public class WebSocketClientHandler { - - private SpringWebSocketClient eventClient; - private Map requestClientMap = new ConcurrentHashMap<>(); + private final SpringWebSocketClient eventClient; + private final Map requestClientMap = new ConcurrentHashMap<>(); @Getter private String relayName; @@ -36,7 +35,17 @@ protected List sendEvent(@NonNull IEvent event) { } protected List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId) { - return requestClientMap.get(subscriptionId).send(new ReqMessage(subscriptionId, filters)); + return Optional + .ofNullable( + requestClientMap.get(subscriptionId)) + .map(client -> + client.send(new ReqMessage(subscriptionId, filters))).or(() -> { + requestClientMap.put(subscriptionId, new SpringWebSocketClient(relayUri)); + return Optional.ofNullable( + requestClientMap.get(subscriptionId).send( + new ReqMessage(subscriptionId, filters))); + }) + .orElse(new ArrayList<>()); } public void close() throws IOException { diff --git a/nostr-java-command-provider/pom.xml b/nostr-java-command-provider/pom.xml deleted file mode 100644 index 8ab9e4a0d..000000000 --- a/nostr-java-command-provider/pom.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - 4.0.0 - - - xyz.tcheeric - nostr-java - 0.6.3-SNAPSHOT - - - nostr-java-command-provider - jar - - - - ${project.groupId} - nostr-java-id - ${project.version} - - - ${project.groupId} - nostr-java-client - ${project.version} - - - ${project.groupId} - nostr-java-context - ${project.version} - compile - - - ${project.groupId} - nostr-java-command-interface - ${project.version} - - - - - - - \ No newline at end of file diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/AbstractCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/AbstractCommandHandler.java deleted file mode 100644 index c73ea3692..000000000 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/AbstractCommandHandler.java +++ /dev/null @@ -1,14 +0,0 @@ -package nostr.command.provider; - -import lombok.Getter; -import nostr.client.springwebsocket.WebSocketClientIF; -import nostr.command.CommandHandler; - -@Getter -public abstract class AbstractCommandHandler implements CommandHandler { - private final WebSocketClientIF client; - - public AbstractCommandHandler(WebSocketClientIF client) { - this.client = client; - } -} diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/AuthCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/AuthCommandHandler.java deleted file mode 100644 index 3eab74aba..000000000 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/AuthCommandHandler.java +++ /dev/null @@ -1,61 +0,0 @@ -package nostr.command.provider; - -import lombok.NonNull; -import lombok.extern.java.Log; -import nostr.base.Command; -import nostr.base.PrivateKey; -import nostr.base.annotation.DefaultHandler; -import nostr.client.springwebsocket.WebSocketClientIF; -import nostr.context.CommandContext; -import nostr.context.impl.DefaultCommandContext; -import nostr.event.impl.CanonicalAuthenticationEvent; -import nostr.event.message.CanonicalAuthenticationMessage; -import nostr.event.message.RelayAuthenticationMessage; -import nostr.id.Identity; - -import java.io.IOException; -import java.util.logging.Level; - -@DefaultHandler(command = Command.AUTH) -@Log -public class AuthCommandHandler extends AbstractCommandHandler { - - public AuthCommandHandler(WebSocketClientIF client) { - super(client); - } - - @Override - public void handle(@NonNull CommandContext context) throws IOException { - - log.log(Level.INFO, "onAuth event - {0}", context); - - if (context instanceof DefaultCommandContext defaultCommandContext) { - var message = defaultCommandContext.getMessage(); - - if (message instanceof RelayAuthenticationMessage) { - log.log(Level.INFO, "Authentication required on relay {0}", defaultCommandContext.getRelay()); - - var privateKey = defaultCommandContext.getPrivateKey(); - var identity = Identity.create(new PrivateKey(privateKey)); - var publicKey = identity.getPublicKey(); - var challenge = defaultCommandContext.getChallenge(); - var relay = defaultCommandContext.getRelay(); - - // Create the event - var canonicalAuthenticationEvent = new CanonicalAuthenticationEvent(publicKey, challenge, relay); - - // Sign the event - identity.sign(canonicalAuthenticationEvent); - - var canonicalAuthenticationMessage = new CanonicalAuthenticationMessage(canonicalAuthenticationEvent); - var encodedMessage = canonicalAuthenticationMessage.encode(); - log.log(Level.INFO, "Sending authentication event {0} to the relay {1}", new Object[]{encodedMessage, relay}); - - // Publish the event to the relay - getClient().send(canonicalAuthenticationMessage); - } - } else { - throw new IllegalArgumentException("Invalid context type"); - } - } -} diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/ClosedCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/ClosedCommandHandler.java deleted file mode 100644 index ca0c2e9d2..000000000 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/ClosedCommandHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -package nostr.command.provider; - -import lombok.extern.java.Log; -import nostr.base.Command; -import nostr.base.PrivateKey; -import nostr.base.annotation.DefaultHandler; -import nostr.client.springwebsocket.WebSocketClientIF; -import nostr.context.CommandContext; -import nostr.context.impl.DefaultCommandContext; -import nostr.event.impl.CanonicalAuthenticationEvent; -import nostr.event.message.CanonicalAuthenticationMessage; -import nostr.event.message.ClosedMessage; -import nostr.id.Identity; - -import java.io.IOException; -import java.util.logging.Level; - -@Log -@DefaultHandler(command = Command.CLOSED) -public class ClosedCommandHandler extends AbstractCommandHandler { - - public ClosedCommandHandler(WebSocketClientIF client) { - super(client); - } - - @Override - public void handle(CommandContext context) throws IOException { - - log.info("onClosed event {0}" + context); - - if (context instanceof DefaultCommandContext defaultCommandContext) { - var message = defaultCommandContext.getMessage(); - - if (message instanceof ClosedMessage closedMessage) { - if (closedMessage.getMessage().startsWith("auth-required:")) { - log.log(Level.INFO, "Authentication required on relay {0}", defaultCommandContext.getRelay()); - - var privateKey = defaultCommandContext.getPrivateKey(); - var identity = Identity.create(new PrivateKey(privateKey)); - var publicKey = identity.getPublicKey(); - var challenge = defaultCommandContext.getChallenge(); - var relay = defaultCommandContext.getRelay(); - - // Create the event - var canonicalAuthenticationEvent = new CanonicalAuthenticationEvent(publicKey, challenge, relay); - - // Sign the event - identity.sign(canonicalAuthenticationEvent); - - var canonicalAuthenticationMessage = new CanonicalAuthenticationMessage(canonicalAuthenticationEvent); - var encodedMessage = canonicalAuthenticationMessage.encode(); - log.log(Level.INFO, "Sending authentication event {0} to the relay {1}", new Object[]{encodedMessage, relay}); - - // Publish the event to the relay - getClient().send(canonicalAuthenticationMessage); - } - } - } else { - throw new IllegalArgumentException("Invalid context type"); - } - } -} diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/EoseCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/EoseCommandHandler.java deleted file mode 100644 index 347fc58ad..000000000 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/EoseCommandHandler.java +++ /dev/null @@ -1,23 +0,0 @@ -package nostr.command.provider; - -import lombok.extern.java.Log; -import nostr.base.Command; -import nostr.base.annotation.DefaultHandler; -import nostr.client.springwebsocket.WebSocketClientIF; -import nostr.context.CommandContext; - -import java.util.logging.Level; - -@Log -@DefaultHandler(command = Command.EOSE) -public class EoseCommandHandler extends AbstractCommandHandler { - - public EoseCommandHandler(WebSocketClientIF client) { - super(client); - } - - @Override - public void handle(CommandContext context) { - log.log(Level.INFO, "onEose event - {0}", context); - } -} diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/EventCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/EventCommandHandler.java deleted file mode 100644 index a37a59b81..000000000 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/EventCommandHandler.java +++ /dev/null @@ -1,23 +0,0 @@ -package nostr.command.provider; - -import lombok.extern.java.Log; -import nostr.base.Command; -import nostr.base.annotation.DefaultHandler; -import nostr.client.springwebsocket.WebSocketClientIF; -import nostr.context.CommandContext; - -import java.util.logging.Level; - -@Log -@DefaultHandler(command = Command.EVENT) -public class EventCommandHandler extends AbstractCommandHandler { - - public EventCommandHandler(WebSocketClientIF client) { - super(client); - } - - @Override - public void handle(CommandContext context) { - log.log(Level.INFO, "onEvent event - {0}", context); - } -} diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/NoticeCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/NoticeCommandHandler.java deleted file mode 100644 index 5ccc0aa7d..000000000 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/NoticeCommandHandler.java +++ /dev/null @@ -1,23 +0,0 @@ -package nostr.command.provider; - -import lombok.extern.java.Log; -import nostr.base.Command; -import nostr.base.annotation.DefaultHandler; -import nostr.client.springwebsocket.WebSocketClientIF; -import nostr.context.CommandContext; - -import java.util.logging.Level; - -@Log -@DefaultHandler(command = Command.NOTICE) -public class NoticeCommandHandler extends AbstractCommandHandler { - - public NoticeCommandHandler(WebSocketClientIF client) { - super(client); - } - - @Override - public void handle(CommandContext context) { - log.log(Level.INFO, "onNotice event - {0}", context); - } -} diff --git a/nostr-java-command-provider/src/main/java/nostr/command/provider/OkCommandHandler.java b/nostr-java-command-provider/src/main/java/nostr/command/provider/OkCommandHandler.java deleted file mode 100644 index 20d44f3a4..000000000 --- a/nostr-java-command-provider/src/main/java/nostr/command/provider/OkCommandHandler.java +++ /dev/null @@ -1,23 +0,0 @@ -package nostr.command.provider; - -import lombok.extern.java.Log; -import nostr.base.Command; -import nostr.base.annotation.DefaultHandler; -import nostr.client.springwebsocket.WebSocketClientIF; -import nostr.context.CommandContext; - -import java.util.logging.Level; - -@Log -@DefaultHandler(command = Command.OK) -public class OkCommandHandler extends AbstractCommandHandler { - - public OkCommandHandler(WebSocketClientIF client) { - super(client); - } - - @Override - public void handle(CommandContext context) { - log.log(Level.INFO, "onOk event - {0}", context); - } -} diff --git a/nostr-java-command-provider/src/main/resources/META-INF/services/nostr.command.CommandHandler b/nostr-java-command-provider/src/main/resources/META-INF/services/nostr.command.CommandHandler deleted file mode 100644 index 723510a03..000000000 --- a/nostr-java-command-provider/src/main/resources/META-INF/services/nostr.command.CommandHandler +++ /dev/null @@ -1,6 +0,0 @@ -nostr.command.provider.EventCommandHandler -nostr.command.provider.AuthCommandHandler -nostr.command.provider.EoseCommandHandler -nostr.command.provider.NoticeCommandHandler -nostr.command.provider.OkCommandHandler -nostr.command.provider.ClosedCommandHandler \ No newline at end of file diff --git a/nostr-java-controller/pom.xml b/nostr-java-controller/pom.xml deleted file mode 100644 index c5de9dd59..000000000 --- a/nostr-java-controller/pom.xml +++ /dev/null @@ -1,46 +0,0 @@ - - 4.0.0 - - xyz.tcheeric - nostr-java - 0.6.3-SNAPSHOT - - - nostr-java-controller - jar - - nostr-java-controller-command - http://maven.apache.org - - - UTF-8 - - - - - ${project.groupId} - nostr-java-event - ${project.version} - compile - - - ${project.groupId} - nostr-java-context - ${project.version} - compile - - - ${project.groupId} - nostr-java-command-interface - ${project.version} - compile - - - ${project.groupId} - nostr-java-base - ${project.version} - compile - - - diff --git a/nostr-java-controller/src/main/java/module-info.java b/nostr-java-controller/src/main/java/module-info.java deleted file mode 100644 index 2fa81f3c5..000000000 --- a/nostr-java-controller/src/main/java/module-info.java +++ /dev/null @@ -1,18 +0,0 @@ -import nostr.command.CommandHandler; - -module nostr.controller { - uses CommandHandler; - - requires static lombok; - - requires java.logging; - - requires nostr.context; - requires nostr.context.impl; - requires nostr.base; - requires nostr.util; - requires nostr.command.handler; - - exports nostr.controller.impl; - exports nostr.controller; -} \ No newline at end of file diff --git a/nostr-java-controller/src/main/java/nostr/controller/ApplicationController.java b/nostr-java-controller/src/main/java/nostr/controller/ApplicationController.java deleted file mode 100644 index 3dbb465a4..000000000 --- a/nostr-java-controller/src/main/java/nostr/controller/ApplicationController.java +++ /dev/null @@ -1,4 +0,0 @@ -package nostr.controller; - -public interface ApplicationController extends Controller { -} diff --git a/nostr-java-controller/src/main/java/nostr/controller/Controller.java b/nostr-java-controller/src/main/java/nostr/controller/Controller.java deleted file mode 100644 index 63764c470..000000000 --- a/nostr-java-controller/src/main/java/nostr/controller/Controller.java +++ /dev/null @@ -1,11 +0,0 @@ -package nostr.controller; - -import nostr.context.Context; -import nostr.util.thread.Task; - -public interface Controller extends Task { - - void initialize(); - - void handleRequest(Context requestContext); -} diff --git a/nostr-java-controller/src/main/java/nostr/controller/impl/ApplicationControllerImpl.java b/nostr-java-controller/src/main/java/nostr/controller/impl/ApplicationControllerImpl.java deleted file mode 100644 index 523681900..000000000 --- a/nostr-java-controller/src/main/java/nostr/controller/impl/ApplicationControllerImpl.java +++ /dev/null @@ -1,85 +0,0 @@ -package nostr.controller.impl; - -import lombok.Getter; -import lombok.NonNull; -import lombok.extern.java.Log; -import nostr.base.Command; -import nostr.base.annotation.CustomHandler; -import nostr.base.annotation.DefaultHandler; -import nostr.command.CommandHandler; -import nostr.context.Context; -import nostr.context.impl.DefaultCommandContext; -import nostr.controller.ApplicationController; -import nostr.util.thread.ThreadUtil; - -import java.io.IOException; -import java.util.Optional; -import java.util.ServiceLoader; -import java.util.concurrent.TimeoutException; - -@Getter -@Log -public class ApplicationControllerImpl implements ApplicationController { - - private final String command; - - public ApplicationControllerImpl(@NonNull String command) { - this.command = command; - } - - @Override - public void initialize() { - } - - @Override - public void handleRequest(Context requestContext) { - requestContext.validate(); - if (requestContext instanceof DefaultCommandContext defaultCommandContext) { - try { - ThreadUtil.builder().blocking(true).task(this).build().run(defaultCommandContext); - } catch (TimeoutException e) { - throw new RuntimeException(e); - } - } - } - - @Override - public Void execute(@NonNull Context context) throws IOException { - if (context instanceof DefaultCommandContext commandContext) { - executeCommand(commandContext); - } - return null; - } - - private void executeCommand(@NonNull DefaultCommandContext defaultCommandContext) throws IOException { - - var commandHandler = getCommandHandler(this.command); - - log.fine("Executing command: " + this.command); - commandHandler.handle(defaultCommandContext); - } - - private static CommandHandler getCommandHandler(@NonNull String command) { - ServiceLoader loader = ServiceLoader.load(CommandHandler.class); - - Optional customHandler = loader.stream() - .map(ServiceLoader.Provider::get) - .filter(ch -> ch.getClass().isAnnotationPresent(CustomHandler.class) && ch.getClass().getAnnotation(CustomHandler.class).command().equals(Command.valueOf(command.toUpperCase()))) - .findFirst(); - - if (customHandler.isPresent()) { - return customHandler.get(); - } - - Optional defaultHandler = loader.stream() - .map(ServiceLoader.Provider::get) - .filter(ch -> ch.getClass().isAnnotationPresent(DefaultHandler.class) && ch.getClass().getAnnotation(DefaultHandler.class).command().equals(Command.valueOf(command.toUpperCase()))) - .findFirst(); - - if (defaultHandler.isPresent()) { - return defaultHandler.get(); - } - - throw new AssertionError("Could not load the default handler"); - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java index 4ad6b9638..1d5653e49 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java @@ -10,27 +10,19 @@ @EqualsAndHashCode public class Filters { + public static final int DEFAULT_FILTERS_LIMIT = 10; @Getter private final Map> filtersMap; @Getter @Setter - private Integer limit = 10; + private Integer limit = DEFAULT_FILTERS_LIMIT; public Filters(@NonNull Map> filtersMap) { validateFiltersMap(filtersMap); this.filtersMap = filtersMap; } - // TODO: unused - public void addFilterable(@NonNull String key, @NonNull Filterable... filterable) { - addFilterable(key, List.of(filterable)); - } - - public void addFilterable(@NonNull String key, @NonNull List filterable) { - filtersMap.put(key, filterable); - } - public List getFilterableByType(@NonNull String type) { return filtersMap.get(type); } @@ -48,30 +40,3 @@ private static void validateFiltersMap(Map> filtersMap) }); } } - -// TODO: no tests currently call below... -// public List getFilterCriterion(@NonNull String type) { -// return Optional -// .ofNullable( -// getFilterableByType(type)) -// .stream().flatMap(filterables -> -// filterables.stream().map(filterable -> -/// / TODO: ...which leavesw below uncalled as well. needs testing -// (T) filterable.getFilterCriterion())) -// .toList(); -// } - -// @SneakyThrows -// public void setFilterableListByType( -// @NonNull String key, -// @NonNull List filterTypeList, -// @NonNull Function filterableFunction) { -// -// if (filterTypeList.isEmpty()) { -// throw new IllegalArgumentException( -// String.format("[%s] filter must contain at least one element", key)); -// } -// -// addFilterable( -// key, -// filterTypeList.stream().map(filterableFunction).collect(Collectors.toList())); diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java index e89ab1e57..385d7f461 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseMessageDecoder.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.NonNull; -import lombok.SneakyThrows; import nostr.base.IDecoder; import nostr.event.BaseMessage; import nostr.event.impl.GenericMessage; @@ -34,10 +33,9 @@ public BaseMessageDecoder() { @Override public T decode(@NonNull String jsonString) throws JsonProcessingException { - ValidJsonNode validJsonNode = validateJson(jsonString); - String command = validJsonNode.formerly_strCmd(); - Object subscriptionId = validJsonNode.formerly_arg(); // subscriptionId - String filtersJson = validJsonNode.formerly_msgArr(); // filters + ValidJsonNodeFirstPair validJsonNodeFirstPair = jsonFirstPair(jsonString); + String command = validJsonNodeFirstPair.formerly_strCmd(); + Object subscriptionId = validJsonNodeFirstPair.formerly_arg(); // subscriptionId Object[] msgArr = mapper.readValue(jsonString, Object[].class); // TODO: replace with jsonNode after ReqMessage.decode() is finished @@ -50,29 +48,35 @@ public T decode(@NonNull String jsonString) throws JsonProcessingException { case "EVENT" -> EventMessage.decode(msgArr, mapper); case "NOTICE" -> NoticeMessage.decode(subscriptionId); case "OK" -> OkMessage.decode(msgArr); - case "REQ" -> ReqMessage.decode(subscriptionId, List.of(filtersJson)); + case "REQ" -> { + String filtersJson = jsonSecondPair(jsonString).formerly_msgArr(); // filters + yield ReqMessage.decode(subscriptionId, List.of(filtersJson)); + } default -> GenericMessage.decode(msgArr); }; } - private ValidJsonNode validateJson(@NonNull String jsonString) throws JsonProcessingException { + private ValidJsonNodeFirstPair jsonFirstPair(@NonNull String jsonString) throws JsonProcessingException { final JsonNode jsonNode = mapper.readTree(jsonString); - if (jsonNode.size() > MAX_JSON_NODE_THRESHOLD) - throw new IllegalArgumentException( - String.format("BaseMessageDecoder expected max [%d] JSON nodes but received [%s] instead with contents:\n\n[%s]\n", - MAX_JSON_NODE_THRESHOLD, - jsonNode.size(), - jsonNode.toPrettyString() - )); - - return new ValidJsonNode( + return new ValidJsonNodeFirstPair( jsonNode.get(0).asText(), - jsonNode.get(1).asText(), + jsonNode.get(1).asText()); + } + + private ValidJsonNodeSecondPair jsonSecondPair(@NonNull String jsonString) throws JsonProcessingException { + final JsonNode jsonNode = mapper.readTree(jsonString); + + return new ValidJsonNodeSecondPair( jsonNode.get(2).toString()); } - private record ValidJsonNode(@NonNull String formerly_strCmd, @NonNull Object formerly_arg, - @NonNull String formerly_msgArr) { + private record ValidJsonNodeFirstPair( + @NonNull String formerly_strCmd, + @NonNull Object formerly_arg) { + } + + private record ValidJsonNodeSecondPair( + @NonNull String formerly_msgArr) { } } diff --git a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java index e5fa2222e..1c4b25081 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java @@ -31,6 +31,7 @@ public class ReqMessage extends BaseMessage { private final List filtersList; public ReqMessage(@NonNull String subscriptionId, Filters... filtersList) { +// TODO: complete logic for a list of filters this(subscriptionId, List.of(filtersList)); } diff --git a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java index 2213f7f90..68807872b 100644 --- a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java +++ b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java @@ -15,6 +15,11 @@ import nostr.event.BaseTag; import nostr.event.Kind; import nostr.event.Reaction; +import nostr.event.filter.Filterable; +import nostr.event.filter.Filters; +import nostr.event.filter.KindFilter; +import nostr.event.filter.AuthorFilter; +import nostr.event.filter.SinceFilter; import nostr.event.impl.ChannelCreateEvent; import nostr.event.impl.ChannelMessageEvent; import nostr.event.impl.DeletionEvent; @@ -40,6 +45,7 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Calendar; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -295,20 +301,20 @@ private static void internetIdMetadata() { public static void filters() throws InterruptedException { logHeader("filters"); - var kinds = List.of(Kind.EPHEMEREAL_EVENT, Kind.TEXT_NOTE); - var authors = new ArrayList<>(List.of(new PublicKey("21ef0d8541375ae4bca85285097fba370f7e540b5a30e5e75670c16679f9d144"))); - var date = Calendar.getInstance(); var subId = "subId" + date.getTimeInMillis(); date.add(Calendar.DAY_OF_MONTH, -5); - Filters filters = Filters.builder() - .authors(authors) - .kinds(kinds) - .since(date.getTimeInMillis()/1000) - .build(); + + Map> filterablesMap = new HashMap<>(); + filterablesMap.put(KindFilter.filterKey, List.of( + new KindFilter<>(Kind.EPHEMEREAL_EVENT), + new KindFilter<>(Kind.TEXT_NOTE))); + filterablesMap.put(AuthorFilter.filterKey, List.of( + new AuthorFilter<>(new PublicKey("21ef0d8541375ae4bca85285097fba370f7e540b5a30e5e75670c16679f9d144")))); + filterablesMap.put(SinceFilter.filterKey, List.of(new SinceFilter(date.getTimeInMillis()/1000))); var nip01 = NIP01.getInstance(); - nip01.setRelays(RELAYS).send(filters, subId); + nip01.setRelays(RELAYS).sendRequest(new Filters(filterablesMap), subId); Thread.sleep(5000); } diff --git a/nostr-java-test/pom.xml b/nostr-java-test/pom.xml index 15e049822..c6f872c2a 100644 --- a/nostr-java-test/pom.xml +++ b/nostr-java-test/pom.xml @@ -58,13 +58,6 @@ ${project.version} test - - ${project.groupId} - nostr-java-command-provider - ${project.version} - test - - org.springframework @@ -193,4 +186,4 @@ - \ No newline at end of file + diff --git a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java b/nostr-java-test/src/main/java/nostr/test/EntityFactory.java index bcdb51614..b8bba323e 100644 --- a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java +++ b/nostr-java-test/src/main/java/nostr/test/EntityFactory.java @@ -158,7 +158,7 @@ public static GenericTag createGenericTag(PublicKey publicKey, IEvent event, Int // .build(); // } - public static GenericTagQuery createGenericTagQuery() { + public static List createGenericTagQuery() { Character c = generateRamdomAlpha(1).charAt(0); String v1 = generateRamdomAlpha(5); String v2 = generateRamdomAlpha(6); @@ -169,10 +169,12 @@ public static GenericTagQuery createGenericTagQuery() { list.add(v2); list.add(v1); - var result = new GenericTagQuery(); - result.setTagName(c.toString()); - result.setValue(list); - return result; + return list.stream().map(item -> { + var result = new GenericTagQuery(); + result.setTagName(c.toString()); + result.setValue(item); + return result; + }).toList(); } } diff --git a/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java b/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java index 95bd5e462..a1bc6cc8b 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java @@ -2,12 +2,14 @@ import nostr.api.NIP01; import nostr.api.NIP09; -import nostr.api.NostrSpringWebSocketClient; import nostr.base.Relay; import nostr.event.BaseMessage; import nostr.event.BaseTag; import nostr.event.Kind; -import nostr.event.impl.Filters; +import nostr.event.filter.AuthorFilter; +import nostr.event.filter.Filterable; +import nostr.event.filter.Filters; +import nostr.event.filter.KindFilter; import nostr.event.impl.GenericEvent; import nostr.event.impl.ReplaceableEvent; import nostr.event.impl.TextNoteEvent; @@ -19,6 +21,7 @@ import org.junit.jupiter.api.Test; import java.io.IOException; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -32,11 +35,6 @@ public class APINIP09EventTest { private static final String RELAY_URI = "ws://localhost:5555"; - private final NostrSpringWebSocketClient nostrSpringWebSocketClient; - - public APINIP09EventTest() { - nostrSpringWebSocketClient = new NostrSpringWebSocketClient("localhost", RELAY_URI); - } @Test public void deleteEvent() throws IOException { @@ -47,21 +45,21 @@ public void deleteEvent() throws IOException { NIP01 nip01 = new NIP01<>(identity); nip01.createTextNoteEvent("Delete me!").signAndSend(Map.of("local", RELAY_URI)); + Map> expectedFilters = new HashMap<>(); + expectedFilters.put(KindFilter.filterKey, List.of( + new KindFilter<>(Kind.TEXT_NOTE))); + expectedFilters.put(AuthorFilter.filterKey, List.of( + new AuthorFilter<>(identity.getPublicKey()))); - Filters filters = Filters - .builder() - .kinds(List.of(Kind.TEXT_NOTE)) - .authors(List.of(identity.getPublicKey())) - .build(); - - List result = nip01.send(filters, UUID.randomUUID().toString()); + Filters filters = new Filters(expectedFilters); + List result = nip01.sendRequest(filters, UUID.randomUUID().toString()); assertFalse(result.isEmpty()); assertEquals(2, result.size()); nip09.createDeletionEvent(nip01.getEvent()).signAndSend(Map.of("local", RELAY_URI)); - result = nip01.send(filters, UUID.randomUUID().toString()); + result = nip01.sendRequest(filters, UUID.randomUUID().toString()); assertFalse(result.isEmpty()); assertEquals(1, result.size()); @@ -86,9 +84,9 @@ public void deleteEventWithRef() throws IOException { NIP01 nip01 = new NIP01<>(identity); nip01 - .createTextNoteEvent("Reference me!") - .getEvent() - .addTag(nip01.createAddressTag(10_001, identity.getPublicKey(), identifierTag, new Relay(RELAY_URI))); + .createTextNoteEvent("Reference me!") + .getEvent() + .addTag(nip01.createAddressTag(10_001, identity.getPublicKey(), identifierTag, new Relay(RELAY_URI))); BaseMessage message = nip01.signAndSend(Map.of("local", RELAY_URI)); @@ -103,9 +101,9 @@ public void deleteEventWithRef() throws IOException { assertEquals(4, deletedEvent.getTags().size()); List eventTags = deletedEvent.getTags() - .stream() - .filter(t -> "e".equals(t.getCode())) - .collect(Collectors.toList()); + .stream() + .filter(t -> "e".equals(t.getCode())) + .collect(Collectors.toList()); assertEquals(1, eventTags.size()); @@ -113,9 +111,9 @@ public void deleteEventWithRef() throws IOException { assertEquals(event.getId(), eventTag.getIdEvent()); List addressTags = deletedEvent.getTags() - .stream() - .filter(t -> "a".equals(t.getCode())) - .collect(Collectors.toList()); + .stream() + .filter(t -> "a".equals(t.getCode())) + .collect(Collectors.toList()); assertEquals(1, addressTags.size()); @@ -125,9 +123,9 @@ public void deleteEventWithRef() throws IOException { assertEquals(identity.getPublicKey(), addressTag.getPublicKey()); List kindTags = deletedEvent.getTags() - .stream() - .filter(t -> "k".equals(t.getCode())) - .collect(Collectors.toList()); + .stream() + .filter(t -> "k".equals(t.getCode())) + .collect(Collectors.toList()); assertEquals(2, kindTags.size()); diff --git a/nostr-java-test/src/test/java/nostr/test/event/BaseMessageCommandMapperTest.java b/nostr-java-test/src/test/java/nostr/test/event/BaseMessageCommandMapperTest.java new file mode 100644 index 000000000..94000b102 --- /dev/null +++ b/nostr-java-test/src/test/java/nostr/test/event/BaseMessageCommandMapperTest.java @@ -0,0 +1,72 @@ +package nostr.test.event; + +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.extern.java.Log; +import nostr.event.BaseMessage; +import nostr.event.json.codec.BaseMessageDecoder; +import nostr.event.message.EoseMessage; +import nostr.event.message.ReqMessage; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@Log +public class BaseMessageCommandMapperTest { +// TODO: flesh out remaining commands + public final static String REQ_JSON = + "[\"REQ\", " + + "\"npub17x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh\", " + + "{\"kinds\": [1], " + + "\"authors\": [\"f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75\"]," + + "\"#e\": [\"fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}]"; + + @Test + public void testReqMessageDecoder() throws JsonProcessingException { + log.info("testReqMessageDecoder"); + + BaseMessage decode = new BaseMessageDecoder<>().decode(REQ_JSON); + assertInstanceOf(ReqMessage.class, decode); + } + + @Test + public void testReqMessageDecoderType() { + log.info("testReqMessageDecoderType"); + + assertDoesNotThrow(() -> { + new BaseMessageDecoder().decode(REQ_JSON); + }); + + assertDoesNotThrow(() -> { + ReqMessage reqMessage = new BaseMessageDecoder().decode(REQ_JSON); + }); + } + + @Test + public void testReqMessageDecoderThrows() { + log.info("testReqMessageDecoderThrows"); + + assertThrows(ClassCastException.class, () -> { + EoseMessage decode = new BaseMessageDecoder().decode(REQ_JSON); + }); + } + + @Test + public void testReqMessageDecoderDoesNotThrow() { + log.info("testReqMessageDecoderDoesNotThrow"); + + assertDoesNotThrow(() -> { + new BaseMessageDecoder().decode(REQ_JSON); + }); + } + + @Test + public void testReqMessageDecoderThrows3() { + log.info("testReqMessageDecoderThrows"); + + assertThrows(ClassCastException.class, () -> { + EoseMessage decode = new BaseMessageDecoder().decode(REQ_JSON); + }); + } +} diff --git a/nostr-java-util/src/main/java/nostr/util/thread/ThreadUtil.java b/nostr-java-util/src/main/java/nostr/util/thread/ThreadUtil.java deleted file mode 100644 index efed426b8..000000000 --- a/nostr-java-util/src/main/java/nostr/util/thread/ThreadUtil.java +++ /dev/null @@ -1,73 +0,0 @@ -package nostr.util.thread; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.NonNull; -import lombok.extern.java.Log; -import nostr.context.Context; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.locks.ReentrantLock; -import java.util.logging.Level; - -@AllArgsConstructor -@Builder -@Log -public class ThreadUtil { - - private final T task; - - @Builder.Default - private int timeoutSeconds = TIMEOUT_SECONDS; - - @Builder.Default - private boolean blocking = false; - - @Builder.Default - private boolean lock = false; - - public static final int TIMEOUT_SECONDS = 5; - - public static final ReentrantLock LOCK = new ReentrantLock(); - - public ThreadUtil(@NonNull T task) { - this.task = task; - } - - public void run(@NonNull Context context) throws TimeoutException { - log.log(Level.FINE, "Executing thread on {0}...", task); - ExecutorService threadPool = Executors.newCachedThreadPool(); - Future futureTask = threadPool.submit(() -> { - if (lock) { - LOCK.lock(); - } - try { - task.execute(context); - } catch (Exception e) { - log.log(Level.WARNING, "Failed to execute task: {0}", e.getMessage()); - } finally { - if (lock) { - LOCK.unlock(); - } - } - }); - - if (blocking) { - try { - log.log(Level.FINE, "Waiting for thread to complete... "); - futureTask.get(timeoutSeconds, TimeUnit.SECONDS); // Wait for the thread to complete - log.log(Level.FINE, "Thread execution completed!"); - } catch (InterruptedException | ExecutionException e) { - log.log(Level.WARNING, "Failed to execute task: {0}", e.getMessage()); - throw new RuntimeException(e); - } finally { - threadPool.shutdown(); - } - } - } -} diff --git a/pom.xml b/pom.xml index 6ed1e817a..d76600a0e 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ - @tcheeric + @tcheeric @avlo @@ -69,7 +69,6 @@ nostr-java-test nostr-java-util nostr-java-command-interface - nostr-java-command-provider nostr-java-client nostr-java-api nostr-java-encryption @@ -77,7 +76,6 @@ nostr-java-encryption-nip44 nostr-java-context nostr-java-context-interface - nostr-java-controller From 6b8b3b5e078a604f7ee2e7ecbdef5e03975507bd Mon Sep 17 00:00:00 2001 From: nick avlo Date: Wed, 19 Feb 2025 13:39:52 -0800 Subject: [PATCH 35/45] cleanup --- .../src/main/java/nostr/api/EventNostr.java | 2 +- .../nostr/api/NostrSpringWebSocketClient.java | 1 - nostr-java-command-interface/pom.xml | 36 ------------------- .../src/main/java/module-info.java | 10 ------ .../java/nostr/command/CommandHandler.java | 10 ------ .../main/java/nostr/test/EntityFactory.java | 24 ------------- .../src/main/java/nostr/util/thread/Task.java | 11 ------ pom.xml | 1 - 8 files changed, 1 insertion(+), 94 deletions(-) delete mode 100644 nostr-java-command-interface/pom.xml delete mode 100644 nostr-java-command-interface/src/main/java/module-info.java delete mode 100644 nostr-java-command-interface/src/main/java/nostr/command/CommandHandler.java delete mode 100644 nostr-java-util/src/main/java/nostr/util/thread/Task.java diff --git a/nostr-java-api/src/main/java/nostr/api/EventNostr.java b/nostr-java-api/src/main/java/nostr/api/EventNostr.java index cd6749123..4c269219b 100644 --- a/nostr-java-api/src/main/java/nostr/api/EventNostr.java +++ b/nostr-java-api/src/main/java/nostr/api/EventNostr.java @@ -10,7 +10,6 @@ import lombok.Setter; import nostr.api.factory.impl.GenericEventFactory; import nostr.base.PublicKey; -import nostr.event.BaseMessage; import nostr.event.BaseTag; import nostr.event.impl.GenericEvent; import nostr.event.json.codec.BaseMessageDecoder; @@ -20,6 +19,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import nostr.event.BaseMessage; /** * @author guilhermegps diff --git a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java index 612d4b380..a447d3575 100644 --- a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java +++ b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java @@ -14,7 +14,6 @@ import nostr.util.NostrUtil; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/nostr-java-command-interface/pom.xml b/nostr-java-command-interface/pom.xml deleted file mode 100644 index a42859beb..000000000 --- a/nostr-java-command-interface/pom.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - 4.0.0 - - - xyz.tcheeric - nostr-java - 0.6.3-SNAPSHOT - - - nostr-java-command-interface - jar - - - ${project.groupId} - nostr-java-util - ${project.version} - - - ${project.groupId} - nostr-java-base - ${project.version} - - - ${project.groupId} - nostr-java-event - ${project.version} - - - ${project.groupId} - nostr-java-context-interface - ${project.version} - compile - - - \ No newline at end of file diff --git a/nostr-java-command-interface/src/main/java/module-info.java b/nostr-java-command-interface/src/main/java/module-info.java deleted file mode 100644 index 6664203b6..000000000 --- a/nostr-java-command-interface/src/main/java/module-info.java +++ /dev/null @@ -1,10 +0,0 @@ -import nostr.command.CommandHandler; - -module nostr.command.handler { - - requires nostr.context; - - exports nostr.command; - - uses CommandHandler; -} diff --git a/nostr-java-command-interface/src/main/java/nostr/command/CommandHandler.java b/nostr-java-command-interface/src/main/java/nostr/command/CommandHandler.java deleted file mode 100644 index ec4115e8f..000000000 --- a/nostr-java-command-interface/src/main/java/nostr/command/CommandHandler.java +++ /dev/null @@ -1,10 +0,0 @@ -package nostr.command; - -import nostr.context.CommandContext; - -import java.io.IOException; - -public interface CommandHandler { - - void handle(CommandContext context) throws IOException; -} diff --git a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java b/nostr-java-test/src/main/java/nostr/test/EntityFactory.java index b8bba323e..6afb38149 100644 --- a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java +++ b/nostr-java-test/src/main/java/nostr/test/EntityFactory.java @@ -58,11 +58,6 @@ public static DirectMessageEvent createDirectMessageEvent(PublicKey senderPublic return event; } - public static Filters createFilters(List authors, List kindList, Long since) { -// return Filters.builder().authors(authors).kinds(kindList).since(since).build(); - return null; - } - public static InternetIdentifierMetadataEvent createInternetIdentifierMetadataEvent(UserProfile profile) { final PublicKey publicKey = profile.getPublicKey(); InternetIdentifierMetadataEvent event = new InternetIdentifierMetadataEvent(publicKey, profile); @@ -139,25 +134,6 @@ public static GenericTag createGenericTag(PublicKey publicKey, IEvent event, Int return tag; } -// public static Filters createFilters(PublicKey publicKey) { -// List eventList = new ArrayList<>(); -// eventList.add(createTextNoteEvent(publicKey)); -// eventList.add(createEphemeralEvent(publicKey)); -// -// List refEvents = new ArrayList<>(); -// refEvents.add(createTextNoteEvent(publicKey)); -// -// GenericTagQuery genericTagQuery = createGenericTagQuery(); -// return Filters.builder() -// .events(eventList) -// .referencedEvents(refEvents) -// .genericTagQuery( -// Map.of( -// genericTagQuery.getTagName(), -// genericTagQuery.getValue())) -// .build(); -// } - public static List createGenericTagQuery() { Character c = generateRamdomAlpha(1).charAt(0); String v1 = generateRamdomAlpha(5); diff --git a/nostr-java-util/src/main/java/nostr/util/thread/Task.java b/nostr-java-util/src/main/java/nostr/util/thread/Task.java deleted file mode 100644 index a370fbe49..000000000 --- a/nostr-java-util/src/main/java/nostr/util/thread/Task.java +++ /dev/null @@ -1,11 +0,0 @@ -package nostr.util.thread; - -import lombok.NonNull; -import nostr.context.Context; - -import java.io.IOException; - -public interface Task { - - T execute(@NonNull Context context) throws IOException; -} diff --git a/pom.xml b/pom.xml index d76600a0e..7ecf28609 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,6 @@ nostr-java-id nostr-java-test nostr-java-util - nostr-java-command-interface nostr-java-client nostr-java-api nostr-java-encryption From 3574642f95286917c7b287d08e0cc6c7f1d1c975 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Wed, 19 Feb 2025 16:18:34 -0800 Subject: [PATCH 36/45] kind updates --- .../main/java/nostr/api/factory/impl/NIP23Impl.java | 5 ++--- .../main/java/nostr/api/factory/impl/NIP60Impl.java | 9 +++++---- nostr-java-event/src/main/java/nostr/event/Kind.java | 11 +++++++---- .../nostr/event/impl/CreateOrUpdateStallEvent.java | 3 ++- .../src/main/java/nostr/event/impl/GenericEvent.java | 2 +- .../main/java/nostr/examples/NostrApiExamples.java | 4 ++-- 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP23Impl.java b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP23Impl.java index 08a5ffc40..2bcd9f642 100644 --- a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP23Impl.java +++ b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP23Impl.java @@ -10,6 +10,7 @@ import nostr.api.factory.EventFactory; import nostr.api.factory.TagFactory; import nostr.event.BaseTag; +import nostr.event.Kind; import nostr.event.impl.GenericEvent; import nostr.id.Identity; @@ -22,8 +23,6 @@ */ public class NIP23Impl { - public static final Integer KIND_PRE_LONG_FORM_CONTENT = 30023; - @Data @EqualsAndHashCode(callSuper = false) public static class LongFormContentEventFactory extends EventFactory { @@ -38,7 +37,7 @@ public LongFormContentEventFactory(@NonNull Identity sender, @NonNull List tags, @NonNull @Override public GenericEvent create() { - return new GenericEvent(getIdentity().getPublicKey(), 37375, getTags(), getContent()); + return new GenericEvent(getIdentity().getPublicKey(), Kind.WALLET, getTags(), getContent()); } } @@ -30,7 +31,7 @@ public TokenEventFactory(@NonNull Identity sender, List tags, @NonNull @Override public GenericEvent create() { - return new GenericEvent(getIdentity().getPublicKey(), 7375, getTags(), getContent()); + return new GenericEvent(getIdentity().getPublicKey(), Kind.WALLET_UNSPENT_PROOF, getTags(), getContent()); } } @@ -42,7 +43,7 @@ public SpendingHistoryEventFactory(@NonNull Identity sender, List tags, @Override public GenericEvent create() { - return new GenericEvent(getIdentity().getPublicKey(), 7376, getTags(), getContent()); + return new GenericEvent(getIdentity().getPublicKey(), Kind.WALLET_TX_HISTORY, getTags(), getContent()); } } @@ -54,7 +55,7 @@ public RedemptionQuoteEventFactory(@NonNull Identity sender, List tags, @Override public GenericEvent create() { - return new GenericEvent(getIdentity().getPublicKey(), 7374, getTags(), getContent()); + return new GenericEvent(getIdentity().getPublicKey(), Kind.RESERVED_CASHU_WALLET_TOKENS, getTags(), getContent()); } } diff --git a/nostr-java-event/src/main/java/nostr/event/Kind.java b/nostr-java-event/src/main/java/nostr/event/Kind.java index f1fdc0215..0f34b990b 100644 --- a/nostr-java-event/src/main/java/nostr/event/Kind.java +++ b/nostr-java-event/src/main/java/nostr/event/Kind.java @@ -29,22 +29,25 @@ public enum Kind { MUTE_USER(44, "mute_user"), ENCRYPTED_PAYLOADS(44, "encrypted_payloads"), OTS_EVENT(1040, "ots_event"), + RESERVED_CASHU_WALLET_TOKENS(7_374, "reserved_cashu_wallet_tokens"), WALLET_UNSPENT_PROOF(7_375, "wallet_unspent_proof"), WALLET_TX_HISTORY(7_376, "wallet_tx_history"), ZAP_REQUEST(9734, "zap_request"), ZAP_RECEIPT(9735, "zap_receipt"), REPLACEABLE_EVENT(10_000, "replaceable_event"), + PIN_LIST(10_001, "pin_list"), EPHEMEREAL_EVENT(20_000, "ephemereal_event"), CLIENT_AUTH(22_242, "authentication_of_clients_to_relays"), + STALL_CREATE_OR_UPDATE(30_017, "create_or_update_stall"), PRODUCT_CREATE_OR_UPDATE(30_018, "create_or_update_product"), + PRE_LONG_FORM_CONTENT(30_023, "long_form_content"), CLASSIFIED_LISTING(30_402, "classified_listing_active"), CLASSIFIED_LISTING_INACTIVE(30_403, "classified_listing_inactive"), CLASSIFIED_LISTING_DRAFT(30_403, "classified_listing_draft"), CALENDAR_DATE_BASED_EVENT(31_922, "calendar_date_based_event"), CALENDAR_TIME_BASED_EVENT(31_923, "calendar_time_based_event"), CALENDAR_RSVP_EVENT(31_925, "calendar_rsvp_event"), - WALLET(37_375, "wallet"), - UNDEFINED(-1, "undefined"); + WALLET(37_375, "wallet"); @JsonValue private final int value; @@ -53,7 +56,7 @@ public enum Kind { @JsonCreator public static Kind valueOf(int value) { - if (!ValueRange.of(0, 65535).isValidIntValue(value)) { + if (!ValueRange.of(0, 65_535).isValidIntValue(value)) { throw new IllegalArgumentException(String.format("Kind must be between 0 and 65535 but was [%d]", value)); } for (Kind k : values()) { @@ -62,7 +65,7 @@ public static Kind valueOf(int value) { } } - return UNDEFINED; + return TEXT_NOTE; } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/impl/CreateOrUpdateStallEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/CreateOrUpdateStallEvent.java index 752211272..3ab5f9319 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/CreateOrUpdateStallEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/CreateOrUpdateStallEvent.java @@ -15,6 +15,7 @@ import nostr.base.annotation.Event; import nostr.event.AbstractEventContent; import nostr.event.BaseTag; +import nostr.event.Kind; /** * @@ -26,7 +27,7 @@ public class CreateOrUpdateStallEvent extends NostrMarketplaceEvent { public CreateOrUpdateStallEvent(PublicKey sender, List tags, @NonNull Stall stall) { - super(sender, 30017, tags, stall); + super(sender, Kind.STALL_CREATE_OR_UPDATE.getValue(), tags, stall); } @Getter diff --git a/nostr-java-event/src/main/java/nostr/event/impl/GenericEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/GenericEvent.java index d53764347..e31c423e6 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/GenericEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/GenericEvent.java @@ -124,7 +124,7 @@ public GenericEvent(@NonNull PublicKey pubKey, @NonNull Kind kind, @NonNull List public GenericEvent(@NonNull PublicKey pubKey, @NonNull Integer kind, @NonNull List tags, @NonNull String content) { this.pubKey = pubKey; - this.kind = kind; + this.kind = Kind.valueOf(kind).getValue(); this.tags = tags; this.content = content; this.attributes = new ArrayList<>(); diff --git a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java index 68807872b..6154f78f4 100644 --- a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java +++ b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java @@ -248,7 +248,7 @@ private static void ephemerealEvent() { logHeader("ephemeralEvent"); var nip01 = new NIP01(SENDER); - nip01.createEphemeralEvent(21000, "An ephemeral event") + nip01.createEphemeralEvent(Kind.EPHEMEREAL_EVENT.getValue(), "An ephemeral event") .sign() .send(RELAYS); } @@ -281,7 +281,7 @@ private static void replaceableEvent() { var event = nip01.createTextNoteEvent("Hello Astral, Please replace me!"); event.signAndSend(RELAYS); - nip01.createReplaceableEvent(List.of(new EventTag(event.getEvent().getId())), 15_000, "New content").signAndSend(); + nip01.createReplaceableEvent(List.of(new EventTag(event.getEvent().getId())), Kind.REPLACEABLE_EVENT.getValue(), "New content").signAndSend(); } private static void internetIdMetadata() { From 64d82274147785275110a6af705487e35b027831 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Wed, 19 Feb 2025 17:02:01 -0800 Subject: [PATCH 37/45] cleanup --- .../nostr/api/NostrSpringWebSocketClient.java | 55 ++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java index a447d3575..7c7ab2e08 100644 --- a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java +++ b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java @@ -18,7 +18,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -102,40 +101,20 @@ public List sendRequest(@NonNull List filtersList, @NonNull Str } @Override - public List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId2) { - String subscriptionId = "-" + subscriptionId2; - Set> entrySet = clientMap.entrySet(); - - if (entrySet.stream().noneMatch(entry -> - { - String relayName = entry.getValue().getRelayName(); - String targetRelayName = entry.getKey() + subscriptionId; - boolean equals = relayName.equals(targetRelayName); - return equals; - })) { - clientMap.keySet().forEach(clientMapKey -> - entrySet.stream().map(entry -> - { - String relayName = entry.getKey() + subscriptionId; - String relayUri = entry.getValue().getRelayUri(); - return new WebSocketClientHandler(relayName, relayUri); - }).toList().forEach(webSocketClientHandler -> - clientMap.put(clientMapKey, webSocketClientHandler))); - } + public List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId) { + createRequestClient(subscriptionId); - List list = entrySet.stream().filter(entry -> - { - String relayName = entry.getValue().getRelayName(); - String targetRelayName = entry.getKey() + subscriptionId; - return relayName.equals(targetRelayName); - }) + return clientMap.entrySet().stream().filter(entry -> + entry.getValue().getRelayName().equals(String.join(entry.getKey(), subscriptionId))) .map(Entry::getValue) - .map(webSocketClientHandler -> webSocketClientHandler.sendRequest(filters, webSocketClientHandler.getRelayName())) - .flatMap(List::stream).distinct().toList(); - - return list; + .map(webSocketClientHandler -> + webSocketClientHandler.sendRequest( + filters, + webSocketClientHandler.getRelayName())) + .flatMap(List::stream).toList(); } + @Override public List sendRequest(@NonNull BaseMessage message, @NonNull RequestContext context) { return List.of(); @@ -180,4 +159,18 @@ public void close() throws IOException { client.close(); } } + + private void createRequestClient(String subscriptionId) { + if (clientMap.entrySet().stream() // if a request client doesn't yet exist for subscriptionId... + .noneMatch(entry -> + entry.getValue().getRelayName().equals(String.join(entry.getKey(), subscriptionId)))) { + clientMap.keySet().forEach(clientMapKey -> // ... create one for each relay and add it to the client map + clientMap.entrySet().stream().map(entry -> + new WebSocketClientHandler( + String.join(entry.getKey(), subscriptionId), + entry.getValue().getRelayUri())) + .toList().forEach(webSocketClientHandler -> + clientMap.put(clientMapKey, webSocketClientHandler))); + } + } } From 9ea4d33592cf87ae98a350304a2373f8c57ea250 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Wed, 19 Feb 2025 17:07:56 -0800 Subject: [PATCH 38/45] removed empty tests --- .../nostr/test/filters/FiltersListDecoderTest.java | 12 ------------ .../nostr/test/filters/FiltersListEncoderTest.java | 12 ------------ 2 files changed, 24 deletions(-) delete mode 100644 nostr-java-test/src/test/java/nostr/test/filters/FiltersListDecoderTest.java delete mode 100644 nostr-java-test/src/test/java/nostr/test/filters/FiltersListEncoderTest.java diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersListDecoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersListDecoderTest.java deleted file mode 100644 index 884685659..000000000 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersListDecoderTest.java +++ /dev/null @@ -1,12 +0,0 @@ -//package nostr.test.filters; -// -//import org.junit.jupiter.api.Test; -// -//import static org.junit.jupiter.api.Assertions.fail; -// -//public class FiltersListDecoderTest { -// @Test -// public void testFiltersListDecoder() { -// fail(); -// } -//} diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersListEncoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersListEncoderTest.java deleted file mode 100644 index 19a6687eb..000000000 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersListEncoderTest.java +++ /dev/null @@ -1,12 +0,0 @@ -//package nostr.test.filters; -// -//import org.junit.jupiter.api.Test; -// -//import static org.junit.jupiter.api.Assertions.fail; -// -//public class FiltersListEncoderTest { -// @Test -// public void testFiltersListEncoder() { -// fail(); -// } -//} From 9d15e3dcb818b3b0410ebbc6c55aaee75da0e62c Mon Sep 17 00:00:00 2001 From: nick avlo Date: Wed, 19 Feb 2025 18:16:14 -0800 Subject: [PATCH 39/45] add convenience methods --- .../main/java/nostr/event/filter/Filters.java | 18 +- .../test/filters/FiltersEncoderTest.java | 155 +++++++-------- .../java/nostr/test/json/JsonParseTest.java | 182 ++++++------------ 3 files changed, 136 insertions(+), 219 deletions(-) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java index 1d5653e49..e9fb55ef4 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java @@ -8,6 +8,8 @@ import java.util.List; import java.util.Map; +import static java.util.stream.Collectors.groupingBy; + @EqualsAndHashCode public class Filters { public static final int DEFAULT_FILTERS_LIMIT = 10; @@ -18,12 +20,20 @@ public class Filters { @Setter private Integer limit = DEFAULT_FILTERS_LIMIT; - public Filters(@NonNull Map> filtersMap) { - validateFiltersMap(filtersMap); - this.filtersMap = filtersMap; + public Filters(@NonNull Filterable... filterablesByDefaultType) { + this(List.of(filterablesByDefaultType)); + } + + public Filters(@NonNull List filterablesByDefaultType) { + this(filterablesByDefaultType.stream().collect(groupingBy(Filterable::getFilterKey))); + } + + public Filters(@NonNull Map> filterablesByCustomType) { + validateFiltersMap(filterablesByCustomType); + this.filtersMap = filterablesByCustomType; } - public List getFilterableByType(@NonNull String type) { + public List getFilterByType(@NonNull String type) { return filtersMap.get(type); } diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java index 6fa8bee57..4b0fe716b 100644 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java @@ -24,11 +24,13 @@ import org.junit.jupiter.api.Test; import java.time.Instant; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -36,8 +38,34 @@ public class FiltersEncoderTest { @Test - public void testEventFilterEncoder() { - log.info("testEventFilterEncoder"); + public void testEventFilterEncoderUsingVarArgs() { + log.info("testEventFilterEncoderUsingVarArgs"); + + String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + + FiltersEncoder encoder = new FiltersEncoder(new Filters(new EventFilter<>(new GenericEvent(eventId)))); + + String encodedFilters = encoder.encode(); + assertEquals("{\"ids\":[\"" + eventId + "\"]}", encodedFilters); + } + + @Test + public void testEventFilterEncoderUsingList() { + log.info("testEventFilterEncoderUsingList"); + + List expectedFilters = new ArrayList<>(); + + String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + expectedFilters.add(new EventFilter<>(new GenericEvent(eventId))); + + FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + String encodedFilters = encoder.encode(); + assertEquals("{\"ids\":[\"" + eventId + "\"]}", encodedFilters); + } + + @Test + public void testEventFilterEncoderByMap() { + log.info("testEventFilterEncoderByMap"); Map> expectedFilters = new HashMap<>(); @@ -56,15 +84,9 @@ public void testEventFilterEncoder() { public void testKindFiltersEncoder() { log.info("testKindFiltersEncoder"); - Map> expectedFilters = new HashMap<>(); Kind kind = Kind.valueOf(1); + FiltersEncoder encoder = new FiltersEncoder(new Filters(new KindFilter<>(kind))); - String filterKey = KindFilter.filterKey; - expectedFilters.put(filterKey, - List.of( - new KindFilter<>(kind))); - - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String encodedFilters = encoder.encode(); assertEquals("{\"kinds\":[" + kind.toString() + "]}", encodedFilters); } @@ -74,13 +96,8 @@ public void testAuthorFilterEncoder() { log.info("testAuthorFilterEncoder"); String pubKeyString = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; + FiltersEncoder encoder = new FiltersEncoder(new Filters(new AuthorFilter<>(new PublicKey(pubKeyString)))); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(AuthorFilter.filterKey, - List.of( - new AuthorFilter<>(new PublicKey(pubKeyString)))); - - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String encodedFilters = encoder.encode(); assertEquals("{\"authors\":[\"" + pubKeyString + "\"]}", encodedFilters); } @@ -91,14 +108,11 @@ public void testMultipleAuthorFilterEncoder() { String pubKeyString1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String pubKeyString2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(AuthorFilter.filterKey, + FiltersEncoder encoder = new FiltersEncoder(new Filters( List.of( new AuthorFilter<>(new PublicKey(pubKeyString1)), - new AuthorFilter<>(new PublicKey(pubKeyString2)))); + new AuthorFilter<>(new PublicKey(pubKeyString2))))); - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String encodedFilters = encoder.encode(); String authorPubKeys = String.join("\",\"", pubKeyString1, pubKeyString2); @@ -109,17 +123,14 @@ public void testMultipleAuthorFilterEncoder() { public void testMultipleKindFiltersEncoder() { log.info("testMultipleKindFiltersEncoder"); - Map> expectedFilters = new HashMap<>(); Kind kind1 = Kind.valueOf(1); Kind kind2 = Kind.valueOf(2); - String filterKey = KindFilter.filterKey; - expectedFilters.put(filterKey, + FiltersEncoder encoder = new FiltersEncoder(new Filters( List.of( new KindFilter<>(kind1), - new KindFilter<>(kind2))); + new KindFilter<>(kind2)))); - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String encodedFilters = encoder.encode(); String kinds = String.join(",", kind1.toString(), kind2.toString()); assertEquals("{\"kinds\":[" + kinds + "]}", encodedFilters); @@ -131,7 +142,6 @@ public void testAddressableTagFilterEncoder() { Integer kind = 1; String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; - String uuidKey = "#d"; String uuidValue1 = "UUID-1"; AddressTag addressTag = new AddressTag(); @@ -139,12 +149,7 @@ public void testAddressableTagFilterEncoder() { addressTag.setPublicKey(new PublicKey(author)); addressTag.setIdentifierTag(new IdentifierTag(uuidValue1)); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(uuidKey, - List.of( - new AddressableTagFilter<>(addressTag))); - - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + FiltersEncoder encoder = new FiltersEncoder(new Filters(new AddressableTagFilter<>(addressTag))); String encodedFilters = encoder.encode(); String addressableTag = String.join(":", String.valueOf(kind), author, uuidValue1); @@ -157,12 +162,7 @@ public void testIdentifierTagFilterEncoder() { String uuidValue1 = "UUID-1"; - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(IdentifierTagFilter.filterKey, - List.of( - new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)))); - - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + FiltersEncoder encoder = new FiltersEncoder(new Filters(new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)))); String encodedFilters = encoder.encode(); assertEquals("{\"#d\":[\"" + uuidValue1 + "\"]}", encodedFilters); } @@ -174,13 +174,11 @@ public void testMultipleIdentifierTagFilterEncoder() { String uuidValue1 = "UUID-1"; String uuidValue2 = "UUID-2"; - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(IdentifierTagFilter.filterKey, + FiltersEncoder encoder = new FiltersEncoder(new Filters( List.of( new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)), - new IdentifierTagFilter<>(new IdentifierTag(uuidValue2)))); + new IdentifierTagFilter<>(new IdentifierTag(uuidValue2))))); - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String encodedFilters = encoder.encode(); String dTags = String.join("\",\"", uuidValue1, uuidValue2); assertEquals("{\"#d\":[\"" + dTags + "\"]}", encodedFilters); @@ -209,13 +207,11 @@ public void testMultipleReferencedEventFilterEncoder() { String eventId1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String eventId2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(ReferencedEventFilter.filterKey, + FiltersEncoder encoder = new FiltersEncoder(new Filters( List.of( new ReferencedEventFilter<>(new GenericEvent(eventId1)), - new ReferencedEventFilter<>(new GenericEvent(eventId2)))); + new ReferencedEventFilter<>(new GenericEvent(eventId2))))); - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String encodedFilters = encoder.encode(); String eventIds = String.join("\",\"", eventId1, eventId2); assertEquals("{\"#e\":[\"" + eventIds + "\"]}", encodedFilters); @@ -228,12 +224,9 @@ public void testSingleGenericTagQueryFiltersEncoder() { String geohashKey = "#g"; String new_geohash = "2vghde"; - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(geohashKey, - List.of( - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, new_geohash)))); + FiltersEncoder encoder = new FiltersEncoder( + new Filters(new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, new_geohash)))); - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String encodedFilters = encoder.encode(); assertEquals("{\"#g\":[\"2vghde\"]}", encodedFilters); } @@ -244,12 +237,8 @@ public void testReferencedPublicKeyFilterEncoder() { String pubKeyString = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(ReferencedPublicKeyFilter.filterKey, - List.of( - new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString)))); + FiltersEncoder encoder = new FiltersEncoder(new Filters(new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString)))); - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String encodedFilters = encoder.encode(); assertEquals("{\"#p\":[\"" + pubKeyString + "\"]}", encodedFilters); } @@ -261,13 +250,10 @@ public void testMultipleReferencedPublicKeyFilterEncoder() { String pubKeyString1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String pubKeyString2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(ReferencedPublicKeyFilter.filterKey, - List.of( - new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString1)), - new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString2)))); + FiltersEncoder encoder = new FiltersEncoder(new Filters( + new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString1)), + new ReferencedPublicKeyFilter<>(new PublicKey(pubKeyString2)))); - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String encodedFilters = encoder.encode(); String pubKeyTags = String.join("\",\"", pubKeyString1, pubKeyString2); assertEquals("{\"#p\":[\"" + pubKeyTags + "\"]}", encodedFilters); @@ -281,13 +267,10 @@ public void testMultipleGenericTagQueryFiltersEncoder() { String geohashValue1 = "2vghde"; String geohashValue2 = "3abcde"; - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(geohashKey, - List.of( - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); + FiltersEncoder encoder = new FiltersEncoder(new Filters( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String encodedFilters = encoder.encode(); assertEquals("{\"#g\":[\"2vghde\",\"3abcde\"]}", encodedFilters); } @@ -298,7 +281,6 @@ public void testMultipleAddressableTagFilterEncoder() { Integer kind = 1; String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; - String uuidKey = "#a"; String uuidValue1 = "UUID-1"; String uuidValue2 = "UUID-2"; @@ -315,13 +297,10 @@ public void testMultipleAddressableTagFilterEncoder() { addressTag2.setPublicKey(new PublicKey(author)); addressTag2.setIdentifierTag(new IdentifierTag(uuidValue2)); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(uuidKey, - List.of( - new AddressableTagFilter<>(addressTag1), - new AddressableTagFilter<>(addressTag2))); + FiltersEncoder encoder = new FiltersEncoder(new Filters( + new AddressableTagFilter<>(addressTag1), + new AddressableTagFilter<>(addressTag2))); - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); String encoded = encoder.encode(); String addressableTags = String.join("\",\"", addressableTag1, addressableTag2); assertEquals("{\"#a\":[\"" + addressableTags + "\"]}", encoded); @@ -331,15 +310,9 @@ public void testMultipleAddressableTagFilterEncoder() { public void testSinceFiltersEncoder() { log.info("testSinceFiltersEncoder"); - String sinceKey = SinceFilter.filterKey; Long since = Date.from(Instant.now()).getTime(); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(sinceKey, - List.of( - new SinceFilter(since))); - - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + FiltersEncoder encoder = new FiltersEncoder(new Filters(new SinceFilter(since))); String encodedFilters = encoder.encode(); assertEquals("{\"since\":" + since + "}", encodedFilters); } @@ -348,15 +321,9 @@ public void testSinceFiltersEncoder() { public void testUntilFiltersEncoder() { log.info("testUntilFiltersEncoder"); - String untilKey = UntilFilter.filterKey; Long until = Date.from(Instant.now()).getTime(); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(untilKey, - List.of( - new UntilFilter(until))); - - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + FiltersEncoder encoder = new FiltersEncoder(new Filters(new UntilFilter(until))); String encodedFilters = encoder.encode(); assertEquals("{\"until\":" + until + "}", encodedFilters); } @@ -385,6 +352,16 @@ public void testReqMessageEmptyFilters() { public void testReqMessageEmptyFilterKey() { log.info("testReqMessageEmptyFilterKey"); String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; + + assertDoesNotThrow(() -> + new ReqMessage(subscriptionId, new Filters( + new GenericTagQueryFilter<>(new GenericTagQuery("some-tag", "some-value"))))); + } + + @Test + public void testReqMessageEmptyFilterKeyAsMap() { + log.info("testReqMessageEmptyFilterKey"); + String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; Map> emptyFilterKey = new HashMap<>(); emptyFilterKey.put("", List.of( 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 64ea529f7..65a7aed69 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 @@ -44,9 +44,7 @@ import org.junit.jupiter.api.Test; import java.math.BigDecimal; -import java.util.HashMap; import java.util.List; -import java.util.Map; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -82,15 +80,15 @@ public void testBaseMessageDecoderEventFilter() throws JsonProcessingException { Filters filters = ((ReqMessage) message).getFiltersList().getFirst(); - List kindFilters = filters.getFilterableByType(KindFilter.filterKey); + List kindFilters = filters.getFilterByType(KindFilter.filterKey); assertEquals(1, kindFilters.size()); assertEquals(new KindFilter<>(Kind.TEXT_NOTE), kindFilters.getFirst()); - List eventFilter = filters.getFilterableByType(EventFilter.filterKey); + List eventFilter = filters.getFilterByType(EventFilter.filterKey); assertEquals(1, eventFilter.size()); assertEquals(new EventFilter<>(new GenericEvent("f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75")), eventFilter.getFirst()); - List referencedPublicKeyfilter = filters.getFilterableByType(ReferencedPublicKeyFilter.filterKey); + List referencedPublicKeyfilter = filters.getFilterByType(ReferencedPublicKeyFilter.filterKey); assertEquals(1, referencedPublicKeyfilter.size()); assertEquals(new ReferencedPublicKeyFilter<>(new PublicKey("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712")), referencedPublicKeyfilter.getFirst()); } @@ -114,15 +112,15 @@ public void testBaseMessageDecoderKindsAuthorsReferencedPublicKey() throws JsonP Filters filters = ((ReqMessage) message).getFiltersList().getFirst(); - List kindFilters = filters.getFilterableByType(KindFilter.filterKey); + List kindFilters = filters.getFilterByType(KindFilter.filterKey); assertEquals(1, kindFilters.size()); assertEquals(new KindFilter<>(Kind.TEXT_NOTE), kindFilters.getFirst()); - List authorFilters = filters.getFilterableByType(AuthorFilter.filterKey); + List authorFilters = filters.getFilterByType(AuthorFilter.filterKey); assertEquals(1, authorFilters.size()); assertEquals(new AuthorFilter<>(new PublicKey("f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75")), authorFilters.getFirst()); - List referencedPublicKeyfilter = filters.getFilterableByType(ReferencedPublicKeyFilter.filterKey); + List referencedPublicKeyfilter = filters.getFilterByType(ReferencedPublicKeyFilter.filterKey); assertEquals(1, referencedPublicKeyfilter.size()); assertEquals(new ReferencedPublicKeyFilter<>(new PublicKey("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712")), referencedPublicKeyfilter.getFirst()); } @@ -146,15 +144,15 @@ public void testBaseMessageDecoderKindsAuthorsReferencedEvents() throws JsonProc Filters filters = ((ReqMessage) message).getFiltersList().getFirst(); - List kindFilters = filters.getFilterableByType(KindFilter.filterKey); + List kindFilters = filters.getFilterByType(KindFilter.filterKey); assertEquals(1, kindFilters.size()); assertEquals(new KindFilter<>(Kind.TEXT_NOTE), kindFilters.getFirst()); - List authorFilters = filters.getFilterableByType(AuthorFilter.filterKey); + List authorFilters = filters.getFilterByType(AuthorFilter.filterKey); assertEquals(1, authorFilters.size()); assertEquals(new AuthorFilter<>(new PublicKey("f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75")), authorFilters.getFirst()); - List referencedEventFilters = filters.getFilterableByType(ReferencedEventFilter.filterKey); + List referencedEventFilters = filters.getFilterByType(ReferencedEventFilter.filterKey); assertEquals(1, referencedEventFilters.size()); assertEquals(new ReferencedEventFilter<>(new GenericEvent("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712")), referencedEventFilters.getFirst()); } @@ -165,23 +163,13 @@ public void testBaseReqMessageDecoder() throws JsonProcessingException { var publicKey = Identity.generateRandomIdentity().getPublicKey(); - Map> expectedFilters = new HashMap<>(); - - expectedFilters.put(AuthorFilter.filterKey, - List.of( - new AuthorFilter<>(publicKey))); - - expectedFilters.put(KindFilter.filterKey, - List.of( - new KindFilter<>(Kind.CONTACT_LIST), - new KindFilter<>(Kind.DELETION))); - - expectedFilters.put(KindFilter.filterKey, - List.of( + final var expectedReqMessage = new ReqMessage(publicKey.toString(), + new Filters( new KindFilter<>(Kind.SET_METADATA), - new KindFilter<>(Kind.TEXT_NOTE))); - - final var expectedReqMessage = new ReqMessage(publicKey.toString(), new Filters(expectedFilters)); + new KindFilter<>(Kind.TEXT_NOTE), + new KindFilter<>(Kind.CONTACT_LIST), + new KindFilter<>(Kind.DELETION), + new AuthorFilter<>(publicKey))); String jsonMessage = expectedReqMessage.encode(); @@ -377,13 +365,11 @@ public void testReqMessageFilterListSerializer() { String new_geohash = "2vghde"; String second_geohash = "3abcde"; - Map> expectedFilters = new HashMap<>(); - expectedFilters.put("#g", - List.of( + ReqMessage reqMessage = new ReqMessage("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9", + new Filters( new GenericTagQueryFilter<>(new GenericTagQuery("#g", new_geohash)), new GenericTagQueryFilter<>(new GenericTagQuery("#g", second_geohash)))); - ReqMessage reqMessage = new ReqMessage("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9", new Filters(expectedFilters)); assertDoesNotThrow(() -> { String jsonMessage = reqMessage.encode(); String expected = "[\"REQ\",\"npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9\",{\"#g\":[\"2vghde\",\"3abcde\"]}]"; @@ -402,12 +388,10 @@ public void testReqMessageDeserializer() throws JsonProcessingException { ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put("#g", - List.of( + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, + new Filters( new GenericTagQueryFilter<>(new GenericTagQuery("#g", geohashValue)))); - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); assertEquals(expectedReqMessage, decodedReqMessage); } @@ -424,12 +408,11 @@ public void testReqMessageFilterListDecoder() { assertDoesNotThrow(() -> { ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFiltersToDecode); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(geohashKey, List.of( - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, + new Filters( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); assertEquals(reqJsonWithCustomTagQueryFiltersToDecode, decodedReqMessage.encode()); assertEquals(expectedReqMessage, decodedReqMessage); }); @@ -459,28 +442,14 @@ public void testReqMessagePopulatedFilterDecoder() { assertDoesNotThrow(() -> { ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); - Map> expectedFilters = new HashMap<>(); - - expectedFilters.put(AuthorFilter.filterKey, - List.of( - new AuthorFilter<>(new PublicKey(author)))); - - expectedFilters.put(KindFilter.filterKey, - List.of( - new KindFilter<>(Kind.TEXT_NOTE))); - - expectedFilters.put(ReferencedEventFilter.filterKey, - List.of(new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); - - expectedFilters.put(geohashKey, List.of( - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); - - expectedFilters.put(ReferencedPublicKeyFilter.filterKey, - List.of( - new ReferencedPublicKeyFilter<>(new PublicKey(author)))); - - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, + new Filters( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)), + new ReferencedPublicKeyFilter<>(new PublicKey(author)), + new KindFilter<>(Kind.TEXT_NOTE), + new AuthorFilter<>(new PublicKey(author)), + new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); assertEquals(expectedReqMessage, decodedReqMessage); }); @@ -512,30 +481,16 @@ public void testReqMessagePopulatedListOfFiltersWithIdentityDecoder() throws Jso ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(KindFilter.filterKey, - List.of( - new KindFilter<>(Kind.TEXT_NOTE))); - - expectedFilters.put(AuthorFilter.filterKey, - List.of( - new AuthorFilter<>(new PublicKey(author)))); - - expectedFilters.put(ReferencedEventFilter.filterKey, - List.of( - new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); - - expectedFilters.put(geohashKey, - List.of( + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, + new Filters( + new KindFilter<>(Kind.TEXT_NOTE), + new AuthorFilter<>(new PublicKey(author)), + new ReferencedEventFilter<>(new GenericEvent(referencedEventId)), new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); - - expectedFilters.put(uuidKey, - List.of( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)), new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)), new IdentifierTagFilter<>(new IdentifierTag(uuidValue2)))); - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); assertEquals(expectedReqMessage, decodedReqMessage); } @@ -563,20 +518,18 @@ public void testReqMessagePopulatedListOfFiltersListDecoder() throws JsonProcess ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(KindFilter.filterKey, List.of(new KindFilter<>(Kind.TEXT_NOTE))); - expectedFilters.put(AuthorFilter.filterKey, List.of(new AuthorFilter<>(new PublicKey(author)))); - expectedFilters.put(ReferencedEventFilter.filterKey, List.of(new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); - expectedFilters.put(ReferencedPublicKeyFilter.filterKey, List.of(new ReferencedPublicKeyFilter<>(new PublicKey(author)))); - AddressTag addressTag1 = new AddressTag(); addressTag1.setKind(kind); addressTag1.setPublicKey(new PublicKey(author)); addressTag1.setIdentifierTag(new IdentifierTag(uuidValue1)); - expectedFilters.put(AddressableTagFilter.filterKey, List.of(new AddressableTagFilter<>(addressTag1))); - - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, + new Filters( + new KindFilter<>(Kind.TEXT_NOTE), + new AuthorFilter<>(new PublicKey(author)), + new ReferencedEventFilter<>(new GenericEvent(referencedEventId)), + new ReferencedPublicKeyFilter<>(new PublicKey(author)), + new AddressableTagFilter<>(addressTag1))); assertEquals(expectedReqMessage.encode(), decodedReqMessage.encode()); assertEquals(expectedReqMessage, decodedReqMessage); @@ -602,18 +555,14 @@ public void testReqMessagePopulatedListOfMultipleTypeFiltersListDecoder() throws ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(KindFilter.filterKey, - List.of( + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, + new Filters( new KindFilter<>(Kind.TEXT_NOTE), - new KindFilter<>(Kind.RECOMMEND_SERVER))); - expectedFilters.put(AuthorFilter.filterKey, - List.of( + new KindFilter<>(Kind.RECOMMEND_SERVER), new AuthorFilter<>(new PublicKey(author)), - new AuthorFilter<>(new PublicKey(author2)))); - expectedFilters.put(ReferencedEventFilter.filterKey, List.of(new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); + new AuthorFilter<>(new PublicKey(author2)), + new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); assertEquals(expectedReqMessage.encode(), decodedReqMessage.encode()); assertEquals(expectedReqMessage, decodedReqMessage); } @@ -646,33 +595,18 @@ public void testGenericTagQueryListDecoder() throws JsonProcessingException { ReqMessage decodedReqMessage = new BaseMessageDecoder().decode(reqJsonWithCustomTagQueryFilterToDecode); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(KindFilter.filterKey, - List.of( + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, + new Filters( new KindFilter<>(Kind.TEXT_NOTE), - new KindFilter<>(Kind.RECOMMEND_SERVER))); - - expectedFilters.put(AuthorFilter.filterKey, - List.of( + new KindFilter<>(Kind.RECOMMEND_SERVER), new AuthorFilter<>(new PublicKey(author)), - new AuthorFilter<>(new PublicKey(author2)))); - - expectedFilters.put(ReferencedEventFilter.filterKey, - List.of( - new ReferencedEventFilter<>(new GenericEvent(referencedEventId)))); - - expectedFilters.put(geohashKey, - List.of( + new AuthorFilter<>(new PublicKey(author2)), + new ReferencedEventFilter<>(new GenericEvent(referencedEventId)), new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); - - expectedFilters.put(uuidKey, - List.of( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)), new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)), new IdentifierTagFilter<>(new IdentifierTag(uuidValue2)))); - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); - assertTrue(JsonComparator.isEquivalentJson( mapper.createArrayNode() .add(mapper.readTree( @@ -703,11 +637,7 @@ public void testReqMessageAddressableTagDeserializer() throws JsonProcessingExce addressTag1.setPublicKey(new PublicKey(author)); addressTag1.setIdentifierTag(new IdentifierTag(uuidValue1)); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put("#a", - List.of(new AddressableTagFilter<>(addressTag1))); - - ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(expectedFilters)); + ReqMessage expectedReqMessage = new ReqMessage(subscriptionId, new Filters(new AddressableTagFilter<>(addressTag1))); assertEquals(expectedReqMessage.encode(), decodedReqMessage.encode()); assertEquals(expectedReqMessage, decodedReqMessage); From a20215f2d9f855e6de369deaea1a20e18faefffb Mon Sep 17 00:00:00 2001 From: nick avlo Date: Wed, 19 Feb 2025 18:43:22 -0800 Subject: [PATCH 40/45] cleanup --- .../codec}/FilterableProvider.java | 19 ++- .../event/json/codec/FiltersDecoder.java | 1 - .../java/nostr/examples/NostrApiExamples.java | 20 +-- .../test/filters/FiltersDecoderTest.java | 137 ++++++------------ 4 files changed, 69 insertions(+), 108 deletions(-) rename nostr-java-event/src/main/java/nostr/event/{filter => json/codec}/FilterableProvider.java (70%) diff --git a/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FilterableProvider.java similarity index 70% rename from nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java rename to nostr-java-event/src/main/java/nostr/event/json/codec/FilterableProvider.java index c93221b57..507d24f6c 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/FilterableProvider.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FilterableProvider.java @@ -1,9 +1,20 @@ -package nostr.event.filter; +package nostr.event.json.codec; import com.fasterxml.jackson.databind.JsonNode; import nostr.base.GenericTagQuery; import nostr.base.PublicKey; import nostr.event.Kind; +import nostr.event.filter.AddressableTagFilter; +import nostr.event.filter.AuthorFilter; +import nostr.event.filter.EventFilter; +import nostr.event.filter.Filterable; +import nostr.event.filter.GenericTagQueryFilter; +import nostr.event.filter.IdentifierTagFilter; +import nostr.event.filter.KindFilter; +import nostr.event.filter.ReferencedEventFilter; +import nostr.event.filter.ReferencedPublicKeyFilter; +import nostr.event.filter.SinceFilter; +import nostr.event.filter.UntilFilter; import nostr.event.impl.GenericEvent; import nostr.event.tag.IdentifierTag; @@ -11,8 +22,8 @@ import java.util.function.Function; import java.util.stream.StreamSupport; -public class FilterableProvider { - public static List getFilterable(String type, JsonNode node) { +class FilterableProvider { + protected static List getFilterable(String type, JsonNode node) { return switch (type) { case ReferencedPublicKeyFilter.filterKey -> getFilterable(node, referencedPubKey -> new ReferencedPublicKeyFilter<>(new PublicKey(referencedPubKey.asText()))); case ReferencedEventFilter.filterKey -> getFilterable(node, referencedEvent -> new ReferencedEventFilter<>(new GenericEvent(referencedEvent.asText()))); @@ -28,7 +39,7 @@ public static List getFilterable(String type, JsonNode node) { }; } - public static List getFilterable(JsonNode jsonNode, Function filterFunction) { + private static List getFilterable(JsonNode jsonNode, Function filterFunction) { return StreamSupport.stream(jsonNode.spliterator(), false).map(filterFunction).toList(); } } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java index 8a6f790c6..db82d208e 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java @@ -5,7 +5,6 @@ import lombok.NonNull; import lombok.SneakyThrows; import nostr.event.filter.Filterable; -import nostr.event.filter.FilterableProvider; import nostr.event.filter.Filters; import java.util.HashMap; diff --git a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java index 6154f78f4..b2316ca45 100644 --- a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java +++ b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java @@ -15,10 +15,9 @@ import nostr.event.BaseTag; import nostr.event.Kind; import nostr.event.Reaction; -import nostr.event.filter.Filterable; +import nostr.event.filter.AuthorFilter; import nostr.event.filter.Filters; import nostr.event.filter.KindFilter; -import nostr.event.filter.AuthorFilter; import nostr.event.filter.SinceFilter; import nostr.event.impl.ChannelCreateEvent; import nostr.event.impl.ChannelMessageEvent; @@ -45,7 +44,6 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Calendar; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -305,16 +303,14 @@ public static void filters() throws InterruptedException { var subId = "subId" + date.getTimeInMillis(); date.add(Calendar.DAY_OF_MONTH, -5); - Map> filterablesMap = new HashMap<>(); - filterablesMap.put(KindFilter.filterKey, List.of( - new KindFilter<>(Kind.EPHEMEREAL_EVENT), - new KindFilter<>(Kind.TEXT_NOTE))); - filterablesMap.put(AuthorFilter.filterKey, List.of( - new AuthorFilter<>(new PublicKey("21ef0d8541375ae4bca85285097fba370f7e540b5a30e5e75670c16679f9d144")))); - filterablesMap.put(SinceFilter.filterKey, List.of(new SinceFilter(date.getTimeInMillis()/1000))); - var nip01 = NIP01.getInstance(); - nip01.setRelays(RELAYS).sendRequest(new Filters(filterablesMap), subId); + nip01.setRelays(RELAYS).sendRequest( + new Filters( + new KindFilter<>(Kind.EPHEMEREAL_EVENT), + new KindFilter<>(Kind.TEXT_NOTE), + new AuthorFilter<>(new PublicKey("21ef0d8541375ae4bca85285097fba370f7e540b5a30e5e75670c16679f9d144")), + new SinceFilter(date.getTimeInMillis()/1000)), subId); + Thread.sleep(5000); } diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java index db085fb99..b126dae73 100644 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java @@ -6,7 +6,6 @@ import nostr.event.Kind; import nostr.event.filter.AddressableTagFilter; import nostr.event.filter.EventFilter; -import nostr.event.filter.Filterable; import nostr.event.filter.Filters; import nostr.event.filter.GenericTagQueryFilter; import nostr.event.filter.IdentifierTagFilter; @@ -23,9 +22,6 @@ import java.time.Instant; import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -43,10 +39,10 @@ public void testEventFiltersDecoder() { String expected = "{\"" + filterKey + "\":[\"" + eventId + "\"]}"; Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, List.of(new EventFilter<>(new GenericEvent(eventId)))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + assertEquals( + new Filters( + new EventFilter<>(new GenericEvent(eventId))), + decodedFilters); } @Test @@ -62,13 +58,11 @@ public void testMultipleEventFiltersDecoder() { String expected = "{\"" + filterKey + "\":[\"" + joined + "\"]}"; Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, - List.of( + assertEquals( + new Filters( new EventFilter<>(new GenericEvent(eventId1)), - new EventFilter<>(new GenericEvent(eventId2)))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + new EventFilter<>(new GenericEvent(eventId2))), + decodedFilters); } @@ -76,7 +70,6 @@ public void testMultipleEventFiltersDecoder() { public void testAddressableTagFiltersDecoder() { log.info("testAddressableTagFiltersDecoder"); - String filterKey = AddressableTagFilter.filterKey; Integer kind = 1; String author = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String uuidValue1 = "UUID-1"; @@ -91,17 +84,16 @@ public void testAddressableTagFiltersDecoder() { String expected = "{\"#a\":[\"" + joined + "\"]}"; Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, List.of(new AddressableTagFilter<>(addressTag))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + assertEquals( + new Filters( + new AddressableTagFilter<>(addressTag)), + decodedFilters); } @Test public void testMultipleAddressableTagFiltersDecoder() { log.info("testMultipleAddressableTagFiltersDecoder"); - String filterKey = AddressableTagFilter.filterKey; Integer kind1 = 1; String author1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String uuidValue1 = "UUID-1"; @@ -128,13 +120,11 @@ public void testMultipleAddressableTagFiltersDecoder() { String expected = "{\"#a\":[\"" + joined3 + "\"]}"; Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, - List.of( + assertEquals( + new Filters( new AddressableTagFilter<>(addressTag1), - new AddressableTagFilter<>(addressTag2))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + new AddressableTagFilter<>(addressTag2)), + decodedFilters); } @Test @@ -147,10 +137,7 @@ public void testKindFiltersDecoder() { String expected = "{\"" + filterKey + "\":[" + kind.toString() + "]}"; Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, List.of(new KindFilter<>(kind))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + assertEquals(new Filters(new KindFilter<>(kind)), decodedFilters); } @Test @@ -166,35 +153,30 @@ public void testMultipleKindFiltersDecoder() { String expected = "{\"" + filterKey + "\":[" + join + "]}"; Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, List.of( - new KindFilter<>(kind1), - new KindFilter<>(kind2))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + assertEquals( + new Filters( + new KindFilter<>(kind1), + new KindFilter<>(kind2)), + decodedFilters); } @Test public void testIdentifierTagFilterDecoder() { log.info("testIdentifierTagFilterDecoder"); - String filterKey = IdentifierTagFilter.filterKey; String uuidValue1 = "UUID-1"; String expected = "{\"#d\":[\"" + uuidValue1 + "\"]}"; Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, List.of(new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)))); - assertEquals(new Filters(expectedFilters), decodedFilters); + assertEquals(new Filters(new IdentifierTagFilter<>(new IdentifierTag(uuidValue1))), decodedFilters); } @Test public void testMultipleIdentifierTagFilterDecoder() { log.info("testMultipleIdentifierTagFilterDecoder"); - String filterKey = IdentifierTagFilter.filterKey; String uuidValue1 = "UUID-1"; String uuidValue2 = "UUID-2"; @@ -203,36 +185,29 @@ public void testMultipleIdentifierTagFilterDecoder() { Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, - List.of( + assertEquals( + new Filters( new IdentifierTagFilter<>(new IdentifierTag(uuidValue1)), - new IdentifierTagFilter<>(new IdentifierTag(uuidValue2)))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + new IdentifierTagFilter<>(new IdentifierTag(uuidValue2))), + decodedFilters); } @Test public void testReferencedEventFilterDecoder() { log.info("testReferencedEventFilterDecoder"); - String filterKey = ReferencedEventFilter.filterKey; String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String expected = "{\"#e\":[\"" + eventId + "\"]}"; Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, List.of(new ReferencedEventFilter<>(new GenericEvent(eventId)))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + assertEquals(new Filters(new ReferencedEventFilter<>(new GenericEvent(eventId))), decodedFilters); } @Test public void testMultipleReferencedEventFilterDecoder() { log.info("testMultipleReferencedEventFilterDecoder"); - String filterKey = ReferencedEventFilter.filterKey; String eventId1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String eventId2 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; @@ -240,36 +215,29 @@ public void testMultipleReferencedEventFilterDecoder() { String expected = "{\"#e\":[\"" + joined + "\"]}"; Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, - List.of( + assertEquals( + new Filters( new ReferencedEventFilter<>(new GenericEvent(eventId1)), - new ReferencedEventFilter<>(new GenericEvent(eventId2)))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + new ReferencedEventFilter<>(new GenericEvent(eventId2))), + decodedFilters); } @Test public void testReferencedPublicKeyFilterDecoder() { log.info("testReferencedPublicKeyFilterDecoder"); - String filterKey = ReferencedPublicKeyFilter.filterKey; String pubkeyString = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String expected = "{\"#p\":[\"" + pubkeyString + "\"]}"; Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, List.of(new ReferencedPublicKeyFilter<>(new PublicKey(pubkeyString)))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + assertEquals(new Filters(new ReferencedPublicKeyFilter<>(new PublicKey(pubkeyString))), decodedFilters); } @Test public void testMultipleReferencedPublicKeyFilterDecoder() { log.info("testMultipleReferencedPublicKeyFilterDecoder"); - String filterKey = ReferencedPublicKeyFilter.filterKey; String pubkeyString1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String pubkeyString2 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; @@ -278,12 +246,11 @@ public void testMultipleReferencedPublicKeyFilterDecoder() { Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, List.of( - new ReferencedPublicKeyFilter<>(new PublicKey(pubkeyString1)), - new ReferencedPublicKeyFilter<>(new PublicKey(pubkeyString2)))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + assertEquals( + new Filters( + new ReferencedPublicKeyFilter<>(new PublicKey(pubkeyString1)), + new ReferencedPublicKeyFilter<>(new PublicKey(pubkeyString2))), + decodedFilters); } @Test @@ -296,10 +263,7 @@ public void testGenericTagFiltersDecoder() { Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(geohashKey, List.of(new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue)))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + assertEquals(new Filters(new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue))), decodedFilters); } @Test @@ -313,44 +277,35 @@ public void testMultipleGenericTagFiltersDecoder() { Filters decodedFilters = new FiltersDecoder<>().decode(reqJsonWithCustomTagQueryFilterToDecode); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(geohashKey, List.of( - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), - new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2)))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + assertEquals( + new Filters( + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue1)), + new GenericTagQueryFilter<>(new GenericTagQuery(geohashKey, geohashValue2))), + decodedFilters); } @Test public void testSinceFiltersDecoder() { log.info("testSinceFiltersDecoder"); - String filterKey = SinceFilter.filterKey; Long since = Date.from(Instant.now()).getTime(); String expected = "{\"since\":" + since + "}"; Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, List.of(new SinceFilter(since))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + assertEquals(new Filters(new SinceFilter(since)), decodedFilters); } @Test public void testUntilFiltersDecoder() { log.info("testUntilFiltersDecoder"); - String filterKey = UntilFilter.filterKey; Long until = Date.from(Instant.now()).getTime(); String expected = "{\"until\":" + until + "}"; Filters decodedFilters = new FiltersDecoder<>().decode(expected); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(filterKey, List.of(new UntilFilter(until))); - - assertEquals(new Filters(expectedFilters), decodedFilters); + assertEquals(new Filters(new UntilFilter(until)), decodedFilters); } @Test From 3197babde6d256b0b7da1dc3395615a17facbf86 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Wed, 19 Feb 2025 19:21:22 -0800 Subject: [PATCH 41/45] cleanup --- nostr-java-api/pom.xml | 2 +- nostr-java-api/src/main/java/module-info.java | 4 +- .../src/main/java/nostr/api/NIP52.java | 22 +++++------ .../main/java/nostr/base/GenericTagQuery.java | 14 +++---- .../event/json/codec/FiltersDecoder.java | 22 +++++------ .../event/json/codec/FiltersEncoder.java | 38 +++++++++---------- .../nostr/test/event/APINIP09EventTest.java | 11 ++---- ...ntTestUsingSpringWebSocketClientTest.java} | 2 +- pom.xml | 1 + 9 files changed, 56 insertions(+), 60 deletions(-) rename nostr-java-test/src/test/java/nostr/test/event/{ApiEventTestUsingSpringWebSocketClientIFTest.java => ApiEventTestUsingSpringWebSocketClientTest.java} (98%) diff --git a/nostr-java-api/pom.xml b/nostr-java-api/pom.xml index 033a220c3..8976dae99 100644 --- a/nostr-java-api/pom.xml +++ b/nostr-java-api/pom.xml @@ -47,7 +47,7 @@ org.apache.commons commons-lang3 - 3.17.0 + ${commons-lang3.version} diff --git a/nostr-java-api/src/main/java/module-info.java b/nostr-java-api/src/main/java/module-info.java index 14f6eb128..3290a781e 100644 --- a/nostr-java-api/src/main/java/module-info.java +++ b/nostr-java-api/src/main/java/module-info.java @@ -14,7 +14,7 @@ requires lombok; requires java.logging; requires nostr.crypto; - requires org.apache.commons.lang3; + requires org.apache.commons.lang3; - exports nostr.api; + exports nostr.api; } diff --git a/nostr-java-api/src/main/java/nostr/api/NIP52.java b/nostr-java-api/src/main/java/nostr/api/NIP52.java index c3e6c7528..fc2e6e0e5 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP52.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP52.java @@ -12,17 +12,17 @@ import java.util.List; public class NIP52 extends EventNostr { - public NIP52(@NonNull Identity sender) { - setSender(sender); - } + public NIP52(@NonNull Identity sender) { + setSender(sender); + } - public NIP52 createCalendarTimeBasedEvent(@NonNull List baseTags, @NonNull String content, @NonNull CalendarContent calendarContent) { - setEvent((T) new CalendarTimeBasedEventFactory(getSender(), baseTags, content, calendarContent).create()); - return this; - } + public NIP52 createCalendarTimeBasedEvent(@NonNull List baseTags, @NonNull String content, @NonNull CalendarContent calendarContent) { + setEvent((T) new CalendarTimeBasedEventFactory(getSender(), baseTags, content, calendarContent).create()); + return this; + } - public NIP52 createCalendarRsvpEvent(@NonNull List baseTags, @NonNull String content, @NonNull CalendarRsvpContent calendarRsvpContent) { - setEvent((T) new CalendarRsvpEventFactory(getSender(), baseTags, content, calendarRsvpContent).create()); - return this; - } + public NIP52 createCalendarRsvpEvent(@NonNull List baseTags, @NonNull String content, @NonNull CalendarRsvpContent calendarRsvpContent) { + setEvent((T) new CalendarRsvpEventFactory(getSender(), baseTags, content, calendarRsvpContent).create()); + return this; + } } diff --git a/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java b/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java index 89bccb35e..c11446680 100644 --- a/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java +++ b/nostr-java-base/src/main/java/nostr/base/GenericTagQuery.java @@ -15,13 +15,13 @@ @AllArgsConstructor public class GenericTagQuery { - private String tagName; + private String tagName; - @JsonProperty - private String value; + @JsonProperty + private String value; - @JsonIgnore - public Integer getNip() { - return 1; - } + @JsonIgnore + public Integer getNip() { + return 1; + } } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java index db82d208e..c7f35bd9a 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java @@ -16,19 +16,19 @@ */ @Data public class FiltersDecoder implements FDecoder { - private final static ObjectMapper mapper = new ObjectMapper(); + private final static ObjectMapper mapper = new ObjectMapper(); - @SneakyThrows - public T decode(@NonNull String jsonFiltersList) { - final Map> filterPluginsMap = new HashMap<>(); + @SneakyThrows + public T decode(@NonNull String jsonFiltersList) { + final Map> filterPluginsMap = new HashMap<>(); - mapper.readTree(jsonFiltersList).fields().forEachRemaining(field -> - filterPluginsMap.put( - field.getKey(), - FilterableProvider.getFilterable( + mapper.readTree(jsonFiltersList).fields().forEachRemaining(field -> + filterPluginsMap.put( field.getKey(), - field.getValue()))); + FilterableProvider.getFilterable( + field.getKey(), + field.getValue()))); - return (T) new Filters(filterPluginsMap); - } + return (T) new Filters(filterPluginsMap); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java index 8a439e963..5675cd752 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersEncoder.java @@ -9,27 +9,27 @@ @Data @EqualsAndHashCode(callSuper = false) public class FiltersEncoder implements FEncoder { - private final Filters filters; + private final Filters filters; - public FiltersEncoder(Filters filters) { - this.filters = filters; - } + public FiltersEncoder(Filters filters) { + this.filters = filters; + } - @Override - public String encode() { - ObjectNode root = MAPPER.createObjectNode(); + @Override + public String encode() { + ObjectNode root = MAPPER.createObjectNode(); - filters.getFiltersMap().forEach((key, filterableList) -> { - final ObjectNode objectNode = MAPPER.createObjectNode(); - root.setAll( - filterableList - .stream() - .map(filterable -> - filterable.toObjectNode(objectNode)) - .toList() - .getFirst()); - }); + filters.getFiltersMap().forEach((key, filterableList) -> { + final ObjectNode objectNode = MAPPER.createObjectNode(); + root.setAll( + filterableList + .stream() + .map(filterable -> + filterable.toObjectNode(objectNode)) + .toList() + .getFirst()); + }); - return root.toString(); - } + return root.toString(); + } } diff --git a/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java b/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java index a1bc6cc8b..f33bbc732 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java @@ -7,7 +7,6 @@ import nostr.event.BaseTag; import nostr.event.Kind; import nostr.event.filter.AuthorFilter; -import nostr.event.filter.Filterable; import nostr.event.filter.Filters; import nostr.event.filter.KindFilter; import nostr.event.impl.GenericEvent; @@ -21,7 +20,6 @@ import org.junit.jupiter.api.Test; import java.io.IOException; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -45,13 +43,10 @@ public void deleteEvent() throws IOException { NIP01 nip01 = new NIP01<>(identity); nip01.createTextNoteEvent("Delete me!").signAndSend(Map.of("local", RELAY_URI)); - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(KindFilter.filterKey, List.of( - new KindFilter<>(Kind.TEXT_NOTE))); - expectedFilters.put(AuthorFilter.filterKey, List.of( - new AuthorFilter<>(identity.getPublicKey()))); + Filters filters = new Filters( + new KindFilter<>(Kind.TEXT_NOTE), + new AuthorFilter<>(identity.getPublicKey())); - Filters filters = new Filters(expectedFilters); List result = nip01.sendRequest(filters, UUID.randomUUID().toString()); assertFalse(result.isEmpty()); diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientIFTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientTest.java similarity index 98% rename from nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientIFTest.java rename to nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientTest.java index 19ce7f752..a71f2c04c 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientIFTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientTest.java @@ -21,7 +21,7 @@ import static nostr.test.event.ApiEventTest.createProduct; import static nostr.test.event.ApiEventTest.createStall; -class ApiEventTestUsingSpringWebSocketClientIFTest { +class ApiEventTestUsingSpringWebSocketClientTest { private static Map relays; private SpringWebSocketClient springWebSocketClient; diff --git a/pom.xml b/pom.xml index 7ecf28609..e4e6e6d44 100644 --- a/pom.xml +++ b/pom.xml @@ -94,6 +94,7 @@ 1.78 2.17.2 + 3.17.0 5.10.2 From 0b7b97f189389882c4a7c1d768b981cd321cdd09 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Wed, 19 Feb 2025 19:28:28 -0800 Subject: [PATCH 42/45] cleanup --- .../src/main/java/nostr/api/EventNostr.java | 12 +-- .../src/main/java/nostr/api/NIP01.java | 6 +- .../java/nostr/event/message/ReqMessage.java | 88 +++++++++---------- .../nostr/test/event/APINIP09EventTest.java | 28 +++--- 4 files changed, 67 insertions(+), 67 deletions(-) diff --git a/nostr-java-api/src/main/java/nostr/api/EventNostr.java b/nostr-java-api/src/main/java/nostr/api/EventNostr.java index 4c269219b..af3310462 100644 --- a/nostr-java-api/src/main/java/nostr/api/EventNostr.java +++ b/nostr-java-api/src/main/java/nostr/api/EventNostr.java @@ -10,6 +10,7 @@ import lombok.Setter; import nostr.api.factory.impl.GenericEventFactory; import nostr.base.PublicKey; +import nostr.event.BaseMessage; import nostr.event.BaseTag; import nostr.event.impl.GenericEvent; import nostr.event.json.codec.BaseMessageDecoder; @@ -19,7 +20,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import nostr.event.BaseMessage; /** * @author guilhermegps @@ -52,11 +52,11 @@ public U send(Map relays) { BaseMessageDecoder decoder = new BaseMessageDecoder<>(); return new FailableStream<>(messages.stream()) - .map(msg -> (U) decoder.decode(msg)) - .filter(Objects::nonNull) - .stream() - .findFirst() - .orElseThrow(() -> new RuntimeException("No message received")); + .map(msg -> (U) decoder.decode(msg)) + .filter(Objects::nonNull) + .stream() + .findFirst() + .orElseThrow(() -> new RuntimeException("No message received")); } public U signAndSend() { diff --git a/nostr-java-api/src/main/java/nostr/api/NIP01.java b/nostr-java-api/src/main/java/nostr/api/NIP01.java index 5bdbe1aee..16591fe8f 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP01.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP01.java @@ -86,7 +86,7 @@ public NIP01 createTextNoteEvent(@NonNull List tags, @NonNull String public NIP01 createMetadataEvent(@NonNull UserProfile profile) { var sender = getSender(); var event = (sender != null) ? new MetadataEventFactory(sender, profile).create() - : new MetadataEventFactory(profile).create(); + : new MetadataEventFactory(profile).create(); this.setEvent((T) event); return this; @@ -259,7 +259,7 @@ public NIP01 createParameterizedReplaceableEvent(@NonNull Integer kind, Strin * @return */ public NIP01 createParameterizedReplaceableEvent(@NonNull List tags, @NonNull Integer kind, - String comment) { + String comment) { var event = new ParameterizedReplaceableEventFactory(getSender(), tags, kind, comment).create(); this.setEvent((T) event); @@ -284,7 +284,7 @@ public static IdentifierTag createIdentifierTag(@NonNull String id) { * @return */ public static AddressTag createAddressTag(@NonNull Integer kind, @NonNull PublicKey publicKey, - IdentifierTag idTag, Relay relay) { + IdentifierTag idTag, Relay relay) { var result = new AddressTagFactory(publicKey).create(); if(idTag != null) { result.setIdentifierTag(idTag); diff --git a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java index 1c4b25081..7e6a9c547 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/ReqMessage.java @@ -24,60 +24,60 @@ @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) public class ReqMessage extends BaseMessage { - @JsonProperty - private final String subscriptionId; + @JsonProperty + private final String subscriptionId; - @JsonProperty - private final List filtersList; + @JsonProperty + private final List filtersList; - public ReqMessage(@NonNull String subscriptionId, Filters... filtersList) { + public ReqMessage(@NonNull String subscriptionId, Filters... filtersList) { // TODO: complete logic for a list of filters - this(subscriptionId, List.of(filtersList)); - } + this(subscriptionId, List.of(filtersList)); + } - public ReqMessage(@NonNull String subscriptionId, List filtersList) { - super(Command.REQ.name()); - validateSubscriptionId(subscriptionId); - this.subscriptionId = subscriptionId; - this.filtersList = filtersList; - } + public ReqMessage(@NonNull String subscriptionId, List filtersList) { + super(Command.REQ.name()); + validateSubscriptionId(subscriptionId); + this.subscriptionId = subscriptionId; + this.filtersList = filtersList; + } - @Override - public String encode() throws JsonProcessingException { - getArrayNode() - .add(getCommand()) - .add(getSubscriptionId()); + @Override + public String encode() throws JsonProcessingException { + getArrayNode() + .add(getCommand()) + .add(getSubscriptionId()); - filtersList.stream() - .map(FiltersEncoder::new) - .map(FiltersEncoder::encode) - .map(ReqMessage::createJsonNode) - .forEach(jsonNode -> - getArrayNode().add(jsonNode)); + filtersList.stream() + .map(FiltersEncoder::new) + .map(FiltersEncoder::encode) + .map(ReqMessage::createJsonNode) + .forEach(jsonNode -> + getArrayNode().add(jsonNode)); - return IEncoder.MAPPER.writeValueAsString(getArrayNode()); - } + return IEncoder.MAPPER.writeValueAsString(getArrayNode()); + } - public static T decode(@NonNull Object subscriptionId, @NonNull List jsonFiltersList) { - validateSubscriptionId(subscriptionId.toString()); - ReqMessage reqMessage = new ReqMessage( - subscriptionId.toString(), - jsonFiltersList.stream().map(filtersList -> - new FiltersDecoder<>().decode(filtersList)).toList()); - return (T) reqMessage; - } + public static T decode(@NonNull Object subscriptionId, @NonNull List jsonFiltersList) { + validateSubscriptionId(subscriptionId.toString()); + ReqMessage reqMessage = new ReqMessage( + subscriptionId.toString(), + jsonFiltersList.stream().map(filtersList -> + new FiltersDecoder<>().decode(filtersList)).toList()); + return (T) reqMessage; + } - private static void validateSubscriptionId(String subscriptionId) { - if (!ValueRange.of(1, 64).isValidIntValue(subscriptionId.length())) { - throw new IllegalArgumentException(String.format("SubscriptionId length must be between 1 and 64 characters but was [%d]", subscriptionId.length())); + private static void validateSubscriptionId(String subscriptionId) { + if (!ValueRange.of(1, 64).isValidIntValue(subscriptionId.length())) { + throw new IllegalArgumentException(String.format("SubscriptionId length must be between 1 and 64 characters but was [%d]", subscriptionId.length())); + } } - } - private static JsonNode createJsonNode(String jsonNode) { - try { - return IEncoder.MAPPER.readTree(jsonNode); - } catch (JsonProcessingException e) { - throw new IllegalArgumentException(String.format("Malformed encoding ReqMessage json: [%s]", jsonNode), e); + private static JsonNode createJsonNode(String jsonNode) { + try { + return IEncoder.MAPPER.readTree(jsonNode); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException(String.format("Malformed encoding ReqMessage json: [%s]", jsonNode), e); + } } - } } diff --git a/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java b/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java index f33bbc732..f44849bbf 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java +++ b/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java @@ -44,8 +44,8 @@ public void deleteEvent() throws IOException { nip01.createTextNoteEvent("Delete me!").signAndSend(Map.of("local", RELAY_URI)); Filters filters = new Filters( - new KindFilter<>(Kind.TEXT_NOTE), - new AuthorFilter<>(identity.getPublicKey())); + new KindFilter<>(Kind.TEXT_NOTE), + new AuthorFilter<>(identity.getPublicKey())); List result = nip01.sendRequest(filters, UUID.randomUUID().toString()); @@ -79,9 +79,9 @@ public void deleteEventWithRef() throws IOException { NIP01 nip01 = new NIP01<>(identity); nip01 - .createTextNoteEvent("Reference me!") - .getEvent() - .addTag(nip01.createAddressTag(10_001, identity.getPublicKey(), identifierTag, new Relay(RELAY_URI))); + .createTextNoteEvent("Reference me!") + .getEvent() + .addTag(nip01.createAddressTag(10_001, identity.getPublicKey(), identifierTag, new Relay(RELAY_URI))); BaseMessage message = nip01.signAndSend(Map.of("local", RELAY_URI)); @@ -96,9 +96,9 @@ public void deleteEventWithRef() throws IOException { assertEquals(4, deletedEvent.getTags().size()); List eventTags = deletedEvent.getTags() - .stream() - .filter(t -> "e".equals(t.getCode())) - .collect(Collectors.toList()); + .stream() + .filter(t -> "e".equals(t.getCode())) + .collect(Collectors.toList()); assertEquals(1, eventTags.size()); @@ -106,9 +106,9 @@ public void deleteEventWithRef() throws IOException { assertEquals(event.getId(), eventTag.getIdEvent()); List addressTags = deletedEvent.getTags() - .stream() - .filter(t -> "a".equals(t.getCode())) - .collect(Collectors.toList()); + .stream() + .filter(t -> "a".equals(t.getCode())) + .collect(Collectors.toList()); assertEquals(1, addressTags.size()); @@ -118,9 +118,9 @@ public void deleteEventWithRef() throws IOException { assertEquals(identity.getPublicKey(), addressTag.getPublicKey()); List kindTags = deletedEvent.getTags() - .stream() - .filter(t -> "k".equals(t.getCode())) - .collect(Collectors.toList()); + .stream() + .filter(t -> "k".equals(t.getCode())) + .collect(Collectors.toList()); assertEquals(2, kindTags.size()); From 4e251ac7ac03f6b52e646ee3fb9e0b3031888ac0 Mon Sep 17 00:00:00 2001 From: nick avlo Date: Thu, 20 Feb 2025 17:47:28 -0800 Subject: [PATCH 43/45] refactored Filters ctors, map ctor now private --- .../nostr/api/NostrSpringWebSocketClient.java | 9 ++-- .../main/java/nostr/event/filter/Filters.java | 7 ++- .../event/json/codec/FiltersDecoder.java | 24 +++++----- .../test/filters/FiltersDecoderTest.java | 4 +- .../test/filters/FiltersEncoderTest.java | 45 ++----------------- 5 files changed, 28 insertions(+), 61 deletions(-) diff --git a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java index 7c7ab2e08..a2955c1d3 100644 --- a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java +++ b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java @@ -81,13 +81,14 @@ public List sendEvent(@NonNull IEvent event, Map relays) } @Override - public List sendRequest(@NonNull List filtersList, @NonNull String subscriptionId, Map relays) { - return sendRequest(filtersList, subscriptionId); + public List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId, Map relays) { + return sendRequest(List.of(filters), subscriptionId, relays); } @Override - public List sendRequest(@NonNull Filters filters, @NonNull String subscriptionId, Map relays) { - return sendRequest(filters, subscriptionId); + public List sendRequest(@NonNull List filtersList, @NonNull String subscriptionId, Map relays) { + setRelays(relays); + return sendRequest(filtersList, subscriptionId); } @Override diff --git a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java index e9fb55ef4..65e1a12e5 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/Filters.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/Filters.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; +import java.util.Objects; import static java.util.stream.Collectors.groupingBy; @@ -28,7 +29,7 @@ public Filters(@NonNull List filterablesByDefaultType) { this(filterablesByDefaultType.stream().collect(groupingBy(Filterable::getFilterKey))); } - public Filters(@NonNull Map> filterablesByCustomType) { + private Filters(@NonNull Map> filterablesByCustomType) { validateFiltersMap(filterablesByCustomType); this.filtersMap = filterablesByCustomType; } @@ -38,6 +39,10 @@ public List getFilterByType(@NonNull String type) { } private static void validateFiltersMap(Map> filtersMap) throws IllegalArgumentException { + if (filtersMap.isEmpty()) { + throw new IllegalArgumentException("Filters cannot be empty."); + } + filtersMap.values().forEach(filterables -> { if (filterables.isEmpty()) { throw new IllegalArgumentException("Filters cannot be empty."); diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java index c7f35bd9a..71659c7c5 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FiltersDecoder.java @@ -7,28 +7,26 @@ import nostr.event.filter.Filterable; import nostr.event.filter.Filters; -import java.util.HashMap; +import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * @author eric */ @Data public class FiltersDecoder implements FDecoder { - private final static ObjectMapper mapper = new ObjectMapper(); + private final static ObjectMapper mapper = new ObjectMapper(); - @SneakyThrows - public T decode(@NonNull String jsonFiltersList) { - final Map> filterPluginsMap = new HashMap<>(); + @SneakyThrows + public T decode(@NonNull String jsonFiltersList) { + final List filterables = new ArrayList<>(); - mapper.readTree(jsonFiltersList).fields().forEachRemaining(field -> - filterPluginsMap.put( + mapper.readTree(jsonFiltersList).fields().forEachRemaining(field -> + filterables.addAll( + FilterableProvider.getFilterable( field.getKey(), - FilterableProvider.getFilterable( - field.getKey(), - field.getValue()))); + field.getValue()))); - return (T) new Filters(filterPluginsMap); - } + return (T) new Filters(filterables); + } } diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java index b126dae73..85c8bdeca 100644 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java @@ -33,7 +33,7 @@ public class FiltersDecoderTest { public void testEventFiltersDecoder() { log.info("testEventFiltersDecoder"); - String filterKey = EventFilter.filterKey; + String filterKey = "ids"; String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String expected = "{\"" + filterKey + "\":[\"" + eventId + "\"]}"; @@ -49,7 +49,7 @@ public void testEventFiltersDecoder() { public void testMultipleEventFiltersDecoder() { log.info("testMultipleEventFiltersDecoder"); - String filterKey = EventFilter.filterKey; + String filterKey = "ids"; String eventId1 = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; String eventId2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java index 4b0fe716b..3331c8106 100644 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java +++ b/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java @@ -26,9 +26,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.List; -import java.util.Map; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -67,15 +65,9 @@ public void testEventFilterEncoderUsingList() { public void testEventFilterEncoderByMap() { log.info("testEventFilterEncoderByMap"); - Map> expectedFilters = new HashMap<>(); - - String filterKey = EventFilter.filterKey; String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; - expectedFilters.put(filterKey, - List.of( - new EventFilter<>(new GenericEvent(eventId)))); - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + FiltersEncoder encoder = new FiltersEncoder(new Filters(new EventFilter<>(new GenericEvent(eventId)))); String encodedFilters = encoder.encode(); assertEquals("{\"ids\":[\"" + eventId + "\"]}", encodedFilters); } @@ -190,12 +182,7 @@ public void testReferencedEventFilterEncoder() { String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75"; - Map> expectedFilters = new HashMap<>(); - expectedFilters.put(ReferencedEventFilter.filterKey, - List.of( - new ReferencedEventFilter<>(new GenericEvent(eventId)))); - - FiltersEncoder encoder = new FiltersEncoder(new Filters(expectedFilters)); + FiltersEncoder encoder = new FiltersEncoder(new Filters(new ReferencedEventFilter<>(new GenericEvent(eventId)))); String encodedFilters = encoder.encode(); assertEquals("{\"#e\":[\"" + eventId + "\"]}", encodedFilters); } @@ -328,28 +315,16 @@ public void testUntilFiltersEncoder() { assertEquals("{\"until\":" + until + "}", encodedFilters); } - @Test - public void testReqMessageEmptyFilterMap() { - log.info("testReqMessageEmptyFilterMap"); - String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; - Map> emptyFilters = new HashMap<>(); - emptyFilters.put("", List.of()); - - assertThrows(IllegalArgumentException.class, () -> new ReqMessage(subscriptionId, new Filters(emptyFilters))); - } - @Test public void testReqMessageEmptyFilters() { log.info("testReqMessageEmptyFilters"); String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; - Map> emptyFilters = new HashMap<>(); - emptyFilters.put("#g", List.of()); - assertThrows(IllegalArgumentException.class, () -> new ReqMessage(subscriptionId, new Filters(emptyFilters))); + assertThrows(IllegalArgumentException.class, () -> new ReqMessage(subscriptionId, new Filters(List.of()))); } @Test - public void testReqMessageEmptyFilterKey() { + public void testReqMessageCustomGenericTagFilter() { log.info("testReqMessageEmptyFilterKey"); String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; @@ -357,16 +332,4 @@ public void testReqMessageEmptyFilterKey() { new ReqMessage(subscriptionId, new Filters( new GenericTagQueryFilter<>(new GenericTagQuery("some-tag", "some-value"))))); } - - @Test - public void testReqMessageEmptyFilterKeyAsMap() { - log.info("testReqMessageEmptyFilterKey"); - String subscriptionId = "npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9"; - Map> emptyFilterKey = new HashMap<>(); - emptyFilterKey.put("", - List.of( - new GenericTagQueryFilter<>(new GenericTagQuery("some-tag", "some-value")))); - - assertThrows(IllegalArgumentException.class, () -> new ReqMessage(subscriptionId, new Filters(emptyFilterKey))); - } } From d536c5c56b5bdeb10e74ba9a058928b739979f92 Mon Sep 17 00:00:00 2001 From: erict875 Date: Sun, 23 Feb 2025 09:55:25 +0000 Subject: [PATCH 44/45] Removed unused context modules --- nostr-java-context-interface/pom.xml | 22 ---------- .../src/main/java/module-info.java | 4 -- .../java/nostr/context/CommandContext.java | 4 -- .../src/main/java/nostr/context/Context.java | 11 ----- .../java/nostr/context/RequestContext.java | 4 -- nostr-java-context/pom.xml | 42 ------------------- .../src/main/java/module-info.java | 9 ---- .../context/impl/DefaultCommandContext.java | 21 ---------- .../context/impl/DefaultRequestContext.java | 39 ----------------- 9 files changed, 156 deletions(-) delete mode 100644 nostr-java-context-interface/pom.xml delete mode 100644 nostr-java-context-interface/src/main/java/module-info.java delete mode 100644 nostr-java-context-interface/src/main/java/nostr/context/CommandContext.java delete mode 100644 nostr-java-context-interface/src/main/java/nostr/context/Context.java delete mode 100644 nostr-java-context-interface/src/main/java/nostr/context/RequestContext.java delete mode 100644 nostr-java-context/pom.xml delete mode 100644 nostr-java-context/src/main/java/module-info.java delete mode 100644 nostr-java-context/src/main/java/nostr/context/impl/DefaultCommandContext.java delete mode 100644 nostr-java-context/src/main/java/nostr/context/impl/DefaultRequestContext.java diff --git a/nostr-java-context-interface/pom.xml b/nostr-java-context-interface/pom.xml deleted file mode 100644 index f4393d987..000000000 --- a/nostr-java-context-interface/pom.xml +++ /dev/null @@ -1,22 +0,0 @@ - - 4.0.0 - - xyz.tcheeric - nostr-java - 0.6.3-SNAPSHOT - - - nostr-java-context-interface - jar - - nostr-java-context-interface - http://maven.apache.org - - - UTF-8 - - - - - diff --git a/nostr-java-context-interface/src/main/java/module-info.java b/nostr-java-context-interface/src/main/java/module-info.java deleted file mode 100644 index 739d96fc7..000000000 --- a/nostr-java-context-interface/src/main/java/module-info.java +++ /dev/null @@ -1,4 +0,0 @@ -module nostr.context { - - exports nostr.context; -} \ No newline at end of file diff --git a/nostr-java-context-interface/src/main/java/nostr/context/CommandContext.java b/nostr-java-context-interface/src/main/java/nostr/context/CommandContext.java deleted file mode 100644 index 0a6413171..000000000 --- a/nostr-java-context-interface/src/main/java/nostr/context/CommandContext.java +++ /dev/null @@ -1,4 +0,0 @@ -package nostr.context; - -public interface CommandContext extends Context { -} diff --git a/nostr-java-context-interface/src/main/java/nostr/context/Context.java b/nostr-java-context-interface/src/main/java/nostr/context/Context.java deleted file mode 100644 index 66ca9e9b6..000000000 --- a/nostr-java-context-interface/src/main/java/nostr/context/Context.java +++ /dev/null @@ -1,11 +0,0 @@ -package nostr.context; - -public interface Context { - - void validate(); - - enum Type { - REQUEST, - COMMAND - } -} diff --git a/nostr-java-context-interface/src/main/java/nostr/context/RequestContext.java b/nostr-java-context-interface/src/main/java/nostr/context/RequestContext.java deleted file mode 100644 index 9b3aac647..000000000 --- a/nostr-java-context-interface/src/main/java/nostr/context/RequestContext.java +++ /dev/null @@ -1,4 +0,0 @@ -package nostr.context; - -public interface RequestContext extends Context { -} diff --git a/nostr-java-context/pom.xml b/nostr-java-context/pom.xml deleted file mode 100644 index 6c30861e0..000000000 --- a/nostr-java-context/pom.xml +++ /dev/null @@ -1,42 +0,0 @@ - - 4.0.0 - - xyz.tcheeric - nostr-java - 0.6.3-SNAPSHOT - - - nostr-java-context - jar - - nostr-java-context - http://maven.apache.org - - - UTF-8 - - - - - org.projectlombok - lombok - - - ${project.groupId} - nostr-java-event - ${project.version} - - - ${project.groupId} - nostr-java-base - ${project.version} - - - ${project.groupId} - nostr-java-context-interface - ${project.version} - compile - - - diff --git a/nostr-java-context/src/main/java/module-info.java b/nostr-java-context/src/main/java/module-info.java deleted file mode 100644 index d8d637eb1..000000000 --- a/nostr-java-context/src/main/java/module-info.java +++ /dev/null @@ -1,9 +0,0 @@ -module nostr.context.impl { - requires lombok; - - requires nostr.context; - requires nostr.event; - requires nostr.base; - - exports nostr.context.impl; -} \ No newline at end of file diff --git a/nostr-java-context/src/main/java/nostr/context/impl/DefaultCommandContext.java b/nostr-java-context/src/main/java/nostr/context/impl/DefaultCommandContext.java deleted file mode 100644 index 8949f63b8..000000000 --- a/nostr-java-context/src/main/java/nostr/context/impl/DefaultCommandContext.java +++ /dev/null @@ -1,21 +0,0 @@ -package nostr.context.impl; - -import lombok.Data; -import lombok.NoArgsConstructor; -import nostr.base.Relay; -import nostr.context.CommandContext; -import nostr.event.BaseMessage; - -@Data -@NoArgsConstructor -public class DefaultCommandContext implements CommandContext { - private BaseMessage message; - private String challenge; - private Relay relay; - private byte[] privateKey; - - @Override - public void validate() { - - } -} diff --git a/nostr-java-context/src/main/java/nostr/context/impl/DefaultRequestContext.java b/nostr-java-context/src/main/java/nostr/context/impl/DefaultRequestContext.java deleted file mode 100644 index 5f0a4b0f0..000000000 --- a/nostr-java-context/src/main/java/nostr/context/impl/DefaultRequestContext.java +++ /dev/null @@ -1,39 +0,0 @@ -package nostr.context.impl; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.NonNull; -import nostr.base.Relay; -import nostr.context.RequestContext; - -import java.util.HashMap; -import java.util.Map; - -@Data -@NoArgsConstructor -public class DefaultRequestContext implements RequestContext { - private byte[] privateKey; - private String subscriptionId; - private Map relays; - private Map challenges = new HashMap<>(); - - @Override - public void validate() { - } - - public String getChallenge(@NonNull Relay relay) { - return challenges.get(relay); - } - - public String getChallenge(String relay) { - return getChallenge(new Relay(relay)); - } - - public void setChallenge(@NonNull Relay relay, @NonNull String challenge) { - challenges.put(relay, challenge); - } - - public void setChallenge(String relay, String challenge) { - setChallenge(new Relay(relay), challenge); - } -} \ No newline at end of file From dd2f62b10e8eaa6c51aeebfc4ffcda8faa985cbf Mon Sep 17 00:00:00 2001 From: erict875 Date: Sun, 23 Feb 2025 09:55:49 +0000 Subject: [PATCH 45/45] 0.6.4-SNAPSHOT --- nostr-java-api/pom.xml | 2 +- nostr-java-base/pom.xml | 2 +- nostr-java-client/pom.xml | 2 +- nostr-java-crypto/pom.xml | 2 +- nostr-java-encryption-nip04/pom.xml | 2 +- nostr-java-encryption-nip44/pom.xml | 2 +- nostr-java-encryption/pom.xml | 2 +- nostr-java-event/pom.xml | 2 +- nostr-java-examples/pom.xml | 2 +- nostr-java-id/pom.xml | 2 +- nostr-java-test/pom.xml | 2 +- nostr-java-util/pom.xml | 2 +- pom.xml | 4 +--- 13 files changed, 13 insertions(+), 15 deletions(-) diff --git a/nostr-java-api/pom.xml b/nostr-java-api/pom.xml index 8976dae99..c324dde9f 100644 --- a/nostr-java-api/pom.xml +++ b/nostr-java-api/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT nostr-java-api jar diff --git a/nostr-java-base/pom.xml b/nostr-java-base/pom.xml index 6ff5f6659..7a1f2e4b6 100644 --- a/nostr-java-base/pom.xml +++ b/nostr-java-base/pom.xml @@ -6,7 +6,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT nostr-java-base diff --git a/nostr-java-client/pom.xml b/nostr-java-client/pom.xml index a605b06da..4aa6500ba 100644 --- a/nostr-java-client/pom.xml +++ b/nostr-java-client/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT nostr-java-client jar diff --git a/nostr-java-crypto/pom.xml b/nostr-java-crypto/pom.xml index ffc0034a1..ce4557a33 100644 --- a/nostr-java-crypto/pom.xml +++ b/nostr-java-crypto/pom.xml @@ -6,7 +6,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT nostr-java-crypto diff --git a/nostr-java-encryption-nip04/pom.xml b/nostr-java-encryption-nip04/pom.xml index c2dcce47a..ced97d6bf 100644 --- a/nostr-java-encryption-nip04/pom.xml +++ b/nostr-java-encryption-nip04/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT nostr-java-encryption-nip04 diff --git a/nostr-java-encryption-nip44/pom.xml b/nostr-java-encryption-nip44/pom.xml index aa83e53f4..bb95d4948 100644 --- a/nostr-java-encryption-nip44/pom.xml +++ b/nostr-java-encryption-nip44/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT nostr-java-encryption-nip44 diff --git a/nostr-java-encryption/pom.xml b/nostr-java-encryption/pom.xml index a236f3f18..169f58443 100644 --- a/nostr-java-encryption/pom.xml +++ b/nostr-java-encryption/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT nostr-java-encryption diff --git a/nostr-java-event/pom.xml b/nostr-java-event/pom.xml index 6e8893ef4..8a7b7c9c5 100644 --- a/nostr-java-event/pom.xml +++ b/nostr-java-event/pom.xml @@ -6,7 +6,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT nostr-java-event diff --git a/nostr-java-examples/pom.xml b/nostr-java-examples/pom.xml index a3a9b808f..ad1dfcf4d 100644 --- a/nostr-java-examples/pom.xml +++ b/nostr-java-examples/pom.xml @@ -6,7 +6,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT nostr-java-examples diff --git a/nostr-java-id/pom.xml b/nostr-java-id/pom.xml index f31c7bd08..3b9796f47 100644 --- a/nostr-java-id/pom.xml +++ b/nostr-java-id/pom.xml @@ -6,7 +6,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT nostr-java-id diff --git a/nostr-java-test/pom.xml b/nostr-java-test/pom.xml index c6f872c2a..25ff07bd8 100644 --- a/nostr-java-test/pom.xml +++ b/nostr-java-test/pom.xml @@ -6,7 +6,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT nostr-java-test diff --git a/nostr-java-util/pom.xml b/nostr-java-util/pom.xml index 96b8fe48e..055036fd6 100644 --- a/nostr-java-util/pom.xml +++ b/nostr-java-util/pom.xml @@ -6,7 +6,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT nostr-java-util diff --git a/pom.xml b/pom.xml index e4e6e6d44..6db953756 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ xyz.tcheeric nostr-java - 0.6.3-SNAPSHOT + 0.6.4-SNAPSHOT pom ${project.artifactId} @@ -73,8 +73,6 @@ nostr-java-encryption nostr-java-encryption-nip04 nostr-java-encryption-nip44 - nostr-java-context - nostr-java-context-interface