Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

packed node repr helpers #1212

Merged
merged 7 commits into from
May 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Features and Enhancements
- Add POST support to the ``/api/v1/model/norm`` HTTP API endpoint. (`#1207 <https://github.com/vertexproject/synapse/pull/1207>`_)
- Add ``getPropNorm()`` and ``getTypeNorm()`` Telepath API endpoints to the Cortex and CoreApi. (`#1207 <https://github.com/vertexproject/synapse/pull/1207>`_)
- Add list ``length()`` and ``index()`` methods to Storm types. (`#1208 <https://github.com/vertexproject/synapse/pull/1208>`_)
- Add helper functions to ``synapse.lib.node`` for extracting repr values from packed nodes. (`#1212 <https://github.com/vertexproject/synapse/pull/1212>`_)
- Add ``--nodes-only`` to the Cmdr ``log`` command to only record raw nodes. (`#1213 <https://github.com/vertexproject/synapse/pull/1213>`_)

Bugfixes
Expand Down
22 changes: 8 additions & 14 deletions synapse/cmds/cortex.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,23 +308,20 @@ def _onNode(self, mesg):

node = mesg[1]
opts = node[1].pop('_opts', {})
formname = node[0][0]

formvalu = node[1].get('repr')
if formvalu is None:
formvalu = str(node[0][1])

if opts.get('raw'):
self.printf(repr(node))
return

formname, formvalu = s_node.reprNdef(node)

self.printf(f'{formname}={formvalu}')

if not opts.get('hide-props'):

for name, valu in sorted(node[1]['props'].items()):
for name in sorted(s_node.props(node).keys()):

valu = node[1]['reprs'].get(name, valu)
valu = s_node.reprProp(node, name)

if name[0] != '.':
name = ':' + name
Expand All @@ -335,14 +332,11 @@ def _onNode(self, mesg):

for tag in sorted(s_node.tags(node, leaf=True)):

valu = node[1]['tags'].get(tag)
if valu == (None, None):
valu = s_node.reprTag(node, tag)
if valu:
self.printf(f' #{tag} = {valu}')
else:
self.printf(f' #{tag}')
continue

mint = s_time.repr(valu[0])
maxt = s_time.repr(valu[1])
self.printf(f' #{tag} = ({mint}, {maxt})')

def _onInit(self, mesg):
pass
Expand Down
87 changes: 85 additions & 2 deletions synapse/lib/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,10 +704,10 @@ def tagged(pode, tag):

def ndef(pode):
'''
Return a node definition (<form>,<valu> tuple from the node.
Return a node definition (<form>,<valu>) tuple from the node.

Args:
node (tuple): A packed node.
pode (tuple): A packed node.

Returns:
((str,obj)): The (<form>,<valu>) tuple for the node
Expand All @@ -725,3 +725,86 @@ def iden(pode):
str: The node iden.
'''
return pode[1].get('iden')

def reprNdef(pode):
'''
Get the ndef of the pode with a human readable value.

Args:
pode (tuple): A packed node.

Notes:
The human readable value is only available if the node came from a
storm query execution where the ``repr`` key was passed into the
``opts`` argument with a True value.

Returns:
(str, str): A tuple of form and the human readable value.

'''
((form, valu), info) = pode
formvalu = info.get('repr')
if formvalu is None:
formvalu = str(valu)
return form, formvalu

def reprProp(pode, prop):
'''
Get the human readable value for a secondary property from the pode.

Args:
pode (tuple): A packed node.
prop:

Notes:
The human readable value is only available if the node came from a
storm query execution where the ``repr`` key was passed into the
``opts`` argument with a True value.

The prop argument may be the full property name (foo:bar:baz), relative
property name (:baz) , or the unadorned property name (baz).

Returns:
str: The human readable property value. If the property is not present, returns None.
'''
form = pode[0][0]
if prop.startswith(form):
prop = prop[len(form):]
if prop[0] == ':':
prop = prop[1:]
opropvalu = pode[1].get('props').get(prop)
if opropvalu is None:
return None
propvalu = pode[1].get('reprs', {}).get(prop)
if propvalu is None:
return str(opropvalu)
return propvalu

