Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

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;
import lombok.SneakyThrows;
import nostr.base.IDecoder;
import nostr.event.BaseMessage;
import nostr.event.impl.GenericMessage;
Expand All @@ -19,12 +19,16 @@

import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;

/**
* @author eric
*/
public class BaseMessageDecoder<T extends BaseMessage> implements IDecoder<T> {
public static final int MAX_JSON_NODE_THRESHOLD = 99;
public static final int COMMAND_INDEX = 0;
public static final int ARG_INDEX = 1;
public static final int FILTERS_START_INDEX = 2;

private final ObjectMapper mapper = new ObjectMapper();

public BaseMessageDecoder() {
Expand All @@ -33,9 +37,9 @@ public BaseMessageDecoder() {

@Override
public T decode(@NonNull String jsonString) throws JsonProcessingException {
ValidJsonNodeFirstPair validJsonNodeFirstPair = jsonFirstPair(jsonString);
ValidJsonNodeFirstPair validJsonNodeFirstPair = json_strCmd_arg(jsonString);
String command = validJsonNodeFirstPair.formerly_strCmd();
Object subscriptionId = validJsonNodeFirstPair.formerly_arg(); // subscriptionId
Object subscriptionId = validJsonNodeFirstPair.formerly_arg();

Object[] msgArr = mapper.readValue(jsonString, Object[].class); // TODO: replace with jsonNode after ReqMessage.decode() is finished

Expand All @@ -48,35 +52,28 @@ 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" -> {
String filtersJson = jsonSecondPair(jsonString).formerly_msgArr(); // filters
yield ReqMessage.decode(subscriptionId, List.of(filtersJson));
}
case "REQ" -> ReqMessage.decode(subscriptionId, json_msgArr(jsonString));
default -> GenericMessage.decode(msgArr);
};
}

private ValidJsonNodeFirstPair jsonFirstPair(@NonNull String jsonString) throws JsonProcessingException {
final JsonNode jsonNode = mapper.readTree(jsonString);

private ValidJsonNodeFirstPair json_strCmd_arg(@NonNull String jsonString) throws JsonProcessingException {
return new ValidJsonNodeFirstPair(
jsonNode.get(0).asText(),
jsonNode.get(1).asText());
mapper.readTree(jsonString).get(COMMAND_INDEX).asText(),
mapper.readTree(jsonString).get(ARG_INDEX).asText());
}

private ValidJsonNodeSecondPair jsonSecondPair(@NonNull String jsonString) throws JsonProcessingException {
final JsonNode jsonNode = mapper.readTree(jsonString);
private List<String> json_msgArr(@NonNull String jsonString) throws JsonProcessingException {
return IntStream.range(FILTERS_START_INDEX, mapper.readTree(jsonString).size())
.mapToObj(idx -> readTree(jsonString, idx)).toList();
}

return new ValidJsonNodeSecondPair(
jsonNode.get(2).toString());
@SneakyThrows
private String readTree(String jsonString, int idx) {
return mapper.readTree(jsonString).get(idx).toString();
}

private record ValidJsonNodeFirstPair(
@NonNull String formerly_strCmd,
@NonNull Object formerly_arg) {
}

private record ValidJsonNodeSecondPair(
@NonNull String formerly_msgArr) {
}
@NonNull Object formerly_arg) {}
}
98 changes: 98 additions & 0 deletions nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,104 @@ public void testNIP01SendTextNoteEventCustomGenericTag() throws IOException {
nip01.close();
}

@Test
public void testFiltersListReturnSameSingularEvent() throws IOException {
System.out.println("testFiltersListReturnSameSingularEvent");

Identity identity = Identity.generateRandomIdentity();

String geoHashTagTarget = "geohash_tag-location_SameSingularEvent";
GeohashTag geohashTag = new GeohashTag(geoHashTagTarget);

String genericTagTarget = "generic-tag-value_SameSingularEvent";
GenericTag genericTag = GenericTag.create("m", 1, genericTagTarget);

NIP01<NIP01Event> nip01 = new NIP01<>(identity);

nip01.createTextNoteEvent(List.of(geohashTag, genericTag), "Multiple Filters").signAndSend(Map.of("local", "ws://localhost:5555"));

Filters filters1 = new Filters(
new GeohashTagFilter<>(new GeohashTag(geoHashTagTarget)));
Filters filters2 = new Filters(
new GenericTagQueryFilter<>(new GenericTagQuery("#m", genericTagTarget)));

List<String> result = nip01.sendRequest(List.of(filters1, filters2), UUID.randomUUID().toString());

assertFalse(result.isEmpty());
assertEquals(2, result.size());
assertTrue(result.stream().anyMatch(s -> s.contains(geoHashTagTarget)));

nip01.close();
}

