Skip to content

Commit

Permalink
feat: add metadata to knowledge api properties
Browse files Browse the repository at this point in the history
implementing: ENG-3022
  • Loading branch information
cowan-macady committed Mar 7, 2024
1 parent 5c56653 commit 2db6352
Show file tree
Hide file tree
Showing 10 changed files with 475 additions and 278 deletions.
2 changes: 1 addition & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pytest = "*"
pytest-cov = "*"
authlib = "*"
grpcio-tools = "*"
urllib3 = ">=2.1.0"
urllib3 = "*"

[requires]
python_version = "3.11"
485 changes: 243 additions & 242 deletions Pipfile.lock

Large diffs are not rendered by default.

40 changes: 33 additions & 7 deletions indykite_sdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"""
import argparse
import json
from datetime import datetime
import uuid
import time
import requests
Expand Down Expand Up @@ -37,10 +36,10 @@
from indykite_sdk.indykite.identity.v1beta2 import attributes_pb2 as attributes
from indykite_sdk.ingest import IngestClient
from indykite_sdk.knowledge import KnowledgeClient
from indykite_sdk.model.identity_knowledge import Node as NodeModel, Return as ReturnModel
from indykite_sdk.model.identity_knowledge import Node as NodeModel, Metadata
from indykite_sdk.utils import credentials_config
from indykite_sdk.identity import helper
from indykite_sdk.utils.message_to_value import arg_to_value
from indykite_sdk.utils.message_to_value import arg_to_value, param_to_value
from indykite_sdk import api_helper


Expand Down Expand Up @@ -696,6 +695,7 @@ def main():
list_identities_by_property_parser = subparsers.add_parser("list_identities_by_property")
list_nodes_by_property_parser = subparsers.add_parser("list_nodes_by_property")
get_property_parser = subparsers.add_parser("get_property")
get_metadata_parser = subparsers.add_parser("get_metadata")
delete_all_nodes_parser = subparsers.add_parser("delete_all_nodes")
delete_all_nodes_parser.add_argument("node_type", help="DigitalTwin, Resource")

Expand Down Expand Up @@ -2820,17 +2820,17 @@ def main():
responses = client_knowledge.list_identities()
if responses:
for response in responses:
api_helper.print_response(response)
print(vars(response))
else:
print("No result")

elif command == "list_nodes_by_property":
# replace by own values
property = {"role": "Employee"}
property = {"color": "white"}
responses = client_knowledge.list_nodes_by_property(property)
if responses:
for response in responses:
api_helper.print_response(response)
print(vars(response))
else:
print("No result")

Expand All @@ -2840,7 +2840,7 @@ def main():
responses = client_knowledge.list_identities_by_property(property)
if responses:
for response in responses:
api_helper.print_response(response)
print(vars(response))
else:
print("No result")

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

elif command == "get_metadata":
metadata1 = Metadata(
assurance_level=1,
verification_time=datetime.now().timestamp(),
source="Myself",
custom_metadata={
"customData": param_to_value("customValue")
}
)
node1 = NodeModel(
id="gid:AAAAFVCygmDZtk8KtTtw9CBopC8",
external_id="PEpkjOvUJQvqTFw",
type="individual",
tags=[],
properties=[
{
"key": "last_name",
"value": {
"stringValue": "mushu"
},
"metadata": metadata1
}
])
metadata1 = node1.get_metadata(node1, "last_name")
print(print(metadata1.__dir__()))

elif command == "delete_all_nodes":
responses = client_knowledge.delete_all_with_node_type(args.node_type)
if responses:
Expand Down
18 changes: 14 additions & 4 deletions indykite_sdk/indykite/knowledge/objects/v1beta1/ikg_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 46 additions & 3 deletions indykite_sdk/ingest/ingest_record.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import sys
from datetime import datetime

from indykite_sdk.indykite.ingest.v1beta3 import ingest_api_pb2 as pb2
from indykite_sdk.indykite.ingest.v1beta3 import model_pb2
from indykite_sdk.indykite.knowledge.objects.v1beta1 import ikg_pb2
from indykite_sdk.indykite.objects.v1beta2 import value_pb2
from indykite_sdk.model.ingest_record import IngestRecordResponse
import sys
import indykite_sdk.utils.logger as logger
from indykite_sdk.utils.message_to_value import param_to_value

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


def ingest_property(self, type, value):
def ingest_property(self, type, value, metadata=None):
"""
create Property object
:param self:
:param type:
:param value:
:param metadata:MetadataObject
:return: Property object
"""
sys.excepthook = logger.handle_excepthook
try:
if not type:
raise Exception('type is missing')
if not value:
raise Exception('value is missing')
ip = ikg_pb2.Property(
type=str(type),
value=param_to_value(value)
value=param_to_value(value),
metadata=metadata
)
return ip
except Exception as exception:
return logger.logger_error(exception)


def ingest_metadata(self,
assurance_level=None,
source=None,
custom_metadata={},
verification_time=datetime.now().timestamp(),):
"""
create Metadata object
:param self:
:param assurance_level: 1,2,3
:param verification_time: datetime
:param source: string
:param custom_metadata: dict
:return: Metadata object
"""
sys.excepthook = logger.handle_excepthook
try:
custom_metadata_dict = {}
if custom_metadata:
custom_metadata_dict = {
k: value_pb2.Value(string_value=str(v))
for k, v in custom_metadata.items()
}
ip = ikg_pb2.Metadata(
assurance_level=assurance_level,
verification_time=verification_time,
source=source,
custom_metadata=custom_metadata_dict

)
return ip
except Exception as exception:
Expand Down
10 changes: 7 additions & 3 deletions indykite_sdk/knowledge/identity_knowledge.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from indykite_sdk.indykite.objects.v1beta2 import value_pb2
import indykite_sdk.utils.logger as logger
from indykite_sdk.ingest import IngestClient
from indykite_sdk.utils.message_to_value import param_to_value


def identity_knowledge_read(self, query, input_params={}, returns=[]):
Expand Down Expand Up @@ -45,6 +46,8 @@ def get_node_by_id(self, id , is_identity=False):
"""
sys.excepthook = logger.handle_excepthook
try:
if not id:
raise Exception('id is missing')
label = "Resource"
if is_identity:
label = "DigitalTwin"
Expand Down Expand Up @@ -166,7 +169,7 @@ def list_nodes_by_property(self, property, is_identity=False):
"""
list all nodes, DTs like resources
:param self:
:param property: Knowledge Object Property
:param property: dict key/value
:param is_identity: boolean
:return: list of Node objects
"""
Expand All @@ -176,7 +179,8 @@ def list_nodes_by_property(self, property, is_identity=False):
label = "Resource"
if is_identity:
label = "DigitalTwin"
query: str = "MATCH (n:{0}) WHERE n.{1} = ${2}".format(label, k, k)
query: str = ("MATCH (n:{0}) -[:HAS]->(p:Property) "
"WHERE p.type = '{1}' and p.value = '{2}'").format(label, str(k), v)
params = {k: v}
returns = [model_pb2.Return(variable="n")]
identity_knowledge_response = self.stub.IdentityKnowledgeRead(
Expand Down Expand Up @@ -224,7 +228,7 @@ def request_input_params(input_params):
:return: dict input_params_dict
"""
input_params_dict = {
k: value_pb2.Value(string_value=str(v))
k: param_to_value(v)
for k, v in input_params.items()
}
return input_params_dict
Expand Down
44 changes: 41 additions & 3 deletions indykite_sdk/model/identity_knowledge.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,24 @@ def get_property(cls, node, property):
return res[0].get('stringValue', None)
return None

def __init__(self, id=None, external_id=None, type=None, tags=None, create_time=None, update_time=None,
@classmethod
def get_metadata(cls, node, property):
"""
get the property metadata from the node's list of properties
:param cls:
:param node: node object
:param property: string
:return: value if found, None if not found
"""
if not node.properties:
return None
res = [p for p in node.properties if p['key'] == property]
if len(res) > 0:
return res[0].get('metadata', None)
return None

def __init__(self, id=None, external_id=None, type=None,
tags=None, create_time=None, update_time=None,
properties=None, is_identity=None):
self.id = id
self.external_id = external_id
Expand Down Expand Up @@ -121,12 +138,33 @@ def deserialize(cls, property):
return None
return Property(
property.type if property.type else None,
grpc_to_value(property.value) if property.value else None
grpc_to_value(property.value) if property.value else None,
property.metadata if property.metadata else None,
)

def __init__(self, type=None, value=None):
def __init__(self, type=None, value=None, metadata=None):
self.type = type
self.value = value
self.metadata = metadata


class Metadata:
@classmethod
def deserialize(cls, metadata):
if metadata is None:
return None
return Metadata(
metadata.assurance_level if hasattr(metadata, 'assurance_level') else None,
metadata.verification_time if hasattr(metadata, 'verification_time') else None,
metadata.source if hasattr(metadata, 'source') else None,
metadata.custom_metadata if hasattr(metadata, 'custom_metadata') else {}
)

def __init__(self, assurance_level=None, verification_time=None, source=None, custom_metadata={}):
self.assurance_level = assurance_level
self.verification_time = verification_time
self.source = source
self.custom_metadata = custom_metadata


class Return:
Expand Down
2 changes: 1 addition & 1 deletion indykite_sdk/utils/message_to_value.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def arg_to_value(value):

def param_to_value(v):
if not v:
return value.Value(null_value=value)
return None

if isinstance(v, int):
return value.Value(integer_value=v)
Expand Down
16 changes: 12 additions & 4 deletions tests/test_ingest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def test_ingest_record_digital_twin_success():
record_id = "745898"
external_id = "external-dt-id345"
type = "Owner"
ingest_property = client.ingest_property("customProp", "741")
ingest_property = client.ingest_property("customProp2", "742")
assert isinstance(ingest_property, ikg_pb2.Property)
properties = [ingest_property]
upsert = client.upsert_data_node(
Expand Down Expand Up @@ -164,12 +164,20 @@ def test_delete_record_relationship_property():
assert isinstance(response, IngestRecordResponse)


def test_ingest_property(capsys):
def test_ingest_property_no_type(capsys):
client = IngestClient()
assert client is not None
ing_property = client.ingest_property([],[])
ing_property = client.ingest_property("","")
captured = capsys.readouterr()
assert "ERROR" in captured.err
assert "type is missing" in captured.err


def test_ingest_property_no_value(capsys):
client = IngestClient()
assert client is not None
ing_property = client.ingest_property("role","")
captured = capsys.readouterr()
assert "value is missing" in captured.err


def test_upsert_node_error(capsys):
Expand Down

0 comments on commit 2db6352

Please sign in to comment.