deltafi 2.0rc1713535554033__py3-none-any.whl → 2.0rc1715691282958__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
@@ -20,8 +20,8 @@ from abc import ABC, abstractmethod
20
20
  from typing import Any, List
21
21
 
22
22
  from deltafi.actiontype import ActionType
23
- from deltafi.genericmodel import GenericModel
24
23
  from deltafi.domain import Context, DeltaFileMessage
24
+ from deltafi.genericmodel import GenericModel
25
25
  from deltafi.input import EgressInput, TransformInput
26
26
  from deltafi.result import *
27
27
  from pydantic import BaseModel
@@ -48,20 +48,23 @@ class Action(ABC):
48
48
  def execute_action(self, event):
49
49
  if event.delta_file_messages is None or not len(event.delta_file_messages):
50
50
  raise RuntimeError(f"Received event with no delta file messages for did {event.context.did}")
51
-
52
51
  if event.context.collect is not None:
53
- result = self.execute(event.context, self.collect([self.build_input(event.context, delta_file_message)
54
- for delta_file_message in event.delta_file_messages]),
55
- self.param_class().model_validate(event.params))
52
+ result = self.execute(
53
+ event.context,
54
+ self.collect([self.build_input(event.context, delta_file_message)
55
+ for delta_file_message in event.delta_file_messages]),
56
+ self.param_class().model_validate(event.params))
56
57
  else:
57
- result = self.execute(event.context, self.build_input(event.context, event.delta_file_messages[0]),
58
- self.param_class().model_validate(event.params))
58
+ result = self.execute(
59
+ event.context,
60
+ self.build_input(event.context, event.delta_file_messages[0]),
61
+ self.param_class().model_validate(event.params))
59
62
 
60
63
  self.validate_type(result)
61
64
  return result
62
65
 
63
66
  @staticmethod
