Skip to content

Commit 61d6f23

Browse files
authored
Merge pull request #3 from HTTP-APIs/master
Sync with upstream
2 parents 58afa35 + 43d17ae commit 61d6f23

File tree

9 files changed

+387
-153
lines changed

9 files changed

+387
-153
lines changed

dockerfile renamed to Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ COPY ./hydra_redis /home/app/hydra_redis
1010
ENV PYTHONPATH $PYTHONPATH:/home/app/
1111

1212
ENTRYPOINT ["python", "/home/app/hydra_redis/querying_mechanism.py"]
13+
14+
15+

README.md

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,40 @@
11
# python-hydra-agent
22

3-
The python-hydra-agent is a smart python hydra client which is working with [hydrus](https://github.com/HTTP-APIs/hydrus).
3+
For a general introduction to Hydra Ecosystem, see [hydraecosystem.org](http://hydraecosystem.org).
44

5-
It caches the server data from hydra server for fast data querying.
5+
`python-hydra-agent` is a smart Hydra client implemented in Python which works with [hydrus](https://github.com/HTTP-APIs/hydrus). Reference implementation is [Heracles.ts](https://github.com/HydraCG/Heracles.ts). Smart clients are generic automated clients that establish resilient connected data networks leveraging knowledge graphs.
66

7-
It uses Redis to cache the data at the end of the client.
7+
## General characteristics
88

9-
So, Data is loaded from the server and store in Redis memory as a graph using redisgraph.
9+
The client is designed to:
10+
* Cache metadata from the Hydra server it connects to, to allow querying on the client-side;
11+
* Use Redis as a graph-store leveraging `redisgraph` (see [here](https://oss.redislabs.com/redisgraph/));
12+
* simply, metadata and data are loaded from the server and stored in Redis;
13+
* The graph can be queried using OpenCypher.
1014

11-
With the help of Redis, clients become faster and easier to query the data.
15+
The starting objective is to create a querying layer that is able to reach data in one or more Hydra srever/s. Leveraging Redis, clients can construct their own representation of the data stored in one or more Hydra servers; querying the data as they need it, and respond complex semantic queries. This will allow any client connected to any server to have access to an "aggregated view" of the connected network (the network of all the servers it connects to).
16+
17+
## Missing bits at the moment
18+
* For now it is a proof-of-concept, only `GET` functionality
19+
* Soon to develop, a reliable synchronization mechanism to allow strong consistency between server-side data and client-side representation.
1220

1321
## Installation
1422

1523
**NOTE:** You'll need to use python3.
1624

25+
To install only requirements:
26+
27+
pip3 install -r requirements.txt
28+
29+
or,
30+
1731
To install or setup the client environment, you have to run:
1832

1933
python3 setup.py install
2034

21-
or,
2235

23-
To install only requirements:
24-
25-
pip3 install -r requirements.txt
26-
27-
To install Redis and start Redis server:
36+
To install Redis and other Redis modules:
2837

29-
cd hydra_redis
3038
./redis_setup.sh
3139

3240
## Quickstart
@@ -48,17 +56,16 @@ To run the demo for python-hydra-agent, you have to follow the instructions:
4856

4957
you should follow the instructions of [installation](#installation).
5058

51-
After setup the environment and run the Redis server. You can query or run the client.
59+
After setup the environment. You can query or run the client.
5260

53-
* To run the client you should run querying_mechanism.py like:
54-
55-
cd hydra_redis
56-
python3 querying_mechanism.py
61+
* To run both the things Redis server and the client. You can run the command:
62+
63+
docker-compose run client
5764

5865

5966
and provide a valid URL and then you can query in querying format.
6067

61-
`>>>url` #here url should be a valid link, for testing you can use https://storage.googleapis.com/api3/api
68+
`>>>url` #here url should be a valid link, for testing you can use http://35.224.198.158:8080/api
6269
`>>>help` # it will provide the querying format
6370

6471
#### Code simplification
@@ -96,18 +103,24 @@ The client takes the query as input, like:
96103
you can query as following querying formats:
97104

98105
```
99-
print("for endpoint:- show endpoint")
100-
print("for class_endpoint:- show classEndpoint")
101-
print("for collection_endpoint:- show collectionEndpoint")
102-
print("for members of collection_endpoint:-",
106+
print("querying format")
107+
print("Get all endpoints:- show endpoints")
108+
print("Get all class_endpoints:- show classEndpoints")
109+
print("Get all collection_endpoints:- show collectionEndpoints")
110+
print("Get all members of collection_endpoint:-",
103111
"show <collection_endpoint> members")
104-
print("for properties of any member:-",
112+
print("Get all properties of objects:-",
113+
"show objects<endpoint_type> properties")
114+
print("Get all properties of any member:-",
105115
"show object<id_of_member> properties ")
106-
print("for properties of objects:-show objects<endpoint_type> properties")
107-
print("for collection properties:-",
108-
"show <collection_endpoint> properties")
109-
print("for classes properties:- show class<class_endpoint> properties")
110-
print("for compare properties:-show <key> <value> and/or <key1> <value1>")
116+
print("Get all classes properties:-show class<class_endpoint> properties")
117+
print("Get data with compare properties:-",
118+
"show <key> <value> and/or <key1> <value1>")
119+
print("Get data by using both opeartions(and,or)",
120+
" you should use brackets like:-",
121+
"show model xyz and (name Drone1 or name Drone2)",
122+
"or, show <key> <value> and (<key> <value> or <key> <value>)")
123+
111124
```
112125

113126
Query test can be done like this:

hydra_redis/classes_objects.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
import urllib.request
22
import json
3+
import logging
34
from redisgraph import Node, Edge
5+
from urllib.error import URLError, HTTPError
46

7+
logging.basicConfig(level=logging.INFO)
8+
logger = logging.getLogger(__name__)
9+
10+
class RequestError(Exception):
11+
"""A class for client-side exceptions"""
12+
pass
513

614
class ClassEndpoints:
715
"""Contains all the classes endpoint and the objects"""
@@ -119,8 +127,19 @@ def load_from_server(
119127
# new_url is url for the classes endpoint
120128
new_url = base_url + "/" + endpoint
121129
# retreiving data for the classes endpoint from server
122-
response = urllib.request.urlopen(new_url)
123-
new_file = json.loads(response.read().decode('utf-8'))
130+
try:
131+
response = urllib.request.urlopen(new_url)
132+
except HTTPError as e:
133+
logger.info('Error code: ', e.code)
134+
return None
135+
except URLError as e:
136+
logger.info('Reason: ', e.reason)
137+
return None
138+
except ValueError as e:
139+
logger.info("value error:", e)
140+
return None
141+
else:
142+
new_file = json.loads(response.read().decode('utf-8'))
124143
# endpoint_property store all properties which is class/object but not
125144
# endpoint.
126145
for support_property in api_doc.parsed_classes[
@@ -161,7 +180,12 @@ def load_from_server(
161180
endpoint_property,
162181
no_endpoint_property,
163182
api_doc)
164-
# save the graph changes.
183+
# delete all the old data that has saved in Redis using redis_graph.
184+
# It will remove duplicate data from Redis.
185+
for key in redis_connection.keys():
186+
if "fs:" not in key.decode("utf8"):
187+
redis_connection.delete(key)
188+
# save the new data.
165189
self.redis_graph.commit()
166190

167191
def endpointclasses(

hydra_redis/collections_endpoint.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import urllib.request
22
import json
33
import re
4-
from hydra_redis.classes_objects import ClassEndpoints
4+
import logging
5+
from urllib.error import URLError, HTTPError
6+
from hydra_redis.classes_objects import ClassEndpoints,RequestError
57

8+
logging.basicConfig(level=logging.INFO)
9+
logger = logging.getLogger(__name__)
610

711
class CollectionEndpoints:
812
"""Contains all the collections endpoints and objects"""
@@ -16,8 +20,19 @@ def fetch_data(self, new_url):
1620
:param new_url: url for fetching the data.
1721
:return: loaded data.
1822
"""
19-
response = urllib.request.urlopen(new_url)
20-
return json.loads(response.read().decode('utf-8'))
23+
try:
24+
response = urllib.request.urlopen(new_url)
25+
except HTTPError as e:
26+
logger.info('Error code: ', e.code)
27+
return RequestError("error")
28+
except URLError as e:
29+
logger.info('Reason: ', e.reason)
30+
return RequestError("error")
31+
except ValueError as e:
32+
logger.info("value error:",e)
33+
return RequestError("error")
34+
else:
35+
return json.loads(response.read().decode('utf-8'))
2136

2237
def faceted_key(self, fs, key, value):
2338
return ("{}".format(fs + ":" + key + ":" + value))
@@ -68,6 +83,8 @@ def collectionobjects(
6883
member_url = new_url + "/" + member_id
6984
# object data retrieving from the server
7085
new_file = self.fetch_data(member_url)
86+
if isinstance (new_file, RequestError):
87+
return None
7188
for support_operation in api_doc.parsed_classes[
7289
endpoint["@type"]
7390
]["class"
@@ -161,6 +178,8 @@ def load_from_server(
161178
new_url = url + "/" + endpoint
162179
# url for every collection endpoint
163180
new_file = self.fetch_data(new_url)
181+
if isinstance (new_file, RequestError):
182+
return None
164183
# retrieving the objects from the collection endpoint
165184
for node in self.redis_graph.nodes.values():
166185
if node.alias == endpoint:
@@ -176,6 +195,12 @@ def load_from_server(
176195
url,
177196
redis_connection
178197
)
198+
# delete all the old data that has saved in Redis using redis_graph.
199+
# It will remove duplicate data from Redis.
200+
for key in redis_connection.keys():
201+
if "fs:" not in key.decode("utf8"):
202+
redis_connection.delete(key)
203+
# save the new data.
179204
self.redis_graph.commit()
180205
# for node in self.redis_graph.nodes.values():
181206
# print("\n",node.alias)

hydra_redis/hydra_graph.py

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,14 @@
55
from hydrus.hydraspec import doc_maker
66
import hydrus
77
from graphviz import Digraph
8-
from hydra_redis.classes_objects import ClassEndpoints
8+
from hydra_redis.classes_objects import ClassEndpoints,RequestError
99
from hydra_redis.collections_endpoint import CollectionEndpoints
1010
from hydra_redis.redis_proxy import RedisProxy
1111

1212

1313
class InitialGraph:
1414

1515

16-
def final_file(self,url):
17-
"""Open the given url and read and load the Json data.
18-
:param url: given url to access the data from the server.
19-
:return: data loaded from the server.
20-
"""
21-
response = urllib.request.urlopen(url)
22-
return json.loads(response.read().decode('utf-8'))
23-
24-
2516
def get_apistructure(self,entrypoint_node, api_doc):
2617
""" It breaks the endpoint into two parts collection and classes"""
2718
self.collection_endpoints = {}
@@ -68,17 +59,19 @@ def get_endpoints(self,api_doc, redis_connection):
6859
return self.get_apistructure(entrypoint_node, api_doc)
6960

7061

71-
def main(self,new_url,api_doc):
62+
63+
def main(self,new_url,api_doc,check_commit):
7264
redis_connection = RedisProxy()
7365
redis_con = redis_connection.get_connection()
7466
self.url = new_url
7567
self.redis_graph = Graph("apidoc", redis_con)
7668
print("loading... of graph")
7769
self.get_endpoints(api_doc, redis_con)
78-
print("commiting")
79-
self.redis_graph.commit()
80-
# creating whole the graph in redis
81-
print("done!!!!")
70+
if check_commit:
71+
print("commiting")
72+
self.redis_graph.commit()
73+
# creating whole the graph in redis
74+
print("done!!!!")
8275
# uncomment below 2 lines for getting nodes for whole graph
8376
# for node in redis_graph.nodes.values():
8477
# print("\n",node.alias)

0 commit comments

Comments
 (0)