synapse 2.213.0__py311-none-any.whl → 2.214.0__py311-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synapse might be problematic. Click here for more details.

Files changed (68) hide show
  1. synapse/cortex.py +37 -6
  2. synapse/daemon.py +6 -6
  3. synapse/exc.py +13 -1
  4. synapse/lib/aha.py +5 -0
  5. synapse/lib/ast.py +2 -6
  6. synapse/lib/boss.py +47 -2
  7. synapse/lib/cell.py +193 -3
  8. synapse/lib/certdir.py +44 -1
  9. synapse/lib/cmd.py +24 -0
  10. synapse/lib/coro.py +8 -2
  11. synapse/lib/drive.py +7 -2
  12. synapse/lib/link.py +11 -3
  13. synapse/lib/schemas.py +1 -1
  14. synapse/lib/snap.py +76 -65
  15. synapse/lib/storm.py +2 -1
  16. synapse/lib/stormlib/imap.py +3 -2
  17. synapse/lib/stormlib/spooled.py +1 -0
  18. synapse/lib/task.py +1 -0
  19. synapse/lib/version.py +2 -2
  20. synapse/models/inet.py +5 -0
  21. synapse/tests/files/testpkg_build_docs/docs/bar.rst +15 -0
  22. synapse/tests/files/testpkg_build_docs/docs/foo.rst +4 -0
  23. synapse/tests/files/testpkg_build_docs/storm/commands/testcmd.storm +0 -0
  24. synapse/tests/files/testpkg_build_docs/storm/modules/apimod.storm +0 -0
  25. synapse/tests/files/testpkg_build_docs/storm/modules/testmod.storm +0 -0
  26. synapse/tests/files/testpkg_build_docs/storm/testcmd.storm +5 -0
  27. synapse/tests/files/testpkg_build_docs/testpkg.yaml +69 -0
  28. synapse/tests/test_cortex.py +20 -1
  29. synapse/tests/test_daemon.py +1 -1
  30. synapse/tests/test_exc.py +6 -0
  31. synapse/tests/test_lib_ast.py +69 -14
  32. synapse/tests/test_lib_boss.py +8 -0
  33. synapse/tests/test_lib_cell.py +104 -5
  34. synapse/tests/test_lib_certdir.py +8 -0
  35. synapse/tests/test_lib_coro.py +5 -0
  36. synapse/tests/test_lib_httpapi.py +10 -2
  37. synapse/tests/test_lib_link.py +1 -1
  38. synapse/tests/test_lib_storm.py +121 -1
  39. synapse/tests/test_lib_stormlib_spooled.py +20 -0
  40. synapse/tests/test_lib_types.py +1 -1
  41. synapse/tests/test_model_inet.py +7 -0
  42. synapse/tests/test_telepath.py +32 -5
  43. synapse/tests/test_tools_axon.py +304 -0
  44. synapse/tests/test_tools_cortex_layer.py +419 -0
  45. synapse/tests/test_tools_demote.py +114 -0
  46. synapse/tests/test_tools_pkgs_gendocs.py +100 -0
  47. synapse/tests/test_tools_shutdown.py +95 -0
  48. synapse/tests/test_utils.py +22 -1
  49. synapse/tests/utils.py +44 -29
  50. synapse/tools/aha/easycert.py +2 -0
  51. synapse/tools/aha/enroll.py +3 -0
  52. synapse/tools/axon/__init__.py +0 -0
  53. synapse/tools/axon/dump.py +155 -0
  54. synapse/tools/axon/load.py +89 -0
  55. synapse/tools/cortex/__init__.py +0 -0
  56. synapse/tools/cortex/layer/__init__.py +0 -0
  57. synapse/tools/cortex/layer/dump.py +184 -0
  58. synapse/tools/cortex/layer/load.py +129 -0
  59. synapse/tools/demote.py +52 -0
  60. synapse/tools/healthcheck.py +1 -1
  61. synapse/tools/pkgs/gendocs.py +176 -0
  62. synapse/tools/pkgs/pandoc_filter.py +79 -0
  63. synapse/tools/shutdown.py +52 -0
  64. {synapse-2.213.0.dist-info → synapse-2.214.0.dist-info}/METADATA +1 -1
  65. {synapse-2.213.0.dist-info → synapse-2.214.0.dist-info}/RECORD +68 -45
  66. {synapse-2.213.0.dist-info → synapse-2.214.0.dist-info}/WHEEL +0 -0
  67. {synapse-2.213.0.dist-info → synapse-2.214.0.dist-info}/licenses/LICENSE +0 -0
  68. {synapse-2.213.0.dist-info → synapse-2.214.0.dist-info}/top_level.txt +0 -0