64
- def param_class( ):
67
+ def param_class():
65
68
  """Factory method to create and return an empty GenericModel instance.
66
69
 
67
70
  Returns
@@ -110,7 +113,8 @@ class TimedIngressAction(Action, ABC):
110
113
 
111
114
  class TransformAction(Action, ABC):
112
115
  def __init__(self, description: str):
113
- super().__init__(ActionType.TRANSFORM, description, (TransformResult, TransformResults, ErrorResult, FilterResult))
116
+ super().__init__(ActionType.TRANSFORM, description,
117
+ (TransformResult, TransformResults, ErrorResult, FilterResult))
114
118
 
115
119
  def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
116
120
  return TransformInput(content=delta_file_message.content_list, metadata=delta_file_message.metadata)
deltafi/actiontype.py CHANGED
@@ -20,7 +20,9 @@ from enum import Enum
20
20
 
21
21
 
22
22
  class ActionType(Enum):
23
+ INGRESS = "INGRESS"
23
24
  TIMED_INGRESS = "TIMED_INGRESS"
24
25
  TRANSFORM = "TRANSFORM"
25
26
  EGRESS = "EGRESS"
27
+ PUBLISH = "PUBLISH"
26
28
  UNKNOWN = "UNKNOWN"
deltafi/domain.py CHANGED
@@ -40,13 +40,15 @@ class ActionExecution(NamedTuple):
40
40
 
41
41
  class Context(NamedTuple):
42
42
  did: str
43
- action_flow: str
43
+ delta_file_name: str
44
+ data_source: str
45
+ flow_name: str
46
+ flow_id: str
44
47
  action_name: str
45
- source_filename: str
46
- ingress_flow: str
47
- egress_flow: str
48
- system: str
48
+ action_id: str
49
+ action_version: str
49
50
  hostname: str
51
+ system_name: str
50
52
  content_service: ContentService
51
53
  collect: dict = None
52
54
  collected_dids: List[str] = None
@@ -56,19 +58,42 @@ class Context(NamedTuple):
56
58
  @classmethod
57
59
  def create(cls, context: dict, hostname: str, content_service: ContentService, logger: Logger):
58
60
  did = context['did']
59
- action_name_parts = context['name'].split(".")
60
- action_flow = action_name_parts[0]
61
- action_name = action_name_parts[1]
62
- if 'sourceFilename' in context:
63
- source_filename = context['sourceFilename']
61
+ if 'deltaFileName' in context:
62
+ delta_file_name = context['deltaFileName']
64
63
  else:
65
- source_filename = None
66
- ingress_flow = context['ingressFlow']
67
- if 'egressFlow' in context:
68
- egress_flow = context['egressFlow']
64
+ delta_file_name = None
65
+ if 'dataSource' in context:
66
+ data_source = context['dataSource']
69
67
  else:
70
- egress_flow = None
71
- system = context['systemName']
68
+ data_source = None
69
+ if 'flowName' in context:
70
+ flow_name = context['flowName']
71
+ else:
72
+ flow_name = None
73
+ if 'flowId' in context:
74
+ flow_id = context['flowId']
75
+ else:
76
+ flow_id = None
77
+ if 'actionName' in context:
78
+ action_name = context['actionName']
79
+ else:
80
+ action_name = None
81
+ if 'actionId' in context:
82
+ action_id = context['actionId']
83
+ else:
84
+ action_id = None
85
+ if 'actionVersion' in context:
86
+ action_version = context['actionVersion']
87
+ else:
88
+ action_version = None
89
+ if 'hostname' in context:
90
+ hostname = context['hostname']
91
+ else:
92
+ hostname = None
93
+ if 'systemName' in context:
94
+ system_name = context['systemName']
95
+ else:
96
+ system_name = None
72
97
  if 'collect' in context:
73
98
  collect = context['collect']
74
99
  else:
@@ -81,18 +106,21 @@ class Context(NamedTuple):
81
106
  memo = context['memo']
82
107
  else:
83
108
  memo = None
109
+
84
110
  return Context(did=did,
85
- action_flow=action_flow,
111
+ delta_file_name=delta_file_name,
112
+ data_source=data_source,
113
+ flow_name=flow_name,
114
+ flow_id=flow_id,
86
115
  action_name=action_name,
87
- source_filename=source_filename,
88
- ingress_flow=ingress_flow,
89
- egress_flow=egress_flow,
90
- system=system,
116
+ action_id=action_id,
117
+ action_version=action_version,
91
118
  hostname=hostname,
92
- content_service=content_service,
119
+ system_name=system_name,
93
120
  collect=collect,
94
121
  collected_dids=collected_dids,
95
122
  memo=memo,
123
+ content_service=content_service,
96
124
  logger=logger)
97
125
 
98
126
 
@@ -197,7 +225,6 @@ class Content:
197
225
 
198
226
  return new_segments
199
227
 
200
-
201
228
  def get_size(self):
202
229
  """
203
230
  Returns the size of the content in bytes.
@@ -318,7 +345,8 @@ class Event(NamedTuple):
318
345
 
319
346
  @classmethod
320
347
  def create(cls, event: dict, hostname: str, content_service: ContentService, logger: Logger):
321
- delta_file_messages = [DeltaFileMessage.from_dict(delta_file_message, content_service) for delta_file_message in event['deltaFileMessages']]
348
+ delta_file_messages = [DeltaFileMessage.from_dict(delta_file_message, content_service) for delta_file_message in
349
+ event['deltaFileMessages']]
322
350
  context = Context.create(event['actionContext'], hostname, content_service, logger)
323
351
  params = event['actionParams']
324
352
  queue_name = None
deltafi/plugin.py CHANGED
@@ -46,9 +46,9 @@ def _coordinates():
46
46
 
47
47
 
48
48
  def _setup_queue(max_connections):
49
- redis_url = os.getenv('REDIS_URL', 'http://deltafi-redis-master:6379')
50
- password = os.getenv('REDIS_PASSWORD')
51
- return ActionEventQueue(redis_url, max_connections, password)
49
+ url = os.getenv('VALKEY_URL', 'http://deltafi-valkey-master:6379')
50
+ password = os.getenv('VALKEY_PASSWORD')
51
+ return ActionEventQueue(url, max_connections, password)
52
52
 
53
53
 
54
54
  def _setup_content_service():
@@ -286,7 +286,10 @@ class Plugin(object):
286
286
 