def reprTag(pode, tag):
'''
Get the human readable value for the tag timestamp from the pode.

Args:
pode (tuple): A packed node.
tag (str): The tag to get the value for.

Notes:
The human readable value is only available if the node came from a
storm query execution where the ``repr`` key was passed into the
``opts`` argument with a True value.

If the tag does not have a timestamp, this returns a empty string.

Returns:
str: The human readable value for the tag. If the tag is not present, returns None.
'''
tag = tag.lstrip('#')
valu = pode[1]['tags'].get(tag)
if valu is None:
return None
if valu == (None, None):
return ''
mint = s_time.repr(valu[0])
maxt = s_time.repr(valu[1])
valu = f'({mint}, {maxt})'
return valu
25 changes: 22 additions & 3 deletions synapse/tests/test_lib_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,17 +173,26 @@ async def test_tags(self):
async def test_helpers(self):
form = 'test:str'
valu = 'cool'
props = {'tick': 12345}
props = {'tick': 12345,
'hehe': 'hehe',
}
tval = (None, None)

async with self.getTestCore() as core:
async with await core.snap() as snap:
node = await snap.addNode(form, valu, props=props)
await node.addTag('test.foo.bar.duck', tval)
await node.addTag('test.foo.baz', tval)
await node.addTag('test.foo.time', ('2016', '2019'))
pode = node.pack(dorepr=True)

node2 = await snap.addNode('test:int', '1234')
pode2 = node2.pack(dorepr=True)

self.eq(s_node.ndef(pode), ('test:str', 'cool'))
self.eq(s_node.reprNdef(pode), ('test:str', 'cool'))
self.eq(s_node.ndef(pode2), ('test:int', 1234))
self.eq(s_node.reprNdef(pode2), ('test:int', '1234'))

e = 'bf1198c5f28dae61d595434b0788dd6f7206b1e62d06b0798e012685f1abc85d'
self.eq(s_node.iden(pode), e)
Expand All @@ -193,14 +202,24 @@ async def test_helpers(self):
self.true(s_node.tagged(pode, 'test.foo.bar.duck'))
self.false(s_node.tagged(pode, 'test.foo.bar.newp'))

self.len(2, s_node.tags(pode, leaf=True))
self.len(5, s_node.tags(pode))
self.len(3, s_node.tags(pode, leaf=True))
self.len(6, s_node.tags(pode))
self.eq(s_node.reprTag(pode, '#test.foo.bar'), '')
self.eq(s_node.reprTag(pode, '#test.foo.time'), '(2016/01/01 00:00:00.000, 2019/01/01 00:00:00.000)')
self.none(s_node.reprTag(pode, 'test.foo.newp'))

self.eq(s_node.prop(pode, 'hehe'), 'hehe')
self.eq(s_node.prop(pode, 'tick'), 12345)
self.eq(s_node.prop(pode, ':tick'), 12345)
self.eq(s_node.prop(pode, 'test:str:tick'), 12345)
self.none(s_node.prop(pode, 'newp'))

self.eq(s_node.reprProp(pode, 'hehe'), 'hehe')
self.eq(s_node.reprProp(pode, 'tick'), '1970/01/01 00:00:12.345')
self.eq(s_node.reprProp(pode, ':tick'), '1970/01/01 00:00:12.345')
self.eq(s_node.reprProp(pode, 'test:str:tick'), '1970/01/01 00:00:12.345')
self.none(s_node.reprProp(pode, 'newp'))

props = s_node.props(pode)
self.isin('.created', props)
self.isin('tick', props)
Expand Down
1 change: 1 addition & 0 deletions synapse/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ def indx(self, norm):
('bar', ('ndef', {}), {}),
('baz', ('nodeprop', {}), {}),
('tick', ('test:time', {}), {}),
('hehe', ('str', {}), {}),
)),

('test:migr', {}, (
Expand Down