synapse 2.196.0__py311-none-any.whl → 2.198.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 (61) hide show
  1. synapse/axon.py +3 -0
  2. synapse/common.py +3 -0
  3. synapse/cortex.py +13 -11
  4. synapse/cryotank.py +2 -2
  5. synapse/lib/aha.py +3 -0
  6. synapse/lib/ast.py +277 -165
  7. synapse/lib/auth.py +39 -11
  8. synapse/lib/cell.py +24 -6
  9. synapse/lib/config.py +3 -3
  10. synapse/lib/hive.py +2 -1
  11. synapse/lib/hiveauth.py +10 -1
  12. synapse/lib/jsonstor.py +6 -5
  13. synapse/lib/layer.py +6 -5
  14. synapse/lib/multislabseqn.py +2 -2
  15. synapse/lib/node.py +10 -4
  16. synapse/lib/parser.py +46 -21
  17. synapse/lib/schemas.py +491 -1
  18. synapse/lib/snap.py +68 -26
  19. synapse/lib/storm.lark +13 -11
  20. synapse/lib/storm.py +13 -395
  21. synapse/lib/storm_format.py +3 -2
  22. synapse/lib/stormlib/graph.py +0 -61
  23. synapse/lib/stormlib/index.py +52 -0
  24. synapse/lib/stormtypes.py +16 -5
  25. synapse/lib/task.py +13 -2
  26. synapse/lib/urlhelp.py +1 -1
  27. synapse/lib/version.py +2 -2
  28. synapse/models/doc.py +62 -0
  29. synapse/models/infotech.py +18 -0
  30. synapse/models/orgs.py +6 -4
  31. synapse/models/risk.py +9 -0
  32. synapse/models/syn.py +18 -2
  33. synapse/tests/files/stormpkg/badendpoints.yaml +7 -0
  34. synapse/tests/files/stormpkg/testpkg.yaml +8 -0
  35. synapse/tests/test_cortex.py +108 -0
  36. synapse/tests/test_datamodel.py +7 -0
  37. synapse/tests/test_lib_aha.py +12 -42
  38. synapse/tests/test_lib_ast.py +57 -0
  39. synapse/tests/test_lib_auth.py +143 -2
  40. synapse/tests/test_lib_boss.py +15 -6
  41. synapse/tests/test_lib_cell.py +43 -0
  42. synapse/tests/test_lib_grammar.py +54 -2
  43. synapse/tests/test_lib_lmdbslab.py +24 -0
  44. synapse/tests/test_lib_storm.py +20 -0
  45. synapse/tests/test_lib_stormlib_index.py +39 -0
  46. synapse/tests/test_lib_stormlib_macro.py +3 -3
  47. synapse/tests/test_lib_stormtypes.py +14 -2
  48. synapse/tests/test_lib_task.py +31 -13
  49. synapse/tests/test_model_doc.py +38 -0
  50. synapse/tests/test_model_infotech.py +13 -0
  51. synapse/tests/test_model_orgs.py +7 -0
  52. synapse/tests/test_model_risk.py +6 -0
  53. synapse/tests/test_model_syn.py +58 -0
  54. synapse/tests/test_tools_genpkg.py +10 -0
  55. synapse/tools/genpkg.py +2 -2
  56. synapse/tools/hive/load.py +1 -0
  57. {synapse-2.196.0.dist-info → synapse-2.198.0.dist-info}/METADATA +1 -1
  58. {synapse-2.196.0.dist-info → synapse-2.198.0.dist-info}/RECORD +61 -58
  59. {synapse-2.196.0.dist-info → synapse-2.198.0.dist-info}/LICENSE +0 -0
  60. {synapse-2.196.0.dist-info → synapse-2.198.0.dist-info}/WHEEL +0 -0
  61. {synapse-2.196.0.dist-info → synapse-2.198.0.dist-info}/top_level.txt +0 -0
synapse/lib/stormtypes.py CHANGED
@@ -3490,7 +3490,11 @@ class LibRegx(Lib):
3490
3490
  lkey = (pattern, flags)
3491
3491
  regx = self.compiled.get(lkey)
3492
3492
  if regx is None:
