splunk-soar-sdk 2.2.0__py3-none-any.whl → 2.3.0__py3-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.
@@ -2,10 +2,10 @@ from typing import Optional, Union, get_origin, get_args, Any
2
2
  from collections.abc import Iterator
3
3
  from typing_extensions import NotRequired, TypedDict
4
4
  from pydantic import BaseModel, Field
5
+ import itertools
5
6
 
6
7
  from soar_sdk.compat import remove_when_soar_newer_than
7
8
  from soar_sdk.shims.phantom.action_result import ActionResult as PhantomActionResult
8
-
9
9
  from soar_sdk.meta.datatypes import as_datatype
10
10
 
11
11
  remove_when_soar_newer_than(
@@ -92,7 +92,6 @@ def OutputField(
92
92
  example_values: Optional[list[Union[str, float, bool]]] = None,
93
93
  alias: Optional[str] = None,
94
94
  column_name: Optional[str] = None,
95
- column_order: Optional[int] = None,
96
95
  ) -> Any: # noqa: ANN401
97
96
  """Define metadata for an action output field.
98
97
 
@@ -107,7 +106,6 @@ def OutputField(
107
106
  in documentation and for testing/validation purposes.
108
107
  alias: Optional alternative name for the field when serialized.
109
108
  column_name: Optional name for the field when displayed in a table.
110
- column_order: Optional order for the field when displayed in a table (0-indexed).
111
109
 
112
110
  Note:
113
111
  Column name and order must be set together, if one is set but the other is not, an error will be raised.
@@ -128,7 +126,6 @@ def OutputField(
128
126
  alias=alias,
129
127
  cef_types=cef_types,
130
128
  column_name=column_name,
131
- column_order=column_order,
132
129
  )
133
130
 
134
131
 
@@ -163,7 +160,9 @@ class ActionOutput(BaseModel):
163
160
 
164
161
  @classmethod
165
162
  def _to_json_schema(
166
- cls, parent_datapath: str = "action_result.data.*"
163
+ cls,
164
+ parent_datapath: str = "action_result.data.*",
165
+ column_order_counter: Optional[itertools.count] = None,
167
166
  ) -> Iterator[OutputFieldSpecification]:
168
167
  """Convert the ActionOutput class to SOAR-compatible JSON schema.
169
168
 
@@ -174,6 +173,8 @@ class ActionOutput(BaseModel):
174
173
  Args:
175
174
  parent_datapath: The base datapath for fields in this output.
176
175
  Defaults to "action_result.data.*" for top-level outputs.
176
+ column_order_counter: Iterator for tracking column order across fields.
177
+ Used internally to maintain sequential column ordering. Defaults to itertools.count().
177
178
 
178
179
  Yields:
179
180
  OutputFieldSpecification objects describing each field in the schema.
@@ -187,6 +188,9 @@ class ActionOutput(BaseModel):
187
188
  Nested ActionOutput classes are recursively processed.
188
189
  Boolean fields automatically get [True, False] example values.
189
190
  """
191
+ if column_order_counter is None:
192
+ column_order_counter = itertools.count()
193
+
190
194
  for _field_name, field in cls.__fields__.items():
191
195
  field_name = alias if (alias := field.alias) else _field_name
192
196
 
@@ -219,7 +223,7 @@ class ActionOutput(BaseModel):
219
223
 
220
224
  if issubclass(field_type, ActionOutput):
221
225
  # If the field is another ActionOutput, recursively call _to_json_schema
222
- yield from field_type._to_json_schema(datapath)
226
+ yield from field_type._to_json_schema(datapath, column_order_counter)
223
227
  continue
224
228
  else:
225
229
  try:
@@ -241,20 +245,11 @@ class ActionOutput(BaseModel):
241
245
  if field_type is bool:
242
246
  schema_field["example_values"] = [True, False]
243
247
 
244
- # Validate column metadata - both column_name and column_order must be present together
245
248
  column_name = field.field_info.extra.get("column_name")
246
- column_order = field.field_info.extra.get("column_order")
247
-
248
- # Check if exactly one is set (XOR condition - invalid)
249
- if (column_name is None) != (column_order is None):
250
- raise ValueError(
251
- f"Field '{field_name}' must have both 'column_name' and 'column_order' "
252
- f"or neither. Found: column_name={column_name}, column_order={column_order}"
253
- )
254
249
 
255
- if column_name is not None and column_order is not None:
250
+ if column_name is not None:
256
251
  schema_field["column_name"] = column_name
257
- schema_field["column_order"] = column_order
252
+ schema_field["column_order"] = next(column_order_counter)
258
253
 
259
254
  yield schema_field
260
255
 
@@ -1,6 +1,7 @@
1
1
  from typing import Any, Optional
2
2
  from collections.abc import Iterator
3
3
  from logging import getLogger
4
+ import itertools
4
5
 
5
6
  from soar_sdk.meta.datatypes import as_datatype
6
7
  from soar_sdk.params import Params
@@ -29,8 +30,12 @@ class OutputsSerializer:
29
30
  @staticmethod
30
31
  def serialize_parameter_datapaths(
31
32
  params_class: type[Params],
33
+ column_order_counter: Optional[itertools.count] = None,
32
34
  ) -> Iterator[OutputFieldSpecification]:
33
35
  """Serializes the parameter data paths of a Params class to JSON schema."""
36
+ if column_order_counter is None:
37
+ column_order_counter = itertools.count()
38
+
34
39
  for field_name, field in params_class.__fields__.items():
35
40
  spec = OutputFieldSpecification(
36
41
  data_path=f"action_result.parameter.{field_name}",
@@ -38,6 +43,12 @@ class OutputsSerializer:
38
43
  )
39
44
  if cef_types := field.field_info.extra.get("cef_types"):
40
45
  spec["contains"] = cef_types
46
+
47
+ column_name = field.field_info.extra.get("column_name")
48
+
49
+ if column_name is not None:
50
+ spec["column_name"] = column_name
51
+ spec["column_order"] = next(column_order_counter)
41
52
  yield spec
42
53
 
43
54
  @classmethod
@@ -57,10 +68,13 @@ class OutputsSerializer:
57
68
  data_path="action_result.message",
58
69
  data_type="string",
59
70
  )
60
- params = cls.serialize_parameter_datapaths(params_class)
61
- outputs = outputs_class._to_json_schema()
71
+ column_order_counter = itertools.count()
72
+ params = cls.serialize_parameter_datapaths(params_class, column_order_counter)
73
+ outputs = outputs_class._to_json_schema(
74
+ column_order_counter=column_order_counter
75
+ )
62
76
  summary = (
63
- summary_class._to_json_schema("action_result.summary")
77
+ summary_class._to_json_schema("action_result.summary", column_order_counter)
64
78
  if summary_class
65
79
  else []
66
80
  )
soar_sdk/params.py CHANGED
@@ -22,6 +22,7 @@ def Param(
22
22
  allow_list: bool = False,
23
23
  sensitive: bool = False,
24
24
  alias: Optional[str] = None,
25
+ column_name: Optional[str] = None,
25
26
  ) -> Any: # noqa: ANN401
26
27
  """Representation of a single complex action parameter.
27
28
 
@@ -49,6 +50,7 @@ def Param(
49
50
  :param allow_list: Use this key to specify if the parameter supports specifying
50
51
  multiple values as a comma separated string.
51
52
  :param kwargs: additional kwargs accepted by pydantic.Field
53
+ :param column_name: Optional name for the parameter when displayed in an output table.
52
54
  :return: returns the FieldInfo object as pydantic.Field
53
55
  """
54
56
  if value_list is None:
@@ -64,6 +66,7 @@ def Param(
64
66
  allow_list=allow_list,
65
67
  sensitive=sensitive,
66
68
  alias=alias,
69
+ column_name=column_name,
67
70
  )
68
71
 
69
72
 
@@ -80,6 +83,8 @@ class InputFieldSpecification(TypedDict):
80
83
  value_list: NotRequired[list[str]]
81
84
  allow_list: bool
82
85
  default: NotRequired[Union[str, int, float, bool]]
86
+ column_name: NotRequired[str]
87
+ column_order: NotRequired[int]
83
88
 
84
89
 
85
90
  class Params(BaseModel):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splunk-soar-sdk
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: The official framework for developing and testing Splunk SOAR Apps
5
5
  Project-URL: Homepage, https://github.com/phantomcyber/splunk-soar-sdk
6
6
  Project-URL: Documentation, https://github.com/phantomcyber/splunk-soar-sdk
@@ -1,6 +1,6 @@
1
1
  soar_sdk/__init__.py,sha256=RzAng-ARqpK01SY82lNy4uYJFVG0yW6Q3CccEqbToJ4,726
2
2
  soar_sdk/abstract.py,sha256=jGXs2Fv5TRpnh5Duz3mWjY8_DAOpY4RSSzvw_z4XN4I,7950
3
- soar_sdk/action_results.py,sha256=5Lr3J1G2hcVVTQD35eo7OG3Cvr7eGZgb-FL2Eb5pBx8,11424
3
+ soar_sdk/action_results.py,sha256=NQTcQ3NJcYB6h-YNRqO0bCG_hILmTidv2D4FdvCzyDE,11092
4
4
  soar_sdk/actions_manager.py,sha256=wJCyfzkI_6OKZ-Kmll4vRJpGvYdL93Uw-JyEEGnKcw0,5779
5
5
  soar_sdk/app.py,sha256=lYvaDUYEV6b_uF4CyPh6wlAIbCNISya7bJ8Z2xJyyGY,33356
6
6
  soar_sdk/app_cli_runner.py,sha256=fJoozhyAt7QUMuc02nE5RL_InpsjQBpr6U4rF9sey3E,11627
@@ -13,7 +13,7 @@ soar_sdk/crypto.py,sha256=qiBMHUQqgn5lPI1DbujSj700s89FuLJrkQgCO9_eBn4,392
13
13
  soar_sdk/exceptions.py,sha256=CxJ_Q6N1jlknO_3ItDQNhHEw2pNWZr3sMLqutYmr5HA,1863
14
14
  soar_sdk/input_spec.py,sha256=BAa36l8IKDvM8SVMjgZ1XcnWZ2F7O052n2415tLeKK8,4690
15
15
  soar_sdk/logging.py,sha256=lSz8PA6hOCw2MHGE0ZSKbw-FzSr1WdbfQ7BHnXBUUY0,11440
16
- soar_sdk/params.py,sha256=8wo6buia91Hh-bxq0jVUU9MPN3XZVRzkMfKZ2BDuMLQ,7488
16
+ soar_sdk/params.py,sha256=Q2tWOzZWNl2Spvnu29iSUfMleW8U7OD-DdCWtkOXRMw,7720
17
17
  soar_sdk/paths.py,sha256=XhpanQCAiTXaulRx440oKu36mnll7P05TethHXgMpgQ,239
18
18
  soar_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  soar_sdk/types.py,sha256=uMFnNOHpmCLrbAhQOgmXjScXiGE67sM8ySN04MhkC3U,602
@@ -38,7 +38,7 @@ soar_sdk/cli/manifests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
38
38
  soar_sdk/cli/manifests/cli.py,sha256=cly5xVdj4bBIdZVMQPIWTXRgUfd1ON3qKO-76Fwql18,524
39
39
  soar_sdk/cli/manifests/deserializers.py,sha256=FtXv-OywYGb_O5TptS2Hg6niWcfihRPF_RaS5zDBXRk,17045
40
40
  soar_sdk/cli/manifests/processors.py,sha256=6B1fQC2WGVaUP-7E9Y5g7BipaVwEomJCkUQ_7gRfSn8,4155
41
- soar_sdk/cli/manifests/serializers.py,sha256=tlW8QPuaVUF8u384lN02xIK2bS2XkDL52kPNwwPmj9c,2859
41
+ soar_sdk/cli/manifests/serializers.py,sha256=0qps_2jsySXa4ytX3DvFt0tbbZlZ7mowIjMu9FEhQMc,3417
42
42
  soar_sdk/cli/package/cli.py,sha256=oCpP9E3PtXq-zCdzQD8Z-4dowKF1YT-uKjTpbt_YT-A,9516
43
43
  soar_sdk/cli/package/utils.py,sha256=NQgMxWZSf20hqd4Orov77b7qK23QAO3zIEvRTj9HW-o,1590
44
44
  soar_sdk/code_renderers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -96,8 +96,8 @@ soar_sdk/views/components/pie_chart.py,sha256=LVTeHVJN6nf2vjUs9y7PDBhS0U1fKW750l
96
96
  soar_sdk/webhooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
97
  soar_sdk/webhooks/models.py,sha256=-rjuFA9cRX5zTLp7cHSHVTkt5eVJD6BdESGbj_qkyHI,4540
98
98
  soar_sdk/webhooks/routing.py,sha256=BKbURSrBPdOTS5UFL-mHzFEr-Fj04mJMx9KeiPrZ2VQ,6872
99
- splunk_soar_sdk-2.2.0.dist-info/METADATA,sha256=N4S0W2q6KuT_hGulekYRuub3Ux29bFmEShcrwHguVzc,7361
100
- splunk_soar_sdk-2.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
101
- splunk_soar_sdk-2.2.0.dist-info/entry_points.txt,sha256=CgBjo2ZWpYNkt9TgvToL26h2Tg1yt8FbvYTb5NVgNuc,51
102
- splunk_soar_sdk-2.2.0.dist-info/licenses/LICENSE,sha256=gNCGrGhrSQb1PUzBOByVUN1tvaliwLZfna-QU2r2hQ8,11345
103
- splunk_soar_sdk-2.2.0.dist-info/RECORD,,
99
+ splunk_soar_sdk-2.3.0.dist-info/METADATA,sha256=WGypNiW3i7PR8Eef-5QYJUwWSR1ua062kpRU86c7g8Y,7361
100
+ splunk_soar_sdk-2.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
101
+ splunk_soar_sdk-2.3.0.dist-info/entry_points.txt,sha256=CgBjo2ZWpYNkt9TgvToL26h2Tg1yt8FbvYTb5NVgNuc,51
102
+ splunk_soar_sdk-2.3.0.dist-info/licenses/LICENSE,sha256=gNCGrGhrSQb1PUzBOByVUN1tvaliwLZfna-QU2r2hQ8,11345
103
+ splunk_soar_sdk-2.3.0.dist-info/RECORD,,