287
287
  response = {
288
288
  'did': event.context.did,
289
- 'action': event.context.action_flow + "." + event.context.action_name,
289
+ 'flowName': event.context.flow_name,
290
+ 'flowId': event.context.flow_id,
291
+ 'actionName': event.context.action_name,
292
+ 'actionId': event.context.action_id,
290
293
  'start': start_time,
291
294
  'stop': time.time(),
292
295
  'type': result.result_type,
deltafi/result.py CHANGED
@@ -96,12 +96,12 @@ class FilterResult(Result):
96
96
 
97
97
 
98
98
  class IngressResultItem:
99
- def __init__(self, context: Context, filename: str):
99
+ def __init__(self, context: Context, delta_file_name: str):
100
100
  self.context = context
101
- self.filename = filename
102
101
  self._did = str(uuid.uuid4())
103
102
  self.content = []
104
103
  self.metadata = {}
104
+ self.delta_file_name = delta_file_name
105
105
 
106
106
  @property
107
107
  def did(self):
@@ -140,7 +140,7 @@ class IngressResultItem:
140
140
  def response(self):
141
141
  return {
142
142
  'did': self._did,
143
- 'filename': self.filename,
143
+ 'deltaFileName': self.delta_file_name,
144
144
  'metadata': self.metadata,
145
145
  'content': [content.json() for content in self.content]
146
146
  }
@@ -156,8 +156,8 @@ class IngressResult(Result):
156
156
  def __init__(self, context: Context):
157
157
  super().__init__('ingress', 'INGRESS', context)
158
158
  self.memo = None
159
- self.execute_immediate = False
160
159
  self.ingress_result_items = []
160
+ self.execute_immediate = False
161
161
  self.status = IngressStatusEnum.HEALTHY
162
162
  self.statusMessage = None
163
163
 
@@ -17,11 +17,12 @@
17
17
  #
18
18
 
19
19
  import json
20
+ import re
20
21
  from abc import ABC
21
22
  from abc import abstractmethod
22
- from typing import List
23
+ from itertools import repeat
23
24
 
24
- from deepdiff import DeepDiff
25
+ from deepdiff import DeepDiff, DeepSearch
25
26
 
26
27
  from .assertions import *
27
28
 
@@ -38,13 +39,255 @@ class GenericCompareHelper(CompareHelper):
38
39
 
39
40
 
40
41
  class JsonCompareHelper(CompareHelper):
41
- def __init__(self, regex_exclusion_list: List):
42
+ """Provides helper functions for comparing JSON/dict objects.
43
+
44
+ Are these two JSON/dict objects equivalent?
45
+ - compare(...)
46
+
47
+ Are these two lists equivalent?
48
+ - compare_lists(...)
49
+
50
+ Is this list a subset/superset of that list?
51
+ - compare_lists_subset(...)
52
+ - compare_lists_superset(...)
53
+
54
+ Select a list of values from an existing list and put them into a new list to facilitate list comparisons:
55
+ - create_list_from_list_using_filter_regex(...)
56
+
57
+ Is this value found (or not) in this JSON/dict object?
58
+ - is_found(...)
59
+ - is_not_found(...)
60
+ """
61
+
62
+ def __init__(self, regex_exclusion_list=None, ignore_order=True):
63
+ """Creates and configures a JsonCompareHelper object. If the optional 'ignore_order' is true, then the order of
64
+ data is ignored when checking else order is enforced. The optional 'regex_exclusion_list' excludes
65
+ paths within the object from comparison; if empty or not provided, then no excludes are applied."""
66
+ if regex_exclusion_list is None:
67
+ regex_exclusion_list = []
42
68
  self.excludes = regex_exclusion_list
69
+ self.ignore_order = ignore_order
70
+
71
+ def __perform_find(self, obj: object, item):
72
+ """Returns a dict of matches of the 'item' in the object 'obj'. Both keys and values of dicts are included in
73
+ the search. The item may be compiled regex pattern or a string that compiles to a regex pattern. The
74
+ returned dict is empty if there are no matches. Excludes path(s) determined by the constructor."""
75
+ return DeepSearch(obj, item, verbose_level=2, exclude_regex_paths=self.excludes, use_regexp=True)
76
+
77
+ def is_not_found(self, obj: object, item):
78
+ """Returns None if there are no occurrences of 'item' in object 'obj' else raises a ValueError. Both keys and
79
+ values of dicts are included in the search. If 'item' is a list, then all elements of item must not be
80
+ found in list, else a ValueError is raised. The argument 'item' may be a compiled regex pattern, a
81
+ string that compiles to a regex pattern, or a list of either or both. Excludes path(s) and failure on
82
+ ordering of elements are determined by the constructor."""
83
+
84
+ all_matches = []
85
+
86
+ if isinstance(item, list):
87
+ for value in item:
88
+ matches = self.__perform_find(obj, value)
89
+ if len(matches) > 0:
90
+ all_matches.append(matches)
91
+ else:
92
+ matches = self.__perform_find(obj, item)
93
+ if len(matches) > 0:
94
+ all_matches.append(matches)
95
+
96
+ if len(all_matches) > 0:
97
+ raise ValueError("Matches found for items '" + f"{all_matches}" + "'")
98
+
99
+ assert len(all_matches) == 0
100
+
101
+ def is_found(self, obj: object, item):
102
+ """Returns None if 'item' occurs in object 'obj' else raises a ValueError. Both keys and values of dicts are
103
+ included in the search. If 'item' is a list, then all elements of item must occur in the object else a
104
+ ValueError is returned. The argument 'item' may be a compiled regex pattern, a string that compiles to
105
+ a regex pattern, or a list of either or both. Excludes path(s) and failure on ordering of elements are
106
+ determined by the constructor."""
107
+
108
+ not_found_items = []
109
+
110
+ if isinstance(item, list):
111
+ for value in item:
112
+ matches = self.__perform_find(obj, value)
113
+ if len(matches) == 0:
114
+ not_found_items.append(value)
115
+ else:
116
+ matches = self.__perform_find(obj, item)
117
+ if len(matches) == 0:
118
+ not_found_items.append(item)
119
+
120
+ if len(not_found_items) > 0:
121
+ raise ValueError("No matches found for items '" + f"{not_found_items}" + "'")
122
+
123
+ assert len(not_found_items) == 0
124
+
125
+ def __perform_diff(self, expected, actual):
126
+ """Returns a dict with differences between 'expected' and 'actual'. The returned dict is empty if 'expected'
127
+ and 'actual' are equivalent. Both 'expected' and 'actual' must be dicts. Excludes path and failure on
128
+ ordering of elements are determined by the constructor. Elements must match number of repetitions."""
129
+ return DeepDiff(expected, actual, ignore_order=self.ignore_order, report_repetition=True,
130
+ exclude_regex_paths=self.excludes)
131
+
132
+ def __perform_diff_with_eval(self, expected, actual):
133
+ """Returns None if 'expected' and 'actual' are equivalent else returns a ValueError. Both 'expected' and
134
+ 'actual' must be dicts. Excludes path and failure on ordering of elements are determined by the
135
+ constructor. Elements must match number of repetitions."""
136
+
137
+ diffs = self.__perform_diff(expected, actual)
43
138
 
44
- def compare(self, expected: str, actual: str, label: str):
45
- exp = json.loads(expected)
46
- act = json.loads(actual)
47
- diffs = DeepDiff(exp, act, exclude_regex_paths=self.excludes)
48
139
  if len(diffs) > 0:
49
140
  raise ValueError(f"{diffs}")
141
+
50
142
  assert len(diffs) == 0
143
+
144
+ def compare(self, expected, actual, label: str):
145
+ """Returns None if 'expected' and 'actual' are equivalent else returns a ValueError. Both 'expected' and
146
+ 'actual' must be either dicts or strings that parse as JSON to dicts. Excludes path and failure on
147
+ ordering of elements are determined by the constructor. Elements must match number of repetitions."""
148
+
149
+ if isinstance(expected, str):
150
+ exp = json.loads(expected)
151
+ else:
152
+ exp = expected
153
+
154
+ if isinstance(actual, str):
155
+ act = json.loads(actual)
156
+ else:
157
+ act = actual
158
+
159
+ return self.__perform_diff_with_eval(exp, act)
160
+
161
+ def compare_lists(self, expected, actual: list, label: str):
162
+ """Returns None if 'actual' is equivalent to 'expected' else returns a ValueError.
163
+
164
+ The 'actual' argument must be a list. The 'expected' argument may be a list or a dict. If a list, then
165
+ 'expected' and 'actual' are compared against each other. If a dict, then 'actual' is equivalent if it
166
+ contains elements with the same repetitions as defined in the 'expected' dict with the key equal to the
167
+ element in 'actual' and the value equal to the number of repetitions.
168
+
169
+ Order of elements is ignored. Elements must match number of repetitions."""
170
+
171
+ expected_list = []
172
+
173
+ if isinstance(expected, dict):
174
+ for key, value in expected.items():
175
+ expected_list += list(repeat(key, value))
176
+ else:
177
+ expected_list = expected
178
+
179
+ return self.__perform_diff_with_eval({"json-compare-helper-internal-list": expected_list},
180
+ {"json-compare-helper-internal-list": actual})
181
+
182
+ @staticmethod
183
+ def compare_lists_subset(expected_subset, actual: list):
184
+ """Returns None if the 'actual' list contains at least 'expected_subset' else returns a ValueError. The
185
+ 'actual' list may contain 0 or more additional elements than defined in 'expected_subset'.
186
+
187
+ The 'actual' argument must be a list. The argument 'expected_subset' may be a list or a dict. In the
188
+ latter case, the key defines the item that must appear in 'actual' and the value defines the number of
189
+ repetitions.
190
+
191
+ Order of elements is ignored. Elements must match number of repetitions."""
192
+
193
+ expected_subset_map = {}
194
+
195
+ if isinstance(expected_subset, list):
196
+ for item in expected_subset:
197
+ value = expected_subset_map.get(item)
198
+ if value is None:
199
+ expected_subset_map[item] = 1
200
+ else:
201
+ expected_subset_map[item] = value + 1
202
+ else:
203
+ expected_subset_map = expected_subset
204
+
205
+ actual_map = {}
206
+
207
+ for item in actual:
208
+ value = actual_map.get(item)
209
+ if value is None:
210
+ actual_map[item] = 1
211
+ else:
212
+ actual_map[item] = value + 1
213
+
214
+ for key, value in expected_subset_map.items():
215
+ actual_value = actual_map.get(key)
216
+ if actual_value is None:
217
+ raise ValueError("Actual list did not contain element '" + str(key) + "'")
218
+ else:
219
+ if actual_value != value:
220
+ raise ValueError("Actual list had item '" + str(key) + "' with repetition " + str(actual_value)
221
+ + " but required repetition " + str(value))
222
+
223
+ return None
224
+
225
+ @staticmethod
226
+ def compare_lists_superset(expected_superset, actual: list):
227
+ """Returns None if the 'actual' list contains only elements that appear in 'expected_superset' else returns a
228
+ ValueError. The 'actual' list cannot contain more elements than are defined in 'expected_superset'; the
229
+ 'expected_superset' may define 0 or more values not contained in 'actual'.
230
+
231
+ The 'actual' argument must be a list. The argument 'expected_superset' may be a list or a dict. In the
232
+ latter case, the key defines the item that must appear in 'actual' and the value defines the number of
233
+ repetitions.
234
+
235
+ Order of elements is ignored. Elements must match number of repetitions."""
236
+
237
+ expected_superset_map = {}
238
+
239
+ if isinstance(expected_superset, list):
240
+ for item in expected_superset:
241
+ value = expected_superset_map.get(item)
242
+ if value is None:
243
+ expected_superset_map[item] = 1
244
+ else:
245
+ expected_superset_map[item] = value + 1
246
+ else:
247
+ expected_superset_map = expected_superset
248
+
249
+ actual_map = {}
250
+
251
+ for item in actual:
252
+ value = actual_map.get(item)
253
+ if value is None:
254
+ actual_map[item] = 1
255
+ else:
256
+ actual_map[item] = value + 1
257
+
258
+ for key, value in actual_map.items():
259
+ expected_value = expected_superset_map.get(key)
260
+ if expected_value is None:
261
+ raise ValueError("Actual list contained element '" + str(key)
262
+ + "' that did not appear in the expected superset")
263
+ else:
264
+ if expected_value != value:
265
+ raise ValueError("Actual list had item '" + str(key) + "' with repetition " + str(value)
266
+ + " but required repetition " + str(expected_value))
267
+
268
+ return None
269
+
270
+ @staticmethod
271
+ def create_list_from_list_using_filter_regex(regex, source_list: list):
272
+ """Creates and returns a list of those items in the 'source_list' matching the regex in the 'regex'. The
273
+ argument 'regex' may be a string or a compiled regex object.
274
+
275
+ For the 'regex', consider using anchors to explicitly match from the start (or end) of a string. For
276
+ example, a string such as "^malware--" will explicitly match from the start of a string."""
277
+
278
+ def search_funct(element):
279
+
280
+ if isinstance(regex, str):
281
+ # 'regex' is a string
282
+ match = re.search(regex, element)
283
+ else:
284
+ # assume 'regex' is a compiled regex pattern object
285
+ match = regex.match(element)
286
+
287
+ if match is None:
288
+ return False
289
+ else:
290
+ return True
291
+
292
+ return list(filter(search_funct, source_list))
293
+
@@ -188,21 +188,10 @@ class ActionTest(ABC):
188
188
 
189
189
  def __reset__(self, did: str):
190
190
  self.content_service = InternalContentService()
191
- <<<<<<< HEAD
192
191
  if did is None:
193
192
  self.did = str(uuid.uuid4())
194
193
  else:
195
194
  self.did = did
196
- self.expected_outputs = []
197
- ||||||| parent of 831733c7 (2.0 Refactor)
198
- self.did = str(uuid.uuid4())
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)
206
195
  self.loaded_inputs = []