3493
- regx = self.compiled[lkey] = regex.compile(pattern, flags=flags)
3493
+ try:
3494
+ regx = self.compiled[lkey] = regex.compile(pattern, flags=flags)
3495
+ except (regex.error, ValueError) as e:
3496
+ mesg = f'Error compiling regex pattern: {e}: pattern="{s_common.trimText(pattern)}"'
3497
+ raise s_exc.BadArg(mesg=mesg) from None
3494
3498
  return regx
3495
3499
 
3496
3500
  @stormfunc(readonly=True)
@@ -3500,7 +3504,12 @@ class LibRegx(Lib):
3500
3504
  pattern = await tostr(pattern)
3501
3505
  replace = await tostr(replace)
3502
3506
  regx = await self._getRegx(pattern, flags)
3503
- return regx.sub(replace, text)
3507
+
3508
+ try:
3509
+ return regx.sub(replace, text)
3510
+ except (regex.error, IndexError) as e:
3511
+ mesg = f'$lib.regex.replace() error: {e}'
3512
+ raise s_exc.BadArg(mesg=mesg) from None
3504
3513
 
3505
3514
  @stormfunc(readonly=True)
3506
3515
  async def matches(self, pattern, text, flags=0):
@@ -6839,7 +6848,8 @@ class Layer(Prim):
6839
6848
  Implements the Storm api for a layer instance.
6840
6849
  '''
6841
6850
  _storm_locals = (
6842
- {'name': 'iden', 'desc': 'The iden of the Layer.', 'type': 'str', },
6851
+ {'name': 'iden', 'desc': 'The iden of the Layer.', 'type': 'str'},
6852
+ {'name': 'name', 'desc': 'The name of the Layer.', 'type': 'str'},
6843
6853
  {'name': 'set', 'desc': 'Set an arbitrary value in the Layer definition.',
6844
6854
  'type': {'type': 'function', '_funcname': '_methLayerSet',
6845
6855
  'args': (
@@ -7194,6 +7204,7 @@ class Layer(Prim):
7194
7204
 
7195
7205
  self.locls.update(self.getObjLocals())
7196
7206
  self.locls['iden'] = self.valu.get('iden')
7207
+ self.locls['name'] = self.valu.get('name')
7197
7208
 
7198
7209
  def __hash__(self):
7199
7210
  return hash((self._storm_typename, self.locls['iden']))
@@ -9866,7 +9877,7 @@ async def tostor(valu, isndef=False):
9866
9877
  retn = []
9867
9878
  for v in valu:
9868
9879
  try:
9869
- retn.append(await tostor(v))
9880
+ retn.append(await tostor(v, isndef=isndef))
9870
9881
  except s_exc.NoSuchType:
9871
9882
  pass
9872
9883
  return tuple(retn)
@@ -9875,7 +9886,7 @@ async def tostor(valu, isndef=False):
9875
9886
  retn = {}
9876
9887
  for k, v in valu.items():
9877
9888
  try:
9878
- retn[k] = await tostor(v)
9889
+ retn[k] = await tostor(v, isndef=isndef)
9879
9890
  except s_exc.NoSuchType:
9880
9891
  pass
9881
9892
  return retn
synapse/lib/task.py CHANGED
@@ -1,4 +1,3 @@
1
- import copy
2
1
  import asyncio
3
2
  import logging
4
3
 
@@ -6,6 +5,7 @@ import synapse.exc as s_exc
6
5
  import synapse.common as s_common
7
6
 
8
7
  import synapse.lib.base as s_base
8
+ import synapse.lib.msgpack as s_msgpack
9
9
 
10
10
  logger = logging.getLogger(__name__)
11
11
 
@@ -104,7 +104,7 @@ class Task(s_base.Base):
104
104
  pask = {
105
105
  'iden': self.iden,
106
106
  'name': self.name,
107
- 'info': copy.deepcopy(self.info),
107
+ 'info': s_msgpack.deepcopy(self.info),
108
108
  'tick': self.tick,
109
109
  'user': 'root',
110
110
  'kids': {i: k.pack() for i, k in self.kids.items()},
@@ -115,6 +115,17 @@ class Task(s_base.Base):
115
115
 
116
116
  return pask
117
117
 
118
+ def packv2(self):
119
+ return {
120
+ 'iden': self.iden,
121
+ 'name': self.name,
122
+ 'info': s_msgpack.deepcopy(self.info),
123
+ 'tick': self.tick,
124
+ 'user': self.user.iden,
125
+ 'username': self.user.name,
126
+ 'kids': {i: k.packv2() for i, k in self.kids.items()},
127
+ }
128
+
118
129
  def loop():
119
130
  try:
120
131
  return asyncio.get_running_loop()
synapse/lib/urlhelp.py CHANGED
@@ -13,7 +13,7 @@ def chopurl(url):
13
13
  '''
