splunk-soar-sdk 2.1.0__py3-none-any.whl → 2.2.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.
- soar_sdk/action_results.py +27 -1
- soar_sdk/app.py +14 -2
- soar_sdk/decorators/action.py +11 -0
- soar_sdk/meta/actions.py +10 -4
- {splunk_soar_sdk-2.1.0.dist-info → splunk_soar_sdk-2.2.0.dist-info}/METADATA +1 -1
- {splunk_soar_sdk-2.1.0.dist-info → splunk_soar_sdk-2.2.0.dist-info}/RECORD +9 -9
- {splunk_soar_sdk-2.1.0.dist-info → splunk_soar_sdk-2.2.0.dist-info}/WHEEL +0 -0
- {splunk_soar_sdk-2.1.0.dist-info → splunk_soar_sdk-2.2.0.dist-info}/entry_points.txt +0 -0
- {splunk_soar_sdk-2.1.0.dist-info → splunk_soar_sdk-2.2.0.dist-info}/licenses/LICENSE +0 -0
soar_sdk/action_results.py
CHANGED
|
@@ -83,12 +83,16 @@ class OutputFieldSpecification(TypedDict):
|
|
|
83
83
|
data_type: str
|
|
84
84
|
contains: NotRequired[list[str]]
|
|
85
85
|
example_values: NotRequired[list[Union[str, float, bool]]]
|
|
86
|
+
column_name: NotRequired[str]
|
|
87
|
+
column_order: NotRequired[int]
|
|
86
88
|
|
|
87
89
|
|
|
88
90
|
def OutputField(
|
|
89
91
|
cef_types: Optional[list[str]] = None,
|
|
90
92
|
example_values: Optional[list[Union[str, float, bool]]] = None,
|
|
91
93
|
alias: Optional[str] = None,
|
|
94
|
+
column_name: Optional[str] = None,
|
|
95
|
+
column_order: Optional[int] = None,
|
|
92
96
|
) -> Any: # noqa: ANN401
|
|
93
97
|
"""Define metadata for an action output field.
|
|
94
98
|
|
|
@@ -102,6 +106,11 @@ def OutputField(
|
|
|
102
106
|
example_values: Optional list of example values for this field, used
|
|
103
107
|
in documentation and for testing/validation purposes.
|
|
104
108
|
alias: Optional alternative name for the field when serialized.
|
|
109
|
+
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
|
+
|
|
112
|
+
Note:
|
|
113
|
+
Column name and order must be set together, if one is set but the other is not, an error will be raised.
|
|
105
114
|
|
|
106
115
|
Returns:
|
|
107
116
|
A Pydantic Field object with the specified metadata.
|
|
@@ -116,8 +125,10 @@ def OutputField(
|
|
|
116
125
|
"""
|
|
117
126
|
return Field(
|
|
118
127
|
examples=example_values,
|
|
119
|
-
cef_types=cef_types,
|
|
120
128
|
alias=alias,
|
|
129
|
+
cef_types=cef_types,
|
|
130
|
+
column_name=column_name,
|
|
131
|
+
column_order=column_order,
|
|
121
132
|
)
|
|
122
133
|
|
|
123
134
|
|
|
@@ -230,6 +241,21 @@ class ActionOutput(BaseModel):
|
|
|
230
241
|
if field_type is bool:
|
|
231
242
|
schema_field["example_values"] = [True, False]
|
|
232
243
|
|
|
244
|
+
# Validate column metadata - both column_name and column_order must be present together
|
|
245
|
+
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
|
+
|
|
255
|
+
if column_name is not None and column_order is not None:
|
|
256
|
+
schema_field["column_name"] = column_name
|
|
257
|
+
schema_field["column_order"] = column_order
|
|
258
|
+
|
|
233
259
|
yield schema_field
|
|
234
260
|
|
|
235
261
|
|
soar_sdk/app.py
CHANGED
|
@@ -237,6 +237,7 @@ class App:
|
|
|
237
237
|
read_only: bool = True,
|
|
238
238
|
params_class: Optional[type[Params]] = None,
|
|
239
239
|
output_class: Optional[type[ActionOutput]] = None,
|
|
240
|
+
render_as: Optional[str] = None,
|
|
240
241
|
view_handler: Union[str, Callable, None] = None,
|
|
241
242
|
view_template: Optional[str] = None,
|
|
242
243
|
versions: str = "EQ(*)",
|
|
@@ -355,6 +356,7 @@ class App:
|
|
|
355
356
|
read_only=read_only,
|
|
356
357
|
params_class=params_class,
|
|
357
358
|
output_class=output_class,
|
|
359
|
+
render_as=render_as,
|
|
358
360
|
view_handler=view_handler,
|
|
359
361
|
versions=versions,
|
|
360
362
|
summary_type=summary_type,
|
|
@@ -421,6 +423,7 @@ class App:
|
|
|
421
423
|
read_only: bool = True,
|
|
422
424
|
params_class: Optional[type[Params]] = None,
|
|
423
425
|
output_class: Optional[type[ActionOutput]] = None,
|
|
426
|
+
render_as: Optional[str] = None,
|
|
424
427
|
view_handler: Optional[Callable] = None,
|
|
425
428
|
versions: str = "EQ(*)",
|
|
426
429
|
summary_type: Optional[type[ActionOutput]] = None,
|
|
@@ -440,6 +443,7 @@ class App:
|
|
|
440
443
|
read_only=read_only,
|
|
441
444
|
params_class=params_class,
|
|
442
445
|
output_class=output_class,
|
|
446
|
+
render_as=render_as,
|
|
443
447
|
view_handler=view_handler,
|
|
444
448
|
versions=versions,
|
|
445
449
|
summary_type=summary_type,
|
|
@@ -647,9 +651,17 @@ class App:
|
|
|
647
651
|
statuses = []
|
|
648
652
|
for item in result:
|
|
649
653
|
statuses.append(
|
|
650
|
-
App._adapt_action_result(
|
|
654
|
+
App._adapt_action_result(
|
|
655
|
+
item, actions_manager, action_params, message, summary
|
|
656
|
+
)
|
|
657
|
+
)
|
|
658
|
+
# Handle empty list/iterator case
|
|
659
|
+
if not statuses:
|
|
660
|
+
result = ActionOutput(
|
|
661
|
+
status=True, message=message or "Action completed successfully"
|
|
651
662
|
)
|
|
652
|
-
|
|
663
|
+
else:
|
|
664
|
+
return all(statuses)
|
|
653
665
|
|
|
654
666
|
if isinstance(result, ActionOutput):
|
|
655
667
|
output_dict = result.dict(by_alias=True)
|
soar_sdk/decorators/action.py
CHANGED
|
@@ -39,6 +39,7 @@ class ActionDecorator:
|
|
|
39
39
|
AsyncGenerator[type[ActionOutput]],
|
|
40
40
|
list[type[ActionOutput]],
|
|
41
41
|
] = None,
|
|
42
|
+
render_as: Optional[str] = None,
|
|
42
43
|
view_handler: Optional[Callable] = None,
|
|
43
44
|
versions: str = "EQ(*)",
|
|
44
45
|
summary_type: Optional[type[ActionOutput]] = None,
|
|
@@ -53,6 +54,7 @@ class ActionDecorator:
|
|
|
53
54
|
self.read_only = read_only
|
|
54
55
|
self.params_class = params_class
|
|
55
56
|
self.output_class = output_class
|
|
57
|
+
self.render_as = render_as
|
|
56
58
|
self.view_handler = view_handler
|
|
57
59
|
self.versions = versions
|
|
58
60
|
self.summary_type = summary_type
|
|
@@ -100,6 +102,14 @@ class ActionDecorator:
|
|
|
100
102
|
"Return type for action function must be derived from ActionOutput class."
|
|
101
103
|
)
|
|
102
104
|
|
|
105
|
+
if self.view_handler:
|
|
106
|
+
self.render_as = "custom"
|
|
107
|
+
|
|
108
|
+
if self.render_as and self.render_as not in ("table", "json", "custom"):
|
|
109
|
+
raise ValueError(
|
|
110
|
+
"Please only specify render_as as 'table' or 'json' or 'custom'."
|
|
111
|
+
)
|
|
112
|
+
|
|
103
113
|
@action_protocol
|
|
104
114
|
@wraps(function)
|
|
105
115
|
def inner(
|
|
@@ -152,6 +162,7 @@ class ActionDecorator:
|
|
|
152
162
|
parameters=validated_params_class,
|
|
153
163
|
output=validated_output_class,
|
|
154
164
|
versions=self.versions,
|
|
165
|
+
render_as=self.render_as,
|
|
155
166
|
view_handler=self.view_handler,
|
|
156
167
|
summary_type=self.summary_type,
|
|
157
168
|
enable_concurrency_lock=self.enable_concurrency_lock,
|
soar_sdk/meta/actions.py
CHANGED
|
@@ -20,6 +20,7 @@ class ActionMeta(BaseModel):
|
|
|
20
20
|
verbose: str = ""
|
|
21
21
|
parameters: Type[Params] = Field(default=Params) # noqa: UP006
|
|
22
22
|
output: Type[ActionOutput] = Field(default=ActionOutput) # noqa: UP006
|
|
23
|
+
render_as: Optional[str] = None
|
|
23
24
|
view_handler: Optional[Callable] = None
|
|
24
25
|
summary_type: Optional[Type[ActionOutput]] = Field(default=None, exclude=True) # noqa: UP006
|
|
25
26
|
enable_concurrency_lock: bool = False
|
|
@@ -31,6 +32,13 @@ class ActionMeta(BaseModel):
|
|
|
31
32
|
data["output"] = OutputsSerializer.serialize_datapaths(
|
|
32
33
|
self.parameters, self.output, summary_class=self.summary_type
|
|
33
34
|
)
|
|
35
|
+
if self.view_handler:
|
|
36
|
+
self.render_as = "custom"
|
|
37
|
+
|
|
38
|
+
if self.render_as:
|
|
39
|
+
data["render"] = {
|
|
40
|
+
"type": self.render_as,
|
|
41
|
+
}
|
|
34
42
|
|
|
35
43
|
if self.view_handler:
|
|
36
44
|
remove_when_soar_newer_than("6.4.1")
|
|
@@ -45,13 +53,11 @@ class ActionMeta(BaseModel):
|
|
|
45
53
|
else:
|
|
46
54
|
relative_module = module
|
|
47
55
|
|
|
48
|
-
data["render"] = {
|
|
49
|
-
"type": "custom",
|
|
50
|
-
"view": f"{relative_module}.{self.view_handler.__name__}",
|
|
51
|
-
}
|
|
56
|
+
data["render"]["view"] = f"{relative_module}.{self.view_handler.__name__}"
|
|
52
57
|
|
|
53
58
|
# Remove view_handler from the output since in render
|
|
54
59
|
data.pop("view_handler", None)
|
|
60
|
+
data.pop("render_as", None)
|
|
55
61
|
|
|
56
62
|
if self.enable_concurrency_lock:
|
|
57
63
|
data["lock"] = {"enabled": True}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: splunk-soar-sdk
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.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,8 +1,8 @@
|
|
|
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=
|
|
3
|
+
soar_sdk/action_results.py,sha256=5Lr3J1G2hcVVTQD35eo7OG3Cvr7eGZgb-FL2Eb5pBx8,11424
|
|
4
4
|
soar_sdk/actions_manager.py,sha256=wJCyfzkI_6OKZ-Kmll4vRJpGvYdL93Uw-JyEEGnKcw0,5779
|
|
5
|
-
soar_sdk/app.py,sha256=
|
|
5
|
+
soar_sdk/app.py,sha256=lYvaDUYEV6b_uF4CyPh6wlAIbCNISya7bJ8Z2xJyyGY,33356
|
|
6
6
|
soar_sdk/app_cli_runner.py,sha256=fJoozhyAt7QUMuc02nE5RL_InpsjQBpr6U4rF9sey3E,11627
|
|
7
7
|
soar_sdk/app_client.py,sha256=0r3jIvMM8szCEHXOgRu07VaovKH96pZut5rn2GfYcsc,6275
|
|
8
8
|
soar_sdk/asset.py,sha256=deS8_B5hr7W2fED8_6wUpVriRgiQ5r8TkGVHiasIaro,10666
|
|
@@ -49,14 +49,14 @@ soar_sdk/code_renderers/renderer.py,sha256=oDlmU-MzDqpc4Yq6zupoT4XN-vRjgL4UV5lOG
|
|
|
49
49
|
soar_sdk/code_renderers/toml_renderer.py,sha256=-zP8UzlYMCVVA5ex9slaNLeFTu4xLjkv88YLmRNLrTM,1505
|
|
50
50
|
soar_sdk/code_renderers/templates/pyproject.toml.jinja,sha256=Ti6A5kWMb902Lbd1kmw8qPgVDPNNzlV6rd0pcVEbVUo,3917
|
|
51
51
|
soar_sdk/decorators/__init__.py,sha256=ttvapTczeQpReZVYgjTw4qnEqKd7b8pR7lNaCpO0npQ,513
|
|
52
|
-
soar_sdk/decorators/action.py,sha256=
|
|
52
|
+
soar_sdk/decorators/action.py,sha256=ZuSsowsBeEuenGQw03iyd2uv-Ymp9kQYnhYsx7k7pX8,6695
|
|
53
53
|
soar_sdk/decorators/make_request.py,sha256=W_ltGvryTvdKomiJ8gL7rE_KVc1VVodhFYstGxB8d4Q,5527
|
|
54
54
|
soar_sdk/decorators/on_poll.py,sha256=xdT0QSa_dnh37XdJNGW-DAZsb9oQO5tjxPbIQmWpaZs,8232
|
|
55
55
|
soar_sdk/decorators/test_connectivity.py,sha256=8uXMD4NW5bokpsAfBctUrfOR4K_geYLEZUY0Y6uI6aU,3568
|
|
56
56
|
soar_sdk/decorators/view_handler.py,sha256=jhBzbJcokWOeUWR4_orDRWTXiiVwE9RZdRSvNUYF3S0,7362
|
|
57
57
|
soar_sdk/decorators/webhook.py,sha256=Pde0MjxC2kckpxoKb3m4WVfLWtiFAAzAwZe5AF5VIbE,2400
|
|
58
58
|
soar_sdk/meta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
|
-
soar_sdk/meta/actions.py,sha256=
|
|
59
|
+
soar_sdk/meta/actions.py,sha256=nrTBwCnCwjOzof-_8Ww9vfoTIfZfauiL6C6DTRg2Mhs,2580
|
|
60
60
|
soar_sdk/meta/adapters.py,sha256=KjSYIUtkCz2eesA_vhsNCjfi5C-Uz71tbSuDIjhuB8U,1112
|
|
61
61
|
soar_sdk/meta/app.py,sha256=rwgoFfIFnLutLB9PF1kR7X1neKs175VA0Xx4KpJ6c7I,1765
|
|
62
62
|
soar_sdk/meta/datatypes.py,sha256=piR-oBVAATiRciXSdVE7XaqjUZTgSaOvTEqcOcNvCS0,795
|
|
@@ -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.
|
|
100
|
-
splunk_soar_sdk-2.
|
|
101
|
-
splunk_soar_sdk-2.
|
|
102
|
-
splunk_soar_sdk-2.
|
|
103
|
-
splunk_soar_sdk-2.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|