deltafi 2.0rc1720728217472__tar.gz → 2.0rc1722532930396__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.0rc1720728217472 → deltafi-2.0rc1722532930396}/PKG-INFO +6 -6
- deltafi-2.0rc1722532930396/deltafi/action.py +139 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/actiontype.py +2 -6
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/domain.py +66 -62
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/exception.py +0 -10
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/genericmodel.py +3 -1
- deltafi-2.0rc1722532930396/deltafi/input.py +52 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/logger.py +2 -2
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/plugin.py +12 -18
- deltafi-2.0rc1722532930396/deltafi/result.py +261 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/test_kit/framework.py +69 -66
- deltafi-2.0rc1722532930396/deltafi/test_kit/transform.py +103 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/pyproject.toml +9 -9
- deltafi-2.0rc1720728217472/deltafi/action.py +0 -237
- deltafi-2.0rc1720728217472/deltafi/input.py +0 -216
- deltafi-2.0rc1720728217472/deltafi/result.py +0 -474
- deltafi-2.0rc1720728217472/deltafi/test_kit/domain.py +0 -59
- deltafi-2.0rc1720728217472/deltafi/test_kit/enrich.py +0 -70
- deltafi-2.0rc1720728217472/deltafi/test_kit/format.py +0 -105
- deltafi-2.0rc1720728217472/deltafi/test_kit/load.py +0 -128
- deltafi-2.0rc1720728217472/deltafi/test_kit/transform.py +0 -75
- deltafi-2.0rc1720728217472/deltafi/test_kit/validate.py +0 -54
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/README.md +0 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/__init__.py +0 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/actioneventqueue.py +0 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/metric.py +0 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/storage.py +0 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/test_kit/__init__.py +0 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/test_kit/assertions.py +0 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/test_kit/compare_helpers.py +0 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/test_kit/constants.py +0 -0
- {deltafi-2.0rc1720728217472 → deltafi-2.0rc1722532930396}/deltafi/test_kit/egress.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: deltafi
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.0rc1722532930396
|
|
4
4
|
Summary: SDK for DeltaFi plugins and actions
|
|
5
5
|
License: Apache License, Version 2.0
|
|
6
6
|
Keywords: deltafi
|
|
@@ -20,11 +20,11 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
20
20
|
Classifier: Topic :: Software Development
|
|
21
21
|
Requires-Dist: deepdiff (>=6.7.1)
|
|
22
22
|
Requires-Dist: json-logging (>=1.3.0)
|
|
23
|
-
Requires-Dist: minio (>=7.2.
|
|
24
|
-
Requires-Dist: pydantic (>=2.
|
|
25
|
-
Requires-Dist: redis (>=5.0.
|
|
26
|
-
Requires-Dist: requests (>=2.
|
|
27
|
-
Requires-Dist: urllib3 (>=2.
|
|
23
|
+
Requires-Dist: minio (>=7.2.7)
|
|
24
|
+
Requires-Dist: pydantic (>=2.8.2)
|
|
25
|
+
Requires-Dist: redis (>=5.0.7)
|
|
26
|
+
Requires-Dist: requests (>=2.32.3)
|
|
27
|
+
Requires-Dist: urllib3 (>=2.2.2)
|
|
28
28
|
Project-URL: Bug Reports, https://chat.deltafi.org/deltafi/channels/bug-reports
|
|
29
29
|
Project-URL: Documentation, https://docs.deltafi.org/#/
|
|
30
30
|
Project-URL: Source Code, https://gitlab.com/deltafi/deltafi
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#
|
|
2
|
+
# DeltaFi - Data transformation and enrichment platform
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2021-2023 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 deltafi.actiontype import ActionType
|
|
23
|
+
from deltafi.domain import Context, DeltaFileMessage
|
|
24
|
+
from deltafi.genericmodel import GenericModel
|
|
25
|
+
from deltafi.input import EgressInput, TransformInput
|
|
26
|
+
from deltafi.result import *
|
|
27
|
+
from pydantic import BaseModel
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Action(ABC):
|
|
31
|
+
def __init__(self, action_type: ActionType, description: str, valid_result_types: tuple):
|
|
32
|
+
self.action_type = action_type
|
|
33
|
+
self.description = description
|
|
34
|
+
self.valid_result_types = valid_result_types
|
|
35
|
+
self.action_execution = None
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
def join(self, action_inputs: List[Any]):
|
|
42
|
+
raise RuntimeError(f"Join is not supported for {self.__class__.__name__}")
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def execute(self, context: Context, action_input: Any, params: BaseModel):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
def execute_action(self, event):
|
|
49
|
+
if event.delta_file_messages is None or not len(event.delta_file_messages):
|
|
50
|
+
raise RuntimeError(f"Received event with no delta file messages for did {event.context.did}")
|
|
51
|
+
if event.context.join is not None:
|
|
52
|
+
result = self.execute(
|
|
53
|
+
event.context,
|
|
54
|
+
self.join([self.build_input(event.context, delta_file_message)
|
|
55
|
+
for delta_file_message in event.delta_file_messages]),
|
|
56
|
+
self.param_class().model_validate(event.params))
|
|
57
|
+
else:
|
|
58
|
+
result = self.execute(
|
|
59
|
+
event.context,
|
|
60
|
+
self.build_input(event.context, event.delta_file_messages[0]),
|
|
61
|
+
self.param_class().model_validate(event.params))
|
|
62
|
+
|
|
63
|
+
self.validate_type(result)
|
|
64
|
+
return result
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def param_class():
|
|
68
|
+
"""Factory method to create and return an empty GenericModel instance.
|
|
69
|
+
|
|
70
|
+
All action parameter classes must inherit pydantic.BaseModel.
|
|
71
|
+
Use of complex types in custom action parameter classes must specify
|
|
72
|
+
the internal types when defined. E.g., dict[str, str], or List[str]
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
GenericModel
|
|
77
|
+
an empty GenericModel instance
|
|
78
|
+
"""
|
|
79
|
+
return GenericModel
|
|
80
|
+
|
|
81
|
+
def validate_type(self, result):
|
|
82
|
+
if not isinstance(result, self.valid_result_types):
|
|
83
|
+
raise ValueError(f"{self.__class__.__name__} must return one of "
|
|
84
|
+
f"{[result_type.__name__ for result_type in self.valid_result_types]} "
|
|
85
|
+
f"but a {result.__class__.__name__} was returned")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class EgressAction(Action, ABC):
|
|
89
|
+
def __init__(self, description: str):
|
|
90
|
+
super().__init__(ActionType.EGRESS, description, (EgressResult, ErrorResult, FilterResult))
|
|
91
|
+
|
|
92
|
+
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
93
|
+
return EgressInput(content=delta_file_message.content_list[0], metadata=delta_file_message.metadata)
|
|
94
|
+
|
|
95
|
+
@abstractmethod
|
|
96
|
+
def egress(self, context: Context, params: BaseModel, egress_input: EgressInput):
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
def execute(self, context: Context, egress_input: EgressInput, params: BaseModel):
|
|
100
|
+
return self.egress(context, params, egress_input)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class TimedIngressAction(Action, ABC):
|
|
104
|
+
def __init__(self, description: str):
|
|
105
|
+
super().__init__(ActionType.TIMED_INGRESS, description, (IngressResult, ErrorResult))
|
|
106
|
+
|
|
107
|
+
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
@abstractmethod
|
|
111
|
+
def ingress(self, context: Context, params: BaseModel):
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
def execute(self, context: Context, input_placeholder: Any, params: BaseModel):
|
|
115
|
+
return self.ingress(context, params)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class TransformAction(Action, ABC):
|
|
119
|
+
def __init__(self, description: str):
|
|
120
|
+
super().__init__(ActionType.TRANSFORM, description,
|
|
121
|
+
(TransformResult, TransformResults, ErrorResult, FilterResult))
|
|
122
|
+
|
|
123
|
+
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
124
|
+
return TransformInput(content=delta_file_message.content_list, metadata=delta_file_message.metadata)
|
|
125
|
+
|
|
126
|
+
def join(self, transform_inputs: List[TransformInput]):
|
|
127
|
+
all_content = []
|
|
128
|
+
all_metadata = {}
|
|
129
|
+
for transform_input in transform_inputs:
|
|
130
|
+
all_content += transform_input.content
|
|
131
|
+
all_metadata.update(transform_input.metadata)
|
|
132
|
+
return TransformInput(content=all_content, metadata=all_metadata)
|
|
133
|
+
|
|
134
|
+
@abstractmethod
|
|
135
|
+
def transform(self, context: Context, params: BaseModel, transform_input: TransformInput):
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
def execute(self, context: Context, transform_input: TransformInput, params: BaseModel):
|
|
139
|
+
return self.transform(context, params, transform_input)
|
|
@@ -20,13 +20,9 @@ from enum import Enum
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class ActionType(Enum):
|
|
23
|
+
INGRESS = "INGRESS"
|
|
23
24
|
TIMED_INGRESS = "TIMED_INGRESS"
|
|
24
25
|
TRANSFORM = "TRANSFORM"
|
|
25
|
-
LOAD = "LOAD"
|
|
26
|
-
DOMAIN = "DOMAIN"
|
|
27
|
-
ENRICH = "ENRICH"
|
|
28
|
-
FORMAT = "FORMAT"
|
|
29
|
-
VALIDATE = "VALIDATE"
|
|
30
26
|
EGRESS = "EGRESS"
|
|
31
|
-
|
|
27
|
+
PUBLISH = "PUBLISH"
|
|
32
28
|
UNKNOWN = "UNKNOWN"
|
|
@@ -40,59 +40,87 @@ class ActionExecution(NamedTuple):
|
|
|
40
40
|
|
|
41
41
|
class Context(NamedTuple):
|
|
42
42
|
did: str
|
|
43
|
-
|
|
43
|
+
delta_file_name: str
|
|
44
|
+
data_source: str
|
|
45
|
+
flow_name: str
|
|
46
|
+
flow_id: str
|
|
44
47
|
action_name: str
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
egress_flow: str
|
|
48
|
-
system: str
|
|
48
|
+
action_id: str
|
|
49
|
+
action_version: str
|
|
49
50
|
hostname: str
|
|
51
|
+
system_name: str
|
|
50
52
|
content_service: ContentService
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
join: dict = None
|
|
54
|
+
joined_dids: List[str] = None
|
|
53
55
|
memo: str = None
|
|
54
56
|
logger: Logger = None
|
|
55
57
|
|
|
56
58
|
@classmethod
|
|
57
|
-
def create(cls, context: dict,
|
|
59
|
+
def create(cls, context: dict, content_service: ContentService, logger: Logger):
|
|
58
60
|
did = context['did']
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
action_name = action_name_parts[1]
|
|
62
|
-
if 'sourceFilename' in context:
|
|
63
|
-
source_filename = context['sourceFilename']
|
|
61
|
+
if 'deltaFileName' in context:
|
|
62
|
+
delta_file_name = context['deltaFileName']
|
|
64
63
|
else:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
egress_flow = context['egressFlow']
|
|
64
|
+
delta_file_name = None
|
|
65
|
+
if 'dataSource' in context:
|
|
66
|
+
data_source = context['dataSource']
|
|
69
67
|
else:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
collect = context['collect']
|
|
68
|
+
data_source = None
|
|
69
|
+
if 'flowName' in context:
|
|
70
|
+
flow_name = context['flowName']
|
|
74
71
|
else:
|
|
75
|
-
|
|
76
|
-
if '
|
|
77
|
-
|
|
72
|
+
flow_name = None
|
|
73
|
+
if 'flowId' in context:
|
|
74
|
+
flow_id = context['flowId']
|
|
78
75
|
else:
|
|
79
|
-
|
|
76
|
+
flow_id = None
|
|
77
|
+
if 'actionName' in context:
|
|
78
|
+
action_name = context['actionName']
|
|
79
|
+
else:
|
|
80
|
+
action_name = None
|
|
81
|
+
if 'actionId' in context:
|
|
82
|
+
action_id = context['actionId']
|
|
83
|
+
else:
|
|
84
|
+
action_id = None
|
|
85
|
+
if 'actionVersion' in context:
|
|
86
|
+
action_version = context['actionVersion']
|
|
87
|
+
else:
|
|
88
|
+
action_version = None
|
|
89
|
+
if 'hostname' in context:
|
|
90
|
+
hostname = context['hostname']
|
|
91
|
+
else:
|
|
92
|
+
hostname = None
|
|
93
|
+
if 'systemName' in context:
|
|
94
|
+
system_name = context['systemName']
|
|
95
|
+
else:
|
|
96
|
+
system_name = None
|
|
97
|
+
if 'join' in context:
|
|
98
|
+
join = context['join']
|
|
99
|
+
else:
|
|
100
|
+
join = None
|
|
101
|
+
if 'joinedDids' in context:
|
|
102
|
+
joined_dids = context['joinedDids']
|
|
103
|
+
else:
|
|
104
|
+
joined_dids = None
|
|
80
105
|
if 'memo' in context:
|
|
81
106
|
memo = context['memo']
|
|
82
107
|
else:
|
|
83
108
|
memo = None
|
|
109
|
+
|
|
84
110
|
return Context(did=did,
|
|
85
|
-
|
|
111
|
+
delta_file_name=delta_file_name,
|
|
112
|
+
data_source=data_source,
|
|
113
|
+
flow_name=flow_name,
|
|
114
|
+
flow_id=flow_id,
|
|
86
115
|
action_name=action_name,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
egress_flow=egress_flow,
|
|
90
|
-
system=system,
|
|
116
|
+
action_id=action_id,
|
|
117
|
+
action_version=action_version,
|
|
91
118
|
hostname=hostname,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
119
|
+
system_name=system_name,
|
|
120
|
+
join=join,
|
|
121
|
+
joined_dids=joined_dids,
|
|
95
122
|
memo=memo,
|
|
123
|
+
content_service=content_service,
|
|
96
124
|
logger=logger)
|
|
97
125
|
|
|
98
126
|
|
|
@@ -197,7 +225,6 @@ class Content:
|
|
|
197
225
|
|
|
198
226
|
return new_segments
|
|
199
227
|
|
|
200
|
-
|
|
201
228
|
def get_size(self):
|
|
202
229
|
"""
|
|
203
230
|
Returns the size of the content in bytes.
|
|
@@ -296,41 +323,17 @@ class Content:
|
|
|
296
323
|
content_service=content_service)
|
|
297
324
|
|
|
298
325
|
|
|
299
|
-
class Domain(NamedTuple):
|
|
300
|
-
name: str
|
|
301
|
-
value: str
|
|
302
|
-
media_type: str
|
|
303
|
-
|
|
304
|
-
@classmethod
|
|
305
|
-
def from_dict(cls, domain: dict):
|
|
306
|
-
name = domain['name']
|
|
307
|
-
if 'value' in domain:
|
|
308
|
-
value = domain['value']
|
|
309
|
-
else:
|
|
310
|
-
value = None
|
|
311
|
-
media_type = domain['mediaType']
|
|
312
|
-
return Domain(name=name,
|
|
313
|
-
value=value,
|
|
314
|
-
media_type=media_type)
|
|
315
|
-
|
|
316
|
-
|
|
317
326
|
class DeltaFileMessage(NamedTuple):
|
|
318
327
|
metadata: Dict[str, str]
|
|
319
328
|
content_list: List[Content]
|
|
320
|
-
domains: List[Domain]
|
|
321
|
-
enrichments: List[Domain]
|
|
322
329
|
|
|
323
330
|
@classmethod
|
|
324
331
|
def from_dict(cls, delta_file_message: dict, content_service: ContentService):
|
|
325
332
|
metadata = delta_file_message['metadata']
|
|
326
333
|
content_list = [Content.from_dict(content, content_service) for content in delta_file_message['contentList']]
|
|
327
|
-
domains = [Domain.from_dict(domain) for domain in delta_file_message['domains']] if 'domains' in delta_file_message else []
|
|
328
|
-
enrichments = [Domain.from_dict(domain) for domain in delta_file_message['enrichments']] if 'enrichments' in delta_file_message else []
|
|
329
334
|
|
|
330
335
|
return DeltaFileMessage(metadata=metadata,
|
|
331
|
-
content_list=content_list
|
|
332
|
-
domains=domains,
|
|
333
|
-
enrichments=enrichments)
|
|
336
|
+
content_list=content_list)
|
|
334
337
|
|
|
335
338
|
|
|
336
339
|
class Event(NamedTuple):
|
|
@@ -341,9 +344,10 @@ class Event(NamedTuple):
|
|
|
341
344
|
return_address: str
|
|
342
345
|
|
|
343
346
|
@classmethod
|
|
344
|
-
def create(cls, event: dict,
|
|
345
|
-
delta_file_messages = [DeltaFileMessage.from_dict(delta_file_message, content_service) for delta_file_message in
|
|
346
|
-
|
|
347
|
+
def create(cls, event: dict, content_service: ContentService, logger: Logger):
|
|
348
|
+
delta_file_messages = [DeltaFileMessage.from_dict(delta_file_message, content_service) for delta_file_message in
|
|
349
|
+
event['deltaFileMessages']]
|
|
350
|
+
context = Context.create(event['actionContext'], content_service, logger)
|
|
347
351
|
params = event['actionParams']
|
|
348
352
|
queue_name = None
|
|
349
353
|
if 'queueName' in event:
|
|
@@ -23,16 +23,6 @@ class ExpectedContentException(RuntimeError):
|
|
|
23
23
|
self.size = size
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
class MissingDomainException(RuntimeError):
|
|
27
|
-
def __init__(self, name):
|
|
28
|
-
self.name = name
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class MissingEnrichmentException(RuntimeError):
|
|
32
|
-
def __init__(self, name):
|
|
33
|
-
self.name = name
|
|
34
|
-
|
|
35
|
-
|
|
36
26
|
class MissingMetadataException(RuntimeError):
|
|
37
27
|
def __init__(self, key):
|
|
38
28
|
self.key = key
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
|
|
23
23
|
Provides an empty subclass of pydantic.BaseModel.
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
All action parameter classes must inherit pydantic.BaseModel.
|
|
26
|
+
|
|
27
|
+
Starting Pydantic v2, the BaseModel cannot be directly instantiated. This class provides for instantiation of GenericModel objects that inherit from BaseModel.
|
|
26
28
|
|
|
27
29
|
This class does not define fields for validation or any other purpose.
|
|
28
30
|
"""
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#
|
|
2
|
+
# DeltaFi - Data transformation and enrichment platform
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2021-2023 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 deltafi.domain import *
|
|
20
|
+
from deltafi.exception import MissingMetadataException, ExpectedContentException
|
|
21
|
+
|
|
22
|
+
class EgressInput(NamedTuple):
|
|
23
|
+
content: Content
|
|
24
|
+
metadata: dict
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TransformInput(NamedTuple):
|
|
28
|
+
content: List[Content]
|
|
29
|
+
metadata: dict
|
|
30
|
+
|
|
31
|
+
def has_content(self) -> bool:
|
|
32
|
+
return len(self.content) > 0
|
|
33
|
+
|
|
34
|
+
def content_at(self, index: int) -> Content:
|
|
35
|
+
if len(self.content) < index + 1:
|
|
36
|
+
raise ExpectedContentException(index, len(self.content))
|
|
37
|
+
return self.content[index]
|
|
38
|
+
|
|
39
|
+
def first_content(self):
|
|
40
|
+
return self.content_at(0)
|
|
41
|
+
|
|
42
|
+
def get_metadata(self, key: str):
|
|
43
|
+
if key in self.metadata:
|
|
44
|
+
return self.metadata[key]
|
|
45
|
+
else:
|
|
46
|
+
raise MissingMetadataException(key)
|
|
47
|
+
|
|
48
|
+
def get_metadata_or_else(self, key: str, default: str) -> str:
|
|
49
|
+
if key in self.metadata:
|
|
50
|
+
return self.metadata[key]
|
|
51
|
+
else:
|
|
52
|
+
return default
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import logging
|
|
20
20
|
import sys
|
|
21
|
-
from datetime import datetime
|
|
21
|
+
from datetime import datetime, UTC
|
|
22
22
|
|
|
23
23
|
import json_logging
|
|
24
24
|
|
|
@@ -42,7 +42,7 @@ def _sanitize_log_msg(record):
|
|
|
42
42
|
class JSONLogFormatter(json_logging.JSONLogFormatter):
|
|
43
43
|
|
|
44
44
|
def _format_log_object(self, record, request_util):
|
|
45
|
-
utcnow = datetime.
|
|
45
|
+
utcnow = datetime.now(UTC)
|
|
46
46
|
|
|
47
47
|
json_log_object = {
|
|
48
48
|
'timestamp': json_logging.util.iso_time_format(utcnow),
|
|
@@ -34,8 +34,7 @@ from importlib import metadata
|
|
|
34
34
|
import requests
|
|
35
35
|
from deltafi.actioneventqueue import ActionEventQueue
|
|
36
36
|
from deltafi.domain import Event, ActionExecution
|
|
37
|
-
from deltafi.exception import ExpectedContentException,
|
|
38
|
-
MissingMetadataException
|
|
37
|
+
from deltafi.exception import ExpectedContentException, MissingMetadataException
|
|
39
38
|
from deltafi.logger import get_logger
|
|
40
39
|
from deltafi.result import ErrorResult
|
|
41
40
|
from deltafi.storage import ContentService
|
|
@@ -47,9 +46,9 @@ def _coordinates():
|
|
|
47
46
|
|
|
48
47
|
|
|
49
48
|
def _setup_queue(max_connections):
|
|
50
|
-
|
|
51
|
-
password = os.getenv('
|
|
52
|
-
return ActionEventQueue(
|
|
49
|
+
url = os.getenv('VALKEY_URL', 'http://deltafi-valkey-master:6379')
|
|
50
|
+
password = os.getenv('VALKEY_PASSWORD')
|
|
51
|
+
return ActionEventQueue(url, max_connections, password)
|
|
53
52
|
|
|
54
53
|
|
|
55
54
|
def _setup_content_service():
|
|
@@ -165,8 +164,6 @@ class Plugin(object):
|
|
|
165
164
|
'name': self.action_name(action),
|
|
166
165
|
'description': action.description,
|
|
167
166
|
'type': action.action_type.name,
|
|
168
|
-
'requiresDomains': action.requires_domains,
|
|
169
|
-
'requiresEnrichments': action.requires_enrichments,
|
|
170
167
|
'schema': action.param_class().model_json_schema()
|
|
171
168
|
}
|
|
172
169
|
|
|
@@ -219,7 +216,8 @@ class Plugin(object):
|
|
|
219
216
|
for action in self.actions:
|
|
220
217
|
threading.Thread(target=self._do_action, args=(action,)).start()
|
|
221
218
|
|
|
222
|
-
threading.Thread(target=self._heartbeat)
|
|
219
|
+
hb_thread = threading.Thread(target=self._heartbeat)
|
|
220
|
+
hb_thread.start()
|
|
223
221
|
|
|
224
222
|
self.logger.info("All threads running")
|
|
225
223
|
|
|
@@ -227,6 +225,7 @@ class Plugin(object):
|
|
|
227
225
|
f.close()
|
|
228
226
|
|
|
229
227
|
self.logger.info("Application initialization complete")
|
|
228
|
+
hb_thread.join()
|
|
230
229
|
|
|
231
230
|
def _heartbeat(self):
|
|
232
231
|
long_running_actions = set()
|
|
@@ -263,7 +262,7 @@ class Plugin(object):
|
|
|
263
262
|
while True:
|
|
264
263
|
try:
|
|
265
264
|
event_string = self.queue.take(self.action_name(action))
|
|
266
|
-
event = Event.create(json.loads(event_string), self.
|
|
265
|
+
event = Event.create(json.loads(event_string), self.content_service, action_logger)
|
|
267
266
|
start_time = time.time()
|
|
268
267
|
action_logger.debug(f"Processing event for did {event.context.did}")
|
|
269
268
|
|
|
@@ -277,14 +276,6 @@ class Plugin(object):
|
|
|
277
276
|
f"Action attempted to look up element {e.index + 1} (index {e.index}) from "
|
|
278
277
|
f"content list of size {e.size}",
|
|
279
278
|
f"{str(e)}\n{traceback.format_exc()}")
|
|
280
|
-
except MissingDomainException as e:
|
|
281
|
-
result = ErrorResult(event.context,
|
|
282
|
-
f"Action attempted to access domain {e.name}, which does not exist",
|
|
283
|
-
f"{str(e)}\n{traceback.format_exc()}")
|
|
284
|
-
except MissingEnrichmentException as e:
|
|
285
|
-
result = ErrorResult(event.context,
|
|
286
|
-
f"Action attempted to access enrichment {e.name}, which does not exist",
|
|
287
|
-
f"{str(e)}\n{traceback.format_exc()}")
|
|
288
279
|
except MissingMetadataException as e:
|
|
289
280
|
result = ErrorResult(event.context,
|
|
290
281
|
f"Missing metadata with key {e.key}",
|
|
@@ -297,7 +288,10 @@ class Plugin(object):
|
|
|
297
288
|
|
|
298
289
|
response = {
|
|
299
290
|
'did': event.context.did,
|
|
300
|
-
'
|
|
291
|
+
'flowName': event.context.flow_name,
|
|
292
|
+
'flowId': event.context.flow_id,
|
|
293
|
+
'actionName': event.context.action_name,
|
|
294
|
+
'actionId': event.context.action_id,
|
|
301
295
|
'start': start_time,
|
|
302
296
|
'stop': time.time(),
|
|
303
297
|
'type': result.result_type,
|