deltafi 2.0rc1705080991758__py3-none-any.whl → 2.0rc1713530263087__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/action.py CHANGED
@@ -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)
deltafi/domain.py CHANGED
@@ -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):
deltafi/exception.py CHANGED
@@ -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
deltafi/plugin.py CHANGED
@@ -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, MissingDomainException, MissingEnrichmentException, \
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}",
deltafi/result.py CHANGED
@@ -17,9 +17,9 @@
17
17
  #
18
18
 
19
19
  import abc
20
- from enum import Enum
21
20
  import uuid
22
- from typing import Dict, List
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 response(self):
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
- - in_domains: (optional) List[Domain]: list of domains as input to action
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.in_domains = data["in_domains"] if "in_domains" in data else []
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.cause_regex = None
154
- self.context_regex = None
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 expect_error_result(self, cause: str, context: str):
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.cause_regex = cause
164
- self.context_regex = context
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 error context (optional)
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.cause_regex = cause
174
- self.context_regex = context
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(did=self.did,
246
- action_flow=INGRESS_FLOW,
247
- action_name=action_name,
248
- source_filename=test_case.file_name,
249
- ingress_flow=INGRESS_FLOW,
250
- egress_flow=EGRESS_FLOW,
251
- system=SYSTEM,
252
- hostname=HOSTNAME,
253
- content_service=self.content_service,
254
- collect=None,
255
- collected_dids=None,
256
- logger=get_logger())
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
- delta_file_messages=[self.make_df_msg(test_case)],
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
- assert resp['cause'] == test_case.cause_regex
283
- assert resp['context'] == test_case.context_regex
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
- assert resp['message'] == test_case.cause_regex
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 isinstance(test_case.expected_result_type, ErrorResult.__class__):
308
+ if test_case.expected_result_type == ErrorResult:
292
309
  self.execute_error(test_case)
293
- elif isinstance(test_case.expected_result_type, FilterResult.__class__):
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
- expected.data,
307
- self.content_service.get_output(seg_id),
308
- f"Content[{index}]"
309
- )
310
-
311
- def compare_all_output(self, comparitor: CompareHelper, content: List):
312
- assert_equal_len(self.expected_outputs, content)
313
- for index, expected in enumerate(self.expected_outputs):
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 compare_domains(self, comparitor: CompareHelper, expected_items: List[Dict], results: List[Dict]):
317
- assert_equal_len(expected_items, results)
318
- for index, expected in enumerate(expected_items):
319
- actual = results[index]
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
- f"unknown expected_value type: {type(expected_value)}")
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])
@@ -18,24 +18,34 @@
18
18
 
19
19
  from typing import List
20
20
 
21
- from deltafi.result import TransformResult
21
+ from deltafi.result import TransformResult, TransformResults
22
22
 
23
23
  from .assertions import *
24
- from .framework import TestCaseBase, ActionTest
24
+ from .framework import TestCaseBase, ActionTest, IOContent
25
25
 
26
26
 
27
27
  class TransformTestCase(TestCaseBase):
28
28
  def __init__(self, fields: Dict):
29
29
  super().__init__(fields)
30
- self.metadata = {}
31
- self.delete_metadata_keys = []
32
- self.annotations = {}
30
+ self.results = []
33
31
 
34
- def expect_transform_result(self, metadata: Dict, delete_metadata_keys: List[str], annotations: Dict):
32
+ def expect_transform_result(self):
35
33
  self.expected_result_type = TransformResult
36
- self.metadata = metadata
37
- self.delete_metadata_keys = delete_metadata_keys
38
- self.annotations = annotations
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
+ )
39
49
 
40
50
 
41
51
  class TransformActionTest(ActionTest):
@@ -50,6 +60,8 @@ class TransformActionTest(ActionTest):
50
60
  def transform(self, test_case: TransformTestCase):
51
61
  if test_case.expected_result_type == TransformResult:
52
62
  self.expect_transform_result(test_case)
63
+ elif test_case.expected_result_type == TransformResults:
64
+ self.expect_transform_results(test_case)
53
65
  else:
54
66
  super().execute(test_case)
55
67
 
@@ -57,16 +69,35 @@ class TransformActionTest(ActionTest):
57
69
  result = super().run_and_check_result_type(test_case, TransformResult)
58
70
  self.assert_transform_result(test_case, result)