207
196
  self.res_path = ""
208
197
 
@@ -237,36 +226,23 @@ class ActionTest(ABC):
237
226
  content_list = self.make_content_list(test_case)
238
227
  self.content_service.load(self.loaded_inputs)
239
228
 
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)
247
- return DeltaFileMessage(metadata=test_case.in_meta,
248
- content_list=content_list,
249
- domains=test_case.in_domains,
250
- enrichments=test_case.in_enrichments)
251
- =======
252
229
  return DeltaFileMessage(metadata=test_case.in_meta,
253
230
  content_list=content_list)
254
- >>>>>>> 831733c7 (2.0 Refactor)
255
231
 
256
232
  def make_context(self, test_case: TestCaseBase):
257
233
  action_name = INGRESS_FLOW + "." + test_case.action.__class__.__name__
258
234
  return Context(
259
235
  did=self.did,
260
- action_flow=INGRESS_FLOW,
236
+ delta_file_name=test_case.file_name,
237
+ data_source="DATASRC",
238
+ flow_name=INGRESS_FLOW,
239
+ flow_id="FLOWID",
261
240
  action_name=action_name,
262
- source_filename=test_case.file_name,
263
- ingress_flow=INGRESS_FLOW,
264
- egress_flow=EGRESS_FLOW,
265
- system=SYSTEM,
241
+ action_id="ACTIONID",
242
+ action_version="1.0",
266
243
  hostname=HOSTNAME,
244
+ system_name=SYSTEM,
267
245
  content_service=self.content_service,
268
- collect=None,
269
- collected_dids=None,
270
246
  logger=get_logger())
