synapse 2.207.0__py311-none-any.whl → 2.209.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 (37) hide show
  1. synapse/cortex.py +12 -3
  2. synapse/lib/boss.py +5 -1
  3. synapse/lib/layer.py +23 -1
  4. synapse/lib/modelrev.py +27 -4
  5. synapse/lib/rstorm.py +3 -1
  6. synapse/lib/storm.lark +2 -2
  7. synapse/lib/storm.py +5 -5
  8. synapse/lib/stormlib/auth.py +1 -1
  9. synapse/lib/stormlib/cortex.py +1 -1
  10. synapse/lib/stormlib/vault.py +1 -1
  11. synapse/lib/stormsvc.py +1 -1
  12. synapse/lib/stormtypes.py +31 -22
  13. synapse/lib/task.py +7 -1
  14. synapse/lib/time.py +8 -2
  15. synapse/lib/version.py +2 -2
  16. synapse/lib/view.py +25 -7
  17. synapse/tests/test_cortex.py +4 -4
  18. synapse/tests/test_datamodel.py +1 -1
  19. synapse/tests/test_lib_ast.py +5 -5
  20. synapse/tests/test_lib_boss.py +2 -2
  21. synapse/tests/test_lib_nexus.py +5 -11
  22. synapse/tests/test_lib_rstorm.py +4 -3
  23. synapse/tests/test_lib_storm.py +83 -6
  24. synapse/tests/test_lib_stormhttp.py +8 -8
  25. synapse/tests/test_lib_stormlib_cache.py +2 -2
  26. synapse/tests/test_lib_stormlib_scrape.py +1 -1
  27. synapse/tests/test_lib_stormlib_storm.py +63 -0
  28. synapse/tests/test_lib_stormsvc.py +3 -0
  29. synapse/tests/test_lib_stormtypes.py +171 -12
  30. synapse/tests/test_lib_trigger.py +3 -4
  31. synapse/tests/test_lib_view.py +25 -3
  32. synapse/tests/test_tools_csvtool.py +1 -1
  33. {synapse-2.207.0.dist-info → synapse-2.209.0.dist-info}/METADATA +6 -6
  34. {synapse-2.207.0.dist-info → synapse-2.209.0.dist-info}/RECORD +37 -37
  35. {synapse-2.207.0.dist-info → synapse-2.209.0.dist-info}/WHEEL +1 -1
  36. {synapse-2.207.0.dist-info → synapse-2.209.0.dist-info}/licenses/LICENSE +0 -0
  37. {synapse-2.207.0.dist-info → synapse-2.209.0.dist-info}/top_level.txt +0 -0
@@ -385,19 +385,13 @@ class NexusTest(s_t_utils.SynTest):
385
385
  evnt = asyncio.Event()
386
386
 
387
387
  async with self.getTestCore() as core:
388
- orig = core._nexshands['view:delwithlayer'][0]
389
-
390
- async def holdlock(self, viewiden, layriden, nexsitem, newparent=None):
391
- evnt.set()
392
- await asyncio.sleep(1)
393
- await orig(self, viewiden, layriden, nexsitem, newparent=newparent)
394
-
395
- core._nexshands['view:delwithlayer'] = (holdlock, True)
396
-
397
388
  forkiden = await core.callStorm('return($lib.view.get().fork().iden)')
398
389
 
399
- core.schedCoro(core.delViewWithLayer(forkiden))
400
- await asyncio.wait_for(evnt.wait(), timeout=10)
390
+ # Remove the nexus handler for the fork's write layer to simulate a view delete/edit race
391
+ layriden = core.getView(forkiden).layers[0].iden
392
+ layr = core.nexsroot._nexskids.pop(layriden)
401
393
 
402
394
  with self.raises(s_exc.NoSuchIden):
403
395
  await core.nodes('[ it:dev:str=foo ]', opts={'view': forkiden})
396
+
397
+ core.nexsroot._nexskids[layriden] = layr
@@ -119,7 +119,7 @@ clear_storm_opts = '''
119
119
  .. storm-cortex:: default
120
120
  .. storm-opts:: {"vars": {"foobar": "bar"}}
121
121
  .. storm-clear-http:: true
122
- .. storm:: $lib.print($lib.str.concat($foobar, "bizboz"))
122
+ .. storm:: $lib.print(`{$foobar}bizboz`)
123
123
  '''
124
124
 
