From e07334859b481ae4935c3254c1749f841291abc5 Mon Sep 17 00:00:00 2001
From: Vincent Texier <vit@free.fr>
Date: Mon, 16 Jul 2018 12:14:24 +0200
Subject: [PATCH] issue #56 WIP - Support Elasticsearch/Duniter4j nodes
 ES_CORE_API with example

---
 duniterpy/api/client.py                |  3 ++
 duniterpy/api/endpoint.py              | 53 ++++++++++++++++++++++++--
 examples/request_data_elasticsearch.py | 42 ++++++++++++++++++++
 3 files changed, 94 insertions(+), 4 deletions(-)
 create mode 100644 examples/request_data_elasticsearch.py

diff --git a/duniterpy/api/client.py b/duniterpy/api/client.py
index efffcb64..f1b53e85 100644
--- a/duniterpy/api/client.py
+++ b/duniterpy/api/client.py
@@ -203,6 +203,9 @@ class Client:
         else:
             self.endpoint = _endpoint
 
+        if isinstance(self.endpoint, endpoint.UnknownEndpoint):
+            raise NotImplementedError("{0} endpoint in not supported".format(self.endpoint.api))
+
         # if no user session...
         if session is None:
             # open a session
diff --git a/duniterpy/api/endpoint.py b/duniterpy/api/endpoint.py
index 79ceaae0..0d1acd8b 100644
--- a/duniterpy/api/endpoint.py
+++ b/duniterpy/api/endpoint.py
@@ -319,11 +319,56 @@ class WS2PEndpoint(Endpoint):
         return hash((self.ws2pid, self.server, self.port, self.path))
 
 
+class ESCoreEndpoint(Endpoint):
+    API = "ES_CORE_API"
+    re_inline = re.compile(
+        '^ES_CORE_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$'.format(host_regex=HOST_REGEX,
+                                                                            ipv4_regex=IPV4_REGEX))
+
+    def __init__(self, server, port):
+        self.server = server
+        self.port = port
+
+    @classmethod
+    def from_inline(cls, inline):
+        m = ESCoreEndpoint.re_inline.match(inline)
+        if m is None:
+            raise MalformedDocumentError(ESCoreEndpoint.API)
+        server = m.group(1)
+        port = int(m.group(2))
+        return cls(server, port)
+
+    def inline(self):
+        inlined = [str(info) for info in (self.server, self.port) if info]
+        return ESCoreEndpoint.API + " " + " ".join(inlined)
+
+    def conn_handler(self, session: aiohttp.ClientSession, proxy: str = None) -> ConnectionHandler:
+        """
+        Return connection handler instance for the endpoint
+
+        :param aiohttp.ClientSession session: AIOHTTP client session instance
+        :param str proxy: Proxy url
+        :rtype: ConnectionHandler
+        """
+        return ConnectionHandler("https", "wss", self.server, self.port, "", proxy, session)
+
+    def __str__(self):
+        return self.inline()
+
+    def __eq__(self, other):
+        if isinstance(other, ESCoreEndpoint):
+            return self.server == other.server and self.port == other.port
+        else:
+            return False
+
+    def __hash__(self):
+        return hash((self.server, self.port))
+
+
 class ESUserEndpoint(Endpoint):
     API = "ES_USER_API"
     re_inline = re.compile(
-        '^ES_USER_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$'.format(ws2pid_regex=WS2PID_REGEX,
-                                                                            host_regex=HOST_REGEX,
+        '^ES_USER_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$'.format(host_regex=HOST_REGEX,
                                                                             ipv4_regex=IPV4_REGEX))
 
     def __init__(self, server, port):
@@ -369,8 +414,7 @@ class ESUserEndpoint(Endpoint):
 class ESSubscribtionEndpoint(Endpoint):
     API = "ES_SUBSCRIPTION_API"
     re_inline = re.compile(
-        '^ES_SUBSCRIPTION_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$'.format(ws2pid_regex=WS2PID_REGEX,
-                                                                                    host_regex=HOST_REGEX,
+        '^ES_SUBSCRIPTION_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$'.format(host_regex=HOST_REGEX,
                                                                                     ipv4_regex=IPV4_REGEX))
 
     def __init__(self, server, port):
@@ -417,6 +461,7 @@ MANAGED_API = {
     BMAEndpoint.API: BMAEndpoint,
     SecuredBMAEndpoint.API: SecuredBMAEndpoint,
     WS2PEndpoint.API: WS2PEndpoint,
+    ESCoreEndpoint.API: ESCoreEndpoint,
     ESUserEndpoint.API: ESUserEndpoint,
     ESSubscribtionEndpoint.API: ESSubscribtionEndpoint
 }
diff --git a/examples/request_data_elasticsearch.py b/examples/request_data_elasticsearch.py
new file mode 100644
index 00000000..ba4d1959
--- /dev/null
+++ b/examples/request_data_elasticsearch.py
@@ -0,0 +1,42 @@
+import asyncio
+
+from duniterpy.api.client import Client
+
+# Duniter4j ES API documentation: https://git.duniter.org/clients/java/duniter4j/blob/master/src/site/markdown/ES_API.md
+# Duniter4j project: https://git.duniter.org/clients/java/duniter4j/
+
+# CONFIG #######################################
+
+# You can either use a complete defined endpoint : [NAME_OF_THE_API] [DOMAIN] [IPv4] [IPv6] [PORT]
+# or the simple definition : [NAME_OF_THE_API] [DOMAIN] [PORT]
+# Here we use the secure BASIC_MERKLED_API (BMAS)
+ES_CORE_ENDPOINT = "ES_CORE_API g1-test.data.duniter.fr 443"
+
+
+################################################
+
+
+async def main():
+    """
+    Main code (synchronous requests)
+    """
+    # Create Client from endpoint string in Duniter format
+    client = Client(ES_CORE_ENDPOINT)
+
+    # Get the current node (direct REST GET request)
+    print("\nGET g1-test/block/current/_source:")
+    response = await client.get('g1-test/block/current/_source')
+    print(response)
+
+    # Get the node number 2 with only selected fields (direct REST GET request)
+    print("\nGET g1-test/block/2/_source:")
+    response = await client.get('g1-test/block/2/_source', {'_source': 'number,hash,dividend,membersCount'})
+    print(response)
+
+    # Close client aiohttp session
+    await client.close()
+
+
+# Latest duniter-python-api is asynchronous and you have to use asyncio, an asyncio loop and a "as" on the data.
+# ( https://docs.python.org/3/library/asyncio.html )
+asyncio.get_event_loop().run_until_complete(main())
-- 
GitLab