271
247
 
272
248
  def make_event(self, test_case: TestCaseBase):
@@ -312,39 +288,27 @@ class ActionTest(ABC):
312
288
  else:
313
289
  raise ValueError(f"unknown type: {test_case.expected_result_type}")
314
290
 
315
- def compare_content_details(self, expected: LoadedContent, actual: Content):
291
+ @staticmethod
292
+ def compare_content_details(expected: LoadedContent, actual: Content):
316
293
  assert_equal(expected.content_type, actual.media_type)
317
294
  assert_equal(expected.name, actual.name)
318
295
 
319
- def compare_one_content(self, comparitor: CompareHelper, expected: LoadedContent, actual, index):
296
+ def compare_one_content(self, comparator: CompareHelper, expected: LoadedContent, actual, index):
320
297
  self.compare_content_details(expected, actual)
321
298
  seg_id = actual.segments[0].uuid
322
- comparitor.compare(expected.data, self.content_service.get_output(seg_id), f"Content[{index}]")
299
+ comparator.compare(expected.data, self.content_service.get_output(seg_id), f"Content[{index}]")
323
300
 
324
- def compare_content_list(self, comparitor: CompareHelper, expected_outputs: List[IOContent], content: List):
301
+ def compare_content_list(self, comparator: CompareHelper, expected_outputs: List[IOContent], content: List):
325
302
  assert_equal_len(expected_outputs, content)