@@ -2192,17 +2192,24 @@ class AstTest(s_test.SynTest):
2192
2192
  self.eq(nodes[1].ndef, ('test:str', 'init2'))
2193
2193
  self.eq(nodes[1].get('hehe'), 'hi')
2194
2194
 
2195
- # Non-runtsafe init fails to execute
2195
+ # Non-runtsafe values allowed in init
2196
+ q = '''
2197
+ init {
2198
+ [test:str=cool :hehe=runtsafety]
2199
+ $lib.print(:hehe)
2200
+ }
2201
+ '''
2202
+ msgs = await core.stormlist(q)
2203
+ self.stormIsInPrint('runtsafety', msgs)
2204
+
2205
+ # Non-runtsafe init doesn't create the node
2196
2206
  q = '''
2197
2207
  test:str^=init +:hehe $hehe=:hehe
2198
2208
  init {
2199
2209
  [test:str=$hehe]
2200
2210
  }
2201
2211
  '''
2202
- msgs = await core.stormlist(q)
2203
- erfo = [m for m in msgs if m[0] == 'err'][0]
2204
- self.eq(erfo[1][0], 'StormRuntimeError')
2205
- self.eq(erfo[1][1].get('mesg'), 'Init block query must be runtsafe')
2212
+ self.len(2, await core.nodes(q))
2206
2213
 
2207
2214
  # Runtsafe init works and can yield nodes, this has inbound nodes as well
2208
2215
  q = '''
@@ -2231,7 +2238,7 @@ class AstTest(s_test.SynTest):
2231
2238
  self.eq(nodes[1].ndef, ('test:str', 'fini2'))
2232
2239
  self.eq(nodes[1].get('hehe'), 'hehe')
2233
2240
 
2234
- # Non-runtsafe fini example which fails
2241
+ # Non-runtsafe fini example
2235
2242
  q = '''
2236
2243
  [test:str=fini3 :hehe="number3"]
2237
2244
  $hehe=:hehe
@@ -2239,10 +2246,10 @@ class AstTest(s_test.SynTest):
2239
2246
  [(test:str=fini4 :hehe=$hehe)]
2240
2247
  }
2241
2248
  '''
2242
- msgs = await core.stormlist(q)
2243
- erfo = [m for m in msgs if m[0] == 'err'][0]
2244
- self.eq(erfo[1][0], 'StormRuntimeError')
2245
- self.eq(erfo[1][1].get('mesg'), 'Fini block query must be runtsafe')
2249
+ nodes = await core.nodes(q)
2250
+ self.len(2, nodes)
2251
+ for node in nodes:
2252
+ self.eq('number3', node.get('hehe'))
2246
2253
 
2247
2254
  # Tally use - case example for counting
2248
2255
  q = '''
@@ -2413,12 +2420,21 @@ class AstTest(s_test.SynTest):
2413
2420
  self.stormIsInPrint('blorp', msgs)
2414
2421
 
2415
2422
  q = '''
2416
- [test:str=latte :hehe=milk] $beep=:hehe | spin | empty { $lib.print($beep) }
2423
+ [test:str=latte :hehe=milk] $beep=:hehe | spin | empty { $lib.print($beep) $lib.print(ok) }
2417
2424
  '''
2418
2425
  msgs = await core.stormlist(q)
2419
2426
  nodes = [m[1] for m in msgs if m[0] == 'node']
2420
2427
  self.len(0, nodes)
2421
- self.stormIsInErr('Empty block query must be runtsafe', msgs)
2428
+ self.stormIsInPrint('ok', msgs)
2429
+ self.stormNotInPrint('milk', msgs)
2430
+
2431
+ q = '''
2432
+ empty { [test:str=runtsafety :hehe=optional] $beep=:hehe $lib.print($beep) }
2433
+ '''
2434
+ msgs = await core.stormlist(q)
2435
+ nodes = [m[1] for m in msgs if m[0] == 'node']
2436
+ self.len(1, nodes)
2437
+ self.stormIsInPrint('optional', msgs)
2422
2438
 
