From fb96b2a7e112a075a9c9bf133de690b4b297f6d7 Mon Sep 17 00:00:00 2001
From: blavenie <benoit.lavenier@e-is.pro>
Date: Thu, 15 Dec 2016 08:31:39 +0100
Subject: [PATCH] - Notify user when new message

---
 .../duniter/core/client/model/ModelUtils.java | 20 +++++----
 .../service/AbstractService.java              |  2 +-
 .../user/model/UserEventCodes.java            |  5 ++-
 .../service/BlockchainUserEventService.java   | 45 ++-----------------
 .../user/service/MessageService.java          | 23 +++++++++-
 .../user/service/UserService.java             | 25 +++++++++++
 .../i18n/duniter4j-es-user_en_GB.properties   |  1 +
 .../i18n/duniter4j-es-user_fr_FR.properties   |  1 +
 8 files changed, 69 insertions(+), 53 deletions(-)

diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/ModelUtils.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/ModelUtils.java
index 06f2cdd8..5b024359 100644
--- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/ModelUtils.java
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/ModelUtils.java
@@ -145,16 +145,20 @@ public class ModelUtils {
         return pubkey.substring(0, 8);
     }
 
-    public static String joinPubkeys(Collection<String> pubkeys, boolean minify, String separator) {
-        Preconditions.checkArgument(CollectionUtils.isNotEmpty(pubkeys));
-        Preconditions.checkNotNull(separator);
+    public static String joinPubkeys(Set<String> pubkeys, String separator, boolean minify) {
+        Preconditions.checkNotNull(pubkeys);
+        Preconditions.checkArgument(pubkeys.size()>0);
+        if (pubkeys.size() == 1) {
+            String pubkey = pubkeys.iterator().next();
+            return (minify ? ModelUtils.minifyPubkey(pubkey) : pubkey);
+        }
 
         StringBuilder sb = new StringBuilder();
-        for (String pubkey : pubkeys) {
-            sb.append(separator)
-                    .append(minify ? ModelUtils.minifyPubkey(pubkey) : pubkey);
-        }
+        pubkeys.stream().forEach((pubkey)-> {
+            sb.append(separator);
+            sb.append(minify ? ModelUtils.minifyPubkey(pubkey) : pubkey);
+        });
 
-        return sb.toString().substring(separator.length());
+        return sb.substring(separator.length());
     }
 }
diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java
index dce12461..852643dd 100644
--- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java
@@ -233,7 +233,7 @@ public abstract class AbstractService implements Bean {
 
 
     protected String getIssuer(JsonNode actualObj) {
-        return  actualObj.get(Record.PROPERTY_ISSUER).asText();
+        return  getMandatoryField(actualObj, Record.PROPERTY_ISSUER).asText();
     }
 
     protected JsonNode getMandatoryField(JsonNode actualObj, String fieldName) {
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/model/UserEventCodes.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/model/UserEventCodes.java
index e1a5053e..7f7ac356 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/model/UserEventCodes.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/model/UserEventCodes.java
@@ -38,6 +38,9 @@ public enum UserEventCodes {
 
     // TX
     TX_SENT,
-    TX_RECEIVED
+    TX_RECEIVED,
+
+    // Message
+    MESSAGE_RECEIVED
 
 }
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java
index 690a76bc..dec3f835 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java
@@ -204,8 +204,8 @@ public class BlockchainUserEventService extends AbstractService implements Chang
         Set<String> senders = ImmutableSet.copyOf(tx.getIssuers());
 
         // Received
-        String senderNames = getNamesFromPubkeys(senders, true);
-        String sendersPubkeys = joinPubkeys(senders, false);
+        String senderNames = userService.joinNamesFromPubkeys(senders, DEFAULT_PUBKEYS_SEPARATOR, true);
+        String sendersPubkeys = ModelUtils.joinPubkeys(senders, DEFAULT_PUBKEYS_SEPARATOR, false);
         Set<String> receivers = new HashSet<>();
         for (String output : tx.getOutputs()) {
             String[] parts = output.split(":");
@@ -220,8 +220,8 @@ public class BlockchainUserEventService extends AbstractService implements Chang
 
         // Sent
         if (CollectionUtils.isNotEmpty(receivers)) {
-            String receiverNames = getNamesFromPubkeys(receivers, true);
-            String receiverPubkeys = joinPubkeys(receivers, false);
+            String receiverNames = userService.joinNamesFromPubkeys(receivers, DEFAULT_PUBKEYS_SEPARATOR, true);
+            String receiverPubkeys = ModelUtils.joinPubkeys(receivers, DEFAULT_PUBKEYS_SEPARATOR, false);
             for (String sender : senders) {
                 notifyUserEvent(block, sender, UserEventCodes.TX_SENT, I18n.n("duniter.user.event.tx.sent"), receiverPubkeys, receiverNames);
             }
@@ -249,42 +249,5 @@ public class BlockchainUserEventService extends AbstractService implements Chang
         userEventService.deleteEventsByReference(new UserEvent.Reference(change.getIndex(), change.getType(), change.getId()));
     }
 
-    private String getNamesFromPubkeys(Set<String> pubkeys, boolean minify) {
-        Preconditions.checkNotNull(pubkeys);
-        Preconditions.checkArgument(pubkeys.size()>0);
-        if (pubkeys.size() == 1) {
-            String pubkey = pubkeys.iterator().next();
-            String title = userService.getProfileTitle(pubkey);
-            return title != null ? title :
-                    (minify ? ModelUtils.minifyPubkey(pubkey) : pubkey);
-        }
-
-        Map<String, String> profileTitles = userService.getProfileTitles(pubkeys);
-        StringBuilder sb = new StringBuilder();
-        pubkeys.stream().forEach((pubkey)-> {
-            String title = profileTitles != null ? profileTitles.get(pubkey) : null;
-            sb.append(DEFAULT_PUBKEYS_SEPARATOR);
-            sb.append(title != null ? title :
-                    (minify ? ModelUtils.minifyPubkey(pubkey) : pubkey));
-        });
-
-        return sb.substring(DEFAULT_PUBKEYS_SEPARATOR.length());
-    }
 
-    private String joinPubkeys(Set<String> pubkeys, boolean minify) {
-        Preconditions.checkNotNull(pubkeys);
-        Preconditions.checkArgument(pubkeys.size()>0);
-        if (pubkeys.size() == 1) {
-            String pubkey = pubkeys.iterator().next();
-            return (minify ? ModelUtils.minifyPubkey(pubkey) : pubkey);
-        }
-
-        StringBuilder sb = new StringBuilder();
-        pubkeys.stream().forEach((pubkey)-> {
-            sb.append(DEFAULT_PUBKEYS_SEPARATOR);
-            sb.append(minify ? ModelUtils.minifyPubkey(pubkey) : pubkey);
-        });
-
-        return sb.substring(DEFAULT_PUBKEYS_SEPARATOR.length());
-    }
 }
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java
index 78053edf..e6cd1aa0 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java
@@ -25,14 +25,17 @@ package org.duniter.elasticsearch.user.service;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
+import org.duniter.core.client.model.ModelUtils;
 import org.duniter.core.client.model.elasticsearch.Record;
 import org.duniter.core.exception.TechnicalException;
 import org.duniter.core.service.CryptoService;
 import org.duniter.elasticsearch.PluginSettings;
 import org.duniter.elasticsearch.exception.InvalidSignatureException;
 import org.duniter.elasticsearch.service.AbstractService;
+import org.duniter.elasticsearch.service.BlockchainService;
 import org.duniter.elasticsearch.user.model.Message;
 import org.duniter.elasticsearch.user.model.UserEvent;
+import org.duniter.elasticsearch.user.model.UserEventCodes;
 import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
 import org.elasticsearch.action.index.IndexResponse;
 import org.elasticsearch.action.update.UpdateRequestBuilder;
@@ -41,6 +44,7 @@ import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
+import org.nuiton.i18n.I18n;
 
 import java.io.IOException;
 import java.util.Map;
@@ -54,10 +58,13 @@ public class MessageService extends AbstractService {
     public static final String RECORD_TYPE = "record";
     public static final String OUTBOX_TYPE = "outbox";
 
+    private final UserEventService userEventService;
 
     @Inject
-    public MessageService(Client client, PluginSettings settings, CryptoService cryptoService, UserService userService) {
+    public MessageService(Client client, PluginSettings settings,
+                          CryptoService cryptoService, UserEventService userEventService) {
         super("duniter." + INDEX, client, settings, cryptoService);
+        this.userEventService = userEventService;
     }
 
     /**
@@ -114,6 +121,8 @@ public class MessageService extends AbstractService {
 
         JsonNode actualObj = readAndVerifyIssuerSignature(recordJson);
         String issuer = getIssuer(actualObj);
+        String recipient = getMandatoryField(actualObj, Message.PROPERTY_RECIPIENT).asText();
+        Long time = getMandatoryField(actualObj, Message.PROPERTY_TIME).asLong();
 
         if (logger.isDebugEnabled()) {
             logger.debug(String.format("Indexing a message from issuer [%s]", issuer.substring(0, 8)));
@@ -124,7 +133,17 @@ public class MessageService extends AbstractService {
                 .setRefresh(false)
                 .execute().actionGet();
 
-        return response.getId();
+        String messageId = response.getId();
+
+        // Notify recipient
+        userEventService.notifyUser(UserEvent.newBuilder(UserEvent.EventType.INFO, UserEventCodes.MESSAGE_RECEIVED.name())
+                .setRecipient(recipient)
+                .setMessage(I18n.n("duniter.user.event.message.received"), issuer, ModelUtils.minifyPubkey(issuer))
+                .setTime(time)
+                .setReference(INDEX, RECORD_TYPE, messageId)
+                .build());
+
+        return messageId;
     }
 
     public String indexOuboxFromJson(String recordJson) {
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java
index 003d7089..96b162cc 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java
@@ -25,7 +25,9 @@ package org.duniter.elasticsearch.user.service;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.base.Preconditions;
 import org.apache.commons.collections4.MapUtils;
+import org.duniter.core.client.model.ModelUtils;
 import org.duniter.core.client.model.elasticsearch.UserProfile;
 import org.duniter.core.exception.TechnicalException;
 import org.duniter.core.service.CryptoService;
@@ -214,6 +216,29 @@ public class UserService extends AbstractService {
         return result;
     }
 
+    public String joinNamesFromPubkeys(Set<String> pubkeys, String separator, boolean minify) {
+        Preconditions.checkNotNull(pubkeys);
+        Preconditions.checkNotNull(separator);
+        Preconditions.checkArgument(pubkeys.size()>0);
+        if (pubkeys.size() == 1) {
+            String pubkey = pubkeys.iterator().next();
+            String title = getProfileTitle(pubkey);
+            return title != null ? title :
+                    (minify ? ModelUtils.minifyPubkey(pubkey) : pubkey);
+        }
+
+        Map<String, String> profileTitles = getProfileTitles(pubkeys);
+        StringBuilder sb = new StringBuilder();
+        pubkeys.stream().forEach((pubkey)-> {
+            String title = profileTitles != null ? profileTitles.get(pubkey) : null;
+            sb.append(separator);
+            sb.append(title != null ? title :
+                    (minify ? ModelUtils.minifyPubkey(pubkey) : pubkey));
+        });
+
+        return sb.substring(separator.length());
+    }
+
     /* -- Internal methods -- */
 
 
diff --git a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties
index 4917ad91..d5b454c0 100644
--- a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties
+++ b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties
@@ -4,6 +4,7 @@ duniter.event.NODE_STARTED=Node started on cluster Duniter4j ES [%s]
 duniter.user.event.active=
 duniter.user.event.join=
 duniter.user.event.leave=
+duniter.user.event.message.received=
 duniter.user.event.ms.active=
 duniter.user.event.ms.join=
 duniter.user.event.ms.leave=
diff --git a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties
index 03e86fc3..8e74a2b7 100644
--- a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties
+++ b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties
@@ -1,6 +1,7 @@
 duniter.event.NODE_BMA_DOWN=Noeud Duniter [%1$s\:%2$s] non joignable, depuis le noeud ES API [%3$s]. Dernière connexion à %4$d. Indexation de blockchain en attente.
 duniter.event.NODE_BMA_UP=Noeud Duniter [%1$s\:%2$s] à nouveau accessible.
 duniter.event.NODE_STARTED=Noeud ES API démarré sur le cluster Duniter [%1$s]
+duniter.user.event.message.received=Vous avez reçu un message de %2$s
 duniter.user.event.ms.active=Votre adhésion comme membre a bien été renouvellée
 duniter.user.event.ms.join=Vous êtes maintenant membre de la monnaie
 duniter.user.event.ms.leave=Votre adhésion comme membre à expirée
-- 
GitLab