deltafi 2.0rc1705080991758__tar.gz → 2.0rc1713530263087__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.0rc1705080991758 → deltafi-2.0rc1713530263087}/PKG-INFO +7 -8
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/action.py +1 -1
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/domain.py +1 -25
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/exception.py +0 -10
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/plugin.py +1 -10
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/result.py +38 -6
- deltafi-2.0rc1713530263087/deltafi/test_kit/domain.py +59 -0
- deltafi-2.0rc1713530263087/deltafi/test_kit/egress.py +54 -0
- deltafi-2.0rc1713530263087/deltafi/test_kit/enrich.py +70 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/test_kit/framework.py +105 -80
- deltafi-2.0rc1713530263087/deltafi/test_kit/transform.py +103 -0
- deltafi-2.0rc1713530263087/deltafi/test_kit/validate.py +54 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/pyproject.toml +11 -11
- deltafi-2.0rc1705080991758/deltafi/test_kit/transform.py +0 -72
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/README.md +0 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/__init__.py +0 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/actioneventqueue.py +0 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/actiontype.py +0 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/genericmodel.py +0 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/input.py +0 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/logger.py +0 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/metric.py +0 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/storage.py +0 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/test_kit/__init__.py +0 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/test_kit/assertions.py +0 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/test_kit/compare_helpers.py +0 -0
- {deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/test_kit/constants.py +0 -0
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: deltafi
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.0rc1713530263087
|
|
4
4
|
Summary: SDK for DeltaFi plugins and actions
|
|
5
5
|
License: Apache License, Version 2.0
|
|
6
6
|
Keywords: deltafi
|
|
7
7
|
Author: DeltaFi
|
|
8
8
|
Author-email: deltafi@systolic.com
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.9,<4.0
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
13
|
Classifier: License :: Other/Proprietary License
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
18
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
19
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
20
|
Classifier: Topic :: Software Development
|
|
22
|
-
Requires-Dist: deepdiff (>=6.
|
|
21
|
+
Requires-Dist: deepdiff (>=6.7.1)
|
|
23
22
|
Requires-Dist: json-logging (>=1.3.0)
|
|
24
|
-
Requires-Dist: minio (>=7.
|
|
25
|
-
Requires-Dist: pydantic (>=2.
|
|
23
|
+
Requires-Dist: minio (>=7.2.3)
|
|
24
|
+
Requires-Dist: pydantic (>=2.5.3)
|
|
26
25
|
Requires-Dist: redis (>=5.0.1)
|
|
27
26
|
Requires-Dist: requests (>=2.31.0)
|
|
28
|
-
Requires-Dist: urllib3 (>=2.0
|
|
27
|
+
Requires-Dist: urllib3 (>=2.1.0)
|
|
29
28
|
Project-URL: Bug Reports, https://chat.deltafi.org/deltafi/channels/bug-reports
|
|
30
29
|
Project-URL: Documentation, https://docs.deltafi.org/#/
|
|
31
30
|
Project-URL: Source Code, https://gitlab.com/deltafi/deltafi
|
|
@@ -110,7 +110,7 @@ class TimedIngressAction(Action, ABC):
|
|
|
110
110
|
|
|
111
111
|
class TransformAction(Action, ABC):
|
|
112
112
|
def __init__(self, description: str):
|
|
113
|
-
super().__init__(ActionType.TRANSFORM, description, (TransformResult, ErrorResult, FilterResult))
|
|
113
|
+
super().__init__(ActionType.TRANSFORM, description, (TransformResult, TransformResults, ErrorResult, FilterResult))
|
|
114
114
|
|
|
115
115
|
def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
|
|
116
116
|
return TransformInput(content=delta_file_message.content_list, metadata=delta_file_message.metadata)
|
|
@@ -296,41 +296,17 @@ class Content:
|
|
|
296
296
|
content_service=content_service)
|
|
297
297
|
|
|
298
298
|
|
|
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
299
|
class DeltaFileMessage(NamedTuple):
|
|
318
300
|
metadata: Dict[str, str]
|
|
319
301
|
content_list: List[Content]
|
|
320
|
-
domains: List[Domain]
|
|
321
|
-
enrichments: List[Domain]
|
|
322
302
|
|
|
323
303
|
@classmethod
|
|
324
304
|
def from_dict(cls, delta_file_message: dict, content_service: ContentService):
|
|
325
305
|
metadata = delta_file_message['metadata']
|
|
326
306
|
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
307
|
|
|
330
308
|
return DeltaFileMessage(metadata=metadata,
|
|
331
|
-
content_list=content_list
|
|
332
|
-
domains=domains,
|
|
333
|
-
enrichments=enrichments)
|
|
309
|
+
content_list=content_list)
|
|
334
310
|
|
|
335
311
|
|
|
336
312
|
class Event(NamedTuple):
|
|
@@ -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
|
|
@@ -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
|
|
@@ -275,14 +274,6 @@ class Plugin(object):
|
|
|
275
274
|
f"Action attempted to look up element {e.index + 1} (index {e.index}) from "
|
|
276
275
|
f"content list of size {e.size}",
|
|
277
276
|
f"{str(e)}\n{traceback.format_exc()}")
|
|
278
|
-
except MissingDomainException as e:
|
|
279
|
-
result = ErrorResult(event.context,
|
|
280
|
-
f"Action attempted to access domain {e.name}, which does not exist",
|
|
281
|
-
f"{str(e)}\n{traceback.format_exc()}")
|
|
282
|
-
except MissingEnrichmentException as e:
|
|
283
|
-
result = ErrorResult(event.context,
|
|
284
|
-
f"Action attempted to access enrichment {e.name}, which does not exist",
|
|
285
|
-
f"{str(e)}\n{traceback.format_exc()}")
|
|
286
277
|
except MissingMetadataException as e:
|
|
287
278
|
result = ErrorResult(event.context,
|
|
288
279
|
f"Missing metadata with key {e.key}",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
#
|
|
18
18
|
|
|
19
19
|
import abc
|
|
20
|
-
from enum import Enum
|
|
21
20
|
import uuid
|
|
22
|
-
from
|
|
21
|
+
from enum import Enum
|
|
22
|
+
from typing import NamedTuple
|
|
23
23
|
|
|
24
24
|
from deltafi.domain import Content, Context
|
|
25
25
|
from deltafi.metric import Metric
|
|
@@ -44,6 +44,7 @@ class Result:
|
|
|
44
44
|
|
|
45
45
|
def add_metric(self, metric: Metric):
|
|
46
46
|
self.metrics.append(metric)
|
|
47
|
+
return self
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
class EgressResult(Result):
|
|
@@ -76,7 +77,7 @@ class ErrorResult(Result):
|
|
|
76
77
|
|
|
77
78
|
|
|
78
79
|
class FilterResult(Result):
|
|
79
|
-
def __init__(self, context: Context, filtered_cause: str, filtered_context: str=None):
|
|
80
|
+
def __init__(self, context: Context, filtered_cause: str, filtered_context: str = None):
|
|
80
81
|
super().__init__('filter', 'FILTER', context)
|
|
81
82
|
self.filtered_cause = filtered_cause
|
|
82
83
|
self.filtered_context = filtered_context
|
|
@@ -178,8 +179,8 @@ class TransformResult(Result):
|
|
|
178
179
|
def __init__(self, context: Context):
|
|
179
180
|
super().__init__('transform', 'TRANSFORM', context)
|
|
180
181
|
self.content = []
|
|
181
|
-
self.metadata = {}
|
|
182
182
|
self.annotations = {}
|
|
183
|
+
self.metadata = {}
|
|
183
184
|
self.delete_metadata_keys = []
|
|
184
185
|
|
|
185
186
|
# content can be a single Content or a List[Content]
|
|
@@ -220,10 +221,41 @@ class TransformResult(Result):
|
|
|
220
221
|
self.delete_metadata_keys.append(key)
|
|
221
222
|
return self
|
|
222
223
|
|
|
223
|
-
def
|
|
224
|
+
def json(self):
|
|
224
225
|
return {
|
|
225
226
|
'content': [content.json() for content in self.content],
|
|
226
|
-
'metadata': self.metadata,
|
|
227
227
|
'annotations': self.annotations,
|
|
228
|
+
'metadata': self.metadata,
|
|
228
229
|
'deleteMetadataKeys': self.delete_metadata_keys
|
|
229
230
|
}
|
|
231
|
+
|
|
232
|
+
def response(self):
|
|
233
|
+
return [self.json()]
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class NamedTransformResult(NamedTuple):
|
|
237
|
+
result: TransformResult
|
|
238
|
+
name: str
|
|
239
|
+
|
|
240
|
+
def json(self):
|
|
241
|
+
j = self.result.json()
|
|
242
|
+
if self.name is not None:
|
|
243
|
+
j['name'] = self.name
|
|
244
|
+
return j
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class TransformResults(Result):
|
|
248
|
+
def __init__(self, context: Context):
|
|
249
|
+
super().__init__('transform', 'TRANSFORM', context)
|
|
250
|
+
self.named_results = []
|
|
251
|
+
|
|
252
|
+
def add_result(self, result: TransformResult, name: str = None):
|
|
253
|
+
self.named_results.append(NamedTransformResult(result, name))
|
|
254
|
+
return self
|
|
255
|
+
|
|
256
|
+
def response(self):
|
|
257
|
+
transform_events = []
|
|
258
|
+
for named_result in self.named_results:
|
|
259
|
+
json_dict = named_result.json()
|
|
260
|
+
transform_events.append(json_dict)
|
|
261
|
+
return transform_events
|
|
@@ -0,0 +1,59 @@
|
|
|
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.result import DomainResult
|
|
20
|
+
|
|
21
|
+
from .assertions import *
|
|
22
|
+
from .framework import TestCaseBase, ActionTest
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DomainTestCase(TestCaseBase):
|
|
26
|
+
def __init__(self, fields: Dict):
|
|
27
|
+
super().__init__(fields)
|
|
28
|
+
self.annotations = {}
|
|
29
|
+
|
|
30
|
+
def expect_domain_result(self, annotations: Dict):
|
|
31
|
+
self.expected_result_type = DomainResult
|
|
32
|
+
self.annotations = annotations
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class DomainActionTest(ActionTest):
|
|
36
|
+
def __init__(self, package_name: str):
|
|
37
|
+
"""
|
|
38
|
+
Provides structure for testing DeltaFi Domain action
|
|
39
|
+
Args:
|
|
40
|
+
package_name: name of the actions package for finding resources
|
|
41
|
+
"""
|
|
42
|
+
super().__init__(package_name)
|
|
43
|
+
|
|
44
|
+
def domain(self, test_case: DomainTestCase):
|
|
45
|
+
if test_case.expected_result_type == DomainResult:
|
|
46
|
+
self.expect_domain_result(test_case)
|
|
47
|
+
else:
|
|
48
|
+
super().execute(test_case)
|
|
49
|
+
|
|
50
|
+
def expect_domain_result(self, test_case: DomainTestCase):
|
|
51
|
+
result = super().run_and_check_result_type(test_case, DomainResult)
|
|
52
|
+
self.assert_domain_result(test_case, result)
|
|
53
|
+
|
|
54
|
+
def assert_domain_result(self, test_case: DomainTestCase, result: DomainResult):
|
|
55
|
+
# Check metrics
|
|
56
|
+
self.compare_metrics(test_case.expected_metrics, result.metrics)
|
|
57
|
+
|
|
58
|
+
# Check annotations
|
|
59
|
+
assert_keys_and_values(test_case.annotations, result.annotations)
|
|
@@ -0,0 +1,54 @@
|
|
|
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.result import EgressResult
|
|
20
|
+
|
|
21
|
+
from .assertions import *
|
|
22
|
+
from .framework import TestCaseBase, ActionTest
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class EgressTestCase(TestCaseBase):
|
|
26
|
+
def __init__(self, fields: Dict):
|
|
27
|
+
super().__init__(fields)
|
|
28
|
+
|
|
29
|
+
def expect_egress_result(self):
|
|
30
|
+
self.expected_result_type = EgressResult
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class EgressActionTest(ActionTest):
|
|
34
|
+
def __init__(self, package_name: str):
|
|
35
|
+
"""
|
|
36
|
+
Provides structure for testing DeltaFi Egress action
|
|
37
|
+
Args:
|
|
38
|
+
package_name: name of the actions package for finding resources
|
|
39
|
+
"""
|
|
40
|
+
super().__init__(package_name)
|
|
41
|
+
|
|
42
|
+
def egress(self, test_case: EgressTestCase):
|
|
43
|
+
if test_case.expected_result_type == EgressResult:
|
|
44
|
+
self.expect_egress_result(test_case)
|
|
45
|
+
else:
|
|
46
|
+
super().execute(test_case)
|
|
47
|
+
|
|
48
|
+
def expect_egress_result(self, test_case: EgressTestCase):
|
|
49
|
+
result = super().run_and_check_result_type(test_case, EgressResult)
|
|
50
|
+
self.assert_egress_result(test_case, result)
|
|
51
|
+
|
|
52
|
+
def assert_egress_result(self, test_case: EgressTestCase, result: EgressResult):
|
|
53
|
+
# Check metrics
|
|
54
|
+
self.compare_metrics(test_case.expected_metrics, result.metrics)
|
|
@@ -0,0 +1,70 @@
|
|
|
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.result import EnrichResult
|
|
20
|
+
|
|
21
|
+
from .assertions import *
|
|
22
|
+
from .framework import TestCaseBase, ActionTest
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class EnrichTestCase(TestCaseBase):
|
|
26
|
+
def __init__(self, fields: Dict):
|
|
27
|
+
super().__init__(fields)
|
|
28
|
+
self.enrichments = []
|
|
29
|
+
self.annotations = {}
|
|
30
|
+
|
|
31
|
+
def expect_enrich_result(self, annotations: Dict):
|
|
32
|
+
self.expected_result_type = EnrichResult
|
|
33
|
+
self.annotations = annotations
|
|
34
|
+
|
|
35
|
+
def add_enrichment(self, name: str, value: str, media_type: str):
|
|
36
|
+
self.enrichments.append({'name': name, 'value': value, 'mediaType': media_type})
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class EnrichActionTest(ActionTest):
|
|
40
|
+
def __init__(self, package_name: str):
|
|
41
|
+
"""
|
|
42
|
+
Provides structure for testing DeltaFi Enrich action
|
|
43
|
+
Args:
|
|
44
|
+
package_name: name of the actions package for finding resources
|
|
45
|
+
"""
|
|
46
|
+
super().__init__(package_name)
|
|
47
|
+
|
|
48
|
+
def enrich(self, test_case: EnrichTestCase):
|
|
49
|
+
if test_case.expected_result_type == EnrichResult:
|
|
50
|
+
self.expect_enrich_result(test_case)
|
|
51
|
+
else:
|
|
52
|
+
super().execute(test_case)
|
|
53
|
+
|
|
54
|
+
def expect_enrich_result(self, test_case: EnrichTestCase):
|
|
55
|
+
result = super().run_and_check_result_type(test_case, EnrichResult)
|
|
56
|
+
self.assert_enrich_result(test_case, result)
|
|
57
|
+
|
|
58
|
+
def assert_enrich_result(self, test_case: EnrichTestCase, result: EnrichResult):
|
|
59
|
+
# Check metrics
|
|
60
|
+
self.compare_metrics(test_case.expected_metrics, result.metrics)
|
|
61
|
+
|
|
62
|
+
# Check annotations
|
|
63
|
+
assert_keys_and_values(test_case.annotations, result.annotations)
|
|
64
|
+
|
|
65
|
+
# Check enrichments
|
|
66
|
+
assert_equal_len(test_case.enrichments, result.enrichments)
|
|
67
|
+
if len(test_case.enrichments) > 0:
|
|
68
|
+
for index, expected in enumerate(test_case.enrichments):
|
|
69
|
+
actual = result.enrichments[index]
|
|
70
|
+
assert_equal(expected, actual)
|
|
@@ -23,6 +23,7 @@ from typing import List
|
|
|
23
23
|
|
|
24
24
|
from deltafi.domain import DeltaFileMessage, Event, Content, Context
|
|
25
25
|
from deltafi.logger import get_logger
|
|
26
|
+
from deltafi.metric import Metric
|
|
26
27
|
from deltafi.result import ErrorResult, FilterResult
|
|
27
28
|
from deltafi.storage import Segment
|
|
28
29
|
|
|
@@ -43,8 +44,7 @@ class IOContent:
|
|
|
43
44
|
content_bytes (str): Bypass file read, and uses these bytes for content
|
|
44
45
|
"""
|
|
45
46
|
|
|
46
|
-
def __init__(self, file_name: str, content_name: str = None,
|
|
47
|
-
content_type: str = None, offset: int = 0,
|
|
47
|
+
def __init__(self, file_name: str, content_name: str = None, content_type: str = None, offset: int = 0,
|
|
48
48
|
content_bytes: str = ""):
|
|
49
49
|
self.file_name = file_name
|
|
50
50
|
if content_name is None:
|
|
@@ -79,12 +79,8 @@ class LoadedContent:
|
|
|
79
79
|
self.data = data
|
|
80
80
|
else:
|
|
81
81
|
self.data = ioc.content_bytes
|
|
82
|
-
self.segment = Segment.from_dict(
|
|
83
|
-
"uuid": str(uuid.uuid4()),
|
|
84
|
-
"offset": self.offset,
|
|
85
|
-
"size": len(self.data),
|
|
86
|
-
"did": did
|
|
87
|
-
})
|
|
82
|
+
self.segment = Segment.from_dict(
|
|
83
|
+
{"uuid": str(uuid.uuid4()), "offset": self.offset, "size": len(self.data), "did": did})
|
|
88
84
|
|
|
89
85
|
|
|
90
86
|
class InternalContentService:
|
|
@@ -97,10 +93,7 @@ class InternalContentService:
|
|
|
97
93
|
self.loaded_content[c.segment.uuid] = c
|
|
98
94
|
|
|
99
95
|
def put_str(self, did: str, string_data: str):
|
|
100
|
-
segment = Segment(uuid=str(uuid.uuid4()),
|
|
101
|
-
offset=0,
|
|
102
|
-
size=len(string_data),
|
|
103
|
-
did=did)
|
|
96
|
+
segment = Segment(uuid=str(uuid.uuid4()), offset=0, size=len(string_data), did=did)
|
|
104
97
|
self.outputs[segment.uuid] = string_data
|
|
105
98
|
return segment
|
|
106
99
|
|
|
@@ -124,8 +117,7 @@ class TestCaseBase(ABC):
|
|
|
124
117
|
- inputs: (optional) List[IOContent]: input content to action
|
|
125
118
|
- parameters: (optional) Dict: map of action input parameters
|
|
126
119
|
- in_meta: (optional) Dict: map of metadata as input to action
|
|
127
|
-
-
|
|
128
|
-
- in_enrichments: (optional) List[Domain]: list of enrichments as input to action
|
|
120
|
+
- did: (optional): str: overrides random DID
|
|
129
121
|
"""
|
|
130
122
|
if "action" in data:
|
|
131
123
|
self.action = data["action"]
|
|
@@ -144,34 +136,41 @@ class TestCaseBase(ABC):
|
|
|
144
136
|
|
|
145
137
|
self.inputs = data["inputs"] if "inputs" in data else []
|
|
146
138
|
self.file_name = data["file_name"] if "file_name" in data else "filename"
|
|
147
|
-
self.outputs = data["outputs"] if "outputs" in data else []
|
|
148
139
|
self.parameters = data["parameters"] if "parameters" in data else {}
|
|
149
140
|
self.in_meta = data["in_meta"] if "in_meta" in data else {}
|
|
150
|
-
self.
|
|
151
|
-
self.in_enrichments = data["in_enrichments"] if "in_enrichments" in data else []
|
|
141
|
+
self.use_did = data["did"] if "did" in data else None
|
|
152
142
|
self.expected_result_type = None
|
|
153
|
-
self.
|
|
154
|
-
self.
|
|
143
|
+
self.err_or_filt_cause = None
|
|
144
|
+
self.err_or_filt_context = None
|
|
145
|
+
self.err_or_filt_annotations = None
|
|
146
|
+
self.expected_metrics = []
|
|
155
147
|
|
|
156
|
-
def
|
|
148
|
+
def add_metric(self, metric: Metric):
|
|
149
|
+
self.expected_metrics.append(metric)
|
|
150
|
+
|
|
151
|
+
def expect_error_result(self, cause: str, context: str, annotations: Dict = None):
|
|
157
152
|
"""
|
|
158
153
|
A Sets the expected output of the action to an Error Result
|
|
159
154
|
:param cause: the expected error cause
|
|
160
155
|
:param context: the expected error context
|
|
156
|
+
:param annotations (Optional): Dict: the expected annotations
|
|
161
157
|
"""
|
|
162
158
|
self.expected_result_type = ErrorResult
|
|
163
|
-
self.
|
|
164
|
-
self.
|
|
159
|
+
self.err_or_filt_cause = cause
|
|
160
|
+
self.err_or_filt_context = context
|
|
161
|
+
self.err_or_filt_annotations = annotations
|
|
165
162
|
|
|
166
|
-
def expect_filter_result(self, cause: str, context: str=None):
|
|
163
|
+
def expect_filter_result(self, cause: str, context: str = None, annotations: Dict = None):
|
|
167
164
|
"""
|
|
168
165
|
A Sets the expected output of the action to a Filter Result
|
|
169
166
|
:param cause: the expected filter cause (message)
|
|
170
|
-
:param context: the expected
|
|
167
|
+
:param context (Optional): the expected filter context
|
|
168
|
+
:param annotations (Optional): Dict: the expected annotations
|
|
171
169
|
"""
|
|
172
170
|
self.expected_result_type = FilterResult
|
|
173
|
-
self.
|
|
174
|
-
self.
|
|
171
|
+
self.err_or_filt_cause = cause
|
|
172
|
+
self.err_or_filt_context = context
|
|
173
|
+
self.err_or_filt_annotations = annotations
|
|
175
174
|
|
|
176
175
|
|
|
177
176
|
class ActionTest(ABC):
|
|
@@ -183,15 +182,27 @@ class ActionTest(ABC):
|
|
|
183
182
|
"""
|
|
184
183
|
self.content_service = InternalContentService()
|
|
185
184
|
self.did = ""
|
|
186
|
-
self.expected_outputs = []
|
|
187
185
|
self.loaded_inputs = []
|
|
188
186
|
self.package_name = package_name
|
|
189
187
|
self.res_path = ""
|
|
190
188
|
|
|
191
|
-
def __reset__(self):
|
|
189
|
+
def __reset__(self, did: str):
|
|
192
190
|
self.content_service = InternalContentService()
|
|
191
|
+
<<<<<<< HEAD
|
|
192
|
+
if did is None:
|
|
193
|
+
self.did = str(uuid.uuid4())
|
|
194
|
+
else:
|
|
195
|
+
self.did = did
|
|
196
|
+
self.expected_outputs = []
|
|
197
|
+
||||||| parent of 831733c7 (2.0 Refactor)
|
|
193
198
|
self.did = str(uuid.uuid4())
|
|
194
199
|
self.expected_outputs = []
|
|
200
|
+
=======
|
|
201
|
+
if did is None:
|
|
202
|
+
self.did = str(uuid.uuid4())
|
|
203
|
+
else:
|
|
204
|
+
self.did = did
|
|
205
|
+
>>>>>>> 831733c7 (2.0 Refactor)
|
|
195
206
|
self.loaded_inputs = []
|
|
196
207
|
self.res_path = ""
|
|
197
208
|
|
|
@@ -212,19 +223,10 @@ class ActionTest(ABC):
|
|
|
212
223
|
else:
|
|
213
224
|
self.loaded_inputs.append(LoadedContent(self.did, input_ioc, None))
|
|
214
225
|
|
|
215
|
-
# Load expected outputs
|
|
216
|
-
for output_ioc in test_case.outputs:
|
|
217
|
-
if len(output_ioc.content_bytes) == 0:
|
|
218
|
-
self.expected_outputs.append(LoadedContent(self.did, output_ioc, self.load_file(output_ioc)))
|
|
219
|
-
else:
|
|
220
|
-
self.expected_outputs.append(LoadedContent(self.did, output_ioc, None))
|
|
221
|
-
|
|
222
226
|
def make_content_list(self, test_case: TestCaseBase):
|
|
223
227
|
content_list = []
|
|
224
228
|
for loaded_input in self.loaded_inputs:
|
|
225
|
-
c = Content(name=loaded_input.name,
|
|
226
|
-
segments=[loaded_input.segment],
|
|
227
|
-
media_type=loaded_input.content_type,
|
|
229
|
+
c = Content(name=loaded_input.name, segments=[loaded_input.segment], media_type=loaded_input.content_type,
|
|
228
230
|
content_service=self.content_service)
|
|
229
231
|
content_list.append(c)
|
|
230
232
|
loaded_input.content = c
|
|
@@ -235,40 +237,48 @@ class ActionTest(ABC):
|
|
|
235
237
|
content_list = self.make_content_list(test_case)
|
|
236
238
|
self.content_service.load(self.loaded_inputs)
|
|
237
239
|
|
|
240
|
+
<<<<<<< HEAD
|
|
241
|
+
return DeltaFileMessage(
|
|
242
|
+
metadata=test_case.in_meta,
|
|
243
|
+
content_list=content_list,
|
|
244
|
+
domains=test_case.in_domains,
|
|
245
|
+
enrichments=test_case.in_enrichments)
|
|
246
|
+
||||||| parent of 831733c7 (2.0 Refactor)
|
|
238
247
|
return DeltaFileMessage(metadata=test_case.in_meta,
|
|
239
248
|
content_list=content_list,
|
|
240
249
|
domains=test_case.in_domains,
|
|
241
250
|
enrichments=test_case.in_enrichments)
|
|
251
|
+
=======
|
|
252
|
+
return DeltaFileMessage(metadata=test_case.in_meta,
|
|
253
|
+
content_list=content_list)
|
|
254
|
+
>>>>>>> 831733c7 (2.0 Refactor)
|
|
242
255
|
|
|
243
256
|
def make_context(self, test_case: TestCaseBase):
|
|
244
257
|
action_name = INGRESS_FLOW + "." + test_case.action.__class__.__name__
|
|
245
|
-
return Context(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
258
|
+
return Context(
|
|
259
|
+
did=self.did,
|
|
260
|
+
action_flow=INGRESS_FLOW,
|
|
261
|
+
action_name=action_name,
|
|
262
|
+
source_filename=test_case.file_name,
|
|
263
|
+
ingress_flow=INGRESS_FLOW,
|
|
264
|
+
egress_flow=EGRESS_FLOW,
|
|
265
|
+
system=SYSTEM,
|
|
266
|
+
hostname=HOSTNAME,
|
|
267
|
+
content_service=self.content_service,
|
|
268
|
+
collect=None,
|
|
269
|
+
collected_dids=None,
|
|
270
|
+
logger=get_logger())
|
|
257
271
|
|
|
258
272
|
def make_event(self, test_case: TestCaseBase):
|
|
259
|
-
return Event(
|
|
260
|
-
|
|
261
|
-
context=self.make_context(test_case),
|
|
262
|
-
params=test_case.parameters,
|
|
263
|
-
queue_name="",
|
|
264
|
-
return_address="")
|
|
273
|
+
return Event(delta_file_messages=[self.make_df_msg(test_case)], context=self.make_context(test_case),
|
|
274
|
+
params=test_case.parameters, queue_name="", return_address="")
|
|
265
275
|
|
|
266
276
|
def call_action(self, test_case: TestCaseBase):
|
|
267
277
|
self.get_contents(test_case)
|
|
268
278
|
return test_case.action.execute_action(self.make_event(test_case))
|
|
269
279
|
|
|
270
280
|
def run_and_check_result_type(self, test_case: TestCaseBase, result_type):
|
|
271
|
-
self.__reset__()
|
|
281
|
+
self.__reset__(test_case.use_did)
|
|
272
282
|
result = self.call_action(test_case)
|
|
273
283
|
|
|
274
284
|
if not isinstance(result, result_type):
|
|
@@ -279,18 +289,25 @@ class ActionTest(ABC):
|
|
|
279
289
|
def execute_error(self, test_case: TestCaseBase):
|
|
280
290
|
result = self.run_and_check_result_type(test_case, ErrorResult)
|
|
281
291
|
resp = result.response()
|
|
282
|
-
|
|
283
|
-
|
|
292
|
+
assert_equal_with_label(test_case.err_or_filt_cause, resp['cause'], "error cause")
|
|
293
|
+
if test_case.err_or_filt_context is not None:
|
|
294
|
+
assert_equal_with_label(test_case.err_or_filt_context, resp['context'], "error context")
|
|
295
|
+
if test_case.err_or_filt_annotations is not None:
|
|
296
|
+
assert_keys_and_values(test_case.err_or_filt_annotations, result.annotations)
|
|
284
297
|
|
|
285
|
-
def execute_filter(self, test_case):
|
|
298
|
+
def execute_filter(self, test_case: TestCaseBase):
|
|
286
299
|
result = self.run_and_check_result_type(test_case, FilterResult)
|
|
287
300
|
resp = result.response()
|
|
288
|
-
|
|
301
|
+
assert_equal_with_label(test_case.err_or_filt_cause, resp['message'], "filter cause")
|
|
302
|
+
if test_case.err_or_filt_context is not None:
|
|
303
|
+
assert_equal_with_label(test_case.err_or_filt_context, resp['context'], "filter context")
|
|
304
|
+
if test_case.err_or_filt_annotations is not None:
|
|
305
|
+
assert_keys_and_values(test_case.err_or_filt_annotations, result.annotations)
|
|
289
306
|
|
|
290
307
|
def execute(self, test_case: TestCaseBase):
|
|
291
|
-
if
|
|
308
|
+
if test_case.expected_result_type == ErrorResult:
|
|
292
309
|
self.execute_error(test_case)
|
|
293
|
-
elif
|
|
310
|
+
elif test_case.expected_result_type == FilterResult:
|
|
294
311
|
self.execute_filter(test_case)
|
|
295
312
|
else:
|
|
296
313
|
raise ValueError(f"unknown type: {test_case.expected_result_type}")
|
|
@@ -302,23 +319,21 @@ class ActionTest(ABC):
|
|
|
302
319
|
def compare_one_content(self, comparitor: CompareHelper, expected: LoadedContent, actual, index):
|
|
303
320
|
self.compare_content_details(expected, actual)
|
|
304
321
|
seg_id = actual.segments[0].uuid
|
|
305
|
-
comparitor.compare(
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
322
|
+
comparitor.compare(expected.data, self.content_service.get_output(seg_id), f"Content[{index}]")
|
|
323
|
+
|
|
324
|
+
def compare_content_list(self, comparitor: CompareHelper, expected_outputs: List[IOContent], content: List):
|
|
325
|
+
assert_equal_len(expected_outputs, content)
|
|
326
|
+
for index, expected_ioc in enumerate(expected_outputs):
|
|
327
|
+
if len(expected_ioc.content_bytes) == 0:
|
|
328
|
+
expected = LoadedContent(self.did, expected_ioc, self.load_file(output_ioc))
|
|
329
|
+
else:
|
|
330
|
+
expected = LoadedContent(self.did, expected_ioc, None)
|
|
314
331
|
self.compare_one_content(comparitor, expected, content[index], index)
|
|
315
332
|
|
|
316
|
-
def
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
assert_equal(expected['name'], actual['name'])
|
|
321
|
-
assert_equal(expected['mediaType'], actual['mediaType'])
|
|
333
|
+
def compare_one_metric(self, expected: Metric, result: Metric):
|
|
334
|
+
assert expected.name == result.name
|
|
335
|
+
assert_equal_with_label(expected.value, result.value, expected.name)
|
|
336
|
+
assert_keys_and_values(expected.tags, result.tags)
|
|
322
337
|
|
|
323
338
|
expected_value = expected['value']
|
|
324
339
|
if type(expected_value) == str:
|
|
@@ -327,5 +342,15 @@ class ActionTest(ABC):
|
|
|
327
342
|
expected_data = self.load_file(expected_value)
|
|
328
343
|
comparitor.compare(expected_data, actual['value'], f"Domain[{index}]")
|
|
329
344
|
else:
|
|
330
|
-
raise ValueError(
|
|
331
|
-
|
|
345
|
+
raise ValueError(f"unknown expected_value type: {type(expected_value)}")
|
|
346
|
+
|
|
347
|
+
def compare_one_metric(self, expected: Metric, result: Metric):
|
|
348
|
+
assert expected.name == result.name
|
|
349
|
+
assert_equal_with_label(expected.value, result.value, expected.name)
|
|
350
|
+
assert_keys_and_values(expected.tags, result.tags)
|
|
351
|
+
|
|
352
|
+
def compare_metrics(self, expected_metrics: List[Metric], results: List[Metric]):
|
|
353
|
+
if len(expected_metrics) > 0:
|
|
354
|
+
assert_equal_len(expected_metrics, results)
|
|
355
|
+
for index, expected in enumerate(expected_metrics):
|
|
356
|
+
self.compare_one_metric(expected, results[index])
|
|
@@ -0,0 +1,103 @@
|
|
|
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 typing import List
|
|
20
|
+
|
|
21
|
+
from deltafi.result import TransformResult, TransformResults
|
|
22
|
+
|
|
23
|
+
from .assertions import *
|
|
24
|
+
from .framework import TestCaseBase, ActionTest, IOContent
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TransformTestCase(TestCaseBase):
|
|
28
|
+
def __init__(self, fields: Dict):
|
|
29
|
+
super().__init__(fields)
|
|
30
|
+
self.results = []
|
|
31
|
+
|
|
32
|
+
def expect_transform_result(self):
|
|
33
|
+
self.expected_result_type = TransformResult
|
|
34
|
+
|
|
35
|
+
def expect_transform_results(self):
|
|
36
|
+
self.expected_result_type = TransformResults
|
|
37
|
+
|
|
38
|
+
def add_transform_result(self, content: List[IOContent], metadata: Dict, delete_metadata_keys: List[str],
|
|
39
|
+
annotations: Dict, name: str = None):
|
|
40
|
+
self.results.append(
|
|
41
|
+
{
|
|
42
|
+
'content': content,
|
|
43
|
+
'metadata': metadata,
|
|
44
|
+
'delete_metadata_keys': delete_metadata_keys,
|
|
45
|
+
'annotations': annotations,
|
|
46
|
+
'name': name
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TransformActionTest(ActionTest):
|
|
52
|
+
def __init__(self, package_name: str):
|
|
53
|
+
"""
|
|
54
|
+
Provides structure for testing DeltaFi Transform action
|
|
55
|
+
Args:
|
|
56
|
+
package_name: name of the actions package for finding resources
|
|
57
|
+
"""
|
|
58
|
+
super().__init__(package_name)
|
|
59
|
+
|
|
60
|
+
def transform(self, test_case: TransformTestCase):
|
|
61
|
+
if test_case.expected_result_type == TransformResult:
|
|
62
|
+
self.expect_transform_result(test_case)
|
|
63
|
+
elif test_case.expected_result_type == TransformResults:
|
|
64
|
+
self.expect_transform_results(test_case)
|
|
65
|
+
else:
|
|
66
|
+
super().execute(test_case)
|
|
67
|
+
|
|
68
|
+
def expect_transform_result(self, test_case: TransformTestCase):
|
|
69
|
+
result = super().run_and_check_result_type(test_case, TransformResult)
|
|
70
|
+
self.assert_transform_result(test_case, result)
|
|
71
|
+
|
|
72
|
+
def expect_transform_results(self, test_case: TransformTestCase):
|
|
73
|
+
result = super().run_and_check_result_type(test_case, TransformResults)
|
|
74
|
+
self.assert_transform_results(test_case, result)
|
|
75
|
+
|
|
76
|
+
def assert_transform_results(self, test_case: TransformTestCase, result: TransformResults):
|
|
77
|
+
assert_equal_len(test_case.results, result.named_results)
|
|
78
|
+
for index, named_result in enumerate(result.named_results):
|
|
79
|
+
self.compare_one_transform_result(test_case, named_result.result, index)
|
|
80
|
+
expected = test_case.results[index]
|
|
81
|
+
if 'name' in expected:
|
|
82
|
+
assert_equal_with_label(expected["name"], named_result.name, f"name[{index}]")
|
|
83
|
+
|
|
84
|
+
def assert_transform_result(self, test_case: TransformTestCase, result: TransformResult):
|
|
85
|
+
# Check metrics
|
|
86
|
+
self.compare_metrics(test_case.expected_metrics, result.metrics)
|
|
87
|
+
self.compare_one_transform_result(test_case, result, 0)
|
|
88
|
+
|
|
89
|
+
def compare_one_transform_result(self, test_case: TransformTestCase, result: TransformResult, 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)
|
|
97
|
+
|
|
98
|
+
# Check deleted metadata
|
|
99
|
+
for key in expected['delete_metadata_keys']:
|
|
100
|
+
assert_key_in(key, result.delete_metadata_keys)
|
|
101
|
+
|
|
102
|
+
# Check annotations
|
|
103
|
+
assert_keys_and_values(expected['annotations'], result.annotations)
|
|
@@ -0,0 +1,54 @@
|
|
|
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.result import ValidateResult
|
|
20
|
+
|
|
21
|
+
from .assertions import *
|
|
22
|
+
from .framework import TestCaseBase, ActionTest
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ValidateTestCase(TestCaseBase):
|
|
26
|
+
def __init__(self, fields: Dict):
|
|
27
|
+
super().__init__(fields)
|
|
28
|
+
|
|
29
|
+
def expect_validate_result(self):
|
|
30
|
+
self.expected_result_type = ValidateResult
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ValidateActionTest(ActionTest):
|
|
34
|
+
def __init__(self, package_name: str):
|
|
35
|
+
"""
|
|
36
|
+
Provides structure for testing DeltaFi Validate action
|
|
37
|
+
Args:
|
|
38
|
+
package_name: name of the actions package for finding resources
|
|
39
|
+
"""
|
|
40
|
+
super().__init__(package_name)
|
|
41
|
+
|
|
42
|
+
def validate(self, test_case: ValidateTestCase):
|
|
43
|
+
if test_case.expected_result_type == ValidateResult:
|
|
44
|
+
self.expect_validate_result(test_case)
|
|
45
|
+
else:
|
|
46
|
+
super().execute(test_case)
|
|
47
|
+
|
|
48
|
+
def expect_validate_result(self, test_case: ValidateTestCase):
|
|
49
|
+
result = super().run_and_check_result_type(test_case, ValidateResult)
|
|
50
|
+
self.assert_validate_result(test_case, result)
|
|
51
|
+
|
|
52
|
+
def assert_validate_result(self, test_case: ValidateTestCase, result: ValidateResult):
|
|
53
|
+
# Check metrics
|
|
54
|
+
self.compare_metrics(test_case.expected_metrics, result.metrics)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "deltafi"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.0rc1713530263087"
|
|
4
4
|
description = "SDK for DeltaFi plugins and actions"
|
|
5
5
|
authors = ["DeltaFi <deltafi@systolic.com>"]
|
|
6
6
|
license = "Apache License, Version 2.0"
|
|
@@ -13,28 +13,28 @@ classifiers = [
|
|
|
13
13
|
"Development Status :: 4 - Beta",
|
|
14
14
|
"Intended Audience :: Developers",
|
|
15
15
|
"Programming Language :: Python :: 3",
|
|
16
|
-
"Programming Language :: Python :: 3.7",
|
|
17
|
-
"Programming Language :: Python :: 3.8",
|
|
18
16
|
"Programming Language :: Python :: 3.9",
|
|
19
|
-
"Programming Language :: Python :: 3.10"
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12"
|
|
20
20
|
]
|
|
21
21
|
|
|
22
22
|
[tool.poetry.dependencies]
|
|
23
|
-
python = "^3.
|
|
24
|
-
deepdiff = ">=6.
|
|
23
|
+
python = "^3.9"
|
|
24
|
+
deepdiff = ">=6.7.1"
|
|
25
25
|
json-logging = ">=1.3.0"
|
|
26
|
-
minio = ">=7.
|
|
27
|
-
pydantic = ">=2.
|
|
26
|
+
minio = ">=7.2.3"
|
|
27
|
+
pydantic = ">=2.5.3"
|
|
28
28
|
redis = ">=5.0.1"
|
|
29
29
|
requests = ">=2.31.0"
|
|
30
|
-
urllib3 = ">=2.0
|
|
30
|
+
urllib3 = ">=2.1.0"
|
|
31
31
|
|
|
32
32
|
[tool.poetry.group.test]
|
|
33
33
|
optional = true
|
|
34
34
|
|
|
35
35
|
[tool.poetry.group.test.dependencies]
|
|
36
|
-
pytest = ">=7.4.
|
|
37
|
-
pytest-mock = ">=3.
|
|
36
|
+
pytest = ">=7.4.4"
|
|
37
|
+
pytest-mock = ">=3.12.0"
|
|
38
38
|
mockito = ">=1.4.0"
|
|
39
39
|
|
|
40
40
|
[tool.poetry.urls]
|
|
@@ -1,72 +0,0 @@
|
|
|
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 typing import List
|
|
20
|
-
|
|
21
|
-
from deltafi.result import TransformResult
|
|
22
|
-
|
|
23
|
-
from .assertions import *
|
|
24
|
-
from .framework import TestCaseBase, ActionTest
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class TransformTestCase(TestCaseBase):
|
|
28
|
-
def __init__(self, fields: Dict):
|
|
29
|
-
super().__init__(fields)
|
|
30
|
-
self.metadata = {}
|
|
31
|
-
self.delete_metadata_keys = []
|
|
32
|
-
self.annotations = {}
|
|
33
|
-
|
|
34
|
-
def expect_transform_result(self, metadata: Dict, delete_metadata_keys: List[str], annotations: Dict):
|
|
35
|
-
self.expected_result_type = TransformResult
|
|
36
|
-
self.metadata = metadata
|
|
37
|
-
self.delete_metadata_keys = delete_metadata_keys
|
|
38
|
-
self.annotations = annotations
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class TransformActionTest(ActionTest):
|
|
42
|
-
def __init__(self, package_name: str):
|
|
43
|
-
"""
|
|
44
|
-
Provides structure for testing DeltaFi Transform action
|
|
45
|
-
Args:
|
|
46
|
-
package_name: name of the actions package for finding resources
|
|
47
|
-
"""
|
|
48
|
-
super().__init__(package_name)
|
|
49
|
-
|
|
50
|
-
def transform(self, test_case: TransformTestCase):
|
|
51
|
-
if test_case.expected_result_type == TransformResult:
|
|
52
|
-
self.expect_transform_result(test_case)
|
|
53
|
-
else:
|
|
54
|
-
super().execute(test_case)
|
|
55
|
-
|
|
56
|
-
def expect_transform_result(self, test_case: TransformTestCase):
|
|
57
|
-
result = super().run_and_check_result_type(test_case, TransformResult)
|
|
58
|
-
self.assert_transform_result(test_case, result)
|
|
59
|
-
|
|
60
|
-
def assert_transform_result(self, test_case: TransformTestCase, result: TransformResult):
|
|
61
|
-
# Check output
|
|
62
|
-
self.compare_all_output(test_case.compare_tool, result.content)
|
|
63
|
-
|
|
64
|
-
# Check metadata
|
|
65
|
-
assert_keys_and_values(test_case.metadata, result.metadata)
|
|
66
|
-
|
|
67
|
-
# Check deleted metadata
|
|
68
|
-
for key in test_case.delete_metadata_keys:
|
|
69
|
-
assert_key_in(key, result.delete_metadata_keys)
|
|
70
|
-
|
|
71
|
-
# Check annotations
|
|
72
|
-
assert_keys_and_values(test_case.annotations, result.annotations)
|
|
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
|
{deltafi-2.0rc1705080991758 → deltafi-2.0rc1713530263087}/deltafi/test_kit/compare_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|