326
303
  for index, expected_ioc in enumerate(expected_outputs):
327
304
  if len(expected_ioc.content_bytes) == 0:
328
- expected = LoadedContent(self.did, expected_ioc, self.load_file(output_ioc))
305
+ expected = LoadedContent(self.did, expected_ioc, self.load_file(expected_ioc))
329
306
  else:
330
307
  expected = LoadedContent(self.did, expected_ioc, None)
331
- self.compare_one_content(comparitor, expected, content[index], index)
332
-
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)
337
-
338
- expected_value = expected['value']
339
- if type(expected_value) == str:
340
- comparitor.compare(expected_value, actual['value'], f"Domain[{index}]")
341
- elif type(expected_value) == IOContent:
342
- expected_data = self.load_file(expected_value)
343
- comparitor.compare(expected_data, actual['value'], f"Domain[{index}]")
344
- else:
345
- raise ValueError(f"unknown expected_value type: {type(expected_value)}")
308
+ self.compare_one_content(comparator, expected, content[index], index)
346
309
 
347
- def compare_one_metric(self, expected: Metric, result: Metric):
310
+ @staticmethod
311
+ def compare_one_metric(expected: Metric, result: Metric):
348
312
  assert expected.name == result.name
349
313
  assert_equal_with_label(expected.value, result.value, expected.name)