@Test
public void testFiltersListReturnTwoDifferentEvents() throws IOException {
System.out.println("testFiltersListReturnTwoDifferentEvents");

// first event
Identity identity1 = Identity.generateRandomIdentity();
String geoHashTagTarget1 = "geohash_tag-location-1";
GeohashTag geohashTag1 = new GeohashTag(geoHashTagTarget1);
String genericTagTarget1 = "generic-tag-value-1";
GenericTag genericTag1 = GenericTag.create("m", 1, genericTagTarget1);
NIP01<NIP01Event> nip01_1 = new NIP01<>(identity1);
nip01_1.createTextNoteEvent(List.of(geohashTag1, genericTag1), "Multiple Filters 1").signAndSend(Map.of("local", "ws://localhost:5555"));

// second event
Identity identity2 = Identity.generateRandomIdentity();
String geoHashTagTarget2 = "geohash_tag-location-2";
GeohashTag geohashTag2 = new GeohashTag(geoHashTagTarget2);
String genericTagTarget2 = "generic-tag-value-2";
GenericTag genericTag2 = GenericTag.create("m", 1, genericTagTarget2);
NIP01<NIP01Event> nip01_2 = new NIP01<>(identity2);
nip01_2.createTextNoteEvent(List.of(geohashTag2, genericTag2), "Multiple Filters 2").signAndSend(Map.of("local", "ws://localhost:5555"));

Filters filters1 = new Filters(
new GeohashTagFilter<>(new GeohashTag(geoHashTagTarget1))); // 1st filter should find match in 1st event

Filters filters2 = new Filters(
new GenericTagQueryFilter<>(new GenericTagQuery("#m", genericTagTarget2))); // 2nd filter should find match in 2nd event

List<String> result = nip01_1.sendRequest(List.of(filters1, filters2), UUID.randomUUID().toString());

assertFalse(result.isEmpty());
assertEquals(3, result.size());
assertTrue(result.stream().anyMatch(s -> s.contains(geoHashTagTarget1)));
assertTrue(result.stream().anyMatch(s -> s.contains(genericTagTarget2)));

nip01_1.close();
nip01_2.close();
}

@Test
public void testMultipleFiltersDifferentTypesReturnSameEvent() throws IOException {
System.out.println("testMultipleFilters");

Identity identity = Identity.generateRandomIdentity();

String geoHashTagTarget = "geohash_tag-location-DifferentTypesReturnSameEvent";
GeohashTag geohashTag = new GeohashTag(geoHashTagTarget);

String genericTagTarget = "generic-tag-value-DifferentTypesReturnSameEvent";
GenericTag genericTag = GenericTag.create("m", 1, genericTagTarget);

NIP01<NIP01Event> nip01 = new NIP01<>(identity);

nip01.createTextNoteEvent(List.of(geohashTag, genericTag), "Multiple Filters").signAndSend(Map.of("local", "ws://localhost:5555"));

Filters filters = new Filters(
new GeohashTagFilter<>(new GeohashTag(geoHashTagTarget)),
new GenericTagQueryFilter<>(new GenericTagQuery("#m", genericTagTarget)));

List<String> result = nip01.sendRequest(filters, UUID.randomUUID().toString());

assertFalse(result.isEmpty());
assertEquals(2, result.size());
assertTrue(result.stream().anyMatch(s -> s.contains(geoHashTagTarget)));

nip01.close();
}

@Test
public void testNIP04EncryptDecrypt() {
System.out.println("testNIP04EncryptDecrypt");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
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.GeohashTagFilter;
Expand All @@ -30,7 +29,6 @@
import org.junit.jupiter.api.Test;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

Expand All @@ -42,8 +40,8 @@
public class FiltersEncoderTest {

@Test
public void testEventFilterEncoderUsingVarArgs() {
log.info("testEventFilterEncoderUsingVarArgs");
public void testEventFilterEncoder() {
log.info("testEventFilterEncoder");

String eventId = "f1b419a95cb0233a11d431423b41a42734e7165fcab16081cd08ef1c90e0be75";

Expand All @@ -53,20 +51,6 @@ public void testEventFilterEncoderUsingVarArgs() {
assertEquals("{\"ids\":[\"" + eventId + "\"]}", encodedFilters);
}

@Test
public void testEventFilterEncoderUsingList() {
log.info("testEventFilterEncoderUsingList");

List<Filterable> 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 testMultipleEventFilterEncoder() {
log.info("testMultipleEventFilterEncoder");
Expand Down
41 changes: 40 additions & 1 deletion nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -717,5 +717,44 @@ public void testReqMessageSubscriptionIdTooShort() {

assertThrows(IllegalArgumentException.class, () -> new BaseMessageDecoder<>().decode(parseTarget));
}
}

@Test
public void testBaseEventMessageDecoderMultipleFiltersJson() throws JsonProcessingException {
log.info("testBaseEventMessageDecoderMultipleFiltersJson");

final String eventJson
= "[\"EVENT\","
+ "{"
+ "\"content\":\"直ん直んないわ。まあええか\","
+ "\"created_at\":1786199583,"
+ "\"id\":\"ec7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\","
+ "\"kind\":1,"
+ "\"pubkey\":\"9c59239319637f97e007dad0d681e65ce35b1ace333b629e2d33f9465c132608\","
+ "\"sig\":\"9584afd231c52fcbcec6ce668a2cc4b6dc9b4d9da20510dcb9005c6844679b4844edb7a2e1e0591958b0295241567c774dbf7d39a73932877542de1a5f963f4b\","
+ "\"tags\":[]"
+ "}]";

final var eventMessage = new BaseMessageDecoder<>().decode(eventJson);

assertEquals(Command.EVENT.toString(), eventMessage.getCommand());

final var event = (GenericEvent) (((EventMessage) eventMessage).getEvent());
assertEquals(1, event.getKind().intValue());
assertEquals(1786199583, event.getCreatedAt().longValue());
assertEquals("ec7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", event.getId());

String subscriptionId = "npub27x6pn22ukq3n5yw5x9prksdyyu6ww9jle2ckpqwdprh3ey8qhe6stnpujh";
final String requestJson =
"[\"REQ\", " +
"\"" + subscriptionId + "\", " +
"{\"kinds\": [1], \"authors\": [\"9c59239319637f97e007dad0d681e65ce35b1ace333b629e2d33f9465c132608\"]}," + // first filter set
"{\"kinds\": [1], \"#p\": [\"ec7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712\"]}" + // second filter set
"]";

final var message = new BaseMessageDecoder<>().decode(requestJson);

assertEquals(Command.REQ.toString(), message.getCommand());
assertEquals(subscriptionId, ((ReqMessage) message).getSubscriptionId());
assertEquals(2, ((ReqMessage) message).getFiltersList().size());
}
}