125
125
  stormenv = '''
@@ -381,8 +381,9 @@ class RStormLibTest(s_test.SynTest):
381
381
  path = s_common.genpath(dirn, 'clear_storm_opts.rst')
382
382
  with s_common.genfile(path) as fd:
383
383
  fd.write(clear_storm_opts.encode())
384
- with self.raises(s_exc.StormRuntimeError):
385
- text = await get_rst_text(path)
384
+ with self.raises(s_exc.StormRuntimeError) as cm:
385
+ await get_rst_text(path)
386
+ self.eq('Missing variable: foobar', cm.exception.get('mesg'))
386
387
 
387
388
  # boom1 test
388
389
  path = s_common.genpath(dirn, 'boom1.rst')
@@ -308,7 +308,7 @@ class StormTest(s_t_utils.SynTest):
308
308
  ''')
309
309
  self.eq('valu=12', retn)
310
310
 
311
- q = "$hehe=({'k': 'v'}) $fs=$lib.str.format('{v}56', v=$hehe) return((`{$hehe}56`, $fs))"
311
+ q = "$hehe=({'k': 'v'}) $fs=`{$hehe}56` return((`{$hehe}56`, $fs))"
312
312
  retn = await core.callStorm(q)
313
313
  self.eq("{'k': 'v'}56", retn[0])
314
314
  self.eq(retn[0], retn[1])
@@ -322,6 +322,14 @@ class StormTest(s_t_utils.SynTest):
322
322
 
323
323
  self.eq("foo 'bar'", await core.callStorm("$foo=bar return(`foo '{$foo}'`)"))
324
324
  self.eq(r"\'''''bar'''", await core.callStorm(r"$foo=bar return(`\\'\''''{$foo}'''`)"))
325
+ self.eq(r"\bar", await core.callStorm(r"$foo=bar return(`\\{$foo}`)"))
326
+ self.eq(r"\`bar", await core.callStorm(r"$foo=bar return(`\\\`{$foo}`)"))
327
+ self.eq(r"\{bar", await core.callStorm(r"$foo=bar return(`\\\{{$foo}`)"))
328
+ self.eq(r"foo\bar", await core.callStorm(r"$foo=foo $bar=bar return(`{$foo}\\{$bar}`)"))
329
+ self.eq(r"foo \bar", await core.callStorm(r"$foo=foo $bar=bar return(`{$foo} \\{$bar}`)"))
330
+
331
+ with self.raises(s_exc.BadSyntax):
332
+ await core.callStorm(r"$foo=bar return(`\\{{$foo}`)")
325
333
 
326
334
  async def test_lib_storm_emit(self):
327
335
  async with self.getTestCore() as core:
@@ -2179,7 +2187,7 @@ class StormTest(s_t_utils.SynTest):
2179
2187
  [ ou:org=(cov,) ]
2180
2188
 