350
314
  assert_keys_and_values(expected.tags, result.tags)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deltafi
3
- Version: 2.0rc1713535554033
3
+ Version: 2.0rc1715691282958
4
4
  Summary: SDK for DeltaFi plugins and actions
5
5
  License: Apache License, Version 2.0
6
6
  Keywords: deltafi
@@ -20,11 +20,11 @@ Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Topic :: Software Development
21
21
  Requires-Dist: deepdiff (>=6.7.1)
22
22
  Requires-Dist: json-logging (>=1.3.0)
23
- Requires-Dist: minio (>=7.2.3)
24
- Requires-Dist: pydantic (>=2.5.3)
25
- Requires-Dist: redis (>=5.0.1)
23
+ Requires-Dist: minio (>=7.2.5)
24
+ Requires-Dist: pydantic (>=2.7.1)
25
+ Requires-Dist: redis (>=5.0.4)
26
26
  Requires-Dist: requests (>=2.31.0)
27
- Requires-Dist: urllib3 (>=2.1.0)
27
+ Requires-Dist: urllib3 (>=2.2.1)
28
28
  Project-URL: Bug Reports, https://chat.deltafi.org/deltafi/channels/bug-reports
29
29
  Project-URL: Documentation, https://docs.deltafi.org/#/
30
30
  Project-URL: Source Code, https://gitlab.com/deltafi/deltafi
@@ -1,26 +1,26 @@
1
1
  deltafi/__init__.py,sha256=qv6y7PpBG0ekTN9EuD9Lj8JYOLVqZA6tvHwgjplncAM,709
2
- deltafi/action.py,sha256=xxDQD-gye6gW9Q13Ylm51qiVDgqD36WMK191Gi7FEis,5323
2
+ deltafi/action.py,sha256=DGtbzrOM5BJRfn0NGAm19rLgzQyt6iiXy2_VpxHSyaw,5343
3
3
  deltafi/actioneventqueue.py,sha256=mCRE1PFXy_KvaeezTeMze5N400CO1V06zEF8FFD6xZk,2847
4
- deltafi/actiontype.py,sha256=6OoZNEMcreD5f_QaGhTu8MEYLr3CalzcNu9XuFx40zg,865
5
- deltafi/domain.py,sha256=weZII1PfUOoCUpk3LfS8EXhX3ZQ73fFuoSwKgnyBX3Y,11301
4
+ deltafi/actiontype.py,sha256=V7JbTgHQIaWQ2N36pG-XV7kAXhq8qvj954z71iHw_wI,913
5
+ deltafi/domain.py,sha256=FK6XYN0xzQDIUxLDEYw4KqBZMtwrQcmuGjlEwnw6cLo,12112
6
6
  deltafi/exception.py,sha256=Z8LwxxTiTyuG3v7FAsjfz6cAqRAJFKK2q_2TbEDrIJE,943