2423
2439
  q = '''
2424
2440
  function foo() {
@@ -3224,11 +3240,11 @@ class AstTest(s_test.SynTest):
3224
3240
  off, end = errm[1][1]['highlight']['offsets']
3225
3241
  self.eq(':foo:bar', text[off:end])
3226
3242
 
3227
- text = 'init { $foo = :bar }'
3243
+ text = 'init { $foo = $bar }'
3228
3244
  msgs = await core.stormlist(text)
3229
3245
  errm = [m for m in msgs if m[0] == 'err'][0]
3230
3246
  off, end = errm[1][1]['highlight']['offsets']
3231
- self.eq(':bar', text[off:end])
3247
+ self.eq('bar', text[off:end])
3232
3248
 
3233
3249
  text = 'inet:ipv5'
3234
3250
  msgs = await core.stormlist(text)
@@ -3554,6 +3570,45 @@ class AstTest(s_test.SynTest):
3554
3570
 
3555
3571
  self.len(1, nodes)
3556
3572
 
3573
+ async def test_ast_subgraph_multipivot(self):
3574
+ async with self.getTestCore() as core:
3575
+ guid = s_common.guid()
3576
+ await core.nodes('''[
3577
+ (test:guid=$guid :size=1234 :tick=now) +(refs)> {[test:str=blorp]}
3578
+ ]''', opts={'vars': {'guid': guid}})
3579
+
3580
+ opts = {
3581
+ 'graph': {
3582
+ 'pivots': ('-> *', '-(refs)> *'),
3583
+ 'refs': True,
3584
+ 'degrees': 1
3585
+ }
3586
+ }
3587
+
3588
+ nodes = []
3589
+ async with await core.snap() as snap:
3590
+ async for node, path in snap.storm('test:guid', opts=opts):
3591
+ nodes.append(node)
3592
+
3593
+ opts = {
3594
+ 'graph': {
3595
+ 'pivots': ('-(refs)> *', '-> *'),
3596
+ 'refs': True,
3597
+ 'degrees': 1
3598
+ }
3599
+ }
3600
+ nodes2 = []
3601
+ async with await core.snap() as snap:
3602
+ async for node, path in snap.storm('test:guid', opts=opts):
3603
+ nodes2.append(node)
3604
+
3605
+ self.eq(set(n.iden() for n in nodes), set(n.iden() for n in nodes2))
3606
+ self.len(3, nodes)
3607
+ ndefs = [n.ndef for n in nodes]
3608
+ self.isin(('test:guid', guid), ndefs)
3609
+ self.isin(('test:str', 'blorp'), ndefs)
3610
+ self.isin(('test:int', 1234), ndefs)
3611
+
3557
3612
  async def test_ast_double_init_fini(self):
3558
3613
  async with self.getTestCore() as core:
3559
3614
  q = '''
@@ -62,3 +62,11 @@ class BossTest(s_test.SynTest):
62
62
  coro = boss.schedCoro(double_promote())
63
63
  self.true(await stream.wait(timeout=6))
64
64
  await coro
65
+
66
+ async with boss.shutdown_lock:
67
+ with self.raises(s_exc.ShuttingDown):
68
+ boss.reqNotShut()
69
+
70
+ boss.is_shutdown = True
71
+ with self.raises(s_exc.ShuttingDown):
72
+ boss.reqNotShut()
@@ -187,6 +187,20 @@ testDataSchema_v1 = {
187
187
  'size': {'type': 'number'},
188
188
  'stuff': {'type': ['number', 'null'], 'default': None},
189
189
  'woot': {'type': 'string'},
190
+ 'blorp': {
191
+ 'type': 'object',
192
+ 'properties': {
193
+ 'bleep': {
194
+ 'type': 'array',
195
+ 'items': {
196
+ 'type': 'object',
197
+ 'properties': {
198
+ 'neato': {'type': 'string'}
199
+ }
200
+ }
201
+ }
202
+ }
203
+ }
190
204
  },
191
205
  'required': ['type', 'size', 'woot'],
192
206
  'additionalProperties': False,
@@ -244,13 +258,21 @@ class CellTest(s_t_utils.SynTest):
244
258
  with self.raises(s_exc.BadVersion):
245
259
  await cell.drive.setTypeSchema('woot', testDataSchema_v0, vers=0)
246
260
 
247
- info = {'name': 'win32k.sys', 'type': 'woot'}
261
+ info = {'name': 'win32k.sys', 'type': 'woot', 'perm': {'users': {}}}
248
262
  info = await cell.addDriveItem(info, reldir=rootdir)
263
+ self.notin('perm', info)
264
+ self.eq(info[0]['permissions'], {
265
+ 'users': {},
266
+ 'roles': {}
267
+ })
249
268
 