2181
2189
  { for $i in $lib.range(1001) {
2182
- $prop = $lib.str.format('_test{i}', i=$i)
2190
+ $prop = `_test{$i}`
2183
2191
  [ :$prop = $i
2184
2192
  +#$prop:score = $i
2185
2193
  +($i)> { ou:org=(cov,) }
@@ -2826,6 +2834,75 @@ class StormTest(s_t_utils.SynTest):
2826
2834
  ]
2827
2835
  self.eq(exp, evnts)
2828
2836
 
2837
+ async def test_storm_pkg_onload_active(self):
2838
+ pkg = {
2839
+ 'name': 'testload',
2840
+ 'version': '0.3.0',
2841
+ 'modules': (
2842
+ {
2843
+ 'name': 'testload',
2844
+ 'storm': 'function x() { return((0)) }',
2845
+ },
2846
+ ),
2847
+ 'onload': '''
2848
+ $lib.print(testprint)
2849
+ $lib.warn(testwarn)
2850
+
2851
+ $queue = $lib.queue.gen(onload:test)
2852
+
2853
+ $vers = $lib.globals.get(testload:version, (0))
2854
+ $vers = ($vers + 1)
2855
+ $lib.globals.set(testload:version, $vers)
2856
+ $queue.put($vers)
2857
+ '''
2858
+ }
2859
+
2860
+ with self.getTestDir() as dirn:
2861
+ dirn00 = s_common.gendir(dirn, 'core00')
2862
+ dirn01 = s_common.gendir(dirn, 'core01')
2863
+
2864
+ async with self.getTestCore(dirn=dirn00) as core00:
2865
+
2866
+ waiter = core00.waiter(2, 'core:pkg:onload:start', 'core:pkg:onload:complete')
2867
+
2868
+ await core00.addStormPkg(pkg)
2869
+
2870
+ events = await waiter.wait(timeout=10)
2871
+ self.eq(events, [
2872
+ ('core:pkg:onload:start', {'pkg': 'testload'}),
2873
+ ('core:pkg:onload:complete', {'pkg': 'testload'}),
2874
+ ])
2875
+
2876
+ self.eq((0, 1), await core00.callStorm('return($lib.queue.gen(onload:test).get((0), cull=(false)))'))
2877
+
2878
+ s_tools_backup.backup(dirn00, dirn01)
2879
+
2880
+ async with self.getTestCore(dirn=dirn00) as core00:
2881
+
2882
+ self.eq((1, 2), await core00.callStorm('return($lib.queue.gen(onload:test).get((1), cull=(false)))'))
2883
+
2884
+ conf01 = {'mirror': core00.getLocalUrl()}
2885
+
2886
+ async with self.getTestCore(dirn=dirn01, conf=conf01) as core01:
2887
+
2888
+ await core01.sync()
2889
+
2890
+ waiter = core01.waiter(2, 'core:pkg:onload:start', 'core:pkg:onload:complete')
2891
+
2892
+ await core01.promote()
2893
+
2894
+ events = await waiter.wait(timeout=10)
2895
+ self.eq(events, [
2896
+ ('core:pkg:onload:start', {'pkg': 'testload'}),
2897
+ ('core:pkg:onload:complete', {'pkg': 'testload'}),
2898
+ ])
2899
+
2900
+ self.eq((2, 3), await core01.callStorm('return($lib.queue.gen(onload:test).get((2), cull=(false)))'))
2901
+
2902
+ await core01.waitfini()
2903
+
2904
+ await core00.waitfini()
2905
+
2829
2906
  async def test_storm_tree(self):
2830
2907
 
2831
2908
  async with self.getTestCore() as core:
@@ -5083,7 +5160,7 @@ class StormTest(s_t_utils.SynTest):
5083
5160
  batch $lib.true --size 5 ${
5084
5161
  $vals=([])
5085
5162
  for $n in $nodes { $vals.append($n.repr()) }
5086
- $lib.print($lib.str.join(',', $vals))
5163
+ $lib.print((',').join($vals))
5087
5164
  }
5088
5165
  '''
5089
5166
  msgs = await core.stormlist(q)
@@ -5098,7 +5175,7 @@ class StormTest(s_t_utils.SynTest):
5098
5175
  batch $lib.false --size 5 {
5099
5176
  $vals=([])
5100
5177
  for $n in $nodes { $vals.append($n.repr()) }
5101
- $lib.print($lib.str.join(',', $vals))
5178
+ $lib.print((',').join($vals))
5102
5179
  }
5103
5180
  '''
5104
5181
  msgs = await core.stormlist(q)
@@ -5204,7 +5281,7 @@ class StormTest(s_t_utils.SynTest):
5204
5281
  msgs = await core.stormlist(q)
5205
5282
  self.stormHasNoErr(msgs)
5206
5283
 
5207
- await core.nodes('''$token=foo $lib.print(({"Authorization":$lib.str.format("Bearer {token}", token=$token)}))''')
5284
+ await core.nodes('''$token=foo $lib.print(({"Authorization":`Bearer {$token}`}))''')
5208
5285
 
5209
5286
  q = '#rep.clearsky.dreamjob -># +syn:tag^=rep |uniq -syn:tag~=rep.clearsky'
5210
5287
  msgs = await core.stormlist(q)
@@ -5218,7 +5295,7 @@ class StormTest(s_t_utils.SynTest):
5218
5295
  msgs = await core.stormlist(q)
5219
5296
  self.stormIsInWarn('Failed to decode iden: [ssl://svcrs:27492?certname=root=bar]', msgs)
5220
5297
 
5221
- q = "$foo=one $bar=two $lib.print($lib.str.concat($foo, '=', $bar))"
5298
+ q = "$foo=one $bar=two $lib.print(`{$foo}={$bar}`)"
5222
5299
  msgs = await core.stormlist(q)
5223
5300
  self.stormIsInPrint("one=two", msgs)
5224
5301
 
@@ -494,7 +494,7 @@ class StormHttpTest(s_test.SynTest):
494
494
  await root.setPasswd('root')
495
495
 
496
496
  adduser = '''
497
- $url = $lib.str.format("https://root:root@127.0.0.1:{port}/api/v1/auth/adduser", port=$port)
497
+ $url = `https://root:root@127.0.0.1:{$port}/api/v1/auth/adduser`
498
498
  $user = ({"name": $name, "passwd": $passwd})
499
499
  $post = $lib.inet.http.post($url, json=$user, ssl_verify=$(0)).json().result.name
500
500
  $lib.print($post)
@@ -506,8 +506,8 @@ class StormHttpTest(s_test.SynTest):
506
506
  self.assertIn('foo', [u.name for u in core.auth.users()])
507
507
 
508
508
  adduser = '''
509
- $url = $lib.str.format("https://root:root@127.0.0.1:{port}/api/v1/auth/adduser", port=$port)
510
- $user = $lib.str.format('{"name": "{name}", "passwd": "{passwd}"}', name=$name, passwd=$passwd)
509
+ $url = `https://root:root@127.0.0.1:{$port}/api/v1/auth/adduser`
510
+ $user = $lib.json.save( ({"name": $name, "passwd": $passwd}) )
511
511
  $header = ({"Content-Type": "application/json"})
512
512
  $post = $lib.inet.http.post($url, headers=$header, body=$user, ssl_verify=$(0)).json().result.name
513
513
  [ test:str=$post ]
@@ -586,7 +586,7 @@ class StormHttpTest(s_test.SynTest):
586
586
  root = await core.auth.getUserByName('root')
587
587
  await root.setPasswd('root')
588
588
  text = '''
589
- $url = $lib.str.format("https://root:root@127.0.0.1:{port}/api/v1/storm", port=$port)
589
+ $url = `https://root:root@127.0.0.1:{$port}/api/v1/storm`
590
590
  $stormq = "($size, $sha2) = $lib.axon.put($lib.base64.decode('dmVydGV4')) [ test:str = $sha2 ] [ test:int = $size ]"
591
591
  $json = ({"query": $stormq})
592
592
  $bytez = $lib.inet.http.post($url, json=$json, ssl_verify=$(0))
@@ -602,7 +602,7 @@ class StormHttpTest(s_test.SynTest):
602
602
  self.eq(nodes[0].ndef, ('test:int', 6))
603
603
 
604
604
  text = '''
605
- $url = $lib.str.format("https://root:root@127.0.0.1:{port}/api/v1/storm", port=$port)
605
+ $url = `https://root:root@127.0.0.1:{$port}/api/v1/storm`
606
606
  $json = ({"query": "test:str"})
607
607
  $body = $json
608
608
  $resp=$lib.inet.http.post($url, json=$json, body=$body, ssl_verify=$(0))
@@ -780,7 +780,7 @@ class StormHttpTest(s_test.SynTest):
780
780
  mesg = await core.callStorm('''
781
781
  $params = ( { "param1": "somevalu" } )
782
782
  $hdr = ( { "key": $lib.false } )
783
- $url = $lib.str.format('https://127.0.0.1:{port}/test/ws', port=$port)
783
+ $url = `https://127.0.0.1:{$port}/test/ws`
784
784
 
785
785
  ($ok, $sock) = $lib.inet.http.connect($url, headers=$hdr, params=$params, ssl_verify=$lib.false)
786
786
  if (not $ok) { $lib.exit($sock) }
@@ -796,7 +796,7 @@ class StormHttpTest(s_test.SynTest):
796
796
 
797
797
  mesg = await core.callStorm('''
798
798
  $hdr = ( { "key": $lib.false } )
799
- $url = $lib.str.format('https://127.0.0.1:{port}/test/ws', port=$port)
799
+ $url = `https://127.0.0.1:{$port}/test/ws`
800
800
 
801
801
  ($ok, $sock) = $lib.inet.http.connect($url, headers=$hdr, ssl_verify=$lib.false)
802
802
  if (not $ok) { $lib.exit($sock) }
@@ -810,7 +810,7 @@ class StormHttpTest(s_test.SynTest):
810
810
  self.none(mesg.get('params'))
811
811
 
812
812
  query = '''
813
- $url = $lib.str.format('https://127.0.0.1:{port}/test/ws', port=$port)
813
+ $url = `https://127.0.0.1:{$port}/test/ws`
814
814
 
815
815
  ($ok, $sock) = $lib.inet.http.connect($url, proxy=$proxy, ssl_verify=$lib.false)
816
816
  if (not $ok) { $lib.exit($sock) }
@@ -120,12 +120,12 @@ class StormlibCacheTest(s_test.SynTest):
120
120
 
121
121
  $rets.append($cache.get(foo))
122
122
  $rets.append($sent)
123
- $rets.append($lib.str.join(",", $vals))
123
+ $rets.append((',').join($vals))
124
124
 
125
125
  $val = one
126
126
  $rets.append($cache.get(bar))
127
127
  $rets.append($sent)
128
- $rets.append($lib.str.join(",", $vals))
128
+ $rets.append((',').join($vals))
129
129
 
130
130
  return($rets)
131
131
  ''')
@@ -172,7 +172,7 @@ class StormScrapeTest(s_test.SynTest):
172
172
 
173
173
  text = text + ' and then there was another 1.2.3.4 that happened at woot.com '
174
174
  query = '''$tally = $lib.stats.tally() for ($form, $ndef) in $lib.scrape.ndefs($text)
175
- { $valu=$lib.str.format('{f}={n}', f=$form, n=$ndef) $tally.inc($valu) }
175
+ { $valu=`{$form}={$ndef}` $tally.inc($valu) }
176
176
  fini { return ( $tally ) }
177
177
  '''
178
178
  varz = {'text': text}
@@ -1,3 +1,5 @@
1
+ import asyncio
2
+
1
3
  import synapse.exc as s_exc
2
4
  import synapse.lib.parser as s_parser
3
5
 
@@ -142,3 +144,64 @@ class LibStormTest(s_test.SynTest):
142
144
  }
143
145
  ''')
144
146
  self.stormIsInPrint('mesg=hello', msgs)
147
+
148
+ async def test_lib_stormlib_storm_tasks(self):
149
+
150
+ with self.getStructuredAsyncLoggerStream('synapse') as stream:
151
+
152
+ async with self.getTestCore() as core:
153
+
154
+ async with core.getLocalProxy() as prox:
155
+
156
+ event = asyncio.Event()
157
+
158
+ q = 'for $mesg in $lib.storm.run("$lib.time.sleep(120)") { $lib.fire(storm, mesg=$mesg) }'
159
+
160
+ async def doit():
161
+ async for mesg in prox.storm(q):
162
+ if mesg[0] == 'storm:fire':
163
+ event.set()
164
+
165
+ task00 = core.schedCoro(doit())
166
+ await asyncio.wait_for(event.wait(), timeout=10)
167
+
168
+ viewiden = core.getView().iden
169
+
170
+ # One task for the main query
171
+ tasks = core.boss.ps()
172
+ self.len(1, tasks)
173
+ self.eq(tasks[0].name, 'storm')
174
+ self.eq(tasks[0].info, {'query': q, 'view': viewiden})
175
+ self.len(1, tasks[0].kids)
176
+
177
+ # The main query has one kid, the worker
178
+ kid = list(tasks[0].kids.values())[0]
179
+ self.nn(kid.iden)
180
+ self.nn(kid.user)
181
+ self.eq(kid.name, 'runstorm')
182
+ self.eq(kid.info, {})
183
+
184
+ # The worker has a kid which is the $lib.storm.run() worker
185
+ self.len(1, kid.kids)
186
+ gkid = list(kid.kids.values())[0]
187
+ self.eq(gkid.name, 'runstorm')
188
+ self.eq(gkid.info, {})
189
+
190
+ # Kill the main task
191
+ tasks = core.boss.ps()
192
+ self.len(1, tasks)
193
+ await tasks[0].kill()
194
+
195
+ # No tasks running
196
+ tasks = core.boss.ps()
197
+ self.len(0, tasks)
198
+
199
+ task00.cancel('oh bye')
200
+
201
+ # Verify we saw two storm runtime cancellations
202
+ msgs = stream.jsonlines()
203
+ self.gt(len(msgs), 0)
204
+
205
+ msgs = [(k.get('message'), k.get('text')) for k in msgs]
206
+ self.isin(('Storm runtime cancelled.', '$lib.time.sleep(120)'), msgs)
207
+ self.isin(('Storm runtime cancelled.', q), msgs)
@@ -347,6 +347,9 @@ class StormvarServiceCell(s_cell.Cell):
347
347
  'bar': {
348
348
  'type': 'string',
349
349
  },
350
+ 'name': {
351
+ 'type': 'string',
352
+ },
350
353
  },
351
354
  'required': ['name', ],
352
355
  'additionalProperties': False,
@@ -156,7 +156,7 @@ class StormTypesTest(s_test.SynTest):
156
156
  await core.callStorm(q, opts=opts)
157
157
 
158
158
  # Push a handful of notifications and list a subset of them
159
- q = '''$m=$lib.str.format('hello {i}', i=$i) return($lib.auth.users.byname(root).tell($m))'''
159
+ q = '''$m=`hello {$i}` return($lib.auth.users.byname(root).tell($m))'''
160
160
  for i in range(5):
161
161
  opts = {'user': visi.iden, 'vars': {'i': i}}
162
162
  await core.callStorm(q, opts=opts)
@@ -1071,7 +1071,7 @@ class StormTypesTest(s_test.SynTest):
1071
1071
  # exec vars do not populate upwards
1072
1072
  q = '''
1073
1073
  $foo = "that is one neato burrito"
1074
- $baz = ${ $bar=$lib.str.concat(wompwomp, $lib.guid()) $lib.print("in exec") }
1074
+ $baz = ${ $bar=`wompwomp{$lib.guid()}` $lib.print("in exec") }
1075
1075
  $baz.exec()
1076
1076
  $lib.print("post exec {bar}", bar=$bar)
1077
1077
  [ test:str=$foo ]
@@ -1283,6 +1283,8 @@ class StormTypesTest(s_test.SynTest):
1283
1283
 
1284
1284
  async def test_storm_lib_str(self):
1285
1285
  async with self.getTestCore() as core:
1286
+
1287
+ # TODO $lib.str.concat and rmat are deprecated should be removed in 3.0.0
1286
1288
  q = '$v=vertex $l=link $fqdn=$lib.str.concat($v, ".", $l)' \
1287
1289
  ' [ inet:email=$lib.str.format("visi@{domain}", domain=$fqdn) ]'
1288
1290
  nodes = await core.nodes(q)
@@ -1310,7 +1312,7 @@ class StormTypesTest(s_test.SynTest):
1310
1312
  sobj = s_stormtypes.Str('beepbeep')
1311
1313
  self.len(8, sobj)
1312
1314
 
1313
- nodes = await core.nodes('$s = (foo, bar, baz) [ test:str=$lib.str.join(".", $s) ]')
1315
+ nodes = await core.nodes("$s = (foo, bar, baz) [ test:str=('.').join($s) ]")
1314
1316
  self.eq('foo.bar.baz', nodes[0].ndef[1])
1315
1317
 
1316
1318
  nodes = await core.nodes('$s = foo-bar-baz [ test:str=$s.replace("-", ".") ]')
@@ -1760,7 +1762,7 @@ class StormTypesTest(s_test.SynTest):
1760
1762
  $ipv4 = $node.repr()
1761
1763
  $loc = $node.repr(loc)
1762
1764
  $latlong = $node.repr(latlong, defv="??")
1763
- $valu = $lib.str.format("{ipv4} in {loc} at {latlong}", ipv4=$ipv4, loc=$loc, latlong=$latlong)
1765
+ $valu = `{$ipv4} in {$loc} at {$latlong}`
1764
1766
  [ test:str=$valu ]
1765
1767
  +test:str
1766
1768
  '''
@@ -2462,7 +2464,7 @@ class StormTypesTest(s_test.SynTest):
2462
2464
  self.stormIsInPrint('pop valu is 0', mesgs)
2463
2465
 
2464
2466
  listq = '''for ($key, $valu) in $lib.globals.list() {
2465
- $string = $lib.str.format("{key} is {valu}", key=$key, valu=$valu)
2467
+ $string = `{$key} is {$valu}`
2466
2468
  $lib.print($string)
2467
2469
  }
2468
2470
  '''
@@ -2506,7 +2508,7 @@ class StormTypesTest(s_test.SynTest):
2506
2508
  self.len(1, await core.nodes('test:str=hehe'))
2507
2509
 
2508
2510
  listq = '''for ($key, $valu) in $lib.user.vars.list() {
2509
- $string = $lib.str.format("{key} is {valu}", key=$key, valu=$valu)
2511
+ $string = `{$key} is {$valu}`
2510
2512
  $lib.print($string)
2511
2513
  }
2512
2514
  '''
@@ -2579,7 +2581,7 @@ class StormTypesTest(s_test.SynTest):
2579
2581
  # core.vars, they only get the values they can read.
2580
2582
  corelistq = '''
2581
2583
  for ($key, $valu) in $lib.globals.list() {
2582
- $string = $lib.str.format("{key} is {valu}", key=$key, valu=$valu)
2584
+ $string = `{$key} is {$valu}`
2583
2585
  $lib.print($string)
2584
2586
  }
2585
2587
  '''
@@ -2734,6 +2736,21 @@ class StormTypesTest(s_test.SynTest):
2734
2736
  self.false(valu[0])
2735
2737
  self.eq(valu[1]['err'], 'BadArg')
2736
2738
 
2739
+ query = '''$valu="2020-10-01 01:30:00"
2740
+ $parsed=$lib.time.parse($valu, "%Y-%m-%d %H:%M:%S")
2741
+ $lib.print($lib.time.toUTC($parsed, US/Eastern))
2742
+ '''
2743
+ mesgs = await core.stormlist(query)
2744
+ self.stormIsInPrint('1601530200000', mesgs)
2745
+
2746
+ query = '''$valu="2020-11-01 01:30:00"
2747
+ $parsed=$lib.time.parse($valu, "%Y-%m-%d %H:%M:%S")
2748
+ return($lib.time.toUTC($parsed, America/New_York))
2749
+ '''
2750
+ mesgs = await core.callStorm(query)
2751
+ self.false(mesgs[0])
2752
+ self.isin('Ambiguous time', mesgs[1]['errinfo']['mesg'])
2753
+
2737
2754
  async def test_storm_lib_time_ticker(self):
2738
2755
 
2739
2756
  async with self.getTestCore() as core:
@@ -3785,7 +3802,6 @@ class StormTypesTest(s_test.SynTest):
3785
3802
  async with self.getTestCore() as core:
3786
3803
  async with self.getTestCore() as core2:
3787
3804
 
3788
- await core2.nodes('[ inet:ipv4=1.2.3.4 ]')
3789
3805
  url = core2.getLocalUrl('*/layer')
3790
3806
 
3791
3807
  layriden = core2.view.layers[0].iden
@@ -3802,8 +3818,141 @@ class StormTypesTest(s_test.SynTest):
3802
3818
 
3803
3819
  layr = core.getLayer(uplayr)
3804
3820
 
3805
- evnt = await layr.waitUpstreamOffs(layriden, offs)
3806
- self.true(await asyncio.wait_for(evnt.wait(), timeout=6))
3821
+ async def query(q):
3822
+ '''
3823
+ Run a query on core2 and wait for it to sync to layr from core
3824
+ '''
3825
+ nodes = await core2.nodes(q)
3826
+ offs = await core2.view.layers[0].getEditIndx()
3827
+ evnt = await layr.waitUpstreamOffs(layriden, offs)
3828
+ self.true(await asyncio.wait_for(evnt.wait(), timeout=6))
3829
+ return nodes
3830
+
3831
+ vdef = {
3832
+ 'layers': [layr.iden]
3833
+ }
3834
+
3835
+ view00 = await core.addView(vdef)
3836
+ self.nn(view00)
3837
+
3838
+ # No foobar in core
3839
+ opts = {'view': view00.get('iden')}
3840
+ nodes = await core.nodes('it:dev:str=foobar', opts=opts)
3841
+ self.len(0, nodes)
3842
+
3843
+ # Add foobar in core2
3844
+ nodes = await query('[ it:dev:str=foobar ]')
3845
+ self.len(1, nodes)
3846
+
3847
+ # foobar shows up in core
3848
+ nodes = await core.nodes('it:dev:str=foobar', opts=opts)
3849
+ self.len(1, nodes)
3850
+
3851
+ self.len(1, layr.activetasks)
3852
+
3853
+ # The upstream key only accepts null
3854
+ q = f'layer.set {uplayr} upstream (true)'
3855
+ msgs = await core.stormlist(q)
3856
+ self.stormIsInErr('Layer only supports setting "mirror" and "upstream" to null.', msgs)
3857
+
3858
+ with self.raises(s_exc.BadOptValu) as exc:
3859
+ await layr.setLayerInfo('upstream', False)
3860
+ self.eq(exc.exception.get('mesg'), 'Layer only supports setting "mirror" and "upstream" to None.', msgs)
3861
+
3862
+ # Now remove the upstream configuration
3863
+ q = f'layer.set {uplayr} upstream (null)'
3864
+ msgs = await core.stormlist(q)
3865
+ self.stormHasNoWarnErr(msgs)
3866
+
3867
+ layr = core.getLayer(uplayr)
3868
+ self.len(0, layr.activetasks)
3869
+ self.none(layr.layrinfo.get('upstream'))
3870
+
3871
+ with self.raises(TimeoutError):
3872
+ await query('[ it:dev:str=newp ]')
3873
+
3874
+ # No newp in core because layer upstream is disabled
3875
+ nodes = await core.nodes('it:dev:str=newp', opts=opts)
3876
+ self.len(0, nodes)
3877
+
3878
+ async def test_storm_lib_layer_mirror(self):
3879
+ async with self.getTestCore() as core:
3880
+ async with self.getTestCore() as core2:
3881
+
3882
+ url = core2.getLocalUrl('*/layer')
3883
+
3884
+ layers = set(core.layers.keys())
3885
+ q = f'layer.add --mirror {url}'
3886
+ mesgs = await core.stormlist(q)
3887
+ uplayr = list(set(core.layers.keys()) - layers)[0]
3888
+
3889
+ q = f'layer.set {uplayr} name "woot woot"'
3890
+ mesgs = await core.stormlist(q)
3891
+ self.stormIsInPrint('(name: woot woot)', mesgs)
3892
+
3893
+ layr = core.getLayer(uplayr)
3894
+
3895
+ async def query(q):
3896
+ '''
3897
+ Run a query on core2 and wait for it to sync to layr from core
3898
+ '''
3899
+ nodes = await core2.nodes(q)
3900
+ offs = await core2.view.layers[0].getEditOffs()
3901
+ self.true(await layr.waitEditOffs(offs, timeout=10))
3902
+ return nodes
3903
+
3904
+ vdef = {
3905
+ 'layers': [layr.iden]
3906
+ }
3907
+
3908
+ view00 = await core.addView(vdef)
3909
+ self.nn(view00)
3910
+
3911
+ # No foobar in core
3912
+ opts = {'view': view00.get('iden')}
3913
+ nodes = await core.nodes('it:dev:str=foobar', opts=opts)
3914
+ self.len(0, nodes)
3915
+
3916
+ # Add foobar in core2
3917
+ nodes = await query('[ it:dev:str=foobar ]')
3918
+ self.len(1, nodes)
3919
+
3920
+ # foobar shows up in core
3921
+ nodes = await core.nodes('it:dev:str=foobar', opts=opts)
3922
+ self.len(1, nodes)
3923
+
3924
+ self.true(layr.ismirror)
3925
+ self.nn(layr.leadtask)
3926
+ self.nn(layr.leader)
3927
+ self.len(0, layr.activetasks)
3928
+
3929
+ # The mirror key only accepts null
3930
+ q = f'layer.set {uplayr} mirror (true)'
3931
+ msgs = await core.stormlist(q)
3932
+ self.stormIsInErr('Layer only supports setting "mirror" and "upstream" to null.', msgs)
3933
+
3934
+ with self.raises(s_exc.BadOptValu) as exc:
3935
+ await layr.setLayerInfo('mirror', False)
3936
+ self.eq(exc.exception.get('mesg'), 'Layer only supports setting "mirror" and "upstream" to None.', msgs)
3937
+
3938
+ # Now remove the mirror configuration
3939
+ q = f'layer.set {uplayr} mirror (null)'
3940
+ msgs = await core.stormlist(q)
3941
+ self.stormHasNoWarnErr(msgs)
3942
+
3943
+ layr = core.getLayer(uplayr)
3944
+ self.none(layr.layrinfo.get('mirror'))
3945
+ self.none(layr.leadtask)
3946
+ self.none(layr.leader)
3947
+ self.false(layr.ismirror)
3948
+
3949
+ # Add newp in core2
3950
+ nodes = await query('[ it:dev:str=newp ]')
3951
+ self.len(1, nodes)
3952
+
3953
+ # No newp in core because layer mirroring is disabled
3954
+ nodes = await core.nodes('it:dev:str=newp', opts=opts)
3955
+ self.len(0, nodes)
3807
3956
 
3808
3957
  async def test_storm_lib_view(self):
3809
3958
 
@@ -4927,7 +5076,7 @@ class StormTypesTest(s_test.SynTest):
4927
5076
  unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=10,
4928
5077
  tzinfo=tz.utc).timestamp()
4929
5078
 
4930
- q = '{$lib.queue.get(foo).put(m3) $s=$lib.str.format("m3 {t} {i}", t=$auto.type, i=$auto.iden) $lib.log.info($s, ({"iden": $auto.iden})) }'
5079
+ q = '{$lib.queue.get(foo).put(m3) $s=`m3 {$auto.type} {$auto.iden}` $lib.log.info($s, ({"iden": $auto.iden})) }'
4931
5080
  text = f'cron.add --minute 17 {q}'
4932
5081
  async with getCronJob(text) as guid:
4933
5082
  with self.getStructuredAsyncLoggerStream('synapse.storm.log', 'm3 cron') as stream:
@@ -6057,6 +6206,16 @@ class StormTypesTest(s_test.SynTest):
6057
6206
  self.eq({'d', 'c'}, ret)
6058
6207
 
6059
6208
  # str join
6209
+ ret = await core.callStorm('$x=(foo,bar,baz) $y=("-").join($x) return($y)')
6210
+ self.eq('foo-bar-baz', ret)
6211
+
6212
+ ret = await core.callStorm('$y=("-").join((foo, bar, baz)) return($y)')
6213
+ self.eq('foo-bar-baz', ret)
6214
+
6215
+ ret = await core.callStorm('$x=abcd $y=("-").join($x) return($y)')
6216
+ self.eq('a-b-c-d', ret)
6217
+
6218
+ # TODO $lib.str.join is deprecated and will be removed in 3.0.0
6060
6219
  ret = await core.callStorm('$x=(foo,bar,baz) $y=$lib.str.join("-", $x) return($y)')
6061
6220
  self.eq('foo-bar-baz', ret)
6062
6221
 
@@ -6077,7 +6236,7 @@ class StormTypesTest(s_test.SynTest):
6077
6236
 
6078
6237
  opts = {'user': visi.iden, 'vars': {'port': port}}
6079
6238
  wget = '''
6080
- $url = $lib.str.format("https://visi:secret@127.0.0.1:{port}/api/v1/healthcheck", port=$port)
6239
+ $url = `https://visi:secret@127.0.0.1:{$port}/api/v1/healthcheck`
6081
6240
  return($lib.axon.wget($url, ssl=$lib.false))
6082
6241
  '''
6083
6242
  with self.raises(s_exc.AuthDeny):