7
7
  deltafi/genericmodel.py,sha256=8VXWgFMjIwG4o3rkcLOa3bQH0Nf-T1Kw621QtMw-4q0,1090
8
8
  deltafi/input.py,sha256=2qQcOWqz7lOXZIZipTz5XtcGESpjdnUAsZ7b9YqHw7M,1656
9
9
  deltafi/logger.py,sha256=q76R_Gn8BfcASH3Zy0V82m5ot34bjTnSELyKbjhvx3E,2136
10
10
  deltafi/metric.py,sha256=eIDjZQVO53KuAFoEtjLNFwqMrp_7BC0Td9GLD1tb6sE,967
11
- deltafi/plugin.py,sha256=5mx7Og6aREwjPDY4BknCnDsuWDg7mz8On6J0M8mQ16I,12294
12
- deltafi/result.py,sha256=rugAkh9NZdl0QDHzyxDoZbkE8dnJNAFLbqBNdIjjn_k,8177
11
+ deltafi/plugin.py,sha256=0T_YY8DvEXAN9xjrmVBfh4-p4kbSwlnqRKy2Mii8zU4,12422
12
+ deltafi/result.py,sha256=wHWbQSk5sQId3CtareUgkbvFthuX0aiM_atsdV59xFA,8210
13
13
  deltafi/storage.py,sha256=toq58EPZgzj2TfDF-YpFUmRnsWSjACA0KQAZzkM04xU,2740
14
14
  deltafi/test_kit/__init__.py,sha256=qv6y7PpBG0ekTN9EuD9Lj8JYOLVqZA6tvHwgjplncAM,709
15
15
  deltafi/test_kit/assertions.py,sha256=2eahqmbedhnHXMguiJHdaV-6sx4_jvCbxH4HSzx6PV8,1378
16
- deltafi/test_kit/compare_helpers.py,sha256=Oi07zsq7jlpAHxqjwHsea84acC3lAL3ooG0VC42lLAc,1581
16
+ deltafi/test_kit/compare_helpers.py,sha256=Q0e14q4XQ4rlMUbPt3aMHThAXHFRXlue3-M4oAayyJE,12957
17
17
  deltafi/test_kit/constants.py,sha256=epz0OS-qILcc8t7iIs5B3m2-wvZx8FpYpyb19ZsImbI,833
18
18
  deltafi/test_kit/domain.py,sha256=uqpDravlb6E3Ddm4QpRaJTdiVuQ2MMdZnzc4Oi9EZJA,2090
19
19
  deltafi/test_kit/egress.py,sha256=nF7YYak-_X35m7h2rSIcju86IJBf8cVaFHs7xw7-_b8,1899
20
20
  deltafi/test_kit/enrich.py,sha256=M682cqInVS4aipI0jOrR25x7m0ErrpjGpebXGZDPwxo,2587
21
- deltafi/test_kit/framework.py,sha256=m11V6JMZHn4oXnmB_pEgI5p7fZWCWAO7HjHLunSh5AA,14602
21
+ deltafi/test_kit/framework.py,sha256=SGIw7nh86sUM91NNsk8AlYwC8Rm4lE9qAGbw2vscHKM,13033
22
22
  deltafi/test_kit/transform.py,sha256=q9zQdZswTKwU5Om8E9Lwg0G5JI8LEXuRZjT9oam-Zoo,4085
23
23
  deltafi/test_kit/validate.py,sha256=NnXD5amOE_9a4Zr-v9DKYIv5f_HvwtmlI-IdFtVp7ko,1933
24
- deltafi-2.0rc1713535554033.dist-info/METADATA,sha256=z65D0vtHncFxlrxn3AgJQ_Qy-fi72-JlATRVoKAxUYc,1446
25
- deltafi-2.0rc1713535554033.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
- deltafi-2.0rc1713535554033.dist-info/RECORD,,
24
+ deltafi-2.0rc1715691282958.dist-info/METADATA,sha256=DoXwFeyzapfqgVPal-muIS6YOzBWn2VkKVVJOodM9yc,1446
25
+ deltafi-2.0rc1715691282958.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
+ deltafi-2.0rc1715691282958.dist-info/RECORD,,