250
269
  iden = info[-1].get('iden')
251
270
 
252
271
  tick = s_common.now()
253
272
  rootuser = cell.auth.rootuser.iden
273
+ fooser = await cell.auth.addUser('foo')
274
+ neatrole = await cell.auth.addRole('neatrole')
275
+ await fooser.grant(neatrole.iden)
254
276
 
255
277
  with self.raises(s_exc.SchemaViolation):
256
278
  versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
@@ -305,6 +327,10 @@ class CellTest(s_t_utils.SynTest):
305
327
  info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'haha', 'size': 17, 'stuff': 15})
306
328
  self.eq(versinfo, (await cell.getDriveData(iden))[0])
307
329
 
330
+ await cell.setDriveItemProp(iden, versinfo, ('stuff',), 1234)
331
+ data = await cell.getDriveData(iden)
332
+ self.eq(data[1]['stuff'], 1234)
333
+
308
334
  # This will be done by the cell in a cell storage version migration...
309
335
  async def migrate_v1(info, versinfo, data):
310
336
  data['woot'] = 'woot'
@@ -312,6 +338,37 @@ class CellTest(s_t_utils.SynTest):
312
338
 
313
339
  await cell.drive.setTypeSchema('woot', testDataSchema_v1, migrate_v1)
314
340
 
341
+ versinfo['version'] = (1, 1, 1)
342
+ await cell.setDriveItemProp(iden, versinfo, 'stuff', 3829)
343
+ data = await cell.getDriveData(iden)
344
+ self.eq(data[0]['version'], (1, 1, 1))
345
+ self.eq(data[1]['stuff'], 3829)
346
+
347
+ await self.asyncraises(s_exc.NoSuchIden, cell.setDriveItemProp(s_common.guid(), versinfo, ('lolnope',), 'not real'))
348
+
349
+ await self.asyncraises(s_exc.BadArg, cell.setDriveItemProp(iden, versinfo, ('blorp', 0, 'neato'), 'my special string'))
350
+ data[1]['blorp'] = {
351
+ 'bleep': [{'neato': 'thing'}]
352
+ }
353
+ info, versinfo = await cell.setDriveData(iden, versinfo, data[1])
354
+ now = s_common.now()
355
+ versinfo['updated'] = now
356
+ await cell.setDriveItemProp(iden, versinfo, ('blorp', 'bleep', 0, 'neato'), 'my special string')
357
+ data = await cell.getDriveData(iden)
358
+ self.eq(now, data[0]['updated'])
359
+ self.eq('my special string', data[1]['blorp']['bleep'][0]['neato'])
360
+
361
+ versinfo['version'] = (1, 2, 1)
362
+ await cell.delDriveItemProp(iden, versinfo, ('blorp', 'bleep', 0, 'neato'))
363
+ vers, data = await cell.getDriveData(iden)
364
+ self.eq((1, 2, 1), vers['version'])
365
+ self.nn(data['blorp']['bleep'][0])
366
+ self.notin('neato', data['blorp']['bleep'][0])
367
+
368
+ await self.asyncraises(s_exc.NoSuchIden, cell.delDriveItemProp(s_common.guid(), versinfo, 'blorp'))
369
+
370
+ self.none(await cell.delDriveItemProp(iden, versinfo, ('lolnope', 'nopath')))
371
+
315
372
  versinfo, data = await cell.getDriveData(iden, vers=(1, 0, 0))
316
373
  self.eq('woot', data.get('woot'))
317
374
 
@@ -325,10 +382,10 @@ class CellTest(s_t_utils.SynTest):
325
382
  await cell.getDriveInfo(iden, typename='newp')
326
383
 
327
384
  self.nn(await cell.getDriveInfo(iden))
328
- self.len(2, [vers async for vers in cell.getDriveDataVersions(iden)])
385
+ self.len(4, [vers async for vers in cell.getDriveDataVersions(iden)])
329
386
 
330
387
  await cell.delDriveData(iden)
331
- self.len(1, [vers async for vers in cell.getDriveDataVersions(iden)])
388
+ self.len(3, [vers async for vers in cell.getDriveDataVersions(iden)])
332
389
 
333
390
  await cell.delDriveInfo(iden)
334
391
 
@@ -352,8 +409,12 @@ class CellTest(s_t_utils.SynTest):
352
409
  baziden = pathinfo[2].get('iden')
353
410
  self.eq(pathinfo, await cell.drive.getItemPath(baziden))
354
411
 
