splunk-soar-sdk 2.3.6__py3-none-any.whl → 3.0.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/abstract.py +38 -41
- soar_sdk/action_results.py +41 -18
- soar_sdk/actions_manager.py +10 -13
- soar_sdk/apis/utils.py +3 -3
- soar_sdk/apis/vault.py +10 -10
- soar_sdk/app.py +58 -51
- soar_sdk/app_cli_runner.py +8 -8
- soar_sdk/app_client.py +10 -10
- soar_sdk/asset.py +45 -33
- soar_sdk/async_utils.py +2 -2
- soar_sdk/cli/init/cli.py +7 -9
- soar_sdk/cli/manifests/deserializers.py +15 -15
- soar_sdk/cli/manifests/processors.py +4 -10
- soar_sdk/cli/manifests/serializers.py +16 -8
- soar_sdk/cli/package/cli.py +6 -6
- soar_sdk/cli/package/utils.py +1 -1
- soar_sdk/code_renderers/action_renderer.py +35 -18
- soar_sdk/code_renderers/app_renderer.py +1 -2
- soar_sdk/code_renderers/asset_renderer.py +4 -5
- soar_sdk/code_renderers/renderer.py +2 -2
- soar_sdk/code_renderers/templates/pyproject.toml.jinja +1 -1
- soar_sdk/compat.py +6 -6
- soar_sdk/decorators/action.py +14 -15
- soar_sdk/decorators/make_request.py +4 -3
- soar_sdk/decorators/on_poll.py +5 -4
- soar_sdk/decorators/test_connectivity.py +2 -2
- soar_sdk/decorators/view_handler.py +11 -17
- soar_sdk/decorators/webhook.py +1 -2
- soar_sdk/exceptions.py +1 -4
- soar_sdk/field_utils.py +8 -0
- soar_sdk/input_spec.py +13 -17
- soar_sdk/logging.py +3 -3
- soar_sdk/meta/actions.py +6 -22
- soar_sdk/meta/app.py +10 -7
- soar_sdk/meta/dependencies.py +48 -42
- soar_sdk/meta/webhooks.py +12 -12
- soar_sdk/models/artifact.py +20 -23
- soar_sdk/models/container.py +30 -33
- soar_sdk/models/vault_attachment.py +6 -6
- soar_sdk/models/view.py +10 -13
- soar_sdk/params.py +57 -39
- soar_sdk/shims/phantom/action_result.py +4 -4
- soar_sdk/shims/phantom/base_connector.py +13 -5
- soar_sdk/shims/phantom/install_info.py +15 -2
- soar_sdk/shims/phantom/ph_ipc.py +3 -3
- soar_sdk/shims/phantom/vault.py +35 -34
- soar_sdk/types.py +3 -2
- soar_sdk/views/template_filters.py +4 -4
- soar_sdk/views/template_renderer.py +2 -2
- soar_sdk/views/view_parser.py +3 -4
- soar_sdk/webhooks/models.py +7 -6
- soar_sdk/webhooks/routing.py +4 -3
- {splunk_soar_sdk-2.3.6.dist-info → splunk_soar_sdk-3.0.0.dist-info}/METADATA +5 -6
- splunk_soar_sdk-3.0.0.dist-info/RECORD +104 -0
- splunk_soar_sdk-2.3.6.dist-info/RECORD +0 -103
- {splunk_soar_sdk-2.3.6.dist-info → splunk_soar_sdk-3.0.0.dist-info}/WHEEL +0 -0
- {splunk_soar_sdk-2.3.6.dist-info → splunk_soar_sdk-3.0.0.dist-info}/entry_points.txt +0 -0
- {splunk_soar_sdk-2.3.6.dist-info → splunk_soar_sdk-3.0.0.dist-info}/licenses/LICENSE +0 -0
soar_sdk/abstract.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
-
from typing import Any,
|
|
2
|
+
from typing import Any, Generic, TypeVar
|
|
3
3
|
from collections.abc import Mapping, Iterable, AsyncIterable
|
|
4
4
|
|
|
5
5
|
from soar_sdk.apis.vault import Vault
|
|
@@ -8,9 +8,9 @@ from soar_sdk.apis.container import Container
|
|
|
8
8
|
from soar_sdk.action_results import ActionOutput
|
|
9
9
|
import httpx
|
|
10
10
|
from pydantic.dataclasses import dataclass
|
|
11
|
-
from pydantic import
|
|
11
|
+
from pydantic import field_validator
|
|
12
12
|
|
|
13
|
-
JSONType =
|
|
13
|
+
JSONType = dict[str, Any] | list[Any] | str | int | float | bool | None
|
|
14
14
|
SummaryType = TypeVar("SummaryType", bound=ActionOutput)
|
|
15
15
|
|
|
16
16
|
|
|
@@ -23,7 +23,8 @@ class SOARClientAuth:
|
|
|
23
23
|
password: str = ""
|
|
24
24
|
user_session_token: str = ""
|
|
25
25
|
|
|
26
|
-
@
|
|
26
|
+
@field_validator("base_url")
|
|
27
|
+
@classmethod
|
|
27
28
|
def validate_phantom_url(cls, value: str) -> str:
|
|
28
29
|
"""Validate and format the base URL for the SOAR API."""
|
|
29
30
|
return (
|
|
@@ -74,13 +75,13 @@ class SOARClient(Generic[SummaryType]):
|
|
|
74
75
|
self,
|
|
75
76
|
endpoint: str,
|
|
76
77
|
*,
|
|
77
|
-
params:
|
|
78
|
-
headers:
|
|
79
|
-
cookies:
|
|
80
|
-
timeout:
|
|
81
|
-
auth:
|
|
78
|
+
params: dict[str, Any] | httpx.QueryParams | None = None,
|
|
79
|
+
headers: dict[str, str] | None = None,
|
|
80
|
+
cookies: dict[str, str] | None = None,
|
|
81
|
+
timeout: httpx.Timeout | None = None,
|
|
82
|
+
auth: httpx.Auth | tuple[str, str] | None = None,
|
|
82
83
|
follow_redirects: bool = False,
|
|
83
|
-
extensions:
|
|
84
|
+
extensions: Mapping[str, Any] | None = None,
|
|
84
85
|
) -> httpx.Response:
|
|
85
86
|
"""Perform a GET request to the specific endpoint using the SOAR client."""
|
|
86
87
|
response = self.client.get(
|
|
@@ -100,19 +101,17 @@ class SOARClient(Generic[SummaryType]):
|
|
|
100
101
|
self,
|
|
101
102
|
endpoint: str,
|
|
102
103
|
*,
|
|
103
|
-
content:
|
|
104
|
-
|
|
105
|
-
] = None,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
auth: Optional[Union[httpx.Auth, tuple[str, str]]] = None,
|
|
113
|
-
timeout: Optional[Union[float, httpx.Timeout]] = None,
|
|
104
|
+
content: str | bytes | Iterable[bytes] | AsyncIterable[bytes] | None = None,
|
|
105
|
+
data: Mapping[str, Any] | None = None,
|
|
106
|
+
files: dict[str, Any] | None = None,
|
|
107
|
+
json: JSONType | None = None,
|
|
108
|
+
params: dict[str, Any] | None = None,
|
|
109
|
+
headers: dict[str, str] | None = None,
|
|
110
|
+
cookies: dict[str, str] | None = None,
|
|
111
|
+
auth: httpx.Auth | tuple[str, str] | None = None,
|
|
112
|
+
timeout: float | httpx.Timeout | None = None,
|
|
114
113
|
follow_redirects: bool = True,
|
|
115
|
-
extensions:
|
|
114
|
+
extensions: Mapping[str, Any] | None = None,
|
|
116
115
|
) -> httpx.Response:
|
|
117
116
|
"""Perform a POST request to the specific endpoint using the SOAR client."""
|
|
118
117
|
headers = headers or {}
|
|
@@ -138,19 +137,17 @@ class SOARClient(Generic[SummaryType]):
|
|
|
138
137
|
self,
|
|
139
138
|
endpoint: str,
|
|
140
139
|
*,
|
|
141
|
-
content:
|
|
142
|
-
|
|
143
|
-
] = None,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
auth: Optional[Union[httpx.Auth, tuple[str, str]]] = None,
|
|
151
|
-
timeout: Optional[Union[float, httpx.Timeout]] = None,
|
|
140
|
+
content: str | bytes | Iterable[bytes] | AsyncIterable[bytes] | None = None,
|
|
141
|
+
data: Mapping[str, Any] | None = None,
|
|
142
|
+
files: dict[str, Any] | None = None,
|
|
143
|
+
json: JSONType | None = None,
|
|
144
|
+
params: dict[str, Any] | None = None,
|
|
145
|
+
headers: dict[str, str] | None = None,
|
|
146
|
+
cookies: dict[str, str] | None = None,
|
|
147
|
+
auth: httpx.Auth | tuple[str, str] | None = None,
|
|
148
|
+
timeout: float | httpx.Timeout | None = None,
|
|
152
149
|
follow_redirects: bool = True,
|
|
153
|
-
extensions:
|
|
150
|
+
extensions: Mapping[str, Any] | None = None,
|
|
154
151
|
) -> httpx.Response:
|
|
155
152
|
"""Perform a PUT request to the specific endpoint using the SOAR client."""
|
|
156
153
|
headers = headers or {}
|
|
@@ -176,13 +173,13 @@ class SOARClient(Generic[SummaryType]):
|
|
|
176
173
|
self,
|
|
177
174
|
endpoint: str,
|
|
178
175
|
*,
|
|
179
|
-
params:
|
|
180
|
-
headers:
|
|
181
|
-
cookies:
|
|
182
|
-
auth:
|
|
183
|
-
timeout:
|
|
176
|
+
params: dict[str, Any] | httpx.QueryParams | None = None,
|
|
177
|
+
headers: dict[str, str] | None = None,
|
|
178
|
+
cookies: dict[str, str] | None = None,
|
|
179
|
+
auth: httpx.Auth | tuple[str, str] | None = None,
|
|
180
|
+
timeout: httpx.Timeout | None = None,
|
|
184
181
|
follow_redirects: bool = False,
|
|
185
|
-
extensions:
|
|
182
|
+
extensions: Mapping[str, Any] | None = None,
|
|
186
183
|
) -> httpx.Response:
|
|
187
184
|
"""Perform a DELETE request to the specific endpoint using the SOAR client."""
|
|
188
185
|
headers = headers or {}
|
|
@@ -229,7 +226,7 @@ class SOARClient(Generic[SummaryType]):
|
|
|
229
226
|
pass
|
|
230
227
|
|
|
231
228
|
@abstractmethod
|
|
232
|
-
def get_summary(self) ->
|
|
229
|
+
def get_summary(self) -> SummaryType | None:
|
|
233
230
|
"""Get the summary for the action run."""
|
|
234
231
|
pass
|
|
235
232
|
|
soar_sdk/action_results.py
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Union, get_origin, get_args, Any
|
|
2
2
|
from collections.abc import Iterator
|
|
3
|
-
from typing_extensions import
|
|
4
|
-
from
|
|
3
|
+
from typing_extensions import TypedDict
|
|
4
|
+
from typing import NotRequired
|
|
5
|
+
from pydantic import BaseModel, Field, ConfigDict
|
|
5
6
|
import itertools
|
|
7
|
+
import types
|
|
6
8
|
|
|
7
9
|
from soar_sdk.compat import remove_when_soar_newer_than
|
|
8
10
|
from soar_sdk.shims.phantom.action_result import ActionResult as PhantomActionResult
|
|
9
11
|
from soar_sdk.meta.datatypes import as_datatype
|
|
12
|
+
from soar_sdk.field_utils import parse_json_schema_extra
|
|
10
13
|
|
|
11
14
|
remove_when_soar_newer_than(
|
|
12
15
|
"7.0.0", "NotRequired from typing_extensions is in typing in Python 3.11+"
|
|
@@ -40,7 +43,7 @@ class ActionResult(PhantomActionResult):
|
|
|
40
43
|
self,
|
|
41
44
|
status: bool,
|
|
42
45
|
message: str,
|
|
43
|
-
param:
|
|
46
|
+
param: dict | None = None,
|
|
44
47
|
) -> None:
|
|
45
48
|
"""Initialize an ActionResult with status, message, and optional parameters.
|
|
46
49
|
|
|
@@ -82,16 +85,16 @@ class OutputFieldSpecification(TypedDict):
|
|
|
82
85
|
data_path: str
|
|
83
86
|
data_type: str
|
|
84
87
|
contains: NotRequired[list[str]]
|
|
85
|
-
example_values: NotRequired[list[
|
|
88
|
+
example_values: NotRequired[list[str | float | bool]]
|
|
86
89
|
column_name: NotRequired[str]
|
|
87
90
|
column_order: NotRequired[int]
|
|
88
91
|
|
|
89
92
|
|
|
90
93
|
def OutputField(
|
|
91
|
-
cef_types:
|
|
92
|
-
example_values:
|
|
93
|
-
alias:
|
|
94
|
-
column_name:
|
|
94
|
+
cef_types: list[str] | None = None,
|
|
95
|
+
example_values: list[str | float | bool] | None = None,
|
|
96
|
+
alias: str | None = None,
|
|
97
|
+
column_name: str | None = None,
|
|
95
98
|
) -> Any: # noqa: ANN401
|
|
96
99
|
"""Define metadata for an action output field.
|
|
97
100
|
|
|
@@ -121,11 +124,18 @@ def OutputField(
|
|
|
121
124
|
... )
|
|
122
125
|
... count: int = OutputField(example_values=[1, 5, 10])
|
|
123
126
|
"""
|
|
127
|
+
json_schema_extra: dict[str, Any] = {}
|
|
128
|
+
if cef_types is not None:
|
|
129
|
+
json_schema_extra["cef_types"] = cef_types
|
|
130
|
+
if example_values is not None:
|
|
131
|
+
json_schema_extra["examples"] = example_values
|
|
132
|
+
if column_name is not None:
|
|
133
|
+
json_schema_extra["column_name"] = column_name
|
|
134
|
+
|
|
124
135
|
return Field(
|
|
125
|
-
|
|
136
|
+
default=...,
|
|
126
137
|
alias=alias,
|
|
127
|
-
|
|
128
|
-
column_name=column_name,
|
|
138
|
+
json_schema_extra=json_schema_extra if json_schema_extra else None,
|
|
129
139
|
)
|
|
130
140
|
|
|
131
141
|
|
|
@@ -158,11 +168,14 @@ class ActionOutput(BaseModel):
|
|
|
158
168
|
Nested ActionOutput classes are supported for complex data structures.
|
|
159
169
|
"""
|
|
160
170
|
|
|
171
|
+
# Allow instantiation with both field names and aliases for backward compatibility
|
|
172
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
173
|
+
|
|
161
174
|
@classmethod
|
|
162
175
|
def _to_json_schema(
|
|
163
176
|
cls,
|
|
164
177
|
parent_datapath: str = "action_result.data.*",
|
|
165
|
-
column_order_counter:
|
|
178
|
+
column_order_counter: itertools.count | None = None,
|
|
166
179
|
) -> Iterator[OutputFieldSpecification]:
|
|
167
180
|
"""Convert the ActionOutput class to SOAR-compatible JSON schema.
|
|
168
181
|
|
|
@@ -191,15 +204,18 @@ class ActionOutput(BaseModel):
|
|
|
191
204
|
if column_order_counter is None:
|
|
192
205
|
column_order_counter = itertools.count()
|
|
193
206
|
|
|
194
|
-
for _field_name, field in cls.
|
|
207
|
+
for _field_name, field in cls.model_fields.items():
|
|
195
208
|
field_name = alias if (alias := field.alias) else _field_name
|
|
196
209
|
|
|
197
210
|
field_type = field.annotation
|
|
211
|
+
if field_type is None:
|
|
212
|
+
continue
|
|
213
|
+
|
|
198
214
|
datapath = parent_datapath + f".{field_name}"
|
|
199
215
|
|
|
200
216
|
# Handle lists and optional types, even nested ones
|
|
201
217
|
origin = get_origin(field_type)
|
|
202
|
-
while origin in [list, Union,
|
|
218
|
+
while origin in [list, Union, types.UnionType]:
|
|
203
219
|
type_args = [
|
|
204
220
|
arg
|
|
205
221
|
for arg in get_args(field_type)
|
|
@@ -221,6 +237,11 @@ class ActionOutput(BaseModel):
|
|
|
221
237
|
field_type = type_args[0]
|
|
222
238
|
origin = get_origin(field_type)
|
|
223
239
|
|
|
240
|
+
if not isinstance(field_type, type):
|
|
241
|
+
raise TypeError(
|
|
242
|
+
f"Output field {field_name} has invalid type annotation: {field_type}"
|
|
243
|
+
)
|
|
244
|
+
|
|
224
245
|
if issubclass(field_type, ActionOutput):
|
|
225
246
|
# If the field is another ActionOutput, recursively call _to_json_schema
|
|
226
247
|
yield from field_type._to_json_schema(datapath, column_order_counter)
|
|
@@ -237,15 +258,17 @@ class ActionOutput(BaseModel):
|
|
|
237
258
|
data_path=datapath, data_type=type_name
|
|
238
259
|
)
|
|
239
260
|
|
|
240
|
-
|
|
261
|
+
json_schema_extra = parse_json_schema_extra(field.json_schema_extra)
|
|
262
|
+
|
|
263
|
+
if cef_types := json_schema_extra.get("cef_types"):
|
|
241
264
|
schema_field["contains"] = cef_types
|
|
242
|
-
if examples :=
|
|
265
|
+
if examples := json_schema_extra.get("examples"):
|
|
243
266
|
schema_field["example_values"] = examples
|
|
244
267
|
|
|
245
268
|
if field_type is bool:
|
|
246
269
|
schema_field["example_values"] = [True, False]
|
|
247
270
|
|
|
248
|
-
column_name =
|
|
271
|
+
column_name = json_schema_extra.get("column_name")
|
|
249
272
|
|
|
250
273
|
if column_name is not None:
|
|
251
274
|
schema_field["column_name"] = column_name
|
soar_sdk/actions_manager.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Any
|
|
2
2
|
import os
|
|
3
3
|
|
|
4
|
+
from soar_sdk.compat import remove_when_soar_newer_than
|
|
4
5
|
from soar_sdk.input_spec import InputSpecification
|
|
5
6
|
from soar_sdk.shims.phantom.base_connector import BaseConnector
|
|
6
7
|
|
|
@@ -8,13 +9,9 @@ from soar_sdk.meta.actions import ActionMeta
|
|
|
8
9
|
from soar_sdk.types import Action
|
|
9
10
|
from pydantic import ValidationError
|
|
10
11
|
from soar_sdk.shims.phantom.action_result import ActionResult as PhantomActionResult
|
|
12
|
+
from soar_sdk.shims.phantom.install_info import is_onprem_broker_install
|
|
11
13
|
from soar_sdk.logging import getLogger
|
|
12
14
|
|
|
13
|
-
try:
|
|
14
|
-
from phantom_common import install_info
|
|
15
|
-
except ImportError:
|
|
16
|
-
install_info = None
|
|
17
|
-
|
|
18
15
|
|
|
19
16
|
_INGEST_STATE_KEY = "ingestion_state"
|
|
20
17
|
_AUTH_STATE_KEY = "auth_state"
|
|
@@ -34,7 +31,7 @@ class ActionsManager(BaseConnector):
|
|
|
34
31
|
self.auth_state: dict = {}
|
|
35
32
|
self.asset_cache: dict = {}
|
|
36
33
|
|
|
37
|
-
def get_action(self, identifier: str) ->
|
|
34
|
+
def get_action(self, identifier: str) -> Action | None:
|
|
38
35
|
"""Convenience method for getting an Action callable from its identifier.
|
|
39
36
|
|
|
40
37
|
Returns None if there are no actions managed by this object matching the given
|
|
@@ -63,14 +60,12 @@ class ActionsManager(BaseConnector):
|
|
|
63
60
|
"""
|
|
64
61
|
self._actions[action_identifier] = wrapped_function
|
|
65
62
|
|
|
66
|
-
def handle(
|
|
67
|
-
self, input_data: InputSpecification, handle: Optional[int] = None
|
|
68
|
-
) -> str:
|
|
63
|
+
def handle(self, input_data: InputSpecification, handle: int | None = None) -> str:
|
|
69
64
|
"""Runs handling of the input data on connector."""
|
|
70
65
|
action_id = input_data.identifier
|
|
71
66
|
if self.get_action(action_id):
|
|
72
67
|
self.print_progress_message = True
|
|
73
|
-
return self._handle_action(input_data.
|
|
68
|
+
return self._handle_action(input_data.model_dump_json(), handle or 0)
|
|
74
69
|
else:
|
|
75
70
|
raise RuntimeError(
|
|
76
71
|
f"Action {action_id} not recognized"
|
|
@@ -89,7 +84,7 @@ class ActionsManager(BaseConnector):
|
|
|
89
84
|
|
|
90
85
|
if handler := self.get_action(action_id):
|
|
91
86
|
try:
|
|
92
|
-
params = handler.meta.parameters.
|
|
87
|
+
params = handler.meta.parameters.model_validate(param)
|
|
93
88
|
except (ValueError, ValidationError) as e:
|
|
94
89
|
self.save_progress(
|
|
95
90
|
f"Validation Error - the params data for action could not be parsed: {e!s}"
|
|
@@ -150,8 +145,10 @@ class ActionsManager(BaseConnector):
|
|
|
150
145
|
|
|
151
146
|
Returns APP_HOME directly on brokers, which contains the correct SDK app path.
|
|
152
147
|
"""
|
|
148
|
+
# Remove when 7.1.0 is the min supported broker version
|
|
149
|
+
remove_when_soar_newer_than("7.1.1")
|
|
153
150
|
# On AB, APP_HOME is set by spawn to the full app path at runtime
|
|
154
|
-
if
|
|
151
|
+
if is_onprem_broker_install():
|
|
155
152
|
return os.getenv("APP_HOME", "")
|
|
156
153
|
|
|
157
154
|
# For non-broker just proceed as we did before
|
soar_sdk/apis/utils.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import httpx
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Any
|
|
3
3
|
from collections.abc import Generator
|
|
4
4
|
|
|
5
5
|
|
|
@@ -11,9 +11,9 @@ def is_client_authenticated(client: httpx.Client) -> bool:
|
|
|
11
11
|
def get_request_iter_pages(
|
|
12
12
|
client: httpx.Client,
|
|
13
13
|
endpoint: str,
|
|
14
|
-
params:
|
|
14
|
+
params: dict | None = None,
|
|
15
15
|
page_size: int = 50,
|
|
16
|
-
) -> Generator[Any
|
|
16
|
+
) -> Generator[Any]:
|
|
17
17
|
"""Iterate through REST JSON results using the provided paging."""
|
|
18
18
|
params = params or {}
|
|
19
19
|
|
soar_sdk/apis/vault.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
2
|
from soar_sdk.models.vault_attachment import VaultAttachment
|
|
3
3
|
from soar_sdk.shims.phantom.vault import PhantomVault, VaultBase
|
|
4
4
|
|
|
@@ -47,9 +47,9 @@ class Vault:
|
|
|
47
47
|
def create_attachment(
|
|
48
48
|
self,
|
|
49
49
|
container_id: int,
|
|
50
|
-
file_content:
|
|
50
|
+
file_content: str | bytes,
|
|
51
51
|
file_name: str,
|
|
52
|
-
metadata:
|
|
52
|
+
metadata: dict[str, str] | None = None,
|
|
53
53
|
) -> str:
|
|
54
54
|
"""Create a vault attachment from file content.
|
|
55
55
|
|
|
@@ -87,7 +87,7 @@ class Vault:
|
|
|
87
87
|
container_id: int,
|
|
88
88
|
file_location: str,
|
|
89
89
|
file_name: str,
|
|
90
|
-
metadata:
|
|
90
|
+
metadata: dict[str, str] | None = None,
|
|
91
91
|
) -> str:
|
|
92
92
|
"""Add an existing file to the vault as an attachment.
|
|
93
93
|
|
|
@@ -121,9 +121,9 @@ class Vault:
|
|
|
121
121
|
|
|
122
122
|
def get_attachment(
|
|
123
123
|
self,
|
|
124
|
-
vault_id:
|
|
125
|
-
file_name:
|
|
126
|
-
container_id:
|
|
124
|
+
vault_id: str | None = None,
|
|
125
|
+
file_name: str | None = None,
|
|
126
|
+
container_id: int | None = None,
|
|
127
127
|
) -> list[VaultAttachment]:
|
|
128
128
|
"""Retrieve attachment(s) from the vault.
|
|
129
129
|
|
|
@@ -161,9 +161,9 @@ class Vault:
|
|
|
161
161
|
|
|
162
162
|
def delete_attachment(
|
|
163
163
|
self,
|
|
164
|
-
vault_id:
|
|
165
|
-
file_name:
|
|
166
|
-
container_id:
|
|
164
|
+
vault_id: str | None = None,
|
|
165
|
+
file_name: str | None = None,
|
|
166
|
+
container_id: int | None = None,
|
|
167
167
|
remove_all: bool = False,
|
|
168
168
|
) -> list[str]:
|
|
169
169
|
"""Delete attachment(s) from the vault.
|