59
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
+
60
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
+
61
92
  # Check output
62
- self.compare_all_output(test_case.compare_tool, result.content)
93
+ self.compare_content_list(test_case.compare_tool, expected['content'], result.content)
63
94
 
64
95
  # Check metadata
65
- assert_keys_and_values(test_case.metadata, result.metadata)
96
+ assert_keys_and_values(expected['metadata'], result.metadata)
66
97
 
67
98
  # Check deleted metadata
68
- for key in test_case.delete_metadata_keys:
99
+ for key in expected['delete_metadata_keys']:
69
100
  assert_key_in(key, result.delete_metadata_keys)
70
101
 
71
102
  # Check annotations
72
- assert_keys_and_values(test_case.annotations, result.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,31 +1,30 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deltafi
3
- Version: 2.0rc1705080991758
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.7,<4.0
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.3.1)
21
+ Requires-Dist: deepdiff (>=6.7.1)
23
22
  Requires-Dist: json-logging (>=1.3.0)
24
- Requires-Dist: minio (>=7.1.17)
25
- Requires-Dist: pydantic (>=2.4.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.6)
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
@@ -0,0 +1,26 @@
1
+ deltafi/__init__.py,sha256=qv6y7PpBG0ekTN9EuD9Lj8JYOLVqZA6tvHwgjplncAM,709
2
+ deltafi/action.py,sha256=xxDQD-gye6gW9Q13Ylm51qiVDgqD36WMK191Gi7FEis,5323
3
+ deltafi/actioneventqueue.py,sha256=mCRE1PFXy_KvaeezTeMze5N400CO1V06zEF8FFD6xZk,2847
4
+ deltafi/actiontype.py,sha256=6OoZNEMcreD5f_QaGhTu8MEYLr3CalzcNu9XuFx40zg,865
5
+ deltafi/domain.py,sha256=weZII1PfUOoCUpk3LfS8EXhX3ZQ73fFuoSwKgnyBX3Y,11301
6
+ deltafi/exception.py,sha256=Z8LwxxTiTyuG3v7FAsjfz6cAqRAJFKK2q_2TbEDrIJE,943
7
+ deltafi/genericmodel.py,sha256=8VXWgFMjIwG4o3rkcLOa3bQH0Nf-T1Kw621QtMw-4q0,1090
8
+ deltafi/input.py,sha256=2qQcOWqz7lOXZIZipTz5XtcGESpjdnUAsZ7b9YqHw7M,1656
9
+ deltafi/logger.py,sha256=q76R_Gn8BfcASH3Zy0V82m5ot34bjTnSELyKbjhvx3E,2136
10
+ deltafi/metric.py,sha256=eIDjZQVO53KuAFoEtjLNFwqMrp_7BC0Td9GLD1tb6sE,967
11
+ deltafi/plugin.py,sha256=5mx7Og6aREwjPDY4BknCnDsuWDg7mz8On6J0M8mQ16I,12294
12
+ deltafi/result.py,sha256=rugAkh9NZdl0QDHzyxDoZbkE8dnJNAFLbqBNdIjjn_k,8177
13
+ deltafi/storage.py,sha256=toq58EPZgzj2TfDF-YpFUmRnsWSjACA0KQAZzkM04xU,2740
14
+ deltafi/test_kit/__init__.py,sha256=qv6y7PpBG0ekTN9EuD9Lj8JYOLVqZA6tvHwgjplncAM,709
15
+ deltafi/test_kit/assertions.py,sha256=2eahqmbedhnHXMguiJHdaV-6sx4_jvCbxH4HSzx6PV8,1378
16
+ deltafi/test_kit/compare_helpers.py,sha256=Oi07zsq7jlpAHxqjwHsea84acC3lAL3ooG0VC42lLAc,1581
17
+ deltafi/test_kit/constants.py,sha256=epz0OS-qILcc8t7iIs5B3m2-wvZx8FpYpyb19ZsImbI,833
18
+ deltafi/test_kit/domain.py,sha256=uqpDravlb6E3Ddm4QpRaJTdiVuQ2MMdZnzc4Oi9EZJA,2090
19
+ deltafi/test_kit/egress.py,sha256=nF7YYak-_X35m7h2rSIcju86IJBf8cVaFHs7xw7-_b8,1899
20
+ deltafi/test_kit/enrich.py,sha256=M682cqInVS4aipI0jOrR25x7m0ErrpjGpebXGZDPwxo,2587
21
+ deltafi/test_kit/framework.py,sha256=m11V6JMZHn4oXnmB_pEgI5p7fZWCWAO7HjHLunSh5AA,14602
22
+ deltafi/test_kit/transform.py,sha256=q9zQdZswTKwU5Om8E9Lwg0G5JI8LEXuRZjT9oam-Zoo,4085
23
+ deltafi/test_kit/validate.py,sha256=NnXD5amOE_9a4Zr-v9DKYIv5f_HvwtmlI-IdFtVp7ko,1933
24
+ deltafi-2.0rc1713530263087.dist-info/METADATA,sha256=M8bj2tsQIgAkHIKkYzYQ3eAT6A51colJHptMVKPJnMo,1446
25
+ deltafi-2.0rc1713530263087.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
+ deltafi-2.0rc1713530263087.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.7.0
2
+ Generator: poetry-core 1.9.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,22 +0,0 @@
1
- deltafi/__init__.py,sha256=qv6y7PpBG0ekTN9EuD9Lj8JYOLVqZA6tvHwgjplncAM,709
2
- deltafi/action.py,sha256=cjnRsmBNCSCosFipIS_UXMHKC5MRITVSdc6MIeYUyL4,5305
3
- deltafi/actioneventqueue.py,sha256=mCRE1PFXy_KvaeezTeMze5N400CO1V06zEF8FFD6xZk,2847
4
- deltafi/actiontype.py,sha256=6OoZNEMcreD5f_QaGhTu8MEYLr3CalzcNu9XuFx40zg,865
5
- deltafi/domain.py,sha256=M97_n6KsMmIxCh3h622jNj1n6c5iMEAOn-SFH36oLis,12161
6
- deltafi/exception.py,sha256=qQ2TY2QPtMU9t5RH8jmFo_cX98AJ-zOFWP_Wfm5U6qQ,1149
7
- deltafi/genericmodel.py,sha256=8VXWgFMjIwG4o3rkcLOa3bQH0Nf-T1Kw621QtMw-4q0,1090
8
- deltafi/input.py,sha256=2qQcOWqz7lOXZIZipTz5XtcGESpjdnUAsZ7b9YqHw7M,1656
9
- deltafi/logger.py,sha256=q76R_Gn8BfcASH3Zy0V82m5ot34bjTnSELyKbjhvx3E,2136
10
- deltafi/metric.py,sha256=eIDjZQVO53KuAFoEtjLNFwqMrp_7BC0Td9GLD1tb6sE,967
11
- deltafi/plugin.py,sha256=LTkAII2ur6BFylxnp80slzximkDyKr5bvHEzczR9C4U,12956
12
- deltafi/result.py,sha256=2r3BKNQntlMpFXZtYOm7skoLzBocDRBwtRP3oRkGrfc,7330
13
- deltafi/storage.py,sha256=toq58EPZgzj2TfDF-YpFUmRnsWSjACA0KQAZzkM04xU,2740
14
- deltafi/test_kit/__init__.py,sha256=qv6y7PpBG0ekTN9EuD9Lj8JYOLVqZA6tvHwgjplncAM,709
15
- deltafi/test_kit/assertions.py,sha256=2eahqmbedhnHXMguiJHdaV-6sx4_jvCbxH4HSzx6PV8,1378
16
- deltafi/test_kit/compare_helpers.py,sha256=Oi07zsq7jlpAHxqjwHsea84acC3lAL3ooG0VC42lLAc,1581
17
- deltafi/test_kit/constants.py,sha256=epz0OS-qILcc8t7iIs5B3m2-wvZx8FpYpyb19ZsImbI,833
18
- deltafi/test_kit/framework.py,sha256=R07AH4zxWXaJ3QNufGrtiOvdZrZKEoo2FtZLGmgAnRk,13062
19
- deltafi/test_kit/transform.py,sha256=x2TNTV39TmJQbVImm-sOfUlS_qaWcDRi5mfK4ZE0ONE,2608
20
- deltafi-2.0rc1705080991758.dist-info/METADATA,sha256=jNjBLNkWpeCzQl1GiXamnwcmszP1DjtQB8Cm8XOGfE8,1496
21
- deltafi-2.0rc1705080991758.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
22
- deltafi-2.0rc1705080991758.dist-info/RECORD,,