355
- info = await cell.setDriveInfoPerm(baziden, {'users': {rootuser: 3}, 'roles': {}})
356
- self.eq(3, info['perm']['users'][rootuser])
412
+ info = await cell.setDriveInfoPerm(baziden, {'users': {rootuser: s_cell.PERM_ADMIN}, 'roles': {}})
413
+ # make sure drive perms work with easy perms
414
+ self.true(cell._hasEasyPerm(info, cell.auth.rootuser, s_cell.PERM_ADMIN))
415
+ # defaults to READ
416
+ self.true(cell._hasEasyPerm(info, fooser, s_cell.PERM_READ))
417
+ self.false(cell._hasEasyPerm(info, fooser, s_cell.PERM_EDIT))
357
418
 
358
419
  with self.raises(s_exc.NoSuchIden):
359
420
  # s_drive.rootdir is all 00s... ;)
@@ -648,6 +709,44 @@ class CellTest(s_t_utils.SynTest):
648
709
  with self.raises(s_exc.NeedConfValu):
649
710
  await echo.reqAhaProxy()
650
711
 
712
+ async def test_cell_drive_perm_migration(self):
713
+ async with self.getRegrCore('drive-perm-migr') as core:
714
+ item = await core.getDrivePath('driveitemdefaultperms')
715
+ self.len(1, item)
716
+ self.notin('perm', item)
717
+ self.eq(item[0]['permissions'], {'users': {}, 'roles': {}})
718
+
719
+ ldog = await core.auth.getRoleByName('littledog')
720
+ bdog = await core.auth.getRoleByName('bigdog')
721
+
722
+ louis = await core.auth.getUserByName('lewis')
723
+ tim = await core.auth.getUserByName('tim')
724
+ mj = await core.auth.getUserByName('mj')
725
+
726
+ item = await core.getDrivePath('permfolder/driveitemwithperms')
727
+ self.len(2, item)
728
+ self.notin('perm', item[0])
729
+ self.notin('perm', item[1])
730
+ self.eq(item[0]['permissions'], {'users': {tim.iden: s_cell.PERM_ADMIN}, 'roles': {}})
731
+ self.eq(item[1]['permissions'], {
732
+ 'users': {
733
+ mj.iden: s_cell.PERM_ADMIN
734
+ },
735
+ 'roles': {
736
+ ldog.iden: s_cell.PERM_READ,
737
+ bdog.iden: s_cell.PERM_EDIT,
738
+ },
739
+ 'default': s_cell.PERM_DENY
740
+ })
741
+
742
+ # make sure it's all good with easy perms
743
+ self.true(core._hasEasyPerm(item[0], tim, s_cell.PERM_ADMIN))
744
+ self.false(core._hasEasyPerm(item[0], mj, s_cell.PERM_EDIT))
745
+
746
+ self.true(core._hasEasyPerm(item[1], mj, s_cell.PERM_ADMIN))
747
+ self.true(core._hasEasyPerm(item[1], tim, s_cell.PERM_READ))
748
+ self.true(core._hasEasyPerm(item[1], louis, s_cell.PERM_EDIT))
749
+
651
750
  async def test_cell_unix_sock(self):
652
751
 
653
752
  async with self.getTestCore() as core:
@@ -576,6 +576,10 @@ class CertDirTest(s_t_utils.SynTest):
576
576
  key = cdir.getHostKey(hostname)
577
577
  self.basic_assertions(cdir, cert, key, cacert=cacert)
578
578
 
579
+ self.true(cdir.delHostCsr(hostname))
580
+ self.false(os.path.isfile(path))
581
+ self.false(cdir.delHostCsr(hostname))
582
+
579
583
  # Per RFC5280 common-name has a length of 1 to 64 characters
580
584
  # Do not generate CSRs which exceed that name range.
581
585
  with self.raises(s_exc.CryptoErr) as cm:
@@ -610,6 +614,10 @@ class CertDirTest(s_t_utils.SynTest):
610
614
  key = cdir.getUserKey(username)
611
615
  self.basic_assertions(cdir, cert, key, cacert=cacert)
612
616
 
617
+ self.true(cdir.delUserCsr(username))
618
+ self.false(os.path.isfile(path))
619
+ self.false(cdir.delUserCsr(username))
620
+
613
621
  # Per RFC5280 common-name has a length of 1 to 64 characters
614
622
  # Do not generate CSRs which exceed that name range.
615
623
  with self.raises(s_exc.CryptoErr) as cm:
@@ -235,3 +235,8 @@ class CoroTest(s_t_utils.SynTest):
235
235
  results = await s_coro.await_bg_tasks()