14
14
  ret = {}
15
15
  if url.find('://') == -1:
16
- raise s_exc.BadUrl(':// not found in [{}]!'.format(url))
16
+ raise s_exc.BadUrl(mesg=':// not found in [{}]!'.format(url))
17
17
 
18
18
  scheme, remain = url.split('://', 1)
19
19
  ret['scheme'] = scheme.lower()
synapse/lib/version.py CHANGED
@@ -223,6 +223,6 @@ def reqVersion(valu, reqver,
223
223
  ##############################################################################
224
224
  # The following are touched during the release process by bumpversion.
225
225
  # Do not modify these directly.
226
- version = (2, 196, 0)
226
+ version = (2, 198, 0)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = '8182360d2ebf8e8bdd00cb72f03812061c819cc2'
228
+ commit = '047e5e996d0bcaad4ada4ea390a726aca1abba9b'
synapse/models/doc.py CHANGED
@@ -77,6 +77,30 @@ class DocModule(s_module.CoreModule):
77
77
  'documents': 'standards',
78
78
  'type': 'doc:standard:type:taxonomy'},
79
79
  'doc': 'A group of requirements which define how to implement a policy or goal.'}),
80
+
81
+ ('doc:requirement:type:taxonomy', ('taxonomy', {}), {
82
+ 'interfaces': ('meta:taxonomy',),
83
+ 'doc': 'A taxonomy of requirement types.'}),
84
+
85
+ ('doc:requirement', ('guid', {}), {
86
+ 'interfaces': ('doc:document',),
87
+ 'template': {
88
+ 'document': 'requirement',
89
+ 'documents': 'requirements',
90
+ 'type': 'doc:requirement:type:taxonomy'},
91
+ 'doc': 'A single requirement, often defined by a standard.'}),
92
+
93
+ ('doc:resume:type:taxonomy', ('taxonomy', {}), {
94
+ 'interfaces': ('meta:taxonomy',),
95
+ 'doc': 'A taxonomy of resume types.'}),
96
+
97
+ ('doc:resume', ('guid', {}), {
98
+ 'interfaces': ('doc:document',),
99
+ 'template': {
100
+ 'document': 'resume',
101
+ 'documents': 'resumes',
102
+ 'type': 'doc:resume:type:taxonomy'},
103
+ 'doc': 'A CV/resume document.'}),
80
104
  ),
81
105
  'forms': (
82
106
 
@@ -88,6 +112,44 @@ class DocModule(s_module.CoreModule):
88
112
  ('policy', ('doc:policy', {}), {
89
113
  'doc': 'The policy which was used to derive the standard.'}),
90
114
  )),
115
+
116
+ ('doc:requirement:type:taxonomy', {}, ()),
117
+ ('doc:requirement', {}, (
118
+
119
+ ('summary', ('str', {}), {
120
+ 'disp': {'hint': 'text'},
121
+ 'doc': 'A summary of the requirement definition.'}),
122
+
123
+ ('optional', ('bool', {}), {
124
+ 'doc': 'Set to true if the requirement is optional as defined by the standard.'}),
125
+
126
+ ('priority', ('meta:priority', {}), {
127
+ 'doc': 'The priority of the requirement as defined by the standard.'}),
128
+
129
+ ('standard', ('doc:standard', {}), {
130
+ 'doc': 'The standard which defined the requirement.'}),
131
+ )),
132
+
133
+ ('doc:resume:type:taxonomy', {}, ()),
134
+ ('doc:resume', {}, (
135
+
136
+ ('contact', ('ps:contact', {}), {
137
+ 'doc': 'Contact information for subject of the resume.'}),
138
+
139
+ ('summary', ('str', {}), {
140
+ 'disp': {'hint': 'text'},
141
+ 'doc': 'The summary of qualifications from the resume.'}),
142
+
143
+ ('workhist', ('array', {'type': 'ps:workhist', 'sorted': True, 'uniq': True}), {
144
+ 'doc': 'Work history described in the resume.'}),
145
+
146
+ ('education', ('array', {'type': 'ps:education', 'sorted': True, 'uniq': True}), {
147
+ 'doc': 'Education experience described in the resume.'}),
148
+
149
+ ('achievements', ('array', {'type': 'ps:achievement', 'sorted': True, 'uniq': True}), {
150
+ 'doc': 'Achievements described in the resume.'}),
151
+
152
+ )),
91
153
  ),
