deltafi 2.13.0__tar.gz → 2.15.0__tar.gz
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 deltafi might be problematic. Click here for more details.
- {deltafi-2.13.0 → deltafi-2.15.0}/PKG-INFO +1 -1
- deltafi-2.15.0/deltafi/action.py +321 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/plugin.py +4 -16
- {deltafi-2.13.0 → deltafi-2.15.0}/pyproject.toml +1 -1
- deltafi-2.13.0/deltafi/action.py +0 -147
- {deltafi-2.13.0 → deltafi-2.15.0}/README.md +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/__init__.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/actioneventqueue.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/actiontype.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/domain.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/exception.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/genericmodel.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/input.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/logger.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/metric.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/result.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/storage.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/test_kit/__init__.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/test_kit/assertions.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/test_kit/compare_helpers.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/test_kit/constants.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/test_kit/egress.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/test_kit/framework.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/test_kit/timed_ingress.py +0 -0
- {deltafi-2.13.0 → deltafi-2.15.0}/deltafi/test_kit/transform.py +0 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#
|
|
2
|
+
# DeltaFi - Data transformation and enrichment platform
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2021-2025 DeltaFi Contributors <deltafi@deltafi.org>
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
from abc import ABC, abstractmethod
|
|
20
|
+
from typing import Any, List
|
|
21
|
+
|
|
22
|
+
from pydantic import BaseModel
|
|
23
|
+
|
|
24
|
+
from deltafi.actiontype import ActionType
|
|
25
|
+
from deltafi.domain import DeltaFileMessage
|
|
26
|
+
from deltafi.genericmodel import GenericModel
|
|
27
|
+
from deltafi.input import EgressInput, TransformInput
|
|
28
|
+
from deltafi.result import *
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Join(ABC):
|
|
32
|
+
def join(self, transform_inputs: List[TransformInput]):
|
|
33
|
+
all_content = []
|
|
34
|
+
all_metadata = {}
|
|
35
|
+
for transform_input in transform_inputs:
|
|
36
|
+
all_content += transform_input.content
|
|
37
|
+
all_metadata.update(transform_input.metadata)
|
|
38
|
+
return TransformInput(content=all_content, metadata=all_metadata)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ContentSpec:
|
|
42
|
+
name: str
|
|
43
|
+
media_type: str
|
|
44
|
+
description: str
|
|
45
|
+
|
|
46
|
+
def __init__(self, name: str = None, media_type: str = None, description: str = None):
|
|
47
|
+
self.name = name
|
|
48
|
+
self.media_type = media_type
|
|
49
|
+
self.description = description
|
|
50
|
+
|
|
51
|
+
def json(self):
|
|
52
|
+
json_dictionary = {}
|
|
53
|
+
if self.name is not None:
|
|
54
|
+
json_dictionary['name'] = self.name
|
|
55
|
+
if self.media_type is not None:
|
|
56
|
+
json_dictionary['mediaType'] = self.media_type
|
|
57
|
+
if self.description is not None:
|
|
58
|
+
json_dictionary['description'] = self.description
|
|
59
|
+
return json_dictionary
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class KeyedDescription:
|
|
63
|
+
key: str
|
|
64
|
+
description: str
|
|
65
|
+
|
|
66
|
+
def __init__(self, key: str, description: str):
|
|
67
|
+
self.key = key
|
|
68
|
+
self.description = description
|
|
69
|
+
|
|
70
|
+
def json(self):
|
|
71
|
+
json_dictionary = {}
|
|
72
|
+
if self.key is not None:
|
|
73
|
+
json_dictionary['key'] = self.key
|
|
74
|
+
json_dictionary['description'] = self.description
|
|
75
|
+
return json_dictionary
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class InputSpec:
|
|
79
|
+
content_summary: str
|
|
80
|
+
content_specs: List[ContentSpec]
|
|
81
|
+
metadata_summary: str
|
|
82
|
+
metadata_descriptions: List[KeyedDescription]
|
|
83
|
+
|
|
84
|
+
def __init__(self, content_summary: str = None, content_specs: List[ContentSpec] = None,
|
|
85
|
+
metadata_summary: str = None, metadata_descriptions: List[KeyedDescription] = None):
|
|
86
|
+
self.content_summary = content_summary
|
|
87
|
+
self.content_specs = content_specs
|
|
88
|
+
self.metadata_summary = metadata_summary
|
|
89
|
+
self.metadata_descriptions = metadata_descriptions
|
|
90
|
+
|
|
91
|
+
def json(self):
|
|
92
|
+
json_dictionary = {}
|
|
93
|
+
if self.content_summary is not None:
|
|
94
|
+
json_dictionary['contentSummary'] = self.content_summary
|
|
95
|
+
if self.content_specs is not None:
|
|
96
|
+
json_dictionary['contentSpecs'] = [cs.json() for cs in self.content_specs]
|
|
97
|
+
if self.metadata_summary is not None:
|
|
98
|
+
json_dictionary['metadataSummary'] = self.metadata_summary
|
|
99
|
+
if self.metadata_descriptions is not None:
|
|
100
|
+
json_dictionary['metadataDescriptions'] = [md.json() for md in self.metadata_descriptions]
|
|
101
|
+
return json_dictionary
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class OutputSpec:
|
|
105
|
+
content_summary: str
|
|
106
|
+
content_specs: List[ContentSpec]
|
|
107
|
+
metadata_summary: str
|
|
108
|
+
metadata_descriptions: List[KeyedDescription]
|
|
109
|
+
passthrough: bool
|
|
110
|
+
annotations_summary: str
|
|
111
|
+
annotation_descriptions: List[KeyedDescription]
|
|
112
|
+
|
|
113
|
+
def __init__(self, content_summary: str = None, content_specs: List[ContentSpec] = None,
|
|
114
|
+
metadata_summary: str = None, metadata_descriptions: List[KeyedDescription] = None,
|
|
115
|
+
passthrough: bool = False, annotations_summary: str = None,
|
|
116
|
+
annotation_descriptions: List[KeyedDescription] = None):
|
|
117
|
+
self.content_summary = content_summary
|
|
118
|
+
self.content_specs = content_specs
|
|
119
|
+
self.metadata_summary = metadata_summary
|
|
120
|
+
self.metadata_descriptions = metadata_descriptions
|
|
121
|
+
self.passthrough = passthrough
|
|
122
|
+
self.annotations_summary = annotations_summary
|
|
123
|
+
self.annotation_descriptions = annotation_descriptions
|
|
124
|
+
|
|
125
|
+
def json(self):
|
|
126
|
+
json_dictionary = {}
|
|
127
|
+
if self.content_summary is not None:
|
|
128
|
+
json_dictionary['contentSummary'] = self.content_summary
|
|
129
|
+
if self.content_specs is not None:
|
|
130
|
+
json_dictionary['contentSpecs'] = [cs.json() for cs in self.content_specs]
|
|
131
|
+
if self.metadata_summary is not None:
|
|
132
|
+
json_dictionary['metadataSummary'] = self.metadata_summary
|
|
133
|
+
if self.metadata_descriptions is not None:
|
|
134
|
+
json_dictionary['metadataDescriptions'] = [md.json() for md in self.metadata_descriptions]
|
|
135
|
+
if self.passthrough is not None:
|
|
136
|
+
json_dictionary['passthrough'] = self.passthrough
|
|
137
|
+
if self.annotations_summary is not None:
|
|
138
|
+
json_dictionary['annotationsSummary'] = self.annotations_summary
|
|
139
|
+
if self.annotation_descriptions is not None:
|
|
140
|
+
json_dictionary['annotationDescriptions'] = [ad.json() for ad in self.annotation_descriptions]
|
|
141
|
+
return json_dictionary
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class DescriptionWithConditions:
|
|
145
|
+
description: str
|
|
146
|
+
conditions: List[str]
|
|
147
|
+
|
|
148
|
+
def __init__(self, description: str = None, conditions: List[str] = None):
|
|
149
|
+
self.description = description
|
|
150
|
+
self.conditions = conditions
|
|
151
|
+
|
|
152
|
+
def json(self):
|
|
153
|
+
json_dictionary = {}
|
|
154
|
+
if self.description is not None:
|
|
155
|
+
json_dictionary['description'] = self.description
|
|
156
|
+
if self.conditions is not None:
|
|
157
|
+
json_dictionary['conditions'] = [c for c in self.conditions]
|
|
158
|
+
return json_dictionary
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class ActionOptions:
|
|
162
|
+
description: str
|
|
163
|
+
input_spec: InputSpec
|
|
164
|
+
output_spec: OutputSpec
|
|
165
|
+
filters: List[DescriptionWithConditions] = None
|
|
166
|
+
errors: List[DescriptionWithConditions] = None
|
|
167
|
+
notes: List[str]
|
|
168
|
+
details: str
|
|
169
|
+
|
|
170
|
+
def __init__(self, description: str = None, input_spec: InputSpec = None, output_spec: OutputSpec = None,
|
|
171
|
+
filters: List = None, errors: List = None, notes: List[str] = None, details: str = None):
|
|
172
|
+
self.description = description
|
|
173
|
+
self.input_spec = input_spec
|
|
174
|
+
self.output_spec = output_spec
|
|
175
|
+
if filters is not None:
|
|
176
|
+
self.filters = []
|
|
177
|
+
for f in filters:
|
|
178
|
+
if isinstance(f, DescriptionWithConditions):
|
|
179
|
+
self.filters.append(f)
|
|
180
|
+
else:
|
|
181
|
+
self.filters.append(DescriptionWithConditions(description=f))
|
|
182
|
+
if errors is not None:
|
|
183
|
+
self.errors = []
|
|
184
|
+
for e in errors:
|
|
185
|
+
if isinstance(e, DescriptionWithConditions):
|
|
186
|
+
self.errors.append(e)
|
|
187
|
+
else:
|
|
188
|
+
self.errors.append(DescriptionWithConditions(description=e))
|
|
189
|
+
self.notes = notes
|
|
190
|
+
self.details = details
|
|
191
|
+
|
|
192
|
+
def json(self):
|
|
193
|
+
json_dictionary = {}
|
|
194
|
+
if self.description is not None:
|
|
195
|
+
json_dictionary['description'] = self.description
|
|
196
|
+
if self.input_spec is not None:
|
|
197
|
+
json_dictionary['inputSpec'] = self.input_spec.json()
|
|
198
|
+
if self.output_spec is not None:
|
|
199
|
+
json_dictionary['outputSpec'] = self.output_spec.json()
|
|
200
|
+
if self.filters is not None:
|
|
201
|
+
json_dictionary['filters'] = [f.json() for f in self.filters]
|
|
202
|
+
if self.errors is not None:
|
|
203
|
+
json_dictionary['errors'] = [e.json() for e in self.errors]
|
|
204
|
+
if self.notes is not None:
|
|
205
|
+
json_dictionary['notes'] = [n for n in self.notes]
|
|
206
|
+
if self.details is not None:
|
|
207
|
+
json_dictionary['details'] = self.details
|
|
208
|
+
return json_dictionary
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class Action(ABC):
|
|
212
|
+
def __init__(self, action_type: ActionType, description: str, valid_result_types: tuple,
|
|
213
|
+
action_options: ActionOptions = None):
|
|
214
|
+
self.action_type = action_type
|
|
215
|
+
if action_options is None:
|
|
216
|
+
self.action_options = ActionOptions(description=description)
|
|
217
|
+
else:
|
|
218
|
+
self.action_options = action_options
|
|
219
|
+
self.valid_result_types = valid_result_types
|
|
220
|
+
|
|
221
|
+
@abstractmethod
|
|
222
|
+
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
def execute_join_action(self, event):
|
|
226
|
+
raise RuntimeError(f"Join is not supported for {self.__class__.__name__}")
|
|
227
|
+
|
|
228
|
+
@abstractmethod
|
|
229
|
+
def execute(self, context: Context, action_input: Any, params: BaseModel):
|
|
230
|
+
pass
|
|
231
|
+
|
|
232
|
+
def execute_action(self, event):
|
|
233
|
+
if event.delta_file_messages is None or not len(event.delta_file_messages):
|
|
234
|
+
raise RuntimeError(f"Received event with no delta file messages for did {event.context.did}")
|
|
235
|
+
if event.context.join is not None:
|
|
236
|
+
result = self.execute_join_action(event)
|
|
237
|
+
else:
|
|
238
|
+
result = self.execute(
|
|
239
|
+
event.context,
|
|
240
|
+
self.build_input(event.context, event.delta_file_messages[0]),
|
|
241
|
+
self.param_class().model_validate(event.params))
|
|
242
|
+
|
|
243
|
+
self.validate_type(result)
|
|
244
|
+
return result
|
|
245
|
+
|
|
246
|
+
@staticmethod
|
|
247
|
+
def param_class():
|
|
248
|
+
"""Factory method to create and return an empty GenericModel instance.
|
|
249
|
+
|
|
250
|
+
All action parameter classes must inherit pydantic.BaseModel.
|
|
251
|
+
Use of complex types in custom action parameter classes must specify
|
|
252
|
+
the internal types when defined. E.g., dict[str, str], or List[str]
|
|
253
|
+
|
|
254
|
+
Returns
|
|
255
|
+
-------
|
|
256
|
+
GenericModel
|
|
257
|
+
an empty GenericModel instance
|
|
258
|
+
"""
|
|
259
|
+
return GenericModel
|
|
260
|
+
|
|
261
|
+
def validate_type(self, result):
|
|
262
|
+
if not isinstance(result, self.valid_result_types):
|
|
263
|
+
raise ValueError(f"{self.__class__.__name__} must return one of "
|
|
264
|
+
f"{[result_type.__name__ for result_type in self.valid_result_types]} "
|
|
265
|
+
f"but a {result.__class__.__name__} was returned")
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class EgressAction(Action, ABC):
|
|
269
|
+
def __init__(self, description: str, action_options: ActionOptions = None):
|
|
270
|
+
super().__init__(ActionType.EGRESS, description, (EgressResult, ErrorResult, FilterResult), action_options)
|
|
271
|
+
|
|
272
|
+
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
273
|
+
return EgressInput(content=delta_file_message.content_list[0], metadata=delta_file_message.metadata)
|
|
274
|
+
|
|
275
|
+
@abstractmethod
|
|
276
|
+
def egress(self, context: Context, params: BaseModel, egress_input: EgressInput):
|
|
277
|
+
pass
|
|
278
|
+
|
|
279
|
+
def execute(self, context: Context, egress_input: EgressInput, params: BaseModel):
|
|
280
|
+
return self.egress(context, params, egress_input)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class TimedIngressAction(Action, ABC):
|
|
284
|
+
def __init__(self, description: str, action_options: ActionOptions = None):
|
|
285
|
+
super().__init__(ActionType.TIMED_INGRESS, description, (IngressResult, ErrorResult), action_options)
|
|
286
|
+
|
|
287
|
+
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
@abstractmethod
|
|
291
|
+
def ingress(self, context: Context, params: BaseModel):
|
|
292
|
+
pass
|
|
293
|
+
|
|
294
|
+
def execute(self, context: Context, input_placeholder: Any, params: BaseModel):
|
|
295
|
+
return self.ingress(context, params)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class TransformAction(Action, ABC):
|
|
299
|
+
def __init__(self, description: str, action_options: ActionOptions = None):
|
|
300
|
+
super().__init__(ActionType.TRANSFORM, description,
|
|
301
|
+
(TransformResult, TransformResults, ErrorResult, FilterResult), action_options)
|
|
302
|
+
|
|
303
|
+
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
304
|
+
return TransformInput(content=delta_file_message.content_list, metadata=delta_file_message.metadata)
|
|
305
|
+
|
|
306
|
+
def execute_join_action(self, event):
|
|
307
|
+
if isinstance(self, Join):
|
|
308
|
+
return self.execute(
|
|
309
|
+
event.context,
|
|
310
|
+
self.join([self.build_input(event.context, delta_file_message)
|
|
311
|
+
for delta_file_message in event.delta_file_messages]),
|
|
312
|
+
self.param_class().model_validate(event.params))
|
|
313
|
+
else:
|
|
314
|
+
super().execute_join_action(event)
|
|
315
|
+
|
|
316
|
+
@abstractmethod
|
|
317
|
+
def transform(self, context: Context, params: BaseModel, transform_input: TransformInput):
|
|
318
|
+
pass
|
|
319
|
+
|
|
320
|
+
def execute(self, context: Context, transform_input: TransformInput, params: BaseModel):
|
|
321
|
+
return self.transform(context, params, transform_input)
|
|
@@ -29,7 +29,7 @@ from datetime import datetime, timezone, timedelta
|
|
|
29
29
|
from importlib import metadata
|
|
30
30
|
from os.path import isdir, isfile, join
|
|
31
31
|
from pathlib import Path
|
|
32
|
-
from typing import List
|
|
32
|
+
from typing import List, NamedTuple
|
|
33
33
|
|
|
34
34
|
import requests
|
|
35
35
|
import yaml
|
|
@@ -250,25 +250,13 @@ class Plugin(object):
|
|
|
250
250
|
def action_name(self, action):
|
|
251
251
|
return f"{self.coordinates.group_id}.{action.__class__.__name__}"
|
|
252
252
|
|
|
253
|
-
def _load_action_docs(self, action):
|
|
254
|
-
docs_path = str(Path(os.path.dirname(os.path.abspath(sys.argv[0]))) / 'docs')
|
|
255
|
-
if not isdir(docs_path):
|
|
256
|
-
return None
|
|
257
|
-
|
|
258
|
-
action_docs_file = join(docs_path, action.__class__.__name__ + '.md')
|
|
259
|
-
if not isfile(action_docs_file):
|
|
260
|
-
return None
|
|
261
|
-
|
|
262
|
-
return open(action_docs_file).read()
|
|
263
|
-
|
|
264
253
|
def _action_json(self, action):
|
|
265
254
|
return {
|
|
266
255
|
'name': self.action_name(action),
|
|
267
|
-
'description': action.description,
|
|
268
256
|
'type': action.action_type.name,
|
|
269
257
|
'supportsJoin': isinstance(action, Join),
|
|
270
258
|
'schema': action.param_class().model_json_schema(),
|
|
271
|
-
'
|
|
259
|
+
'actionOptions': action.action_options.json()
|
|
272
260
|
}
|
|
273
261
|
|
|
274
262
|
@staticmethod
|
|
@@ -337,10 +325,10 @@ class Plugin(object):
|
|
|
337
325
|
self.logger.info("Plugin starting")
|
|
338
326
|
|
|
339
327
|
for action in self.singleton_actions:
|
|
340
|
-
num_threads = 1
|
|
328
|
+
num_threads = 1
|
|
341
329
|
if self.action_name(action) in self.thread_config:
|
|
342
330
|
maybe_num_threads = self.thread_config[self.action_name(action)]
|
|
343
|
-
if
|
|
331
|
+
if maybe_num_threads is int and maybe_num_threads > 0:
|
|
344
332
|
num_threads = maybe_num_threads
|
|
345
333
|
else:
|
|
346
334
|
self.logger.error(f"Ignoring non-int or invalid thread value {maybe_num_threads}")
|
deltafi-2.13.0/deltafi/action.py
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# DeltaFi - Data transformation and enrichment platform
|
|
3
|
-
#
|
|
4
|
-
# Copyright 2021-2025 DeltaFi Contributors <deltafi@deltafi.org>
|
|
5
|
-
#
|
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
-
# you may not use this file except in compliance with the License.
|
|
8
|
-
# You may obtain a copy of the License at
|
|
9
|
-
#
|
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
#
|
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
-
# See the License for the specific language governing permissions and
|
|
16
|
-
# limitations under the License.
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
from abc import ABC, abstractmethod
|
|
20
|
-
from typing import Any, List
|
|
21
|
-
|
|
22
|
-
from pydantic import BaseModel
|
|
23
|
-
|
|
24
|
-
from deltafi.actiontype import ActionType
|
|
25
|
-
from deltafi.domain import DeltaFileMessage
|
|
26
|
-
from deltafi.genericmodel import GenericModel
|
|
27
|
-
from deltafi.input import EgressInput, TransformInput
|
|
28
|
-
from deltafi.result import *
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class Join(ABC):
|
|
32
|
-
def join(self, transform_inputs: List[TransformInput]):
|
|
33
|
-
all_content = []
|
|
34
|
-
all_metadata = {}
|
|
35
|
-
for transform_input in transform_inputs:
|
|
36
|
-
all_content += transform_input.content
|
|
37
|
-
all_metadata.update(transform_input.metadata)
|
|
38
|
-
return TransformInput(content=all_content, metadata=all_metadata)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class Action(ABC):
|
|
42
|
-
def __init__(self, action_type: ActionType, description: str, valid_result_types: tuple):
|
|
43
|
-
self.action_type = action_type
|
|
44
|
-
self.description = description
|
|
45
|
-
self.valid_result_types = valid_result_types
|
|
46
|
-
|
|
47
|
-
@abstractmethod
|
|
48
|
-
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
49
|
-
pass
|
|
50
|
-
|
|
51
|
-
def execute_join_action(self, event):
|
|
52
|
-
raise RuntimeError(f"Join is not supported for {self.__class__.__name__}")
|
|
53
|
-
|
|
54
|
-
@abstractmethod
|
|
55
|
-
def execute(self, context: Context, action_input: Any, params: BaseModel):
|
|
56
|
-
pass
|
|
57
|
-
|
|
58
|
-
def execute_action(self, event):
|
|
59
|
-
if event.delta_file_messages is None or not len(event.delta_file_messages):
|
|
60
|
-
raise RuntimeError(f"Received event with no delta file messages for did {event.context.did}")
|
|
61
|
-
if event.context.join is not None:
|
|
62
|
-
result = self.execute_join_action(event)
|
|
63
|
-
else:
|
|
64
|
-
result = self.execute(
|
|
65
|
-
event.context,
|
|
66
|
-
self.build_input(event.context, event.delta_file_messages[0]),
|
|
67
|
-
self.param_class().model_validate(event.params))
|
|
68
|
-
|
|
69
|
-
self.validate_type(result)
|
|
70
|
-
return result
|
|
71
|
-
|
|
72
|
-
@staticmethod
|
|
73
|
-
def param_class():
|
|
74
|
-
"""Factory method to create and return an empty GenericModel instance.
|
|
75
|
-
|
|
76
|
-
All action parameter classes must inherit pydantic.BaseModel.
|
|
77
|
-
Use of complex types in custom action parameter classes must specify
|
|
78
|
-
the internal types when defined. E.g., dict[str, str], or List[str]
|
|
79
|
-
|
|
80
|
-
Returns
|
|
81
|
-
-------
|
|
82
|
-
GenericModel
|
|
83
|
-
an empty GenericModel instance
|
|
84
|
-
"""
|
|
85
|
-
return GenericModel
|
|
86
|
-
|
|
87
|
-
def validate_type(self, result):
|
|
88
|
-
if not isinstance(result, self.valid_result_types):
|
|
89
|
-
raise ValueError(f"{self.__class__.__name__} must return one of "
|
|
90
|
-
f"{[result_type.__name__ for result_type in self.valid_result_types]} "
|
|
91
|
-
f"but a {result.__class__.__name__} was returned")
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
class EgressAction(Action, ABC):
|
|
95
|
-
def __init__(self, description: str):
|
|
96
|
-
super().__init__(ActionType.EGRESS, description, (EgressResult, ErrorResult, FilterResult))
|
|
97
|
-
|
|
98
|
-
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
99
|
-
return EgressInput(content=delta_file_message.content_list[0], metadata=delta_file_message.metadata)
|
|
100
|
-
|
|
101
|
-
@abstractmethod
|
|
102
|
-
def egress(self, context: Context, params: BaseModel, egress_input: EgressInput):
|
|
103
|
-
pass
|
|
104
|
-
|
|
105
|
-
def execute(self, context: Context, egress_input: EgressInput, params: BaseModel):
|
|
106
|
-
return self.egress(context, params, egress_input)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
class TimedIngressAction(Action, ABC):
|
|
110
|
-
def __init__(self, description: str):
|
|
111
|
-
super().__init__(ActionType.TIMED_INGRESS, description, (IngressResult, ErrorResult))
|
|
112
|
-
|
|
113
|
-
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
114
|
-
return None
|
|
115
|
-
|
|
116
|
-
@abstractmethod
|
|
117
|
-
def ingress(self, context: Context, params: BaseModel):
|
|
118
|
-
pass
|
|
119
|
-
|
|
120
|
-
def execute(self, context: Context, input_placeholder: Any, params: BaseModel):
|
|
121
|
-
return self.ingress(context, params)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
class TransformAction(Action, ABC):
|
|
125
|
-
def __init__(self, description: str):
|
|
126
|
-
super().__init__(ActionType.TRANSFORM, description,
|
|
127
|
-
(TransformResult, TransformResults, ErrorResult, FilterResult))
|
|
128
|
-
|
|
129
|
-
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
130
|
-
return TransformInput(content=delta_file_message.content_list, metadata=delta_file_message.metadata)
|
|
131
|
-
|
|
132
|
-
def execute_join_action(self, event):
|
|
133
|
-
if isinstance(self, Join):
|
|
134
|
-
return self.execute(
|
|
135
|
-
event.context,
|
|
136
|
-
self.join([self.build_input(event.context, delta_file_message)
|
|
137
|
-
for delta_file_message in event.delta_file_messages]),
|
|
138
|
-
self.param_class().model_validate(event.params))
|
|
139
|
-
else:
|
|
140
|
-
super().execute_join_action(event)
|
|
141
|
-
|
|
142
|
-
@abstractmethod
|
|
143
|
-
def transform(self, context: Context, params: BaseModel, transform_input: TransformInput):
|
|
144
|
-
pass
|
|
145
|
-
|
|
146
|
-
def execute(self, context: Context, transform_input: TransformInput, params: BaseModel):
|
|
147
|
-
return self.transform(context, params, transform_input)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|