diff --git a/.gitignore b/.gitignore index 9298972c8..b32ca7f82 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ - # Created by https://www.gitignore.io/api/java,eclipse,java-web,code-java,intellij+all,visualstudiocode # Edit at https://www.gitignore.io/?templates=java,eclipse,java-web,code-java,intellij+all,visualstudiocode @@ -212,6 +211,14 @@ hs_err_pid* ## ignoring target file target/ +# Gradle files +.gradle/ +build/ +gradle/ +gradlew +gradlew.bat +buildSrc/build + ### VisualStudioCode ### .vscode .vscode/* diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index c1dd12f17..000000000 Binary files a/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 000000000..1ee59078a --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,36 @@ +/* +gradle project-build tools configuration file + +nostr-java specific configs can be found in: + buildSrc/src/main/groovy/nostr-java.conventions.gradle +*/ + +plugins { + id 'java' + id 'groovy-gradle-plugin' +} + +group = 'xyz.tcheeric' +version = '0.6.5-SNAPSHOT' + +repositories { + mavenCentral() + gradlePluginPortal { + url = 'https://plugins.gradle.org/m2/' + } +} + +dependencies { + def springBootVersion = '3.4.3' + def retry = '1.6.2' + + implementation 'org.springframework.boot:spring-boot:' + springBootVersion + implementation 'org.springframework.boot:spring-boot-gradle-plugin:' + springBootVersion + implementation 'org.gradle.test-retry:org.gradle.test-retry.gradle.plugin:' + retry +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} diff --git a/buildSrc/src/main/groovy/nostr-java.conventions.gradle b/buildSrc/src/main/groovy/nostr-java.conventions.gradle new file mode 100644 index 000000000..b6e19f20b --- /dev/null +++ b/buildSrc/src/main/groovy/nostr-java.conventions.gradle @@ -0,0 +1,123 @@ +/* +nostr-java application/library dependencies & configuration file + +gradle project-build tools specific configs can be found in: + buildSrc/build.gradle +*/ + +plugins { + id 'java' + id 'java-library' + id 'maven-publish' + id 'org.springframework.boot' + id 'io.spring.dependency-management' + id 'com.adarshr.test-logger' + id 'org.gradle.test-retry' +} + +group = version = rootProject.property("nostr-java.group") +version = rootProject.property("nostr-java.version") +description = rootProject.property("nostr-java.description") + +repositories { + gradlePluginPortal() + mavenLocal() + mavenCentral() +} + +publishing { + publications { + nostrjava(MavenPublication) { + from components.java + } + } + repositories { + mavenLocal() +//// TODO: below placeholder for eric +// someRemoteRepo() + } +} + + +dependencies { + def springBootVersion = rootProject.property("nostr-java.springBootVersion") + def apacheCommonsLang3 = rootProject.property("nostr-java.apacheCommonsLang3") + def jacksonModuleAfterburner = rootProject.property("nostr-java.jacksonModuleAfterburner") + def googleGuava = rootProject.property("nostr-java.googleGuava") + def bouncyCastle = rootProject.property("nostr-java.bouncyCastle") + def awaitility = rootProject.property("nostr-java.awaitility") + def lombok = rootProject.property("nostr-java.lombok") + def logger = rootProject.property("nostr-java.adarshrGradleTestLoggerPlugin") + + implementation 'org.springframework.boot:spring-boot-starter:' + springBootVersion + implementation 'org.springframework.boot:spring-boot-devtools:' + springBootVersion + annotationProcessor 'org.springframework.boot:spring-boot-autoconfigure:' + springBootVersion + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor:' + springBootVersion + + implementation 'org.springframework.boot:spring-boot-starter-validation:' + springBootVersion + implementation 'org.springframework.boot:spring-boot-starter-websocket:' + springBootVersion + + implementation 'com.fasterxml.jackson.module:jackson-module-afterburner:' + jacksonModuleAfterburner + implementation 'org.bouncycastle:bcprov-jdk18on:' + bouncyCastle + + implementation 'org.apache.commons:commons-lang3:' + apacheCommonsLang3 + implementation 'com.google.guava:guava:' + googleGuava + implementation 'org.awaitility:awaitility:' + awaitility + implementation 'org.projectlombok:lombok:' + lombok + annotationProcessor 'org.awaitility:awaitility:' + awaitility + annotationProcessor 'org.projectlombok:lombok:' + lombok + + testImplementation 'org.springframework.boot:spring-boot-starter-test:' + springBootVersion + testImplementation 'org.projectlombok:lombok:' + lombok + testImplementation 'org.awaitility:awaitility:' + awaitility + testImplementation 'com.adarshr:gradle-test-logger-plugin:' + logger + + testAnnotationProcessor 'org.projectlombok:lombok:' + lombok +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +tasks.test { + systemProperty("spring.profiles.active", "test") + useJUnitPlatform() +// retry { +// maxRetries.set(1) +//// maxFailures.set(20) +//// failOnPassedAfterRetry.set(false) +//// failOnSkippedAfterRetry.set(true) +// } +} + +test { + filter { + excludeTestsMatching("nostr.api.integration.*"); + } +} + +tasks.bootJar { + enabled = false +} + +tasks.jar { + archiveClassifier = '' +} + +java.sourceCompatibility = JavaVersion.toVersion(rootProject.property("nostr-java.java-version")) +java.targetCompatibility = JavaVersion.toVersion(rootProject.property("nostr-java.java-version")) +java { + toolchain { + languageVersion = JavaLanguageVersion.of(rootProject.property("nostr-java.java-version").toString()) + } +} + +tasks.withType(JavaCompile).configureEach { + options.encoding = 'UTF-8' +} + +tasks.withType(Javadoc).configureEach { + options.encoding = 'UTF-8' +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..40e91dec1 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,21 @@ +org.gradle.configuration-cache=false +org.gradle.daemon=true +org.gradle.caching=true +org.gradle.parallel=true +#createReports=true +org.gradle.warning.mode=none + +nostr-java.group=xyz.tcheeric +nostr-java.version=0.6.5-SNAPSHOT +nostr-java.description=nostr-java + +nostr-java.java-version=21 +nostr-java.springBootVersion=3.4.3 +nostr-java.apacheCommonsLang3=3.17.0 +nostr-java.jacksonModuleAfterburner=2.18.3 +nostr-java.googleGuava=33.2.1-jre +nostr-java.bouncyCastle=1.80 +nostr-java.awaitility=4.2.0 +nostr-java.lombok=1.18.36 +nostr-java.adarshrGradleTestLoggerPlugin=4.0.0 +nostr-java.gradleTestRetry=1.6.2 diff --git a/metrics.md b/metrics.md new file mode 100644 index 000000000..49ccce034 --- /dev/null +++ b/metrics.md @@ -0,0 +1,232 @@ +### nostr-java & superconductor: gradle -vs- maven metrics + + +[useful gradle commands (w/ maven equivalents) @ bottom of page](https://github.com/avlo/nostr-java-avlo-fork/blob/79d3f521e177ebd5e741490e1cdc0f1001e07c49/metrics.md#useful-gradle-commands-w-maven-equivalents) + + +---- +##### nostr-java clean build + +###### maven: ~19 seconds + +```java +$ time mvn clean install -Dmaven.test.skip=true + + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 17.592 s + [INFO] Finished at: 2025-03-14T22:31:52-07:00 + [INFO] ------------------------------------------------------------------------ + + real 0m19.156s <----------------------------------------------------------- ~19 sec + user 0m58.042s + sys 0m1.616s +``` + +###### gradle: ~9 seconds +``` java +$ time gradle clean build -x test + +BUILD SUCCESSFUL in 9s +38 actionable tasks: 38 executed +Configuration cache entry reused. + + real 0m9.618s <------------------------------------------------------------- ~9 sec + user 0m2.383s + sys 0m0.116s +``` +---- + +#### nostr-java, subsequent build + +###### maven: ~21 seconds + +```java +$ time mvn install -Dmaven.test.skip=true + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 19.680 s + [INFO] Finished at: 2025-03-14T22:39:35-07:00 + [INFO] ------------------------------------------------------------------------ + + real 0m21.324s <------------------------------------------------------------- ~21sec + user 1m3.300s + sys 0m1.771s +``` + +###### gradle: ~1 second + +```java +$ time gradle build -x test + Reusing configuration cache. + + BUILD SUCCESSFUL in 1s + 27 actionable tasks: 27 up-to-date + Configuration cache entry reused. + + real 0m1.178s <------------------------------------------------------------- ~1sec + user 0m2.082s + sys 0m0.176s +``` + +---- +#### superconductor clean build: + +###### maven: ~11 seconds + +```java +$ time mvn clean install -Dmaven.test.skip=true + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 10.437 s + [INFO] Finished at: 2025-03-14T22:46:58-07:00 + [INFO] ------------------------------------------------------------------------ + + real 0m11.989s <------------------------------------------------------------- ~11sec + user 0m36.291s + sys 0m1.351s +``` + +###### gradle: ~7 seconds + +```java +$ time gradle clean build -x test + + BUILD SUCCESSFUL in 7s + 7 actionable tasks: 4 executed, 2 from cache, 1 up-to-date + + real 0m7.655s <------------------------------------------------------------ ~7sec + user 0m2.800s + sys 0m0.164s +``` + +---- +#### superconductor clean build integration- test: + +###### maven: ~1 minute, 15 seconds +``` java +$ mvn clean install + + [INFO] Results: + [INFO] + [INFO] Tests run: 52, Failures: 0, Errors: 0, Skipped: 0 + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 01:15 min <--------------------------------------------------- ~1min 15sec* + [INFO] Finished at: 2025-03-14T23:26:59-07:00 + [INFO] ------------------------------------------------------------------------ + + real 1m16.800s <----------------------------------------------------------------- ~1min 16sec* + user 2m58.945s + sys 0m4.427s +``` +_*one-second time diff re: maven publish to local repo (`~/.m2/xyz/tcheeric/...`)_ + +###### gradle: ~49 seconds +``` java +$ time gradle build + + SUCCESS: Executed 61 tests in 41.1s + + BUILD SUCCESSFUL in 49s + 10 actionable tasks: 6 executed, 4 from cache + + real 0m49.269s <------------------------------------------------------------- ~49sec + user 0m2.854s + sys 0m0.201s +``` + +---- + +#### superconductor subsequent build test +_historical note: significant down/wait-time using maven occurs here, thus motivating gradle build/test option for superconductor, initially, with nostr-java subsequently profiting from same feature/option. **from** **~1 minute** **down to** **~4 seconds** significantly improves workflow continuity. furthermore, multiple iterative test builds occurring as per typical development results in significant cumulative time saved._ + +###### maven: ~1 minute, 3 seconds + +```java +$ time mvn install + + [INFO] Results: + [INFO] + [INFO] Tests run: 52, Failures: 0, Errors: 0, Skipped: 0 + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 01:03 min <--------------------------------------------------------- ~1min 3sec** + [INFO] Finished at: 2025-03-14T23:35:18-07:00 + [INFO] ------------------------------------------------------------------------ + + real 1m6.132s <------------------------------------------------------------------------ ~1min 6sec** + user 2m32.305s + sys 0m3.797s +``` +_** three-second time diff re: maven publish to local repo (`~/.m2/xyz/tcheeric/...`)_ + +###### gradle: ~4 seconds +```java +$ time gradle build + +BUILD SUCCESSFUL in 3s +10 actionable tasks: 10 up-to-date + +real 0m4.020s <------------------------------------------------------------------------ ~4sec +user 0m2.913s +``` + +---- + +#### nostr-java integration tests against running SC + +###### maven: ~29 seconds + +```java +$ time mvn test + + [INFO] Results: + [INFO] + [INFO] Tests run: 159, Failures: 0, Errors: 0, Skipped: 0 + [INFO] + [INFO] ------------------------------------------------------------------------ + [INFO] Reactor Summary for nostr-java 0.6.5-SNAPSHOT: + [INFO] + [INFO] nostr-java ......................................... SUCCESS [ 0.012 s] + [INFO] nostr-java-util .................................... SUCCESS [ 0.900 s] + [INFO] nostr-java-crypto .................................. SUCCESS [ 0.353 s] + [INFO] nostr-java-base .................................... SUCCESS [ 0.246 s] + [INFO] nostr-java-event ................................... SUCCESS [ 0.142 s] + [INFO] nostr-java-id ...................................... SUCCESS [ 0.164 s] + [INFO] nostr-java-client .................................. SUCCESS [ 0.438 s] + [INFO] nostr-java-encryption .............................. SUCCESS [ 0.050 s] + [INFO] nostr-java-encryption-nip04 ........................ SUCCESS [ 0.107 s] + [INFO] nostr-java-encryption-nip44 ........................ SUCCESS [ 0.114 s] + [INFO] nostr-java-api ..................................... SUCCESS [ 0.190 s] + [INFO] nostr-java-examples ................................ SUCCESS [ 0.212 s] + [INFO] nostr-java-test .................................... SUCCESS [ 25.933 s] + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 29.084 s <------------------------------------------------------------------------ ~29sec*** + [INFO] Finished at: 2025-03-15T12:41:13-07:00 + [INFO] ------------------------------------------------------------------------ + + real 0m30.669s <-------------------------------------------------------------------------------------- ~30sec*** + user 1m2.185s + sys 0m1.698s +``` +_*** one-second time diff re: maven publish to local repo (`~/.m2/xyz/tcheeric/...`)_ + +---- + +##### useful gradle commands (w/ maven equivalents) + +```java +alias gc='gradle clean' // 'maven clean' +alias gb='gradle build' // 'maven build' +alias gcb='gradle clean build' // 'maven clean build' + +alias gbnotest='gradle build -x test' // 'maven build -Dmaven.test.skip=true' +alias gcbnotest='gradle clean build -x test' // 'maven clean build -Dmaven.test.skip=true' + +alias gpub='gradle publishToMavenLocal' // 'maven install' +``` diff --git a/nostr-java-api/build.gradle b/nostr-java-api/build.gradle new file mode 100644 index 000000000..6d8ec99ad --- /dev/null +++ b/nostr-java-api/build.gradle @@ -0,0 +1,30 @@ +plugins { + id 'nostr-java.conventions' +} + +description = 'nostr-java-api' + +dependencies { + api project(':nostr-java-client') + api project(':nostr-java-encryption') +} + +tasks.register('integrationTest', Test) { + description = 'api integration tests.' + group = 'verification' + systemProperty("spring.profiles.active", "test") + useJUnitPlatform() + filter { + excludeTestsMatching("nostr.api.unit.*"); + includeTestsMatching("nostr.api.integration.*"); + } +} + +integrationTest { + retry { + failOnPassedAfterRetry = false + maxRetries = 1 + } +} + +check.dependsOn integrationTest diff --git a/nostr-java-api/pom.xml b/nostr-java-api/pom.xml index 21777ec5e..b60f18204 100644 --- a/nostr-java-api/pom.xml +++ b/nostr-java-api/pom.xml @@ -1,56 +1,26 @@ - 4.0.0 + xyz.tcheeric nostr-java 0.6.5-SNAPSHOT + ../pom.xml + nostr-java-api jar + - - ${project.groupId} - nostr-java-base - ${project.version} - - - ${project.groupId} - nostr-java-event - ${project.version} - - - ${project.groupId} - nostr-java-id - ${project.version} - ${project.groupId} nostr-java-client - ${project.version} + ${nostr-java.version} ${project.groupId} nostr-java-encryption - ${project.version} - - - ${project.groupId} - nostr-java-encryption-nip04 - ${project.version} - - - ${project.groupId} - nostr-java-encryption-nip44 - ${project.version} - - - org.apache.commons - commons-lang3 - ${commons-lang3.version} + ${nostr-java.version} - - nostr.java.api.NostrJavaApi - diff --git a/nostr-java-api/src/main/java/module-info.java b/nostr-java-api/src/main/java/module-info.java deleted file mode 100644 index 5238893c9..000000000 --- a/nostr-java-api/src/main/java/module-info.java +++ /dev/null @@ -1,19 +0,0 @@ -module nostr.api { - requires nostr.base; - requires nostr.util; - requires nostr.event; - requires nostr.id; - requires nostr.client; - 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; -} 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 af3310462..564839acb 100644 --- a/nostr-java-api/src/main/java/nostr/api/EventNostr.java +++ b/nostr-java-api/src/main/java/nostr/api/EventNostr.java @@ -39,7 +39,6 @@ public EventNostr(@NonNull Identity sender) { public EventNostr sign() { super.sign(getSender(), event); - return this; } @@ -69,19 +68,16 @@ public U signAndSend(Map relays) { public EventNostr setSender(@NonNull Identity sender) { super.setSender(sender); - return this; } public EventNostr setRelays(@NonNull Map relays) { super.setRelays(relays); - return this; } public EventNostr setRecipient(@NonNull PublicKey recipient) { this.recipient = recipient; - return this; } diff --git a/nostr-java-api/src/main/java/nostr/api/NIP04.java b/nostr-java-api/src/main/java/nostr/api/NIP04.java index b9788ca80..b2f4dcd8b 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP04.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP04.java @@ -10,7 +10,7 @@ import nostr.base.ITag; import nostr.base.PublicKey; import nostr.encryption.MessageCipher; -import nostr.encryption.nip04.MessageCipher04; +import nostr.encryption.MessageCipher04; import nostr.event.NIP04Event; import nostr.event.impl.DirectMessageEvent; import nostr.event.impl.GenericEvent; diff --git a/nostr-java-api/src/main/java/nostr/api/NIP44.java b/nostr-java-api/src/main/java/nostr/api/NIP44.java index 778918268..ec5c50f66 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP44.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP44.java @@ -6,8 +6,9 @@ import nostr.base.ITag; import nostr.base.PublicKey; import nostr.encryption.MessageCipher; -import nostr.encryption.nip44.MessageCipher44; +import nostr.encryption.MessageCipher44; import nostr.event.BaseTag; +import nostr.event.filter.Filterable; import nostr.event.impl.EncryptedPayloadEvent; import nostr.event.impl.GenericEvent; import nostr.event.tag.PubKeyTag; @@ -26,12 +27,6 @@ public NIP44(@NonNull Identity sender, @NonNull PublicKey recipient) { setRecipient(recipient); } - /** - * Create a NIP44 Encrypted Payload - * - * @param content the EP content in clear-text - * @return the EP event - */ public NIP44 createDirectMessageEvent(@NonNull String content) { var encryptedContent = encrypt(getSender(), content, getRecipient()); var factory = new NIP44Impl.EncryptedPayloadEventFactory(getSender(), getRecipient(), encryptedContent); @@ -40,14 +35,6 @@ public NIP44 createDirectMessageEvent(@NonNull String content) { return this; } - /** - * Create a NIP44 Encrypted Payload - * - * @param tags additional note's tags - * @param recipient the EP recipient - * @param content the EP content - * @return the EP event - */ public NIP44 createDirectMessageEvent(@NonNull List tags, @NonNull PublicKey recipient, @NonNull String content) { var encryptedContent = encrypt(getSender(), content, recipient); var factory = new NIP44Impl.EncryptedPayloadEventFactory(getSender(), tags, recipient, encryptedContent); @@ -56,14 +43,8 @@ public NIP44 createDirectMessageEvent(@NonNull List tags, @NonNull P return this; } - /** - * Encrypt an Encrypted Payload event - * - * @param senderId - * @param ep the EP event - */ - public static void encrypt(@NonNull Identity senderId, @NonNull EncryptedPayloadEvent ep) { - encryptDirectMessage(senderId, ep); + public static void encrypt(@NonNull Identity sender, @NonNull EncryptedPayloadEvent ep) { + encryptDirectMessage(sender, ep); } public NIP44 encrypt() { @@ -71,63 +52,45 @@ public NIP44 encrypt() { return this; } - public static String encrypt(@NonNull Identity senderId, @NonNull String message, @NonNull PublicKey recipient) { - MessageCipher cipher = new MessageCipher44(senderId.getPrivateKey().getRawData(), recipient.getRawData()); + public static String encrypt(@NonNull Identity sender, @NonNull String message, @NonNull PublicKey recipient) { + MessageCipher cipher = new MessageCipher44(sender.getPrivateKey().getRawData(), recipient.getRawData()); return cipher.encrypt(message); } - /** - * Decrypt an encrypted Payloads - * - * @param rcptId - * @param ep the encrypted Payloads - * @return the ep content in clear-text - */ - public static String decrypt(@NonNull Identity rcptId, @NonNull EncryptedPayloadEvent ep) { - return NIP44.decrypt(rcptId, (GenericEvent) ep); + public static String decrypt(@NonNull Identity recipient, @NonNull EncryptedPayloadEvent ep) { + return NIP44.decrypt(recipient, (GenericEvent) ep); } - /** - * Decrypt an encrypted Payloads - * - * @param identity the sender's identity - * @param encrypteEPessage the encrypted message - * @param recipient the recipient's public key - * @return the ep content in clear-text - */ public static String decrypt(@NonNull Identity identity, @NonNull String encrypteEPessage, @NonNull PublicKey recipient) { MessageCipher cipher = new MessageCipher44(identity.getPrivateKey().getRawData(), recipient.getRawData()); return cipher.decrypt(encrypteEPessage); } - public static String decrypt(@NonNull Identity rcptId, @NonNull GenericEvent event) { - var recipient = event.getTags() - .stream() - .filter(t -> t.getCode().equalsIgnoreCase("p")) - .findFirst() - .orElseThrow(() -> new NoSuchElementException("No matching p-tag found.")); - var pTag = (PubKeyTag) recipient; - - boolean rcptFlag = amITheRecipient(rcptId, event); + public static String decrypt(@NonNull Identity recipient, @NonNull GenericEvent event) { + boolean rcptFlag = amITheRecipient(recipient, event); if (!rcptFlag) { // I am the message sender - MessageCipher cipher = new MessageCipher44(rcptId.getPrivateKey().getRawData(), pTag.getPublicKey().getRawData()); + MessageCipher cipher = new MessageCipher44(recipient.getPrivateKey().getRawData(), + Filterable.getTypeSpecificTags(PubKeyTag.class, event) + .stream() + .findFirst() + .orElseThrow(() -> new NoSuchElementException("No matching p-tag found.")).getPublicKey().getRawData()); return cipher.decrypt(event.getContent()); } // I am the message recipient var sender = event.getPubKey(); log.log(Level.FINE, "The message is being decrypted for {0}", sender); - MessageCipher cipher = new MessageCipher44(rcptId.getPrivateKey().getRawData(), sender.getRawData()); + MessageCipher cipher = new MessageCipher44(recipient.getPrivateKey().getRawData(), sender.getRawData()); return cipher.decrypt(event.getContent()); } - private static void encryptDirectMessage(@NonNull Identity senderId, @NonNull EncryptedPayloadEvent ep) { + private static void encryptDirectMessage(@NonNull Identity sender, @NonNull EncryptedPayloadEvent ep) { ITag pkTag = ep.getTags().get(0); if (pkTag instanceof PubKeyTag pubKeyTag) { var rcptPublicKey = pubKeyTag.getPublicKey(); - MessageCipher cipher = new MessageCipher44(senderId.getPrivateKey().getRawData(), rcptPublicKey.getRawData()); + MessageCipher cipher = new MessageCipher44(sender.getPrivateKey().getRawData(), rcptPublicKey.getRawData()); var encryptedContent = cipher.encrypt(ep.getContent()); ep.setContent(encryptedContent); } diff --git a/nostr-java-api/src/main/java/nostr/api/NIP46.java b/nostr-java-api/src/main/java/nostr/api/NIP46.java index c806e1b07..4af9e0584 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP46.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP46.java @@ -1,7 +1,6 @@ package nostr.api; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -17,6 +16,8 @@ import java.util.Set; import java.util.logging.Level; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; + public final class NIP46 extends EventNostr { public NIP46(@NonNull Identity sender) { @@ -70,8 +71,7 @@ public void addParam(String param) { public String toString() { try { - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.writeValueAsString(this); + return MAPPER_AFTERBURNER.writeValueAsString(this); } catch (JsonProcessingException ex) { // Handle the exception if needed log.log(Level.WARNING, "Error converting to JSON: {0}", ex.getMessage()); @@ -80,9 +80,8 @@ public String toString() { } public static Request fromString(@NonNull String jsonString) { - ObjectMapper objectMapper = new ObjectMapper(); try { - return objectMapper.readValue(jsonString, Request.class); + return MAPPER_AFTERBURNER.readValue(jsonString, Request.class); } catch (JsonProcessingException e) { throw new RuntimeException(e); } @@ -100,8 +99,7 @@ public static final class Response implements Serializable { public String toString() { try { - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.writeValueAsString(this); + return MAPPER_AFTERBURNER.writeValueAsString(this); } catch (JsonProcessingException ex) { // Handle the exception if needed log.log(Level.WARNING, "Error converting to JSON: {0}", ex.getMessage()); @@ -110,9 +108,8 @@ public String toString() { } public static Response fromString(@NonNull String jsonString) { - ObjectMapper objectMapper = new ObjectMapper(); try { - return objectMapper.readValue(jsonString, Response.class); + return MAPPER_AFTERBURNER.readValue(jsonString, Response.class); } catch (JsonProcessingException e) { throw new RuntimeException(e); } diff --git a/nostr-java-api/src/main/java/nostr/api/NIP57.java b/nostr-java-api/src/main/java/nostr/api/NIP57.java index c85f7195a..c3c0cb1e4 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP57.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP57.java @@ -11,7 +11,11 @@ import nostr.event.impl.GenericEvent; import nostr.event.impl.GenericTag; import nostr.event.impl.ZapRequest; -import nostr.event.tag.*; +import nostr.event.tag.AddressTag; +import nostr.event.tag.EventTag; +import nostr.event.tag.IdentifierTag; +import nostr.event.tag.PubKeyTag; +import nostr.event.tag.RelaysTag; import nostr.id.Identity; import java.util.ArrayList; @@ -54,7 +58,7 @@ public NIP57 createZapRequestEvent(@NonNull PublicKey recipientPubKey, @NonNu } public NIP57 createZapReceiptEvent(@NonNull PubKeyTag zapRequestPubKeyTag, List baseTags, EventTag zapRequestEventTag, AddressTag zapRequestAddressTag, @NonNull String bolt11, - @NonNull String descriptionSha256, @NonNull String preimage) { + @NonNull String descriptionSha256, @NonNull String preimage) { setEvent((T) new ZapReceiptEventFactory(getSender(), baseTags, zapRequestPubKeyTag, zapRequestEventTag, zapRequestAddressTag, bolt11, descriptionSha256, preimage).create()); return this; } @@ -169,16 +173,16 @@ public static GenericTag createAmountTag(@NonNull Integer amount) { */ public static GenericTag createZapTag(@NonNull PublicKey receiver, @NonNull List relays, Integer weight) { List attributes = new ArrayList<>(); - var receiverAttr = new ElementAttribute("receiver", receiver.toString(), 57); - var relayAttrs = relays.stream().map(relay -> new ElementAttribute("relay", relay.getUri(), 57)).toList(); + var receiverAttr = new ElementAttribute("receiver", receiver.toString()); + var relayAttrs = relays.stream().map(relay -> new ElementAttribute("relay", relay.getUri())).toList(); if (weight != null) { - var weightAttr = new ElementAttribute("weight", weight, 57); + var weightAttr = new ElementAttribute("weight", weight); attributes.add(weightAttr); } attributes.add(receiverAttr); attributes.addAll(relayAttrs); - return new GenericTag(ZAP_TAG_NAME, 57, attributes); + return new GenericTag(ZAP_TAG_NAME, attributes); } /** diff --git a/nostr-java-api/src/main/java/nostr/api/NIP60.java b/nostr-java-api/src/main/java/nostr/api/NIP60.java index e6a3135c3..bc5b0d367 100644 --- a/nostr-java-api/src/main/java/nostr/api/NIP60.java +++ b/nostr-java-api/src/main/java/nostr/api/NIP60.java @@ -6,7 +6,6 @@ import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonValue; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.AllArgsConstructor; import lombok.Builder; @@ -30,6 +29,8 @@ import nostr.event.tag.EventTag; import nostr.id.Identity; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; + public class NIP60 extends EventNostr { private static final String MINT_TAG_NAME = "mint"; @@ -189,12 +190,12 @@ private String getWalletEventContent(@NonNull Wallet wallet) { tags.add(NIP60.createBalanceTag(wallet.getBalance(), wallet.getUnit())); tags.add(NIP60.createPrivKeyTag(wallet.getPrivateKey())); - return NIP44.encrypt(getSender(), new ObjectMapper().writeValueAsString(tags), getSender().getPublicKey()); + return NIP44.encrypt(getSender(), MAPPER_AFTERBURNER.writeValueAsString(tags), getSender().getPublicKey()); } @SneakyThrows private String getTokenEventContent(@NonNull Token token) { - return NIP44.encrypt(getSender(), new ObjectMapper().writeValueAsString(token), getSender().getPublicKey()); + return NIP44.encrypt(getSender(), MAPPER_AFTERBURNER.writeValueAsString(token), getSender().getPublicKey()); } @SneakyThrows 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 f297dfb21..7a28e197b 100644 --- a/nostr-java-api/src/main/java/nostr/api/NostrIF.java +++ b/nostr-java-api/src/main/java/nostr/api/NostrIF.java @@ -20,7 +20,7 @@ public interface NostrIF { 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); - NostrIF sign(@NonNull Identity identity, @NonNull ISignable signable); + NostrIF sign(@NonNull Identity identity, @NonNull ISignable signable) throws Exception; boolean verify(@NonNull GenericEvent event); Identity getSender(); Map getRelays(); 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 9cf09d1fc..31ba1a3eb 100644 --- a/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java +++ b/nostr-java-api/src/main/java/nostr/api/NostrSpringWebSocketClient.java @@ -136,11 +136,6 @@ public boolean verify(@NonNull GenericEvent event) { } } - @Override - public Identity getSender() { - return sender; - } - @Override public Map getRelays() { return clientMap.values().stream() 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 98e9a679b..971265ef4 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 @@ -58,7 +58,7 @@ public TextNoteEventFactory(@NonNull Identity sender, @NonNull List tag @Override public TextNoteEvent create() { var event = new TextNoteEvent(getSender(), getTags(), getContent()); - getTags().forEach(t -> event.addTag(t)); + getTags().forEach(event::addTag); return event; } } diff --git a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP32Impl.java b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP32Impl.java index a9cbc5209..577e8239e 100644 --- a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP32Impl.java +++ b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP32Impl.java @@ -5,7 +5,6 @@ package nostr.api.factory.impl; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -14,6 +13,7 @@ import java.util.Map; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; import static nostr.util.NostrUtil.escapeJsonString; /** @@ -65,7 +65,7 @@ public String[] toParams() { result = new String[3]; result[0] = value; result[1] = nameSpace.getValue(); - result[2] = escapeJsonString(new ObjectMapper().writeValueAsString(metadata)); + result[2] = escapeJsonString(MAPPER_AFTERBURNER.writeValueAsString(metadata)); } else { result = new String[2]; result[0] = value; diff --git a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP42Impl.java b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP42Impl.java index d5c46d91b..35b2f739e 100644 --- a/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP42Impl.java +++ b/nostr-java-api/src/main/java/nostr/api/factory/impl/NIP42Impl.java @@ -93,7 +93,7 @@ public static class RelayAuthenticationMessageFactory extends MessageFactory attributes = new ArrayList<>(); - final var attr = new ElementAttribute("challenge", challenge, 42); + final var attr = new ElementAttribute("challenge", challenge); attributes.add(attr); return new GenericMessage(Command.AUTH.name(), attributes, 42); } diff --git a/nostr-java-api/src/main/java/nostr/config/RelayProperties.java b/nostr-java-api/src/main/java/nostr/config/RelayProperties.java new file mode 100644 index 000000000..ae8d4f066 --- /dev/null +++ b/nostr-java-api/src/main/java/nostr/config/RelayProperties.java @@ -0,0 +1,21 @@ +package nostr.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +import java.util.Map; +import java.util.ResourceBundle; +import java.util.stream.Collectors; + +@Configuration +@PropertySource("classpath:relays.properties") +public class RelayProperties { + + @Bean + public Map relays() { + ResourceBundle relaysBundle = ResourceBundle.getBundle("relays"); + return relaysBundle.keySet().stream() + .collect(Collectors.toMap(key -> key, relaysBundle::getString)); + } +} diff --git a/nostr-java-test/src/test/resources/relays.properties b/nostr-java-api/src/main/resources/relays.properties similarity index 100% rename from nostr-java-test/src/test/resources/relays.properties rename to nostr-java-api/src/main/resources/relays.properties diff --git a/nostr-java-api/src/test/java/nostr/api/integration/ApiEventIT.java b/nostr-java-api/src/test/java/nostr/api/integration/ApiEventIT.java new file mode 100644 index 000000000..7cc37d63b --- /dev/null +++ b/nostr-java-api/src/test/java/nostr/api/integration/ApiEventIT.java @@ -0,0 +1,620 @@ +package nostr.api.integration; + +import com.fasterxml.jackson.core.JsonProcessingException; +import nostr.api.EventNostr; +import nostr.api.NIP01; +import nostr.api.NIP04; +import nostr.api.NIP15; +import nostr.api.NIP32; +import nostr.api.NIP44; +import nostr.api.NIP52; +import nostr.api.NIP57; +import nostr.base.ElementAttribute; +import nostr.base.GenericTagQuery; +import nostr.base.PrivateKey; +import nostr.config.RelayProperties; +import nostr.crypto.bech32.Bech32; +import nostr.crypto.bech32.Bech32Prefix; +import nostr.event.BaseTag; +import nostr.event.NIP01Event; +import nostr.event.filter.Filters; +import nostr.event.filter.GenericTagQueryFilter; +import nostr.event.filter.GeohashTagFilter; +import nostr.event.filter.HashtagTagFilter; +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.GenericTag; +import nostr.event.impl.NostrMarketplaceEvent; +import nostr.event.impl.NostrMarketplaceEvent.Product.Spec; +import nostr.event.impl.TextNoteEvent; +import nostr.event.impl.ZapReceiptEvent; +import nostr.event.impl.ZapRequestEvent; +import nostr.event.message.OkMessage; +import nostr.event.tag.GeohashTag; +import nostr.event.tag.HashtagTag; +import nostr.event.tag.IdentifierTag; +import nostr.event.tag.PubKeyTag; +import nostr.id.Identity; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static nostr.base.IEvent.MAPPER_AFTERBURNER; +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.*; + +@SpringJUnitConfig(RelayProperties.class) +@ActiveProfiles("test") +public class ApiEventIT { + @Autowired + private Map relays; + + @Test + public void testNIP01CreateTextNoteEvent() throws Exception { + System.out.println("testNIP01CreateTextNoteEvent"); + + var nip01 = new NIP01(Identity.generateRandomIdentity()); + var instance = nip01.createTextNoteEvent( + List.of(NIP01.createPubKeyTag(Identity.generateRandomIdentity().getPublicKey())), + "Hello simplified nostr-java!") + .getEvent(); + instance.update(); + + assertNotNull(instance.getId()); + assertNotNull(instance.getCreatedAt()); + assertNull(instance.getSignature()); + + final String bech32 = instance.toBech32(); + assertNotNull(bech32); + assertEquals(Bech32Prefix.NOTE.getCode(), Bech32.decode(bech32).hrp); + + await().atMost(Duration.ofSeconds(3)); + } + + @Test + public void testNIP01SendTextNoteEvent() throws IOException { + System.out.println("testNIP01SendTextNoteEvent"); + + var nip01 = new NIP01(Identity.generateRandomIdentity()); + var instance = nip01.createTextNoteEvent("Hello simplified nostr-java!").sign(); + + var response = instance.setRelays(relays).send(); + assertTrue(response instanceof OkMessage); + assertEquals(nip01.getEvent().getId(), ((OkMessage) response).getEventId()); + +// nip01.close(); + } + + @Test + public void testNIP04SendDirectMessage() throws IOException { + System.out.println("testNIP04SendDirectMessage"); + + var nip04 = new NIP04( + Identity.generateRandomIdentity(), + Identity.generateRandomIdentity().getPublicKey()); + + var instance = nip04 + .createDirectMessageEvent("Quand on n'a que l'amour pour tracer un chemin et forcer le destin...") + .sign(); + + var signature = instance.getEvent().getSignature(); + assertNotNull(signature); + var response = instance.setRelays(relays).send(); + assertTrue(response instanceof OkMessage); + assertEquals(nip04.getEvent().getId(), ((OkMessage) response).getEventId()); +// nip04.close(); + } + + @Test + public void testNIP44SendDirectMessage() throws IOException { + System.out.println("testNIP44SendDirectMessage"); + + var nip44 = new NIP44( + Identity.generateRandomIdentity(), + Identity.generateRandomIdentity().getPublicKey()); + + var instance = nip44 + .createDirectMessageEvent("Quand on n'a que l'amour pour tracer un chemin et forcer le destin...").sign(); + assertNotNull(instance.getEvent().getSignature()); + var response = instance.setRelays(relays).send(); + assertTrue(response instanceof OkMessage); + assertEquals(nip44.getEvent().getId(), ((OkMessage) response).getEventId()); +// nip44.close(); + } + + @Test + public void testNIP01SendTextNoteEventGeoHashTag() throws IOException { + System.out.println("testNIP01SendTextNoteEventGeoHashTag"); + + String targetString = "geohash_tag-location-testNIP01SendTextNoteEventGeoHashTag"; + GeohashTag geohashTag = new GeohashTag(targetString); + + NIP01 nip01 = new NIP01<>(Identity.generateRandomIdentity()); + nip01.createTextNoteEvent(List.of(geohashTag), "GeohashTag Test location testNIP01SendTextNoteEventGeoHashTag").signAndSend(relays); + + Filters filters = new Filters( + new GeohashTagFilter<>(new GeohashTag(targetString))); + + List result = nip01.sendRequest(filters, UUID.randomUUID().toString()); + + assertFalse(result.isEmpty()); + assertEquals(2, result.size()); + assertTrue(result.stream().anyMatch(s -> s.contains(targetString))); + +// nip01.close(); + } + + @Test + public void testNIP01SendTextNoteEventHashtagTag() throws IOException { + System.out.println("testNIP01SendTextNoteEventHashtagTag"); + + String targetString = "hashtag-tag-value-testNIP01SendTextNoteEventHashtagTag"; + HashtagTag hashtagTag = new HashtagTag(targetString); + + NIP01 nip01 = new NIP01<>(Identity.generateRandomIdentity()); + nip01.createTextNoteEvent(List.of(hashtagTag), "Hashtag Tag Test value testNIP01SendTextNoteEventHashtagTag").signAndSend(relays); + + Filters filters = new Filters( + new HashtagTagFilter<>(new HashtagTag(targetString))); + + List result = nip01.sendRequest(filters, UUID.randomUUID().toString()); + + assertFalse(result.isEmpty()); + assertEquals(2, result.size()); + assertTrue(result.stream().anyMatch(s -> s.contains(targetString))); + +// nip01.close(); + } + + @Test + public void testNIP01SendTextNoteEventCustomGenericTag() throws IOException { + System.out.println("testNIP01SendTextNoteEventCustomGenericTag"); + + String targetString = "custom-generic-tag-testNIP01SendTextNoteEventCustomGenericTag"; + GenericTag genericTag = GenericTag.create("m", targetString); + + NIP01 nip01 = new NIP01<>(Identity.generateRandomIdentity()); + nip01.createTextNoteEvent(List.of(genericTag), "Custom Generic Tag Test testNIP01SendTextNoteEventCustomGenericTag").signAndSend(relays); + + Filters filters = new Filters( + new GenericTagQueryFilter<>(new GenericTagQuery("#m", targetString))); + + List result = nip01.sendRequest(filters, UUID.randomUUID().toString()); + + assertFalse(result.isEmpty()); + assertEquals(2, result.size()); + + String matcher = """ + ["m","custom-generic-tag-testNIP01SendTextNoteEventCustomGenericTag"]"""; + + assertTrue(result.stream().anyMatch(s -> s.contains(matcher))); + +// nip01.close(); + } + + @Test + public void testFiltersListReturnSameSingularEvent() throws IOException { + System.out.println("testFiltersListReturnSameSingularEvent"); + + String geoHashTagTarget = "geohash_tag-location_SameSingularEvent"; + GeohashTag geohashTag = new GeohashTag(geoHashTagTarget); + + String genericTagTarget = "generic-tag-value_SameSingularEvent"; + GenericTag genericTag = GenericTag.create("m", genericTagTarget); + + NIP01 nip01 = new NIP01<>(Identity.generateRandomIdentity()); + + nip01.createTextNoteEvent(List.of(geohashTag, genericTag), "Multiple Filters").signAndSend(relays); + + Filters filters1 = new Filters( + new GeohashTagFilter<>(new GeohashTag(geoHashTagTarget))); + Filters filters2 = new Filters( + new GenericTagQueryFilter<>(new GenericTagQuery("#m", genericTagTarget))); + + List 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 + String geoHashTagTarget1 = "geohash_tag-location-1"; + GeohashTag geohashTag1 = new GeohashTag(geoHashTagTarget1); + String genericTagTarget1 = "generic-tag-value-1"; + GenericTag genericTag1 = GenericTag.create("m", genericTagTarget1); + NIP01 nip01_1 = new NIP01<>(Identity.generateRandomIdentity()); + nip01_1.createTextNoteEvent(List.of(geohashTag1, genericTag1), "Multiple Filters 1").signAndSend(relays); + +// second event + String geoHashTagTarget2 = "geohash_tag-location-2"; + GeohashTag geohashTag2 = new GeohashTag(geoHashTagTarget2); + String genericTagTarget2 = "generic-tag-value-2"; + GenericTag genericTag2 = GenericTag.create("m", genericTagTarget2); + NIP01 nip01_2 = new NIP01<>(Identity.generateRandomIdentity()); + nip01_2.createTextNoteEvent(List.of(geohashTag2, genericTag2), "Multiple Filters 2").signAndSend(relays); + + 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 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"); + + String geoHashTagTarget = "geohash_tag-location-DifferentTypesReturnSameEvent"; + GeohashTag geohashTag = new GeohashTag(geoHashTagTarget); + + String genericTagTarget = "generic-tag-value-DifferentTypesReturnSameEvent"; + GenericTag genericTag = GenericTag.create("m", genericTagTarget); + + NIP01 nip01 = new NIP01<>(Identity.generateRandomIdentity()); + nip01.createTextNoteEvent(List.of(geohashTag, genericTag), "Multiple Filters").signAndSend(relays); + + Filters filters = new Filters( + new GeohashTagFilter<>(new GeohashTag(geoHashTagTarget)), + new GenericTagQueryFilter<>(new GenericTagQuery("#m", genericTagTarget))); + + List 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"); + + Identity identity = Identity.generateRandomIdentity(); + var nip04 = new NIP04(identity, Identity.generateRandomIdentity().getPublicKey()); + var instance = nip04 + .createDirectMessageEvent("Quand on n'a que l'amour pour tracer un chemin et forcer le destin...") + .sign(); + + var message = NIP04.decrypt(identity, instance.getEvent()); + + assertEquals("Quand on n'a que l'amour pour tracer un chemin et forcer le destin...", message); + } + + @Test + public void testNIP44EncryptDecrypt() { + System.out.println("testNIP44EncryptDecrypt"); + + Identity identity = Identity.generateRandomIdentity(); + var nip44 = new NIP44(identity, Identity.generateRandomIdentity().getPublicKey()); + + var instance = nip44 + .createDirectMessageEvent("Quand on n'a que l'amour pour tracer un chemin et forcer le destin...").sign(); + var message = NIP44.decrypt(identity, instance.getEvent()); + + assertEquals("Quand on n'a que l'amour pour tracer un chemin et forcer le destin...", message); + } + + @Test + public void testNIP15CreateStallEvent() throws JsonProcessingException { + System.out.println("testNIP15CreateStallEvent"); + + Stall stall = createStall(); + var nip15 = new NIP15<>(Identity.create(PrivateKey.generateRandomPrivKey())); + + // Create and send the nostr event + var instance = nip15.createCreateOrUpdateStallEvent(stall).sign(); + var signature = instance.getEvent().getSignature(); + assertNotNull(signature); + + // Fetch the content and compare with the above original + var content = instance.getEvent().getContent(); + var expected = MAPPER_AFTERBURNER.readValue(content, Stall.class); + + assertEquals(expected, stall); + } + + @Test + public void testNIP15UpdateStallEvent() throws IOException { + System.out.println("testNIP15UpdateStallEvent"); + + var stall = createStall(); + var nip15 = new NIP15<>(Identity.create(PrivateKey.generateRandomPrivKey())); + + // Create and send the nostr event + var instance = nip15.createCreateOrUpdateStallEvent(stall).sign(); + var signature = instance.getEvent().getSignature(); + assertNotNull(signature); + + var response = instance.setRelays(relays).send(); + assertTrue(response instanceof OkMessage); + assertEquals(nip15.getEvent().getId(), ((OkMessage) response).getEventId()); + + // Update the shipping + var shipping = stall.getShipping(); + shipping.setCost(20.00f); + + EventNostr event = nip15.createCreateOrUpdateStallEvent(stall).sign(); + response = event.setRelays(relays).send(); + assertTrue(response instanceof OkMessage); + assertEquals(nip15.getEvent().getId(), ((OkMessage) response).getEventId()); + +// nip15.close(); + } + + @Test + public void testNIP15CreateProductEvent() throws IOException { + + System.out.println("testNIP15CreateProductEvent"); + + // Create the stall object + var stall = createStall(); + var nip15 = new NIP15<>(Identity.create(PrivateKey.generateRandomPrivKey())); + + // Create the product + var product = createProduct(stall); + + List categories = new ArrayList<>(); + categories.add("bijoux"); + categories.add("Hommes"); + + EventNostr event = nip15.createCreateOrUpdateProductEvent(product, categories).sign(); + var response = event.setRelays(relays).send(); + assertTrue(response instanceof OkMessage); + assertEquals(nip15.getEvent().getId(), ((OkMessage) response).getEventId()); + +// nip15.close(); + } + + @Test + public void testNIP15UpdateProductEvent() throws IOException { + + System.out.println("testNIP15UpdateProductEvent"); + + // Create the stall object + var stall = createStall(); + var nip15 = new NIP15<>(Identity.create(PrivateKey.generateRandomPrivKey())); + + // Create the product + var product = createProduct(stall); + + List categories = new ArrayList<>(); + categories.add("bijoux"); + categories.add("Hommes"); + + EventNostr event1 = nip15.createCreateOrUpdateProductEvent(product, categories).sign(); + var response = event1.setRelays(relays).send(); + assertTrue(response instanceof OkMessage); + assertEquals(nip15.getEvent().getId(), ((OkMessage) response).getEventId()); + + product.setDescription("Un nouveau bijou en or"); + categories.add("bagues"); + + EventNostr event2 = nip15.createCreateOrUpdateProductEvent(product, categories).sign(); + response = event2.setRelays(relays).send(); + assertTrue(response instanceof OkMessage); + assertEquals(nip15.getEvent().getId(), ((OkMessage) response).getEventId()); + +// nip15.close(); + } + + @Test + public void testNIP32CreateNameSpace() { + + System.out.println("testNIP32CreateNameSpace"); + + var langNS = NIP32.createNameSpaceTag("Languages"); + + assertEquals("L", langNS.getCode()); + assertEquals(1, langNS.getAttributes().size()); + assertEquals("Languages", langNS.getAttributes().iterator().next().getValue()); + } + + @Test + public void testNIP32CreateLabel1() { + + System.out.println("testNIP32CreateLabel1"); + + var label = NIP32.createLabelTag("Languages", "english"); + + assertEquals("l", label.getCode()); + assertEquals(2, label.getAttributes().size()); + assertTrue(label.getAttributes().contains(new ElementAttribute("param0", "english"))); + assertTrue(label.getAttributes().contains(new ElementAttribute("param1", "Languages"))); + } + + @Test + public void testNIP32CreateLabel2() { + + System.out.println("testNIP32CreateLabel2"); + + var metadata = new HashMap(); + metadata.put("article", "the"); + var label = NIP32.createLabelTag("Languages", "english", metadata); + + assertEquals("l", label.getCode()); + assertEquals(3, label.getAttributes().size()); + assertTrue(label.getAttributes().contains(new ElementAttribute("param0", "english"))); + assertTrue(label.getAttributes().contains(new ElementAttribute("param1", "Languages"))); + assertTrue(label.getAttributes().contains(new ElementAttribute("param2", "{\\\"article\\\":\\\"the\\\"}")), + "{\\\"article\\\":\\\"the\\\"}"); + } + + @Test + public void testNIP52CalendarTimeBasedEventEvent() throws IOException { + System.out.println("testNIP52CalendarTimeBasedEventEvent"); + + CalendarContent calendarContent = CalendarContent.builder( + new IdentifierTag("UUID-CalendarTimeBasedEventTest"), + "Calendar Time-Based Event title", + 1716513986268L).build(); + + calendarContent.setStartTzid("1687765220"); + calendarContent.setEndTzid("1687765230"); + + calendarContent.setLabels(List.of("english", "mycenaean greek")); + + List tags = new ArrayList<>(); + tags.add(new PubKeyTag(Identity.generateRandomIdentity().getPublicKey(), + "ws://localhost:5555", + "ISSUER")); + tags.add(new PubKeyTag(Identity.generateRandomIdentity().getPublicKey(), + "", + "COUNTERPARTY")); + + var nip52 = new NIP52<>(Identity.create(PrivateKey.generateRandomPrivKey())); + EventNostr event = nip52.createCalendarTimeBasedEvent(tags, "content", calendarContent).sign(); + var response = event.setRelays(relays).send(); + assertTrue(response instanceof OkMessage); + assertEquals(nip52.getEvent().getId(), ((OkMessage) response).getEventId()); + +// nip52.close(); + } + + @Test + void testNIP57CreateZapRequestEvent() throws Exception { + System.out.println("testNIP57CreateZapRequestEvent"); + + var nip57 = new NIP57(Identity.generateRandomIdentity()); + final String ZAP_REQUEST_CONTENT = "zap request content"; + final Long AMOUNT = 1232456L; + final String LNURL = "lnUrl"; + final String RELAYS_TAG = "ws://localhost:5555"; + ZapRequestEvent instance = nip57 + .createZapRequestEvent(Identity.generateRandomIdentity().getPublicKey(), new ArrayList(), ZAP_REQUEST_CONTENT, AMOUNT, LNURL, RELAYS_TAG).getEvent(); + instance.update(); + + assertNotNull(instance.getId()); + assertNotNull(instance.getCreatedAt()); + assertNotNull(instance.getContent()); + assertNull(instance.getSignature()); + + assertNotNull(instance.getZapRequest()); + assertNotNull(instance.getZapRequest().getRelaysTag()); + assertNotNull(instance.getZapRequest().getAmount()); + assertNotNull(instance.getZapRequest().getLnUrl()); + + assertEquals(ZAP_REQUEST_CONTENT, instance.getContent()); + assertTrue(instance.getZapRequest().getRelaysTag().getRelays().stream() + .anyMatch(relay -> relay.getUri().equals(RELAYS_TAG))); + assertEquals(AMOUNT, instance.getZapRequest().getAmount()); + assertEquals(LNURL, instance.getZapRequest().getLnUrl()); + + final String bech32 = instance.toBech32(); + assertNotNull(bech32); + assertEquals(Bech32Prefix.NOTE.getCode(), Bech32.decode(bech32).hrp); + } + + @Test + void testNIP57CreateZapReceiptEvent() throws Exception { + System.out.println("testNIP57CreateZapReceiptEvent"); + + String zapRequestPubKeyTag = Identity.generateRandomIdentity().getPublicKey().toString(); + String zapRequestEventTag = Identity.generateRandomIdentity().getPublicKey().toString(); + String zapRequestAddressTag = Identity.generateRandomIdentity().getPublicKey().toString(); + final String ZAP_RECEIPT_IDENTIFIER = "ipsum"; + final String ZAP_RECEIPT_RELAY_URI = "ws://localhost:5555"; + final String BOLT_11 = "bolt11"; + final String DESCRIPTION_SHA256 = "descriptionSha256"; + final String PRE_IMAGE = "preimage"; + var nip57 = new NIP57(Identity.generateRandomIdentity()); + + ZapReceiptEvent instance = nip57.createZapReceiptEvent(zapRequestPubKeyTag, getBaseTags(), zapRequestEventTag, + zapRequestAddressTag, ZAP_RECEIPT_IDENTIFIER, ZAP_RECEIPT_RELAY_URI, BOLT_11, DESCRIPTION_SHA256, PRE_IMAGE) + .getEvent(); + instance.update(); + + assertNotNull(instance.getId()); + assertNotNull(instance.getCreatedAt()); + assertNull(instance.getSignature()); + + assertNotNull(instance.getZapReceipt()); + assertNotNull(instance.getZapReceipt().getBolt11()); + assertNotNull(instance.getZapReceipt().getDescriptionSha256()); + assertNotNull(instance.getZapReceipt().getPreimage()); + + assertEquals(BOLT_11, instance.getZapReceipt().getBolt11()); + assertEquals(DESCRIPTION_SHA256, instance.getZapReceipt().getDescriptionSha256()); + assertEquals(PRE_IMAGE, instance.getZapReceipt().getPreimage()); + + final String bech32 = instance.toBech32(); + assertNotNull(bech32); + assertEquals(Bech32Prefix.NOTE.getCode(), Bech32.decode(bech32).hrp); + } + + private static List getBaseTags() { + return new ArrayList(); + } + + public static Stall createStall() { + + // Create the county list + List countries = new ArrayList<>(); + countries.add("France"); + countries.add("Canada"); + countries.add("Cameroun"); + + // Create the shipping object + var shipping = new CreateOrUpdateStallEvent.Stall.Shipping(); + shipping.setCost(12.00f); + shipping.setCountries(countries); + shipping.setName("French Countries"); + + // Create the stall object + var stall = new CreateOrUpdateStallEvent.Stall(); + stall.setCurrency("USD"); + stall.setDescription("This is a test stall"); + stall.setName("Maximus Primus"); + stall.setShipping(shipping); + + return stall; + } + + public static NostrMarketplaceEvent.Product createProduct(Stall stall) { + + // Create the product + var product = new NostrMarketplaceEvent.Product(); + product.setCurrency("USD"); + product.setDescription("Un bijou en or"); + product.setImages(new ArrayList<>()); + product.setName("Bague"); + product.setPrice(450.00f); + product.setQuantity(4); + List specs = new ArrayList<>(); + specs.add(new Spec("couleur", "or")); + specs.add(new Spec("poids", "150g")); + product.setSpecs(specs); + product.setStall(stall); + + return product; + } +} diff --git a/nostr-java-api/src/test/java/nostr/api/integration/ApiEventTestUsingSpringWebSocketClientIT.java b/nostr-java-api/src/test/java/nostr/api/integration/ApiEventTestUsingSpringWebSocketClientIT.java new file mode 100644 index 000000000..b6b38c8da --- /dev/null +++ b/nostr-java-api/src/test/java/nostr/api/integration/ApiEventTestUsingSpringWebSocketClientIT.java @@ -0,0 +1,75 @@ +package nostr.api.integration; + +import lombok.SneakyThrows; +import nostr.api.NIP15; +import nostr.base.PrivateKey; +import nostr.client.springwebsocket.SpringWebSocketClient; +import nostr.config.RelayProperties; +import nostr.event.impl.GenericEvent; +import nostr.event.message.EventMessage; +import nostr.id.Identity; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static nostr.api.integration.ApiEventIT.createProduct; +import static nostr.api.integration.ApiEventIT.createStall; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SpringJUnitConfig(RelayProperties.class) +@ActiveProfiles("test") +class ApiEventTestUsingSpringWebSocketClientIT { + private final List springWebSocketClients; + + @Autowired + public ApiEventTestUsingSpringWebSocketClientIT(Map relays) { + this.springWebSocketClients = relays.values().stream().map(SpringWebSocketClient::new).toList(); + } + + @Test + void doForEach() { + springWebSocketClients.forEach(this::testNIP15SendProductEventUsingSpringWebSocketClient); + } + + @SneakyThrows + void testNIP15SendProductEventUsingSpringWebSocketClient(SpringWebSocketClient springWebSocketClient) { + System.out.println("testNIP15CreateProductEventUsingSpringWebSocketClient"); + var product = createProduct(createStall()); + + List categories = new ArrayList<>(); + categories.add("bijoux"); + categories.add("Hommes"); + + var nip15 = new NIP15<>(Identity.create(PrivateKey.generateRandomPrivKey())); + + GenericEvent event = nip15.createCreateOrUpdateProductEvent(product, categories).sign().getEvent(); + EventMessage message = new EventMessage(event); + + String eventResponse = springWebSocketClient.send(message).stream().findFirst().orElseThrow(); + + // Extract and compare only first 3 elements of the JSON array + var expectedArray = MAPPER_AFTERBURNER.readTree(expectedResponseJson(event.getId())).get(0).asText(); + var expectedSubscriptionId = MAPPER_AFTERBURNER.readTree(expectedResponseJson(event.getId())).get(1).asText(); + var expectedSuccess = MAPPER_AFTERBURNER.readTree(expectedResponseJson(event.getId())).get(2).asBoolean(); + + var actualArray = MAPPER_AFTERBURNER.readTree(eventResponse).get(0).asText(); + var actualSubscriptionId = MAPPER_AFTERBURNER.readTree(eventResponse).get(1).asText(); + var actualSuccess = MAPPER_AFTERBURNER.readTree(eventResponse).get(2).asBoolean(); + + assertEquals(expectedArray, actualArray, "First element should match"); + assertEquals(expectedSubscriptionId, actualSubscriptionId, "Subscription ID should match"); + assertEquals(expectedSuccess, actualSuccess, "Success flag should match"); + +// springWebSocketClient.closeSocket(); + } + + private String expectedResponseJson(String sha256) { + return "[\"OK\",\"" + sha256 + "\",true,\"success: request processed\"]"; + } +} diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52EventTest.java b/nostr-java-api/src/test/java/nostr/api/integration/ApiNIP52EventIT.java similarity index 79% rename from nostr-java-test/src/test/java/nostr/test/event/ApiNIP52EventTest.java rename to nostr-java-api/src/test/java/nostr/api/integration/ApiNIP52EventIT.java index fa0e7186b..a06da9197 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52EventTest.java +++ b/nostr-java-api/src/test/java/nostr/api/integration/ApiNIP52EventIT.java @@ -1,15 +1,7 @@ -package nostr.test.event; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.databind.ObjectMapper; +package nostr.api.integration; import nostr.api.NIP52; +import nostr.api.util.JsonComparator; import nostr.base.PrivateKey; import nostr.base.PublicKey; import nostr.client.springwebsocket.SpringWebSocketClient; @@ -20,13 +12,22 @@ import nostr.event.tag.IdentifierTag; import nostr.event.tag.PubKeyTag; import nostr.id.Identity; -import nostr.test.util.JsonComparator; +import org.junit.jupiter.api.Test; +import org.springframework.test.context.ActiveProfiles; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static nostr.base.IEvent.MAPPER_AFTERBURNER; +import static org.junit.jupiter.api.Assertions.assertTrue; -class ApiNIP52EventTest { +@ActiveProfiles("test") +class ApiNIP52EventIT { private static final String RELAY_URI = "ws://localhost:5555"; private final SpringWebSocketClient springWebSocketClient; - public ApiNIP52EventTest() { + public ApiNIP52EventIT() { springWebSocketClient = new SpringWebSocketClient(RELAY_URI); } @@ -47,24 +48,22 @@ void testNIP52CalendarTimeBasedEventEventUsingSpringWebSocketClient() throws IOE GenericEvent event = nip52.createCalendarTimeBasedEvent(tags, "content", createCalendarContent()).sign().getEvent(); EventMessage message = new EventMessage(event); - ObjectMapper mapper = new ObjectMapper(); + var expectedJson = MAPPER_AFTERBURNER.readTree(expectedResponseJson(event.getId())); + var actualJson = MAPPER_AFTERBURNER.readTree(springWebSocketClient.send(message).stream().findFirst().orElseThrow()); - var expectedJson = mapper.readTree(expectedResponseJson(event.getId())); - var actualJson = mapper.readTree(springWebSocketClient.send(message).stream().findFirst().get()); - // Compare only first 3 elements of the JSON arrays assertTrue( JsonComparator.isEquivalentJson( - mapper.createArrayNode() + MAPPER_AFTERBURNER.createArrayNode() .add(expectedJson.get(0)) // OK Command .add(expectedJson.get(1)) // event id .add(expectedJson.get(2)), // Accepted? - mapper.createArrayNode() + MAPPER_AFTERBURNER.createArrayNode() .add(actualJson.get(0)) .add(actualJson.get(1)) .add(actualJson.get(2)))); - springWebSocketClient.closeSocket(); +// springWebSocketClient.closeSocket(); } private String expectedResponseJson(String sha256) { diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java b/nostr-java-api/src/test/java/nostr/api/integration/ApiNIP52RequestIT.java similarity index 78% rename from nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java rename to nostr-java-api/src/test/java/nostr/api/integration/ApiNIP52RequestIT.java index 6baba40fa..c12167a27 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP52RequestTest.java +++ b/nostr-java-api/src/test/java/nostr/api/integration/ApiNIP52RequestIT.java @@ -1,7 +1,7 @@ -package nostr.test.event; +package nostr.api.integration; -import com.fasterxml.jackson.databind.ObjectMapper; import nostr.api.NIP52; +import nostr.api.util.JsonComparator; import nostr.base.PublicKey; import nostr.client.springwebsocket.SpringWebSocketClient; import nostr.event.BaseTag; @@ -16,20 +16,22 @@ import nostr.event.tag.PubKeyTag; import nostr.event.tag.ReferenceTag; import nostr.id.Identity; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.springframework.test.context.ActiveProfiles; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.List; +import java.util.UUID; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; import static org.junit.jupiter.api.Assertions.assertTrue; -class ApiNIP52RequestTest { +@ActiveProfiles("test") +class ApiNIP52RequestIT { private static final String PRV_KEY_VALUE = "23c011c4c02de9aa98d48c3646c70bb0e7ae30bdae1dfed4d251cbceadaeeb7b"; private static final String RELAY_URI = "ws://localhost:5555"; - private static final String SUBSCRIBER_ID = "ApiNIP52RequestTest-subscriber_001"; private static final String UUID_CALENDAR_TIME_BASED_EVENT_TEST = "UUID-CalendarTimeBasedEventTest"; public static final String ID = "299ab85049a7923e9cd82329c0fa489ca6fd6d21feeeac33543b1237e14a9e07"; @@ -76,7 +78,6 @@ class ApiNIP52RequestTest { public String eventPubKey; public String signature; - @Order(1) @Test void testNIP99CalendarContentPreRequest() throws IOException { System.out.println("testNIP52CalendarContentEvent"); @@ -85,13 +86,13 @@ void testNIP99CalendarContentPreRequest() throws IOException { tags.add(E_TAG); tags.add(P1_TAG); tags.add(P2_TAG); - tags.add(GenericTag.create(START_TZID_CODE, 52, START_TZID)); - tags.add(GenericTag.create(END_TZID_CODE, 52, END_TZID)); - tags.add(GenericTag.create(SUMMARY_CODE, 52, SUMMARY)); - tags.add(GenericTag.create(LABEL_CODE, 52, LABEL_1)); - tags.add(GenericTag.create(LABEL_CODE, 52, LABEL_2)); - tags.add(GenericTag.create(LOCATION_CODE, 52, LOCATION)); - tags.add(GenericTag.create(END_CODE, 52, END)); + tags.add(GenericTag.create(START_TZID_CODE, START_TZID)); + tags.add(GenericTag.create(END_TZID_CODE, END_TZID)); + tags.add(GenericTag.create(SUMMARY_CODE, SUMMARY)); + tags.add(GenericTag.create(LABEL_CODE, LABEL_1)); + tags.add(GenericTag.create(LABEL_CODE, LABEL_2)); + tags.add(GenericTag.create(LOCATION_CODE, LOCATION)); + tags.add(GenericTag.create(END_CODE, END)); tags.add(G_TAG); tags.add(T_TAG); tags.add(R_TAG); @@ -106,44 +107,45 @@ void testNIP99CalendarContentPreRequest() throws IOException { GenericEvent event = nip52.createCalendarTimeBasedEvent(tags, CALENDAR_CONTENT, calendarContent).sign().getEvent(); event.setCreatedAt(Long.valueOf(CREATED_AT)); + eventId = event.getId(); signature = event.getSignature().toString(); eventPubKey = event.getPubKey().toString(); EventMessage eventMessage = new EventMessage(event); SpringWebSocketClient springWebSocketEventClient = new SpringWebSocketClient(RELAY_URI); - String eventResponse = springWebSocketEventClient.send(eventMessage).stream().findFirst().get(); - - ObjectMapper mapper = new ObjectMapper(); + String eventResponse = springWebSocketEventClient.send(eventMessage).stream().findFirst().orElseThrow(); // Extract and compare only first 3 elements of the JSON array - var expectedArray = mapper.readTree(expectedEventResponseJson(event.getId())).get(0).asText(); - var expectedSubscriptionId = mapper.readTree(expectedEventResponseJson(event.getId())).get(1).asText(); - var expectedSuccess = mapper.readTree(expectedEventResponseJson(event.getId())).get(2).asBoolean(); + var expectedArray = MAPPER_AFTERBURNER.readTree(expectedEventResponseJson(event.getId())).get(0).asText(); + var expectedSubscriptionId = MAPPER_AFTERBURNER.readTree(expectedEventResponseJson(event.getId())).get(1).asText(); + var expectedSuccess = MAPPER_AFTERBURNER.readTree(expectedEventResponseJson(event.getId())).get(2).asBoolean(); - var actualArray = mapper.readTree(eventResponse).get(0).asText(); - var actualSubscriptionId = mapper.readTree(eventResponse).get(1).asText(); - var actualSuccess = mapper.readTree(eventResponse).get(2).asBoolean(); + var actualArray = MAPPER_AFTERBURNER.readTree(eventResponse).get(0).asText(); + var actualSubscriptionId = MAPPER_AFTERBURNER.readTree(eventResponse).get(1).asText(); + var actualSuccess = MAPPER_AFTERBURNER.readTree(eventResponse).get(2).asBoolean(); assertTrue(expectedArray.equals(actualArray), "First element should match"); assertTrue(expectedSubscriptionId.equals(actualSubscriptionId), "Subscription ID should match"); //assertTrue(expectedSuccess == actualSuccess, "Success flag should match"); -- This test is not required. The relay will always return false because we resending the same event, causing duplicates. - springWebSocketEventClient.closeSocket(); +// springWebSocketEventClient.closeSocket(); // TODO - This assertion fails with superdonductor and nostr-rs-relay -/* +///* SpringWebSocketClient springWebSocketRequestClient = new SpringWebSocketClient(RELAY_URI); - String reqJson = createReqJson(SUBSCRIBER_ID, eventId); - String reqResponse = springWebSocketRequestClient.send(reqJson).stream().findFirst().get(); + String subscriberId = UUID.randomUUID().toString(); + String reqJson = createReqJson(subscriberId, eventId); + String reqResponse = springWebSocketRequestClient.send(reqJson).stream().findFirst().orElseThrow(); + String expected = expectedRequestResponseJson(subscriberId); assertTrue( JsonComparator.isEquivalentJson( - mapper.readTree(expectedRequestResponseJson()), - mapper.readTree(reqResponse))); + MAPPER_AFTERBURNER.readTree(expected), + MAPPER_AFTERBURNER.readTree(reqResponse))); - springWebSocketRequestClient.closeSocket(); -*/ +// springWebSocketRequestClient.closeSocket(); +//*/ } private String expectedEventResponseJson(String subscriptionId) { @@ -154,15 +156,15 @@ private String createReqJson(String subscriberId, String id) { return "[\"REQ\",\"" + subscriberId + "\",{\"ids\":[\"" + id + "\"]}]"; } - private String expectedRequestResponseJson() { - return " [\"EVENT\",\"" + SUBSCRIBER_ID + "\",\n" + + private String expectedRequestResponseJson(String subscriberId) { + return " [\"EVENT\",\"" + subscriberId + "\",\n" + " {\"id\": \"" + eventId + "\",\n" + " \"kind\": " + KIND + ",\n" + " \"content\": \"" + CALENDAR_CONTENT + "\",\n" + " \"pubkey\": \"" + eventPubKey + "\",\n" + " \"created_at\": " + CREATED_AT + ",\n" + " \"tags\": [\n" + - " [ \"e\", \"" + E_TAG.getIdEvent() + "\", \"" + E_TAG.getMarker() + "\" ],\n" + + " [ \"e\", \"" + E_TAG.getIdEvent() + "\" ],\n" + " [ \"g\", \"" + G_TAG.getLocation() + "\" ],\n" + " [ \"t\", \"" + T_TAG.getHashTag() + "\" ],\n" + " [ \"d\", \"" + UUID_CALENDAR_TIME_BASED_EVENT_TEST + "\" ],\n" + diff --git a/nostr-java-api/src/test/java/nostr/api/integration/ApiNIP99EventIT.java b/nostr-java-api/src/test/java/nostr/api/integration/ApiNIP99EventIT.java new file mode 100644 index 000000000..04197533b --- /dev/null +++ b/nostr-java-api/src/test/java/nostr/api/integration/ApiNIP99EventIT.java @@ -0,0 +1,107 @@ +package nostr.api.integration; + +import nostr.api.NIP99; +import nostr.base.PrivateKey; +import nostr.base.PublicKey; +import nostr.client.springwebsocket.SpringWebSocketClient; +import nostr.event.BaseTag; +import nostr.event.impl.ClassifiedListing; +import nostr.event.impl.GenericEvent; +import nostr.event.impl.GenericTag; +import nostr.event.message.EventMessage; +import nostr.event.tag.EventTag; +import nostr.event.tag.GeohashTag; +import nostr.event.tag.HashtagTag; +import nostr.event.tag.PriceTag; +import nostr.event.tag.PubKeyTag; +import nostr.event.tag.SubjectTag; +import nostr.id.Identity; +import org.junit.jupiter.api.Test; +import org.springframework.test.context.ActiveProfiles; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import static nostr.base.IEvent.MAPPER_AFTERBURNER; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ActiveProfiles("test") +class ApiNIP99EventIT { + private static final String RELAY_URI = "ws://localhost:5555"; + public static final String CLASSIFIED_LISTING_CONTENT = "classified listing content"; + + public static final String PTAG_HEX = "2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76985"; + public static final String ETAG_HEX = "494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4347"; + + public static final PubKeyTag P_TAG = new PubKeyTag(new PublicKey(PTAG_HEX)); + public static final EventTag E_TAG = new EventTag(ETAG_HEX); + + public static final String SUBJECT = "Classified Listing Test Subject Tag"; + public static final SubjectTag SUBJECT_TAG = new SubjectTag(SUBJECT); + public static final GeohashTag G_TAG = new GeohashTag("Classified Listing Test Geohash Tag"); + public static final HashtagTag T_TAG = new HashtagTag("Classified Listing Test Hashtag Tag"); + + public static final BigDecimal NUMBER = new BigDecimal("2.71"); + public static final String FREQUENCY = "NANOSECOND"; + public static final String CURRENCY = "BTC"; + + public static final String CLASSIFIED_LISTING_PUBLISHED_AT = "1687765220"; + public static final String CLASSIFIED_LISTING_LOCATION = "classified listing location"; + public static final String TITLE_CODE = "title"; + public static final String SUMMARY_CODE = "summary"; + public static final String PUBLISHED_AT_CODE = "published_at"; + public static final String LOCATION_CODE = "location"; + private final SpringWebSocketClient springWebSocketClient; + + public ApiNIP99EventIT() { + springWebSocketClient = new SpringWebSocketClient(RELAY_URI); + } + + @Test + void testNIP99ClassifiedListingEvent() throws IOException { + System.out.println("testNIP99ClassifiedListingEvent"); + + List tags = new ArrayList<>(); + tags.add(E_TAG); + tags.add(P_TAG); + tags.add(GenericTag.create(PUBLISHED_AT_CODE, CLASSIFIED_LISTING_PUBLISHED_AT)); + tags.add(GenericTag.create(LOCATION_CODE, CLASSIFIED_LISTING_LOCATION)); + tags.add(SUBJECT_TAG); + tags.add(G_TAG); + tags.add(T_TAG); + + PriceTag priceTag = new PriceTag(NUMBER, CURRENCY, FREQUENCY); + ClassifiedListing classifiedListing = ClassifiedListing.builder( + TITLE_CODE, + SUMMARY_CODE, + priceTag) + .build(); + + var nip99 = new NIP99<>(Identity.create(PrivateKey.generateRandomPrivKey())); + + GenericEvent event = nip99.createClassifiedListingEvent(tags, CLASSIFIED_LISTING_CONTENT, classifiedListing).sign().getEvent(); + EventMessage message = new EventMessage(event); + + // Extract and compare only first 3 elements of the JSON array + var expectedArray = MAPPER_AFTERBURNER.readTree(expectedResponseJson(event.getId())).get(0).asText(); + var expectedSubscriptionId = MAPPER_AFTERBURNER.readTree(expectedResponseJson(event.getId())).get(1).asText(); + var expectedSuccess = MAPPER_AFTERBURNER.readTree(expectedResponseJson(event.getId())).get(2).asBoolean(); + + String eventResponse = springWebSocketClient.send(message).stream().findFirst().get(); + var actualArray = MAPPER_AFTERBURNER.readTree(eventResponse).get(0).asText(); + var actualSubscriptionId = MAPPER_AFTERBURNER.readTree(eventResponse).get(1).asText(); + var actualSuccess = MAPPER_AFTERBURNER.readTree(eventResponse).get(2).asBoolean(); + + assertTrue(expectedArray.equals(actualArray), "First element should match"); + assertTrue(expectedSubscriptionId.equals(actualSubscriptionId), "Subscription ID should match"); + assertTrue(expectedSuccess == actualSuccess, "Success flag should match"); + +// springWebSocketClient.closeSocket(); + } + + private String expectedResponseJson(String sha256) { + return "[\"OK\",\"" + sha256 + "\",true,\"success: request processed\"]"; + } +} diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java b/nostr-java-api/src/test/java/nostr/api/integration/ApiNIP99RequestIT.java similarity index 74% rename from nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java rename to nostr-java-api/src/test/java/nostr/api/integration/ApiNIP99RequestIT.java index 10b9017b0..70c5255f1 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99RequestTest.java +++ b/nostr-java-api/src/test/java/nostr/api/integration/ApiNIP99RequestIT.java @@ -1,7 +1,6 @@ -package nostr.test.event; +package nostr.api.integration; 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; @@ -17,22 +16,25 @@ 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 org.springframework.test.context.ActiveProfiles; import java.io.IOException; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.UUID; -import static nostr.test.event.ClassifiedListingEventTest.LOCATION_CODE; -import static nostr.test.event.ClassifiedListingEventTest.PUBLISHED_AT_CODE; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -class ApiNIP99RequestTest { +@ActiveProfiles("test") +class ApiNIP99RequestIT { private static final String PRV_KEY_VALUE = "23c011c4c02de9aa98d48c3646c70bb0e7ae30bdae1dfed4d251cbceadaeeb7b"; private static final String RELAY_URI = "ws://localhost:5555"; - private static final String SUBSCRIBER_ID = "ApiNIP99RequestTest-subscriber_001"; + public static final String PUBLISHED_AT_CODE = "published_at"; + public static final String LOCATION_CODE = "location"; public static final String ID = "299ab85049a7923e9cd82329c0fa489ca6fd6d21feeeac33543b1237e14a9e07"; public static final String KIND = "30402"; @@ -64,7 +66,6 @@ class ApiNIP99RequestTest { public String eventPubKey; public String signature; - @Order(1) @Test void testNIP99ClassifiedListingPreRequest() throws IOException { System.out.println("testNIP99ClassifiedListingEvent"); @@ -72,8 +73,8 @@ void testNIP99ClassifiedListingPreRequest() throws IOException { List tags = new ArrayList<>(); tags.add(E_TAG); tags.add(P_TAG); - tags.add(GenericTag.create(PUBLISHED_AT_CODE, 99, CREATED_AT)); - tags.add(GenericTag.create(LOCATION_CODE, 99, LOCATION)); + tags.add(GenericTag.create(PUBLISHED_AT_CODE, CREATED_AT)); + tags.add(GenericTag.create(LOCATION_CODE, LOCATION)); tags.add(SUBJECT_TAG); tags.add(G_TAG); tags.add(T_TAG); @@ -97,41 +98,39 @@ void testNIP99ClassifiedListingPreRequest() throws IOException { SpringWebSocketClient springWebSocketEventClient = new SpringWebSocketClient(RELAY_URI); List eventResponses = springWebSocketEventClient.send(eventMessage); - assertTrue(eventResponses.size() == 1, "Expected 1 event response, but got " + eventResponses.size()); - - ObjectMapper mapper = new ObjectMapper(); + assertEquals(1, eventResponses.size(), "Expected 1 event response, but got " + eventResponses.size()); // Extract and compare only first 3 elements of the JSON array - var expectedArray = mapper.readTree(expectedEventResponseJson(event.getId())).get(0).asText(); - var expectedSubscriptionId = mapper.readTree(expectedEventResponseJson(event.getId())).get(1).asText(); - var expectedSuccess = mapper.readTree(expectedEventResponseJson(event.getId())).get(2).asBoolean(); + var expectedArray = MAPPER_AFTERBURNER.readTree(expectedEventResponseJson(event.getId())).get(0).asText(); + var expectedSubscriptionId = MAPPER_AFTERBURNER.readTree(expectedEventResponseJson(event.getId())).get(1).asText(); + var expectedSuccess = MAPPER_AFTERBURNER.readTree(expectedEventResponseJson(event.getId())).get(2).asBoolean(); - var actualArray = mapper.readTree(eventResponses.get(0)).get(0).asText(); - var actualSubscriptionId = mapper.readTree(eventResponses.get(0)).get(1).asText(); - var actualSuccess = mapper.readTree(eventResponses.get(0)).get(2).asBoolean(); + var actualArray = MAPPER_AFTERBURNER.readTree(eventResponses.getFirst()).get(0).asText(); + var actualSubscriptionId = MAPPER_AFTERBURNER.readTree(eventResponses.getFirst()).get(1).asText(); + var actualSuccess = MAPPER_AFTERBURNER.readTree(eventResponses.getFirst()).get(2).asBoolean(); - assertTrue(expectedArray.equals(actualArray), "First element should match"); - assertTrue(expectedSubscriptionId.equals(actualSubscriptionId), "Subscription ID should match"); - assertTrue(expectedSuccess == actualSuccess, "Success flag should match"); + assertEquals(expectedArray, actualArray, "First element should match"); + assertEquals(expectedSubscriptionId, actualSubscriptionId, "Subscription ID should match"); + assertEquals(expectedSuccess, actualSuccess, "Success flag should match"); - springWebSocketEventClient.closeSocket(); +// springWebSocketEventClient.closeSocket(); // TODO - Investigate why EOSE, instead of EVENT, is returned from nostr-rs-relay, and not superconductor -/* +///* SpringWebSocketClient springWebSocketRequestClient = new SpringWebSocketClient(RELAY_URI); - String reqJson = createReqJson(SUBSCRIBER_ID, eventId); + String reqJson = createReqJson(UUID.randomUUID().toString(), eventId); List reqResponses = springWebSocketRequestClient.send(reqJson).stream().toList(); - springWebSocketRequestClient.closeSocket(); +// springWebSocketRequestClient.closeSocket(); - var actualJson = mapper.readTree(reqResponses.getFirst()); - var expectedJson = mapper.readTree(expectedRequestResponseJson()); + var actualJson = MAPPER_AFTERBURNER.readTree(reqResponses.getFirst()); + var expectedJson = MAPPER_AFTERBURNER.readTree(expectedRequestResponseJson()); // Verify you receive the event - assertTrue(actualJson.get(0).asText().equals("EVENT"), "Event should be received, and not " + actualJson.get(0).asText()); + assertEquals("EVENT", actualJson.get(0).asText(), "Event should be received, and not " + actualJson.get(0).asText()); // Verify only required fields - assertTrue(actualJson.size() == 3, "Expected 3 elements in the array, but got " + actualJson.size()); + assertEquals(3, actualJson.size(), "Expected 3 elements in the array, but got " + actualJson.size()); assertTrue(actualJson.get(2).get("id").asText().equals(expectedJson.get(2).get("id").asText()), "ID should match"); assertTrue(actualJson.get(2).get("kind").asInt() == expectedJson.get(2).get("kind").asInt(), "Kind should match"); @@ -140,7 +139,7 @@ void testNIP99ClassifiedListingPreRequest() throws IOException { assertTrue(hasRequiredTag(actualTags, "price", NUMBER.toString()), "Price tag should be present"); assertTrue(hasRequiredTag(actualTags, "title", TITLE), "Title tag should be present"); assertTrue(hasRequiredTag(actualTags, "summary", SUMMARY), "Summary tag should be present"); -*/ +//*/ } private String expectedEventResponseJson(String subscriptionId) { diff --git a/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java b/nostr-java-api/src/test/java/nostr/api/integration/ZDoLastApiNIP09EventIT.java similarity index 66% rename from nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java rename to nostr-java-api/src/test/java/nostr/api/integration/ZDoLastApiNIP09EventIT.java index f44849bbf..95857593d 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/APINIP09EventTest.java +++ b/nostr-java-api/src/test/java/nostr/api/integration/ZDoLastApiNIP09EventIT.java @@ -1,8 +1,9 @@ -package nostr.test.event; +package nostr.api.integration; import nostr.api.NIP01; import nostr.api.NIP09; import nostr.base.Relay; +import nostr.config.RelayProperties; import nostr.event.BaseMessage; import nostr.event.BaseTag; import nostr.event.Kind; @@ -18,6 +19,9 @@ import nostr.event.tag.IdentifierTag; import nostr.id.Identity; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import java.io.IOException; import java.util.List; @@ -25,14 +29,13 @@ import java.util.UUID; import java.util.stream.Collectors; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; -public class APINIP09EventTest { - - private static final String RELAY_URI = "ws://localhost:5555"; +@SpringJUnitConfig(RelayProperties.class) +@ActiveProfiles("test") +public class ZDoLastApiNIP09EventIT { + @Autowired + private Map relays; @Test public void deleteEvent() throws IOException { @@ -41,35 +44,36 @@ public void deleteEvent() throws IOException { NIP09 nip09 = new NIP09<>(identity); NIP01 nip01 = new NIP01<>(identity); - nip01.createTextNoteEvent("Delete me!").signAndSend(Map.of("local", RELAY_URI)); + nip01.createTextNoteEvent("Delete me!").signAndSend(relays); 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()); assertFalse(result.isEmpty()); assertEquals(2, result.size()); - nip09.createDeletionEvent(nip01.getEvent()).signAndSend(Map.of("local", RELAY_URI)); + nip09.createDeletionEvent(nip01.getEvent()).signAndSend(relays); result = nip01.sendRequest(filters, UUID.randomUUID().toString()); assertFalse(result.isEmpty()); assertEquals(1, result.size()); - nip01.close(); - nip09.close(); +// nip01.close(); +// nip09.close(); } @Test public void deleteEventWithRef() throws IOException { + final String RELAY_URI = "ws://localhost:5555"; Identity identity = Identity.generateRandomIdentity(); NIP01 nip011 = new NIP01<>(identity); - BaseMessage replaceableMessage = nip011.createReplaceableEvent(10_001, "replaceable event").signAndSend(Map.of("local", RELAY_URI)); + BaseMessage replaceableMessage = nip011.createReplaceableEvent(10_001, "replaceable event").signAndSend(relays); assertNotNull(replaceableMessage); assertTrue(replaceableMessage instanceof OkMessage); @@ -79,11 +83,11 @@ 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)); + BaseMessage message = nip01.signAndSend(relays); assertNotNull(message); assertTrue(message instanceof OkMessage); @@ -96,9 +100,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 +110,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,16 +122,16 @@ 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()); - nip09.signAndSend(Map.of("local", RELAY_URI)); + nip09.signAndSend(relays); - nip01.close(); - nip011.close(); - nip09.close(); +// nip01.close(); +// nip011.close(); +// nip09.close(); } } diff --git a/nostr-java-test/src/test/java/nostr/test/event/CalendarTimeBasedEventTest.java b/nostr-java-api/src/test/java/nostr/api/unit/CalendarTimeBasedEventTest.java similarity index 63% rename from nostr-java-test/src/test/java/nostr/test/event/CalendarTimeBasedEventTest.java rename to nostr-java-api/src/test/java/nostr/api/unit/CalendarTimeBasedEventTest.java index e35652e07..9a2266c30 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/CalendarTimeBasedEventTest.java +++ b/nostr-java-api/src/test/java/nostr/api/unit/CalendarTimeBasedEventTest.java @@ -1,8 +1,7 @@ -package nostr.test.event; +package nostr.api.unit; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import nostr.api.NIP52; import nostr.base.PublicKey; import nostr.base.Signature; @@ -28,6 +27,7 @@ import java.util.List; import java.util.function.BiFunction; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; import static org.junit.jupiter.api.Assertions.assertTrue; @TestInstance(TestInstance.Lifecycle.PER_CLASS) @@ -73,16 +73,16 @@ void setup() throws URISyntaxException { List tags = new ArrayList<>(); tags.add(P_1_TAG); tags.add(P_2_TAG); - tags.add(GenericTag.create(LOCATION_CODE, 52, CALENDAR_TIME_BASED_EVENT_LOCATION)); + tags.add(GenericTag.create(LOCATION_CODE, CALENDAR_TIME_BASED_EVENT_LOCATION)); tags.add(SUBJECT_TAG); tags.add(G_TAG); tags.add(T_TAG); - tags.add(GenericTag.create(START_TZID_CODE, 52, CALENDAR_TIME_BASED_EVENT_START_TZID)); + tags.add(GenericTag.create(START_TZID_CODE, CALENDAR_TIME_BASED_EVENT_START_TZID)); Long l = START + 100L; - tags.add(GenericTag.create(END_CODE, 52, l.toString())); + tags.add(GenericTag.create(END_CODE, l.toString())); CalendarContent calendarContent = CalendarContent.builder(identifierTag, CALENDAR_TIME_BASED_EVENT_TITLE, START) - .build(); + .build(); // a random set of calendar tags // calendarContent.setEndTzid(CALENDAR_TIME_BASED_EVENT_END_TZID); calendarContent.setSummary(CALENDAR_TIME_BASED_EVENT_SUMMARY); @@ -90,40 +90,39 @@ void setup() throws URISyntaxException { // calendarContent.setReferenceTags(List.of(new ReferenceTag(uri))); instance = new NIP52<>(identity) - .createCalendarTimeBasedEvent(tags, CALENDAR_TIME_BASED_EVENT_CONTENT, calendarContent).getEvent(); + .createCalendarTimeBasedEvent(tags, CALENDAR_TIME_BASED_EVENT_CONTENT, calendarContent).getEvent(); signature = identity.sign(instance); instance.setSignature(signature); expectedEncodedJson = "{" - + "\"id\":\"" + instance.getId() + "\"," - + "\"kind\":31923," - + "\"content\":\"calendar Time-Based Event content\"," - + "\"pubkey\":\"" + senderPubkey + "\"," - + "\"created_at\":" + instance.getCreatedAt() + "," - + "\"tags\":[" - + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76985\",\"http://some.url\",\"ISSUER\"]," - + "[\"p\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4347\",\"http://some.url\",\"COUNTERPARTY\"]," - + "[\"location\",\"Calendar Time-Based Event location\"]," - + "[\"subject\",\"Calendar Time-Based Event Test Subject Tag\"]," - + "[\"g\",\"Calendar Time-Based Event Test Geohash Tag\"]," - + "[\"t\",\"Calendar Time-Based Event Test Hashtag Tag\"]," - + "[\"start_tzid\",\"1687765220\"]," - + "[\"end\",\"1716513986368\"]," - + "[\"d\",\"UUID-CalendarTimeBasedEventTest\"]," - + "[\"title\",\"Calendar Time-Based Event title\"]," - + "[\"start\",\"1716513986268\"]," - + "[\"end_tzid\",\"1687765220\"]," - + "[\"summary\",\"Calendar Time-Based Event summary\"]," - + "[\"r\",\"http://some.url\"]]," - + "\"sig\":\"" + signature.toString() + "\"" - + "}"; + + "\"id\":\"" + instance.getId() + "\"," + + "\"kind\":31923," + + "\"content\":\"calendar Time-Based Event content\"," + + "\"pubkey\":\"" + senderPubkey + "\"," + + "\"created_at\":" + instance.getCreatedAt() + "," + + "\"tags\":[" + + "[\"p\",\"2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76985\",\"http://some.url\",\"ISSUER\"]," + + "[\"p\",\"494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4347\",\"http://some.url\",\"COUNTERPARTY\"]," + + "[\"location\",\"Calendar Time-Based Event location\"]," + + "[\"subject\",\"Calendar Time-Based Event Test Subject Tag\"]," + + "[\"g\",\"Calendar Time-Based Event Test Geohash Tag\"]," + + "[\"t\",\"Calendar Time-Based Event Test Hashtag Tag\"]," + + "[\"start_tzid\",\"1687765220\"]," + + "[\"end\",\"1716513986368\"]," + + "[\"d\",\"UUID-CalendarTimeBasedEventTest\"]," + + "[\"title\",\"Calendar Time-Based Event title\"]," + + "[\"start\",\"1716513986268\"]," + + "[\"end_tzid\",\"1687765220\"]," + + "[\"summary\",\"Calendar Time-Based Event summary\"]," + + "[\"r\",\"http://some.url\"]]," + + "\"sig\":\"" + signature.toString() + "\"" + + "}"; } @Test void testCalendarTimeBasedEventEncoding() throws JsonProcessingException { - ObjectMapper mapper = new ObjectMapper(); - var instanceJson = mapper.readTree(new BaseEventEncoder<>(instance).encode()); - var expectedJson = mapper.readTree(expectedEncodedJson); + var instanceJson = MAPPER_AFTERBURNER.readTree(new BaseEventEncoder<>(instance).encode()); + var expectedJson = MAPPER_AFTERBURNER.readTree(expectedEncodedJson); // Helper function to find tag value BiFunction findTagArray = (tags, tagName) -> { @@ -137,21 +136,20 @@ void testCalendarTimeBasedEventEncoding() throws JsonProcessingException { // Verify required fields match assertTrue(findTagArray.apply(instanceJson.get("tags"), "d").get(1).asText() - .equals(findTagArray.apply(expectedJson.get("tags"), "d").get(1).asText())); + .equals(findTagArray.apply(expectedJson.get("tags"), "d").get(1).asText())); assertTrue(findTagArray.apply(instanceJson.get("tags"), "title").get(1).asText() - .equals(findTagArray.apply(expectedJson.get("tags"), "title").get(1).asText())); + .equals(findTagArray.apply(expectedJson.get("tags"), "title").get(1).asText())); assertTrue(findTagArray.apply(instanceJson.get("tags"), "start").get(1).asText() - .equals(findTagArray.apply(expectedJson.get("tags"), "start").get(1).asText())); + .equals(findTagArray.apply(expectedJson.get("tags"), "start").get(1).asText())); } @Test void testCalendarTimeBasedEventDecoding() throws JsonProcessingException { - ObjectMapper mapper = new ObjectMapper(); - var decodedJson = mapper.readTree( - new BaseEventEncoder<>( - mapper.readValue(expectedEncodedJson, GenericEvent.class)) - .encode()); - var instanceJson = mapper.readTree(new BaseEventEncoder<>(instance).encode()); + var decodedJson = MAPPER_AFTERBURNER.readTree( + new BaseEventEncoder<>( + MAPPER_AFTERBURNER.readValue(expectedEncodedJson, GenericEvent.class)) + .encode()); + var instanceJson = MAPPER_AFTERBURNER.readTree(new BaseEventEncoder<>(instance).encode()); // Helper function to find tag value BiFunction findTagArray = (tags, tagName) -> { @@ -168,10 +166,10 @@ void testCalendarTimeBasedEventDecoding() throws JsonProcessingException { var instanceTags = instanceJson.get("tags"); assertTrue(findTagArray.apply(decodedTags, "d").get(1).asText() - .equals(findTagArray.apply(instanceTags, "d").get(1).asText())); + .equals(findTagArray.apply(instanceTags, "d").get(1).asText())); assertTrue(findTagArray.apply(decodedTags, "title").get(1).asText() - .equals(findTagArray.apply(instanceTags, "title").get(1).asText())); + .equals(findTagArray.apply(instanceTags, "title").get(1).asText())); assertTrue(findTagArray.apply(decodedTags, "start").get(1).asText() - .equals(findTagArray.apply(instanceTags, "start").get(1).asText())); + .equals(findTagArray.apply(instanceTags, "start").get(1).asText())); } } diff --git a/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java b/nostr-java-api/src/test/java/nostr/api/unit/JsonParseTest.java similarity index 97% rename from nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java rename to nostr-java-api/src/test/java/nostr/api/unit/JsonParseTest.java index 1041ac5e1..ccfff117e 100644 --- a/nostr-java-test/src/test/java/nostr/test/json/JsonParseTest.java +++ b/nostr-java-api/src/test/java/nostr/api/unit/JsonParseTest.java @@ -1,9 +1,9 @@ -package nostr.test.json; +package nostr.api.unit; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.java.Log; import nostr.api.NIP01; +import nostr.api.util.JsonComparator; import nostr.base.Command; import nostr.base.ElementAttribute; import nostr.base.GenericTagQuery; @@ -43,27 +43,19 @@ 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.List; -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; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; +import static org.junit.jupiter.api.Assertions.*; /** * @author eric */ @Log public class JsonParseTest { - ObjectMapper mapper = new ObjectMapper(); - @Test public void testBaseMessageDecoderEventFilter() throws JsonProcessingException { log.info("testBaseMessageDecoderEventFilter"); @@ -334,7 +326,7 @@ public void testClassifiedListingTagSerializer() throws JsonProcessingException } @Test - public void testDeserializeTag() throws NostrException { + public void testDeserializeTag() throws Exception { log.info("testDeserializeTag"); String npubHex = new PublicKey(Bech32.fromBech32("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9")).toString(); @@ -350,7 +342,7 @@ public void testDeserializeTag() throws NostrException { } @Test - public void testDeserializeGenericTag() throws NostrException { + public void testDeserializeGenericTag() throws Exception { log.info("testDeserializeGenericTag"); String npubHex = new PublicKey(Bech32.fromBech32("npub1clk6vc9xhjp8q5cws262wuf2eh4zuvwupft03hy4ttqqnm7e0jrq3upup9")).toString(); final String jsonString = "[\"gt\", \"" + npubHex + "\", \"wss://nostr.java\", \"alice\"]"; @@ -653,11 +645,11 @@ public void testGenericTagQueryListDecoder() throws JsonProcessingException { new IdentifierTagFilter<>(new IdentifierTag(uuidValue2)))); assertTrue(JsonComparator.isEquivalentJson( - mapper.createArrayNode() - .add(mapper.readTree( + MAPPER_AFTERBURNER.createArrayNode() + .add(MAPPER_AFTERBURNER.readTree( expectedReqMessage.encode())), - mapper.createArrayNode() - .add(mapper.readTree(decodedReqMessage.encode())))); + MAPPER_AFTERBURNER.createArrayNode() + .add(MAPPER_AFTERBURNER.readTree(decodedReqMessage.encode())))); assertEquals(expectedReqMessage, decodedReqMessage); } diff --git a/nostr-java-test/src/test/java/nostr/test/event/NIP52ImplTest.java b/nostr-java-api/src/test/java/nostr/api/unit/NIP52ImplTest.java similarity index 95% rename from nostr-java-test/src/test/java/nostr/test/event/NIP52ImplTest.java rename to nostr-java-api/src/test/java/nostr/api/unit/NIP52ImplTest.java index e4400f76f..034148118 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/NIP52ImplTest.java +++ b/nostr-java-api/src/test/java/nostr/api/unit/NIP52ImplTest.java @@ -1,13 +1,4 @@ -package nostr.test.event; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +package nostr.api.unit; import nostr.api.NIP52; import nostr.base.PublicKey; @@ -21,6 +12,13 @@ import nostr.event.tag.PubKeyTag; import nostr.event.tag.SubjectTag; import nostr.id.Identity; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; class NIP52ImplTest { public static final String TIME_BASED_EVENT_CONTENT = "CalendarTimeBasedEvent unit test content"; @@ -104,6 +102,6 @@ void testNIP52CreateTimeBasedCalendarCalendarEventWithAllOptionalParameters() { } private GenericTag containsGeneric(String key, String value) { - return GenericTag.create(key, 52, value); + return GenericTag.create(key, value); } } diff --git a/nostr-java-test/src/test/java/nostr/test/event/NIP57ImplTest.java b/nostr-java-api/src/test/java/nostr/api/unit/NIP57ImplTest.java similarity index 96% rename from nostr-java-test/src/test/java/nostr/test/event/NIP57ImplTest.java rename to nostr-java-api/src/test/java/nostr/api/unit/NIP57ImplTest.java index a69874a69..af0dd2eb8 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/NIP57ImplTest.java +++ b/nostr-java-api/src/test/java/nostr/api/unit/NIP57ImplTest.java @@ -1,4 +1,4 @@ -package nostr.test.event; +package nostr.api.unit; import lombok.extern.java.Log; import nostr.api.factory.impl.NIP57Impl.ZapRequestEventFactory; @@ -43,4 +43,4 @@ void testNIP57CreateZapRequestEventFactory() { assertEquals(AMOUNT, instance.getZapRequest().getAmount()); } -} \ No newline at end of file +} diff --git a/nostr-java-test/src/test/java/nostr/test/api/NIP60Test.java b/nostr-java-api/src/test/java/nostr/api/unit/NIP60Test.java similarity index 93% rename from nostr-java-test/src/test/java/nostr/test/api/NIP60Test.java rename to nostr-java-api/src/test/java/nostr/api/unit/NIP60Test.java index db697af1e..d24fc641c 100644 --- a/nostr-java-test/src/test/java/nostr/test/api/NIP60Test.java +++ b/nostr-java-api/src/test/java/nostr/api/unit/NIP60Test.java @@ -1,15 +1,6 @@ -package nostr.test.api; - -import java.util.Arrays; -import java.util.List; -import java.util.Set; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +package nostr.api.unit; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - import lombok.NonNull; import nostr.api.NIP44; import nostr.api.NIP60; @@ -26,6 +17,14 @@ import nostr.event.tag.AddressTag; import nostr.event.tag.EventTag; import nostr.id.Identity; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import static nostr.base.IEvent.MAPPER_AFTERBURNER; public class NIP60Test { @@ -70,22 +69,21 @@ public void createWalletEvent() throws JsonProcessingException { // Assert relay tags List relayTags = tags.stream() - .filter(tag -> tag.getCode().equals("relay")) - .toList(); + .filter(tag -> tag.getCode().equals("relay")) + .toList(); Assertions.assertEquals(2, relayTags.size()); // Assert mint tags List mintTags = tags.stream() - .filter(tag -> tag.getCode().equals("mint")) - .toList(); + .filter(tag -> tag.getCode().equals("mint")) + .toList(); Assertions.assertEquals(3, mintTags.size()); // Decrypt and verify content String decryptedContent = NIP44.decrypt(sender, event.getContent(), sender.getPublicKey()); - ObjectMapper mapper = new ObjectMapper(); - GenericTag[] contentTags = mapper.readValue(decryptedContent, GenericTag[].class); + GenericTag[] contentTags = MAPPER_AFTERBURNER.readValue(decryptedContent, GenericTag[].class); // First tag should be balance Assertions.assertEquals("balance", contentTags[0].getCode()); @@ -145,8 +143,7 @@ public void createTokenEvent() throws JsonProcessingException { // Decrypt and verify content String decryptedContent = NIP44.decrypt(sender, event.getContent(), sender.getPublicKey()); - ObjectMapper mapper = new ObjectMapper(); - Token contentToken = mapper.readValue(decryptedContent, Token.class); + Token contentToken = MAPPER_AFTERBURNER.readValue(decryptedContent, Token.class); Assertions.assertEquals("https://stablenut.umint.cash", contentToken.getMint().getUrl()); Proof proofContent = contentToken.getProofs().get(0); @@ -198,14 +195,13 @@ public void createSpendingHistoryEvent() throws JsonProcessingException { // Decrypt and verify content String decryptedContent = NIP44.decrypt(sender, event.getContent(), sender.getPublicKey()); - ObjectMapper mapper = new ObjectMapper(); - BaseTag[] contentTags = mapper.readValue(decryptedContent, BaseTag[].class); + BaseTag[] contentTags = MAPPER_AFTERBURNER.readValue(decryptedContent, BaseTag[].class); // Assert direction GenericTag directionTag = (GenericTag) contentTags[0]; Assertions.assertEquals("direction", directionTag.getCode()); Assertions.assertEquals("in", - directionTag.getAttributes().get(0).getValue().toString()); + directionTag.getAttributes().get(0).getValue().toString()); // Assert amount GenericTag amountTag = (GenericTag) contentTags[1]; diff --git a/nostr-java-test/src/test/java/nostr/test/api/NIP61Test.java b/nostr-java-api/src/test/java/nostr/api/unit/NIP61Test.java similarity index 99% rename from nostr-java-test/src/test/java/nostr/test/api/NIP61Test.java rename to nostr-java-api/src/test/java/nostr/api/unit/NIP61Test.java index 79ddabf46..c30e826f8 100644 --- a/nostr-java-test/src/test/java/nostr/test/api/NIP61Test.java +++ b/nostr-java-api/src/test/java/nostr/api/unit/NIP61Test.java @@ -1,4 +1,4 @@ -package nostr.test.api; +package nostr.api.unit; import java.util.Arrays; import java.util.List; diff --git a/nostr-java-test/src/test/java/nostr/test/event/NIP99ImplTest.java b/nostr-java-api/src/test/java/nostr/api/unit/NIP99ImplTest.java similarity index 96% rename from nostr-java-test/src/test/java/nostr/test/event/NIP99ImplTest.java rename to nostr-java-api/src/test/java/nostr/api/unit/NIP99ImplTest.java index 017c97b54..284e06766 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/NIP99ImplTest.java +++ b/nostr-java-api/src/test/java/nostr/api/unit/NIP99ImplTest.java @@ -1,4 +1,4 @@ -package nostr.test.event; +package nostr.api.unit; import nostr.api.NIP99; import nostr.event.BaseTag; @@ -96,7 +96,7 @@ void testNIP99CreateClassifiedListingEventWithDuplicateParameters() { classifiedListing.setLocation(LOCATION); classifiedListing.setPublishedAt(PUBLISHED_AT); - baseTags.add(GenericTag.create("published_at", 99, String.valueOf(PUBLISHED_AT))); + baseTags.add(GenericTag.create("published_at", String.valueOf(PUBLISHED_AT))); ClassifiedListingEvent instance = nip99.createClassifiedListingEvent(baseTags, CONTENT, classifiedListing).getEvent(); instance.update(); @@ -114,6 +114,6 @@ void testNIP99CreateClassifiedListingEventNullParameters() { } private GenericTag containsGeneric(String key, String value) { - return GenericTag.create(key, 99, value); + return GenericTag.create(key, value); } } diff --git a/nostr-java-test/src/test/java/nostr/test/util/JsonComparator.java b/nostr-java-api/src/test/java/nostr/api/util/JsonComparator.java similarity index 99% rename from nostr-java-test/src/test/java/nostr/test/util/JsonComparator.java rename to nostr-java-api/src/test/java/nostr/api/util/JsonComparator.java index 7758fee66..01d33435b 100644 --- a/nostr-java-test/src/test/java/nostr/test/util/JsonComparator.java +++ b/nostr-java-api/src/test/java/nostr/api/util/JsonComparator.java @@ -1,4 +1,4 @@ -package nostr.test.util; +package nostr.api.util; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; diff --git a/nostr-java-api/src/test/resources/application-test.properties b/nostr-java-api/src/test/resources/application-test.properties new file mode 100644 index 000000000..5c1a515fb --- /dev/null +++ b/nostr-java-api/src/test/resources/application-test.properties @@ -0,0 +1,4 @@ +spring.threads.virtual.enabled=true + +logging.level.nostr.api=INFO +logging.pattern.console=%msg%n diff --git a/nostr-java-api/src/test/resources/junit-platform.properties b/nostr-java-api/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..beae0cf29 --- /dev/null +++ b/nostr-java-api/src/test/resources/junit-platform.properties @@ -0,0 +1,9 @@ +# junit-platform.properties + +junit.jupiter.execution.parallel.enabled=true +junit.jupiter.execution.parallel.config.strategy=dynamic +junit.jupiter.execution.parallel.mode.default=same_thread +#junit.jupiter.execution.parallel.mode.default=concurrent +#junit.jupiter.execution.parallel.mode.classes.default=concurrent + +#junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$ClassName diff --git a/nostr-java-base/build.gradle b/nostr-java-base/build.gradle new file mode 100644 index 000000000..03572bb8c --- /dev/null +++ b/nostr-java-base/build.gradle @@ -0,0 +1,10 @@ +plugins { + id 'nostr-java.conventions' +} + +description = 'nostr-java-base' + +dependencies { + api project(':nostr-java-util') + api project(':nostr-java-crypto') +} diff --git a/nostr-java-base/pom.xml b/nostr-java-base/pom.xml index acf2f5122..103219fe7 100644 --- a/nostr-java-base/pom.xml +++ b/nostr-java-base/pom.xml @@ -1,38 +1,26 @@ - - + 4.0.0 xyz.tcheeric nostr-java 0.6.5-SNAPSHOT + ../pom.xml - + nostr-java-base jar - - - org.projectlombok - lombok - - - com.fasterxml.jackson.core - jackson-databind - - - ${project.groupId} nostr-java-util - ${project.version} + ${nostr-java.version} ${project.groupId} nostr-java-crypto - ${project.version} + ${nostr-java.version} - \ No newline at end of file + diff --git a/nostr-java-base/src/main/java/module-info.java b/nostr-java-base/src/main/java/module-info.java deleted file mode 100644 index 8e935de14..000000000 --- a/nostr-java-base/src/main/java/module-info.java +++ /dev/null @@ -1,13 +0,0 @@ - -module nostr.base { - requires static lombok; - requires com.fasterxml.jackson.databind; - requires com.fasterxml.jackson.annotation; - requires com.fasterxml.jackson.core; - requires nostr.util; - requires nostr.crypto; - requires java.logging; - - exports nostr.base; - exports nostr.base.annotation; -} diff --git a/nostr-java-base/src/main/java/nostr/base/BaseKey.java b/nostr-java-base/src/main/java/nostr/base/BaseKey.java index a8ca1142d..bd13d13b6 100644 --- a/nostr-java-base/src/main/java/nostr/base/BaseKey.java +++ b/nostr-java-base/src/main/java/nostr/base/BaseKey.java @@ -7,7 +7,6 @@ import lombok.NonNull; import nostr.crypto.bech32.Bech32; import nostr.crypto.bech32.Bech32Prefix; -import nostr.util.NostrException; import nostr.util.NostrUtil; import java.util.Arrays; @@ -32,7 +31,7 @@ public abstract class BaseKey implements IKey { public String toBech32String() { try { return Bech32.toBech32(prefix, rawData); - } catch (NostrException ex) { + } catch (Exception ex) { throw new RuntimeException(ex); } } diff --git a/nostr-java-base/src/main/java/nostr/base/ContentReason.java b/nostr-java-base/src/main/java/nostr/base/ContentReason.java index fd95d1ac4..f196bbcb5 100644 --- a/nostr-java-base/src/main/java/nostr/base/ContentReason.java +++ b/nostr-java-base/src/main/java/nostr/base/ContentReason.java @@ -1,13 +1,13 @@ package nostr.base; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.AllArgsConstructor; - import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; + /** * @author guilhermegps * @@ -22,10 +22,8 @@ public class ContentReason { @Override public String toString() { - ObjectMapper mapper = new ObjectMapper(); - try { - return mapper.writeValueAsString(this); + return MAPPER_AFTERBURNER.writeValueAsString(this); } catch (JsonProcessingException e) { throw new RuntimeException(e); } diff --git a/nostr-java-base/src/main/java/nostr/base/ElementAttribute.java b/nostr-java-base/src/main/java/nostr/base/ElementAttribute.java index 804a58682..4ce02a995 100644 --- a/nostr-java-base/src/main/java/nostr/base/ElementAttribute.java +++ b/nostr-java-base/src/main/java/nostr/base/ElementAttribute.java @@ -29,10 +29,4 @@ public class ElementAttribute { @JsonProperty @EqualsAndHashCode.Include private final Object value; - - @JsonProperty - @JsonInclude(JsonInclude.Include.NON_NULL) - @EqualsAndHashCode.Exclude - private final Integer nip; - } diff --git a/nostr-java-base/src/main/java/nostr/base/Encoder.java b/nostr-java-base/src/main/java/nostr/base/Encoder.java new file mode 100644 index 000000000..326177d98 --- /dev/null +++ b/nostr-java-base/src/main/java/nostr/base/Encoder.java @@ -0,0 +1,14 @@ +package nostr.base; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.module.afterburner.AfterburnerModule; + +public interface Encoder { + ObjectMapper ENCODER_MAPPED_AFTERBURNER = JsonMapper.builder().addModule( + new AfterburnerModule()).build() + .setSerializationInclusion(Include.NON_NULL); + + String encode(); +} diff --git a/nostr-java-base/src/main/java/nostr/base/FEncoder.java b/nostr-java-base/src/main/java/nostr/base/FEncoder.java deleted file mode 100644 index 1304d0f17..000000000 --- a/nostr-java-base/src/main/java/nostr/base/FEncoder.java +++ /dev/null @@ -1,10 +0,0 @@ -package nostr.base; - -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.ObjectMapper; - -public interface FEncoder { - ObjectMapper MAPPER = new ObjectMapper().setSerializationInclusion(Include.NON_NULL); - - String encode(); -} 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 f94d95120..5eab64600 100644 --- a/nostr-java-base/src/main/java/nostr/base/IDecoder.java +++ b/nostr-java-base/src/main/java/nostr/base/IDecoder.java @@ -1,6 +1,10 @@ package nostr.base; +import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.module.afterburner.AfterburnerModule; /** * @@ -8,7 +12,8 @@ * @param */ public interface IDecoder { - + ObjectMapper I_DECODER_MAPPER_AFTERBURNER + = JsonMapper.builder().addModule(new AfterburnerModule()).build().configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); T decode(String str) throws JsonProcessingException; } diff --git a/nostr-java-base/src/main/java/nostr/base/IElement.java b/nostr-java-base/src/main/java/nostr/base/IElement.java index 514ea4556..a2b9daa1b 100644 --- a/nostr-java-base/src/main/java/nostr/base/IElement.java +++ b/nostr-java-base/src/main/java/nostr/base/IElement.java @@ -6,6 +6,7 @@ */ public interface IElement { - Integer getNip(); - + default Integer getNip() { + return 1; + } } diff --git a/nostr-java-base/src/main/java/nostr/base/IEncoder.java b/nostr-java-base/src/main/java/nostr/base/IEncoder.java deleted file mode 100644 index 631fd3fca..000000000 --- a/nostr-java-base/src/main/java/nostr/base/IEncoder.java +++ /dev/null @@ -1,17 +0,0 @@ - -package nostr.base; - -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.ObjectMapper; - - -/** - * - * @author squirrel - * @param - */ -public interface IEncoder { - ObjectMapper MAPPER = new ObjectMapper().setSerializationInclusion(Include.NON_NULL); - - String encode(); -} diff --git a/nostr-java-base/src/main/java/nostr/base/IEvent.java b/nostr-java-base/src/main/java/nostr/base/IEvent.java index 94d78744b..832293c00 100644 --- a/nostr-java-base/src/main/java/nostr/base/IEvent.java +++ b/nostr-java-base/src/main/java/nostr/base/IEvent.java @@ -1,10 +1,14 @@ package nostr.base; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.module.afterburner.AfterburnerModule; + /** * * @author squirrel */ public interface IEvent extends IElement, IBech32Encodable { - + ObjectMapper MAPPER_AFTERBURNER = JsonMapper.builder().addModule(new AfterburnerModule()).build(); String getId(); } diff --git a/nostr-java-base/src/main/java/nostr/base/IGenericElement.java b/nostr-java-base/src/main/java/nostr/base/IGenericElement.java index 3f38b028a..93a83f393 100644 --- a/nostr-java-base/src/main/java/nostr/base/IGenericElement.java +++ b/nostr-java-base/src/main/java/nostr/base/IGenericElement.java @@ -2,13 +2,8 @@ import java.util.List; -/** - * - * @author squirrel - */ public interface IGenericElement extends IElement { - List getAttributes(); - - void addAttribute(ElementAttribute attribute); + void addAttribute(ElementAttribute... attribute); + void addAttributes(List attributes); } diff --git a/nostr-java-base/src/main/java/nostr/base/ISignable.java b/nostr-java-base/src/main/java/nostr/base/ISignable.java index b0a35f38f..c26c3960e 100644 --- a/nostr-java-base/src/main/java/nostr/base/ISignable.java +++ b/nostr-java-base/src/main/java/nostr/base/ISignable.java @@ -1,13 +1,17 @@ package nostr.base; +import java.nio.ByteBuffer; +import java.util.function.Consumer; +import java.util.function.Supplier; + /** * * @author squirrel */ public interface ISignable { - Signature getSignature(); - void setSignature(Signature signature); + Consumer getSignatureConsumer(); + Supplier getByeArraySupplier(); } diff --git a/nostr-java-base/src/main/java/nostr/base/IUnmarshaller.java b/nostr-java-base/src/main/java/nostr/base/IUnmarshaller.java deleted file mode 100644 index 2c1f78c89..000000000 --- a/nostr-java-base/src/main/java/nostr/base/IUnmarshaller.java +++ /dev/null @@ -1,13 +0,0 @@ - -package nostr.base; - -/** - * - * @author squirrel - * @param - */ -public interface IUnmarshaller { - - T unmarshall(); - -} diff --git a/nostr-java-base/src/main/java/nostr/base/Profile.java b/nostr-java-base/src/main/java/nostr/base/Profile.java index 5ecbd0abe..25e16833a 100644 --- a/nostr-java-base/src/main/java/nostr/base/Profile.java +++ b/nostr-java-base/src/main/java/nostr/base/Profile.java @@ -1,11 +1,7 @@ package nostr.base; -import java.net.URL; - import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -13,8 +9,11 @@ import lombok.ToString; import lombok.experimental.SuperBuilder; +import java.net.URL; + +import static nostr.base.IEvent.MAPPER_AFTERBURNER; + /** - * * @author eric */ @Data @@ -24,27 +23,26 @@ @AllArgsConstructor public abstract class Profile { - private final String name; + private final String name; - @ToString.Exclude - private String about; + @ToString.Exclude + private String about; - @ToString.Exclude - private URL picture; + @ToString.Exclude + private URL picture; - protected Profile() { - this.name = null; - } + protected Profile() { + this.name = null; + } - @JsonValue - @Override - public String toString() { - ObjectMapper objectMapper = new ObjectMapper(); - try { - return objectMapper.writeValueAsString(this); - } catch (JsonProcessingException ex) { - throw new RuntimeException(ex); - } + @JsonValue + @Override + public String toString() { + try { + return MAPPER_AFTERBURNER.writeValueAsString(this); + } catch (JsonProcessingException ex) { + throw new RuntimeException(ex); } + } } diff --git a/nostr-java-base/src/main/java/nostr/base/Proof.java b/nostr-java-base/src/main/java/nostr/base/Proof.java index 87a1ac356..1cef1b61c 100644 --- a/nostr-java-base/src/main/java/nostr/base/Proof.java +++ b/nostr-java-base/src/main/java/nostr/base/Proof.java @@ -1,16 +1,14 @@ package nostr.base; -import com.fasterxml.jackson.annotation.JsonKey; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; - import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.SneakyThrows; -import lombok.ToString; + +import static nostr.base.IEvent.MAPPER_AFTERBURNER; @Data @NoArgsConstructor @@ -33,6 +31,6 @@ public class Proof { @SneakyThrows @Override public String toString() { - return new ObjectMapper().writeValueAsString(this); + return MAPPER_AFTERBURNER.writeValueAsString(this); } } diff --git a/nostr-java-base/src/main/java/nostr/base/Relay.java b/nostr-java-base/src/main/java/nostr/base/Relay.java index 20dc5f8fa..525f6fb60 100644 --- a/nostr-java-base/src/main/java/nostr/base/Relay.java +++ b/nostr-java-base/src/main/java/nostr/base/Relay.java @@ -1,236 +1,214 @@ -package nostr.base; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.NonNull; -import lombok.ToString; -import lombok.extern.java.Log; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author squirrel - */ -// @Builder -@Data -@ToString(onlyExplicitlyIncluded = true) -@EqualsAndHashCode(onlyExplicitlyIncluded = true) -@AllArgsConstructor -@Log -public class Relay { - - @EqualsAndHashCode.Include - @ToString.Include - private String scheme; - - @EqualsAndHashCode.Include - @ToString.Include - private String host; - - @ToString.Exclude - @EqualsAndHashCode.Exclude - private RelayInformationDocument informationDocument; - - public Relay(@NonNull String uri) { - this(uri, new RelayInformationDocument()); - } - - public Relay(@NonNull String uri, RelayInformationDocument doc) { - this.informationDocument = doc; - String[] s = uri.split("://", 2); - if (s.length != 2) { - throw new RuntimeException("uri must contain scheme and host, passed in uri: \"" + uri + "\""); - } - this.scheme = s[0]; - this.host = s[1]; - } - - // Helper method - public List getSupportedNips() { - return this.getInformationDocument().getSupportedNips(); - } - - // Helper method - public void addNipSupport(int nip) { - this.getSupportedNips().add(nip); - } - - // Helper method - public String printSupportedNips() { - return convertToJsonArray(this.getInformationDocument().getSupportedNips()); - } - - private static String convertToJsonArray(List list) { - StringBuilder jsonBuilder = new StringBuilder(); - jsonBuilder.append("["); - for (int i = 0; i < list.size(); i++) { - jsonBuilder.append("\"").append(list.get(i)).append("\""); - if (i != list.size() - 1) { - jsonBuilder.append(","); - } - } - jsonBuilder.append("]"); - - return jsonBuilder.toString(); - } - - // Helper method - public String getName() { - return this.getInformationDocument().getName(); - } - - // Helper method - public String getUri() { - return this.scheme + "://" + this.host; - } - - // Helper method - public String getHttpUri() { - return this.scheme.replaceFirst("ws", "http") + "://" + this.host; - } - - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class RelayInformationDocument { - - @JsonProperty - private String name; - - @JsonProperty - private String description; - - @JsonProperty - private String pubkey; - - @JsonProperty - @JsonIgnoreProperties(ignoreUnknown = true) - private String id; - - @JsonProperty - @JsonIgnoreProperties(ignoreUnknown = true) - private String contact; - - @Builder.Default - @JsonProperty("supported_nips") - @JsonIgnoreProperties(ignoreUnknown = true) - private List supportedNips = new ArrayList<>(); - - @Builder.Default - @JsonProperty("supported_nip_extensions") - @JsonIgnoreProperties(ignoreUnknown = true) - private List supportedNipExtensions = new ArrayList<>(); - - @JsonProperty - @JsonIgnoreProperties(ignoreUnknown = true) - private String software; - - @JsonProperty - @JsonIgnoreProperties(ignoreUnknown = true) - private String version; - - @JsonProperty - @JsonIgnoreProperties(ignoreUnknown = true) - private Limitation limitation; - - @JsonProperty("payments_url") - private String paymentsUrl; - - @JsonProperty - @JsonIgnoreProperties(ignoreUnknown = true) - private Fees fees; - - @Data - public static class Limitation { - - @JsonProperty("max_message_length") - @JsonIgnoreProperties(ignoreUnknown = true) - private int maxMessageLength; - - @JsonProperty("max_subscriptions") - @JsonIgnoreProperties(ignoreUnknown = true) - private int maxSubscriptions; - - @JsonProperty("max_filters") - @JsonIgnoreProperties(ignoreUnknown = true) - private int maxFilters; - - @JsonProperty("max_limit") - @JsonIgnoreProperties(ignoreUnknown = true) - private int maxLimit; - - @JsonProperty("max_subid_length") - @JsonIgnoreProperties(ignoreUnknown = true) - private int maxSubIdLength; - - @JsonProperty("min_prefix") - @JsonIgnoreProperties(ignoreUnknown = true) - private int minPrefix; - - @JsonProperty("max_event_tags") - @JsonIgnoreProperties(ignoreUnknown = true) - private int maxEventTags; - - @JsonProperty("max_content_length") - @JsonIgnoreProperties(ignoreUnknown = true) - private int maxContentLength; - - @JsonProperty("min_pow_difficulty") - @JsonIgnoreProperties(ignoreUnknown = true) - private int minPowDifficulty; - - @JsonProperty("auth_required") - @JsonIgnoreProperties(ignoreUnknown = true) - private boolean authRequired; - - @JsonProperty("payment_required") - @JsonIgnoreProperties(ignoreUnknown = true) - private boolean paymentRequired; - - } - - @Data - public static class Fees { - - @JsonProperty - @JsonIgnoreProperties(ignoreUnknown = true) - private List admission; - - @JsonProperty - @JsonIgnoreProperties(ignoreUnknown = true) - private List publication; - - @Data - public static class AdmissionFee { - - @JsonProperty - @JsonIgnoreProperties(ignoreUnknown = true) - private int amount; - - @JsonProperty - @JsonIgnoreProperties(ignoreUnknown = true) - private String unit; - } - - @Data - public static class PublicationFee { - - @JsonProperty - @JsonIgnoreProperties(ignoreUnknown = true) - private int amount; - - @JsonProperty - @JsonIgnoreProperties(ignoreUnknown = true) - private String unit; - } - } - } -} +package nostr.base; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import lombok.extern.java.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author squirrel + */ +// @Builder +@Data +@ToString(onlyExplicitlyIncluded = true) +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@AllArgsConstructor +@Log +public class Relay { + + @EqualsAndHashCode.Include + @ToString.Include + private String scheme; + + @EqualsAndHashCode.Include + @ToString.Include + private String host; + + private RelayInformationDocument informationDocument; + + public Relay(@NonNull String uri) { + this(uri, new RelayInformationDocument()); + } + + public Relay(@NonNull String uri, RelayInformationDocument doc) { + this.informationDocument = doc; + String[] s = uri.split("://", 2); + if (s.length != 2) { + throw new RuntimeException("uri must contain scheme and host, passed in uri: \"" + uri + "\""); + } + this.scheme = s[0]; + this.host = s[1]; + } + + // Helper method + public List getSupportedNips() { + return this.getInformationDocument().getSupportedNips(); + } + + // Helper method + public void addNipSupport(int nip) { + this.getSupportedNips().add(nip); + } + + // Helper method + public String getName() { + return this.getInformationDocument().getName(); + } + + // Helper method + public String getUri() { + return this.scheme + "://" + this.host; + } + + // Helper method + public String getHttpUri() { + return this.scheme.replaceFirst("ws", "http") + "://" + this.host; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RelayInformationDocument { + + @JsonProperty + private String name; + + @JsonProperty + private String description; + + @JsonProperty + private String pubkey; + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private String id; + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private String contact; + + @Builder.Default + @JsonProperty("supported_nips") + @JsonIgnoreProperties(ignoreUnknown = true) + private List supportedNips = new ArrayList<>(); + + @Builder.Default + @JsonProperty("supported_nip_extensions") + @JsonIgnoreProperties(ignoreUnknown = true) + private List supportedNipExtensions = new ArrayList<>(); + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private String software; + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private String version; + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private Limitation limitation; + + @JsonProperty("payments_url") + private String paymentsUrl; + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private Fees fees; + + @Data + public static class Limitation { + + @JsonProperty("max_message_length") + @JsonIgnoreProperties(ignoreUnknown = true) + private int maxMessageLength; + + @JsonProperty("max_subscriptions") + @JsonIgnoreProperties(ignoreUnknown = true) + private int maxSubscriptions; + + @JsonProperty("max_filters") + @JsonIgnoreProperties(ignoreUnknown = true) + private int maxFilters; + + @JsonProperty("max_limit") + @JsonIgnoreProperties(ignoreUnknown = true) + private int maxLimit; + + @JsonProperty("max_subid_length") + @JsonIgnoreProperties(ignoreUnknown = true) + private int maxSubIdLength; + + @JsonProperty("min_prefix") + @JsonIgnoreProperties(ignoreUnknown = true) + private int minPrefix; + + @JsonProperty("max_event_tags") + @JsonIgnoreProperties(ignoreUnknown = true) + private int maxEventTags; + + @JsonProperty("max_content_length") + @JsonIgnoreProperties(ignoreUnknown = true) + private int maxContentLength; + + @JsonProperty("min_pow_difficulty") + @JsonIgnoreProperties(ignoreUnknown = true) + private int minPowDifficulty; + + @JsonProperty("auth_required") + @JsonIgnoreProperties(ignoreUnknown = true) + private boolean authRequired; + + @JsonProperty("payment_required") + @JsonIgnoreProperties(ignoreUnknown = true) + private boolean paymentRequired; + + } + + @Data + public static class Fees { + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private List admission; + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private List publication; + + @Data + public static class AdmissionFee { + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private int amount; + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private String unit; + } + + @Data + public static class PublicationFee { + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private int amount; + + @JsonProperty + @JsonIgnoreProperties(ignoreUnknown = true) + private String unit; + } + } + } +} diff --git a/nostr-java-base/src/main/java/nostr/base/UserProfile.java b/nostr-java-base/src/main/java/nostr/base/UserProfile.java index 998519766..0f0f50f46 100644 --- a/nostr-java-base/src/main/java/nostr/base/UserProfile.java +++ b/nostr-java-base/src/main/java/nostr/base/UserProfile.java @@ -12,7 +12,6 @@ import lombok.extern.java.Log; import nostr.crypto.bech32.Bech32; import nostr.crypto.bech32.Bech32Prefix; -import nostr.util.NostrException; /** * @@ -39,7 +38,7 @@ public UserProfile(@NonNull PublicKey publicKey, String name, String nip05, Stri public String toBech32() { try { return Bech32.encode(Bech32.Encoding.BECH32, Bech32Prefix.NPROFILE.getCode(), this.publicKey.getRawData()); - } catch (NostrException ex) { + } catch (Exception ex) { log.log(Level.SEVERE, null, ex); throw new RuntimeException(ex); } diff --git a/nostr-java-base/src/main/java/nostr/base/annotation/CustomHandler.java b/nostr-java-base/src/main/java/nostr/base/annotation/CustomHandler.java deleted file mode 100644 index b06db1ecf..000000000 --- a/nostr-java-base/src/main/java/nostr/base/annotation/CustomHandler.java +++ /dev/null @@ -1,14 +0,0 @@ -package nostr.base.annotation; - -import nostr.base.Command; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface CustomHandler { - Command command(); -} diff --git a/nostr-java-base/src/main/java/nostr/base/annotation/DefaultHandler.java b/nostr-java-base/src/main/java/nostr/base/annotation/DefaultHandler.java deleted file mode 100644 index d3da28b24..000000000 --- a/nostr-java-base/src/main/java/nostr/base/annotation/DefaultHandler.java +++ /dev/null @@ -1,18 +0,0 @@ -package nostr.base.annotation; - -import nostr.base.Command; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * - * @author squirrel - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface DefaultHandler { - Command command(); -} diff --git a/nostr-java-base/src/test/java/nostr/base/BaseKeyTest.java b/nostr-java-base/src/test/java/nostr/base/BaseKeyTest.java new file mode 100644 index 000000000..bfd85e403 --- /dev/null +++ b/nostr-java-base/src/test/java/nostr/base/BaseKeyTest.java @@ -0,0 +1,79 @@ +package nostr.base; + +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class BaseKeyTest { + public static final String VALID_HEXPUBKEY = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f"; + public static final String INVALID_HEXPUBKEY_NON_HEX_DIGITS = "XYZdf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f"; + public static final String INVALID_HEXPUBKEY_LENGTH_TOO_SHORT = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6"; + public static final String INVALID_HEXPUBKEY_LENGTH_TOO_LONG = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f666"; + public static final String VALID_HEXPUBKEY_ALL_ZEROS = "0000000000000000000000000000000000000000000000000000000000000000"; + public static final String VALID_HEXPUBKEY_ALL_FF = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + public static final String INVALID_HEXPUBKEY_HAS_MULTIPLE_UPPERCASE = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; + public static final String INVALID_HEXPUBKEY_HAS_SINGLE_UPPERCASE = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6F"; + + @Test + public void testValidPublicKeyString() { + System.out.println("testValidPublicKeyString"); + assertDoesNotThrow(() -> new PublicKey(VALID_HEXPUBKEY)); + } + + @Test + public void testValidPublicKeyByteArray() { + System.out.println("testValidPublicKeyByteArray"); + assertDoesNotThrow(() -> new PublicKey(VALID_HEXPUBKEY.getBytes(StandardCharsets.UTF_8))); + } + + @Test + public void testInValidNullPublicKeyString() { + System.out.println("testInValidNullPublicKeyString"); + assertThrows(AssertionError.class, () -> new PublicKey("")); + } + + @Test + public void testInValidPublicKeyNonHexDigits() { + System.out.println("testInValidPublicKeyNonHexDigits"); + assertThrows(AssertionError.class, () -> new PublicKey(INVALID_HEXPUBKEY_NON_HEX_DIGITS)); + } + + @Test + public void testInValidPublicKeyLengthTooShort() { + System.out.println("testInValidPublicKeyLengthTooShort"); + assertThrows(AssertionError.class, () -> new PublicKey(INVALID_HEXPUBKEY_LENGTH_TOO_SHORT)); + } + + @Test + public void testInValidPublicKeyLengthTooLong() { + System.out.println("testInValidPublicKeyLengthTooShort"); + assertThrows(AssertionError.class, () -> new PublicKey(INVALID_HEXPUBKEY_LENGTH_TOO_LONG)); + } + + @Test + public void testValidPublicKeyAllZeros() { + System.out.println("testValidPublicKeyAllZeros"); + assertDoesNotThrow(() -> new PublicKey(VALID_HEXPUBKEY_ALL_ZEROS)); + } + + @Test + public void testValidPublicKeyAllFF() { + System.out.println("testValidPublicKeyAllFF"); + assertDoesNotThrow(() -> new PublicKey(VALID_HEXPUBKEY_ALL_FF)); + } + + @Test + public void testInvalidPublicKeyMultipleUppercase() { + System.out.println("testInvalidPublicKeyMultipleUppercase"); + assertThrows(AssertionError.class, () -> new PublicKey(INVALID_HEXPUBKEY_HAS_MULTIPLE_UPPERCASE)); + } + + @Test + public void testInvalidPublicKeySingleUppercase() { + System.out.println("testInvalidPublicKeySingleUppercase"); + assertThrows(AssertionError.class, () -> new PublicKey(INVALID_HEXPUBKEY_HAS_SINGLE_UPPERCASE)); + } +} diff --git a/nostr-java-base/src/test/resources/application-test.properties b/nostr-java-base/src/test/resources/application-test.properties new file mode 100644 index 000000000..c4bd7b5c1 --- /dev/null +++ b/nostr-java-base/src/test/resources/application-test.properties @@ -0,0 +1,4 @@ +spring.threads.virtual.enabled=true + +logging.level.nostr.base=INFO +logging.pattern.console=%msg%n diff --git a/nostr-java-base/src/test/resources/junit-platform.properties b/nostr-java-base/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..a413a5904 --- /dev/null +++ b/nostr-java-base/src/test/resources/junit-platform.properties @@ -0,0 +1,7 @@ +# junit-platform.properties + +junit.jupiter.execution.parallel.enabled=true +junit.jupiter.execution.parallel.config.strategy=dynamic +junit.jupiter.execution.parallel.mode.default=same_thread +#junit.jupiter.execution.parallel.mode.default=concurrent +#junit.jupiter.execution.parallel.mode.classes.default=concurrent diff --git a/nostr-java-client/build.gradle b/nostr-java-client/build.gradle new file mode 100644 index 000000000..95517328c --- /dev/null +++ b/nostr-java-client/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'nostr-java.conventions' +} + +description = 'nostr-java-client' + +dependencies { + api project(':nostr-java-id') +} diff --git a/nostr-java-client/pom.xml b/nostr-java-client/pom.xml index f2911d6ec..405a337fe 100644 --- a/nostr-java-client/pom.xml +++ b/nostr-java-client/pom.xml @@ -1,61 +1,66 @@ - 4.0.0 + xyz.tcheeric nostr-java 0.6.5-SNAPSHOT + ../pom.xml + nostr-java-client jar + + + 4.2.2 + 2.2.0 + 1.1.20 + 6.1.10 + 6.1.10 + + ${project.groupId} - nostr-java-event - ${project.version} + nostr-java-id + ${nostr-java.version} + - ${project.groupId} - nostr-java-base - ${project.version} + org.springframework.boot + spring-boot-starter-websocket + ${spring-boot.version} - ${project.groupId} - nostr-java-id - ${project.version} + org.springframework + spring-websocket + ${spring-websocket.version} org.springframework - spring-websocket - 6.1.10 + spring-webflux + ${spring-webflux.version} + + + io.projectreactor.netty + reactor-netty-http + ${reactor-netty-http.version} - jakarta.websocket jakarta.websocket-api - 2.2.0 + ${jakarta.websocket-api.version} - jakarta.websocket jakarta.websocket-client-api - 2.2.0 - - - - org.springframework - spring-webflux - 6.1.10 - - - io.projectreactor.netty - reactor-netty-http - 1.1.20 + + ${jakarta.websocket-api.version} org.awaitility awaitility - 4.2.2 + ${awaitility.version} diff --git a/nostr-java-client/src/main/java/module-info.java b/nostr-java-client/src/main/java/module-info.java deleted file mode 100644 index f897a28f3..000000000 --- a/nostr-java-client/src/main/java/module-info.java +++ /dev/null @@ -1,17 +0,0 @@ -module nostr.client { - requires nostr.event; - requires static lombok; - requires java.logging; - requires nostr.util; - requires nostr.base; - requires com.fasterxml.jackson.core; - requires reactor.core; - requires spring.webflux; - requires spring.context; - requires spring.beans; - requires spring.websocket; - requires jakarta.websocket.client; - requires awaitility; - - exports nostr.client.springwebsocket; -} 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 deleted file mode 100644 index 4f9f72647..000000000 --- a/nostr-java-client/src/main/java/nostr/client/springwebsocket/ReactiveWebSocketClient.java +++ /dev/null @@ -1,52 +0,0 @@ -package nostr.client.springwebsocket; - -import com.fasterxml.jackson.core.JsonProcessingException; -import nostr.event.BaseMessage; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.socket.WebSocketMessage; -import org.springframework.web.reactive.socket.WebSocketSession; -import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient; -import reactor.core.publisher.Flux; - -import java.io.IOException; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -@Component -@Scope(BeanDefinition.SCOPE_PROTOTYPE) -public class ReactiveWebSocketClient implements WebSocketClientIF { - private final ReactorNettyWebSocketClient client; - private final URI uri; - - public ReactiveWebSocketClient(@Value("${nostr.relay.uri}") String relayUri) { - this.client = new ReactorNettyWebSocketClient(); - this.uri = URI.create(relayUri); - } - - @Override - public List send(T baseMessage) throws JsonProcessingException { - return send(baseMessage.encode()); - } - - @Override - public List send(String json) { - List events = new ArrayList<>(); - client.execute(uri, - session -> - session - .send(Flux.just(session.textMessage(json))) - .thenMany(session.receive().take(1).map(WebSocketMessage::getPayloadAsText)) - .doOnNext(events::add).then()) - .block(); - return events; - } - - @Override - public void closeSocket() throws IOException { - client.execute(uri, WebSocketSession::close).block(); - } -} diff --git a/nostr-java-client/src/main/resources/relays.properties b/nostr-java-client/src/main/resources/relays.properties deleted file mode 100644 index ced21d9de..000000000 --- a/nostr-java-client/src/main/resources/relays.properties +++ /dev/null @@ -1 +0,0 @@ -nostr_rs_relay=localhost:5555 \ No newline at end of file diff --git a/nostr-java-crypto/build.gradle b/nostr-java-crypto/build.gradle new file mode 100644 index 000000000..3e57327eb --- /dev/null +++ b/nostr-java-crypto/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'nostr-java.conventions' +} + +description = 'nostr-java-crypto' + +dependencies { + api project(':nostr-java-util') +} diff --git a/nostr-java-crypto/pom.xml b/nostr-java-crypto/pom.xml index b0022724f..b60d82e4e 100644 --- a/nostr-java-crypto/pom.xml +++ b/nostr-java-crypto/pom.xml @@ -1,14 +1,13 @@ - - + 4.0.0 xyz.tcheeric nostr-java 0.6.5-SNAPSHOT + ../pom.xml - + nostr-java-crypto jar @@ -20,21 +19,21 @@ https://github.com/unclebob/more-speech/tree/bdd2f32b37264f20bf6abb4887489e70d2b0fdf1 + + 1.78 + + - - - org.projectlombok - lombok - org.bouncycastle bcprov-jdk18on + ${bcprov-jdk18on.version} ${project.groupId} nostr-java-util - ${project.version} + ${nostr-java.version} diff --git a/nostr-java-crypto/src/main/java/module-info.java b/nostr-java-crypto/src/main/java/module-info.java deleted file mode 100644 index a6047471b..000000000 --- a/nostr-java-crypto/src/main/java/module-info.java +++ /dev/null @@ -1,14 +0,0 @@ - -module nostr.crypto { - requires static lombok; - requires java.logging; - - requires org.bouncycastle.provider; - - requires nostr.util; - - exports nostr.crypto.bech32; - exports nostr.crypto.schnorr; - exports nostr.crypto.nip04; - exports nostr.crypto.nip44; -} diff --git a/nostr-java-crypto/src/main/java/nostr/crypto/bech32/Bech32.java b/nostr-java-crypto/src/main/java/nostr/crypto/bech32/Bech32.java index 896ab5b06..9b8efd790 100644 --- a/nostr-java-crypto/src/main/java/nostr/crypto/bech32/Bech32.java +++ b/nostr-java-crypto/src/main/java/nostr/crypto/bech32/Bech32.java @@ -5,7 +5,6 @@ import java.util.List; import java.util.Locale; -import nostr.util.NostrException; import nostr.util.NostrUtil; /** @@ -61,21 +60,21 @@ private Bech32Data(final Encoding encoding, final String hrp, final byte[] data) } } - public static String toBech32(Bech32Prefix hrp, byte[] hexKey) throws NostrException { - var data = convertBits(hexKey, 8, 5, true); + public static String toBech32(Bech32Prefix hrp, byte[] hexKey) throws Exception { + byte[] data = convertBits(hexKey, 8, 5, true); return Bech32.encode(Bech32.Encoding.BECH32, hrp.getCode(), data); } - public static String toBech32(Bech32Prefix hrp, String hexKey) throws NostrException { - var data = NostrUtil.hexToBytes(hexKey); + public static String toBech32(Bech32Prefix hrp, String hexKey) throws Exception { + byte[] data = NostrUtil.hexToBytes(hexKey); return toBech32(hrp, data); } // Added by squirrel - public static String fromBech32(String strBech32) throws NostrException { - var data = Bech32.decode(strBech32).data; + public static String fromBech32(String strBech32) throws Exception { + byte[] data = Bech32.decode(strBech32).data; data = convertBits(data, 5, 8, true); @@ -93,9 +92,9 @@ public static String fromBech32(String strBech32) throws NostrException { * * @param bech32 * @return - * @throws nostr.util.NostrException + * @throws nostr.util.Exception */ - public static String encode(final Bech32Data bech32) throws NostrException { + public static String encode(final Bech32Data bech32) throws Exception { return encode(bech32.encoding, bech32.hrp, bech32.data); } @@ -106,12 +105,12 @@ public static String encode(final Bech32Data bech32) throws NostrException { * @param hrp * @param values * @return - * @throws nostr.util.NostrException + * @throws nostr.util.Exception */ - // Modified to throw NostrExceptions - public static String encode(Encoding encoding, String hrp, final byte[] values) throws NostrException { + // Modified to throw Exceptions + public static String encode(Encoding encoding, String hrp, final byte[] values) throws Exception { if (hrp.isEmpty()) { - throw new NostrException("Human-readable part is too short"); + throw new Exception("Human-readable part is too short"); } hrp = hrp.toLowerCase(Locale.ROOT); @@ -133,53 +132,53 @@ public static String encode(Encoding encoding, String hrp, final byte[] values) * * @param str * @return - * @throws nostr.util.NostrException + * @throws nostr.util.Exception */ - // Modified to throw NostrExceptions - public static Bech32Data decode(final String str) throws NostrException { + // Modified to throw Exceptions + public static Bech32Data decode(final String str) throws Exception { boolean lower = false, upper = false; if (str.length() < 8) { - throw new NostrException("Input too short: " + str.length()); + throw new Exception("Input too short: " + str.length()); } for (int i = 0; i < str.length(); ++i) { char c = str.charAt(i); if (c < 33 || c > 126) { - throw new NostrException(String.format("Invalid Character %c, %d", c, i)); + throw new Exception(String.format("Invalid Character %c, %d", c, i)); } if (c >= 'a' && c <= 'z') { if (upper) { - throw new NostrException(String.format("Invalid Character %c, %d", c, i)); + throw new Exception(String.format("Invalid Character %c, %d", c, i)); } lower = true; } if (c >= 'A' && c <= 'Z') { if (lower) { - throw new NostrException(String.format("Invalid Character %c, %d", c, i)); + throw new Exception(String.format("Invalid Character %c, %d", c, i)); } upper = true; } } final int pos = str.lastIndexOf('1'); if (pos < 1) { - throw new NostrException("Missing human-readable part"); + throw new Exception("Missing human-readable part"); } final int dataPartLength = str.length() - 1 - pos; if (dataPartLength < 6) { - throw new NostrException(String.format("Data part too short: %d)", dataPartLength)); + throw new Exception(String.format("Data part too short: %d)", dataPartLength)); } byte[] values = new byte[dataPartLength]; for (int i = 0; i < dataPartLength; ++i) { char c = str.charAt(i + pos + 1); if (CHARSET_REV[c] == -1) { - throw new NostrException(String.format("Invalid Character %c, %d", c, i + pos + 1)); + throw new Exception(String.format("Invalid Character %c, %d", c, i + pos + 1)); } values[i] = CHARSET_REV[c]; } String hrp = str.substring(0, pos).toLowerCase(Locale.ROOT); Encoding encoding = verifyChecksum(hrp, values); if (encoding == null) { - throw new NostrException("InvalidChecksum"); + throw new Exception("InvalidChecksum"); } return new Bech32Data(encoding, hrp, Arrays.copyOfRange(values, 0, values.length - 6)); } diff --git a/nostr-java-crypto/src/main/java/nostr/crypto/nip04/EncryptedDirectMessage.java b/nostr-java-crypto/src/main/java/nostr/crypto/nip04/EncryptedDirectMessage.java index 92206f73b..feb7b6e7f 100644 --- a/nostr-java-crypto/src/main/java/nostr/crypto/nip04/EncryptedDirectMessage.java +++ b/nostr-java-crypto/src/main/java/nostr/crypto/nip04/EncryptedDirectMessage.java @@ -26,20 +26,20 @@ public static String encrypt(@NonNull String message, @NonNull byte[] senderPriv } public static String decryptMessage(@NonNull byte[] senderPrivKey, @NonNull String encContent, @NonNull byte[] rcptPubKey) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { - var sharedSecret = getSharedSecretKeySpec(senderPrivKey, rcptPubKey); - return decryptMessage(sharedSecret, encContent); + SecretKeySpec sharedSecret = getSharedSecretKeySpec(senderPrivKey, rcptPubKey); + return decryptMessage(sharedSecret, encContent); } private static String decryptMessage(SecretKeySpec sharedSecretKey, String encodedMessage) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { - final var parts = encodedMessage.split("\\?iv="); + String[] parts = encodedMessage.split("\\?iv="); - final var Base64Decoder = Base64.getDecoder(); - final var encryptedMessage = Base64Decoder.decode(parts[0]); - final var iv = Base64Decoder.decode(parts[1]); + Base64.Decoder Base64Decoder = Base64.getDecoder(); + byte[] encryptedMessage = Base64Decoder.decode(parts[0]); + byte[] iv = Base64Decoder.decode(parts[1]); - var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - var ivParamSpec = new IvParameterSpec(iv); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + IvParameterSpec ivParamSpec = new IvParameterSpec(iv); cipher.init(Cipher.DECRYPT_MODE, sharedSecretKey, ivParamSpec); return new String(cipher.doFinal(encryptedMessage), StandardCharsets.UTF_8); @@ -47,21 +47,21 @@ private static String decryptMessage(SecretKeySpec sharedSecretKey, String encod static String encryptMessage(byte[] senderPrivateKey, byte[] rcptPublicKey, String message) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { - var sharedSecretKey = getSharedSecretKeySpec(senderPrivateKey, rcptPublicKey); + SecretKeySpec sharedSecretKey = getSharedSecretKeySpec(senderPrivateKey, rcptPublicKey); - var iv = NostrUtil.createRandomByteArray(16); - var ivParamSpec = new IvParameterSpec(iv); + byte[] iv = NostrUtil.createRandomByteArray(16); + IvParameterSpec ivParamSpec = new IvParameterSpec(iv); - var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, sharedSecretKey, ivParamSpec); - final var msg = message.getBytes(StandardCharsets.UTF_8); - var encryptedMessage = cipher.doFinal(msg); + byte[] msg = message.getBytes(StandardCharsets.UTF_8); + byte[] encryptedMessage = cipher.doFinal(msg); - final var Base64Encoder = Base64.getEncoder(); - var encryptedMessage64 = Base64Encoder.encode(encryptedMessage); + Base64.Encoder Base64Encoder = Base64.getEncoder(); + byte[] encryptedMessage64 = Base64Encoder.encode(encryptedMessage); - var iv64 = Base64Encoder.encode(ivParamSpec.getIV()); + byte[] iv64 = Base64Encoder.encode(ivParamSpec.getIV()); return new String(encryptedMessage64) + "?iv=" + new String(iv64); } @@ -70,8 +70,8 @@ private static SecretKeySpec getSharedSecretKeySpec(byte[] privateKey, byte[] pu final String secKeyHex = NostrUtil.bytesToHex(privateKey); final String pubKeyHex = NostrUtil.bytesToHex(publicKey); - var sharedPoint = getSharedSecret(secKeyHex, pubKeyHex); - var sharedX = Arrays.copyOfRange(sharedPoint, 1, 33); + byte[] sharedPoint = getSharedSecret(secKeyHex, pubKeyHex); + byte[] sharedX = Arrays.copyOfRange(sharedPoint, 1, 33); return new SecretKeySpec(sharedX, "AES"); } diff --git a/nostr-java-crypto/src/test/java/nostr/crypto/CryptoTest.java b/nostr-java-crypto/src/test/java/nostr/crypto/CryptoTest.java new file mode 100644 index 000000000..28d682ee1 --- /dev/null +++ b/nostr-java-crypto/src/test/java/nostr/crypto/CryptoTest.java @@ -0,0 +1,62 @@ +//package nostr.crypto; +// +//import nostr.base.Signature; +//import nostr.crypto.bech32.Bech32; +//import nostr.crypto.bech32.Bech32Prefix; +//import nostr.crypto.schnorr.Schnorr; +//import nostr.event.impl.GenericEvent; +//import nostr.id.Identity; +//import nostr.util.NostrUtil; +//import org.junit.jupiter.api.Test; +// +//import static nostr.crypto.EntityFactory.Events.createTextNoteEvent; +//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; +//import static org.junit.jupiter.api.Assertions.fail; +// +///** +// * +// * @author squirrel +// */ +//public class CryptoTest { +// +// @Test +// public void testBech32() { +// try { +// System.out.println("testBech32"); +// +// final String hexPub = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f"; +// final String npub = "npub126klq89p42wk78p4j5ur8wlxmxdqepdh8tez9e4axpd4run5nahsmff27j"; +// +// assertEquals(npub, Bech32.toBech32(Bech32Prefix.NPUB, hexPub)); +// assertEquals("56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f", Bech32.fromBech32(npub)); +// } catch (Exception ex) { +// fail(ex); +// } +// } +// +// @Test +// public void testVerifySignature() { +// System.out.println("testVerifySignature"); +// +// Identity identity = Identity.generateRandomIdentity(); +// final GenericEvent[] event = {createTextNoteEvent(identity.getPublicKey(), "Hello World")}; +// event[0].update(); +// assertDoesNotThrow(() -> { +// byte[] message = NostrUtil.sha256(event[0].get_serializedEvent()); +// Signature signature = identity.sign(event[0]); +// boolean verification = Schnorr.verify(message, identity.getPublicKey().getRawData(), signature.getRawData()); +// assertTrue(verification, "Schnorr must have a true verify result."); +// +// event[0] = createTextNoteEvent(identity.getPublicKey(), "Guten Tag"); +// event[0].update(); +// message = NostrUtil.sha256(event[0].get_serializedEvent()); +// verification = Schnorr.verify(message, identity.getPublicKey().getRawData(), signature.getRawData()); +// +// assertFalse(verification); +// }); +// } +// +//} diff --git a/nostr-java-crypto/src/test/resources/application-test.properties b/nostr-java-crypto/src/test/resources/application-test.properties new file mode 100644 index 000000000..887cd2666 --- /dev/null +++ b/nostr-java-crypto/src/test/resources/application-test.properties @@ -0,0 +1,4 @@ +spring.threads.virtual.enabled=true + +logging.level.nostr.crypto=INFO +logging.pattern.console=%msg%n diff --git a/nostr-java-crypto/src/test/resources/junit-platform.properties b/nostr-java-crypto/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..a413a5904 --- /dev/null +++ b/nostr-java-crypto/src/test/resources/junit-platform.properties @@ -0,0 +1,7 @@ +# junit-platform.properties + +junit.jupiter.execution.parallel.enabled=true +junit.jupiter.execution.parallel.config.strategy=dynamic +junit.jupiter.execution.parallel.mode.default=same_thread +#junit.jupiter.execution.parallel.mode.default=concurrent +#junit.jupiter.execution.parallel.mode.classes.default=concurrent diff --git a/nostr-java-encryption-nip04/pom.xml b/nostr-java-encryption-nip04/pom.xml deleted file mode 100644 index 5c28eba3c..000000000 --- a/nostr-java-encryption-nip04/pom.xml +++ /dev/null @@ -1,32 +0,0 @@ - - 4.0.0 - - xyz.tcheeric - nostr-java - 0.6.5-SNAPSHOT - - - nostr-java-encryption-nip04 - jar - - nostr-java-encryption-nip04 - http://maven.apache.org - - - UTF-8 - - - - - ${project.groupId} - nostr-java-encryption - ${project.version} - - - ${project.groupId} - nostr-java-crypto - ${project.version} - - - diff --git a/nostr-java-encryption-nip04/src/main/java/module-info.java b/nostr-java-encryption-nip04/src/main/java/module-info.java deleted file mode 100644 index 8acbf4ca2..000000000 --- a/nostr-java-encryption-nip04/src/main/java/module-info.java +++ /dev/null @@ -1,9 +0,0 @@ -module nostr.encryption.nip04dm { - - requires lombok; - - requires nostr.encryption; - requires nostr.crypto; - - exports nostr.encryption.nip04; -} \ No newline at end of file diff --git a/nostr-java-encryption-nip04/src/main/resources/META-INF/services/nostr.encryption.MessageCipher b/nostr-java-encryption-nip04/src/main/resources/META-INF/services/nostr.encryption.MessageCipher deleted file mode 100644 index e18adc2fa..000000000 --- a/nostr-java-encryption-nip04/src/main/resources/META-INF/services/nostr.encryption.MessageCipher +++ /dev/null @@ -1 +0,0 @@ -nostr.encryption.nip04.MessageCipher04 \ No newline at end of file diff --git a/nostr-java-encryption-nip44/pom.xml b/nostr-java-encryption-nip44/pom.xml deleted file mode 100644 index d50eb5b4c..000000000 --- a/nostr-java-encryption-nip44/pom.xml +++ /dev/null @@ -1,32 +0,0 @@ - - 4.0.0 - - xyz.tcheeric - nostr-java - 0.6.5-SNAPSHOT - - - nostr-java-encryption-nip44 - jar - - nostr-java-encryption-nip44 - http://maven.apache.org - - - UTF-8 - - - - - ${project.groupId} - nostr-java-crypto - ${project.version} - - - ${project.groupId} - nostr-java-encryption - ${project.version} - - - diff --git a/nostr-java-encryption-nip44/src/main/java/module-info.java b/nostr-java-encryption-nip44/src/main/java/module-info.java deleted file mode 100644 index a0604a1d3..000000000 --- a/nostr-java-encryption-nip44/src/main/java/module-info.java +++ /dev/null @@ -1,10 +0,0 @@ -module nostr.encryption.nip44dm { - - requires lombok; - - requires nostr.encryption; - requires nostr.crypto; - requires nostr.util; - - exports nostr.encryption.nip44; -} \ No newline at end of file diff --git a/nostr-java-encryption-nip44/src/main/resources/META-INF/services/nostr.encryption.MessageCipher b/nostr-java-encryption-nip44/src/main/resources/META-INF/services/nostr.encryption.MessageCipher deleted file mode 100644 index 73d2713cb..000000000 --- a/nostr-java-encryption-nip44/src/main/resources/META-INF/services/nostr.encryption.MessageCipher +++ /dev/null @@ -1 +0,0 @@ -nostr.encryption.nip44.MessageCipher44 \ No newline at end of file diff --git a/nostr-java-encryption/build.gradle b/nostr-java-encryption/build.gradle new file mode 100644 index 000000000..69bddf914 --- /dev/null +++ b/nostr-java-encryption/build.gradle @@ -0,0 +1,10 @@ +plugins { + id 'nostr-java.conventions' +} + +description = 'nostr-java-encryption' + +dependencies { + api project(':nostr-java-crypto') + api project(':nostr-java-util') +} diff --git a/nostr-java-encryption/pom.xml b/nostr-java-encryption/pom.xml index bc81e01bc..5a7734cf5 100644 --- a/nostr-java-encryption/pom.xml +++ b/nostr-java-encryption/pom.xml @@ -1,12 +1,13 @@ - + 4.0.0 + xyz.tcheeric nostr-java 0.6.5-SNAPSHOT + ../pom.xml - + nostr-java-encryption jar @@ -16,4 +17,12 @@ UTF-8 + + + + ${project.groupId} + nostr-java-crypto + ${nostr-java.version} + + diff --git a/nostr-java-encryption/src/main/java/module-info.java b/nostr-java-encryption/src/main/java/module-info.java deleted file mode 100644 index 94ad056bd..000000000 --- a/nostr-java-encryption/src/main/java/module-info.java +++ /dev/null @@ -1,4 +0,0 @@ -module nostr.encryption { - - exports nostr.encryption; -} \ No newline at end of file diff --git a/nostr-java-encryption-nip04/src/main/java/nostr/encryption/nip04/MessageCipher04.java b/nostr-java-encryption/src/main/java/nostr/encryption/MessageCipher04.java similarity index 95% rename from nostr-java-encryption-nip04/src/main/java/nostr/encryption/nip04/MessageCipher04.java rename to nostr-java-encryption/src/main/java/nostr/encryption/MessageCipher04.java index f759014e6..7fca8d42b 100644 --- a/nostr-java-encryption-nip04/src/main/java/nostr/encryption/nip04/MessageCipher04.java +++ b/nostr-java-encryption/src/main/java/nostr/encryption/MessageCipher04.java @@ -1,10 +1,9 @@ -package nostr.encryption.nip04; +package nostr.encryption; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NonNull; import nostr.crypto.nip04.EncryptedDirectMessage; -import nostr.encryption.MessageCipher; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; diff --git a/nostr-java-encryption-nip44/src/main/java/nostr/encryption/nip44/MessageCipher44.java b/nostr-java-encryption/src/main/java/nostr/encryption/MessageCipher44.java similarity index 95% rename from nostr-java-encryption-nip44/src/main/java/nostr/encryption/nip44/MessageCipher44.java rename to nostr-java-encryption/src/main/java/nostr/encryption/MessageCipher44.java index 6de3c232c..9e636d81b 100644 --- a/nostr-java-encryption-nip44/src/main/java/nostr/encryption/nip44/MessageCipher44.java +++ b/nostr-java-encryption/src/main/java/nostr/encryption/MessageCipher44.java @@ -1,10 +1,9 @@ -package nostr.encryption.nip44; +package nostr.encryption; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NonNull; import nostr.crypto.nip44.EncryptedPayloads; -import nostr.encryption.MessageCipher; import nostr.util.NostrUtil; import java.security.NoSuchAlgorithmException; @@ -51,4 +50,4 @@ private byte[] getConversationKey() { private byte[] generateNonce() { return NostrUtil.createRandomByteArray(NONCE_LENGTH); } -} \ No newline at end of file +} diff --git a/nostr-java-event/build.gradle b/nostr-java-event/build.gradle new file mode 100644 index 000000000..2c195373f --- /dev/null +++ b/nostr-java-event/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'nostr-java.conventions' +} + +description = 'nostr-java-event' + +dependencies { + api project(':nostr-java-base') +} diff --git a/nostr-java-event/pom.xml b/nostr-java-event/pom.xml index 44581be69..00c84a67c 100644 --- a/nostr-java-event/pom.xml +++ b/nostr-java-event/pom.xml @@ -1,34 +1,22 @@ - - + 4.0.0 xyz.tcheeric nostr-java 0.6.5-SNAPSHOT + ../pom.xml - + nostr-java-event jar - - - org.projectlombok - lombok - - ${project.groupId} nostr-java-base - ${project.version} - - - ${project.groupId} - nostr-java-util - ${project.version} + ${nostr-java.version} - \ No newline at end of file + diff --git a/nostr-java-event/src/main/java/module-info.java b/nostr-java-event/src/main/java/module-info.java deleted file mode 100644 index 60e80d4b3..000000000 --- a/nostr-java-event/src/main/java/module-info.java +++ /dev/null @@ -1,22 +0,0 @@ - -module nostr.event { - requires static lombok; - requires nostr.base; - requires com.fasterxml.jackson.databind; - requires com.fasterxml.jackson.annotation; - requires com.fasterxml.jackson.core; - requires nostr.crypto; - requires nostr.util; - requires java.logging; - requires java.desktop; - - exports nostr.event; - exports nostr.event.impl; - exports nostr.event.message; - exports nostr.event.json.codec; - exports nostr.event.json.deserializer; - exports nostr.event.json.serializer; - exports nostr.event.tag; - exports nostr.event.util; - exports nostr.event.filter; -} diff --git a/nostr-java-event/src/main/java/nostr/event/AbstractEventContent.java b/nostr-java-event/src/main/java/nostr/event/AbstractEventContent.java index 09bf7f81e..9a7ecb851 100644 --- a/nostr-java-event/src/main/java/nostr/event/AbstractEventContent.java +++ b/nostr-java-event/src/main/java/nostr/event/AbstractEventContent.java @@ -1,23 +1,22 @@ package nostr.event; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import nostr.base.IEvent; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; + /** - * - * @author eric * @param + * @author eric */ public abstract class AbstractEventContent implements IContent { - - @Override - public String toString() { - try { - ObjectMapper mapper = new ObjectMapper(); - return mapper.writeValueAsString(this); - } catch (JsonProcessingException ex) { - throw new RuntimeException(ex); - } + + @Override + public String toString() { + try { + return MAPPER_AFTERBURNER.writeValueAsString(this); + } catch (JsonProcessingException ex) { + throw new RuntimeException(ex); } + } } diff --git a/nostr-java-event/src/main/java/nostr/event/BaseMessage.java b/nostr-java-event/src/main/java/nostr/event/BaseMessage.java index a78aafcec..681b96178 100644 --- a/nostr-java-event/src/main/java/nostr/event/BaseMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/BaseMessage.java @@ -19,10 +19,5 @@ protected BaseMessage(String command) { this.command = command; } - @Override - public Integer getNip() { - return 1; - } - public abstract String encode() throws JsonProcessingException; } diff --git a/nostr-java-event/src/main/java/nostr/event/BaseTag.java b/nostr-java-event/src/main/java/nostr/event/BaseTag.java index c3a50a909..5c0aaff75 100644 --- a/nostr-java-event/src/main/java/nostr/event/BaseTag.java +++ b/nostr-java-event/src/main/java/nostr/event/BaseTag.java @@ -1,11 +1,12 @@ package nostr.event; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; +import lombok.NonNull; import lombok.ToString; import nostr.base.IEvent; import nostr.base.ITag; @@ -14,22 +15,25 @@ import nostr.event.json.deserializer.TagDeserializer; import nostr.event.json.serializer.TagSerializer; import nostr.util.NostrException; +import org.apache.commons.lang3.stream.Streams; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; /** - * * @author squirrel */ @Data @ToString @EqualsAndHashCode(callSuper = false) -@NoArgsConstructor @JsonDeserialize(using = TagDeserializer.class) @JsonSerialize(using = TagSerializer.class) public abstract class BaseTag implements ITag { @@ -38,19 +42,13 @@ public abstract class BaseTag implements ITag { private IEvent parent; @Override - public void setParent(IEvent event) { + public void setParent(@NonNull IEvent event) { this.parent = event; } @Override public String getCode() { - var tag = this.getClass().getAnnotation(Tag.class); - return tag.code(); - } - - @Override - public Integer getNip() { - return 1; + return this.getClass().getAnnotation(Tag.class).code(); } public String getFieldValue(Field field) throws NostrException { @@ -63,14 +61,19 @@ public String getFieldValue(Field field) throws NostrException { } public List getSupportedFields() throws NostrException { - var fields = this.getClass().getDeclaredFields(); - List fieldList = new ArrayList<>(); - for (Field f : fields) { - if (null != f.getAnnotation(Key.class) && null != getFieldValue(f)) { - fieldList.add(f); - } - } + return new Streams.FailableStream<>(Arrays.stream(this.getClass().getDeclaredFields())) + .filter(f -> + Objects.nonNull(f.getAnnotation(Key.class))) + .filter(f -> + Objects.nonNull(getFieldValue(f))) + .collect(Collectors.toList()); + } + + protected static void setOptionalField(JsonNode node, BiConsumer con, T tag) { + Optional.ofNullable(node).ifPresent(n -> con.accept(n, tag)); + } - return fieldList; + protected static void setRequiredField(JsonNode node, BiConsumer con, T tag) { + con.accept(Optional.ofNullable(node).orElseThrow(), tag); } } 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 50bdbbf56..ae86a6ca3 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 @@ -17,61 +17,61 @@ @EqualsAndHashCode(callSuper = true) public class AddressableTagFilter extends AbstractFilterable { - public final static String FILTER_KEY = "#a"; + public final static String FILTER_KEY = "#a"; - public AddressableTagFilter(T addressableTag) { - super(addressableTag, FILTER_KEY); - } + public AddressableTagFilter(T addressableTag) { + super(addressableTag, FILTER_KEY); + } - @Override - public Predicate getPredicate() { - return this::compare; - } + @Override + public Predicate getPredicate() { + return this::compare; + } - public static AddressTag createAddressTag(@NonNull JsonNode addressableTag) throws IllegalArgumentException { - try { - 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(); - 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; - } catch (NumberFormatException e) { - throw new IllegalArgumentException( - String.format("Malformed JsonNode addressable tag: [%s]", addressableTag.asText()), e); + return addressTag; + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + String.format("Malformed JsonNode addressable tag: [%s]", addressableTag.asText()), e); + } } - } - @Override - public String getFilterableValue() { - T addressableTag = getAddressableTag(); - Integer kind = addressableTag.getKind(); - String hexString = addressableTag.getPublicKey().toHexString(); - String id = addressableTag.getIdentifierTag().getId(); + @Override + public String getFilterableValue() { + T addressableTag = getAddressableTag(); + Integer kind = addressableTag.getKind(); + String hexString = addressableTag.getPublicKey().toHexString(); + String id = addressableTag.getIdentifierTag().getId(); - return Stream.of(kind, hexString, id) - .map(Object::toString) - .collect(Collectors.joining(":")); - } + return Stream.of(kind, hexString, id) + .map(Object::toString) + .collect(Collectors.joining(":")); + } - private boolean compare(@NonNull GenericEvent genericEvent) { - T addressableTag = getAddressableTag(); - return - !genericEvent.getPubKey().toHexString().equals( - addressableTag.getPublicKey().toHexString()) || - !genericEvent.getKind().equals( - addressableTag.getKind()) || - getTypeSpecificTags(IdentifierTag.class, genericEvent).stream() - .anyMatch(identifierTag -> - identifierTag.getId().equals( - addressableTag.getIdentifierTag().getId())); - } + private boolean compare(@NonNull GenericEvent genericEvent) { + T addressableTag = getAddressableTag(); + return + !genericEvent.getPubKey().toHexString().equals( + addressableTag.getPublicKey().toHexString()) || + !genericEvent.getKind().equals( + addressableTag.getKind()) || + Filterable.getTypeSpecificTags(IdentifierTag.class, genericEvent).stream() + .anyMatch(identifierTag -> + identifierTag.getId().equals( + addressableTag.getIdentifierTag().getId())); + } - private T getAddressableTag() { - return super.getFilterable(); - } + private T getAddressableTag() { + return super.getFilterable(); + } - public static Function fxn = node -> new AddressableTagFilter<>(AddressableTagFilter.createAddressTag(node)); + public static Function fxn = node -> new AddressableTagFilter<>(AddressableTagFilter.createAddressTag(node)); } 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 70654ee32..b0cac310c 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,8 @@ package nostr.event.filter; -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; @@ -10,15 +10,15 @@ import java.util.Optional; import java.util.function.Predicate; -public interface Filterable { - ObjectMapper mapper = new ObjectMapper(); +import static nostr.base.IEvent.MAPPER_AFTERBURNER; +public interface Filterable { Predicate getPredicate(); T getFilterable(); Object getFilterableValue(); String getFilterKey(); - default List getTypeSpecificTags(Class tagClass, GenericEvent event) { + static List getTypeSpecificTags(@NonNull Class tagClass, @NonNull GenericEvent event) { return event.getTags().stream() .filter(tagClass::isInstance) .map(tagClass::cast) @@ -26,7 +26,7 @@ default List getTypeSpecificTags(Class tagClass, Gener } default ObjectNode toObjectNode(ObjectNode objectNode) { - ArrayNode arrayNode = mapper.createArrayNode(); + ArrayNode arrayNode = MAPPER_AFTERBURNER.createArrayNode(); Optional.ofNullable(objectNode.get(getFilterKey())) .ifPresent(jsonNode -> @@ -39,7 +39,7 @@ default ObjectNode toObjectNode(ObjectNode objectNode) { default void addToArrayNode(ArrayNode arrayNode) { arrayNode.addAll( - mapper.createArrayNode().add( + MAPPER_AFTERBURNER.createArrayNode().add( getFilterableValue().toString())); } } 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 b624e396b..c92ffeb98 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 @@ -12,47 +12,47 @@ @EqualsAndHashCode(callSuper = true) public class GenericTagQueryFilter extends AbstractFilterable { - public static final String HASH_PREFIX = "#"; - - public GenericTagQueryFilter(T genericTagQuery) { - super(genericTagQuery, genericTagQuery.getTagName()); - } - - @Override - public Predicate getPredicate() { - return (genericEvent) -> - getTypeSpecificTags(GenericTag.class, genericEvent).stream() - .filter(genericTag -> - genericTag.getCode().equals(stripLeadingHashTag())) - .anyMatch(genericTag -> - genericTag - .getAttributes().stream().map( - ElementAttribute::getValue).toList() - .contains( - getFilterableValue())); - } - - @Override - public String getFilterKey() { - return getGenericTagQuery().getTagName(); - } - - @Override - public String getFilterableValue() { - return getGenericTagQuery().getValue(); - } - - private T getGenericTagQuery() { - return super.getFilterable(); - } - - private String stripLeadingHashTag() { - return getFilterKey().startsWith(HASH_PREFIX) ? - getFilterKey().substring(1) : - getFilterKey(); - } - - public static Function fxn(String type) { - return node -> new GenericTagQueryFilter<>(new GenericTagQuery(type, node.asText())); - } + public static final String HASH_PREFIX = "#"; + + public GenericTagQueryFilter(T genericTagQuery) { + super(genericTagQuery, genericTagQuery.getTagName()); + } + + @Override + public Predicate getPredicate() { + return (genericEvent) -> + Filterable.getTypeSpecificTags(GenericTag.class, genericEvent).stream() + .filter(genericTag -> + genericTag.getCode().equals(stripLeadingHashTag())) + .anyMatch(genericTag -> + genericTag + .getAttributes().stream().map( + ElementAttribute::getValue).toList() + .contains( + getFilterableValue())); + } + + @Override + public String getFilterKey() { + return getGenericTagQuery().getTagName(); + } + + @Override + public String getFilterableValue() { + return getGenericTagQuery().getValue(); + } + + private T getGenericTagQuery() { + return super.getFilterable(); + } + + private String stripLeadingHashTag() { + return getFilterKey().startsWith(HASH_PREFIX) ? + getFilterKey().substring(1) : + getFilterKey(); + } + + public static Function fxn(String type) { + return node -> new GenericTagQueryFilter<>(new GenericTagQuery(type, node.asText())); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/GeohashTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/GeohashTagFilter.java index c565ff58d..2cec9cb28 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/GeohashTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/GeohashTagFilter.java @@ -10,27 +10,27 @@ @EqualsAndHashCode(callSuper = true) public class GeohashTagFilter extends AbstractFilterable { - public final static String FILTER_KEY = "#g"; + public final static String FILTER_KEY = "#g"; - public GeohashTagFilter(T geohashTag) { - super(geohashTag, FILTER_KEY); - } + public GeohashTagFilter(T geohashTag) { + super(geohashTag, FILTER_KEY); + } - @Override - public Predicate getPredicate() { - return (genericEvent) -> - getTypeSpecificTags(GeohashTag.class, genericEvent).stream().anyMatch(geoHashTag -> - geoHashTag.getLocation().equals(getFilterableValue())); - } + @Override + public Predicate getPredicate() { + return (genericEvent) -> + Filterable.getTypeSpecificTags(GeohashTag.class, genericEvent).stream().anyMatch(geoHashTag -> + geoHashTag.getLocation().equals(getFilterableValue())); + } - @Override - public String getFilterableValue() { - return getGeoHashTag().getLocation(); - } + @Override + public String getFilterableValue() { + return getGeoHashTag().getLocation(); + } - private T getGeoHashTag() { - return super.getFilterable(); - } + private T getGeoHashTag() { + return super.getFilterable(); + } - public static Function fxn = node -> new GeohashTagFilter<>(new GeohashTag(node.asText())); + public static Function fxn = node -> new GeohashTagFilter<>(new GeohashTag(node.asText())); } diff --git a/nostr-java-event/src/main/java/nostr/event/filter/HashtagTagFilter.java b/nostr-java-event/src/main/java/nostr/event/filter/HashtagTagFilter.java index 926359f1e..f4b4bea04 100644 --- a/nostr-java-event/src/main/java/nostr/event/filter/HashtagTagFilter.java +++ b/nostr-java-event/src/main/java/nostr/event/filter/HashtagTagFilter.java @@ -10,27 +10,27 @@ @EqualsAndHashCode(callSuper = true) public class HashtagTagFilter extends AbstractFilterable { - public final static String FILTER_KEY = "#t"; + public final static String FILTER_KEY = "#t"; - public HashtagTagFilter(T hashtagTag) { - super(hashtagTag, FILTER_KEY); - } + public HashtagTagFilter(T hashtagTag) { + super(hashtagTag, FILTER_KEY); + } - @Override - public Predicate getPredicate() { - return (genericEvent) -> - getTypeSpecificTags(HashtagTag.class, genericEvent).stream().anyMatch(hashtagTag -> - hashtagTag.getHashTag().equals(getFilterableValue())); - } + @Override + public Predicate getPredicate() { + return (genericEvent) -> + Filterable.getTypeSpecificTags(HashtagTag.class, genericEvent).stream().anyMatch(hashtagTag -> + hashtagTag.getHashTag().equals(getFilterableValue())); + } - @Override - public String getFilterableValue() { - return getHashtagTag().getHashTag(); - } + @Override + public String getFilterableValue() { + return getHashtagTag().getHashTag(); + } - private T getHashtagTag() { - return super.getFilterable(); - } + private T getHashtagTag() { + return super.getFilterable(); + } - public static Function fxn = node -> new HashtagTagFilter<>(new HashtagTag(node.asText())); + public static Function fxn = node -> new HashtagTagFilter<>(new HashtagTag(node.asText())); } 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 9e0253689..8eb7b55b9 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 @@ -10,27 +10,28 @@ @EqualsAndHashCode(callSuper = true) public class IdentifierTagFilter extends AbstractFilterable { - public final static String FILTER_KEY = "#d"; - public IdentifierTagFilter(T identifierTag) { - super(identifierTag, FILTER_KEY); - } - - @Override - public Predicate getPredicate() { - return (genericEvent) -> - getTypeSpecificTags(IdentifierTag.class, genericEvent).stream() - .anyMatch(genericEventIdentifierTag -> - genericEventIdentifierTag.getId().equals(getFilterableValue())); - } - - @Override - public String getFilterableValue() { - return getIdentifierTag().getId(); - } - - private T getIdentifierTag() { - return super.getFilterable(); - } - - public static Function fxn = node -> new IdentifierTagFilter<>(new IdentifierTag(node.asText())); + public final static String FILTER_KEY = "#d"; + + public IdentifierTagFilter(T identifierTag) { + super(identifierTag, FILTER_KEY); + } + + @Override + public Predicate getPredicate() { + return (genericEvent) -> + Filterable.getTypeSpecificTags(IdentifierTag.class, genericEvent).stream() + .anyMatch(genericEventIdentifierTag -> + genericEventIdentifierTag.getId().equals(getFilterableValue())); + } + + @Override + public String getFilterableValue() { + return getIdentifierTag().getId(); + } + + private T getIdentifierTag() { + return super.getFilterable(); + } + + public static Function fxn = node -> new IdentifierTagFilter<>(new IdentifierTag(node.asText())); } 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 4d5b10f63..273e8d1f7 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 @@ -9,6 +9,8 @@ import java.util.function.Function; import java.util.function.Predicate; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; + @EqualsAndHashCode(callSuper = true) public class KindFilter extends AbstractFilterable { public final static String FILTER_KEY = "kinds"; @@ -26,7 +28,7 @@ public Predicate getPredicate() { @Override public void addToArrayNode(ArrayNode arrayNode) { arrayNode.addAll( - mapper.createArrayNode().add( + MAPPER_AFTERBURNER.createArrayNode().add( getFilterableValue())); } 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 7db2c97f1..50950847a 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 @@ -10,28 +10,28 @@ @EqualsAndHashCode(callSuper = true) public class ReferencedEventFilter extends AbstractFilterable { - public final static String FILTER_KEY = "#e"; - - public ReferencedEventFilter(T referencedEventTag) { - super(referencedEventTag, FILTER_KEY); - } - - @Override - public Predicate getPredicate() { - return (genericEvent) -> - getTypeSpecificTags(EventTag.class, genericEvent).stream() - .anyMatch(eventTag -> - eventTag.getIdEvent().equals(getFilterableValue())); - } - - @Override - public String getFilterableValue() { - return getReferencedEventTag().getIdEvent(); - } - - private T getReferencedEventTag() { - return super.getFilterable(); - } - - public static Function fxn = node -> new ReferencedEventFilter<>(new EventTag(node.asText())); + public final static String FILTER_KEY = "#e"; + + public ReferencedEventFilter(T referencedEventTag) { + super(referencedEventTag, FILTER_KEY); + } + + @Override + public Predicate getPredicate() { + return (genericEvent) -> + Filterable.getTypeSpecificTags(EventTag.class, genericEvent).stream() + .anyMatch(eventTag -> + eventTag.getIdEvent().equals(getFilterableValue())); + } + + @Override + public String getFilterableValue() { + return getReferencedEventTag().getIdEvent(); + } + + private T getReferencedEventTag() { + return super.getFilterable(); + } + + public static Function fxn = node -> new ReferencedEventFilter<>(new EventTag(node.asText())); } 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 11b54a4c9..a22439cad 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 @@ -11,28 +11,28 @@ @EqualsAndHashCode(callSuper = true) public class ReferencedPublicKeyFilter extends AbstractFilterable { - public final static String FILTER_KEY = "#p"; - - public ReferencedPublicKeyFilter(T referencedPubKeyTag) { - super(referencedPubKeyTag, FILTER_KEY); - } - - @Override - public Predicate getPredicate() { - return (genericEvent) -> - getTypeSpecificTags(PubKeyTag.class, genericEvent).stream() - .anyMatch(pubKeyTag -> - pubKeyTag.getPublicKey().toHexString().equals(getFilterableValue())); - } - - @Override - public String getFilterableValue() { - return getReferencedPublicKey().getPublicKey().toHexString(); - } - - private T getReferencedPublicKey() { - return super.getFilterable(); - } - - public static Function fxn = node -> new ReferencedPublicKeyFilter<>(new PubKeyTag(new PublicKey(node.asText()))); + public final static String FILTER_KEY = "#p"; + + public ReferencedPublicKeyFilter(T referencedPubKeyTag) { + super(referencedPubKeyTag, FILTER_KEY); + } + + @Override + public Predicate getPredicate() { + return (genericEvent) -> + Filterable.getTypeSpecificTags(PubKeyTag.class, genericEvent).stream() + .anyMatch(pubKeyTag -> + pubKeyTag.getPublicKey().toHexString().equals(getFilterableValue())); + } + + @Override + public String getFilterableValue() { + return getReferencedPublicKey().getPublicKey().toHexString(); + } + + private T getReferencedPublicKey() { + return super.getFilterable(); + } + + public static Function fxn = node -> new ReferencedPublicKeyFilter<>(new PubKeyTag(new PublicKey(node.asText()))); } 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 8c7f6ebcd..edcd31087 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 @@ -9,6 +9,8 @@ import java.util.function.Function; import java.util.function.Predicate; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; + @EqualsAndHashCode(callSuper = true) public class SinceFilter extends AbstractFilterable { public final static String FILTER_KEY = "since"; @@ -25,7 +27,7 @@ public Predicate getPredicate() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return mapper.createObjectNode().put(FILTER_KEY, getSince()); + return MAPPER_AFTERBURNER.createObjectNode().put(FILTER_KEY, getSince()); } @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 5876724fe..48e88179e 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 @@ -9,6 +9,8 @@ import java.util.function.Function; import java.util.function.Predicate; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; + @EqualsAndHashCode(callSuper = true) public class UntilFilter extends AbstractFilterable { public final static String FILTER_KEY = "until"; @@ -25,7 +27,7 @@ public Predicate getPredicate() { @Override public ObjectNode toObjectNode(ObjectNode objectNode) { - return mapper.createObjectNode().put(FILTER_KEY, getUntil()); + return MAPPER_AFTERBURNER.createObjectNode().put(FILTER_KEY, getUntil()); } @Override 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 index 6243aa523..722323c84 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/CalendarRsvpEvent.java @@ -4,7 +4,6 @@ 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; @@ -18,7 +17,6 @@ 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; @@ -55,7 +53,7 @@ public CalendarRsvpEventDeserializer() { super(CalendarRsvpEvent.class); } -// TODO: below methods needs comprehensive tags assignment completion + // TODO: below methods needs comprehensive tags assignment completion @Override public CalendarRsvpEvent deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { JsonNode calendarTimeBasedEventNode = jsonParser.getCodec().readTree(jsonParser); @@ -66,7 +64,7 @@ public CalendarRsvpEvent deserialize(JsonParser jsonParser, DeserializationConte .map( JsonNode::elements) .map(element -> - new ObjectMapper().convertValue(element, BaseTag.class)).toList(); + MAPPER_AFTERBURNER.convertValue(element, BaseTag.class)).toList(); List genericTags = baseTags.stream() .filter(GenericTag.class::isInstance) 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..11b5647a7 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 @@ -4,7 +4,6 @@ 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; @@ -74,7 +73,7 @@ public CalendarTimeBasedEvent deserialize(JsonParser jsonParser, Deserialization .map( JsonNode::elements) .map(element -> - new ObjectMapper().convertValue(element, BaseTag.class)).toList(); + MAPPER_AFTERBURNER.convertValue(element, BaseTag.class)).toList(); List genericTags = baseTags.stream() .filter(GenericTag.class::isInstance) diff --git a/nostr-java-event/src/main/java/nostr/event/impl/CanonicalAuthenticationEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/CanonicalAuthenticationEvent.java index 7c4fa5f60..d11b6db12 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/CanonicalAuthenticationEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/CanonicalAuthenticationEvent.java @@ -23,16 +23,16 @@ public CanonicalAuthenticationEvent(@NonNull PublicKey pubKey, @NonNull String c // Challenge tag List chAttributes = new ArrayList<>(); - var attribute = ElementAttribute.builder().nip(42).name("challenge").value(challenge).build(); + var attribute = ElementAttribute.builder().name("challenge").value(challenge).build(); chAttributes.add(attribute); - BaseTag challengeTag = new GenericTag("challenge", 42, chAttributes); + BaseTag challengeTag = new GenericTag("challenge", chAttributes); this.addTag(challengeTag); // Relay tag final List relayAttributes = new ArrayList<>(); - final ElementAttribute relayAttribute = ElementAttribute.builder().nip(42).name("uri").value(relay.getUri()).build(); + final ElementAttribute relayAttribute = ElementAttribute.builder().name("uri").value(relay.getUri()).build(); relayAttributes.add(relayAttribute); - final BaseTag relayTag = new GenericTag("relay", 42, relayAttributes); + final BaseTag relayTag = new GenericTag("relay", relayAttributes); this.addTag(relayTag); } } diff --git a/nostr-java-event/src/main/java/nostr/event/impl/ClassifiedListingEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/ClassifiedListingEvent.java index 6d392578a..c0520514e 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/ClassifiedListingEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/ClassifiedListingEvent.java @@ -4,7 +4,7 @@ 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; @@ -85,7 +85,7 @@ public ClassifiedListingEvent deserialize(JsonParser jsonParser, Deserialization .map( JsonNode::elements) .map(element -> - new ObjectMapper().convertValue(element, BaseTag.class)).toList(); + MAPPER_AFTERBURNER.convertValue(element, BaseTag.class)).toList(); List genericTags = baseTags.stream() .filter(GenericTag.class::isInstance) 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 e31c423e6..59098896d 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 @@ -1,26 +1,15 @@ package nostr.event.impl; -import java.beans.Transient; -import java.nio.charset.StandardCharsets; -import java.security.NoSuchAlgorithmException; -import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.logging.Level; - import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.node.JsonNodeFactory; - import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NonNull; import lombok.extern.java.Log; import nostr.base.ElementAttribute; -import nostr.base.IEncoder; import nostr.base.IGenericElement; import nostr.base.ISignable; import nostr.base.ITag; @@ -39,8 +28,21 @@ import nostr.util.NostrUtil; import nostr.util.thread.HexStringValidator; +import java.beans.Transient; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Level; + +import static nostr.base.Encoder.ENCODER_MAPPED_AFTERBURNER; + /** - * * @author squirrel */ @Log @@ -117,12 +119,12 @@ public GenericEvent(@NonNull PublicKey pubKey, @NonNull Kind kind, @NonNull List } public GenericEvent(@NonNull PublicKey pubKey, @NonNull Kind kind, @NonNull List tags, - @NonNull String content) { + @NonNull String content) { this(pubKey, kind.getValue(), tags, content); } public GenericEvent(@NonNull PublicKey pubKey, @NonNull Integer kind, @NonNull List tags, - @NonNull String content) { + @NonNull String content) { this.pubKey = pubKey; this.kind = Kind.valueOf(kind).getValue(); this.tags = tags; @@ -146,8 +148,8 @@ public String toBech32() { try { return Bech32.toBech32(Bech32Prefix.NOTE, this.getId()); - } catch (NostrException ex) { - throw new RuntimeException(ex); + } catch (Exception e) { + throw new RuntimeException(e); } } @@ -194,8 +196,13 @@ public boolean isSigned() { } @Override - public void addAttribute(ElementAttribute attribute) { - this.attributes.add(attribute); + public void addAttribute(ElementAttribute... attribute) { + addAttributes(List.of(attribute)); + } + + @Override + public void addAttributes(List attributes) { + this.attributes.addAll(attributes); } protected void validate() { @@ -203,7 +210,7 @@ protected void validate() { } private String serialize() throws NostrException { - var mapper = IEncoder.MAPPER; + var mapper = ENCODER_MAPPED_AFTERBURNER; var arrayNode = JsonNodeFactory.instance.arrayNode(); try { @@ -220,6 +227,20 @@ private String serialize() throws NostrException { } } + @Transient + @Override + public Consumer getSignatureConsumer() { + return this::setSignature; + } + + @Transient + @Override + public Supplier getByeArraySupplier() { + this.update(); + log.log(Level.FINER, "Serialized event: {0}", new String(this.get_serializedEvent())); + return () -> ByteBuffer.wrap(this.get_serializedEvent()); + } + protected final void updateTagsParents(List tagList) { if (tagList != null && !tagList.isEmpty()) { for (ITag t : tagList) { @@ -237,10 +258,10 @@ protected void addStandardTag(BaseTag tag) { } protected void addGenericTag(String key, Integer nip, Object value) { - Optional.ofNullable(value).ifPresent(s -> addTag(GenericTag.create(key, nip, s.toString()))); + Optional.ofNullable(value).ifPresent(s -> addTag(GenericTag.create(key, s.toString()))); } protected void addStringListTag(String label, Integer nip, List tag) { - Optional.ofNullable(tag).ifPresent(tagList -> addGenericTag(label, nip, tagList)); + Optional.ofNullable(tag).ifPresent(tagList -> GenericTag.create(label, tagList)); } } diff --git a/nostr-java-event/src/main/java/nostr/event/impl/GenericMessage.java b/nostr-java-event/src/main/java/nostr/event/impl/GenericMessage.java index ee06ffeaf..c96d40e63 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/GenericMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/GenericMessage.java @@ -7,13 +7,14 @@ import lombok.Setter; import nostr.base.ElementAttribute; import nostr.base.IElement; -import nostr.base.IEncoder; import nostr.base.IGenericElement; import nostr.event.BaseMessage; import java.util.ArrayList; import java.util.List; +import static nostr.base.Encoder.ENCODER_MAPPED_AFTERBURNER; + /** * * @author squirrel @@ -26,9 +27,6 @@ public class GenericMessage extends BaseMessage implements IGenericElement, IEle @JsonIgnore private final List attributes; - @JsonIgnore - private final Integer nip; - public GenericMessage(String command) { this(command, new ArrayList<>(), 1); } @@ -40,19 +38,23 @@ public GenericMessage(String command, Integer nip) { public GenericMessage(String command, List attributes, Integer nip) { super(command); this.attributes = attributes; - this.nip = nip; } @Override - public void addAttribute(ElementAttribute attribute) { - this.attributes.add(attribute); + public void addAttribute(ElementAttribute... attribute) { + addAttributes(List.of(attribute)); + } + + @Override + public void addAttributes(List attributes) { + this.attributes.addAll(attributes); } @Override public String encode() throws JsonProcessingException { getArrayNode().add(getCommand()); getAttributes().stream().map(ElementAttribute::getValue).forEach(v -> getArrayNode().add(v.toString())); - return IEncoder.MAPPER.writeValueAsString(getArrayNode()); + return ENCODER_MAPPED_AFTERBURNER.writeValueAsString(getArrayNode()); } public static T decode(@NonNull Object[] msgArr) { diff --git a/nostr-java-event/src/main/java/nostr/event/impl/GenericTag.java b/nostr-java-event/src/main/java/nostr/event/impl/GenericTag.java index f66e09ce1..3148f8a59 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/GenericTag.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/GenericTag.java @@ -1,56 +1,90 @@ package nostr.event.impl; -import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NonNull; import nostr.base.ElementAttribute; import nostr.base.IGenericElement; import nostr.event.BaseTag; import java.util.ArrayList; import java.util.List; +import java.util.stream.IntStream; /** * @author squirrel */ @Data @EqualsAndHashCode(callSuper = false) -@AllArgsConstructor public class GenericTag extends BaseTag implements IGenericElement { private final String code; - @JsonIgnore - @EqualsAndHashCode.Exclude - private final Integer nip; - private final List attributes; public GenericTag(String code) { - this(code, 1); + this(code, new ArrayList<>()); } + /** + * nip parameter to be removed + * + * @deprecated use any available proper constructor variant instead + */ + @Deprecated(forRemoval = true) public GenericTag(String code, Integer nip) { - this(code, nip, new ArrayList<>()); + this(code, new ArrayList<>()); + } + + public GenericTag(@NonNull String code, @NonNull ElementAttribute... attribute) { + this(code, List.of(attribute)); + } + + public GenericTag(@NonNull String code, @NonNull List attributes) { + this.code = code; + this.attributes = attributes; } @Override - public void addAttribute(ElementAttribute attribute) { - this.attributes.add(attribute); + public void addAttribute(@NonNull ElementAttribute... attribute) { + this.addAttributes(List.of(attribute)); } + @Override + public void addAttributes(@NonNull List attributes) { + this.attributes.addAll(attributes); + } + + /** + * nip parameter to be removed + * + * @deprecated use {@link #create(String, String...)} instead. + */ + @Deprecated(forRemoval = true) public static GenericTag create(String code, Integer nip, String... params) { - return create(code, nip, List.of(params)); + return create(code, List.of(params)); } + /** + * nip parameter to be removed + * + * @deprecated use {@link #create(String, List)} instead. + */ + + @Deprecated(forRemoval = true) public static GenericTag create(String code, Integer nip, List params) { - List attributes = new ArrayList<>(); - for (int i = 0; i < params.size(); i++) { - String name = "param" + i; - var p = params.get(i); - attributes.add(i, ElementAttribute.builder().name(name).value(p).build()); - } - return new GenericTag(code, nip, attributes); - } -} \ No newline at end of file + return create(code, params); + } + + public static GenericTag create(@NonNull String code, @NonNull String... params) { + return create(code, List.of(params)); + } + + public static GenericTag create(@NonNull String code, @NonNull List params) { + return new GenericTag(code, + IntStream.range(0, params.size()) + .mapToObj(i -> + new ElementAttribute("param".concat(String.valueOf(i)), params.get(i))) + .toList()); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/impl/InternetIdentifierMetadataEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/InternetIdentifierMetadataEvent.java index 179258a19..12ec416c7 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/InternetIdentifierMetadataEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/InternetIdentifierMetadataEvent.java @@ -3,8 +3,6 @@ import static nostr.util.NostrUtil.escapeJsonString; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -45,8 +43,7 @@ private void init(UserProfile profile) { private void setContent(UserProfile profile) { try { - ObjectMapper objectMapper = new ObjectMapper(); - String jsonString = objectMapper.writeValueAsString(new Nip05Obj(profile.getName(), profile.getNip05())); + String jsonString = MAPPER_AFTERBURNER.writeValueAsString(new Nip05Obj(profile.getName(), profile.getNip05())); // Escape the JSON string String escapedJsonString = escapeJsonString(jsonString); diff --git a/nostr-java-event/src/main/java/nostr/event/impl/MetadataEvent.java b/nostr-java-event/src/main/java/nostr/event/impl/MetadataEvent.java index 52e41e482..2d1340957 100644 --- a/nostr-java-event/src/main/java/nostr/event/impl/MetadataEvent.java +++ b/nostr-java-event/src/main/java/nostr/event/impl/MetadataEvent.java @@ -9,7 +9,6 @@ import lombok.Data; import lombok.EqualsAndHashCode; -import nostr.base.IEncoder; import nostr.base.PublicKey; import nostr.base.UserProfile; import nostr.base.annotation.Event; @@ -17,6 +16,8 @@ import nostr.event.Kind; import nostr.event.NIP01Event; +import static nostr.base.Encoder.ENCODER_MAPPED_AFTERBURNER; + /** * * @author squirrel @@ -59,7 +60,7 @@ public void update() { } private void setContent() { - var mapper = IEncoder.MAPPER; + var mapper = ENCODER_MAPPED_AFTERBURNER; try { ObjectNode objNode = JsonNodeFactory.instance.objectNode(); objNode.set("name", mapper.valueToTree(this.getProfile().getName())); diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseEventEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseEventEncoder.java index 8c47e017f..daf18b5b4 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseEventEncoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseEventEncoder.java @@ -1,17 +1,12 @@ package nostr.event.json.codec; -import com.fasterxml.jackson.core.JsonProcessingException; import lombok.Data; -import nostr.base.IEncoder; +import lombok.SneakyThrows; +import nostr.base.Encoder; import nostr.event.BaseEvent; -import nostr.util.NostrException; -/** - * @author guilhermegps - * - */ @Data -public class BaseEventEncoder implements IEncoder { +public class BaseEventEncoder implements Encoder { private final T event; @@ -20,19 +15,9 @@ public BaseEventEncoder(T event) { } @Override +// TODO: refactor all methods calling this to properly handle invalid json exception + @SneakyThrows public String encode() { - try { - return toJson(); - } catch (NostrException ex) { - throw new RuntimeException(ex); - } - } - - protected String toJson() throws NostrException { - try { - return IEncoder.MAPPER.writeValueAsString(event); - } catch (JsonProcessingException e) { - throw new NostrException(e); - } + return ENCODER_MAPPED_AFTERBURNER.writeValueAsString(event); } } 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 b84c1917d..b688e1193 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,8 +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.NoArgsConstructor; import lombok.NonNull; import lombok.SneakyThrows; import nostr.base.IDecoder; @@ -24,32 +23,27 @@ /** * @author eric */ +@NoArgsConstructor public class BaseMessageDecoder implements IDecoder { 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() { - mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); - } - @Override public T decode(@NonNull String jsonString) throws JsonProcessingException { ValidJsonNodeFirstPair validJsonNodeFirstPair = json_strCmd_arg(jsonString); String command = validJsonNodeFirstPair.formerly_strCmd(); Object subscriptionId = validJsonNodeFirstPair.formerly_arg(); - Object[] msgArr = mapper.readValue(jsonString, Object[].class); // TODO: replace with jsonNode after ReqMessage.decode() is finished + Object[] msgArr = I_DECODER_MAPPER_AFTERBURNER.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) : + CanonicalAuthenticationMessage.decode(map) : RelayAuthenticationMessage.decode(subscriptionId); case "CLOSE" -> CloseMessage.decode(subscriptionId); case "EOSE" -> EoseMessage.decode(subscriptionId); - case "EVENT" -> EventMessage.decode(msgArr, mapper); + case "EVENT" -> EventMessage.decode(msgArr, I_DECODER_MAPPER_AFTERBURNER); case "NOTICE" -> NoticeMessage.decode(subscriptionId); case "OK" -> OkMessage.decode(msgArr); case "REQ" -> ReqMessage.decode(subscriptionId, json_msgArr(jsonString)); @@ -59,18 +53,18 @@ public T decode(@NonNull String jsonString) throws JsonProcessingException { private ValidJsonNodeFirstPair json_strCmd_arg(@NonNull String jsonString) throws JsonProcessingException { return new ValidJsonNodeFirstPair( - mapper.readTree(jsonString).get(COMMAND_INDEX).asText(), - mapper.readTree(jsonString).get(ARG_INDEX).asText()); + I_DECODER_MAPPER_AFTERBURNER.readTree(jsonString).get(COMMAND_INDEX).asText(), + I_DECODER_MAPPER_AFTERBURNER.readTree(jsonString).get(ARG_INDEX).asText()); } private List json_msgArr(@NonNull String jsonString) throws JsonProcessingException { - return IntStream.range(FILTERS_START_INDEX, mapper.readTree(jsonString).size()) + return IntStream.range(FILTERS_START_INDEX, I_DECODER_MAPPER_AFTERBURNER.readTree(jsonString).size()) .mapToObj(idx -> readTree(jsonString, idx)).toList(); } @SneakyThrows private String readTree(String jsonString, int idx) { - return mapper.readTree(jsonString).get(idx).toString(); + return I_DECODER_MAPPER_AFTERBURNER.readTree(jsonString).get(idx).toString(); } private record ValidJsonNodeFirstPair( diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagDecoder.java index 46e6fe3d6..559852baf 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagDecoder.java @@ -1,11 +1,12 @@ package nostr.event.json.codec; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import nostr.base.IDecoder; import nostr.event.BaseTag; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; + /** * * @author eric @@ -22,8 +23,7 @@ public BaseTagDecoder() { @Override public T decode(String jsonString) { try { - ObjectMapper mapper = new ObjectMapper(); - return mapper.readValue(jsonString, clazz); + return MAPPER_AFTERBURNER.readValue(jsonString, clazz); } catch (JsonProcessingException ex) { throw new RuntimeException(ex); } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagEncoder.java index 9dc9dc6f8..71eeefce2 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagEncoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/BaseTagEncoder.java @@ -1,43 +1,33 @@ package nostr.event.json.codec; -import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import lombok.AllArgsConstructor; - import lombok.Data; import lombok.NonNull; -import nostr.base.IEncoder; -import nostr.base.Relay; +import nostr.base.Encoder; import nostr.event.BaseTag; import nostr.event.json.serializer.TagSerializer; -/** - * @author guilhermegps - * - */ @Data -@AllArgsConstructor -public class BaseTagEncoder implements IEncoder { +public class BaseTagEncoder implements Encoder { + public static final ObjectMapper BASETAGENCODER_MAPPED_AFTERBURNER = + ENCODER_MAPPED_AFTERBURNER.copy() + .registerModule( + new SimpleModule().addSerializer( + new TagSerializer())); private final BaseTag tag; - private final Relay relay; public BaseTagEncoder(@NonNull BaseTag tag) { - this(tag, null); + this.tag = tag; } @Override public String encode() { try { - SimpleModule module = new SimpleModule(); - module.addSerializer(new TagSerializer()); - var mapper = (new ObjectMapper()) - .setSerializationInclusion(Include.NON_NULL) - .registerModule(module); - - return mapper.writeValueAsString(tag); + String s = BASETAGENCODER_MAPPED_AFTERBURNER.writeValueAsString(tag); + return s; } catch (JsonProcessingException e) { throw new RuntimeException(e); } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/FDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/FDecoder.java index 48160a18b..24a894d78 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/FDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/FDecoder.java @@ -1,5 +1,6 @@ package nostr.event.json.codec; public interface FDecoder { + T decode(String str); } 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 abde5f764..bffc41cb2 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,42 +1,27 @@ package nostr.event.json.codec; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import lombok.NonNull; import lombok.SneakyThrows; -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; -import nostr.event.filter.HashtagTagFilter; -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 java.util.ArrayList; import java.util.List; -import java.util.function.Function; -import java.util.stream.StreamSupport; + +import static nostr.base.IEvent.MAPPER_AFTERBURNER; /** * @author eric */ @Data public class FiltersDecoder implements FDecoder { - private final static ObjectMapper mapper = new ObjectMapper(); @SneakyThrows public Filters decode(@NonNull String jsonFiltersList) { final List filterables = new ArrayList<>(); - mapper.readTree(jsonFiltersList).fields().forEachRemaining(node -> + MAPPER_AFTERBURNER.readTree(jsonFiltersList).fields().forEachRemaining(node -> filterables.addAll( FilterableProvider.getFilterFunction( node.getValue(), 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 5675cd752..2325bd924 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 @@ -3,12 +3,12 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Data; import lombok.EqualsAndHashCode; -import nostr.base.FEncoder; +import nostr.base.Encoder; import nostr.event.filter.Filters; @Data @EqualsAndHashCode(callSuper = false) -public class FiltersEncoder implements FEncoder { +public class FiltersEncoder implements Encoder { private final Filters filters; public FiltersEncoder(Filters filters) { @@ -17,17 +17,17 @@ public FiltersEncoder(Filters filters) { @Override public String encode() { - ObjectNode root = MAPPER.createObjectNode(); + ObjectNode root = ENCODER_MAPPED_AFTERBURNER.createObjectNode(); filters.getFiltersMap().forEach((key, filterableList) -> { - final ObjectNode objectNode = MAPPER.createObjectNode(); + final ObjectNode objectNode = ENCODER_MAPPED_AFTERBURNER.createObjectNode(); root.setAll( - filterableList - .stream() - .map(filterable -> - filterable.toObjectNode(objectNode)) - .toList() - .getFirst()); + filterableList + .stream() + .map(filterable -> + filterable.toObjectNode(objectNode)) + .toList() + .getFirst()); }); return root.toString(); 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 533d61e19..d78e98332 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 @@ -2,7 +2,6 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import nostr.base.IDecoder; import nostr.event.impl.GenericEvent; @@ -26,8 +25,7 @@ public GenericEventDecoder(Class clazz) { @Override 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); + I_DECODER_MAPPER_AFTERBURNER.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + return I_DECODER_MAPPER_AFTERBURNER.readValue(jsonEvent, clazz); } } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagDecoder.java index e8658a779..d65ba7c22 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagDecoder.java @@ -1,42 +1,40 @@ package nostr.event.json.codec; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.util.ArrayList; -import java.util.List; - import lombok.Data; +import lombok.NonNull; import nostr.base.ElementAttribute; import nostr.base.IDecoder; import nostr.event.impl.GenericTag; +import java.util.stream.IntStream; + @Data public class GenericTagDecoder implements IDecoder { private final Class clazz; public GenericTagDecoder() { - this.clazz = (Class) GenericTag.class; + this((Class) GenericTag.class); + } + + public GenericTagDecoder(@NonNull Class clazz) { + this.clazz = clazz; } @Override - public T decode(String json) { + public T decode(@NonNull String json) { try { - ObjectMapper objectMapper = new ObjectMapper(); - String[] jsonElements = objectMapper.readValue(json, String[].class); - - String code = jsonElements[0]; - - List attributes = new ArrayList<>(); - for (int i = 1; i < jsonElements.length; i++) { - ElementAttribute attribute = new ElementAttribute("param"+(i-1), jsonElements[i], null); - if (!attributes.contains(attribute)) { - attributes.add(attribute); - } - } - - return (T) new GenericTag(code, null, attributes); + String[] jsonElements = I_DECODER_MAPPER_AFTERBURNER.readValue(json, String[].class); + GenericTag genericTag = new GenericTag( + jsonElements[0], + IntStream.of(1, jsonElements.length-1) + .mapToObj(i -> new ElementAttribute( + "param".concat(String.valueOf(i - 1)), + jsonElements[i])) + .distinct() + .toList()); + return (T) genericTag; } catch (JsonProcessingException ex) { throw new RuntimeException(ex); } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagEncoder.java deleted file mode 100644 index 5eb8c3205..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagEncoder.java +++ /dev/null @@ -1,30 +0,0 @@ -package nostr.event.json.codec; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NonNull; -import nostr.base.IEncoder; -import nostr.base.Relay; -import nostr.event.impl.GenericTag; - -/** - * - * @author eric - */ -@Data -@AllArgsConstructor -public class GenericTagEncoder implements IEncoder { - - private final GenericTag tag; - private final Relay relay; - - public GenericTagEncoder(@NonNull GenericTag tag) { - this(tag, null); - } - - @Override - public String encode() { - var encoder = new BaseTagEncoder(tag, relay); - return encoder.encode(); - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagQueryEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagQueryEncoder.java deleted file mode 100644 index 006b13c19..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/GenericTagQueryEncoder.java +++ /dev/null @@ -1,35 +0,0 @@ -package nostr.event.json.codec; - -import com.fasterxml.jackson.core.JsonProcessingException; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import nostr.base.FEncoder; -import nostr.base.GenericTagQuery; -import nostr.base.Relay; - -/** - * @author guilhermegps - * - */ -@Data -@EqualsAndHashCode(callSuper = false) -@AllArgsConstructor -public class GenericTagQueryEncoder implements FEncoder { - - private final GenericTagQuery genericTagQuery; - private final Relay relay; - - public GenericTagQueryEncoder(GenericTagQuery tag) { - this(tag, null); - } - - @Override - public String encode() { - try { - return FEncoder.MAPPER.writeValueAsString(genericTagQuery); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/Nip05ContentDecoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/Nip05ContentDecoder.java index 44cab726e..bf3bb681b 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/Nip05ContentDecoder.java +++ b/nostr-java-event/src/main/java/nostr/event/json/codec/Nip05ContentDecoder.java @@ -1,11 +1,12 @@ package nostr.event.json.codec; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import nostr.base.IDecoder; import nostr.event.Nip05Content; +import static nostr.base.IEvent.MAPPER_AFTERBURNER; + /** * * @author eric @@ -22,7 +23,7 @@ public Nip05ContentDecoder() { @Override public T decode(String jsonContent) { try { - return new ObjectMapper().readValue(jsonContent, clazz); + return MAPPER_AFTERBURNER.readValue(jsonContent, clazz); } catch (JsonProcessingException ex) { throw new RuntimeException(ex); } diff --git a/nostr-java-event/src/main/java/nostr/event/json/codec/OstEventEncoder.java b/nostr-java-event/src/main/java/nostr/event/json/codec/OstEventEncoder.java deleted file mode 100644 index 91b3a581c..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/codec/OstEventEncoder.java +++ /dev/null @@ -1,58 +0,0 @@ -package nostr.event.json.codec; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.HashMap; -import lombok.AllArgsConstructor; - -import lombok.Data; -import nostr.base.ElementAttribute; -import nostr.base.IEncoder; -import nostr.base.Relay; -import nostr.event.impl.OtsEvent; -import nostr.util.NostrException; - -/** - * @author guilhermegps - * - */ -@Data -@AllArgsConstructor -public class OstEventEncoder implements IEncoder { - - private final OtsEvent event; - private final Relay relay; - - public OstEventEncoder(OtsEvent event) { - this(event, null); - } - - @Override - public String encode() { - try { - return toJson(); - } catch (NostrException ex) { - throw new RuntimeException(ex); - } - } - - private String toJson() throws NostrException { - try { - JsonNode node = IEncoder.MAPPER.valueToTree(event); - ObjectNode objNode = (ObjectNode) node; - event.getAttributes().parallelStream() - .map(ElementAttribute::getValue) - .forEach(ev -> { - var expression = (HashMap) ev; - - objNode.set("ots", IEncoder.MAPPER.valueToTree(expression.get("ots"))); - }); - - return IEncoder.MAPPER.writeValueAsString(node); - } catch (JsonProcessingException | IllegalArgumentException e) { - throw new NostrException(e); - } - } - -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomEventListDeserializer.java b/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomEventListDeserializer.java deleted file mode 100644 index ab163dacd..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomEventListDeserializer.java +++ /dev/null @@ -1,29 +0,0 @@ -package nostr.event.json.deserializer; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import lombok.NoArgsConstructor; -import nostr.event.impl.GenericEvent; -import nostr.event.json.codec.GenericEventDecoder; - -import java.util.ArrayList; -import java.util.List; -import java.io.IOException; - -@NoArgsConstructor -public class CustomEventListDeserializer, U extends GenericEvent> extends JsonDeserializer { - - @Override - public T deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { - List eventList = new ArrayList<>(); - JsonNode node = jsonParser.readValueAsTree(); - if (node.isArray()) { - for (JsonNode n : node) { - eventList.add(new GenericEventDecoder().decode(n.toString())); - } - } - return (T) eventList; - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomKindListDeserializer.java b/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomKindListDeserializer.java deleted file mode 100644 index 47aeea1e3..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomKindListDeserializer.java +++ /dev/null @@ -1,26 +0,0 @@ -package nostr.event.json.deserializer; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import nostr.event.Kind; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -public class CustomKindListDeserializer extends JsonDeserializer> { - - @Override - public List deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { - List kindList = new ArrayList<>(); - JsonNode node = jsonParser.readValueAsTree(); - if (node.isArray()) { - for (JsonNode n : node) { - kindList.add(Kind.valueOf(Integer.decode(n.asText()))); - } - } - return kindList; - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomPublicKeyListDeserializer.java b/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomPublicKeyListDeserializer.java deleted file mode 100644 index 0c46be4c7..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/deserializer/CustomPublicKeyListDeserializer.java +++ /dev/null @@ -1,30 +0,0 @@ -package nostr.event.json.deserializer; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import lombok.NoArgsConstructor; -import nostr.base.PublicKey; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -@NoArgsConstructor -public class CustomPublicKeyListDeserializer extends JsonDeserializer> { - - @Override - // TODO: check ctxt use - public List deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - List publicKeyList = new ArrayList<>(); - JsonNode node = p.readValueAsTree(); - if (node.isArray()) { - for (JsonNode n : node) { - String hex = n.asText(); - publicKeyList.add(new PublicKey(hex)); - } - } - return publicKeyList; - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/serializer/ContactSerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/ContactSerializer.java deleted file mode 100644 index aad2b2f31..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/serializer/ContactSerializer.java +++ /dev/null @@ -1,24 +0,0 @@ -package nostr.event.json.serializer; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; -import nostr.event.impl.CustomerOrderEvent.Customer.Contact; - -/** - * - * @author eric - */ -public class ContactSerializer extends JsonSerializer { - - @Override - public void serialize(Contact contact, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException { - jsonGenerator.writeStartObject(); - jsonGenerator.writeStringField("nostr", contact.getPublicKey().toString()); - jsonGenerator.writeStringField("phone", contact.getPhone()); - jsonGenerator.writeStringField("email", contact.getEmail()); - jsonGenerator.writeEndObject(); - } - -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomBaseListSerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomBaseListSerializer.java deleted file mode 100644 index 2d3ba9b02..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomBaseListSerializer.java +++ /dev/null @@ -1,56 +0,0 @@ -package nostr.event.json.serializer; - -import java.io.IOException; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.stream.StreamSupport; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import nostr.base.IEncoder; -import nostr.event.BaseEvent; - -/** - * @author guilhermegps - * - */ -public class CustomBaseListSerializer, U extends BaseEvent> extends JsonSerializer { - - @Override - public void serialize(T value, JsonGenerator gen, SerializerProvider serializers) { - try { - var list = value.parallelStream().map(this::toJson).toList(); - - gen.writePOJO(list); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private JsonNode toJson(Object obj) { - var mapper = IEncoder.MAPPER; - try { - JsonNode node = mapper.valueToTree(obj); - - if (node.isObject()) { - Iterator> fields = node.fields(); - - var list = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(fields, Spliterator.ORDERED), false) - .map(f -> f.getValue().asText().toLowerCase()).toList(); - - return mapper.valueToTree(list); - } - - return node; - } catch (IllegalArgumentException e) { - throw new RuntimeException(e); - } - } - -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomIdEventListSerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomIdEventListSerializer.java deleted file mode 100644 index 63f507ef5..000000000 --- a/nostr-java-event/src/main/java/nostr/event/json/serializer/CustomIdEventListSerializer.java +++ /dev/null @@ -1,29 +0,0 @@ -package nostr.event.json.serializer; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import nostr.event.impl.GenericEvent; - -import java.io.IOException; -import java.util.List; -import java.util.Objects; - -/** - * @author guilhermegps - */ -public class CustomIdEventListSerializer, U extends GenericEvent> extends JsonSerializer { - - @Override - public void serialize(T value, JsonGenerator gen, SerializerProvider serializers) { - try { - - var list = value.stream().filter(Objects::nonNull).map(U::getId).toList(); - - gen.writePOJO(list); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - -} diff --git a/nostr-java-event/src/main/java/nostr/event/json/serializer/TagSerializer.java b/nostr-java-event/src/main/java/nostr/event/json/serializer/TagSerializer.java index 9c40e03e4..f3619a65f 100644 --- a/nostr-java-event/src/main/java/nostr/event/json/serializer/TagSerializer.java +++ b/nostr-java-event/src/main/java/nostr/event/json/serializer/TagSerializer.java @@ -16,6 +16,8 @@ import java.lang.reflect.Field; import java.util.List; +import static nostr.event.json.codec.BaseTagEncoder.BASETAGENCODER_MAPPED_AFTERBURNER; + /** * @author guilhermegps * @@ -33,7 +35,7 @@ public TagSerializer() { public void serialize(BaseTag value, JsonGenerator gen, SerializerProvider serializers) { try { // -- Create the node - final ObjectNode node = new ObjectNode(JsonNodeFactory.instance); + final ObjectNode node = BASETAGENCODER_MAPPED_AFTERBURNER.getNodeFactory().objectNode(); List fields = value.getSupportedFields(); // Populate the node with the fields data diff --git a/nostr-java-event/src/main/java/nostr/event/message/CanonicalAuthenticationMessage.java b/nostr-java-event/src/main/java/nostr/event/message/CanonicalAuthenticationMessage.java index 3599a1a10..c7ea876b5 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/CanonicalAuthenticationMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/CanonicalAuthenticationMessage.java @@ -3,13 +3,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Getter; import lombok.NonNull; import lombok.Setter; import lombok.SneakyThrows; import nostr.base.Command; -import nostr.base.IEncoder; import nostr.base.Relay; import nostr.event.BaseMessage; import nostr.event.impl.CanonicalAuthenticationEvent; @@ -20,6 +18,9 @@ import java.util.List; import java.util.Map; +import static nostr.base.IDecoder.I_DECODER_MAPPER_AFTERBURNER; +import static nostr.base.Encoder.ENCODER_MAPPED_AFTERBURNER; + /** * @author eric */ @@ -36,16 +37,16 @@ public CanonicalAuthenticationMessage(CanonicalAuthenticationEvent event) { } @Override public String encode() throws JsonProcessingException { - return IEncoder.MAPPER.writeValueAsString( + return ENCODER_MAPPED_AFTERBURNER.writeValueAsString( getArrayNode() .add(getCommand()) - .add(IEncoder.MAPPER.readTree( + .add(ENCODER_MAPPED_AFTERBURNER.readTree( new BaseEventEncoder<>(getEvent()).encode()))); } @SneakyThrows - public static T decode(@NonNull Map map, ObjectMapper mapper) { - var event = mapper.convertValue(map, new TypeReference() {}); + public static T decode(@NonNull Map map) { + var event = I_DECODER_MAPPER_AFTERBURNER.convertValue(map, new TypeReference() {}); List genericTags = event.getTags().stream() .filter(GenericTag.class::isInstance) @@ -62,7 +63,8 @@ public static T decode(@NonNull Map map, ObjectMapper ma } private static String getAttributeValue(List genericTags, String attributeName) { +// TODO: stream optional return genericTags.stream() .filter(tag -> tag.getCode().equalsIgnoreCase(attributeName)).map(GenericTag::getAttributes).toList().get(0).get(0).getValue().toString(); } -} \ No newline at end of file +} diff --git a/nostr-java-event/src/main/java/nostr/event/message/CloseMessage.java b/nostr-java-event/src/main/java/nostr/event/message/CloseMessage.java index 91d765bca..2bfbc38c0 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/CloseMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/CloseMessage.java @@ -6,9 +6,10 @@ import lombok.NonNull; import lombok.Setter; import nostr.base.Command; -import nostr.base.IEncoder; import nostr.event.BaseMessage; +import static nostr.base.Encoder.ENCODER_MAPPED_AFTERBURNER; + /** * * @author squirrel @@ -31,7 +32,7 @@ public CloseMessage(String subscriptionId) { @Override public String encode() throws JsonProcessingException { - return IEncoder.MAPPER.writeValueAsString( + return ENCODER_MAPPED_AFTERBURNER.writeValueAsString( getArrayNode() .add(getCommand()) .add(getSubscriptionId())); diff --git a/nostr-java-event/src/main/java/nostr/event/message/ClosedMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ClosedMessage.java deleted file mode 100644 index 628c7adbf..000000000 --- a/nostr-java-event/src/main/java/nostr/event/message/ClosedMessage.java +++ /dev/null @@ -1,35 +0,0 @@ -package nostr.event.message; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonProcessingException; -import lombok.Getter; -import lombok.NonNull; -import lombok.Setter; -import nostr.base.Command; -import nostr.base.IEncoder; -import nostr.event.BaseMessage; - -@Setter -@Getter -public class ClosedMessage extends BaseMessage { - - @JsonProperty - private final String subscriptionId; - - @JsonProperty - private final String message; - - public ClosedMessage(@NonNull String subId, @NonNull String message) { - super(Command.CLOSED.name()); - this.subscriptionId = subId; - this.message = message; - } - - @Override - public String encode() throws JsonProcessingException { - return IEncoder.MAPPER.writeValueAsString( - getArrayNode() - .add(getCommand()) - .add(getSubscriptionId())); - } -} diff --git a/nostr-java-event/src/main/java/nostr/event/message/ContactListMessage.java b/nostr-java-event/src/main/java/nostr/event/message/ContactListMessage.java deleted file mode 100644 index 9b5b70621..000000000 --- a/nostr-java-event/src/main/java/nostr/event/message/ContactListMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package nostr.event.message; - -import java.util.List; -import lombok.ToString; -import nostr.event.Kind; -import nostr.base.PublicKey; -import nostr.event.BaseTag; -import nostr.event.impl.GenericEvent; - -/** - * - * @author squirrel - */ -@ToString -public class ContactListMessage extends EventMessage { - - public ContactListMessage(List contactList, PublicKey publicKey) { - super(new GenericEvent(publicKey, Kind.CONTACT_LIST, contactList)); - } - -} diff --git a/nostr-java-event/src/main/java/nostr/event/message/EoseMessage.java b/nostr-java-event/src/main/java/nostr/event/message/EoseMessage.java index d00b57cf9..204f09623 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/EoseMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/EoseMessage.java @@ -7,9 +7,10 @@ import lombok.NonNull; import lombok.Setter; import nostr.base.Command; -import nostr.base.IEncoder; import nostr.event.BaseMessage; +import static nostr.base.Encoder.ENCODER_MAPPED_AFTERBURNER; + /** * * @author squirrel @@ -31,7 +32,7 @@ public EoseMessage(String subId) { @Override public String encode() throws JsonProcessingException { - return IEncoder.MAPPER.writeValueAsString( + return ENCODER_MAPPED_AFTERBURNER.writeValueAsString( getArrayNode() .add(getCommand()) .add(getSubscriptionId())); 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 7caf29d7d..0897e9684 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 @@ -8,7 +8,6 @@ import lombok.NonNull; import lombok.Setter; import nostr.base.Command; -import nostr.base.IEncoder; import nostr.base.IEvent; import nostr.event.BaseEvent; import nostr.event.BaseMessage; @@ -18,6 +17,8 @@ import java.util.Map; import java.util.Optional; +import static nostr.base.Encoder.ENCODER_MAPPED_AFTERBURNER; + @Setter @Getter public class EventMessage extends BaseMessage { @@ -43,11 +44,12 @@ public String encode() throws JsonProcessingException { var arrayNode = getArrayNode().add(getCommand()); Optional.ofNullable(getSubscriptionId()) .ifPresent(arrayNode::add); - arrayNode.add(IEncoder.MAPPER.readTree( + arrayNode.add(ENCODER_MAPPED_AFTERBURNER.readTree( new BaseEventEncoder<>((BaseEvent)getEvent()).encode())); - return IEncoder.MAPPER.writeValueAsString(arrayNode); + return ENCODER_MAPPED_AFTERBURNER.writeValueAsString(arrayNode); } +// TODO: refactor into stream returning optional public static T decode(@NonNull Object[] msgArr, ObjectMapper mapper) { var arg = msgArr[1]; if (msgArr.length == 2 && arg instanceof Map map) { diff --git a/nostr-java-event/src/main/java/nostr/event/message/NoticeMessage.java b/nostr-java-event/src/main/java/nostr/event/message/NoticeMessage.java index 745e33510..8207c4c4a 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/NoticeMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/NoticeMessage.java @@ -6,9 +6,10 @@ import lombok.NonNull; import lombok.Setter; import nostr.base.Command; -import nostr.base.IEncoder; import nostr.event.BaseMessage; +import static nostr.base.Encoder.ENCODER_MAPPED_AFTERBURNER; + /** * * @author squirrel @@ -27,7 +28,7 @@ public NoticeMessage(@NonNull String message) { @Override public String encode() throws JsonProcessingException { - return IEncoder.MAPPER.writeValueAsString( + return ENCODER_MAPPED_AFTERBURNER.writeValueAsString( getArrayNode() .add(getCommand()) .add(getMessage())); diff --git a/nostr-java-event/src/main/java/nostr/event/message/OkMessage.java b/nostr-java-event/src/main/java/nostr/event/message/OkMessage.java index 9245ba1e1..4c8375b12 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/OkMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/OkMessage.java @@ -6,9 +6,10 @@ import lombok.NonNull; import lombok.Setter; import nostr.base.Command; -import nostr.base.IEncoder; import nostr.event.BaseMessage; +import static nostr.base.Encoder.ENCODER_MAPPED_AFTERBURNER; + /** * * @author squirrel @@ -35,7 +36,7 @@ public OkMessage(String eventId, Boolean flag, String message) { @Override public String encode() throws JsonProcessingException { - return IEncoder.MAPPER.writeValueAsString( + return ENCODER_MAPPED_AFTERBURNER.writeValueAsString( getArrayNode() .add(getCommand()) .add(getEventId()) @@ -43,6 +44,7 @@ public String encode() throws JsonProcessingException { .add(getMessage())); } + // TODO: refactor into stream returning optional public static T decode(@NonNull Object[] msgArr) { if (msgArr.length == 4 && msgArr[2] instanceof Boolean duplicate) { String msgArg = msgArr[3].toString(); diff --git a/nostr-java-event/src/main/java/nostr/event/message/RelayAuthenticationMessage.java b/nostr-java-event/src/main/java/nostr/event/message/RelayAuthenticationMessage.java index bbd894428..841ecd71c 100644 --- a/nostr-java-event/src/main/java/nostr/event/message/RelayAuthenticationMessage.java +++ b/nostr-java-event/src/main/java/nostr/event/message/RelayAuthenticationMessage.java @@ -6,9 +6,10 @@ import lombok.NonNull; import lombok.Setter; import nostr.base.Command; -import nostr.base.IEncoder; import nostr.event.BaseMessage; +import static nostr.base.Encoder.ENCODER_MAPPED_AFTERBURNER; + /** * * @author eric @@ -27,7 +28,7 @@ public RelayAuthenticationMessage(String challenge) { @Override public String encode() throws JsonProcessingException { - return IEncoder.MAPPER.writeValueAsString( + return ENCODER_MAPPED_AFTERBURNER.writeValueAsString( getArrayNode() .add(getCommand()) .add(getChallenge())); 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 f3f2d1491..18e520ae1 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 @@ -8,7 +8,6 @@ import lombok.NonNull; import lombok.ToString; import nostr.base.Command; -import nostr.base.IEncoder; import nostr.event.BaseMessage; import nostr.event.filter.Filters; import nostr.event.json.codec.FiltersDecoder; @@ -17,6 +16,8 @@ import java.time.temporal.ValueRange; import java.util.List; +import static nostr.base.Encoder.ENCODER_MAPPED_AFTERBURNER; + /** * @author squirrel */ @@ -31,7 +32,6 @@ 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)); } @@ -45,25 +45,25 @@ public ReqMessage(@NonNull String subscriptionId, List filtersList) { @Override public String encode() throws JsonProcessingException { getArrayNode() - .add(getCommand()) - .add(getSubscriptionId()); + .add(getCommand()) + .add(getSubscriptionId()); filtersList.stream() - .map(FiltersEncoder::new) - .map(FiltersEncoder::encode) - .map(ReqMessage::createJsonNode) - .forEach(jsonNode -> - getArrayNode().add(jsonNode)); + .map(FiltersEncoder::new) + .map(FiltersEncoder::encode) + .map(ReqMessage::createJsonNode) + .forEach(jsonNode -> + getArrayNode().add(jsonNode)); - return IEncoder.MAPPER.writeValueAsString(getArrayNode()); + return ENCODER_MAPPED_AFTERBURNER.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()); + subscriptionId.toString(), + jsonFiltersList.stream().map(filtersList -> + new FiltersDecoder().decode(filtersList)).toList()); return (T) reqMessage; } @@ -75,7 +75,7 @@ private static void validateSubscriptionId(String subscriptionId) { private static JsonNode createJsonNode(String jsonNode) { try { - return IEncoder.MAPPER.readTree(jsonNode); + return ENCODER_MAPPED_AFTERBURNER.readTree(jsonNode); } catch (JsonProcessingException e) { throw new IllegalArgumentException(String.format("Malformed encoding ReqMessage json: [%s]", jsonNode), e); } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/DelegationTag.java b/nostr-java-event/src/main/java/nostr/event/tag/DelegationTag.java index 3f10518cb..2856bad93 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/DelegationTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/DelegationTag.java @@ -2,20 +2,24 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import nostr.base.ISignable; -import nostr.base.annotation.Key; -import nostr.event.BaseTag; -import nostr.base.PublicKey; -import nostr.base.Signature; -import java.beans.Transient; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import nostr.base.ISignable; +import nostr.base.PublicKey; +import nostr.base.Signature; +import nostr.base.annotation.Key; import nostr.base.annotation.Tag; +import nostr.event.BaseTag; + +import java.beans.Transient; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.function.Consumer; +import java.util.function.Supplier; /** - * * @author squirrel */ @Data @@ -47,4 +51,14 @@ public DelegationTag(PublicKey delegator, String conditions) { public String getToken() { return "nostr:" + getCode() + ":" + delegator.toString() + ":" + conditions; } + + @Override + public Consumer getSignatureConsumer() { + return this::setSignature; + } + + @Override + public Supplier getByeArraySupplier() { + return () -> ByteBuffer.wrap(this.getToken().getBytes(StandardCharsets.UTF_8)); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/EventTag.java b/nostr-java-event/src/main/java/nostr/event/tag/EventTag.java index ea42f1094..bbc55abe0 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/EventTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/EventTag.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package nostr.event.tag; import com.fasterxml.jackson.annotation.JsonInclude; @@ -21,7 +16,6 @@ import nostr.event.Marker; /** - * * @author squirrel */ @Builder @@ -57,21 +51,9 @@ public EventTag(String idEvent) { public static T deserialize(@NonNull JsonNode node) { EventTag tag = new EventTag(); - - final JsonNode nodeIdEvent = node.get(1); - if (nodeIdEvent != null) { - tag.setIdEvent(nodeIdEvent.asText()); - } - - final JsonNode nodeRelay = node.get(2); - if (nodeRelay != null) { - tag.setRecommendedRelayUrl(nodeRelay.asText()); - } - - final JsonNode nodeMarker = node.get(3); - if (nodeMarker != null) { - tag.setMarker(Marker.valueOf(nodeMarker.asText().toUpperCase())); - } + setRequiredField(node.get(1), (n, t) -> tag.setIdEvent(n.asText()), tag); + setOptionalField(node.get(2), (n, t) -> tag.setRecommendedRelayUrl(n.asText()), tag); + setOptionalField(node.get(3), (n, t) -> tag.setMarker(Marker.valueOf(n.asText().toUpperCase())), tag); return (T) tag; } } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/GeohashTag.java b/nostr-java-event/src/main/java/nostr/event/tag/GeohashTag.java index 96302d378..054804a33 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/GeohashTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/GeohashTag.java @@ -13,7 +13,6 @@ import nostr.event.BaseTag; /** - * * @author eric */ @Builder @@ -23,19 +22,14 @@ @NoArgsConstructor @AllArgsConstructor public class GeohashTag extends BaseTag { - + @Key @JsonProperty("g") private String location; public static T deserialize(@NonNull JsonNode node) { GeohashTag tag = new GeohashTag(); - - final JsonNode nodePubKey = node.get(1); - if (nodePubKey != null) { - tag.setLocation(nodePubKey.asText()); - } - + setRequiredField(node.get(1), (n, t) -> tag.setLocation(n.asText()), tag); return (T) tag; } } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/HashtagTag.java b/nostr-java-event/src/main/java/nostr/event/tag/HashtagTag.java index da33f509b..ea1f35c8d 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/HashtagTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/HashtagTag.java @@ -13,7 +13,6 @@ import nostr.event.BaseTag; /** - * * @author eric */ @Builder @@ -30,12 +29,7 @@ public class HashtagTag extends BaseTag { public static T deserialize(@NonNull JsonNode node) { HashtagTag tag = new HashtagTag(); - - final JsonNode nodePubKey = node.get(1); - if (nodePubKey != null) { - tag.setHashTag(nodePubKey.asText()); - } - + setRequiredField(node.get(1), (n, t) -> tag.setHashTag(n.asText()), tag); return (T) tag; } } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/IdentifierTag.java b/nostr-java-event/src/main/java/nostr/event/tag/IdentifierTag.java index 93ed02345..8f1d98cf9 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/IdentifierTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/IdentifierTag.java @@ -13,7 +13,6 @@ import nostr.event.BaseTag; /** - * * @author eric */ @Builder @@ -30,12 +29,7 @@ public class IdentifierTag extends BaseTag { public static T deserialize(@NonNull JsonNode node) { IdentifierTag tag = new IdentifierTag(); - - final JsonNode nodePubKey = node.get(1); - if (nodePubKey != null) { - tag.setId(nodePubKey.asText()); - } - + setRequiredField(node.get(1), (n, t) -> tag.setId(n.asText()), tag); return (T) tag; } } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/NonceTag.java b/nostr-java-event/src/main/java/nostr/event/tag/NonceTag.java index cec19b4dc..370bd8cd7 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/NonceTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/NonceTag.java @@ -4,17 +4,16 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.JsonNode; -import nostr.base.annotation.Key; -import nostr.event.BaseTag; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.NonNull; +import nostr.base.annotation.Key; import nostr.base.annotation.Tag; +import nostr.event.BaseTag; /** - * * @author squirrel */ @Builder @@ -40,16 +39,8 @@ public NonceTag(@NonNull Integer nonce, @NonNull Integer difficulty) { public static T deserialize(@NonNull JsonNode node) { NonceTag tag = new NonceTag(); - - final JsonNode nodeNonce = node.get(1); - if (nodeNonce != null) { - tag.setNonce(Integer.valueOf(nodeNonce.asText())); - } - - final JsonNode nodeDifficulty = node.get(2); - if (nodeDifficulty != null) { - tag.setDifficulty(Integer.valueOf(nodeDifficulty.asText())); - } + setRequiredField(node.get(1), (n, t) -> tag.setNonce(Integer.valueOf(n.asText())), tag); + setRequiredField(node.get(2), (n, t) -> tag.setDifficulty(Integer.valueOf(n.asText())), tag); return (T) tag; } } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/PriceTag.java b/nostr-java-event/src/main/java/nostr/event/tag/PriceTag.java index 54e162922..cb4d199ef 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/PriceTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/PriceTag.java @@ -7,7 +7,6 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; -import lombok.EqualsAndHashCode; import lombok.NonNull; import lombok.RequiredArgsConstructor; import nostr.base.annotation.Key; @@ -15,38 +14,51 @@ import nostr.event.BaseTag; import java.math.BigDecimal; -import java.util.Optional; +import java.util.Objects; @Builder @Data -@EqualsAndHashCode(callSuper = true) @Tag(code = "price", nip = 99) @RequiredArgsConstructor @AllArgsConstructor @JsonPropertyOrder({"number", "currency", "frequency"}) public class PriceTag extends BaseTag { - @Key - @JsonProperty - @JsonFormat(shape = JsonFormat.Shape.STRING) - private BigDecimal number; + @Key + @JsonProperty + @JsonFormat(shape = JsonFormat.Shape.STRING) + private BigDecimal number; - @Key - @JsonProperty - private String currency; + @Key + @JsonProperty + private String currency; - @Key - @JsonProperty - private String frequency; + @Key + @JsonProperty + private String frequency; - public static T deserialize(@NonNull JsonNode node) { - String text = Optional.ofNullable(node.get(1)).orElseThrow().asText(); - final BigDecimal number = new BigDecimal(text); - final String currency = Optional.ofNullable(node.get(2)).orElseThrow().asText(); + public static T deserialize(@NonNull JsonNode node) { + PriceTag tag = new PriceTag(); + setRequiredField(node.get(1), (n, t) -> tag.setNumber(new BigDecimal(n.asText())), tag); + setOptionalField(node.get(2), (n, t) -> tag.setCurrency(n.asText()), tag); + setOptionalField(node.get(3), (n, t) -> tag.setFrequency(n.asText()), tag); + return (T) tag; + } - PriceTag tag = PriceTag.builder().number(number).currency(currency).build(); - Optional.ofNullable(node.get(3)).ifPresent(jsonNode1 -> tag.setFrequency(jsonNode1.asText())); + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + PriceTag priceTag = (PriceTag) o; + return Objects.equals( + number.stripTrailingZeros(), + priceTag.number.stripTrailingZeros() + ) + && Objects.equals(currency, priceTag.currency) && Objects.equals(frequency, priceTag.frequency); + } - return (T) tag; - } + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), number.stripTrailingZeros(), currency, frequency); + } } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/PubKeyTag.java b/nostr-java-event/src/main/java/nostr/event/tag/PubKeyTag.java index 60749556a..2c725de4f 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/PubKeyTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/PubKeyTag.java @@ -20,7 +20,6 @@ import nostr.event.BaseTag; /** - * * @author squirrel */ @JsonPropertyOrder({"pubKey", "mainRelayUrl", "petName"}) @@ -57,22 +56,9 @@ public PubKeyTag(@NonNull PublicKey publicKey, String mainRelayUrl, String petNa public static T deserialize(@NonNull JsonNode node) { PubKeyTag tag = new PubKeyTag(); - - final JsonNode nodePubKey = node.get(1); - if (nodePubKey != null) { - tag.setPublicKey(new PublicKey(nodePubKey.asText())); - } - - final JsonNode nodeMainUrl = node.get(2); - if (nodeMainUrl != null) { - tag.setMainRelayUrl(nodeMainUrl.asText()); - } - - final JsonNode nodePetName = node.get(3); - if (nodePetName != null) { - tag.setPetName(nodePetName.asText()); - } - + setRequiredField(node.get(1), (n, t) -> tag.setPublicKey(new PublicKey(n.asText())), tag); + setOptionalField(node.get(2), (n, t) -> tag.setMainRelayUrl(n.asText()), tag); + setOptionalField(node.get(3), (n, t) -> tag.setPetName(n.asText()), tag); return (T) tag; } } diff --git a/nostr-java-event/src/main/java/nostr/event/tag/RelaysTag.java b/nostr-java-event/src/main/java/nostr/event/tag/RelaysTag.java index 62926f6e6..cec82c762 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/RelaysTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/RelaysTag.java @@ -20,17 +20,17 @@ @Tag(code = "relays", nip = 57) @JsonSerialize(using = RelaysTagSerializer.class) public class RelaysTag extends BaseTag { - private final List relays; + private final List relays; - public RelaysTag(@NonNull List relays) { - this.relays = relays; - } + public RelaysTag(@NonNull List relays) { + this.relays = relays; + } - public RelaysTag(@NonNull Relay... relays) { - this(List.of(relays)); - } + public RelaysTag(@NonNull Relay... relays) { + this(List.of(relays)); + } - public static T deserialize(@NonNull JsonNode node) { - return (T) new RelaysTag(Optional.of(node).stream().map(jsonNode -> new Relay(jsonNode.get(1).asText())).toList()); - } -} \ No newline at end of file + public static T deserialize(JsonNode node) { + return (T) new RelaysTag(Optional.ofNullable(node).map(jsonNode -> new Relay(jsonNode.get(1).asText())).orElseThrow()); + } +} diff --git a/nostr-java-event/src/main/java/nostr/event/tag/SubjectTag.java b/nostr-java-event/src/main/java/nostr/event/tag/SubjectTag.java index 533493b59..6950cab64 100644 --- a/nostr-java-event/src/main/java/nostr/event/tag/SubjectTag.java +++ b/nostr-java-event/src/main/java/nostr/event/tag/SubjectTag.java @@ -33,11 +33,7 @@ public final class SubjectTag extends BaseTag { public static T deserialize(@NonNull JsonNode node) { SubjectTag tag = new SubjectTag(); - - final JsonNode nodeSubject = node.get(1); - if (nodeSubject != null) { - tag.setSubject(nodeSubject.asText()); - } + setOptionalField(node.get(1), (n, t) -> tag.setSubject(n.asText()), tag); return (T) tag; } } diff --git a/nostr-java-event/src/main/java/nostr/event/util/Nip05Validator.java b/nostr-java-event/src/main/java/nostr/event/util/Nip05Validator.java index e9589f477..c3deb2b4e 100644 --- a/nostr-java-event/src/main/java/nostr/event/util/Nip05Validator.java +++ b/nostr-java-event/src/main/java/nostr/event/util/Nip05Validator.java @@ -32,6 +32,7 @@ public class Nip05Validator { private static final String LOCAL_PART_PATTERN = "^[a-zA-Z0-9-_\\.]+$"; +// TODO: refactor public void validate() throws NostrException { if (this.nip05 != null) { var splited = nip05.split("@"); @@ -53,6 +54,7 @@ public void validate() throws NostrException { } } +// TODO: refactor private void validatePublicKey(String domain, String localPart) throws NostrException, IOException, URISyntaxException { // Set up and estgetPublicKeyablish the HTTP connection diff --git a/nostr-java-test/src/test/java/nostr/test/event/BaseMessageCommandMapperTest.java b/nostr-java-event/src/test/java/nostr/event/unit/BaseMessageCommandMapperTest.java similarity index 98% rename from nostr-java-test/src/test/java/nostr/test/event/BaseMessageCommandMapperTest.java rename to nostr-java-event/src/test/java/nostr/event/unit/BaseMessageCommandMapperTest.java index 94000b102..b2c2621f5 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/BaseMessageCommandMapperTest.java +++ b/nostr-java-event/src/test/java/nostr/event/unit/BaseMessageCommandMapperTest.java @@ -1,4 +1,4 @@ -package nostr.test.event; +package nostr.event.unit; import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.java.Log; diff --git a/nostr-java-event/src/test/java/nostr/event/unit/BaseTagTest.java b/nostr-java-event/src/test/java/nostr/event/unit/BaseTagTest.java new file mode 100644 index 000000000..e4548c451 --- /dev/null +++ b/nostr-java-event/src/test/java/nostr/event/unit/BaseTagTest.java @@ -0,0 +1,19 @@ +package nostr.event.unit; + +import nostr.event.BaseTag; +import nostr.event.impl.GenericTag; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class BaseTagTest { + + BaseTag genericTag = GenericTag.create("id", "value"); + + @Test + void testToString() { + String result = "GenericTag(code=id, attributes=[ElementAttribute(name=param0, value=value)])"; + assertEquals(result, genericTag.toString()); + } + +} diff --git a/nostr-java-test/src/test/java/nostr/test/event/CalendarContentDecodeTest.java b/nostr-java-event/src/test/java/nostr/event/unit/CalendarContentDecodeTest.java similarity index 99% rename from nostr-java-test/src/test/java/nostr/test/event/CalendarContentDecodeTest.java rename to nostr-java-event/src/test/java/nostr/event/unit/CalendarContentDecodeTest.java index 7e2dae22b..2ec8eee88 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/CalendarContentDecodeTest.java +++ b/nostr-java-event/src/test/java/nostr/event/unit/CalendarContentDecodeTest.java @@ -1,4 +1,4 @@ -package nostr.test.event; +package nostr.event.unit; import nostr.event.impl.CalendarTimeBasedEvent; import nostr.event.json.codec.GenericEventDecoder; diff --git a/nostr-java-test/src/test/java/nostr/test/event/ClassifiedListingDecodeTest.java b/nostr-java-event/src/test/java/nostr/event/unit/ClassifiedListingDecodeTest.java similarity index 98% rename from nostr-java-test/src/test/java/nostr/test/event/ClassifiedListingDecodeTest.java rename to nostr-java-event/src/test/java/nostr/event/unit/ClassifiedListingDecodeTest.java index 9a57e0baf..055dbad84 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ClassifiedListingDecodeTest.java +++ b/nostr-java-event/src/test/java/nostr/event/unit/ClassifiedListingDecodeTest.java @@ -1,4 +1,4 @@ -package nostr.test.event; +package nostr.event.unit; import nostr.event.impl.ClassifiedListingEvent; import nostr.event.json.codec.GenericEventDecoder; diff --git a/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java b/nostr-java-event/src/test/java/nostr/event/unit/DecodeTest.java similarity index 99% rename from nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java rename to nostr-java-event/src/test/java/nostr/event/unit/DecodeTest.java index 1850e4d01..c08c4d159 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/DecodeTest.java +++ b/nostr-java-event/src/test/java/nostr/event/unit/DecodeTest.java @@ -1,4 +1,4 @@ -package nostr.test.event; +package nostr.event.unit; import com.fasterxml.jackson.core.JsonProcessingException; import nostr.base.PublicKey; diff --git a/nostr-java-test/src/test/java/nostr/test/event/EventTagTest.java b/nostr-java-event/src/test/java/nostr/event/unit/EventTagTest.java similarity index 96% rename from nostr-java-test/src/test/java/nostr/test/event/EventTagTest.java rename to nostr-java-event/src/test/java/nostr/event/unit/EventTagTest.java index a9bdef8e9..3ba32d8b9 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/EventTagTest.java +++ b/nostr-java-event/src/test/java/nostr/event/unit/EventTagTest.java @@ -1,4 +1,4 @@ -package nostr.test.event; +package nostr.event.unit; import nostr.event.Marker; import nostr.event.tag.EventTag; @@ -45,4 +45,4 @@ private String getFieldValue(Field field, EventTag eventTag) { return returnVal[0]; } -} \ No newline at end of file +} diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java b/nostr-java-event/src/test/java/nostr/event/unit/FiltersDecoderTest.java similarity index 99% rename from nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java rename to nostr-java-event/src/test/java/nostr/event/unit/FiltersDecoderTest.java index 6eaec461c..ba46f522c 100644 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersDecoderTest.java +++ b/nostr-java-event/src/test/java/nostr/event/unit/FiltersDecoderTest.java @@ -1,4 +1,4 @@ -package nostr.test.filters; +package nostr.event.unit; import lombok.extern.java.Log; import nostr.base.GenericTagQuery; diff --git a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java b/nostr-java-event/src/test/java/nostr/event/unit/FiltersEncoderTest.java similarity index 99% rename from nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java rename to nostr-java-event/src/test/java/nostr/event/unit/FiltersEncoderTest.java index 5d0937011..6d5822d72 100644 --- a/nostr-java-test/src/test/java/nostr/test/filters/FiltersEncoderTest.java +++ b/nostr-java-event/src/test/java/nostr/event/unit/FiltersEncoderTest.java @@ -1,4 +1,4 @@ -package nostr.test.filters; +package nostr.event.unit; import lombok.extern.java.Log; import nostr.base.GenericTagQuery; diff --git a/nostr-java-test/src/test/java/nostr/test/event/KindMappingTest.java b/nostr-java-event/src/test/java/nostr/event/unit/KindMappingTest.java similarity index 95% rename from nostr-java-test/src/test/java/nostr/test/event/KindMappingTest.java rename to nostr-java-event/src/test/java/nostr/event/unit/KindMappingTest.java index cbb4b44b4..5ab05bd16 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/KindMappingTest.java +++ b/nostr-java-event/src/test/java/nostr/event/unit/KindMappingTest.java @@ -1,4 +1,4 @@ -package nostr.test.event; +package nostr.event.unit; import nostr.event.Kind; import org.junit.jupiter.api.Test; diff --git a/nostr-java-event/src/test/java/nostr/event/unit/PriceTagTest.java b/nostr-java-event/src/test/java/nostr/event/unit/PriceTagTest.java new file mode 100644 index 000000000..3f42d74e5 --- /dev/null +++ b/nostr-java-event/src/test/java/nostr/event/unit/PriceTagTest.java @@ -0,0 +1,74 @@ +package nostr.event.unit; + +import nostr.event.tag.PriceTag; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class PriceTagTest { + private final static BigDecimal aVal = new BigDecimal(10.000); + private final static BigDecimal bVal = new BigDecimal(10.00); + private final static BigDecimal cVal = new BigDecimal(10.0); + private final static BigDecimal dVal = new BigDecimal(10.); + private final static BigDecimal eVal = new BigDecimal(10); + + private final static BigDecimal aString = new BigDecimal("10.000"); + private final static BigDecimal bString = new BigDecimal("10.00"); + private final static BigDecimal cString = new BigDecimal("10.0"); + private final static BigDecimal dString = new BigDecimal("10."); + private final static BigDecimal eString = new BigDecimal("10"); + + private static final String BTC = "BTC"; + private static final String freq = "femptosecond"; + + @Test + void valueParameterCompare() { + List list = Stream.of( + aVal, bVal, cVal, dVal, eVal) + .map(bigDecimal -> + new PriceTag(bigDecimal, BTC, freq)).toList(); + assertTrue(list.stream().allMatch(list.getFirst()::equals)); + } + + @Test + void stringParameterCompare() { + List list = Stream.of( + aString, bString, cString, dString, eString) + .map(bigDecimal -> + new PriceTag(bigDecimal, BTC, freq)).toList(); + assertTrue(list.stream().allMatch(list.getFirst()::equals)); + } + + @Test + void failure() { + List priceTags = List.of( + new PriceTag(new BigDecimal("1"), BTC, freq), + new PriceTag(new BigDecimal("01"), BTC, freq), + new PriceTag(new BigDecimal("001"), BTC, freq), + new PriceTag(new BigDecimal(1), BTC, freq), + new PriceTag(new BigDecimal(01), BTC, freq), + new PriceTag(new BigDecimal(001), BTC, freq) + ); + List list = Stream.of( + aString, bString, cString, dString, eString) + .map(bigDecimal -> + new PriceTag(bigDecimal, BTC, freq)).toList(); + assertTrue(list.stream().noneMatch(priceTags::equals)); + } + + @Test + void getSupportedFields() { + PriceTag priceTag = new PriceTag(new BigDecimal(11111), "BTC", "NANOSECONDS"); + assertDoesNotThrow(() -> { + List list = priceTag.getSupportedFields().stream().toList(); + assertTrue(List.of("number", "currency", "frequency").containsAll(list.stream().map(Field::getName).toList())); + assertTrue(List.of("java.math.BigDecimal", "java.lang.String").containsAll(list.stream().map(field -> field.getAnnotatedType().toString()).toList())); + }); + } +} diff --git a/nostr-java-test/src/test/java/nostr/test/event/PubkeyTagTest.java b/nostr-java-event/src/test/java/nostr/event/unit/PubkeyTagTest.java similarity index 93% rename from nostr-java-test/src/test/java/nostr/test/event/PubkeyTagTest.java rename to nostr-java-event/src/test/java/nostr/event/unit/PubkeyTagTest.java index 986d5e93c..6536e4320 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/PubkeyTagTest.java +++ b/nostr-java-event/src/test/java/nostr/event/unit/PubkeyTagTest.java @@ -1,4 +1,4 @@ -package nostr.test.event; +package nostr.event.unit; import nostr.base.PublicKey; import nostr.event.tag.PubKeyTag; @@ -23,4 +23,4 @@ void getSupportedFields() { }); } -} \ No newline at end of file +} diff --git a/nostr-java-test/src/test/java/nostr/test/event/RelaysTagTest.java b/nostr-java-event/src/test/java/nostr/event/unit/RelaysTagTest.java similarity index 86% rename from nostr-java-test/src/test/java/nostr/test/event/RelaysTagTest.java rename to nostr-java-event/src/test/java/nostr/event/unit/RelaysTagTest.java index 11878d524..87dddb67f 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/RelaysTagTest.java +++ b/nostr-java-event/src/test/java/nostr/event/unit/RelaysTagTest.java @@ -1,43 +1,43 @@ -package nostr.test.event; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import nostr.base.Relay; -import nostr.event.BaseTag; -import nostr.event.json.codec.BaseTagEncoder; -import nostr.event.tag.RelaysTag; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; - -class RelaysTagTest { - - public final static String RELAYS_KEY = "relays"; - public final static String HOST_VALUE = "ws://localhost:5555"; - public final static String HOST_VALUE2 = "ws://anotherlocalhost:5432"; - - @Test - void testSerialize() { - final String expected = "[\"relays\",\"ws://localhost:5555\",\"ws://anotherlocalhost:5432\"]"; - RelaysTag relaysTag = new RelaysTag(List.of(new Relay(HOST_VALUE), new Relay(HOST_VALUE2))); - BaseTagEncoder baseTagEncoder = new BaseTagEncoder(relaysTag); - assertDoesNotThrow(() -> { - assertEquals(expected, baseTagEncoder.encode()); - }); - } - - @Test - void testDeserialize() { - final String EXPECTED = "[\"relays\",\"ws://localhost:5555\"]"; - assertDoesNotThrow(() -> { - JsonNode node = new ObjectMapper().readTree(EXPECTED); - BaseTag deserialize = RelaysTag.deserialize(node); - assertEquals(RELAYS_KEY, deserialize.getCode()); - assertEquals(HOST_VALUE, ((RelaysTag) deserialize).getRelays().get(0).getUri()); - }); - } - -} \ No newline at end of file +package nostr.event.unit; + +import com.fasterxml.jackson.databind.JsonNode; +import nostr.base.Relay; +import nostr.event.BaseTag; +import nostr.event.json.codec.BaseTagEncoder; +import nostr.event.tag.RelaysTag; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static nostr.base.IEvent.MAPPER_AFTERBURNER; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class RelaysTagTest { + + public final static String RELAYS_KEY = "relays"; + public final static String HOST_VALUE = "ws://localhost:5555"; + public final static String HOST_VALUE2 = "ws://anotherlocalhost:5432"; + + @Test + void testSerialize() { + final String expected = "[\"relays\",\"ws://localhost:5555\",\"ws://anotherlocalhost:5432\"]"; + RelaysTag relaysTag = new RelaysTag(List.of(new Relay(HOST_VALUE), new Relay(HOST_VALUE2))); + BaseTagEncoder baseTagEncoder = new BaseTagEncoder(relaysTag); + assertDoesNotThrow(() -> { + assertEquals(expected, baseTagEncoder.encode()); + }); + } + + @Test + void testDeserialize() { + final String EXPECTED = "[\"relays\",\"ws://localhost:5555\"]"; + assertDoesNotThrow(() -> { + JsonNode node = MAPPER_AFTERBURNER.readTree(EXPECTED); + BaseTag deserialize = RelaysTag.deserialize(node); + assertEquals(RELAYS_KEY, deserialize.getCode()); + assertEquals(HOST_VALUE, ((RelaysTag) deserialize).getRelays().getFirst().getUri()); + }); + } + +} diff --git a/nostr-java-event/src/test/java/nostr/event/unit/SignatureTest.java b/nostr-java-event/src/test/java/nostr/event/unit/SignatureTest.java new file mode 100644 index 000000000..7bb0b11cd --- /dev/null +++ b/nostr-java-event/src/test/java/nostr/event/unit/SignatureTest.java @@ -0,0 +1,18 @@ +package nostr.event.unit; + +import nostr.base.Signature; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class SignatureTest { + @Test + public void testSignatureStringLength() { + assertDoesNotThrow(() -> + Signature.fromString("86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546")); + + assertTrue( + assertThrows(AssertionError.class, () -> Signature.fromString("86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546a")) + .getMessage().contains("[129], target length: [128]")); + } +} diff --git a/nostr-java-event/src/test/resources/application-test.properties b/nostr-java-event/src/test/resources/application-test.properties new file mode 100644 index 000000000..a4ce64138 --- /dev/null +++ b/nostr-java-event/src/test/resources/application-test.properties @@ -0,0 +1,4 @@ +spring.threads.virtual.enabled=true + +logging.level.nostr.event=INFO +logging.pattern.console=%msg%n diff --git a/nostr-java-event/src/test/resources/junit-platform.properties b/nostr-java-event/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..a413a5904 --- /dev/null +++ b/nostr-java-event/src/test/resources/junit-platform.properties @@ -0,0 +1,7 @@ +# junit-platform.properties + +junit.jupiter.execution.parallel.enabled=true +junit.jupiter.execution.parallel.config.strategy=dynamic +junit.jupiter.execution.parallel.mode.default=same_thread +#junit.jupiter.execution.parallel.mode.default=concurrent +#junit.jupiter.execution.parallel.mode.classes.default=concurrent diff --git a/nostr-java-examples/build.gradle b/nostr-java-examples/build.gradle new file mode 100644 index 000000000..1900922d1 --- /dev/null +++ b/nostr-java-examples/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'nostr-java.conventions' +} + +description = 'nostr-java-examples' + +dependencies { + api project(':nostr-java-api') +} diff --git a/nostr-java-examples/pom.xml b/nostr-java-examples/pom.xml index 5fa7b1fe7..6ebdb695c 100644 --- a/nostr-java-examples/pom.xml +++ b/nostr-java-examples/pom.xml @@ -1,47 +1,22 @@ - - + 4.0.0 xyz.tcheeric nostr-java 0.6.5-SNAPSHOT - - + ../pom.xml + + nostr-java-examples jar - - nostr.examples.NostrApiExamples - - - - ${project.groupId} - nostr-java-event - ${project.version} - - - ${project.groupId} - nostr-java-base - ${project.version} - - - ${project.groupId} - nostr-java-id - ${project.version} - - - ${project.groupId} - nostr-java-client - ${project.version} - ${project.groupId} nostr-java-api - ${project.version} + ${nostr-java.version} - \ No newline at end of file + diff --git a/nostr-java-examples/src/main/java/module-info.java b/nostr-java-examples/src/main/java/module-info.java deleted file mode 100644 index bab2a15e0..000000000 --- a/nostr-java-examples/src/main/java/module-info.java +++ /dev/null @@ -1,12 +0,0 @@ - -module nostr.examples { - requires nostr.event; - requires nostr.api; - requires static lombok; - requires nostr.util; - requires nostr.base; - requires nostr.id; - requires java.logging; - - exports nostr.examples; -} 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 b2316ca45..ffb6bf3d5 100644 --- a/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java +++ b/nostr-java-examples/src/main/java/nostr/examples/NostrApiExamples.java @@ -36,6 +36,9 @@ import nostr.event.tag.PubKeyTag; import nostr.id.Identity; import nostr.util.NostrException; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.autoconfigure.SpringBootApplication; import java.io.IOException; import java.io.InputStream; @@ -57,7 +60,8 @@ * @author eric */ @Log -public class NostrApiExamples { +@SpringBootApplication +public class NostrApiExamples implements ApplicationRunner { private static final Identity RECIPIENT = Identity.generateRandomIdentity(); private static final Identity SENDER = Identity.generateRandomIdentity(); @@ -82,7 +86,8 @@ public class NostrApiExamples { } } - public static void main(String[] args) throws Exception { + @Override + public void run(ApplicationArguments args) throws Exception { try { log.log(Level.FINE, "================= The Beginning"); logAccountsData(); diff --git a/nostr-java-id/build.gradle b/nostr-java-id/build.gradle new file mode 100644 index 000000000..1d3aa7720 --- /dev/null +++ b/nostr-java-id/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'nostr-java.conventions' +} + +description = 'nostr-java-id' + +dependencies { + api project(':nostr-java-event') +} diff --git a/nostr-java-id/pom.xml b/nostr-java-id/pom.xml index c3a2e4af5..884eade2e 100644 --- a/nostr-java-id/pom.xml +++ b/nostr-java-id/pom.xml @@ -1,47 +1,22 @@ - - + 4.0.0 xyz.tcheeric nostr-java 0.6.5-SNAPSHOT + ../pom.xml - + nostr-java-id jar - - - - - - org.projectlombok - lombok - - - - ${project.groupId} - nostr-java-base - ${project.version} - ${project.groupId} nostr-java-event - ${project.version} - - - ${project.groupId} - nostr-java-util - ${project.version} - - - ${project.groupId} - nostr-java-crypto - ${project.version} + ${nostr-java.version} diff --git a/nostr-java-id/src/main/java/module-info.java b/nostr-java-id/src/main/java/module-info.java deleted file mode 100644 index 59373282f..000000000 --- a/nostr-java-id/src/main/java/module-info.java +++ /dev/null @@ -1,11 +0,0 @@ - -module nostr.id { - requires static lombok; - requires nostr.base; - requires nostr.crypto; - requires nostr.event; - requires nostr.util; - requires java.logging; - - exports nostr.id; -} diff --git a/nostr-java-id/src/main/java/nostr/id/Identity.java b/nostr-java-id/src/main/java/nostr/id/Identity.java index 2bc427d3c..aa29f0911 100644 --- a/nostr-java-id/src/main/java/nostr/id/Identity.java +++ b/nostr-java-id/src/main/java/nostr/id/Identity.java @@ -3,6 +3,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NonNull; +import lombok.SneakyThrows; import lombok.ToString; import lombok.extern.java.Log; import nostr.base.ISignable; @@ -10,13 +11,8 @@ import nostr.base.PublicKey; import nostr.base.Signature; import nostr.crypto.schnorr.Schnorr; -import nostr.event.impl.GenericEvent; -import nostr.event.tag.DelegationTag; import nostr.util.NostrUtil; -import java.nio.charset.StandardCharsets; -import java.util.logging.Level; - /** * @author squirrel */ @@ -60,50 +56,26 @@ public static Identity generateRandomIdentity() { public PublicKey getPublicKey() { try { return new PublicKey(Schnorr.genPubKey(this.getPrivateKey().getRawData())); - } catch (Exception e) { - throw new RuntimeException(e); + } catch (Exception ex) { + throw new RuntimeException(ex); } } - public Signature sign(@NonNull ISignable signable) { - if (signable instanceof GenericEvent genericEvent) { - try { - return signEvent(genericEvent); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } else if (signable instanceof DelegationTag delegationTag) { - try { - return signDelegationTag(delegationTag); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - throw new RuntimeException(); - } - - private Signature signEvent(@NonNull GenericEvent event) throws Exception { - event.update(); - log.log(Level.FINER, "Serialized event: {0}", new String(event.get_serializedEvent())); - final var signedHashedSerializedEvent = Schnorr.sign(NostrUtil.sha256(event.get_serializedEvent()), this.getPrivateKey().getRawData(), generateAuxRand()); - final Signature signature = new Signature(); - signature.setRawData(signedHashedSerializedEvent); - signature.setPubKey(getPublicKey()); - event.setSignature(signature); - return signature; - } - - private Signature signDelegationTag(@NonNull DelegationTag delegationTag) throws Exception { - final var signedHashedToken = Schnorr.sign(NostrUtil.sha256(delegationTag.getToken().getBytes(StandardCharsets.UTF_8)), this.getPrivateKey().getRawData(), generateAuxRand()); +// TODO: exceptions refactor + @SneakyThrows + public Signature sign(@NonNull ISignable signable) { final Signature signature = new Signature(); - signature.setRawData(signedHashedToken); + signature.setRawData( + Schnorr.sign( + NostrUtil.sha256(signable.getByeArraySupplier().get().array()), + this.getPrivateKey().getRawData(), + generateAuxRand())); signature.setPubKey(getPublicKey()); - delegationTag.setSignature(signature); + signable.getSignatureConsumer().accept(signature); return signature; } private byte[] generateAuxRand() { return NostrUtil.createRandomByteArray(32); } - -} \ No newline at end of file +} diff --git a/nostr-java-test/src/test/java/nostr/test/event/ClassifiedListingEventTest.java b/nostr-java-id/src/test/java/nostr/id/ClassifiedListingEventTest.java similarity index 90% rename from nostr-java-test/src/test/java/nostr/test/event/ClassifiedListingEventTest.java rename to nostr-java-id/src/test/java/nostr/id/ClassifiedListingEventTest.java index b7eb8861a..baad1eb9f 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ClassifiedListingEventTest.java +++ b/nostr-java-id/src/test/java/nostr/id/ClassifiedListingEventTest.java @@ -1,4 +1,4 @@ -package nostr.test.event; +package nostr.id; import nostr.base.PublicKey; import nostr.event.BaseTag; @@ -11,7 +11,6 @@ import nostr.event.tag.PriceTag; import nostr.event.tag.PubKeyTag; import nostr.event.tag.SubjectTag; -import nostr.id.Identity; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @@ -59,10 +58,10 @@ void setup() { List tags = new ArrayList<>(); tags.add(E_TAG); tags.add(P_TAG); - tags.add(GenericTag.create(TITLE_CODE, 99, CLASSIFIED_LISTING_TITLE)); - tags.add(GenericTag.create(SUMMARY_CODE, 99, CLASSIFIED_LISTING_SUMMARY)); - tags.add(GenericTag.create(PUBLISHED_AT_CODE, 99, CLASSIFIED_LISTING_PUBLISHED_AT)); - tags.add(GenericTag.create(LOCATION_CODE, 99, CLASSIFIED_LISTING_LOCATION)); + tags.add(GenericTag.create(TITLE_CODE, CLASSIFIED_LISTING_TITLE)); + tags.add(GenericTag.create(SUMMARY_CODE, CLASSIFIED_LISTING_SUMMARY)); + tags.add(GenericTag.create(PUBLISHED_AT_CODE, CLASSIFIED_LISTING_PUBLISHED_AT)); + tags.add(GenericTag.create(LOCATION_CODE, CLASSIFIED_LISTING_LOCATION)); tags.add(SUBJECT_TAG); tags.add(G_TAG); tags.add(T_TAG); @@ -83,4 +82,4 @@ void testConstructClassifiedListingEvent() { assertEquals(senderPubkey.toHexString(), instance.getPubKey().toHexString()); assertEquals(CLASSIFIED_LISTING_CONTENT, instance.getContent()); } -} \ No newline at end of file +} diff --git a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java b/nostr-java-id/src/test/java/nostr/id/EntityFactory.java similarity index 95% rename from nostr-java-test/src/main/java/nostr/test/EntityFactory.java rename to nostr-java-id/src/test/java/nostr/id/EntityFactory.java index 6afb38149..66173ac1a 100644 --- a/nostr-java-test/src/main/java/nostr/test/EntityFactory.java +++ b/nostr-java-id/src/test/java/nostr/id/EntityFactory.java @@ -1,4 +1,4 @@ -package nostr.test; +package nostr.id; import lombok.extern.java.Log; import nostr.base.ElementAttribute; @@ -9,7 +9,6 @@ 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.GenericEvent; @@ -122,14 +121,20 @@ public static GenericTag createGenericTag(PublicKey publicKey) { public static GenericTag createGenericTag(PublicKey publicKey, IEvent event) { GenericTag tag = new GenericTag("devil"); - tag.addAttribute(ElementAttribute.builder().name("param0").value("Lucifer").nip(666).build()); + tag.addAttribute(ElementAttribute.builder().name("param0").value("Lucifer").build()); ((GenericEvent) event).addTag(tag); return tag; } + /** + * @param tagNip parameter to be removed + * + * @deprecated use {@link #createGenericTag(PublicKey, IEvent)} instead. + */ + @Deprecated(forRemoval = true) public static GenericTag createGenericTag(PublicKey publicKey, IEvent event, Integer tagNip) { - GenericTag tag = new GenericTag("devil", tagNip); - tag.addAttribute(ElementAttribute.builder().name("param0").value("Lucifer").nip(666).build()); + GenericTag tag = new GenericTag("devil"); + tag.addAttribute(ElementAttribute.builder().name("param0").value("Lucifer").build()); ((GenericEvent) event).addTag(tag); return tag; } diff --git a/nostr-java-test/src/test/java/nostr/test/event/EventTest.java b/nostr-java-id/src/test/java/nostr/id/EventTest.java similarity index 88% rename from nostr-java-test/src/test/java/nostr/test/event/EventTest.java rename to nostr-java-id/src/test/java/nostr/id/EventTest.java index a65742a23..9292ba99d 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/EventTest.java +++ b/nostr-java-id/src/test/java/nostr/id/EventTest.java @@ -1,11 +1,10 @@ -package nostr.test.event; +package nostr.id; import lombok.extern.java.Log; import nostr.base.ElementAttribute; import org.junit.jupiter.api.Test; -import nostr.base.IEncoder; import nostr.base.PublicKey; import nostr.base.Relay; import nostr.crypto.bech32.Bech32; @@ -16,11 +15,9 @@ import nostr.event.impl.GenericTag; import nostr.event.json.codec.BaseTagEncoder; import nostr.event.util.Nip05Validator; -import nostr.id.Identity; -import nostr.test.EntityFactory; -import nostr.util.NostrException; import nostr.util.NostrUtil; +import static nostr.base.Encoder.ENCODER_MAPPED_AFTERBURNER; import static org.junit.jupiter.api.Assertions.*; /** @@ -55,19 +52,11 @@ public void testCreateGenericTag() { PublicKey publicKey = Identity.generateRandomIdentity().getPublicKey(); GenericTag genericTag = EntityFactory.Events.createGenericTag(publicKey); - Relay relay = new Relay("wss://secret.relay.com"); - relay.addNipSupport(1); - relay.addNipSupport(genericTag.getNip()); - var attrs = genericTag.getAttributes(); - for (var a : attrs) { - relay.addNipSupport(a.getNip()); - } - - var encoder = new BaseTagEncoder(genericTag, relay); + var encoder = new BaseTagEncoder(genericTag); var strJsonEvent = encoder.encode(); assertDoesNotThrow(() -> { - BaseTag tag = IEncoder.MAPPER.readValue(strJsonEvent, BaseTag.class); + BaseTag tag = ENCODER_MAPPED_AFTERBURNER.readValue(strJsonEvent, BaseTag.class); assertEquals(genericTag, tag); }); } @@ -144,7 +133,7 @@ public void testNip05Validator() { var nip05Validator = Nip05Validator.builder().nip05(nip05).publicKey(publicKey).build(); nip05Validator.validate(); - } catch (NostrException ex) { + } catch (Exception ex) { fail(ex); } assertTrue(true); @@ -172,12 +161,12 @@ public void testEventIdConstraints() { String id63chars = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a71"; assertTrue( - assertThrows(IllegalArgumentException.class, () -> genericEvent.setId(id63chars)) + assertThrows(AssertionError.class, () -> genericEvent.setId(id63chars)) .getMessage().contains("[fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a71], length: [63], target length: [64]")); String id65chars = "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a7123"; assertTrue( - assertThrows(IllegalArgumentException.class, () -> genericEvent.setId(id65chars)) + assertThrows(AssertionError.class, () -> genericEvent.setId(id65chars)) .getMessage().contains("[fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a7123], length: [65], target length: [64]")); } } diff --git a/nostr-java-test/src/test/java/nostr/test/id/IdentityTest.java b/nostr-java-id/src/test/java/nostr/id/IdentityTest.java similarity index 93% rename from nostr-java-test/src/test/java/nostr/test/id/IdentityTest.java rename to nostr-java-id/src/test/java/nostr/id/IdentityTest.java index 04f8183e5..64318f925 100644 --- a/nostr-java-test/src/test/java/nostr/test/id/IdentityTest.java +++ b/nostr-java-id/src/test/java/nostr/id/IdentityTest.java @@ -1,10 +1,8 @@ -package nostr.test.id; +package nostr.id; import nostr.base.PublicKey; import nostr.event.tag.DelegationTag; import nostr.event.impl.GenericEvent; -import nostr.id.Identity; -import nostr.test.EntityFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/nostr-java-test/src/test/java/nostr/test/event/ZapReceiptEventTest.java b/nostr-java-id/src/test/java/nostr/id/ZapReceiptEventTest.java similarity index 97% rename from nostr-java-test/src/test/java/nostr/test/event/ZapReceiptEventTest.java rename to nostr-java-id/src/test/java/nostr/id/ZapReceiptEventTest.java index 106cf565d..dd1d94da0 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ZapReceiptEventTest.java +++ b/nostr-java-id/src/test/java/nostr/id/ZapReceiptEventTest.java @@ -1,10 +1,9 @@ -package nostr.test.event; +package nostr.id; import lombok.extern.java.Log; import nostr.base.PublicKey; import nostr.event.impl.ZapReceiptEvent; import nostr.event.tag.AddressTag; -import nostr.id.Identity; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -47,4 +46,4 @@ void testConstructZapReceiptEvent() { assertEquals(PRE_IMAGE, instance.getZapReceipt().getPreimage()); } -} \ No newline at end of file +} diff --git a/nostr-java-test/src/test/java/nostr/test/event/ZapRequestEventTest.java b/nostr-java-id/src/test/java/nostr/id/ZapRequestEventTest.java similarity index 98% rename from nostr-java-test/src/test/java/nostr/test/event/ZapRequestEventTest.java rename to nostr-java-id/src/test/java/nostr/id/ZapRequestEventTest.java index 0250cfd6d..9f1fa1f60 100644 --- a/nostr-java-test/src/test/java/nostr/test/event/ZapRequestEventTest.java +++ b/nostr-java-id/src/test/java/nostr/id/ZapRequestEventTest.java @@ -1,4 +1,4 @@ -package nostr.test.event; +package nostr.id; import nostr.base.PublicKey; import nostr.base.Relay; @@ -11,7 +11,6 @@ import nostr.event.tag.PubKeyTag; import nostr.event.tag.RelaysTag; import nostr.event.tag.SubjectTag; -import nostr.id.Identity; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -69,4 +68,4 @@ void testConstructZapRequestEvent() { Assertions.assertEquals(LNURL, instance.getZapRequest().getLnUrl()); Assertions.assertEquals(AMOUNT, instance.getZapRequest().getAmount()); } -} \ No newline at end of file +} diff --git a/nostr-java-id/src/test/resources/application-test.properties b/nostr-java-id/src/test/resources/application-test.properties new file mode 100644 index 000000000..f375f1180 --- /dev/null +++ b/nostr-java-id/src/test/resources/application-test.properties @@ -0,0 +1,4 @@ +spring.threads.virtual.enabled=true + +logging.level.nostr.id=INFO +logging.pattern.console=%msg%n diff --git a/nostr-java-id/src/test/resources/junit-platform.properties b/nostr-java-id/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..a413a5904 --- /dev/null +++ b/nostr-java-id/src/test/resources/junit-platform.properties @@ -0,0 +1,7 @@ +# junit-platform.properties + +junit.jupiter.execution.parallel.enabled=true +junit.jupiter.execution.parallel.config.strategy=dynamic +junit.jupiter.execution.parallel.mode.default=same_thread +#junit.jupiter.execution.parallel.mode.default=concurrent +#junit.jupiter.execution.parallel.mode.classes.default=concurrent diff --git a/nostr-java-test/README.md b/nostr-java-test/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/nostr-java-test/pom.xml b/nostr-java-test/pom.xml deleted file mode 100644 index 0ad8d8305..000000000 --- a/nostr-java-test/pom.xml +++ /dev/null @@ -1,189 +0,0 @@ - - - 4.0.0 - - - xyz.tcheeric - nostr-java - 0.6.5-SNAPSHOT - - - nostr-java-test - jar - - - - - org.projectlombok - lombok - - - - - ${project.groupId} - nostr-java-event - ${project.version} - - - ${project.groupId} - nostr-java-base - ${project.version} - - - ${project.groupId} - nostr-java-id - ${project.version} - test - - - ${project.groupId} - nostr-java-util - ${project.version} - - - - - org.junit.jupiter - junit-jupiter-engine - - - ${project.groupId} - nostr-java-client - ${project.version} - - - ${project.groupId} - nostr-java-api - ${project.version} - test - - - - org.springframework - spring-websocket - 6.1.10 - - - - jakarta.websocket - jakarta.websocket-api - 2.2.0 - - - - jakarta.websocket - jakarta.websocket-client-api - 2.2.0 - - - - org.glassfish.tyrus.bundles - tyrus-standalone-client - 2.1.5 - - - - org.springframework - spring-webflux - 6.1.10 - - - io.projectreactor.netty - reactor-netty-http - 1.1.20 - - - - org.awaitility - awaitility - 4.2.2 - test - - - com.google.guava - guava - 33.2.1-jre - test - - - - - - - src/main/java - - **/*.java - - - - - src/main/resources - - - - - - src/test/java - - **/*.java - - - - - src/test/resources - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - **/*Test.java - - - - - org.jacoco - jacoco-maven-plugin - - - one - - prepare-agent - - - - two - verify - - report-aggregate - - - - **/jacoco.exec - - ${project.reporting.outputDirectory}/jacoco-aggregate - - - - - - - - - - integration-tests - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - - - - diff --git a/nostr-java-test/src/test/java/nostr/test/base/BaseKeyTest.java b/nostr-java-test/src/test/java/nostr/test/base/BaseKeyTest.java deleted file mode 100644 index 599e6f300..000000000 --- a/nostr-java-test/src/test/java/nostr/test/base/BaseKeyTest.java +++ /dev/null @@ -1,80 +0,0 @@ -package nostr.test.base; - -import nostr.base.PublicKey; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertThrows; - -class BaseKeyTest { - public static final String VALID_HEXPUBKEY = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f"; - public static final String INVALID_HEXPUBKEY_NON_HEX_DIGITS = "XYZdf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f"; - public static final String INVALID_HEXPUBKEY_LENGTH_TOO_SHORT = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6"; - public static final String INVALID_HEXPUBKEY_LENGTH_TOO_LONG = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f666"; - public static final String VALID_HEXPUBKEY_ALL_ZEROS = "0000000000000000000000000000000000000000000000000000000000000000"; - public static final String VALID_HEXPUBKEY_ALL_FF = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; - public static final String INVALID_HEXPUBKEY_HAS_MULTIPLE_UPPERCASE = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; - public static final String INVALID_HEXPUBKEY_HAS_SINGLE_UPPERCASE = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6F"; - - @Test - public void testValidPublicKeyString() { - System.out.println("testValidPublicKeyString"); - assertDoesNotThrow(() -> new PublicKey(VALID_HEXPUBKEY)); - } - - @Test - public void testValidPublicKeyByteArray() { - System.out.println("testValidPublicKeyByteArray"); - assertDoesNotThrow(() -> new PublicKey(VALID_HEXPUBKEY.getBytes(StandardCharsets.UTF_8))); - } - - @Test - public void testInValidNullPublicKeyString() { - System.out.println("testInValidNullPublicKeyString"); - assertThrows(IllegalArgumentException.class, () -> new PublicKey("")); - } - - @Test - public void testInValidPublicKeyNonHexDigits() { - System.out.println("testInValidPublicKeyNonHexDigits"); - assertThrows(IllegalArgumentException.class, () -> new PublicKey(INVALID_HEXPUBKEY_NON_HEX_DIGITS)); - } - - @Test - public void testInValidPublicKeyLengthTooShort() { - System.out.println("testInValidPublicKeyLengthTooShort"); - assertThrows(IllegalArgumentException.class, () -> new PublicKey(INVALID_HEXPUBKEY_LENGTH_TOO_SHORT)); - } - - @Test - public void testInValidPublicKeyLengthTooLong() { - System.out.println("testInValidPublicKeyLengthTooShort"); - assertThrows(IllegalArgumentException.class, () -> new PublicKey(INVALID_HEXPUBKEY_LENGTH_TOO_LONG)); - } - - @Test - public void testValidPublicKeyAllZeros() { - System.out.println("testValidPublicKeyAllZeros"); - assertDoesNotThrow(() -> new PublicKey(VALID_HEXPUBKEY_ALL_ZEROS)); - } - - @Test - public void testValidPublicKeyAllFF() { - System.out.println("testValidPublicKeyAllFF"); - assertDoesNotThrow(() -> new PublicKey(VALID_HEXPUBKEY_ALL_FF)); - } - - @Test - public void testInvalidPublicKeyMultipleUppercase() { - System.out.println("testInvalidPublicKeyMultipleUppercase"); - assertThrows(IllegalArgumentException.class, () -> new PublicKey(INVALID_HEXPUBKEY_HAS_MULTIPLE_UPPERCASE)); - } - - @Test - public void testInvalidPublicKeySingleUppercase() { - System.out.println("testInvalidPublicKeySingleUppercase"); - assertThrows(IllegalArgumentException.class, () -> new PublicKey(INVALID_HEXPUBKEY_HAS_SINGLE_UPPERCASE)); - } -} diff --git a/nostr-java-test/src/test/java/nostr/test/crypto/CryptoTest.java b/nostr-java-test/src/test/java/nostr/test/crypto/CryptoTest.java deleted file mode 100644 index 58c193211..000000000 --- a/nostr-java-test/src/test/java/nostr/test/crypto/CryptoTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package nostr.test.crypto; - -import nostr.base.Signature; -import nostr.crypto.bech32.Bech32; -import nostr.crypto.bech32.Bech32Prefix; -import nostr.crypto.schnorr.Schnorr; -import nostr.event.impl.GenericEvent; -import nostr.id.Identity; -import nostr.util.NostrException; -import nostr.util.NostrUtil; -import org.junit.jupiter.api.Test; - -import static nostr.test.EntityFactory.Events.createTextNoteEvent; -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; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * - * @author squirrel - */ -public class CryptoTest { - - @Test - public void testBech32() { - try { - System.out.println("testBech32"); - - final String hexPub = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f"; - final String npub = "npub126klq89p42wk78p4j5ur8wlxmxdqepdh8tez9e4axpd4run5nahsmff27j"; - - assertEquals(npub, Bech32.toBech32(Bech32Prefix.NPUB, hexPub)); - assertEquals("56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f", Bech32.fromBech32(npub)); - } catch (NostrException ex) { - fail(ex); - } - } - - @Test - public void testVerifySignature() { - System.out.println("testVerifySignature"); - - Identity identity = Identity.generateRandomIdentity(); - final GenericEvent[] event = {createTextNoteEvent(identity.getPublicKey(), "Hello World")}; - event[0].update(); - assertDoesNotThrow(() -> { - byte[] message = NostrUtil.sha256(event[0].get_serializedEvent()); - Signature signature = identity.sign(event[0]); - boolean verification = Schnorr.verify(message, identity.getPublicKey().getRawData(), signature.getRawData()); - assertTrue(verification, "Schnorr must have a true verify result."); - - event[0] = createTextNoteEvent(identity.getPublicKey(), "Guten Tag"); - event[0].update(); - message = NostrUtil.sha256(event[0].get_serializedEvent()); - verification = Schnorr.verify(message, identity.getPublicKey().getRawData(), signature.getRawData()); - - assertFalse(verification); - }); - } - -} 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 deleted file mode 100644 index 76fe053b2..000000000 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTest.java +++ /dev/null @@ -1,664 +0,0 @@ -package nostr.test.event; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import nostr.api.EventNostr; -import nostr.api.NIP01; -import nostr.api.NIP04; -import nostr.api.NIP15; -import nostr.api.NIP32; -import nostr.api.NIP44; -import nostr.api.NIP52; -import nostr.api.NIP57; -import nostr.base.ElementAttribute; -import nostr.base.GenericTagQuery; -import nostr.base.PrivateKey; -import nostr.base.PublicKey; -import nostr.crypto.bech32.Bech32; -import nostr.crypto.bech32.Bech32Prefix; -import nostr.event.BaseTag; -import nostr.event.NIP01Event; -import nostr.event.filter.Filters; -import nostr.event.filter.GenericTagQueryFilter; -import nostr.event.filter.GeohashTagFilter; -import nostr.event.filter.HashtagTagFilter; -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.GenericTag; -import nostr.event.impl.NostrMarketplaceEvent; -import nostr.event.impl.NostrMarketplaceEvent.Product.Spec; -import nostr.event.impl.TextNoteEvent; -import nostr.event.impl.ZapReceiptEvent; -import nostr.event.impl.ZapRequestEvent; -import nostr.event.message.OkMessage; -import nostr.event.tag.GeohashTag; -import nostr.event.tag.HashtagTag; -import nostr.event.tag.IdentifierTag; -import nostr.event.tag.PubKeyTag; -import nostr.id.Identity; -import nostr.util.NostrException; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -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.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * @author eric - */ -public class ApiEventTest { - - public static final String NOSTR_JAVA_PUBKEY = "56adf01ca1aa9d6f1c35953833bbe6d99a0c85b73af222e6bd305b51f2749f6f"; - - private static final Map RELAYS = getRelays(); - - @Test - public void testNIP01CreateTextNoteEvent() throws NostrException { - System.out.println("testNIP01CreateTextNoteEvent"); - - PublicKey publicKey = new PublicKey(NOSTR_JAVA_PUBKEY); - var recipient = NIP01.createPubKeyTag(publicKey); - List tags = new ArrayList<>(); - tags.add(recipient); - Identity identity = Identity.generateRandomIdentity(); - var nip01 = new NIP01(identity); - var instance = nip01.createTextNoteEvent(tags, "Hello simplified nostr-java!") - .getEvent(); - instance.update(); - - assertNotNull(instance.getId()); - assertNotNull(instance.getCreatedAt()); - assertNull(instance.getSignature()); - - final String bech32 = instance.toBech32(); - assertNotNull(bech32); - assertEquals(Bech32Prefix.NOTE.getCode(), Bech32.decode(bech32).hrp); - } - - @Test - public void testNIP01SendTextNoteEvent() throws IOException { - System.out.println("testNIP01SendTextNoteEvent"); - - Identity identity = Identity.generateRandomIdentity(); - var nip01 = new NIP01(identity); - var instance = nip01.createTextNoteEvent("Hello simplified nostr-java!").sign(); - - var response = instance.setRelays(RELAYS).send(); - assertTrue(response instanceof OkMessage); - assertEquals(nip01.getEvent().getId(), ((OkMessage) response).getEventId()); - - nip01.close(); - } - - @Test - public void testNIP04SendDirectMessage() throws IOException { - System.out.println("testNIP04SendDirectMessage"); - - PublicKey nostr_java = new PublicKey(NOSTR_JAVA_PUBKEY); - Identity identity = Identity.generateRandomIdentity(); - var nip04 = new NIP04(identity, nostr_java); - var instance = nip04 - .createDirectMessageEvent("Quand on n'a que l'amour pour tracer un chemin et forcer le destin...") - .sign(); - - var signature = instance.getEvent().getSignature(); - assertNotNull(signature); - var response = instance.setRelays(RELAYS).send(); - assertTrue(response instanceof OkMessage); - assertEquals(nip04.getEvent().getId(), ((OkMessage) response).getEventId()); - nip04.close(); - } - - @Test - public void testNIP44SendDirectMessage() throws IOException { - System.out.println("testNIP44SendDirectMessage"); - - PublicKey nostr_java = new PublicKey(NOSTR_JAVA_PUBKEY); - - Identity identity = Identity.generateRandomIdentity(); - var nip44 = new NIP44(identity, nostr_java); - - var instance = nip44 - .createDirectMessageEvent("Quand on n'a que l'amour pour tracer un chemin et forcer le destin...").sign(); - assertNotNull(instance.getEvent().getSignature()); - var response = instance.setRelays(RELAYS).send(); - assertTrue(response instanceof OkMessage); - assertEquals(nip44.getEvent().getId(), ((OkMessage) response).getEventId()); - nip44.close(); - } - - @Test - public void testNIP01SendTextNoteEventGeoHashTag() throws IOException { - System.out.println("testNIP01SendTextNoteEventGeoHashTag"); - - Identity identity = Identity.generateRandomIdentity(); - - String targetString = "geohash_tag-location"; - GeohashTag geohashTag = new GeohashTag(targetString); - NIP01 nip01 = new NIP01<>(identity); - - nip01.createTextNoteEvent(List.of(geohashTag), "GeohashTag Test location").signAndSend(Map.of("local", "ws://localhost:5555")); - - Filters filters = new Filters( - new GeohashTagFilter<>(new GeohashTag(targetString))); - - List result = nip01.sendRequest(filters, UUID.randomUUID().toString()); - - assertFalse(result.isEmpty()); - assertEquals(2, result.size()); - assertTrue(result.stream().anyMatch(s -> s.contains(targetString))); - - nip01.close(); - } - - @Test - public void testNIP01SendTextNoteEventHashtagTag() throws IOException { - System.out.println("testNIP01SendTextNoteEventHashtagTag"); - - Identity identity = Identity.generateRandomIdentity(); - - String targetString = "hashtag-tag-value"; - HashtagTag hashtagTag = new HashtagTag(targetString); - NIP01 nip01 = new NIP01<>(identity); - - nip01.createTextNoteEvent(List.of(hashtagTag), "Hashtag Tag Test value").signAndSend(Map.of("local", "ws://localhost:5555")); - - Filters filters = new Filters( - new HashtagTagFilter<>(new HashtagTag(targetString))); - - List result = nip01.sendRequest(filters, UUID.randomUUID().toString()); - - assertFalse(result.isEmpty()); - assertEquals(2, result.size()); - assertTrue(result.stream().anyMatch(s -> s.contains(targetString))); - - nip01.close(); - } - - @Test - public void testNIP01SendTextNoteEventCustomGenericTag() throws IOException { - System.out.println("testNIP01SendTextNoteEventCustomGenericTag"); - - Identity identity = Identity.generateRandomIdentity(); - - String targetString = "custom-generic-tag"; - GenericTag genericTag = GenericTag.create("m", 1, targetString); - NIP01 nip01 = new NIP01<>(identity); - nip01.createTextNoteEvent(List.of(genericTag), "Custom Generic Tag Test").signAndSend(Map.of("local", "ws://localhost:5555")); - - Filters filters = new Filters( - new GenericTagQueryFilter<>(new GenericTagQuery("#m", targetString))); - - List result = nip01.sendRequest(filters, UUID.randomUUID().toString()); - - assertFalse(result.isEmpty()); - assertEquals(2, result.size()); - - String matcher = """ - ["m","custom-generic-tag"]"""; - - assertTrue(result.stream().anyMatch(s -> s.contains(matcher))); - - 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 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 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 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 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 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 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 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"); - - var nostr_java = new PublicKey(NOSTR_JAVA_PUBKEY); - Identity identity = Identity.generateRandomIdentity(); - var nip04 = new NIP04(identity, nostr_java); - var instance = nip04 - .createDirectMessageEvent("Quand on n'a que l'amour pour tracer un chemin et forcer le destin...") - .sign(); - - var message = NIP04.decrypt(identity, instance.getEvent()); - - assertEquals("Quand on n'a que l'amour pour tracer un chemin et forcer le destin...", message); - } - - @Test - public void testNIP44EncryptDecrypt() { - System.out.println("testNIP44EncryptDecrypt"); - - var nostr_java = new PublicKey(NOSTR_JAVA_PUBKEY); - - Identity identity = Identity.generateRandomIdentity(); - var nip44 = new NIP44(identity, nostr_java); - - var instance = nip44 - .createDirectMessageEvent("Quand on n'a que l'amour pour tracer un chemin et forcer le destin...").sign(); - var message = NIP44.decrypt(identity, instance.getEvent()); - - assertEquals("Quand on n'a que l'amour pour tracer un chemin et forcer le destin...", message); - } - - @Test - public void testNIP15CreateStallEvent() throws JsonProcessingException { - System.out.println("testNIP15CreateStallEvent"); - - Stall stall = createStall(); - var nip15 = new NIP15<>(Identity.create(PrivateKey.generateRandomPrivKey())); - - // Create and send the nostr event - var instance = nip15.createCreateOrUpdateStallEvent(stall).sign(); - var signature = instance.getEvent().getSignature(); - assertNotNull(signature); - - // Fetch the content and compare with the above original - var content = instance.getEvent().getContent(); - ObjectMapper mapper = new ObjectMapper(); - var expected = mapper.readValue(content, Stall.class); - - assertEquals(expected, stall); - } - - @Test - public void testNIP15UpdateStallEvent() throws IOException { - System.out.println("testNIP15UpdateStallEvent"); - - var stall = createStall(); - var nip15 = new NIP15<>(Identity.create(PrivateKey.generateRandomPrivKey())); - - // Create and send the nostr event - var instance = nip15.createCreateOrUpdateStallEvent(stall).sign(); - var signature = instance.getEvent().getSignature(); - assertNotNull(signature); - - var response = instance.setRelays(RELAYS).send(); - assertTrue(response instanceof OkMessage); - assertEquals(nip15.getEvent().getId(), ((OkMessage) response).getEventId()); - - // Update the shipping - var shipping = stall.getShipping(); - shipping.setCost(20.00f); - - EventNostr event = nip15.createCreateOrUpdateStallEvent(stall).sign(); - response = event.setRelays(RELAYS).send(); - assertTrue(response instanceof OkMessage); - assertEquals(nip15.getEvent().getId(), ((OkMessage) response).getEventId()); - - nip15.close(); - } - - @Test - public void testNIP15CreateProductEvent() throws IOException { - - System.out.println("testNIP15CreateProductEvent"); - - // Create the stall object - var stall = createStall(); - var nip15 = new NIP15<>(Identity.create(PrivateKey.generateRandomPrivKey())); - - // Create the product - var product = createProduct(stall); - - List categories = new ArrayList<>(); - categories.add("bijoux"); - categories.add("Hommes"); - - EventNostr event = nip15.createCreateOrUpdateProductEvent(product, categories).sign(); - var response = event.setRelays(RELAYS).send(); - assertTrue(response instanceof OkMessage); - assertEquals(nip15.getEvent().getId(), ((OkMessage) response).getEventId()); - - nip15.close(); - } - - @Test - public void testNIP15UpdateProductEvent() throws IOException { - - System.out.println("testNIP15UpdateProductEvent"); - - // Create the stall object - var stall = createStall(); - var nip15 = new NIP15<>(Identity.create(PrivateKey.generateRandomPrivKey())); - - // Create the product - var product = createProduct(stall); - - List categories = new ArrayList<>(); - categories.add("bijoux"); - categories.add("Hommes"); - - EventNostr event1 = nip15.createCreateOrUpdateProductEvent(product, categories).sign(); - var response = event1.setRelays(RELAYS).send(); - assertTrue(response instanceof OkMessage); - assertEquals(nip15.getEvent().getId(), ((OkMessage) response).getEventId()); - - product.setDescription("Un nouveau bijou en or"); - categories.add("bagues"); - - EventNostr event2 = nip15.createCreateOrUpdateProductEvent(product, categories).sign(); - response = event2.setRelays(RELAYS).send(); - assertTrue(response instanceof OkMessage); - assertEquals(nip15.getEvent().getId(), ((OkMessage) response).getEventId()); - - nip15.close(); - } - - @Test - public void testNIP32CreateNameSpace() { - - System.out.println("testNIP32CreateNameSpace"); - - var langNS = NIP32.createNameSpaceTag("Languages"); - - assertEquals("L", langNS.getCode()); - assertEquals(1, langNS.getAttributes().size()); - assertEquals("Languages", langNS.getAttributes().iterator().next().getValue()); - } - - @Test - public void testNIP32CreateLabel1() { - - System.out.println("testNIP32CreateLabel1"); - - var label = NIP32.createLabelTag("Languages", "english"); - - assertEquals("l", label.getCode()); - assertEquals(2, label.getAttributes().size()); - assertTrue(label.getAttributes().contains(new ElementAttribute("param0", "english", 32))); - assertTrue(label.getAttributes().contains(new ElementAttribute("param1", "Languages", 32))); - } - - @Test - public void testNIP32CreateLabel2() { - - System.out.println("testNIP32CreateLabel2"); - - var metadata = new HashMap(); - metadata.put("article", "the"); - var label = NIP32.createLabelTag("Languages", "english", metadata); - - assertEquals("l", label.getCode()); - assertEquals(3, label.getAttributes().size()); - assertTrue(label.getAttributes().contains(new ElementAttribute("param0", "english", 32))); - assertTrue(label.getAttributes().contains(new ElementAttribute("param1", "Languages", 32))); - assertTrue(label.getAttributes().contains(new ElementAttribute("param2", "{\\\"article\\\":\\\"the\\\"}", 32)), - "{\\\"article\\\":\\\"the\\\"}"); - } - - @Test - public void testNIP52CalendarTimeBasedEventEvent() throws IOException { - System.out.println("testNIP52CalendarTimeBasedEventEvent"); - - CalendarContent calendarContent = CalendarContent.builder( - new IdentifierTag("UUID-CalendarTimeBasedEventTest"), - "Calendar Time-Based Event title", - 1716513986268L).build(); - - calendarContent.setStartTzid("1687765220"); - calendarContent.setEndTzid("1687765230"); - - calendarContent.setLabels(List.of("english", "mycenaean greek")); - - List tags = new ArrayList<>(); - tags.add(new PubKeyTag(new PublicKey("2bed79f81439ff794cf5ac5f7bff9121e257f399829e472c7a14d3e86fe76985"), - "ws://localhost:5555", - "ISSUER")); - tags.add(new PubKeyTag(new PublicKey("494001ac0c8af2a10f60f23538e5b35d3cdacb8e1cc956fe7a16dfa5cbfc4347"), - "", - "COUNTERPARTY")); - - var nip52 = new NIP52<>(Identity.create(PrivateKey.generateRandomPrivKey())); - EventNostr event = nip52.createCalendarTimeBasedEvent(tags, "content", calendarContent).sign(); - var response = event.setRelays(RELAYS).send(); - assertTrue(response instanceof OkMessage); - assertEquals(nip52.getEvent().getId(), ((OkMessage) response).getEventId()); - - nip52.close(); - } - - @Test - void testNIP57CreateZapRequestEvent() throws NostrException { - System.out.println("testNIP57CreateZapRequestEvent"); - - Identity sender = Identity.generateRandomIdentity(); - List baseTags = new ArrayList(); - PublicKey recipient = Identity.generateRandomIdentity().getPublicKey(); - var nip57 = new NIP57(sender); - final String ZAP_REQUEST_CONTENT = "zap request content"; - final Long AMOUNT = 1232456L; - final String LNURL = "lnUrl"; - final String RELAYS_TAG = "ws://localhost:5555"; - ZapRequestEvent instance = nip57 - .createZapRequestEvent(recipient, baseTags, ZAP_REQUEST_CONTENT, AMOUNT, LNURL, RELAYS_TAG).getEvent(); - instance.update(); - - assertNotNull(instance.getId()); - assertNotNull(instance.getCreatedAt()); - assertNotNull(instance.getContent()); - assertNull(instance.getSignature()); - - assertNotNull(instance.getZapRequest()); - assertNotNull(instance.getZapRequest().getRelaysTag()); - assertNotNull(instance.getZapRequest().getAmount()); - assertNotNull(instance.getZapRequest().getLnUrl()); - - assertEquals(ZAP_REQUEST_CONTENT, instance.getContent()); - assertTrue(instance.getZapRequest().getRelaysTag().getRelays().stream() - .anyMatch(relay -> relay.getUri().equals(RELAYS_TAG))); - assertEquals(AMOUNT, instance.getZapRequest().getAmount()); - assertEquals(LNURL, instance.getZapRequest().getLnUrl()); - - final String bech32 = instance.toBech32(); - assertNotNull(bech32); - assertEquals(Bech32Prefix.NOTE.getCode(), Bech32.decode(bech32).hrp); - } - - @Test - void testNIP57CreateZapReceiptEvent() throws NostrException { - System.out.println("testNIP57CreateZapReceiptEvent"); - - Identity sender = Identity.generateRandomIdentity(); - List baseTags = new ArrayList(); - String zapRequestPubKeyTag = Identity.generateRandomIdentity().getPublicKey().toString(); - String zapRequestEventTag = Identity.generateRandomIdentity().getPublicKey().toString(); - String zapRequestAddressTag = Identity.generateRandomIdentity().getPublicKey().toString(); - final String ZAP_RECEIPT_IDENTIFIER = "ipsum"; - final String ZAP_RECEIPT_RELAY_URI = "ws://localhost:5555"; - final String BOLT_11 = "bolt11"; - final String DESCRIPTION_SHA256 = "descriptionSha256"; - final String PRE_IMAGE = "preimage"; - var nip57 = new NIP57(sender); - - ZapReceiptEvent instance = nip57.createZapReceiptEvent(zapRequestPubKeyTag, baseTags, zapRequestEventTag, - zapRequestAddressTag, ZAP_RECEIPT_IDENTIFIER, ZAP_RECEIPT_RELAY_URI, BOLT_11, DESCRIPTION_SHA256, PRE_IMAGE) - .getEvent(); - instance.update(); - - assertNotNull(instance.getId()); - assertNotNull(instance.getCreatedAt()); - assertNull(instance.getSignature()); - - assertNotNull(instance.getZapReceipt()); - assertNotNull(instance.getZapReceipt().getBolt11()); - assertNotNull(instance.getZapReceipt().getDescriptionSha256()); - assertNotNull(instance.getZapReceipt().getPreimage()); - - assertEquals(BOLT_11, instance.getZapReceipt().getBolt11()); - assertEquals(DESCRIPTION_SHA256, instance.getZapReceipt().getDescriptionSha256()); - assertEquals(PRE_IMAGE, instance.getZapReceipt().getPreimage()); - - final String bech32 = instance.toBech32(); - assertNotNull(bech32); - assertEquals(Bech32Prefix.NOTE.getCode(), Bech32.decode(bech32).hrp); - } - - public static Stall createStall() { - - // Create the county list - List countries = new ArrayList<>(); - countries.add("France"); - countries.add("Canada"); - countries.add("Cameroun"); - - // Create the shipping object - var shipping = new CreateOrUpdateStallEvent.Stall.Shipping(); - shipping.setCost(12.00f); - shipping.setCountries(countries); - shipping.setName("French Countries"); - - // Create the stall object - var stall = new CreateOrUpdateStallEvent.Stall(); - stall.setCurrency("USD"); - stall.setDescription("This is a test stall"); - stall.setName("Maximus Primus"); - stall.setShipping(shipping); - - return stall; - } - - public static NostrMarketplaceEvent.Product createProduct(Stall stall) { - - // Create the product - var product = new NostrMarketplaceEvent.Product(); - product.setCurrency("USD"); - product.setDescription("Un bijou en or"); - product.setImages(new ArrayList<>()); - product.setName("Bague"); - product.setPrice(450.00f); - product.setQuantity(4); - List specs = new ArrayList<>(); - specs.add(new Spec("couleur", "or")); - specs.add(new Spec("poids", "150g")); - product.setSpecs(specs); - product.setStall(stall); - - return product; - } - - public static Map getRelays() { - Map relays = new HashMap<>(); - Properties properties = new Properties(); - try { - InputStream is = ApiEventTest.class.getClassLoader().getResourceAsStream("relays.properties"); - if (is != null) { - properties.load(is); - for (String key : properties.stringPropertyNames()) { - relays.put(key, properties.getProperty(key)); - } - } else { - throw new RuntimeException("Unable to find 'relays.properties' in the classpath"); - } - } catch (IOException e) { - e.printStackTrace(); - } - return relays; - } -} diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientTest.java deleted file mode 100644 index a71f2c04c..000000000 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiEventTestUsingSpringWebSocketClientTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package nostr.test.event; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import nostr.api.NIP15; -import nostr.base.PrivateKey; -import nostr.client.springwebsocket.SpringWebSocketClient; -import nostr.event.impl.GenericEvent; -import nostr.event.message.EventMessage; -import nostr.id.Identity; -import static nostr.test.event.ApiEventTest.createProduct; -import static nostr.test.event.ApiEventTest.createStall; - -class ApiEventTestUsingSpringWebSocketClientTest { - private static Map relays; - private SpringWebSocketClient springWebSocketClient; - - @BeforeAll - static void setupBeforeAll() { - relays = ApiEventTest.getRelays(); - } - - @BeforeEach - void setupBeforeEach() { - relays.forEach((key, value) -> springWebSocketClient = new SpringWebSocketClient(value)); - } - - @Test - void testNIP15SendProductEventUsingSpringWebSocketClient() throws IOException { - System.out.println("testNIP15CreateProductEventUsingSpringWebSocketClient"); - var product = createProduct(createStall()); - - List categories = new ArrayList<>(); - categories.add("bijoux"); - categories.add("Hommes"); - - var nip15 = new NIP15<>(Identity.create(PrivateKey.generateRandomPrivKey())); - - GenericEvent event = nip15.createCreateOrUpdateProductEvent(product, categories).sign().getEvent(); - EventMessage message = new EventMessage(event); - - String eventResponse = springWebSocketClient.send(message).stream().findFirst().get(); - - ObjectMapper mapper = new ObjectMapper(); - - // Extract and compare only first 3 elements of the JSON array - var expectedArray = mapper.readTree(expectedResponseJson(event.getId())).get(0).asText(); - var expectedSubscriptionId = mapper.readTree(expectedResponseJson(event.getId())).get(1).asText(); - var expectedSuccess = mapper.readTree(expectedResponseJson(event.getId())).get(2).asBoolean(); - - var actualArray = mapper.readTree(eventResponse).get(0).asText(); - var actualSubscriptionId = mapper.readTree(eventResponse).get(1).asText(); - var actualSuccess = mapper.readTree(eventResponse).get(2).asBoolean(); - - - assertTrue(expectedArray.equals(actualArray), "First element should match"); - assertTrue(expectedSubscriptionId.equals(actualSubscriptionId), "Subscription ID should match"); - assertTrue(expectedSuccess == actualSuccess, "Success flag should match"); - - springWebSocketClient.closeSocket(); - } - - private String expectedResponseJson(String sha256) { - return "[\"OK\",\"" + sha256 + "\",true,\"success: request processed\"]"; - } -} diff --git a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99EventTest.java b/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99EventTest.java deleted file mode 100644 index b21fbd3eb..000000000 --- a/nostr-java-test/src/test/java/nostr/test/event/ApiNIP99EventTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package nostr.test.event; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import nostr.api.NIP99; -import nostr.base.PrivateKey; -import nostr.client.springwebsocket.SpringWebSocketClient; -import nostr.event.BaseTag; -import nostr.event.impl.ClassifiedListing; -import nostr.event.impl.GenericEvent; -import nostr.event.impl.GenericTag; -import nostr.event.message.EventMessage; -import nostr.event.tag.PriceTag; -import nostr.id.Identity; -import static nostr.test.event.ClassifiedListingEventTest.CLASSIFIED_LISTING_CONTENT; -import static nostr.test.event.ClassifiedListingEventTest.CLASSIFIED_LISTING_LOCATION; -import static nostr.test.event.ClassifiedListingEventTest.CLASSIFIED_LISTING_PUBLISHED_AT; -import static nostr.test.event.ClassifiedListingEventTest.CURRENCY; -import static nostr.test.event.ClassifiedListingEventTest.E_TAG; -import static nostr.test.event.ClassifiedListingEventTest.FREQUENCY; -import static nostr.test.event.ClassifiedListingEventTest.G_TAG; -import static nostr.test.event.ClassifiedListingEventTest.LOCATION_CODE; -import static nostr.test.event.ClassifiedListingEventTest.NUMBER; -import static nostr.test.event.ClassifiedListingEventTest.PUBLISHED_AT_CODE; -import static nostr.test.event.ClassifiedListingEventTest.P_TAG; -import static nostr.test.event.ClassifiedListingEventTest.SUBJECT_TAG; -import static nostr.test.event.ClassifiedListingEventTest.SUMMARY_CODE; -import static nostr.test.event.ClassifiedListingEventTest.TITLE_CODE; -import static nostr.test.event.ClassifiedListingEventTest.T_TAG; - -class ApiNIP99EventTest { - private static final String RELAY_URI = "ws://localhost:5555"; - private final SpringWebSocketClient springWebSocketClient; - - public ApiNIP99EventTest() { - springWebSocketClient = new SpringWebSocketClient(RELAY_URI); - } - - @Test - void testNIP99ClassifiedListingEvent() throws IOException { - System.out.println("testNIP99ClassifiedListingEvent"); - - List tags = new ArrayList<>(); - tags.add(E_TAG); - tags.add(P_TAG); - tags.add(GenericTag.create(PUBLISHED_AT_CODE, 99, CLASSIFIED_LISTING_PUBLISHED_AT)); - tags.add(GenericTag.create(LOCATION_CODE, 99, CLASSIFIED_LISTING_LOCATION)); - tags.add(SUBJECT_TAG); - tags.add(G_TAG); - tags.add(T_TAG); - - PriceTag priceTag = new PriceTag(NUMBER, CURRENCY, FREQUENCY); - ClassifiedListing classifiedListing = ClassifiedListing.builder( - TITLE_CODE, - SUMMARY_CODE, - priceTag) - .build(); - - var nip99 = new NIP99<>(Identity.create(PrivateKey.generateRandomPrivKey())); - - GenericEvent event = nip99.createClassifiedListingEvent(tags, CLASSIFIED_LISTING_CONTENT, classifiedListing).sign().getEvent(); - EventMessage message = new EventMessage(event); - - ObjectMapper mapper = new ObjectMapper(); - - // Extract and compare only first 3 elements of the JSON array - var expectedArray = mapper.readTree(expectedResponseJson(event.getId())).get(0).asText(); - var expectedSubscriptionId = mapper.readTree(expectedResponseJson(event.getId())).get(1).asText(); - var expectedSuccess = mapper.readTree(expectedResponseJson(event.getId())).get(2).asBoolean(); - - String eventResponse = springWebSocketClient.send(message).stream().findFirst().get(); - var actualArray = mapper.readTree(eventResponse).get(0).asText(); - var actualSubscriptionId = mapper.readTree(eventResponse).get(1).asText(); - var actualSuccess = mapper.readTree(eventResponse).get(2).asBoolean(); - - assertTrue(expectedArray.equals(actualArray), "First element should match"); - assertTrue(expectedSubscriptionId.equals(actualSubscriptionId), "Subscription ID should match"); - assertTrue(expectedSuccess == actualSuccess, "Success flag should match"); - - springWebSocketClient.closeSocket(); - } - - private String expectedResponseJson(String sha256) { - return "[\"OK\",\"" + sha256 + "\",true,\"success: request processed\"]"; - } -} diff --git a/nostr-java-test/src/test/java/nostr/test/event/BaseTagTest.java b/nostr-java-test/src/test/java/nostr/test/event/BaseTagTest.java deleted file mode 100644 index 06c1d9fc2..000000000 --- a/nostr-java-test/src/test/java/nostr/test/event/BaseTagTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package nostr.test.event; - -import nostr.event.BaseTag; -import nostr.event.impl.GenericTag; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class BaseTagTest { - - BaseTag genericTag = GenericTag.create("id", 1, "value"); - - @Test - void getNip() { - assertEquals(1, genericTag.getNip()); - } - - @Test - void testHashCode() { - assertEquals(112174237, genericTag.hashCode()); - } - - @Test - void testToString() { - String result = "GenericTag(code=id, nip=1, attributes=[ElementAttribute(name=param0, value=value, nip=null)])"; - assertEquals(result, genericTag.toString()); - } - -} \ No newline at end of file diff --git a/nostr-java-test/src/test/java/nostr/test/event/PriceTagTest.java b/nostr-java-test/src/test/java/nostr/test/event/PriceTagTest.java deleted file mode 100644 index d242d60f7..000000000 --- a/nostr-java-test/src/test/java/nostr/test/event/PriceTagTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package nostr.test.event; - -import nostr.event.tag.PriceTag; -import org.junit.jupiter.api.Test; - -import java.lang.reflect.Field; -import java.math.BigDecimal; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class PriceTagTest { - - @Test - void getSupportedFields() { - PriceTag priceTag = new PriceTag(new BigDecimal(11111), "BTC", "NANOSECONDS"); - assertDoesNotThrow(() -> { - List list = priceTag.getSupportedFields().stream().toList(); - assertTrue(List.of("number", "currency", "frequency").containsAll(list.stream().map(Field::getName).toList())); - assertTrue(List.of("java.math.BigDecimal", "java.lang.String").containsAll(list.stream().map(field -> field.getAnnotatedType().toString()).toList())); - }); - } - -} \ No newline at end of file diff --git a/nostr-java-test/src/test/java/nostr/test/event/SignatureTest.java b/nostr-java-test/src/test/java/nostr/test/event/SignatureTest.java deleted file mode 100644 index 18b016199..000000000 --- a/nostr-java-test/src/test/java/nostr/test/event/SignatureTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package nostr.test.event; - -import nostr.base.Signature; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SignatureTest { - @Test - public void testSignatureStringLength() { - assertDoesNotThrow(() -> - Signature.fromString("86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546")); - - assertTrue( - assertThrows(IllegalArgumentException.class, () -> Signature.fromString("86f25c161fec51b9e441bdb2c09095d5f8b92fdce66cb80d9ef09fad6ce53eaa14c5e16787c42f5404905536e43ebec0e463aee819378a4acbe412c533e60546a")) - .getMessage().contains("[129], target length: [128]")); - } -} diff --git a/nostr-java-test/src/test/resources/app.properties b/nostr-java-test/src/test/resources/app.properties deleted file mode 100644 index 4ff8e632d..000000000 --- a/nostr-java-test/src/test/resources/app.properties +++ /dev/null @@ -1,3 +0,0 @@ -profile=profile.properties -client=client.properties -relays=relays.properties \ No newline at end of file diff --git a/nostr-java-test/src/test/resources/profile.properties b/nostr-java-test/src/test/resources/profile.properties deleted file mode 100644 index 44f2c68c9..000000000 --- a/nostr-java-test/src/test/resources/profile.properties +++ /dev/null @@ -1,3 +0,0 @@ - -privateKey=519672a628310117110bfa93798eb03566f99b47d75b3998a1d65366aff97988 -publicKey= \ No newline at end of file diff --git a/nostr-java-util/build.gradle b/nostr-java-util/build.gradle new file mode 100644 index 000000000..d6be2576a --- /dev/null +++ b/nostr-java-util/build.gradle @@ -0,0 +1,5 @@ +plugins { + id 'nostr-java.conventions' +} + +description = 'nostr-java-util' diff --git a/nostr-java-util/pom.xml b/nostr-java-util/pom.xml index 7dc6acd13..2a63680da 100644 --- a/nostr-java-util/pom.xml +++ b/nostr-java-util/pom.xml @@ -1,26 +1,13 @@ - - + 4.0.0 xyz.tcheeric nostr-java 0.6.5-SNAPSHOT - - + ../pom.xml + + nostr-java-util jar - - - nostr.util.NostrUtil - - - - - - org.projectlombok - lombok - - diff --git a/nostr-java-util/src/main/java/module-info.java b/nostr-java-util/src/main/java/module-info.java deleted file mode 100644 index 8658ffc7d..000000000 --- a/nostr-java-util/src/main/java/module-info.java +++ /dev/null @@ -1,7 +0,0 @@ -module nostr.util { - requires static lombok; - requires java.logging; - - exports nostr.util; - exports nostr.util.thread; -} diff --git a/nostr-java-util/src/main/java/nostr/util/NostrException.java b/nostr-java-util/src/main/java/nostr/util/NostrException.java index 9c405b27d..1210fdbe4 100644 --- a/nostr-java-util/src/main/java/nostr/util/NostrException.java +++ b/nostr-java-util/src/main/java/nostr/util/NostrException.java @@ -8,5 +8,7 @@ */ @StandardException public class NostrException extends Exception { - + public NostrException(String message) { + super(message); + } } diff --git a/nostr-java-util/src/main/java/nostr/util/config/ExceptionConfig.java b/nostr-java-util/src/main/java/nostr/util/config/ExceptionConfig.java new file mode 100644 index 000000000..14612a46d --- /dev/null +++ b/nostr-java-util/src/main/java/nostr/util/config/ExceptionConfig.java @@ -0,0 +1,5 @@ +//package nostr.util.config; + +//@Configuration +//public class ExceptionConfig { +//} diff --git a/nostr-java-util/src/main/java/nostr/util/thread/HexStringValidator.java b/nostr-java-util/src/main/java/nostr/util/thread/HexStringValidator.java index e7da244be..6411b4ef0 100644 --- a/nostr-java-util/src/main/java/nostr/util/thread/HexStringValidator.java +++ b/nostr-java-util/src/main/java/nostr/util/thread/HexStringValidator.java @@ -1,52 +1,30 @@ package nostr.util.thread; import lombok.NonNull; +import org.apache.commons.lang3.StringUtils; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.function.BiFunction; -import java.util.function.Function; +import java.util.Objects; +import java.util.function.BiPredicate; +import java.util.function.Predicate; public class HexStringValidator { - private static final String validHexChars = "0123456789abcdef"; + private static final String validHexChars = "0123456789abcdef"; - private static final BiFunction lengthCheck = (s, targetLength) -> s.length() == targetLength; - private static final Function hexCharsCheck = HexStringValidator::checkValidHexChars; - private static final Function upperCaseCheck = s -> s.toLowerCase().equals(s); + private static final BiPredicate lengthCheck = (s, l) -> Objects.equals(s.length(), l); + private static final Predicate hexCharsCheck = HexStringValidator::validHex; + private static final Predicate upperCaseCheck = s -> s.toLowerCase().equals(s); - public static void validateHex(@NonNull String hexString, int targetLength) { - List exceptions = new ArrayList<>(); - Optional.of(hexString) // non-null enforcement - .filter(s -> { - if (!lengthCheck.apply(s, targetLength)) { - return exceptions.add(String.format("Invalid hex string: [%s], length: [%d], target length: [%d]", hexString, hexString.length(), targetLength)); - } - return true; - }) - .filter(s -> { - if (!hexCharsCheck.apply(s)) { - exceptions.add(String.format("Invalid hex string: [%s] has non-hex characters", hexString)); - } - return true; - }) - .filter(s -> { - if (!upperCaseCheck.apply(s)) { - exceptions.add(String.format("Invalid hex string: [%s] has uppcase characters", hexString)); - } - return true; - }); - - if (!exceptions.isEmpty()) { - throw new IllegalArgumentException(exceptions.getFirst()); + public static void validateHex(@NonNull String hexString, int targetLength) { + // split into distinct checks per unique/specific error message + assert lengthCheck.test(hexString, targetLength) : + String.format("Invalid hex string: [%s], length: [%d], target length: [%d]", hexString, hexString.length(), targetLength); + assert hexCharsCheck.test(hexString) : + String.format("Invalid hex string: [%s] has non-hex characters", hexString); + assert upperCaseCheck.test(hexString) : + String.format("Invalid hex string: [%s] has upper-case characters", hexString); } - } - private static Boolean checkValidHexChars(String aHexString) { - for (char a : aHexString.toLowerCase().toCharArray()) { - if (validHexChars.indexOf(a) < 0) - return false; + private static Boolean validHex(String aHexString) { + return StringUtils.containsOnly(aHexString.toLowerCase(), validHexChars); } - return true; - } } diff --git a/nostr-java-test/src/test/java/nostr/test/base/NostrUtilTest.java b/nostr-java-util/src/test/java/nostr/util/NostrUtilTest.java similarity index 92% rename from nostr-java-test/src/test/java/nostr/test/base/NostrUtilTest.java rename to nostr-java-util/src/test/java/nostr/util/NostrUtilTest.java index 677a3a90f..01c7bb85d 100644 --- a/nostr-java-test/src/test/java/nostr/test/base/NostrUtilTest.java +++ b/nostr-java-util/src/test/java/nostr/util/NostrUtilTest.java @@ -1,7 +1,6 @@ -package nostr.test.base; +package nostr.util; import lombok.extern.java.Log; -import nostr.util.NostrUtil; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/nostr-java-util/src/test/resources/application-test.properties b/nostr-java-util/src/test/resources/application-test.properties new file mode 100644 index 000000000..1d2fa226b --- /dev/null +++ b/nostr-java-util/src/test/resources/application-test.properties @@ -0,0 +1,4 @@ +spring.threads.virtual.enabled=true + +logging.level.nostr.util=INFO +logging.pattern.console=%msg%n diff --git a/nostr-java-util/src/test/resources/junit-platform.properties b/nostr-java-util/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..a413a5904 --- /dev/null +++ b/nostr-java-util/src/test/resources/junit-platform.properties @@ -0,0 +1,7 @@ +# junit-platform.properties + +junit.jupiter.execution.parallel.enabled=true +junit.jupiter.execution.parallel.config.strategy=dynamic +junit.jupiter.execution.parallel.mode.default=same_thread +#junit.jupiter.execution.parallel.mode.default=concurrent +#junit.jupiter.execution.parallel.mode.classes.default=concurrent diff --git a/pom.xml b/pom.xml index df4632823..5de38fe04 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,4 @@ - - + 4.0.0 xyz.tcheeric @@ -8,6 +6,12 @@ 0.6.5-SNAPSHOT pom + + org.springframework.boot + spring-boot-starter-parent + 3.4.3 + + ${project.artifactId} Java SDK for Nostr, for generating, signing and publishing events to relays https://github.com/tcheeric/nostr-java @@ -66,37 +70,29 @@ nostr-java-event nostr-java-examples nostr-java-id - nostr-java-test nostr-java-util nostr-java-client nostr-java-api nostr-java-encryption - nostr-java-encryption-nip04 - nostr-java-encryption-nip44 - ${version} - UTF-8 + 0.6.5-SNAPSHOT 21 + + 3.4.3 ${java.version} ${java.version} - - 4.9.3 - 2.8.0 + UTF-8 - 1.18.34 - - 1.70 - 1.78 - - 2.17.2 + 2.18.3 3.17.0 + 1.18.34 - 5.10.2 - 3.23.1 + 5.10.2 + 33.4.0-jre 3.13.0 @@ -106,42 +102,53 @@ 0.8.12 - - - - - org.projectlombok - lombok - ${lombok.version} - - - com.fasterxml.jackson.core - jackson-databind - ${jackson-databind.version} - - - org.bouncycastle - bcprov-jdk18on - ${bcprov-jdk18on.version} - - - - - org.junit.jupiter - junit-jupiter-engine - ${junit.version} - test - - - org.assertj - assertj-core - ${assertj.version} - test - + + + org.springframework.boot + spring-boot-starter + ${spring-boot.version} + + + org.springframework.boot + spring-boot-devtools + ${spring-boot.version} + - - + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + org.projectlombok + lombok + ${lombok.version} + + + com.fasterxml.jackson.module + jackson-module-afterburner + ${jackson-module-afterburner.version} + + + org.springframework.boot + spring-boot-starter-test + ${spring-boot.version} + test + + + org.junit.jupiter + junit-jupiter + ${junit-jupiter.version} + test + + + com.google.guava + guava + ${guava.version} + test + + @@ -233,25 +240,34 @@ maven-compiler-plugin ${maven-compiler-plugin.version} - true - - - org.projectlombok - lombok - ${lombok.version} - - + ${maven.compiler.source} + ${maven.compiler.target} org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} + + + + test + + + org.jacoco jacoco-maven-plugin ${jacoco-maven-plugin.version} + + + + prepare-agent + report + + + org.apache.maven.plugins @@ -263,10 +279,33 @@ integration-test verify + + 1 + + + + + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + org.jacoco + jacoco-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..6f73d74f1 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,24 @@ +buildscript { + repositories { + gradlePluginPortal { + url = 'https://plugins.gradle.org/m2/' + } + } + dependencies { + classpath 'com.adarshr:gradle-test-logger-plugin:4.0.0' + } +} + +rootProject.name = 'nostr-java' + +include(':nostr-java-crypto') +include(':nostr-java-base') +include(':nostr-java-client') +include(':nostr-java-event') +include(':nostr-java-examples') +include(':nostr-java-util') +include(':nostr-java-api') +include(':nostr-java-id') +include(':nostr-java-encryption') + +//id 'org.gradle.test-retry' version '1.6.2'