236
236
  self.len(1, results)
237
237
  self.isinstance(results[0], ZeroDivisionError)
238
+
239
+ task = s_coro.create_task(sleep(10))
240
+ self.eq([], await s_coro.await_bg_tasks(timeout=0.001))
241
+ task.cancel()
242
+ self.eq([], await s_coro.await_bg_tasks())
@@ -807,8 +807,8 @@ class HttpApiTest(s_tests.SynTest):
807
807
  opts['user'] = newpuser
808
808
  async with sess.get(f'https://localhost:{port}/api/v1/storm', json=data) as resp:
809
809
  self.eq(resp.status, http.HTTPStatus.BAD_REQUEST)
810
- data = await resp.json()
811
- self.eq(data, {'status': 'err', 'code': 'NoSuchUser',
810
+ info = await resp.json()
811
+ self.eq(info, {'status': 'err', 'code': 'NoSuchUser',
812
812
  'mesg': f'No user found with iden: {newpuser}'})
813
813
 
814
814
  async def test_http_coreinfo(self):
@@ -1615,6 +1615,14 @@ class HttpApiTest(s_tests.SynTest):
1615
1615
  json={'query': '.created', 'opts': {'user': lowuser.iden, 'view': fork}}) as resp:
1616
1616
  self.eq(resp.status, http.HTTPStatus.FORBIDDEN)
1617
1617
 
1618
+ # ShuttingDown precondition failure
1619
+ core.boss.is_shutdown = True
1620
+ async with sess.get(f'https://localhost:{port}/api/v1/storm', json={'query': '.created'}) as resp:
1621
+ self.eq(resp.status, http.HTTPStatus.BAD_REQUEST)
1622
+ info = await resp.json()
1623
+ self.eq(info.get('code'), 'ShuttingDown')
1624
+ core.boss.is_shutdown = False
1625
+
1618
1626
  # check reqvalidstorm with various queries
1619
1627
  tvs = (
1620
1628
  ('test:str=test', {}, 'ok'),
@@ -121,7 +121,7 @@ class LinkTest(s_test.SynTest):
121
121
  self.eq(msg0, ('what', {'k': 1}))
122
122
  evt.set()
123
123
  await asyncio.sleep(0)
124
- with self.getAsyncLoggerStream('synapse.lib.link', 'rx error') as stream:
124
+ with self.getAsyncLoggerStream('synapse.lib.link', 'rx closed unexpectedly') as stream:
125
125
  msg1 = await link.rx()
126
126
  self.true(await stream.wait(6))
127
127
  self.none(msg1)
@@ -130,7 +130,6 @@ class StormTest(s_t_utils.SynTest):
130
130
  self.none(props.get('phone'))
131
131
  self.eq(props.get('name'), 'burrito corp')
132
132
  self.eq(props.get('desc'), 'burritos man')
133
- self.stormIsInWarn('Skipping bad value for prop ou:org:phone: requires a digit string', msgs)
134
133
 
135
134
  await self.asyncraises(s_exc.BadTypeValu, core.addNode(core.auth.rootuser, 'ou:org', {'name': 'org name 77', 'phone': 'lolnope'}, props={'desc': 'an org desc'}))
136
135
 
@@ -173,6 +172,127 @@ class StormTest(s_t_utils.SynTest):
173
172
  nodes = await core.nodes('[ it:exec:proc=(nulltime,) ]')
174
173
  self.len(1, nodes)
175
174
 
175
+ # Recursive gutors
176
+ nodes = await core.nodes('''[
177
+ inet:service:message=({
178
+ 'id': 'foomesg',
179
+ 'channel': {
180
+ 'id': 'foochannel',
181
+ 'platform': {
182
+ 'name': 'fooplatform',
183
+ 'url': 'http://foo.com'
184
+ }
185
+ }
186
+ })
187
+ ]''')
188
+ self.len(1, nodes)
189
+ node = nodes[0]
190
+ self.eq(node.ndef[0], 'inet:service:message')
191
+ self.eq(node.get('id'), 'foomesg')
192
+ self.nn(node.get('channel'))
193
+
194
+ nodes = await core.nodes('inet:service:message -> inet:service:channel')
195
+ self.len(1, nodes)
196
+ node = nodes[0]
197
+ self.eq(node.get('id'), 'foochannel')
198
+ self.nn(node.get('platform'))
199
+
200
+ nodes = await core.nodes('inet:service:message -> inet:service:channel -> inet:service:platform')
201
+ self.len(1, nodes)
202
+ node = nodes[0]
203
+ self.eq(node.get('name'), 'fooplatform')
204
+ self.eq(node.get('url'), 'http://foo.com')
205
+
206
+ nodes = await core.nodes('''
207
+ inet:service:message=({
208
+ 'id': 'foomesg',
209
+ 'channel': {
210
+ 'id': 'foochannel',
211
+ 'platform': {
212
+ 'name': 'fooplatform',
213
+ 'url': 'http://foo.com'
214
+ }
215
+ }
216
+ })
217
+ ''')
218
+ self.len(1, nodes)
219
+ node = nodes[0]
220
+ self.eq(node.ndef[0], 'inet:service:message')
221
+ self.eq(node.get('id'), 'foomesg')
222
+
223
+ nodes = await core.nodes('''[
224
+ inet:service:message=({
225
+ 'id': 'barmesg',
226
+ 'channel': {
227
+ 'id': 'barchannel',
228
+ 'platform': {
229
+ 'name': 'barplatform',
230
+ 'url': 'http://bar.com'
231
+ }
232
+ },
233
+ '$props': {
234
+ 'platform': {
235
+ 'name': 'barplatform',
236
+ 'url': 'http://bar.com'
237
+ }
238
+ }
239
+ })
240
+ ]''')
241
+ self.len(1, nodes)
242
+ node = nodes[0]
243
+ self.eq(node.ndef[0], 'inet:service:message')
244
+ self.eq(node.get('id'), 'barmesg')
245
+ self.nn(node.get('channel'))
246
+
247
+ platguid = node.get('platform')
248
+ self.nn(platguid)
249
+ nodes = await core.nodes('inet:service:message:id=barmesg -> inet:service:channel -> inet:service:platform')
250
+ self.len(1, nodes)
251
+ self.eq(platguid, nodes[0].ndef[1])
252
+
253
+ # No node lifted if no matching node for inner gutor
254
+ self.len(0, await core.nodes('''
255
+ inet:service:message=({
256
+ 'id': 'foomesg',
257
+ 'channel': {
258
+ 'id': 'foochannel',
259
+ 'platform': {
260
+ 'name': 'newp',
261
+ 'url': 'http://foo.com'
262
+ }
263
+ }
264
+ })
265
+ '''))
266
+
267
+ # BadTypeValu comes through from inner gutor
268
+ with self.raises(s_exc.BadTypeValu) as cm:
269
+ await core.nodes('''
270
+ inet:service:message=({
271
+ 'id': 'foomesg',
272
+ 'channel': {
273
+ 'id': 'foochannel',
274
+ 'platform': {
275
+ 'name': 'newp',
276
+ 'url': 'newp'
277
+ }
278
+ }
279
+ })
280
+ ''')
281
+
282
+ self.eq(cm.exception.get('form'), 'inet:service:platform')
283
+ self.eq(cm.exception.get('prop'), 'url')
284
+ self.eq(cm.exception.get('mesg'), 'Bad value for prop inet:service:platform:url: Invalid/Missing protocol')
285
+
286
+ # Ensure inner nodes are not created unless the entire gutor is valid.
287
+ self.len(0, await core.nodes('''[
288
+ inet:service:account?=({
289
+ "id": "bar",
290
+ "platform": {"name": "barplat"},
291
+ "url": "newp"})
292
+ ]'''))
293
+
294
+ self.len(0, await core.nodes('inet:service:platform:name=barplat'))
295
+
176
296
  async def test_lib_storm_jsonexpr(self):
177
297
  async with self.getTestCore() as core:
178
298
 
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import synapse.exc as s_exc
2
3
  import synapse.lib.stormtypes as s_stormtypes
3
4
 
@@ -144,6 +145,20 @@ class StormlibSpooledTest(s_test.SynTest):
144
145
  valu = await core.callStorm(q)
145
146
  self.eq(1500, valu)
146
147
 
148
+ subq = '''{
149
+ $sset = $lib.spooled.set()
150
+ $sset.adds($lib.range(1500))
151
+ return($sset)
152
+ }'''
153
+
154
+ q = '''
155
+ $subset = $lib.storm.eval($text)
156
+ $subset.add(2345)
157
+ return($subset.size())
158
+ '''
159
+ valu = await core.callStorm(q, opts={'vars': {'text': subq}})
160
+ self.eq(1501, valu)
161
+
147
162
  # sad paths
148
163
  # too complex
149
164
  q = '''
@@ -199,3 +214,8 @@ class StormlibSpooledTest(s_test.SynTest):
199
214
  return($set)
200
215
  '''
201
216
  await self.asyncraises(s_exc.StormRuntimeError, core.callStorm(q, {'vars': {'stormnode': stormnode}}))
217
+
218
+ # make sure the various spools got trashed
219
+ path = os.path.join(core.dirn, 'tmp')
220
+ files = [f for f in os.listdir(os.path.join(path))]
221
+ self.len(0, files)
@@ -1320,7 +1320,7 @@ class TypesTest(s_t_utils.SynTest):
1320
1320
  nodes = await core.nodes('test:str:tick=201401*')
1321
1321
  self.eq({node.ndef[1] for node in nodes}, {'a'})
1322
1322
 
1323
- nodes = await core.nodes('test:str:tick*range=("-4200 days", now)')
1323
+ nodes = await core.nodes('test:str:tick*range=("-6000 days", now)')
1324
1324
  self.eq({node.ndef[1] for node in nodes}, {'a', 'b', 'c', 'd'})
1325
1325
 
1326
1326
  opts = {'vars': {'tick': tick, 'tock': tock}}
@@ -3002,17 +3002,20 @@ class InetModelTest(s_t_utils.SynTest):
3002
3002
  :url="https://v.vtx.lk/slack"
3003
3003
  :name="Synapse users slack"
3004
3004
  :tenant={[ inet:service:tenant=({"id": "VS-31337"}) ]}
3005
+ :app={[ inet:service:app=({"id": "app00"}) ]}
3005
3006
  ]
3006
3007
  '''
3007
3008
  nodes = await core.nodes(q)
3008
3009
  self.len(1, nodes)
3009
3010
  self.nn(nodes[0].get('tenant'))
3011
+ self.nn(nodes[0].get('app'))
3010
3012
  self.eq(nodes[0].ndef, ('inet:service:instance', s_common.guid(('vertex', 'slack'))))
3011
3013
  self.eq(nodes[0].get('id'), 'T2XK1223Y')
3012
3014
  self.eq(nodes[0].get('platform'), platform.ndef[1])
3013
3015
  self.eq(nodes[0].get('url'), 'https://v.vtx.lk/slack')
3014
3016
  self.eq(nodes[0].get('name'), 'synapse users slack')
3015
3017
  platinst = nodes[0]
3018
+ app00 = nodes[0].get('app')
3016
3019
 
3017
3020
  q = '''
3018
3021
  [
@@ -3023,6 +3026,7 @@ class InetModelTest(s_t_utils.SynTest):
3023
3026
  :email=blackout@vertex.link
3024
3027
  :profile={ gen.ps.contact.email vertex.employee blackout@vertex.link }
3025
3028
  :tenant={[ inet:service:tenant=({"id": "VS-31337"}) ]}
3029
+ :app={[ inet:service:app=({"id": "a001"}) ]}
3026
3030
  )
3027
3031
 
3028
3032
  (inet:service:account=(visi, account, vertex, slack)
@@ -3037,6 +3041,7 @@ class InetModelTest(s_t_utils.SynTest):
3037
3041
  self.len(2, accounts)
3038
3042
 
3039
3043
  self.nn(accounts[0].get('tenant'))
3044
+ self.nn(accounts[0].get('app'))
3040
3045
 
3041
3046
  profiles = await core.nodes('ps:contact')
3042
3047
  self.len(2, profiles)
@@ -3175,6 +3180,7 @@ class InetModelTest(s_t_utils.SynTest):
3175
3180
  :platform=$platiden
3176
3181
  :instance=$instiden
3177
3182
  :topic=' My Topic '
3183
+ :app={ inet:service:app:id=app00 }
3178
3184
  ]
3179
3185
  '''
3180
3186
  opts = {'vars': {
@@ -3185,6 +3191,7 @@ class InetModelTest(s_t_utils.SynTest):
3185
3191
  nodes = await core.nodes(q, opts=opts)
3186
3192
  self.len(1, nodes)
3187
3193
  self.eq(nodes[0].ndef, ('inet:service:channel', s_common.guid(('general', 'channel', 'vertex', 'slack'))))
3194
+ self.eq(nodes[0].get('app'), app00)
3188
3195
  self.eq(nodes[0].get('name'), 'general')
3189
3196
  self.eq(nodes[0].get('topic'), 'my topic')
3190
3197
  self.eq(nodes[0].get('period'), (1420070400000, 9223372036854775807))