From 0eee845d518a20f9d814f31778cbbe729c112716 Mon Sep 17 00:00:00 2001 From: Eric T Date: Fri, 1 Aug 2025 07:03:31 +0100 Subject: [PATCH 1/2] Refactor event sending with service --- .../java/nostr/api/DefaultNoteService.java | 21 +++++++++ .../nostr/api/NostrSpringWebSocketClient.java | 22 +++++++++- .../src/main/java/nostr/api/NoteService.java | 11 +++++ ...gWebSocketClientEventVerificationTest.java | 43 +++++++++++++++++++ 4 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 nostr-java-api/src/main/java/nostr/api/DefaultNoteService.java create mode 100644 nostr-java-api/src/main/java/nostr/api/NoteService.java create mode 100644 nostr-java-api/src/test/java/nostr/api/unit/NostrSpringWebSocketClientEventVerificationTest.java diff --git a/nostr-java-api/src/main/java/nostr/api/DefaultNoteService.java b/nostr-java-api/src/main/java/nostr/api/DefaultNoteService.java new file mode 100644 index 000000000..89538eb6e --- /dev/null +++ b/nostr-java-api/src/main/java/nostr/api/DefaultNoteService.java @@ -0,0 +1,21 @@ +package nostr.api; + +import lombok.NonNull; +import nostr.base.IEvent; + +import java.util.List; +import java.util.Map; + +/** + * Default implementation that dispatches notes through all WebSocket clients. + */ +public class DefaultNoteService implements NoteService { + @Override + public List send(@NonNull IEvent event, @NonNull Map clients) { + return clients.values().stream() + .map(client -> client.sendEvent(event)) + .flatMap(List::stream) + .distinct() + .toList(); + } +} 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 3c12f7ef2..9ea46525c 100644 --- a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java +++ b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java @@ -12,6 +12,8 @@ import nostr.event.message.ReqMessage; import nostr.id.Identity; import nostr.util.NostrUtil; +import nostr.api.DefaultNoteService; +import nostr.api.NoteService; import java.io.IOException; import java.util.HashMap; @@ -27,12 +29,23 @@ public class NostrSpringWebSocketClient implements NostrIF { @Getter private Identity sender; + private NoteService noteService = new DefaultNoteService(); + private static volatile NostrSpringWebSocketClient INSTANCE; public NostrSpringWebSocketClient(String relayName, String relayUri) { setRelays(Map.of(relayName, relayUri)); } + public NostrSpringWebSocketClient(@NonNull NoteService noteService) { + this.noteService = noteService; + } + + public NostrSpringWebSocketClient(@NonNull Identity sender, @NonNull NoteService noteService) { + this.sender = sender; + this.noteService = noteService; + } + public static NostrIF getInstance() { if (INSTANCE == null) { synchronized (NostrSpringWebSocketClient.class) { @@ -78,8 +91,13 @@ public NostrIF setRelays(@NonNull Map relays) { @Override public List sendEvent(@NonNull IEvent event) { - return clientMap.values().stream().map(client -> - client.sendEvent(event)).flatMap(List::stream).distinct().toList(); + if (event instanceof GenericEvent genericEvent) { + if (!verify(genericEvent)) { + throw new IllegalStateException("Event verification failed"); + } + } + + return noteService.send(event, clientMap); } @Override diff --git a/nostr-java-api/src/main/java/nostr/api/NoteService.java b/nostr-java-api/src/main/java/nostr/api/NoteService.java new file mode 100644 index 000000000..f1d4d83e7 --- /dev/null +++ b/nostr-java-api/src/main/java/nostr/api/NoteService.java @@ -0,0 +1,11 @@ +package nostr.api; + +import lombok.NonNull; +import nostr.base.IEvent; + +import java.util.List; +import java.util.Map; + +public interface NoteService { + List send(@NonNull IEvent event, @NonNull Map clients); +} diff --git a/nostr-java-api/src/test/java/nostr/api/unit/NostrSpringWebSocketClientEventVerificationTest.java b/nostr-java-api/src/test/java/nostr/api/unit/NostrSpringWebSocketClientEventVerificationTest.java new file mode 100644 index 000000000..00d8ed416 --- /dev/null +++ b/nostr-java-api/src/test/java/nostr/api/unit/NostrSpringWebSocketClientEventVerificationTest.java @@ -0,0 +1,43 @@ +package nostr.api.unit; + +import nostr.api.NostrSpringWebSocketClient; +import nostr.api.NoteService; +import org.mockito.Mockito; +import nostr.config.Constants; +import nostr.event.impl.GenericEvent; +import nostr.id.Identity; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class NostrSpringWebSocketClientEventVerificationTest { + + @Test + void sendEventThrowsWhenUnsigned() { + GenericEvent event = new GenericEvent(); + event.setPubKey(Identity.generateRandomIdentity().getPublicKey()); + event.setKind(Constants.Kind.SHORT_TEXT_NOTE); + event.setContent("test"); + + NoteService service = Mockito.mock(NoteService.class); + Mockito.when(service.send(Mockito.any(), Mockito.any())).thenReturn(List.of()); + NostrSpringWebSocketClient client = new NostrSpringWebSocketClient(service); + assertThrows(IllegalStateException.class, () -> client.sendEvent(event)); + } + + @Test + void sendEventReturnsEmptyListWhenSigned() { + Identity identity = Identity.generateRandomIdentity(); + GenericEvent event = new GenericEvent(identity.getPublicKey(), Constants.Kind.SHORT_TEXT_NOTE); + event.setContent("signed"); + identity.sign(event); + + NoteService service = Mockito.mock(NoteService.class); + Mockito.when(service.send(Mockito.any(), Mockito.any())).thenReturn(List.of()); + NostrSpringWebSocketClient client = new NostrSpringWebSocketClient(service); + List responses = client.sendEvent(event); + assertTrue(responses.isEmpty()); + } +} From c8147aeaf1c9cca4876c3e44d682283be4985f4d Mon Sep 17 00:00:00 2001 From: erict875 Date: Fri, 1 Aug 2025 07:13:36 +0100 Subject: [PATCH 2/2] refactor: reorganize service package structure and update imports --- .../src/main/java/nostr/api/NostrSpringWebSocketClient.java | 4 ++-- .../src/main/java/nostr/api/WebSocketClientHandler.java | 2 +- .../src/main/java/nostr/api/{ => service}/NoteService.java | 3 ++- .../java/nostr/api/{ => service/impl}/DefaultNoteService.java | 4 +++- .../unit/NostrSpringWebSocketClientEventVerificationTest.java | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) rename nostr-java-api/src/main/java/nostr/api/{ => service}/NoteService.java (77%) rename nostr-java-api/src/main/java/nostr/api/{ => service/impl}/DefaultNoteService.java (83%) 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 9ea46525c..9301a9373 100644 --- a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java +++ b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java @@ -3,6 +3,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.NonNull; +import nostr.api.service.NoteService; import nostr.base.IEvent; import nostr.base.ISignable; import nostr.client.springwebsocket.SpringWebSocketClient; @@ -12,8 +13,7 @@ import nostr.event.message.ReqMessage; import nostr.id.Identity; import nostr.util.NostrUtil; -import nostr.api.DefaultNoteService; -import nostr.api.NoteService; +import nostr.api.service.impl.DefaultNoteService; import java.io.IOException; import java.util.HashMap; 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 512f2a179..904e0af3b 100644 --- a/nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java +++ b/nostr-java-api/src/main/java/nostr/api/WebSocketClientHandler.java @@ -31,7 +31,7 @@ protected WebSocketClientHandler(@NonNull String relayName, @NonNull String rela this.eventClient = new SpringWebSocketClient(relayUri); } - protected List sendEvent(@NonNull IEvent event) { + public List sendEvent(@NonNull IEvent event) { ((GenericEvent) event).validate(); return eventClient.send(new EventMessage(event)).stream().toList(); } diff --git a/nostr-java-api/src/main/java/nostr/api/NoteService.java b/nostr-java-api/src/main/java/nostr/api/service/NoteService.java similarity index 77% rename from nostr-java-api/src/main/java/nostr/api/NoteService.java rename to nostr-java-api/src/main/java/nostr/api/service/NoteService.java index f1d4d83e7..3f3deb9f9 100644 --- a/nostr-java-api/src/main/java/nostr/api/NoteService.java +++ b/nostr-java-api/src/main/java/nostr/api/service/NoteService.java @@ -1,6 +1,7 @@ -package nostr.api; +package nostr.api.service; import lombok.NonNull; +import nostr.api.WebSocketClientHandler; import nostr.base.IEvent; import java.util.List; diff --git a/nostr-java-api/src/main/java/nostr/api/DefaultNoteService.java b/nostr-java-api/src/main/java/nostr/api/service/impl/DefaultNoteService.java similarity index 83% rename from nostr-java-api/src/main/java/nostr/api/DefaultNoteService.java rename to nostr-java-api/src/main/java/nostr/api/service/impl/DefaultNoteService.java index 89538eb6e..ddda0ff7c 100644 --- a/nostr-java-api/src/main/java/nostr/api/DefaultNoteService.java +++ b/nostr-java-api/src/main/java/nostr/api/service/impl/DefaultNoteService.java @@ -1,6 +1,8 @@ -package nostr.api; +package nostr.api.service.impl; import lombok.NonNull; +import nostr.api.service.NoteService; +import nostr.api.WebSocketClientHandler; import nostr.base.IEvent; import java.util.List; diff --git a/nostr-java-api/src/test/java/nostr/api/unit/NostrSpringWebSocketClientEventVerificationTest.java b/nostr-java-api/src/test/java/nostr/api/unit/NostrSpringWebSocketClientEventVerificationTest.java index 00d8ed416..d32d39bf4 100644 --- a/nostr-java-api/src/test/java/nostr/api/unit/NostrSpringWebSocketClientEventVerificationTest.java +++ b/nostr-java-api/src/test/java/nostr/api/unit/NostrSpringWebSocketClientEventVerificationTest.java @@ -1,7 +1,7 @@ package nostr.api.unit; import nostr.api.NostrSpringWebSocketClient; -import nostr.api.NoteService; +import nostr.api.service.NoteService; import org.mockito.Mockito; import nostr.config.Constants; import nostr.event.impl.GenericEvent;