92
154
  'edges': (),
93
155
  }),)
@@ -1252,6 +1252,15 @@ class ItModule(s_module.CoreModule):
1252
1252
  ('product', ('it:prod:softver', {}), {
1253
1253
  'doc': 'The software which produced the log entry.'}),
1254
1254
 
1255
+ ('service:platform', ('inet:service:platform', {}), {
1256
+ 'doc': 'The service platform which generated the log event.'}),
1257
+
1258
+ ('service:instance', ('inet:service:instance', {}), {
1259
+ 'doc': 'The service instance which generated the log event.'}),
1260
+
1261
+ ('service:account', ('inet:service:account', {}), {
1262
+ 'doc': 'The service account which generated the log event.'}),
1263
+
1255
1264
  )),
1256
1265
  ('it:domain', {}, (
1257
1266
  ('name', ('str', {'lower': True, 'onespace': True}), {
@@ -2587,6 +2596,15 @@ class ItModule(s_module.CoreModule):
2587
2596
 
2588
2597
  ('synuser', ('syn:user', {}), {
2589
2598
  'doc': 'The synapse user who executed the query.'}),
2599
+
2600
+ ('service:platform', ('inet:service:platform', {}), {
2601
+ 'doc': 'The service platform which was queried.'}),
2602
+
2603
+ ('service:instance', ('inet:service:instance', {}), {
2604
+ 'doc': 'The service instance which was queried.'}),
2605
+
2606
+ ('service:account', ('inet:service:account', {}), {
2607
+ 'doc': 'The service account which ran the query.'}),
2590
2608
  )),
2591
2609
  ('it:exec:thread', {}, (
2592
2610
  ('proc', ('it:exec:proc', {}), {
synapse/models/orgs.py CHANGED
@@ -93,9 +93,10 @@ class OuModule(s_module.CoreModule):
93
93
  ('ou:industryname', ('str', {'lower': True, 'onespace': True}), {
94
94
  'doc': 'The name of an industry.',
95
95
  }),
96
- ('ou:alias', ('str', {'lower': True, 'regex': r'^[0-9a-z_]+$'}), {
97
- 'doc': 'An alias for the org GUID.',
96
+ ('ou:alias', ('str', {'lower': True, 'regex': r'^[\w0-9_]+$'}), {
97
+ 'deprecated': True,
98
98
  'ex': 'vertexproject',
99
+ 'doc': 'Deprecated. Please use ou:name.',
99
100
  }),
100
101
  ('ou:hasalias', ('comp', {'fields': (('org', 'ou:org'), ('alias', 'ou:alias'))}), {
101
102
  'deprecated': True,
@@ -762,7 +763,8 @@ class OuModule(s_module.CoreModule):
762
763
  'doc': 'A list of alternate names for the organization.',
763
764
  }),
764
765
  ('alias', ('ou:alias', {}), {
765
- 'doc': 'The default alias for an organization.'
766
+ 'deprecated': True,
767
+ 'doc': 'Deprecated. Please use ou:org:names.',
766
768
  }),
767
769
  ('phone', ('tel:phone', {}), {
768
770
  'doc': 'The primary phone number for the organization.',
@@ -1373,7 +1375,7 @@ class OuModule(s_module.CoreModule):
1373
1375
  ('org', ('ou:org', {}), {
1374
1376
  'doc': 'The organization which is enacting the document.'}),
1375
1377
 
1376
- ('doc', ('ndef', {'forms': ('doc:policy', 'doc:standard')}), {
1378
+ ('doc', ('ndef', {'forms': ('doc:policy', 'doc:standard', 'doc:requirement')}), {
1377
1379
  'doc': 'The document enacted by the organization.'}),
1378
1380
 
1379
1381
  ('scope', ('ndef', {}), {
synapse/models/risk.py CHANGED
@@ -810,6 +810,15 @@ class RiskModule(s_module.CoreModule):
810
810
 
811
811
  ('host', ('it:host', {}), {
812
812
  'doc': 'The host which generated the alert.'}),
813
+
814
+ ('service:platform', ('inet:service:platform', {}), {
815
+ 'doc': 'The service platform which generated the alert.'}),
816
+
817
+ ('service:instance', ('inet:service:instance', {}), {
818
+ 'doc': 'The service instance which generated the alert.'}),
819
+
820
+ ('service:account', ('inet:service:account', {}), {
821
+ 'doc': 'The service account which generated the alert.'}),
813
822
  )),
814
823
  ('risk:compromisetype', {}, ()),
815
824
  ('risk:compromise', {}, (
synapse/models/syn.py CHANGED
@@ -23,7 +23,15 @@ class SynUser(s_types.Guid):
23
23
  if user is not None:
24
24
  return user.iden, {}
25
25
 
26
- return s_types.Guid._normPyStr(self, text)
26
+ if text == '*':
27
+ mesg = f'{self.name} values must be a valid username or a guid.'
28
+ raise s_exc.BadTypeValu(mesg=mesg, name=self.name, valu=text)
29
+
30
+ try:
31
+ return s_types.Guid._normPyStr(self, text)
32
+ except s_exc.BadTypeValu:
33
+ mesg = f'No user named {text} and value is not a guid.'
34
+ raise s_exc.BadTypeValu(mesg=mesg, name=self.name, valu=text) from None
27
35
 
28
36
  def repr(self, iden):
29
37
 
@@ -51,7 +59,15 @@ class SynRole(s_types.Guid):
51
59
  if role is not None:
52
60
  return role.iden, {}
53
61
 
54
- return s_types.Guid._normPyStr(self, text)
62
+ if text == '*':
63
+ mesg = f'{self.name} values must be a valid rolename or a guid.'
64
+ raise s_exc.BadTypeValu(mesg=mesg, name=self.name, valu=text)
65
+
66
+ try:
67
+ return s_types.Guid._normPyStr(self, text)
68
+ except s_exc.BadTypeValu:
69
+ mesg = f'No role named {text} and value is not a guid.'
70
+ raise s_exc.BadTypeValu(mesg=mesg, name=self.name, valu=text) from None
55
71
 
56
72
  def repr(self, iden):
57
73
 
@@ -0,0 +1,7 @@
1
+ name: test
2
+ version: 0.0.1
3
+ commands:
4
+ - name: 'foo.bar'
5
+ storm: (null)
6
+ endpoints:
7
+ - host: vertex.link
@@ -113,6 +113,14 @@ commands:
113
113
  - help: Help on foo opt
114
114
  - - --bar
115
115
  - help: Help on bar opt
116
+ storm: |
117
+ test:str
118
+ endpoints:
119
+ - path: /v1/test/one
120
+ - path: /v1/test/two
121
+ host: vertex.link
122
+ - path: /v1/test/three
123
+ desc: endpoint three
116
124
 
117
125
  - name: testpkg.baz
118
126
  descr: |
@@ -1001,6 +1001,114 @@ class CortexTest(s_t_utils.SynTest):
1001
1001
  self.eq(cm.exception.get('mesg'),
1002
1002
  'walk operation expected a string or list. got: 0.')
1003
1003
 
1004
+ await core.nodes('[media:news=*]')
1005
+
1006
+ nodes = await core.nodes('$n = {[it:dev:str=foo]} media:news [ +(refs)> $n ]')
1007
+ self.len(1, nodes)
1008
+ self.eq(nodes[0].ndef[0], 'media:news')
1009
+
1010
+ nodes = await core.nodes('media:news -(refs)> it:dev:str')
1011
+ self.len(1, nodes)
1012
+
1013
+ q = '''
1014
+ function foo() {
1015
+ for $x in $lib.range(5) {
1016
+ [ it:dev:int=$x ]
1017
+ emit $node
1018
+ }
1019
+ }
1020
+ media:news [ +(refs)> $foo() ]
1021
+ '''
1022
+ nodes = await core.nodes(q)
1023
+ self.len(1, nodes)
1024
+ self.eq(nodes[0].ndef[0], 'media:news')
1025
+
1026
+ nodes = await core.nodes('media:news -(refs)> it:dev:int')
1027
+ self.len(5, nodes)
1028
+
1029
+ nodes = await core.nodes('$n = {[it:dev:str=foo]} media:news [ -(refs)> $n ]')
1030
+ self.len(1, nodes)
1031
+ self.eq(nodes[0].ndef[0], 'media:news')
1032
+
1033
+ nodes = await core.nodes('media:news -(refs)> it:dev:str')
1034
+ self.len(0, nodes)
1035
+
1036
+ q = '''
1037
+ function foo() {
1038
+ for $x in $lib.range(5) {
1039
+ [ it:dev:int=$x ]
1040
+ emit $node
1041
+ }
1042
+ }
1043
+ media:news [ -(refs)> $foo() ]
1044
+ '''
1045
+ nodes = await core.nodes(q)
1046
+ self.len(1, nodes)
1047
+ self.eq(nodes[0].ndef[0], 'media:news')
1048
+
1049
+ nodes = await core.nodes('media:news -(refs)> it:dev:int')
1050
+ self.len(0, nodes)
1051
+
1052
+ nodes = await core.nodes('$n = {[it:dev:str=foo]} media:news [ <(refs)+ $n ]')
1053
+ self.len(1, nodes)
1054
+ self.eq(nodes[0].ndef[0], 'media:news')
1055
+
1056
+ nodes = await core.nodes('media:news <(refs)- it:dev:str')
1057
+ self.len(1, nodes)
1058
+
1059
+ q = '''
1060
+ function foo() {
1061
+ for $x in $lib.range(5) {
1062
+ [ it:dev:int=$x ]
1063
+ emit $node
1064
+ }
1065
+ }
1066
+ media:news [ <(refs)+ $foo() ]
1067
+ '''
1068
+ nodes = await core.nodes(q)
1069
+ self.len(1, nodes)
1070
+ self.eq(nodes[0].ndef[0], 'media:news')
1071
+
1072
+ nodes = await core.nodes('media:news <(refs)- it:dev:int')
1073
+ self.len(5, nodes)
1074
+
1075
+ nodes = await core.nodes('$n = {[it:dev:str=foo]} media:news [ <(refs)- $n ]')
1076
+ self.len(1, nodes)
1077
+ self.eq(nodes[0].ndef[0], 'media:news')
1078
+
1079
+ nodes = await core.nodes('media:news <(refs)- it:dev:str')
1080
+ self.len(0, nodes)
1081
+
1082
+ q = '''
1083
+ function foo() {
1084
+ for $x in $lib.range(5) {
1085
+ [ it:dev:int=$x ]
1086
+ emit $node
1087
+ }
1088
+ }
1089
+ media:news [ <(refs)- $foo() ]
1090
+ '''
1091
+ nodes = await core.nodes(q)
1092
+ self.len(1, nodes)
1093
+ self.eq(nodes[0].ndef[0], 'media:news')
1094
+
1095
+ nodes = await core.nodes('media:news <(refs)- it:dev:int')
1096
+ self.len(0, nodes)
1097
+
1098
+ await core.nodes('[media:news=*]')
1099
+
1100
+ nodes = await core.nodes('$n = {[it:dev:str=foo]} $edge=refs media:news [ +($edge)> $n ]')
1101
+ self.len(2, nodes)
1102
+
1103
+ nodes = await core.nodes('media:news -(refs)> it:dev:str')
1104
+ self.len(2, nodes)
1105
+
1106
+ nodes = await core.nodes('$n = {[it:dev:str=foo]} $edge=refs media:news [ -($edge)> $n ]')
1107
+ self.len(2, nodes)
1108
+
1109
+ nodes = await core.nodes('media:news -(refs)> it:dev:str')
1110
+ self.len(0, nodes)
1111
+
1004
1112
  async def test_cortex_callstorm(self):
1005
1113
 
1006
1114
  async with self.getTestCore(conf={'auth:passwd': 'root'}) as core:
@@ -2,6 +2,7 @@ import synapse.exc as s_exc
2
2
  import synapse.datamodel as s_datamodel
3
3
 
4
4
  import synapse.lib.module as s_module
5
+ import synapse.lib.schemas as s_schemas
5
6
 
6
7
  import synapse.cortex as s_cortex
7
8
 
@@ -342,3 +343,9 @@ class DataModelTest(s_t_utils.SynTest):
342
343
  nodes = await core.nodes('[ it:prod:softver=* :semver=3.1.0 ]')
343
344
  self.none(nodes[0].get('semver:major'))
344
345
  self.eq(1, nodes[0].get('semver:minor'))
346
+
347
+ def test_datamodel_schema_basetypes(self):
348
+ # N.B. This test is to keep synapse.lib.schemas.datamodel_basetypes const
349
+ # in sync with the default s_datamodel.Datamodel().types
350
+ basetypes = list(s_datamodel.Model().types)
351
+ self.eq(s_schemas.datamodel_basetypes, basetypes)
@@ -133,9 +133,14 @@ class AhaTest(s_test.SynTest):
133
133
  with self.getTestDir() as dirn:
134
134
  cryo0_dirn = s_common.gendir(dirn, 'cryo0')
135
135
  async with self.getTestAha(dirn=dirn) as aha:
136
+
137
+ replaymult = 1
138
+ if s_common.envbool('SYNDEV_NEXUS_REPLAY'):
139
+ replaymult = 2
140
+
136
141
  purl = await aha.addAhaSvcProv('0.cryo')
137
142
 
138
- wait00 = aha.waiter(1, 'aha:svcadd')
143
+ wait00 = aha.waiter(1 * replaymult, 'aha:svcadd')
139
144
 
140
145
  conf = {'aha:provision': purl}
141
146
  async with self.getTestCryo(dirn=cryo0_dirn, conf=conf) as cryo:
@@ -444,8 +449,13 @@ class AhaTest(s_test.SynTest):
444
449
 
445
450
  async with self.getTestAha() as aha:
446
451
 
452
+ replaymult = 1
453
+ if s_common.envbool('SYNDEV_NEXUS_REPLAY'):
454
+ replaymult = 2
455
+
447
456
  aha.testerr = True
448
457
  wait00 = aha.waiter(1, 'aha:svcadd')
458
+
449
459
  conf = {'aha:provision': await aha.addAhaSvcProv('0.cryo')}
450
460
  async with self.getTestCryo(conf=conf) as cryo:
451
461
 
@@ -454,7 +464,7 @@ class AhaTest(s_test.SynTest):
454
464
  svc = await aha.getAhaSvc('0.cryo...')
455
465
  self.none(svc)
456
466
 
457
- wait01 = aha.waiter(1, 'aha:svcadd')
467
+ wait01 = aha.waiter(1 * replaymult, 'aha:svcadd')
458
468
  aha.testerr = False
459
469
 
460
470
  self.nn(await wait01.wait(timeout=2))
@@ -1434,46 +1444,6 @@ class AhaTest(s_test.SynTest):
1434
1444
  self.len(nexsindx, items)
1435
1445
  self.true(all(item[1][0] for item in items))
1436
1446
 
1437
- # test some of the gather API implementations...
1438
- purl00 = await aha.addAhaSvcProv('0.cell')
1439
- purl01 = await aha.addAhaSvcProv('1.cell', provinfo={'mirror': '0.cell'})
1440
-
1441
- cell00 = await aha.enter_context(self.getTestCell(conf={'aha:provision': purl00}))
1442
- cell01 = await aha.enter_context(self.getTestCell(conf={'aha:provision': purl01}))
1443
-
1444
- await cell01.sync()
1445
-
1446
- async def sleep99(cell):
1447
- await cell.boss.promote('sleep99', cell.auth.rootuser)
1448
- await cell00.fire('sleep99')
1449
- await asyncio.sleep(99)
1450
-
1451
- async with cell00.waiter(2, 'sleep99', timeout=2):
1452
- task00 = cell00.schedCoro(sleep99(cell00))
1453
- task01 = cell01.schedCoro(sleep99(cell01))
1454
-
1455
- proxy = await aha.enter_context(aha.getLocalProxy())
1456
- tasks = [task async for task in cell00.getTasks(timeout=3)]
1457
- self.len(2, tasks)
1458
- self.eq(tasks[0]['service'], '0.cell.synapse')
1459
- self.eq(tasks[1]['service'], '1.cell.synapse')
1460
-
1461
- self.eq(tasks[0], await cell00.getTask(tasks[0].get('iden')))
1462
- self.eq(tasks[1], await cell00.getTask(tasks[1].get('iden')))
1463
- self.none(await cell00.getTask(tasks[1].get('iden'), peers=False))
1464
-
1465
- self.true(await cell00.killTask(tasks[0].get('iden')))
1466
-
1467
- task01 = tasks[1].get('iden')
1468
- self.false(await cell00.killTask(task01, peers=False))
1469
-
1470
- self.true(await cell00.killTask(task01))
1471
-
1472
- self.none(await cell00.getTask(task01))
1473
- self.false(await cell00.killTask(task01))
1474
-
1475
- self.none(await cell00.getAhaProxy(feats=(('newp', 9),)))
1476
-
1477
1447
  async def test_lib_aha_peer_api(self):
1478
1448
 
1479
1449
  async with self.getTestAha() as aha:
@@ -492,6 +492,63 @@ class AstTest(s_test.SynTest):
492
492
  q = '$foo=newp [test:str=foo :hehe*$foo=heval]'
493
493
  nodes = await core.nodes(q)
494
494
 
495
+ async def test_ast_setmultioper(self):
496
+ async with self.getTestCore() as core:
497
+
498
+ nodes = await core.nodes('[ test:arrayprop="*" :ints=(1,) ]')
499
+ self.eq(nodes[0].get('ints'), (1,))
500
+
501
+ nodes = await core.nodes('test:arrayprop [ :ints++=([3, 4]) ]')
502
+ self.eq(nodes[0].get('ints'), (1, 3, 4))
503
+
504
+ nodes = await core.nodes('test:arrayprop [ :ints++=(null) ]')
505
+ self.eq(nodes[0].get('ints'), (1, 3, 4))
506
+
507
+ nodes = await core.nodes('test:arrayprop [ :ints--=(null) ]')
508
+ self.eq(nodes[0].get('ints'), (1, 3, 4))
509
+
510
+ nodes = await core.nodes('test:arrayprop [ :strs++=(foo, bar, baz) ]')
511
+ self.eq(nodes[0].get('strs'), ('foo', 'bar', 'baz'))
512
+
513
+ with self.raises(s_exc.BadTypeValu):
514
+ await core.nodes('test:arrayprop [ :ints++=(["newp", 5, 6]) ]')
515
+
516
+ nodes = await core.nodes('test:arrayprop [ :ints?++=(["newp", 5, 6]) ]')
517
+ self.eq(nodes[0].get('ints'), (1, 3, 4, 5, 6))
518
+
519
+ with self.raises(s_exc.BadTypeValu):
520
+ await core.nodes('test:arrayprop [ :ints--=(["newp", 5, 6]) ]')
521
+
522
+ nodes = await core.nodes('test:arrayprop [ :ints?--=(["newp", 5, 6, 7]) ]')
523
+ self.eq(nodes[0].get('ints'), (1, 3, 4))
524
+
525
+ nodes = await core.nodes('[ test:str=foo :ndefs++={[ test:str=bar ]} ]')
526
+ self.eq(nodes[0].get('ndefs'), (('test:str', 'bar'),))
527
+
528
+ nodes = await core.nodes('test:str=foo [ :ndefs++={[ test:str=baz test:str=faz ]} ]')
529
+ self.eq(nodes[0].get('ndefs'), (('test:str', 'bar'), ('test:str', 'baz'), ('test:str', 'faz')))
530
+
531
+ nodes = await core.nodes('test:str=foo [ :ndefs--={ test:str=baz test:str=faz } ]')
532
+ self.eq(nodes[0].get('ndefs'), (('test:str', 'bar'),))
533
+
534
+ with self.raises(s_exc.NoSuchProp):
535
+ await core.nodes('test:arrayprop [ :newp++=(["newp", 5, 6]) ]')
536
+
537
+ badq = [
538
+ 'test:str [ :hehe++=([3, 4]) ]',
539
+ 'test:str [ :hehe?++=([3, 4]) ]',
540
+ 'test:str [ :hehe--=([3, 4]) ]',
541
+ 'test:str [ :hehe?--=([3, 4]) ]',
542
+ 'test:arrayprop [ :ints++=(3) ]',
543
+ 'test:arrayprop [ :ints?++=(3) ]',
544
+ 'test:arrayprop [ :ints--=(3) ]',
545
+ 'test:arrayprop [ :ints?--=(3) ]',
546
+ ]
547
+
548
+ for q in badq:
549
+ with self.raises(s_exc.StormRuntimeError):
550
+ await core.nodes(q)
551
+
495
552
  async def test_ast_editparens(self):
496
553
 
497
554
  async with self.getTestCore() as core: