deltafi 2.0rc1731040010625__py3-none-any.whl → 2.1.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.
Potentially problematic release.
This version of deltafi might be problematic. Click here for more details.
- deltafi/plugin.py +95 -20
- deltafi/test_kit/framework.py +3 -0
- deltafi/test_kit/timed_ingress.py +96 -0
- {deltafi-2.0rc1731040010625.dist-info → deltafi-2.1.0.dist-info}/METADATA +1 -1
- {deltafi-2.0rc1731040010625.dist-info → deltafi-2.1.0.dist-info}/RECORD +6 -5
- {deltafi-2.0rc1731040010625.dist-info → deltafi-2.1.0.dist-info}/WHEEL +0 -0
deltafi/plugin.py
CHANGED
|
@@ -25,7 +25,6 @@ import sys
|
|
|
25
25
|
import threading
|
|
26
26
|
import time
|
|
27
27
|
import traceback
|
|
28
|
-
import yaml
|
|
29
28
|
from datetime import datetime, timezone, timedelta
|
|
30
29
|
from importlib import metadata
|
|
31
30
|
from os.path import isdir, isfile, join
|
|
@@ -33,6 +32,7 @@ from pathlib import Path
|
|
|
33
32
|
from typing import List
|
|
34
33
|
|
|
35
34
|
import requests
|
|
35
|
+
import yaml
|
|
36
36
|
from deltafi.action import Action, Join
|
|
37
37
|
from deltafi.actioneventqueue import ActionEventQueue
|
|
38
38
|
from deltafi.domain import Event, ActionExecution
|
|
@@ -46,6 +46,75 @@ def _coordinates():
|
|
|
46
46
|
return PluginCoordinates(os.getenv('PROJECT_GROUP'), os.getenv('PROJECT_NAME'), os.getenv('PROJECT_VERSION'))
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
def _valid_file(filename: str):
|
|
50
|
+
return isfile(filename) and \
|
|
51
|
+
(filename.endswith(".json")
|
|
52
|
+
or filename.endswith(".yaml")
|
|
53
|
+
or filename.endswith(".yml"))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _read_valid_files(path: str):
|
|
57
|
+
"""
|
|
58
|
+
Read the contents of a directory, and returns a filtered list of files
|
|
59
|
+
that can be read/parsed for plugin usage, and ignores everything else.
|
|
60
|
+
:param path: name of the directory to scan
|
|
61
|
+
:return: list of filtered, parsable files
|
|
62
|
+
"""
|
|
63
|
+
files = []
|
|
64
|
+
if isdir(path):
|
|
65
|
+
files = [f for f in os.listdir(path) if _valid_file(join(path, f))]
|
|
66
|
+
return files
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _load_resource(path: str, filename: str):
|
|
70
|
+
"""
|
|
71
|
+
Read the content of a JSON or YAML file, and return a Python
|
|
72
|
+
object of its contents, typically as a dict or list.
|
|
73
|
+
To avoid exceptions, use only files returned by _read_valid_files().
|
|
74
|
+
:param path: directory which contains the file to load
|
|
75
|
+
:param filename: name of the file to load
|
|
76
|
+
:return: dict or list of file contents
|
|
77
|
+
"""
|
|
78
|
+
with open(join(path, filename)) as file_in:
|
|
79
|
+
if filename.endswith(".json"):
|
|
80
|
+
return json.load(file_in)
|
|
81
|
+
elif filename.endswith(".yaml") or filename.endswith(".yml"):
|
|
82
|
+
results = []
|
|
83
|
+
yaml_docs = yaml.safe_load_all(file_in)
|
|
84
|
+
for doc_iter in yaml_docs:
|
|
85
|
+
# yaml_docs must be iterated
|
|
86
|
+
results.append(doc_iter)
|
|
87
|
+
if len(results) == 1:
|
|
88
|
+
# Single document YAML file
|
|
89
|
+
return results[0]
|
|
90
|
+
else:
|
|
91
|
+
# Multi-document YAML file
|
|
92
|
+
return results
|
|
93
|
+
raise RuntimeError(f"File type not supported: {filename}")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _load__all_resource(path: str, file_list: List[str]):
|
|
97
|
+
resources = []
|
|
98
|
+
for f in file_list:
|
|
99
|
+
r = _load_resource(path, f)
|
|
100
|
+
if isinstance(r, list):
|
|
101
|
+
resources.extend(r)
|
|
102
|
+
else:
|
|
103
|
+
resources.append(r)
|
|
104
|
+
return resources
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _find_variables_filename(names: List[str]):
|
|
108
|
+
if 'variables.json' in names:
|
|
109
|
+
return 'variables.json'
|
|
110
|
+
elif 'variables.yaml' in names:
|
|
111
|
+
return 'variables.yaml'
|
|
112
|
+
elif 'variables.yml' in names:
|
|
113
|
+
return 'variables.yml'
|
|
114
|
+
else:
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
|
|
49
118
|
def _setup_queue(max_connections):
|
|
50
119
|
url = os.getenv('VALKEY_URL', 'http://deltafi-valkey-master:6379')
|
|
51
120
|
password = os.getenv('VALKEY_PASSWORD')
|
|
@@ -183,34 +252,40 @@ class Plugin(object):
|
|
|
183
252
|
'docsMarkdown': self._load_action_docs(action)
|
|
184
253
|
}
|
|
185
254
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if isdir(tests_path):
|
|
191
|
-
test_files = [f for f in os.listdir(tests_path) if isfile(join(tests_path, f))]
|
|
192
|
-
else:
|
|
193
|
-
self.logger.warning(f"tests directory ({tests_path}) does not exist. No tests will be installed.")
|
|
255
|
+
@staticmethod
|
|
256
|
+
def load_integration_tests(tests_path: str):
|
|
257
|
+
test_files = _read_valid_files(tests_path)
|
|
258
|
+
return _load__all_resource(tests_path, test_files)
|
|
194
259
|
|
|
195
|
-
|
|
196
|
-
|
|
260
|
+
@staticmethod
|
|
261
|
+
def load_variables(flows_path: str, flow_files: List[str]):
|
|
262
|
+
variables = []
|
|
263
|
+
variables_filename = _find_variables_filename(flow_files)
|
|
264
|
+
if variables_filename is not None:
|
|
265
|
+
flow_files.remove(variables_filename)
|
|
266
|
+
variables = _load__all_resource(flows_path, [variables_filename])
|
|
267
|
+
return variables
|
|
197
268
|
|
|
198
269
|
def registration_json(self):
|
|
199
270
|
flows_path = str(Path(os.path.dirname(os.path.abspath(sys.argv[0]))) / 'flows')
|
|
271
|
+
tests_path = str(Path(os.path.dirname(os.path.abspath(sys.argv[0]))) / 'integration')
|
|
200
272
|
|
|
201
|
-
flow_files = []
|
|
202
273
|
variables = []
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
variables = json.load(open(join(flows_path, 'variables.json')))
|
|
274
|
+
flow_files = _read_valid_files(flows_path)
|
|
275
|
+
if len(flow_files) == 0:
|
|
276
|
+
self.logger.warning(
|
|
277
|
+
f"Flows directory ({flows_path}) does not exist or contains no valid files. No flows will be installed.")
|
|
208
278
|
else:
|
|
209
|
-
self.
|
|
279
|
+
variables = self.load_variables(flows_path, flow_files)
|
|
210
280
|
|
|
211
|
-
flows =
|
|
281
|
+
flows = _load__all_resource(flows_path, flow_files)
|
|
212
282
|
actions = [self._action_json(action) for action in self.actions]
|
|
213
283
|
|
|
284
|
+
test_files = self.load_integration_tests(tests_path)
|
|
285
|
+
if len(test_files) == 0:
|
|
286
|
+
self.logger.warning(
|
|
287
|
+
f"tests directory ({tests_path}) does not exist or contains no valid files. No tests will be installed.")
|
|
288
|
+
|
|
214
289
|
return {
|
|
215
290
|
'pluginCoordinates': self.coordinates.__json__(),
|
|
216
291
|
'displayName': self.display_name,
|
|
@@ -222,7 +297,7 @@ class Plugin(object):
|
|
|
222
297
|
'actions': actions,
|
|
223
298
|
'variables': variables,
|
|
224
299
|
'flowPlans': flows,
|
|
225
|
-
'integrationTests':
|
|
300
|
+
'integrationTests': test_files
|
|
226
301
|
}
|
|
227
302
|
|
|
228
303
|
def _register(self):
|
deltafi/test_kit/framework.py
CHANGED
|
@@ -137,6 +137,7 @@ class TestCaseBase(ABC):
|
|
|
137
137
|
- compare_tool: (optional) CompareHelper instanced for comparing output content
|
|
138
138
|
- inputs: (optional) List[IOContent]: input content to action
|
|
139
139
|
- parameters: (optional) Dict: map of action input parameters
|
|
140
|
+
- in_memo: (optional) str: Input 'memo' value for a TimedIngress context
|
|
140
141
|
- in_meta: (optional) Dict: map of metadata as input to action
|
|
141
142
|
- join_meta: (optional): List[Dict]: When a List is provided, this enables the JOIN portion of an action.
|
|
142
143
|
When using JOIN, join_meta must match the size of inputs, though the Dict can be empty
|
|
@@ -161,6 +162,7 @@ class TestCaseBase(ABC):
|
|
|
161
162
|
self.file_name = data["file_name"] if "file_name" in data else "filename"
|
|
162
163
|
self.parameters = data["parameters"] if "parameters" in data else {}
|
|
163
164
|
self.in_meta = data["in_meta"] if "in_meta" in data else {}
|
|
165
|
+
self.in_memo = data["in_memo"] if "in_memo" in data else None
|
|
164
166
|
self.use_did = data["did"] if "did" in data else None
|
|
165
167
|
self.expected_result_type = None
|
|
166
168
|
self.err_or_filt_cause = None
|
|
@@ -280,6 +282,7 @@ class ActionTest(ABC):
|
|
|
280
282
|
content_service=self.content_service,
|
|
281
283
|
saved_content=[],
|
|
282
284
|
join=join,
|
|
285
|
+
memo=test_case.in_memo,
|
|
283
286
|
logger=get_logger())
|
|
284
287
|
return self.context
|
|
285
288
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#
|
|
2
|
+
# DeltaFi - Data transformation and enrichment platform
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2021-2024 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 typing import List
|
|
20
|
+
|
|
21
|
+
from deltafi.result import IngressResult, IngressResultItem, IngressStatusEnum
|
|
22
|
+
|
|
23
|
+
from .assertions import *
|
|
24
|
+
from .framework import TestCaseBase, ActionTest, IOContent
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TimedIngressTestCase(TestCaseBase):
|
|
28
|
+
def __init__(self, fields: Dict):
|
|
29
|
+
super().__init__(fields)
|
|
30
|
+
self.memo = None
|
|
31
|
+
self.results = []
|
|
32
|
+
self.execute_immediate = False
|
|
33
|
+
self.status = IngressStatusEnum.HEALTHY
|
|
34
|
+
self.status_message = None
|
|
35
|
+
|
|
36
|
+
def expect_ingress_result(self,
|
|
37
|
+
memo: str = None,
|
|
38
|
+
exec_immed: bool = False,
|
|
39
|
+
status: IngressStatusEnum = IngressStatusEnum.HEALTHY,
|
|
40
|
+
status_message: str = None):
|
|
41
|
+
self.expected_result_type = IngressResult
|
|
42
|
+
self.memo = memo
|
|
43
|
+
self.execute_immediate = exec_immed
|
|
44
|
+
self.status = status
|
|
45
|
+
self.status_message = status_message
|
|
46
|
+
|
|
47
|
+
def add_ingress_result_item(self, content: List[IOContent], metadata: Dict, name: str = None):
|
|
48
|
+
self.results.append(
|
|
49
|
+
{
|
|
50
|
+
'content': content,
|
|
51
|
+
'metadata': metadata,
|
|
52
|
+
'name': name
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TimedIngressActionTest(ActionTest):
|
|
58
|
+
def __init__(self, package_name: str):
|
|
59
|
+
"""
|
|
60
|
+
Provides structure for testing DeltaFi TimedIngress action
|
|
61
|
+
Args:
|
|
62
|
+
package_name: name of the actions package for finding resources
|
|
63
|
+
"""
|
|
64
|
+
super().__init__(package_name)
|
|
65
|
+
|
|
66
|
+
def ingress(self, test_case: TimedIngressTestCase):
|
|
67
|
+
if test_case.expected_result_type == IngressResult:
|
|
68
|
+
self.expect_ingress_result(test_case)
|
|
69
|
+
else:
|
|
70
|
+
super().execute(test_case)
|
|
71
|
+
|
|
72
|
+
def expect_ingress_result(self, test_case: TimedIngressTestCase):
|
|
73
|
+
result = super().run_and_check_result_type(test_case, IngressResult)
|
|
74
|
+
self.assert_ingress_result(test_case, result)
|
|
75
|
+
|
|
76
|
+
def assert_ingress_result(self, test_case: TimedIngressTestCase, result: IngressResult):
|
|
77
|
+
assert_equal(test_case.memo, result.memo)
|
|
78
|
+
assert_equal(test_case.execute_immediate, result.execute_immediate)
|
|
79
|
+
assert_equal(test_case.status, result.status)
|
|
80
|
+
assert_equal(test_case.status_message, result.status_message)
|
|
81
|
+
|
|
82
|
+
assert_equal_len(test_case.results, result.ingress_result_items)
|
|
83
|
+
for index, ingress_item in enumerate(result.ingress_result_items):
|
|
84
|
+
self.compare_one_ingress_item(test_case, ingress_item, index)
|
|
85
|
+
expected = test_case.results[index]
|
|
86
|
+
if 'name' in expected:
|
|
87
|
+
assert_equal_with_label(expected["name"], ingress_item.delta_file_name, f"name[{index}]")
|
|
88
|
+
|
|
89
|
+
def compare_one_ingress_item(self, test_case: TimedIngressTestCase, result: IngressResultItem, index: int):
|
|
90
|
+
expected = test_case.results[index]
|
|
91
|
+
|
|
92
|
+
# Check output
|
|
93
|
+
self.compare_content_list(test_case.compare_tool, expected['content'], result.content)
|
|
94
|
+
|
|
95
|
+
# Check metadata
|
|
96
|
+
assert_keys_and_values(expected['metadata'], result.metadata)
|
|
@@ -8,7 +8,7 @@ deltafi/genericmodel.py,sha256=WU8zfqEO_n84CZ0KpH9FhgTsL9jyU0EXSuhL0IdLWFw,1152
|
|
|
8
8
|
deltafi/input.py,sha256=ydAhuw68N9qMeacc9YE4U79zRqxWiinZNkm1AxoFBEk,1656
|
|
9
9
|
deltafi/logger.py,sha256=mKfJTnuupf3sto6hV-SIXajtcP-xTSSdJ2Ufd-lJPTo,2140
|
|
10
10
|
deltafi/metric.py,sha256=79Gb2C1qYXeQYshgFPu2BcMT5oEb0LhmNfwyulSqsIc,972
|
|
11
|
-
deltafi/plugin.py,sha256=
|
|
11
|
+
deltafi/plugin.py,sha256=QQ197MWyi3dCkax6rjA-kylbfMrMWHEaG8jIVsOGRW4,17980
|
|
12
12
|
deltafi/result.py,sha256=2mefttQAdvYhsw6IjvouY4L5W1n3s6tP7oJUhLtyyVk,9047
|
|
13
13
|
deltafi/storage.py,sha256=vCE29Yzk5s7ub4O1PdaxXBKPBQy4OHs7j46z3O5MezE,2979
|
|
14
14
|
deltafi/test_kit/__init__.py,sha256=sSGWjpvzcCzgLywoEg55z_0satt0g_LPTwNIWtylgF4,709
|
|
@@ -16,8 +16,9 @@ deltafi/test_kit/assertions.py,sha256=MdUXENLn0aDvknMtsnSAb-DwvpzXlMkaYQ6-RRkWG8
|
|
|
16
16
|
deltafi/test_kit/compare_helpers.py,sha256=qRINvCQqBUaalT1DG2oz2egSkUjiSOfqKF5U7WyeT_g,12957
|
|
17
17
|
deltafi/test_kit/constants.py,sha256=Suygx9CEob2Skw4UyzzMCibQ8hRhGHC_d_Xab8AJMFE,833
|
|
18
18
|
deltafi/test_kit/egress.py,sha256=53SBeJJmphDl-jZdqkC1dKYWZ4ATWlzY02TUt0Sj1zs,1899
|
|
19
|
-
deltafi/test_kit/framework.py,sha256=
|
|
19
|
+
deltafi/test_kit/framework.py,sha256=6txr5vnLSFIDaXgx-cXjDuYJSPcSHR7ez1x4N8TsNNw,15337
|
|
20
|
+
deltafi/test_kit/timed_ingress.py,sha256=ztE57hCuoZTrtFn_HlyHpHMFJN5Oyo3pPhVfbAI_weM,3790
|
|
20
21
|
deltafi/test_kit/transform.py,sha256=Qj9LIyZ8RYj0tCeq7agrDlEkkgtBR86TNinV9oncTnM,4089
|
|
21
|
-
deltafi-2.
|
|
22
|
-
deltafi-2.
|
|
23
|
-
deltafi-2.
|
|
22
|
+
deltafi-2.1.0.dist-info/METADATA,sha256=3NnbYSKHSIZx_m3Gh9wRBQyy3v0wzNV_QHnE5tQlCMs,1517
|
|
23
|
+
deltafi-2.1.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
24
|
+
deltafi-2.1.0.dist-info/RECORD,,
|
|
File without changes
|