From fef17d90e23b356475957847e6223eab8e6be73d Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Wed, 12 Oct 2022 14:43:50 -0400 Subject: [PATCH 01/28] feat: `notify:list` ResponseTransformer --- .../atsign/common/ResponseTransformers.java | 49 ++++++++++++++++++- .../response_models/NotifyListResponse.java | 43 ++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 at_client/src/main/java/org/atsign/common/response_models/NotifyListResponse.java diff --git a/at_client/src/main/java/org/atsign/common/ResponseTransformers.java b/at_client/src/main/java/org/atsign/common/ResponseTransformers.java index dd37da49..a004cf7a 100644 --- a/at_client/src/main/java/org/atsign/common/ResponseTransformers.java +++ b/at_client/src/main/java/org/atsign/common/ResponseTransformers.java @@ -1,10 +1,12 @@ package org.atsign.common; +import java.util.ArrayList; import java.util.List; import java.util.Map; import org.atsign.client.api.Secondary.Response; import org.atsign.common.response_models.LlookupAllResponse; +import org.atsign.common.response_models.NotifyListResponse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -71,6 +73,52 @@ public Map transform(Response value) { } } + public static class NotifyListResponseTransformer implements ResponseTransformer { + /* + * [ + * { + * "id": "8797feda-d2d6-432a-9b9d-cc23184f60eb", + * "from": "@fascinatingsnow", + * "to": "@smoothalligator", + * "key": "@smoothalligator:test@fascinatingsnow", + * "value": null, + * "operation": "update", + * "epochMillis": 1665514962777, + * "messageType": "MessageType.key", + * "isEncrypted": false + * } + * ] + */ + @Override + public NotifyListResponse transform(Response value) { + NotifyListResponse model = new NotifyListResponse(); + model.notifications = new ArrayList(); + + if(value.data == null || value.data.isEmpty() || value.data.equalsIgnoreCase("null")) { + return model; + } + + // value.data == "[{}, {}, {}]" + String s = value.data.substring(1, value.data.length() - 1); // s == "{}, {}, {}" + String[] notificationStrs = s.split(",\\s*\\{"); // notificationStrs == ["{}", "{}", "{}"] + for(int i = 1; i < notificationStrs.length; i++) { + notificationStrs[i] = "{" + notificationStrs[i]; + } + + ObjectMapper mapper = new ObjectMapper(); + for(String notificationStr : notificationStrs) { + try { + NotifyListResponse.Notification notification = mapper.readValue(notificationStr, NotifyListResponse.Notification.class); + model.notifications.add(notification); + } catch (Exception e) { + e.printStackTrace(); + } + } + + return model; + } + } + public static class NotifyResponseTransformer implements ResponseTransformer { @Override public String transform(Response value) { @@ -78,7 +126,6 @@ public String transform(Response value) { } } - public static class NotificationStatusResponseTransformer implements ResponseTransformer { @Override public NotificationStatus transform(Response value) { diff --git a/at_client/src/main/java/org/atsign/common/response_models/NotifyListResponse.java b/at_client/src/main/java/org/atsign/common/response_models/NotifyListResponse.java new file mode 100644 index 00000000..aa44179f --- /dev/null +++ b/at_client/src/main/java/org/atsign/common/response_models/NotifyListResponse.java @@ -0,0 +1,43 @@ +package org.atsign.common.response_models; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class NotifyListResponse { + + @JsonProperty + public List notifications; + + public static class Notification { + @JsonProperty + public String id; + + @JsonProperty + public String from; + + @JsonProperty + public String to; + + @JsonProperty + public String key; + + @JsonProperty + public String value; + + @JsonProperty + public String operation; + + @JsonProperty + public Long epochMillis; + + @JsonProperty + public String messageType; + + @JsonProperty + public Boolean isEncrypted; + + } + + +} From f751af90ce0c19e84a8e9756ce1470d9b969f692 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 20 Oct 2022 18:16:57 -0400 Subject: [PATCH 02/28] feat: AtNotification class --- .../api/impl/notification/AtNotification.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 at_client/src/main/java/org/atsign/client/api/impl/notification/AtNotification.java diff --git a/at_client/src/main/java/org/atsign/client/api/impl/notification/AtNotification.java b/at_client/src/main/java/org/atsign/client/api/impl/notification/AtNotification.java new file mode 100644 index 00000000..76ef3612 --- /dev/null +++ b/at_client/src/main/java/org/atsign/client/api/impl/notification/AtNotification.java @@ -0,0 +1,56 @@ +package org.atsign.client.api.impl.notification; + +import java.util.ArrayList; +import java.util.List; + +import org.atsign.client.api.Secondary; +import org.atsign.common.ResponseTransformers.NotifyListResponseTransformer; +import org.atsign.common.response_models.NotifyListResponse; + +/** + * Class represents a notification in the atProtocol. + */ +public class AtNotification { + + public String id; // id of the notification + public String key; // notification data + public String from; // the atSign the notification is sent by + public String to; // the atSign the notification is being sent to + public Long epochMillis; // when the notification was sent in epoch millis + public String value; // notification value, [OPTIONAL] + public String operation; // "update", "delete", [OPTIONAL] + public String messageType; // MessageType.Text or MessageType.Key + public Boolean isEncrypted; // notification + + @Override + public String toString() { + return "{\"id\": \"" + id + "\", \"key\": \"" + key + "\", \"from\": \"" + from + "\", \"to\": \"" + to + "\", \"epochMillis\": " + epochMillis.toString() + ", \"value\": \"" + value + "\", \"operation\": \"" + operation + "\", \"messageType\": \"" + messageType + "\", \"isEncrypted\": " + isEncrypted.toString() + "}"; + } + + /** + * Convert the Secondary.Response into a List object + * @param response The response data from `data:` after running + * `notify:list` + * @return a List where each AtNotification + */ + public static List fromResponse(Secondary.Response response) { + NotifyListResponseTransformer transformer = new NotifyListResponseTransformer(); + NotifyListResponse model = transformer.transform(response); + List notifications = new ArrayList(); + model.notifications.stream().forEach((n) -> { + AtNotification notification = new AtNotification(); + notification.id = n.id; + notification.key = n.key; + notification.from = n.from; + notification.to = n.to; + notification.epochMillis = n.epochMillis; + notification.value = n.value; + notification.operation = n.operation; + notification.messageType = n.messageType; + notification.isEncrypted = n.isEncrypted; + notifications.add(notification); + }); + return notifications; + } + +} From 1a1a78340a1f2dba2b642b7587edf889340460c7 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 20 Oct 2022 18:17:07 -0400 Subject: [PATCH 03/28] feat: NotificationParams class --- .../impl/notification/NotificationParams.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 at_client/src/main/java/org/atsign/client/api/impl/notification/NotificationParams.java diff --git a/at_client/src/main/java/org/atsign/client/api/impl/notification/NotificationParams.java b/at_client/src/main/java/org/atsign/client/api/impl/notification/NotificationParams.java new file mode 100644 index 00000000..a0bca159 --- /dev/null +++ b/at_client/src/main/java/org/atsign/client/api/impl/notification/NotificationParams.java @@ -0,0 +1,71 @@ +package org.atsign.client.api.impl.notification; + +import java.util.UUID; + +import org.atsign.common.AtSign; +import org.atsign.common.KeyBuilders; +import org.atsign.common.NotificationEnums; +import org.atsign.common.Keys.AtKey; +import org.atsign.common.Keys.SharedKey; +import org.atsign.common.NotificationEnums.Operation; + + +public class NotificationParams { + + private String id; + private AtKey atKey; + private String value; // nullable (optional) + private NotificationEnums.Operation operation; + private NotificationEnums.MessageType messageType; + private NotificationEnums.Priority priority; + private NotificationEnums.Strategy strategy; + // private Integer latestN = 1; + // private String notifier = SYSTEM; + + /** + * + * @param atKey AtKey object which contains the sharedBy and sharedWith atSign. + * @param value nullable (optional) + * @return NotificationParams object + */ + public static NotificationParams forUpdate(AtKey atKey, String value) { + NotificationParams params = new NotificationParams(); + params.id = UUID.randomUUID().toString(); + params.atKey = atKey; + params.value = value; + params.operation = Operation.UPDATE; + params.messageType = NotificationEnums.MessageType.KEY; + params.priority = NotificationEnums.Priority.LOW; + params.strategy = NotificationEnums.Strategy.ALL; + return params; + } + + public static NotificationParams forDelete(AtKey atKey) { + NotificationParams params = new NotificationParams(); + params.id = UUID.randomUUID().toString(); + params.atKey = atKey; + params.operation = Operation.DELETE; + params.messageType = NotificationEnums.MessageType.KEY; + params.priority = NotificationEnums.Priority.LOW; + params.strategy = NotificationEnums.Strategy.ALL; + return params; + } + + public static NotificationParams forText(String text, String whomToNotify, Boolean shouldEncrypt) { + + SharedKey sharedKey = new KeyBuilders.SharedKeyBuilder( + new AtSign(""), new AtSign(whomToNotify)).build(); + sharedKey.metadata.isEncrypted = shouldEncrypt; + + + NotificationParams params = new NotificationParams(); + params.id = UUID.randomUUID().toString(); + params.atKey = sharedKey; + params.operation = Operation.UPDATE; + params.messageType = NotificationEnums.MessageType.TEXT; + params.priority = NotificationEnums.Priority.LOW; + params.strategy = NotificationEnums.Strategy.ALL; + return params; + } + +} \ No newline at end of file From 58abfcc2e6a1a8ef0b5978fceee952693ea5d865 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 20 Oct 2022 18:17:26 -0400 Subject: [PATCH 04/28] feat: NotificationEnums class --- .../org/atsign/common/NotificationEnums.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 at_client/src/main/java/org/atsign/common/NotificationEnums.java diff --git a/at_client/src/main/java/org/atsign/common/NotificationEnums.java b/at_client/src/main/java/org/atsign/common/NotificationEnums.java new file mode 100644 index 00000000..f46d6019 --- /dev/null +++ b/at_client/src/main/java/org/atsign/common/NotificationEnums.java @@ -0,0 +1,38 @@ +package org.atsign.common; + +public class NotificationEnums { + + public enum Operation { + UPDATE, DELETE, APPEND, REMOVE,; + + public String toString() { + return this.name().toLowerCase(); + } + + } + + public enum Priority { + LOW, MEDIUM, HIGH,; + + public String toString() { + return this.name().toLowerCase(); + } + + } + + public enum Strategy { + ALL, LATEST,; + + public String toString() { + return this.name().toLowerCase(); + } + } + + public enum MessageType { + KEY, TEXT,; + + public String toString() { + return "MessageType." + this.name().toLowerCase(); + } + } +} From 1784ac9b733cf745468884c46042d098867ba337 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 24 Oct 2022 18:49:36 -0400 Subject: [PATCH 05/28] feat: AtNotification class --- .../client/api/{impl => }/notification/AtNotification.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename at_client/src/main/java/org/atsign/client/api/{impl => }/notification/AtNotification.java (98%) diff --git a/at_client/src/main/java/org/atsign/client/api/impl/notification/AtNotification.java b/at_client/src/main/java/org/atsign/client/api/notification/AtNotification.java similarity index 98% rename from at_client/src/main/java/org/atsign/client/api/impl/notification/AtNotification.java rename to at_client/src/main/java/org/atsign/client/api/notification/AtNotification.java index 76ef3612..27831e8c 100644 --- a/at_client/src/main/java/org/atsign/client/api/impl/notification/AtNotification.java +++ b/at_client/src/main/java/org/atsign/client/api/notification/AtNotification.java @@ -1,4 +1,4 @@ -package org.atsign.client.api.impl.notification; +package org.atsign.client.api.notification; import java.util.ArrayList; import java.util.List; From fd658d7cfa632a44926abfe3993381f02932892d Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 24 Oct 2022 18:50:09 -0400 Subject: [PATCH 06/28] feat: NotificationParams getters --- .../notification/NotificationParams.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) rename at_client/src/main/java/org/atsign/client/api/{impl => }/notification/NotificationParams.java (81%) diff --git a/at_client/src/main/java/org/atsign/client/api/impl/notification/NotificationParams.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java similarity index 81% rename from at_client/src/main/java/org/atsign/client/api/impl/notification/NotificationParams.java rename to at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java index a0bca159..c69ef34c 100644 --- a/at_client/src/main/java/org/atsign/client/api/impl/notification/NotificationParams.java +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java @@ -1,4 +1,4 @@ -package org.atsign.client.api.impl.notification; +package org.atsign.client.api.notification; import java.util.UUID; @@ -22,6 +22,34 @@ public class NotificationParams { // private Integer latestN = 1; // private String notifier = SYSTEM; + public String getNotificationId() { + return id; + } + + public AtKey getAtKey() { + return atKey; + } + + public String getValue() { + return value; + } + + public NotificationEnums.Operation getOperation() { + return operation; + } + + public NotificationEnums.MessageType getMessageType() { + return messageType; + } + + public NotificationEnums.Priority getPriority() { + return priority; + } + + public NotificationEnums.Strategy getStrategy() { + return strategy; + } + /** * * @param atKey AtKey object which contains the sharedBy and sharedWith atSign. From aac17bb6a0d63f001ec9e1601c16bc669ec74e19 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 24 Oct 2022 18:50:22 -0400 Subject: [PATCH 07/28] feat: NotificationResult class --- .../api/notification/NotificationResult.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 at_client/src/main/java/org/atsign/client/api/notification/NotificationResult.java diff --git a/at_client/src/main/java/org/atsign/client/api/notification/NotificationResult.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationResult.java new file mode 100644 index 00000000..232f89aa --- /dev/null +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationResult.java @@ -0,0 +1,46 @@ +package org.atsign.client.api.notification; + +import org.atsign.common.NotificationStatus; +import org.atsign.common.Keys.AtKey; + +public class NotificationResult { + private String notificationId; + private AtKey atKey; + private NotificationStatus notificationStatus; + + public NotificationResult(String notificationId, AtKey atKey, NotificationStatus notificationStatus) { + this.notificationId = notificationId; + this.atKey = atKey; + this.notificationStatus = notificationStatus; + } + + public void setAtKey(AtKey atKey) { + this.atKey = atKey; + } + + public void setNotificationId(String notificationId) { + this.notificationId = notificationId; + } + + public void setNotificationStatus(NotificationStatus notificationStatus) { + this.notificationStatus = notificationStatus; + } + + public AtKey getAtKey() { + return atKey; + } + + public String getNotificationId() { + return notificationId; + } + + public NotificationStatus getNotificationStatus() { + return notificationStatus; + } + + @Override + public String toString() { + return "id: " + notificationId + " | status: " + notificationStatus.name(); + } + +} From 54339e34331a1204d16ec4997dbb4a181452a60b Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 24 Oct 2022 18:50:39 -0400 Subject: [PATCH 08/28] feat: NotificationService interface --- .../api/notification/NotificationService.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java diff --git a/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java new file mode 100644 index 00000000..6a7dd2bb --- /dev/null +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java @@ -0,0 +1,23 @@ +package org.atsign.client.api.notification; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.atsign.client.api.AtClient; +import org.atsign.client.api.AtEvents; +import org.atsign.client.api.impl.clients.NotificationServiceImpl; + +public interface NotificationService { + + public static NotificationService create(AtClient atClient) { + return new NotificationServiceImpl(atClient); + } + + CompletableFuture subscribe(String regex, boolean shouldDecrypt); // TODO Replace "Object" with Stream or similar. See: https://github.com/atsign-foundation/at_client_sdk/blob/99707459494594fbdc3f0873769eeca29c6a3124/packages/at_client/lib/src/service/notification_service.dart#L14 + CompletableFuture notify(NotificationParams params); + CompletableFuture getStatus(String notificationId); + CompletableFuture> notifyList(); + CompletableFuture notifyDelete(String notificationId); + CompletableFuture notifyDeleteAll(); + +} From 095f428b2fd4e1fc51b3842a6696eaebf37abaa8 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 24 Oct 2022 18:50:49 -0400 Subject: [PATCH 09/28] feat: NotifyListVerbBuilder --- .../java/org/atsign/common/VerbBuilders.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/at_client/src/main/java/org/atsign/common/VerbBuilders.java b/at_client/src/main/java/org/atsign/common/VerbBuilders.java index a4823be9..8aeb97e6 100644 --- a/at_client/src/main/java/org/atsign/common/VerbBuilders.java +++ b/at_client/src/main/java/org/atsign/common/VerbBuilders.java @@ -728,4 +728,41 @@ public String build() { } } + public static class NotifyListVerbBuilder implements VerbBuilder { + + // get a list of notification json objects by running `notify:list` + + private String regex; // optional + private Long from; // optional (epochMillis notification created) + private Long to; // optional (epochMillis notification created) + + public void setRegex(String regex) { + this.regex = regex; + } + + public void setFrom(Long from) { + this.from = from; + } + + public void setTo(Long to) { + this.to = to; + } + + @Override + public String build() { + String b = "notify:list"; + if(from != null) { + b += ":" + from; + } + if(to != null) { + b += ":" + to; + } + if (regex != null) { + b += ":" + regex; + } + return b; + } + + } + } From b19a575d2c976e5e746eff658a48555d71eb0075 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 24 Oct 2022 18:51:13 -0400 Subject: [PATCH 10/28] feat: NotificationServiceImpl init --- .../impl/clients/NotificationServiceImpl.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 at_client/src/main/java/org/atsign/client/api/impl/clients/NotificationServiceImpl.java diff --git a/at_client/src/main/java/org/atsign/client/api/impl/clients/NotificationServiceImpl.java b/at_client/src/main/java/org/atsign/client/api/impl/clients/NotificationServiceImpl.java new file mode 100644 index 00000000..7d9d6f6e --- /dev/null +++ b/at_client/src/main/java/org/atsign/client/api/impl/clients/NotificationServiceImpl.java @@ -0,0 +1,65 @@ +package org.atsign.client.api.impl.clients; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + + +import org.atsign.client.api.AtClient; +import org.atsign.client.api.notification.AtNotification; +import org.atsign.client.api.notification.NotificationParams; +import org.atsign.client.api.notification.NotificationResult; +import org.atsign.client.api.notification.NotificationService; +import org.atsign.client.util.AtClientValidation; +import org.atsign.common.AtSign; +import org.atsign.common.NotificationStatus; + +public class NotificationServiceImpl implements NotificationService { + + private final AtClient atClient; + + public NotificationServiceImpl(AtClient atClient) { + this.atClient = atClient; + } + + @Override + public CompletableFuture subscribe(String regex, boolean shouldDecrypt) { + throw new IllegalCallerException("Not implemented yet"); + } + + @Override + + public CompletableFuture notify(NotificationParams params) { + + return CompletableFuture.supplyAsync(() -> { + NotificationResult result = new NotificationResult(params.getNotificationId(), params.getAtKey(), NotificationStatus.undelivered); + if(params.getAtKey().sharedBy == null) { + params.getAtKey().sharedBy = atClient.getAtSign(); + } + + return result; + }); + + + } + + @Override + public CompletableFuture getStatus(String notificationId) { + throw new IllegalCallerException("Not implemented yet"); + } + + @Override + public CompletableFuture> notifyList() { + throw new IllegalCallerException("Not implemented yet"); + } + + @Override + public CompletableFuture notifyDelete(String notificationId) { + throw new IllegalCallerException("Not implemented yet"); + } + + @Override + public CompletableFuture notifyDeleteAll() { + throw new IllegalCallerException("Not implemented yet"); + } + +} From 67d8cd2ebe0bc951c61367db8497ec20bc37c42b Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Tue, 1 Nov 2022 13:42:39 -0400 Subject: [PATCH 11/28] feat: AtClientValidation.validateNotificationRequest --- .../client/util/AtClientValidation.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/at_client/src/main/java/org/atsign/client/util/AtClientValidation.java b/at_client/src/main/java/org/atsign/client/util/AtClientValidation.java index 39daa758..7f1787c2 100644 --- a/at_client/src/main/java/org/atsign/client/util/AtClientValidation.java +++ b/at_client/src/main/java/org/atsign/client/util/AtClientValidation.java @@ -3,10 +3,12 @@ import java.io.IOException; import org.atsign.client.api.Secondary; +import org.atsign.client.api.notification.NotificationParams; import org.atsign.common.AtException; import org.atsign.common.AtSign; import org.atsign.common.Keys.AtKey; import org.atsign.common.Keys.Metadata; +import org.atsign.common.NotificationEnums.MessageType; public class AtClientValidation { @@ -120,4 +122,21 @@ public static void validateAtKey(AtKey atKey, String rootUrl) throws AtException } + public static void validateNotificationRequest(NotificationParams params, String rootDomain, int rootPort) throws AtException{ + if(params.getAtKey().sharedBy == null) { + throw new AtException("AtKey.sharedBy cannot be null"); + } + + if(params.getAtKey().sharedWith == null) { + throw new AtException("AtKey.sharedWith cannot be null"); + } + + atSignExists(params.getAtKey().sharedWith, rootDomain, rootPort + ""); + + if(params.getMessageType() != MessageType.TEXT) { + validateAtKey(params.getAtKey(), rootDomain + ":" + rootPort); + } + + } + } \ No newline at end of file From 80261be8d77904f27095ab728d70ed0662bf0e29 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Tue, 1 Nov 2022 13:42:49 -0400 Subject: [PATCH 12/28] chore: add unadded import --- at_client/src/main/java/org/atsign/client/api/AtClient.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/at_client/src/main/java/org/atsign/client/api/AtClient.java b/at_client/src/main/java/org/atsign/client/api/AtClient.java index fb89b7b2..80e4c10c 100644 --- a/at_client/src/main/java/org/atsign/client/api/AtClient.java +++ b/at_client/src/main/java/org/atsign/client/api/AtClient.java @@ -5,6 +5,7 @@ import org.atsign.client.api.impl.connections.DefaultAtConnectionFactory; import org.atsign.client.api.impl.events.SimpleAtEventBus; import org.atsign.client.api.impl.secondaries.RemoteSecondary; +import org.atsign.client.api.notification.NotificationService; import org.atsign.common.AtSign; import org.atsign.common.AtException; import org.atsign.client.util.KeysUtil; @@ -161,4 +162,6 @@ static AtClient withRemoteSecondary(Secondary.Address remoteSecondaryAddress, At CompletableFuture put(PublicKey publicKey, byte[] value); CompletableFuture> getAtKeys(String regex); + + NotificationService getNotificationService(); } From c248ed2a7a3790e1c0bb2d18f89ca2db9516de97 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Tue, 1 Nov 2022 13:43:37 -0400 Subject: [PATCH 13/28] feat: add NotificationServiceImpl instance to AtClientImpl --- .../org/atsign/client/api/impl/clients/AtClientImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/at_client/src/main/java/org/atsign/client/api/impl/clients/AtClientImpl.java b/at_client/src/main/java/org/atsign/client/api/impl/clients/AtClientImpl.java index 93ac95db..32557c78 100644 --- a/at_client/src/main/java/org/atsign/client/api/impl/clients/AtClientImpl.java +++ b/at_client/src/main/java/org/atsign/client/api/impl/clients/AtClientImpl.java @@ -25,6 +25,7 @@ import org.atsign.client.api.AtEvents.AtEventBus; import org.atsign.client.api.AtEvents.AtEventListener; import org.atsign.client.api.AtEvents.AtEventType; +import org.atsign.client.api.notification.NotificationService; import org.atsign.client.api.Secondary; import org.atsign.client.util.EncryptionUtil; import org.atsign.client.util.KeysUtil; @@ -57,16 +58,20 @@ public class AtClientImpl implements AtClient { private final Map keys; @Override public Map getEncryptionKeys() {return keys;} + private final Secondary secondary; @Override public Secondary getSecondary() {return secondary;} + private final NotificationService notificationService; + @Override public NotificationService getNotificationService() {return notificationService;} + private final AtEventBus eventBus; public AtClientImpl(AtEventBus eventBus, AtSign atSign, Map keys, Secondary secondary) { this.eventBus = eventBus; this.atSign = atSign; this.keys = keys; this.secondary = secondary; - + this.notificationService = NotificationService.create(this); eventBus.addEventListener(this, EnumSet.allOf(AtEventType.class)); } From 75ea8e23271c9a28417d29a82b9c643df5ef177a Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Wed, 2 Nov 2022 16:26:21 -0400 Subject: [PATCH 14/28] feat: notify() and notifyList() --- .../impl/clients/NotificationServiceImpl.java | 132 ++++++++++++++++-- .../api/notification/NotificationParams.java | 6 +- .../api/notification/NotificationService.java | 1 - .../org/atsign/common/NotificationEnums.java | 2 +- .../java/org/atsign/common/VerbBuilders.java | 41 ++++-- 5 files changed, 153 insertions(+), 29 deletions(-) diff --git a/at_client/src/main/java/org/atsign/client/api/impl/clients/NotificationServiceImpl.java b/at_client/src/main/java/org/atsign/client/api/impl/clients/NotificationServiceImpl.java index 7d9d6f6e..c39884bb 100644 --- a/at_client/src/main/java/org/atsign/client/api/impl/clients/NotificationServiceImpl.java +++ b/at_client/src/main/java/org/atsign/client/api/impl/clients/NotificationServiceImpl.java @@ -1,65 +1,169 @@ package org.atsign.client.api.impl.clients; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; - +import java.util.concurrent.CompletionException; import org.atsign.client.api.AtClient; +import org.atsign.client.api.Secondary; import org.atsign.client.api.notification.AtNotification; import org.atsign.client.api.notification.NotificationParams; import org.atsign.client.api.notification.NotificationResult; import org.atsign.client.api.notification.NotificationService; import org.atsign.client.util.AtClientValidation; +import org.atsign.common.AtException; import org.atsign.common.AtSign; +import org.atsign.common.NotificationEnums; import org.atsign.common.NotificationStatus; +import org.atsign.common.ResponseTransformers.NotifyListResponseTransformer; +import org.atsign.common.VerbBuilders.NotifyDeleteVerbBuilder; +import org.atsign.common.VerbBuilders.NotifyKeyChangeBuilder; +import org.atsign.common.VerbBuilders.NotifyListVerbBuilder; +import org.atsign.common.VerbBuilders.NotifyTextVerbBuilder; +import org.atsign.common.VerbBuilders.VerbBuilder; +import org.atsign.common.response_models.NotifyListResponse; public class NotificationServiceImpl implements NotificationService { - + private final AtClient atClient; - + public NotificationServiceImpl(AtClient atClient) { this.atClient = atClient; } @Override public CompletableFuture subscribe(String regex, boolean shouldDecrypt) { - throw new IllegalCallerException("Not implemented yet"); + throw new RuntimeException("Not implemented yet"); } @Override public CompletableFuture notify(NotificationParams params) { - return CompletableFuture.supplyAsync(() -> { - NotificationResult result = new NotificationResult(params.getNotificationId(), params.getAtKey(), NotificationStatus.undelivered); - if(params.getAtKey().sharedBy == null) { + NotificationResult result = new NotificationResult(params.getNotificationId(), params.getAtKey(), + NotificationStatus.undelivered); + if (params.getAtKey().sharedBy == null) { params.getAtKey().sharedBy = atClient.getAtSign(); } - + + // TODO: AtClientValidation notification + + VerbBuilder builder; + switch (params.getMessageType()) { + case TEXT: + NotifyTextVerbBuilder a = new NotifyTextVerbBuilder(); + a.setRecipientAtSign(params.getAtKey().sharedWith.atSign); + a.setText(params.getAtKey().name); + builder = a; + break; + case KEY: + NotifyKeyChangeBuilder b = new NotifyKeyChangeBuilder(); + if (params.getOperation().toString().equalsIgnoreCase("update") + || params.getOperation().toString().equalsIgnoreCase("delete")) { + b.setOperation(params.getOperation().toString().toLowerCase()); + } + b.setRecipientAtSign(params.getAtKey().sharedWith.atSign); + b.setKey(params.getAtKey().toString()); + b.setSenderAtSign(params.getAtKey().sharedBy.atSign); + b.setValue(params.getValue()); + b.setTtr(params.getAtKey().metadata.ttr); + builder = b; + break; + default: + throw new IllegalArgumentException("Invalid message type"); + } + + String command = builder.build(); + try { + Secondary.Response response = (atClient).executeCommand(command, false); + if (response.data != "null") { + result.setNotificationStatus(NotificationStatus.delivered); + } + } catch (AtException e) { + result.setNotificationStatus(NotificationStatus.errored); + } return result; }); - - } @Override public CompletableFuture getStatus(String notificationId) { - throw new IllegalCallerException("Not implemented yet"); + throw new RuntimeException("Not implemented yet"); } @Override public CompletableFuture> notifyList() { - throw new IllegalCallerException("Not implemented yet"); + return this.notifyList(null); + } + + public CompletableFuture> notifyList(String regex) { + return this.notifyList(null, null); + } + + public CompletableFuture> notifyList(String regex, String fromDate) { + return this.notifyList(regex, fromDate, null); + } + + /** + * Get the list of notifications (notify:list verb) + * + * @param regex - regex to filter the notifications, nullable + * @param fromDate - from date to filter the notifications yyyy-MM-dd (e.g. + * "2021-01-01"), nullable + * @param toDate - to date to filter the notifications yyyy-MM-dd (e.g. + * "2021-01-01"), nullable + * @return non-null List object + */ + public CompletableFuture> notifyList(String regex, String fromDate, String toDate) { + return CompletableFuture.supplyAsync(() -> { + try { + + List notifications = new ArrayList(); + + NotifyListVerbBuilder b = new NotifyListVerbBuilder(); + b.setRegex(regex); + b.setFrom(fromDate); + b.setTo(toDate); + String command = b.build(); + + Secondary.Response response = (atClient).executeCommand(command, false); + + notifications = AtNotification.fromResponse(response); + + return notifications; + } catch (AtException e) { + throw new CompletionException(e); + } + + }); } @Override public CompletableFuture notifyDelete(String notificationId) { - throw new IllegalCallerException("Not implemented yet"); + + return CompletableFuture.supplyAsync(() -> { + try { + NotifyDeleteVerbBuilder b = new NotifyDeleteVerbBuilder(); + b.setNotificationId(notificationId); + String command = b.build(); + NotificationResult result = new NotificationResult(notificationId, null, + NotificationStatus.undelivered); + + Secondary.Response response = (atClient).executeCommand(command, false); + if (response.data != "null") { + result.setNotificationStatus(NotificationStatus.delivered); + } + return result; + } catch (AtException e) { + throw new CompletionException(e); + } + }); } @Override public CompletableFuture notifyDeleteAll() { - throw new IllegalCallerException("Not implemented yet"); + throw new RuntimeException("Not implemented yet"); } } diff --git a/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java index c69ef34c..2601ce0b 100644 --- a/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java @@ -4,6 +4,7 @@ import org.atsign.common.AtSign; import org.atsign.common.KeyBuilders; +import org.atsign.common.Keys; import org.atsign.common.NotificationEnums; import org.atsign.common.Keys.AtKey; import org.atsign.common.Keys.SharedKey; @@ -79,10 +80,9 @@ public static NotificationParams forDelete(AtKey atKey) { return params; } - public static NotificationParams forText(String text, String whomToNotify, Boolean shouldEncrypt) { + public static NotificationParams forText(String text, AtSign sharedBy, AtSign sharedWith, Boolean shouldEncrypt) { - SharedKey sharedKey = new KeyBuilders.SharedKeyBuilder( - new AtSign(""), new AtSign(whomToNotify)).build(); + SharedKey sharedKey = new KeyBuilders.SharedKeyBuilder(sharedBy, sharedWith).key(text).build(); sharedKey.metadata.isEncrypted = shouldEncrypt; diff --git a/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java index 6a7dd2bb..33fbc7f4 100644 --- a/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java @@ -4,7 +4,6 @@ import java.util.concurrent.CompletableFuture; import org.atsign.client.api.AtClient; -import org.atsign.client.api.AtEvents; import org.atsign.client.api.impl.clients.NotificationServiceImpl; public interface NotificationService { diff --git a/at_client/src/main/java/org/atsign/common/NotificationEnums.java b/at_client/src/main/java/org/atsign/common/NotificationEnums.java index f46d6019..c9055b92 100644 --- a/at_client/src/main/java/org/atsign/common/NotificationEnums.java +++ b/at_client/src/main/java/org/atsign/common/NotificationEnums.java @@ -32,7 +32,7 @@ public enum MessageType { KEY, TEXT,; public String toString() { - return "MessageType." + this.name().toLowerCase(); + return "MessageType." + this.name().toLowerCase(); // MessageType.text or MessageType.key } } } diff --git a/at_client/src/main/java/org/atsign/common/VerbBuilders.java b/at_client/src/main/java/org/atsign/common/VerbBuilders.java index 8aeb97e6..14d412c1 100644 --- a/at_client/src/main/java/org/atsign/common/VerbBuilders.java +++ b/at_client/src/main/java/org/atsign/common/VerbBuilders.java @@ -1,10 +1,13 @@ package org.atsign.common; +import java.util.UUID; + import org.apache.commons.lang3.StringUtils; import org.atsign.common.Keys.AtKey; import org.atsign.common.Keys.Metadata; import org.atsign.common.Keys.PublicKey; import org.atsign.common.Keys.SharedKey; +import org.atsign.common.NotificationEnums.MessageType; /** * @@ -659,7 +662,6 @@ public String build() { throw new IllegalArgumentException("key cannot be null or empty"); } - if(!operation.equals("update") && !operation.equals("delete")) { throw new IllegalArgumentException("Only 'update' and 'delete' are allowed for operation"); } @@ -709,7 +711,7 @@ public String build() { } } - public static class NotificationStatusVerbBuilder implements VerbBuilder { + public static class NotifyStatusVerbBuilder implements VerbBuilder { private String notificationId; @@ -732,29 +734,29 @@ public static class NotifyListVerbBuilder implements VerbBuilder { // get a list of notification json objects by running `notify:list` - private String regex; // optional - private Long from; // optional (epochMillis notification created) - private Long to; // optional (epochMillis notification created) + private String regex; // optional regex to filter the list of notifications + private String from; // optional (epochMillis to yyyy-MM-dd format) e.g. "2019-01-01" + private String to; // optional (epochMillis to yyyy-MM-dd format) e.g. "2019-01-01" public void setRegex(String regex) { this.regex = regex; } - public void setFrom(Long from) { + public void setFrom(String from) { this.from = from; } - public void setTo(Long to) { + public void setTo(String to) { this.to = to; } @Override public String build() { String b = "notify:list"; - if(from != null) { + if(from != null && to == null) { // case 1: only from + b += ":" + from; + } else if(from != null && to != null) { // case 2: from and to b += ":" + from; - } - if(to != null) { b += ":" + to; } if (regex != null) { @@ -765,4 +767,23 @@ public String build() { } + public static class NotifyDeleteVerbBuilder implements VerbBuilder { + + private String notificationId; // mandatory + + public void setNotificationId(String notificationId) { + this.notificationId = notificationId; + } + + //notify:delete:(?\S+)$'; + public String build() { + + if(notificationId == null || StringUtils.isBlank(notificationId)) { + throw new IllegalArgumentException("notificationId cannot be null or empty"); + } + + return "notify:delete:" + notificationId; + } + } + } From fc311da32bf59c06ea33cfc978b5125402d97abd Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 15 Dec 2022 17:28:12 -0500 Subject: [PATCH 15/28] fix: invalid class name `NotifyStatusVerbBuilder` --- .../java/org/atsign/common/VerbBuildersTest.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java b/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java index ec5586b4..98b57b47 100644 --- a/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java +++ b/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java @@ -11,8 +11,8 @@ import org.atsign.common.VerbBuilders.FromVerbBuilder; import org.atsign.common.VerbBuilders.LlookupVerbBuilder; import org.atsign.common.VerbBuilders.LookupVerbBuilder; -import org.atsign.common.VerbBuilders.NotificationStatusVerbBuilder; import org.atsign.common.VerbBuilders.NotifyKeyChangeBuilder; +import org.atsign.common.VerbBuilders.NotifyStatusVerbBuilder; import org.atsign.common.VerbBuilders.NotifyTextVerbBuilder; import org.atsign.common.VerbBuilders.PKAMVerbBuilder; import org.atsign.common.VerbBuilders.POLVerbBuilder; @@ -672,21 +672,19 @@ public void notifyKeyChangeBuilderTest() { @Test public void notificationStatusVerbBuilderTest() { - // Test not setting any parameters assertThrows("Mandatory fields are not set. Expecting a IllegalArgumentException being thrown.", IllegalArgumentException.class, () -> { - final NotificationStatusVerbBuilder notificationStatusVerbBuilder = new NotificationStatusVerbBuilder(); - // Expect build to throw Illegal argument exception for not setting mandatory - // parameters + final NotifyStatusVerbBuilder notificationStatusVerbBuilder = new NotifyStatusVerbBuilder(); + // Expect build to throw Illegal argument exception for not setting mandatory parameters notificationStatusVerbBuilder.build(); }); - final NotificationStatusVerbBuilder notificationStatusVerbBuilder = new NotificationStatusVerbBuilder(); + // Test with notification id parameter + final NotifyStatusVerbBuilder notificationStatusVerbBuilder = new NotifyStatusVerbBuilder(); notificationStatusVerbBuilder.setNotificationId("n1234"); String expectedResult = "notify:status:n1234"; assertEquals(expectedResult, notificationStatusVerbBuilder.build()); - } @After From 558bfdea38ec351cce1e63e369f01a738a2c8569 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 15 Dec 2022 17:28:28 -0500 Subject: [PATCH 16/28] feat: protect NotificationParams to force use of static factory methods --- .../atsign/client/api/notification/NotificationParams.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java index 2601ce0b..74c38fbd 100644 --- a/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java @@ -23,6 +23,10 @@ public class NotificationParams { // private Integer latestN = 1; // private String notifier = SYSTEM; + // only this class, children, and other classes in this package can create instances + // use static factory methods to create instances of NotificationParams. + protected NotificationParams() {} + public String getNotificationId() { return id; } From fcc57a34669990b0e75f40ae9faac7a84a1d451f Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 15 Dec 2022 19:20:52 -0500 Subject: [PATCH 17/28] feat: move `NotificationServiceImpl.java`, implement overloaded `notifyList()` methods, `NotificationService.java` interface javadocs --- .../api/notification/NotificationService.java | 58 +++++++++++- .../NotificationServiceImpl.java | 94 +++++++++++-------- 2 files changed, 112 insertions(+), 40 deletions(-) rename at_client/src/main/java/org/atsign/client/api/{impl/clients => notification}/NotificationServiceImpl.java (70%) diff --git a/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java index 33fbc7f4..ed3afef4 100644 --- a/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java @@ -1,10 +1,10 @@ package org.atsign.client.api.notification; +import java.util.Date; import java.util.List; import java.util.concurrent.CompletableFuture; import org.atsign.client.api.AtClient; -import org.atsign.client.api.impl.clients.NotificationServiceImpl; public interface NotificationService { @@ -12,11 +12,67 @@ public static NotificationService create(AtClient atClient) { return new NotificationServiceImpl(atClient); } + /** + * TODO Javadoc + * @param regex + * @param shouldDecrypt + * @return + */ CompletableFuture subscribe(String regex, boolean shouldDecrypt); // TODO Replace "Object" with Stream or similar. See: https://github.com/atsign-foundation/at_client_sdk/blob/99707459494594fbdc3f0873769eeca29c6a3124/packages/at_client/lib/src/service/notification_service.dart#L14 + + /** + * Sends a notification (via the `notify:` verb) + * Use NotificationParams static factory methods to create an instance. + * @param params parameters containing details about the Notification that will be sent. + * @return NotificationResult object containing details about the notification that was sent. + */ CompletableFuture notify(NotificationParams params); + + /** + * Gets the status notification (via the `notify:status:` verb) + * @param notificationId id of the notificaiton (e.g. `4a8bbbe9-3c7c-4679-9721-dfd01b5adf3f`) + * @return NotificationResult object containing details about the status of the notification. + */ CompletableFuture getStatus(String notificationId); + + /** + * Returns a List object containing all the notifications received by the currently onboarded atSign. + * Each AtNotification object represents a notification in the notification store. + * @return List object, non-null, emptyable + */ CompletableFuture> notifyList(); + + /** + * Returns a List object containing all the notifications received by the currently onboarded atSign. + * Each AtNotification object represents a notification in the notification store. + * @param regex regex filter when searching through notifications. Filters what is inside "key": + * @return List object, non-null, emptyable + */ + CompletableFuture> notifyList(String regex); + + /** + * Returns a List object containing all the notifications received by the currently onboarded atSign. + * Each AtNotification object represents a notification in the notification store. + * Filter notifications by regex and between a date range. + * fromDate must be before toDate. + * @param regex regex filter when searching through notifications. Filters what is inside "key": + * @param fromDate Date object, only year, month, and day are considered + * @param toDate Date object, only year, month, and day are considered + * @return List object, non-null, emptyable + */ + CompletableFuture> notifyList(String regex, Date fromDate, Date toDate); + + /** + * Deletes a notification given a String notificationId + * @param notificationId id representing a notification inside the atSign's atServer notification store + * @return NotificationResult object detailing the result of running this method + */ CompletableFuture notifyDelete(String notificationId); + + /** + * Deletes all notiifcation in the atServer's notification store + * @return NotificationResult object detailing the result of running this method + */ CompletableFuture notifyDeleteAll(); } diff --git a/at_client/src/main/java/org/atsign/client/api/impl/clients/NotificationServiceImpl.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationServiceImpl.java similarity index 70% rename from at_client/src/main/java/org/atsign/client/api/impl/clients/NotificationServiceImpl.java rename to at_client/src/main/java/org/atsign/client/api/notification/NotificationServiceImpl.java index c39884bb..ca442d58 100644 --- a/at_client/src/main/java/org/atsign/client/api/impl/clients/NotificationServiceImpl.java +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationServiceImpl.java @@ -1,16 +1,13 @@ -package org.atsign.client.api.impl.clients; +package org.atsign.client.api.notification; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import org.atsign.client.api.AtClient; import org.atsign.client.api.Secondary; -import org.atsign.client.api.notification.AtNotification; -import org.atsign.client.api.notification.NotificationParams; -import org.atsign.client.api.notification.NotificationResult; -import org.atsign.client.api.notification.NotificationService; import org.atsign.client.util.AtClientValidation; import org.atsign.common.AtException; import org.atsign.common.AtSign; @@ -43,6 +40,7 @@ public CompletableFuture notify(NotificationParams params) { return CompletableFuture.supplyAsync(() -> { NotificationResult result = new NotificationResult(params.getNotificationId(), params.getAtKey(), NotificationStatus.undelivered); + if (params.getAtKey().sharedBy == null) { params.getAtKey().sharedBy = atClient.getAtSign(); } @@ -77,6 +75,7 @@ public CompletableFuture notify(NotificationParams params) { String command = builder.build(); try { Secondary.Response response = (atClient).executeCommand(command, false); + // System.out.println("Executed command: " + command + " got: " + response.toString()); if (response.data != "null") { result.setNotificationStatus(NotificationStatus.delivered); } @@ -92,17 +91,24 @@ public CompletableFuture getStatus(String notificationId) { throw new RuntimeException("Not implemented yet"); } + /** + * Get the list of notifications (notify:list verb) + * + * @return non-null List object, emptyable + */ @Override public CompletableFuture> notifyList() { return this.notifyList(null); } + /** + * Get the list of notifications (notify:list verb) + * + * @param regex - regex to filter the notifications, nullable + * @return non-null List object, emptyable + */ public CompletableFuture> notifyList(String regex) { - return this.notifyList(null, null); - } - - public CompletableFuture> notifyList(String regex, String fromDate) { - return this.notifyList(regex, fromDate, null); + return this.notifyList(regex, null, null); } /** @@ -113,48 +119,24 @@ public CompletableFuture> notifyList(String regex, String f * "2021-01-01"), nullable * @param toDate - to date to filter the notifications yyyy-MM-dd (e.g. * "2021-01-01"), nullable - * @return non-null List object + * @return non-null List object, emptyable */ - public CompletableFuture> notifyList(String regex, String fromDate, String toDate) { + public CompletableFuture> notifyList(String regex, Date fromDate, Date toDate) { return CompletableFuture.supplyAsync(() -> { try { - - List notifications = new ArrayList(); - - NotifyListVerbBuilder b = new NotifyListVerbBuilder(); - b.setRegex(regex); - b.setFrom(fromDate); - b.setTo(toDate); - String command = b.build(); - - Secondary.Response response = (atClient).executeCommand(command, false); - - notifications = AtNotification.fromResponse(response); - - return notifications; + List atNotifications = getNotifications(regex, fromDate, toDate); + return atNotifications; } catch (AtException e) { throw new CompletionException(e); } - }); } @Override public CompletableFuture notifyDelete(String notificationId) { - return CompletableFuture.supplyAsync(() -> { try { - NotifyDeleteVerbBuilder b = new NotifyDeleteVerbBuilder(); - b.setNotificationId(notificationId); - String command = b.build(); - NotificationResult result = new NotificationResult(notificationId, null, - NotificationStatus.undelivered); - - Secondary.Response response = (atClient).executeCommand(command, false); - if (response.data != "null") { - result.setNotificationStatus(NotificationStatus.delivered); - } - return result; + return deleteNotification(notificationId); } catch (AtException e) { throw new CompletionException(e); } @@ -166,4 +148,38 @@ public CompletableFuture notifyDeleteAll() { throw new RuntimeException("Not implemented yet"); } + /// ****************************** + /// Private Methods + /// ****************************** + + private NotificationResult deleteNotification(String notificationId) throws AtException { + NotifyDeleteVerbBuilder b = new NotifyDeleteVerbBuilder(); + b.setNotificationId(notificationId); + String command = b.build(); + NotificationResult result = new NotificationResult(notificationId, null, + NotificationStatus.undelivered); + + Secondary.Response response = (atClient).executeCommand(command, false); + if (response.data != "null") { + result.setNotificationStatus(NotificationStatus.delivered); + } + return result; + } + + private List getNotifications(String regex, Date fromDate, Date toDate) throws AtException { + List notifications = new ArrayList(); + + NotifyListVerbBuilder b = new NotifyListVerbBuilder(); + b.setRegex(regex); + b.setFrom(fromDate); + b.setTo(toDate); + String command = b.build(); + + Secondary.Response response = (atClient).executeCommand(command, false); + + notifications = AtNotification.fromResponse(response); + + return notifications; + } + } From b81668874c3b24fc5818dafb367fd167c6f651ad Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 15 Dec 2022 19:21:51 -0500 Subject: [PATCH 18/28] chore: `NotifyListVerbBuilder` Str>Date objects arg --- .../java/org/atsign/common/VerbBuilders.java | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/at_client/src/main/java/org/atsign/common/VerbBuilders.java b/at_client/src/main/java/org/atsign/common/VerbBuilders.java index 14d412c1..014d47aa 100644 --- a/at_client/src/main/java/org/atsign/common/VerbBuilders.java +++ b/at_client/src/main/java/org/atsign/common/VerbBuilders.java @@ -1,5 +1,10 @@ package org.atsign.common; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.util.Date; import java.util.UUID; import org.apache.commons.lang3.StringUtils; @@ -734,30 +739,35 @@ public static class NotifyListVerbBuilder implements VerbBuilder { // get a list of notification json objects by running `notify:list` + private static SimpleDateFormat formatter; + + static { + formatter = new SimpleDateFormat("yyyy-MM-dd"); + } + private String regex; // optional regex to filter the list of notifications - private String from; // optional (epochMillis to yyyy-MM-dd format) e.g. "2019-01-01" - private String to; // optional (epochMillis to yyyy-MM-dd format) e.g. "2019-01-01" + private Date from; // optional (yyyy-MM-dd format) e.g. "2019-01-01" + private Date to; // optional (yyyy-MM-dd format) e.g. "2019-01-01" public void setRegex(String regex) { this.regex = regex; } - public void setFrom(String from) { + public void setFrom(Date from) { this.from = from; } - public void setTo(String to) { + public void setTo(Date to) { this.to = to; } @Override public String build() { String b = "notify:list"; - if(from != null && to == null) { // case 1: only from - b += ":" + from; - } else if(from != null && to != null) { // case 2: from and to - b += ":" + from; - b += ":" + to; + if(from != null && to != null) { + // TODO check if from < to. from < to is valid. + b += ":" + formatter.format(from); + b += ":" + formatter.format(to); } if (regex != null) { b += ":" + regex; From c8126ca7d1a2ce1359facd14ec8723b9af53e0d8 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 15 Dec 2022 19:22:08 -0500 Subject: [PATCH 19/28] chore: use primitive boolean to omit null case --- .../org/atsign/client/api/notification/NotificationParams.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java index 74c38fbd..3ce7630f 100644 --- a/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationParams.java @@ -84,7 +84,7 @@ public static NotificationParams forDelete(AtKey atKey) { return params; } - public static NotificationParams forText(String text, AtSign sharedBy, AtSign sharedWith, Boolean shouldEncrypt) { + public static NotificationParams forText(String text, AtSign sharedBy, AtSign sharedWith, boolean shouldEncrypt) { SharedKey sharedKey = new KeyBuilders.SharedKeyBuilder(sharedBy, sharedWith).key(text).build(); sharedKey.metadata.isEncrypted = shouldEncrypt; From f750f45e134a22641773722f573659d4231f7406 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 15 Dec 2022 19:27:02 -0500 Subject: [PATCH 20/28] feat: invalid from > to check --- at_client/src/main/java/org/atsign/common/VerbBuilders.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/at_client/src/main/java/org/atsign/common/VerbBuilders.java b/at_client/src/main/java/org/atsign/common/VerbBuilders.java index 014d47aa..5b7ddc32 100644 --- a/at_client/src/main/java/org/atsign/common/VerbBuilders.java +++ b/at_client/src/main/java/org/atsign/common/VerbBuilders.java @@ -765,7 +765,9 @@ public void setTo(Date to) { public String build() { String b = "notify:list"; if(from != null && to != null) { - // TODO check if from < to. from < to is valid. + if(from.toInstant().toEpochMilli() > to.toInstant().toEpochMilli()) { + throw new IllegalArgumentException("from date cannot be greater than to date"); + } b += ":" + formatter.format(from); b += ":" + formatter.format(to); } From 6ff72e52d29a3acd8c088ddb2095dcafd2932812 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 15 Dec 2022 21:19:21 -0500 Subject: [PATCH 21/28] test: notifyList and notifyDelete verb builder tests --- .../java/org/atsign/common/VerbBuilders.java | 2 +- .../org/atsign/common/VerbBuildersTest.java | 50 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/at_client/src/main/java/org/atsign/common/VerbBuilders.java b/at_client/src/main/java/org/atsign/common/VerbBuilders.java index 5b7ddc32..e9628c9a 100644 --- a/at_client/src/main/java/org/atsign/common/VerbBuilders.java +++ b/at_client/src/main/java/org/atsign/common/VerbBuilders.java @@ -781,7 +781,7 @@ public String build() { public static class NotifyDeleteVerbBuilder implements VerbBuilder { - private String notificationId; // mandatory + private String notificationId; // mandatory, the id of the notification to delete public void setNotificationId(String notificationId) { this.notificationId = notificationId; diff --git a/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java b/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java index 98b57b47..0ab439ac 100644 --- a/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java +++ b/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java @@ -3,6 +3,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; +import java.util.Calendar; +import java.util.Date; + import org.atsign.common.Keys.PublicKey; import org.atsign.common.Keys.SelfKey; import org.atsign.common.Keys.SharedKey; @@ -11,7 +14,9 @@ import org.atsign.common.VerbBuilders.FromVerbBuilder; import org.atsign.common.VerbBuilders.LlookupVerbBuilder; import org.atsign.common.VerbBuilders.LookupVerbBuilder; +import org.atsign.common.VerbBuilders.NotifyDeleteVerbBuilder; import org.atsign.common.VerbBuilders.NotifyKeyChangeBuilder; +import org.atsign.common.VerbBuilders.NotifyListVerbBuilder; import org.atsign.common.VerbBuilders.NotifyStatusVerbBuilder; import org.atsign.common.VerbBuilders.NotifyTextVerbBuilder; import org.atsign.common.VerbBuilders.PKAMVerbBuilder; @@ -671,7 +676,7 @@ public void notifyKeyChangeBuilderTest() { } @Test - public void notificationStatusVerbBuilderTest() { + public void notifyStatusVerbBuilderTest() { // Test not setting any parameters assertThrows("Mandatory fields are not set. Expecting a IllegalArgumentException being thrown.", IllegalArgumentException.class, () -> { @@ -687,6 +692,49 @@ public void notificationStatusVerbBuilderTest() { assertEquals(expectedResult, notificationStatusVerbBuilder.build()); } + @Test + public void notifyListVerbBuilderTest() { + // test with no argument + NotifyListVerbBuilder notificationListVerbBuilder = new NotifyListVerbBuilder(); + assertEquals("notify:list", notificationListVerbBuilder.build()); + + // Test with regex + notificationListVerbBuilder = new NotifyListVerbBuilder(); + notificationListVerbBuilder.setRegex(".*"); + assertEquals("notify:list:.*", notificationListVerbBuilder.build()); + + // Test with fromDate and toDate + notificationListVerbBuilder = new NotifyListVerbBuilder(); + Date fromDate = new Calendar.Builder().setDate(2020, Calendar.JANUARY, 1).build().getTime(); + Date toDate = new Calendar.Builder().setDate(2020, Calendar.JANUARY, 2).build().getTime(); + notificationListVerbBuilder.setFrom(fromDate); + notificationListVerbBuilder.setTo(toDate); + assertEquals("notify:list:2020-01-01:2020-01-02", notificationListVerbBuilder.build()); + + // test with regex, fromDate, and toDate + notificationListVerbBuilder = new NotifyListVerbBuilder(); + notificationListVerbBuilder.setFrom(fromDate); + notificationListVerbBuilder.setTo(toDate); + notificationListVerbBuilder.setRegex(".*"); + assertEquals("notify:list:2020-01-01:2020-01-02:.*", notificationListVerbBuilder.build()); + } + + @Test + public void notifyDeleteVerbBuilderTest() { + // test with no argument + assertThrows("Mandatory fields are not set. Expecting a IllegalArgumentException being thrown.", + IllegalArgumentException.class, () -> { + NotifyDeleteVerbBuilder notificationDeleteVerbBuilder = new NotifyDeleteVerbBuilder(); + // Expect build to throw Illegal argument exception for not setting mandatory parameters + notificationDeleteVerbBuilder.build(); + }); + + // Test with notification id argument + NotifyDeleteVerbBuilder notificationDeleteVerbBuilder = new NotifyDeleteVerbBuilder(); + notificationDeleteVerbBuilder.setNotificationId("n1234"); + assertEquals("notify:delete:n1234", notificationDeleteVerbBuilder.build()); + } + @After public void tearDown() { } From 36370e1f6f236223979edc9d465401f78c0007da Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 15 Dec 2022 21:52:56 -0500 Subject: [PATCH 22/28] chore: use `this` keyword --- .../client/api/notification/NotificationResult.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/at_client/src/main/java/org/atsign/client/api/notification/NotificationResult.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationResult.java index 232f89aa..e29418e4 100644 --- a/at_client/src/main/java/org/atsign/client/api/notification/NotificationResult.java +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationResult.java @@ -27,20 +27,20 @@ public void setNotificationStatus(NotificationStatus notificationStatus) { } public AtKey getAtKey() { - return atKey; + return this.atKey; } public String getNotificationId() { - return notificationId; + return this.notificationId; } public NotificationStatus getNotificationStatus() { - return notificationStatus; + return this.notificationStatus; } @Override public String toString() { - return "id: " + notificationId + " | status: " + notificationStatus.name(); + return "id: " + this.notificationId + " | status: " + this.notificationStatus.name(); } } From d743cacadc8649015341b71ad42418919536fbea Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 15 Dec 2022 21:53:19 -0500 Subject: [PATCH 23/28] refactor: use "remove" wording instead of "delete" --- .../client/api/notification/NotificationService.java | 8 ++++---- .../src/main/java/org/atsign/common/VerbBuilders.java | 4 ++-- .../src/test/java/org/atsign/common/VerbBuildersTest.java | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java index ed3afef4..28abe5f4 100644 --- a/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationService.java @@ -63,16 +63,16 @@ public static NotificationService create(AtClient atClient) { CompletableFuture> notifyList(String regex, Date fromDate, Date toDate); /** - * Deletes a notification given a String notificationId + * Removes a notification given a String notificationId * @param notificationId id representing a notification inside the atSign's atServer notification store * @return NotificationResult object detailing the result of running this method */ - CompletableFuture notifyDelete(String notificationId); + CompletableFuture notifyRemove(String notificationId); /** - * Deletes all notiifcation in the atServer's notification store + * Removes all notifications in the atServer's notification store * @return NotificationResult object detailing the result of running this method */ - CompletableFuture notifyDeleteAll(); + CompletableFuture notifyRemoveAll(); } diff --git a/at_client/src/main/java/org/atsign/common/VerbBuilders.java b/at_client/src/main/java/org/atsign/common/VerbBuilders.java index e9628c9a..888c6468 100644 --- a/at_client/src/main/java/org/atsign/common/VerbBuilders.java +++ b/at_client/src/main/java/org/atsign/common/VerbBuilders.java @@ -779,7 +779,7 @@ public String build() { } - public static class NotifyDeleteVerbBuilder implements VerbBuilder { + public static class NotifyRemoveVerbBuilder implements VerbBuilder { private String notificationId; // mandatory, the id of the notification to delete @@ -794,7 +794,7 @@ public String build() { throw new IllegalArgumentException("notificationId cannot be null or empty"); } - return "notify:delete:" + notificationId; + return "notify:remove:" + notificationId; } } diff --git a/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java b/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java index 0ab439ac..f9da9559 100644 --- a/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java +++ b/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java @@ -14,7 +14,7 @@ import org.atsign.common.VerbBuilders.FromVerbBuilder; import org.atsign.common.VerbBuilders.LlookupVerbBuilder; import org.atsign.common.VerbBuilders.LookupVerbBuilder; -import org.atsign.common.VerbBuilders.NotifyDeleteVerbBuilder; +import org.atsign.common.VerbBuilders.NotifyRemoveVerbBuilder; import org.atsign.common.VerbBuilders.NotifyKeyChangeBuilder; import org.atsign.common.VerbBuilders.NotifyListVerbBuilder; import org.atsign.common.VerbBuilders.NotifyStatusVerbBuilder; @@ -724,13 +724,13 @@ public void notifyDeleteVerbBuilderTest() { // test with no argument assertThrows("Mandatory fields are not set. Expecting a IllegalArgumentException being thrown.", IllegalArgumentException.class, () -> { - NotifyDeleteVerbBuilder notificationDeleteVerbBuilder = new NotifyDeleteVerbBuilder(); + NotifyRemoveVerbBuilder notificationDeleteVerbBuilder = new NotifyRemoveVerbBuilder(); // Expect build to throw Illegal argument exception for not setting mandatory parameters notificationDeleteVerbBuilder.build(); }); // Test with notification id argument - NotifyDeleteVerbBuilder notificationDeleteVerbBuilder = new NotifyDeleteVerbBuilder(); + NotifyRemoveVerbBuilder notificationDeleteVerbBuilder = new NotifyRemoveVerbBuilder(); notificationDeleteVerbBuilder.setNotificationId("n1234"); assertEquals("notify:delete:n1234", notificationDeleteVerbBuilder.build()); } From 8422eea1dfdbb2af4479aea91ceee246cb5f8137 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 15 Dec 2022 21:53:36 -0500 Subject: [PATCH 24/28] refactor: use notifyRemove instead of notifyDelete --- .../notification/NotificationServiceImpl.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/at_client/src/main/java/org/atsign/client/api/notification/NotificationServiceImpl.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationServiceImpl.java index ca442d58..7a54dace 100644 --- a/at_client/src/main/java/org/atsign/client/api/notification/NotificationServiceImpl.java +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationServiceImpl.java @@ -14,7 +14,7 @@ import org.atsign.common.NotificationEnums; import org.atsign.common.NotificationStatus; import org.atsign.common.ResponseTransformers.NotifyListResponseTransformer; -import org.atsign.common.VerbBuilders.NotifyDeleteVerbBuilder; +import org.atsign.common.VerbBuilders.NotifyRemoveVerbBuilder; import org.atsign.common.VerbBuilders.NotifyKeyChangeBuilder; import org.atsign.common.VerbBuilders.NotifyListVerbBuilder; import org.atsign.common.VerbBuilders.NotifyTextVerbBuilder; @@ -132,8 +132,14 @@ public CompletableFuture> notifyList(String regex, Date fro }); } + /** + * Remove a notification from the notification store. + * Each notification has a notificationId. Obtain these ids from notifyList(...) + * @param notificationId - notificationId to remove from the notification store + * @return NotificationResult object detailing the status of the notification. The status of the notification will be `delivered` as long as there was a response from the server. A `delivered` status does not mean the notification existed in the first place. + */ @Override - public CompletableFuture notifyDelete(String notificationId) { + public CompletableFuture notifyRemove(String notificationId) { return CompletableFuture.supplyAsync(() -> { try { return deleteNotification(notificationId); @@ -144,7 +150,7 @@ public CompletableFuture notifyDelete(String notificationId) } @Override - public CompletableFuture notifyDeleteAll() { + public CompletableFuture notifyRemoveAll() { throw new RuntimeException("Not implemented yet"); } @@ -153,14 +159,16 @@ public CompletableFuture notifyDeleteAll() { /// ****************************** private NotificationResult deleteNotification(String notificationId) throws AtException { - NotifyDeleteVerbBuilder b = new NotifyDeleteVerbBuilder(); + NotifyRemoveVerbBuilder b = new NotifyRemoveVerbBuilder(); b.setNotificationId(notificationId); String command = b.build(); + + NotificationResult result = new NotificationResult(notificationId, null, NotificationStatus.undelivered); Secondary.Response response = (atClient).executeCommand(command, false); - if (response.data != "null") { + if (response.data.equals("success")) { result.setNotificationStatus(NotificationStatus.delivered); } return result; From d70cff6c9ae04f6cd610ac1db7d2cbf127fbfbc5 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Thu, 15 Dec 2022 21:54:42 -0500 Subject: [PATCH 25/28] feat: Notify examples --- at_client/src/examples/NotifyListExample.java | 31 ++++++++++++++++ .../src/examples/NotifyRemoveExample.java | 30 ++++++++++++++++ at_client/src/examples/NotifySendExample.java | 35 +++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 at_client/src/examples/NotifyListExample.java create mode 100644 at_client/src/examples/NotifyRemoveExample.java create mode 100644 at_client/src/examples/NotifySendExample.java diff --git a/at_client/src/examples/NotifyListExample.java b/at_client/src/examples/NotifyListExample.java new file mode 100644 index 00000000..aa63b866 --- /dev/null +++ b/at_client/src/examples/NotifyListExample.java @@ -0,0 +1,31 @@ + +import java.util.List; +import java.util.concurrent.ExecutionException; + +import org.atsign.client.api.AtClient; +import org.atsign.client.api.notification.AtNotification; +import org.atsign.client.api.notification.NotificationService; +import org.atsign.common.AtException; +import org.atsign.common.AtSign; + +public class NotifyListExample { + public static void main(String[] args) throws AtException, InterruptedException, ExecutionException { + final String ROOT_URL = "root.atsign.org:64"; + final String ATSIGN_STR = "@smoothalligator"; + final AtSign atSign = new AtSign(ATSIGN_STR); + final AtClient atClient = AtClient.withRemoteSecondary(ROOT_URL, atSign); + + final NotificationService ns = atClient.getNotificationService(); + final List notifications = ns.notifyList().get(); + + final int numNotifications = notifications.size(); + System.out.println("Notifications Listed: (" + numNotifications + ")"); + for(int i = 0; i < numNotifications; i++) { + AtNotification notification = notifications.get(i); + String id = notification.id; + String from = notification.from; + String key = notification.key; + System.out.println("[" + i + "]: " + id + " | from: " + from + " | key: " + key); + } + } +} diff --git a/at_client/src/examples/NotifyRemoveExample.java b/at_client/src/examples/NotifyRemoveExample.java new file mode 100644 index 00000000..273eb8ea --- /dev/null +++ b/at_client/src/examples/NotifyRemoveExample.java @@ -0,0 +1,30 @@ + +import java.util.Scanner; +import java.util.concurrent.ExecutionException; + + +import org.atsign.client.api.AtClient; +import org.atsign.client.api.notification.NotificationResult; +import org.atsign.client.api.notification.NotificationService; +import org.atsign.common.AtException; +import org.atsign.common.AtSign; + +public class NotifyRemoveExample { + + public static void main(String[] args) throws AtException, InterruptedException, ExecutionException { + final String ROOT_URL = "root.atsign.org:64"; + final String ATSIGN_STR = "@smoothalligator"; + final AtSign atSign = new AtSign(ATSIGN_STR); + final AtClient atClient = AtClient.withRemoteSecondary(ROOT_URL, atSign); + + final NotificationService ns = atClient.getNotificationService(); + + Scanner scanner = new Scanner(System.in); + System.out.println("Enter notification id to delete: "); + String notificationId = scanner.nextLine(); + scanner.close(); + NotificationResult result = ns.notifyRemove(notificationId).get(); + System.out.println(result.toString()); + } + +} diff --git a/at_client/src/examples/NotifySendExample.java b/at_client/src/examples/NotifySendExample.java new file mode 100644 index 00000000..6ae4e05b --- /dev/null +++ b/at_client/src/examples/NotifySendExample.java @@ -0,0 +1,35 @@ + +import java.util.concurrent.ExecutionException; + +import org.atsign.client.api.AtClient; +import org.atsign.client.api.notification.NotificationParams; +import org.atsign.client.api.notification.NotificationResult; +import org.atsign.client.api.notification.NotificationService; +import org.atsign.client.api.notification.NotificationServiceImpl; +import org.atsign.common.AtException; +import org.atsign.common.AtSign; + +public class NotifySendExample { + + public static void main(String[] args) throws AtException, InterruptedException, ExecutionException { + + // initialize AtClient instance + final String ROOT_URL = "root.atsign.org:64"; + final String ATSIGN_STR = "@soccer0"; + final AtSign atSign = new AtSign(ATSIGN_STR); + final AtClient atClient = AtClient.withRemoteSecondary(ROOT_URL, atSign); + + // get notification service + final NotificationService ns = (NotificationServiceImpl) atClient.getNotificationService(); + + final String RECIPIENT_ATSIGN_STR = "@smoothalligator"; + final AtSign recipient = new AtSign(RECIPIENT_ATSIGN_STR); + final NotificationResult result = ns.notify(NotificationParams.forText("12345", atSign, recipient, false)).get(); + + System.out.println(result.toString()); + + + + } + +} From 2a376e609bea3f887b4ff1b7379e5b8f95b37165 Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 19 Dec 2022 19:25:54 -0500 Subject: [PATCH 26/28] test: notify list response transformer tests --- .../common/ResponseTransformerTest.java | 160 ++++++++++++++---- 1 file changed, 129 insertions(+), 31 deletions(-) diff --git a/at_client/src/test/java/org/atsign/common/ResponseTransformerTest.java b/at_client/src/test/java/org/atsign/common/ResponseTransformerTest.java index 32b96059..2eda49b2 100644 --- a/at_client/src/test/java/org/atsign/common/ResponseTransformerTest.java +++ b/at_client/src/test/java/org/atsign/common/ResponseTransformerTest.java @@ -5,13 +5,15 @@ import org.atsign.client.api.Secondary; import org.atsign.common.ResponseTransformers.LlookupAllResponseTransformer; +import org.atsign.common.ResponseTransformers.NotifyListResponseTransformer; import org.atsign.common.response_models.LlookupAllResponse; +import org.atsign.common.response_models.NotifyListResponse; import org.junit.After; import org.junit.Before; import org.junit.Test; public class ResponseTransformerTest { - + @Before public void setUp() { } @@ -24,32 +26,37 @@ public void scanResponseTransformerTest() { @Test public void llookupAllResponseTransformerTest() { String RESPONSE_STR = "{" + - "\"key\":\"public:publickey@cooking2\"," + - "\"data\":\"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCz9nTBDDLxLgSxYu4+mDF3anWuTlKysXBBLsp3glrBP9xEXDx4muOHuHZIzuNvFlsjcCDF/mLSAJcvbxUoTsOQp+QD5XMhlNS9TWGsmNks7KHylNEhcqo2Va7RZxNS6MZBRacl+OusnebVKdOXDnbuevoED5fSklOz7mvdm9Mb2wIDAQAB\"," + - "\"metaData\":{" + - "\"createdBy\":null," + - "\"updatedBy\":null," + - "\"createdAt\":\"2022-08-12 01:50:15.398Z\"," + - "\"updatedAt\":\"2022-08-12 01:50:15.398Z\"," + - "\"availableAt\":\"2022-08-12 01:50:15.398Z\"," + - "\"expiresAt\":null," + - "\"refreshAt\":null," + - "\"status\":\"active\"," + - "\"version\":0," + - "\"ttl\":0," + - "\"ttb\":0," + - "\"ttr\":null," + - "\"ccd\":null," + - "\"isBinary\":false," + - "\"isEncrypted\":false," + - "\"dataSignature\":null," + - "\"sharedKeyEnc\":null," + - "\"pubKeyCS\":null," + - "\"encoding\":null" + - "}" + - "}"; - - // System.out.println(RESPONSE_STR); //{"key":"public:publickey@cooking2","data":"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCz9nTBDDLxLgSxYu4+mDF3anWuTlKysXBBLsp3glrBP9xEXDx4muOHuHZIzuNvFlsjcCDF/mLSAJcvbxUoTsOQp+QD5XMhlNS9TWGsmNks7KHylNEhcqo2Va7RZxNS6MZBRacl+OusnebVKdOXDnbuevoED5fSklOz7mvdm9Mb2wIDAQAB","metaData":{"createdBy":null,"updatedBy":null,"createdAt":"2022-08-12 01:50:15.398Z","updatedAt":"2022-08-12 01:50:15.398Z","availableAt":"2022-08-12 01:50:15.398Z","expiresAt":null,"refreshAt":null,"status":"active","version":0,"ttl":0,"ttb":0,"ttr":null,"ccd":null,"isBinary":false,"isEncrypted":false,"dataSignature":null,"sharedKeyEnc":null,"pubKeyCS":null,"encoding":null}} + "\"key\":\"public:publickey@cooking2\"," + + "\"data\":\"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCz9nTBDDLxLgSxYu4+mDF3anWuTlKysXBBLsp3glrBP9xEXDx4muOHuHZIzuNvFlsjcCDF/mLSAJcvbxUoTsOQp+QD5XMhlNS9TWGsmNks7KHylNEhcqo2Va7RZxNS6MZBRacl+OusnebVKdOXDnbuevoED5fSklOz7mvdm9Mb2wIDAQAB\"," + + + "\"metaData\":{" + + "\"createdBy\":null," + + "\"updatedBy\":null," + + "\"createdAt\":\"2022-08-12 01:50:15.398Z\"," + + "\"updatedAt\":\"2022-08-12 01:50:15.398Z\"," + + "\"availableAt\":\"2022-08-12 01:50:15.398Z\"," + + "\"expiresAt\":null," + + "\"refreshAt\":null," + + "\"status\":\"active\"," + + "\"version\":0," + + "\"ttl\":0," + + "\"ttb\":0," + + "\"ttr\":null," + + "\"ccd\":null," + + "\"isBinary\":false," + + "\"isEncrypted\":false," + + "\"dataSignature\":null," + + "\"sharedKeyEnc\":null," + + "\"pubKeyCS\":null," + + "\"encoding\":null" + + "}" + + "}"; + + // System.out.println(RESPONSE_STR); + // //{"key":"public:publickey@cooking2","data":"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCz9nTBDDLxLgSxYu4+mDF3anWuTlKysXBBLsp3glrBP9xEXDx4muOHuHZIzuNvFlsjcCDF/mLSAJcvbxUoTsOQp+QD5XMhlNS9TWGsmNks7KHylNEhcqo2Va7RZxNS6MZBRacl+OusnebVKdOXDnbuevoED5fSklOz7mvdm9Mb2wIDAQAB","metaData":{"createdBy":null,"updatedBy":null,"createdAt":"2022-08-12 + // 01:50:15.398Z","updatedAt":"2022-08-12 + // 01:50:15.398Z","availableAt":"2022-08-12 + // 01:50:15.398Z","expiresAt":null,"refreshAt":null,"status":"active","version":0,"ttl":0,"ttb":0,"ttr":null,"ccd":null,"isBinary":false,"isEncrypted":false,"dataSignature":null,"sharedKeyEnc":null,"pubKeyCS":null,"encoding":null}} Secondary.Response response = new Secondary.Response(); response.isError = false; response.data = RESPONSE_STR; @@ -58,8 +65,10 @@ public void llookupAllResponseTransformerTest() { model = transformer.transform(response); assertEquals("public:publickey@cooking2", model.key); - assertEquals("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCz9nTBDDLxLgSxYu4+mDF3anWuTlKysXBBLsp3glrBP9xEXDx4muOHuHZIzuNvFlsjcCDF/mLSAJcvbxUoTsOQp+QD5XMhlNS9TWGsmNks7KHylNEhcqo2Va7RZxNS6MZBRacl+OusnebVKdOXDnbuevoED5fSklOz7mvdm9Mb2wIDAQAB", model.data); - assertEquals(null, model.metaData.createdBy); + assertEquals( + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCz9nTBDDLxLgSxYu4+mDF3anWuTlKysXBBLsp3glrBP9xEXDx4muOHuHZIzuNvFlsjcCDF/mLSAJcvbxUoTsOQp+QD5XMhlNS9TWGsmNks7KHylNEhcqo2Va7RZxNS6MZBRacl+OusnebVKdOXDnbuevoED5fSklOz7mvdm9Mb2wIDAQAB", + model.data); + assertEquals(null, model.metaData.createdBy); assertEquals(null, model.metaData.updatedBy); assertEquals("2022-08-12 01:50:15.398Z", model.metaData.createdAt); assertEquals("2022-08-12 01:50:15.398Z", model.metaData.updatedAt); @@ -78,7 +87,7 @@ public void llookupAllResponseTransformerTest() { assertEquals(null, model.metaData.sharedKeyEnc); assertEquals(null, model.metaData.pubKeyCS); assertEquals(null, model.metaData.encoding); - + // System.out.println("FullKeyName: " + model.key); // System.out.println("Data: " + model.data); // System.out.println("MetaData.createdBy: " + model.metaData.createdBy); @@ -96,13 +105,102 @@ public void llookupAllResponseTransformerTest() { // System.out.println("MetaData.ccd: " + model.metaData.ccd); // System.out.println("MetaData.isBinary: " + model.metaData.isBinary); // System.out.println("MetaData.isEncrypted: " + model.metaData.isEncrypted); - // System.out.println("MetaData.dataSignature: " + model.metaData.dataSignature); + // System.out.println("MetaData.dataSignature: " + + // model.metaData.dataSignature); // System.out.println("MetaData.sharedKeyEnc: " + model.metaData.sharedKeyEnc); // System.out.println("MetaData.pubKeyCS: " + model.metaData.pubKeyCS); // System.out.println("MetaData.encoding: " + model.metaData.encoding); } + @Test + public void notifyListResponseTransformerTest() { + /** + * [ + * { + * "id":"915a5c6a-314e-437f-b464-9a8c0d80770d", + * "from":"@soccer0", + * "to":"@22easy", + * "key":"@22easy:12345", + * "value":null, + * "operation":"null" + * "epochMillis":1671491608714, + * "messageType":"MessageType.text", + * "isEncrypted":false + * }, + * { + * "id":"c18d15ef-3da9-4538-b699-4a6542666678" + * "from":"@soccer0", + * "to":"@22easy", + * "key":"@22easy:12345", + * "value":null, + * "operation":"null", + * "epochMillis":1671491611414, + * "messageType":"MessageType.text", + * "isEncrypted":false + * }, + * { + * "id":"ce872442-a3e5-4624-b27c-f37a69e6bd3e", + * "from":"@soccer0", + * "to":"@22easy", + * "key":"@22easy:12345", + * "value":null, + * "operation":"null", + * "epochMillis":1671491603639, + * "messageType":"MessageType.text", + * "isEncrypted":false + * } + * ] + */ + + final String RESPONSE_STR = "[{\"id\":\"915a5c6a-314e-437f-b464-9a8c0d80770d\",\"from\":\"@soccer0\",\"to\":\"@22easy\",\"key\":\"@22easy:12345\",\"value\":null,\"operation\":\"null\",\"epochMillis\":1671491608714,\"messageType\":\"MessageType.text\",\"isEncrypted\":false},{\"id\":\"c18d15ef-3da9-4538-b699-4a6542666678\",\"from\":\"@soccer0\",\"to\":\"@22easy\",\"key\":\"@22easy:12345\",\"value\":null,\"operation\":\"null\",\"epochMillis\":1671491611414,\"messageType\":\"MessageType.text\",\"isEncrypted\":false},{\"id\":\"ce872442-a3e5-4624-b27c-f37a69e6bd3e\",\"from\":\"@soccer0\",\"to\":\"@22easy\",\"key\":\"@22easy:12345\",\"value\":null,\"operation\":\"null\",\"epochMillis\":1671491603639,\"messageType\":\"MessageType.text\",\"isEncrypted\":false}]"; + + Secondary.Response response = new Secondary.Response(); + response.isError = false; + response.data = RESPONSE_STR; + + NotifyListResponseTransformer transformer = new NotifyListResponseTransformer(); + NotifyListResponse model = transformer.transform(response); + + assertEquals(3, model.notifications.size()); + + // check notification 1 + NotifyListResponse.Notification n1 = model.notifications.get(0); + assertEquals("915a5c6a-314e-437f-b464-9a8c0d80770d", n1.id); + assertEquals("@soccer0", n1.from); + assertEquals("@22easy", n1.to); + assertEquals("@22easy:12345", n1.key); + assertEquals(null, n1.value); + assertEquals("null", n1.operation); + assertEquals(1671491608714L, n1.epochMillis.longValue()); + assertEquals("MessageType.text", n1.messageType); + assertEquals(false, n1.isEncrypted); + + // check notification 2 + NotifyListResponse.Notification n2 = model.notifications.get(1); + assertEquals("c18d15ef-3da9-4538-b699-4a6542666678", n2.id); + assertEquals("@soccer0", n2.from); + assertEquals("@22easy", n2.to); + assertEquals("@22easy:12345", n2.key); + assertEquals(null, n2.value); + assertEquals("null", n2.operation); + assertEquals(1671491611414L, n2.epochMillis.longValue()); + assertEquals("MessageType.text", n2.messageType); + assertEquals(false, n2.isEncrypted); + + // check notification 3 + NotifyListResponse.Notification n3 = model.notifications.get(2); + assertEquals("ce872442-a3e5-4624-b27c-f37a69e6bd3e", n3.id); + assertEquals("@soccer0", n3.from); + assertEquals("@22easy", n3.to); + assertEquals("@22easy:12345", n3.key); + assertEquals(null, n3.value); + assertEquals("null", n3.operation); + assertEquals(1671491603639L, n3.epochMillis.longValue()); + assertEquals("MessageType.text", n3.messageType); + assertEquals(false, n3.isEncrypted); + } + @After public void tearDown() { } From bb3bcf4e7d888565e4320abd019e78dec41452eb Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 19 Dec 2022 19:31:45 -0500 Subject: [PATCH 27/28] feat: conceptual notification validation --- .../notification/NotificationServiceImpl.java | 1 + .../atsign/common/AtClientValidationTest.java | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/at_client/src/main/java/org/atsign/client/api/notification/NotificationServiceImpl.java b/at_client/src/main/java/org/atsign/client/api/notification/NotificationServiceImpl.java index 7a54dace..10550836 100644 --- a/at_client/src/main/java/org/atsign/client/api/notification/NotificationServiceImpl.java +++ b/at_client/src/main/java/org/atsign/client/api/notification/NotificationServiceImpl.java @@ -46,6 +46,7 @@ public CompletableFuture notify(NotificationParams params) { } // TODO: AtClientValidation notification + // AtClientValidation.validateNotificationRequest(params, /* get hold of rootDomain somehow */, /* get hold of root port somehow */); VerbBuilder builder; switch (params.getMessageType()) { diff --git a/at_client/src/test/java/org/atsign/common/AtClientValidationTest.java b/at_client/src/test/java/org/atsign/common/AtClientValidationTest.java index 0ebc8121..3d7ebfe5 100644 --- a/at_client/src/test/java/org/atsign/common/AtClientValidationTest.java +++ b/at_client/src/test/java/org/atsign/common/AtClientValidationTest.java @@ -2,6 +2,7 @@ import static org.junit.Assert.assertThrows; +import org.atsign.client.api.notification.NotificationParams; import org.atsign.client.util.AtClientValidation; import org.atsign.common.Keys.Metadata; import org.atsign.common.Keys.PublicKey; @@ -243,6 +244,53 @@ public void validateAtKeyTest() { // TODO } + @Test + public void validateNotificationRequestTest() { + // AtSign dummy1 = new AtSign("@alice"); + // AtSign dummy2 = new AtSign("@bob"); + // final String ROOT_DOMAIN = "root.atsign.org"; + // final int ROOT_PORT = 64; + + + // // null params + // assertThrows(AtException.class, () -> { + // NotificationParams params = null; + // AtClientValidation.validateNotificationRequest(params, ROOT_DOMAIN, ROOT_PORT); + // }); + + // // forText with null text + // assertThrows(AtException.class, () -> { + // NotificationParams params = NotificationParams.forText(null, dummy1, dummy2, false); + // AtClientValidation.validateNotificationRequest(params, ROOT_DOMAIN, ROOT_PORT); + // }); + + // // forText with null sharedBy + // assertThrows(AtException.class, () -> { + // NotificationParams params = NotificationParams.forText("test", null, dummy2, false); + // AtClientValidation.validateNotificationRequest(params, ROOT_DOMAIN, ROOT_PORT); + // }); + + // // forText with null sharedWith + // assertThrows(AtException.class, () -> { + // NotificationParams params = NotificationParams.forText("test", dummy1, null, false); + // AtClientValidation.validateNotificationRequest(params, ROOT_DOMAIN, ROOT_PORT); + // }); + + // // forUpdate with null sharedBy + // assertThrows(AtException.class, () -> { + // SharedKey sk = new KeyBuilders.SharedKeyBuilder(null, dummy2).key("test").build(); + // NotificationParams params = NotificationParams.forUpdate(sk, "lmao"); + // AtClientValidation.validateNotificationRequest(params, ROOT_DOMAIN, ROOT_PORT); + // }); + + // // forUpdate with null sharedWith + // assertThrows(AtException.class, () -> { + // SharedKey sk = new KeyBuilders.SharedKeyBuilder(dummy1, null).key("test").build(); + // NotificationParams params = NotificationParams.forUpdate(sk, "lmao"); + // AtClientValidation.validateNotificationRequest(params, ROOT_DOMAIN, ROOT_PORT); + // }); + } + @After public void tearDown() { } From 4e561971c1ebf175d1bfa116521b8fad8d7bb6ca Mon Sep 17 00:00:00 2001 From: JeremyTubongbanua Date: Mon, 19 Dec 2022 19:49:21 -0500 Subject: [PATCH 28/28] fix: failing test --- at_client/src/test/java/org/atsign/common/VerbBuildersTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java b/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java index f9da9559..54b2797b 100644 --- a/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java +++ b/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java @@ -732,7 +732,7 @@ public void notifyDeleteVerbBuilderTest() { // Test with notification id argument NotifyRemoveVerbBuilder notificationDeleteVerbBuilder = new NotifyRemoveVerbBuilder(); notificationDeleteVerbBuilder.setNotificationId("n1234"); - assertEquals("notify:delete:n1234", notificationDeleteVerbBuilder.build()); + assertEquals("notify:remove:n1234", notificationDeleteVerbBuilder.build()); } @After