Skip to content

Commit 2db6352

Browse files
committed
feat: add metadata to knowledge api properties
implementing: ENG-3022
1 parent 5c56653 commit 2db6352

File tree

10 files changed

+475
-278
lines changed

10 files changed

+475
-278
lines changed

Pipfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pytest = "*"
2121
pytest-cov = "*"
2222
authlib = "*"
2323
grpcio-tools = "*"
24-
urllib3 = ">=2.1.0"
24+
urllib3 = "*"
2525

2626
[requires]
2727
python_version = "3.11"

Pipfile.lock

+243-242
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

indykite_sdk/api.py

+33-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"""
44
import argparse
55
import json
6-
from datetime import datetime
76
import uuid
87
import time
98
import requests
@@ -37,10 +36,10 @@
3736
from indykite_sdk.indykite.identity.v1beta2 import attributes_pb2 as attributes
3837
from indykite_sdk.ingest import IngestClient
3938
from indykite_sdk.knowledge import KnowledgeClient
40-
from indykite_sdk.model.identity_knowledge import Node as NodeModel, Return as ReturnModel
39+
from indykite_sdk.model.identity_knowledge import Node as NodeModel, Metadata
4140
from indykite_sdk.utils import credentials_config
4241
from indykite_sdk.identity import helper
43-
from indykite_sdk.utils.message_to_value import arg_to_value
42+
from indykite_sdk.utils.message_to_value import arg_to_value, param_to_value
4443
from indykite_sdk import api_helper
4544

4645

@@ -696,6 +695,7 @@ def main():
696695
list_identities_by_property_parser = subparsers.add_parser("list_identities_by_property")
697696
list_nodes_by_property_parser = subparsers.add_parser("list_nodes_by_property")
698697
get_property_parser = subparsers.add_parser("get_property")
698+
get_metadata_parser = subparsers.add_parser("get_metadata")
699699
delete_all_nodes_parser = subparsers.add_parser("delete_all_nodes")
700700
delete_all_nodes_parser.add_argument("node_type", help="DigitalTwin, Resource")
701701

@@ -2820,17 +2820,17 @@ def main():
28202820
responses = client_knowledge.list_identities()
28212821
if responses:
28222822
for response in responses:
2823-
api_helper.print_response(response)
2823+
print(vars(response))
28242824
else:
28252825
print("No result")
28262826

28272827
elif command == "list_nodes_by_property":
28282828
# replace by own values
2829-
property = {"role": "Employee"}
2829+
property = {"color": "white"}
28302830
responses = client_knowledge.list_nodes_by_property(property)
28312831
if responses:
28322832
for response in responses:
2833-
api_helper.print_response(response)
2833+
print(vars(response))
28342834
else:
28352835
print("No result")
28362836

@@ -2840,7 +2840,7 @@ def main():
28402840
responses = client_knowledge.list_identities_by_property(property)
28412841
if responses:
28422842
for response in responses:
2843-
api_helper.print_response(response)
2843+
print(vars(response))
28442844
else:
28452845
print("No result")
28462846

@@ -2861,6 +2861,32 @@ def main():
28612861
property1 = node1.get_property(node1, "last_name")
28622862
print(property1)
28632863

2864+
elif command == "get_metadata":
2865+
metadata1 = Metadata(
2866+
assurance_level=1,
2867+
verification_time=datetime.now().timestamp(),
2868+
source="Myself",
2869+
custom_metadata={
2870+
"customData": param_to_value("customValue")
2871+
}
2872+
)
2873+
node1 = NodeModel(
2874+
id="gid:AAAAFVCygmDZtk8KtTtw9CBopC8",
2875+
external_id="PEpkjOvUJQvqTFw",
2876+
type="individual",
2877+
tags=[],
2878+
properties=[
2879+
{
2880+
"key": "last_name",
2881+
"value": {
2882+
"stringValue": "mushu"
2883+
},
2884+
"metadata": metadata1
2885+
}
2886+
])
2887+
metadata1 = node1.get_metadata(node1, "last_name")
2888+
print(print(metadata1.__dir__()))
2889+
28642890
elif command == "delete_all_nodes":
28652891
responses = client_knowledge.delete_all_with_node_type(args.node_type)
28662892
if responses:

indykite_sdk/indykite/knowledge/objects/v1beta1/ikg_pb2.py

+14-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

indykite_sdk/ingest/ingest_record.py

+46-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import sys
2+
from datetime import datetime
3+
14
from indykite_sdk.indykite.ingest.v1beta3 import ingest_api_pb2 as pb2
25
from indykite_sdk.indykite.ingest.v1beta3 import model_pb2
36
from indykite_sdk.indykite.knowledge.objects.v1beta1 import ikg_pb2
7+
from indykite_sdk.indykite.objects.v1beta2 import value_pb2
48
from indykite_sdk.model.ingest_record import IngestRecordResponse
5-
import sys
69
import indykite_sdk.utils.logger as logger
710
from indykite_sdk.utils.message_to_value import param_to_value
811

@@ -71,19 +74,59 @@ def record_delete(self, id, delete):
7174
return logger.logger_error(exception)
7275

7376

74-
def ingest_property(self, type, value):
77+
def ingest_property(self, type, value, metadata=None):
7578
"""
7679
create Property object
7780
:param self:
7881
:param type:
7982
:param value:
83+
:param metadata:MetadataObject
8084
:return: Property object
8185
"""
8286
sys.excepthook = logger.handle_excepthook
8387
try:
88+
if not type:
89+
raise Exception('type is missing')
90+
if not value:
91+
raise Exception('value is missing')
8492
ip = ikg_pb2.Property(
8593
type=str(type),
86-
value=param_to_value(value)
94+
value=param_to_value(value),
95+
metadata=metadata
96+
)
97+
return ip
98+
except Exception as exception:
99+
return logger.logger_error(exception)
100+
101+
102+
def ingest_metadata(self,
103+
assurance_level=None,
104+
source=None,
105+
custom_metadata={},
106+
verification_time=datetime.now().timestamp(),):
107+
"""
108+
create Metadata object
109+
:param self:
110+
:param assurance_level: 1,2,3
111+
:param verification_time: datetime
112+
:param source: string
113+
:param custom_metadata: dict
114+
:return: Metadata object
115+
"""
116+
sys.excepthook = logger.handle_excepthook
117+
try:
118+
custom_metadata_dict = {}
119+
if custom_metadata:
120+
custom_metadata_dict = {
121+
k: value_pb2.Value(string_value=str(v))
122+
for k, v in custom_metadata.items()
123+
}
124+
ip = ikg_pb2.Metadata(
125+
assurance_level=assurance_level,
126+
verification_time=verification_time,
127+
source=source,
128+
custom_metadata=custom_metadata_dict
129+
87130
)
88131
return ip
89132
except Exception as exception:

indykite_sdk/knowledge/identity_knowledge.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from indykite_sdk.indykite.objects.v1beta2 import value_pb2
88
import indykite_sdk.utils.logger as logger
99
from indykite_sdk.ingest import IngestClient
10+
from indykite_sdk.utils.message_to_value import param_to_value
1011

1112

1213
def identity_knowledge_read(self, query, input_params={}, returns=[]):
@@ -45,6 +46,8 @@ def get_node_by_id(self, id , is_identity=False):
4546
"""
4647
sys.excepthook = logger.handle_excepthook
4748
try:
49+
if not id:
50+
raise Exception('id is missing')
4851
label = "Resource"
4952
if is_identity:
5053
label = "DigitalTwin"
@@ -166,7 +169,7 @@ def list_nodes_by_property(self, property, is_identity=False):
166169
"""
167170
list all nodes, DTs like resources
168171
:param self:
169-
:param property: Knowledge Object Property
172+
:param property: dict key/value
170173
:param is_identity: boolean
171174
:return: list of Node objects
172175
"""
@@ -176,7 +179,8 @@ def list_nodes_by_property(self, property, is_identity=False):
176179
label = "Resource"
177180
if is_identity:
178181
label = "DigitalTwin"
179-
query: str = "MATCH (n:{0}) WHERE n.{1} = ${2}".format(label, k, k)
182+
query: str = ("MATCH (n:{0}) -[:HAS]->(p:Property) "
183+
"WHERE p.type = '{1}' and p.value = '{2}'").format(label, str(k), v)
180184
params = {k: v}
181185
returns = [model_pb2.Return(variable="n")]
182186
identity_knowledge_response = self.stub.IdentityKnowledgeRead(
@@ -224,7 +228,7 @@ def request_input_params(input_params):
224228
:return: dict input_params_dict
225229
"""
226230
input_params_dict = {
227-
k: value_pb2.Value(string_value=str(v))
231+
k: param_to_value(v)
228232
for k, v in input_params.items()
229233
}
230234
return input_params_dict

indykite_sdk/model/identity_knowledge.py

+41-3
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,24 @@ def get_property(cls, node, property):
6969
return res[0].get('stringValue', None)
7070
return None
7171

72-
def __init__(self, id=None, external_id=None, type=None, tags=None, create_time=None, update_time=None,
72+
@classmethod
73+
def get_metadata(cls, node, property):
74+
"""
75+
get the property metadata from the node's list of properties
76+
:param cls:
77+
:param node: node object
78+
:param property: string
79+
:return: value if found, None if not found
80+
"""
81+
if not node.properties:
82+
return None
83+
res = [p for p in node.properties if p['key'] == property]
84+
if len(res) > 0:
85+
return res[0].get('metadata', None)
86+
return None
87+
88+
def __init__(self, id=None, external_id=None, type=None,
89+
tags=None, create_time=None, update_time=None,
7390
properties=None, is_identity=None):
7491
self.id = id
7592
self.external_id = external_id
@@ -121,12 +138,33 @@ def deserialize(cls, property):
121138
return None
122139
return Property(
123140
property.type if property.type else None,
124-
grpc_to_value(property.value) if property.value else None
141+
grpc_to_value(property.value) if property.value else None,
142+
property.metadata if property.metadata else None,
125143
)
126144

127-
def __init__(self, type=None, value=None):
145+
def __init__(self, type=None, value=None, metadata=None):
128146
self.type = type
129147
self.value = value
148+
self.metadata = metadata
149+
150+
151+
class Metadata:
152+
@classmethod
153+
def deserialize(cls, metadata):
154+
if metadata is None:
155+
return None
156+
return Metadata(
157+
metadata.assurance_level if hasattr(metadata, 'assurance_level') else None,
158+
metadata.verification_time if hasattr(metadata, 'verification_time') else None,
159+
metadata.source if hasattr(metadata, 'source') else None,
160+
metadata.custom_metadata if hasattr(metadata, 'custom_metadata') else {}
161+
)
162+
163+
def __init__(self, assurance_level=None, verification_time=None, source=None, custom_metadata={}):
164+
self.assurance_level = assurance_level
165+
self.verification_time = verification_time
166+
self.source = source
167+
self.custom_metadata = custom_metadata
130168

131169

132170
class Return:

indykite_sdk/utils/message_to_value.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def arg_to_value(value):
126126

127127
def param_to_value(v):
128128
if not v:
129-
return value.Value(null_value=value)
129+
return None
130130

131131
if isinstance(v, int):
132132
return value.Value(integer_value=v)

tests/test_ingest.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def test_ingest_record_digital_twin_success():
1010
record_id = "745898"
1111
external_id = "external-dt-id345"
1212
type = "Owner"
13-
ingest_property = client.ingest_property("customProp", "741")
13+
ingest_property = client.ingest_property("customProp2", "742")
1414
assert isinstance(ingest_property, ikg_pb2.Property)
1515
properties = [ingest_property]
1616
upsert = client.upsert_data_node(
@@ -164,12 +164,20 @@ def test_delete_record_relationship_property():
164164
assert isinstance(response, IngestRecordResponse)
165165

166166

167-
def test_ingest_property(capsys):
167+
def test_ingest_property_no_type(capsys):
168168
client = IngestClient()
169169
assert client is not None
170-
ing_property = client.ingest_property([],[])
170+
ing_property = client.ingest_property("","")
171171
captured = capsys.readouterr()
172-
assert "ERROR" in captured.err
172+
assert "type is missing" in captured.err
173+
174+
175+
def test_ingest_property_no_value(capsys):
176+
client = IngestClient()
177+
assert client is not None
178+
ing_property = client.ingest_property("role","")
179+
captured = capsys.readouterr()
180+
assert "value is missing" in captured.err
173181

174182

175183
def test_upsert_node_error(capsys):

0 commit comments

Comments
 (0)