Skip to content

Commit 730ba6c

Browse files
Add tests for parent level similarity and priority fields
1 parent 0ac2000 commit 730ba6c

17 files changed

Lines changed: 1442 additions & 452 deletions

cumulusci/core/tests/test_datasets_e2e.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ def write_yaml(filename: str, json: Any):
304304
"after": "Insert Account",
305305
}
306306
},
307+
"select_options": {},
307308
},
308309
"Insert Event": {
309310
"sf_object": "Event",
@@ -316,16 +317,19 @@ def write_yaml(filename: str, json: Any):
316317
"after": "Insert Lead",
317318
}
318319
},
320+
"select_options": {},
319321
},
320322
"Insert Account": {
321323
"sf_object": "Account",
322324
"table": "Account",
323325
"fields": ["Name"],
326+
"select_options": {},
324327
},
325328
"Insert Lead": {
326329
"sf_object": "Lead",
327330
"table": "Lead",
328331
"fields": ["Company", "LastName"],
332+
"select_options": {},
329333
},
330334
}
331335
assert tuple(actual.items()) == tuple(expected.items()), actual.items()

cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_load_mapping_from_declarations.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def test_simple_generate_mapping_from_declarations(self, org_config):
4141
"sf_object": "Account",
4242
"table": "Account",
4343
"fields": ["Name", "Description"],
44+
"select_options": {},
4445
}
4546
}
4647

@@ -74,11 +75,13 @@ def test_generate_mapping_from_both_kinds_of_declarations(self, org_config):
7475
"sf_object": "Contact",
7576
"table": "Contact",
7677
"fields": ["FirstName", "LastName"],
78+
"select_options": {},
7779
},
7880
"Insert Account": {
7981
"sf_object": "Account",
8082
"table": "Account",
8183
"fields": ["Name", "Description"],
84+
"select_options": {},
8285
},
8386
}.items()
8487
)
@@ -111,6 +114,7 @@ def test_generate_load_mapping_from_declarations__lookups(self, org_config):
111114
"sf_object": "Account",
112115
"table": "Account",
113116
"fields": ["Name", "Description"],
117+
"select_options": {},
114118
},
115119
"Insert Contact": {
116120
"sf_object": "Contact",
@@ -119,6 +123,7 @@ def test_generate_load_mapping_from_declarations__lookups(self, org_config):
119123
"lookups": {
120124
"AccountId": {"table": ["Account"], "key_field": "AccountId"}
121125
},
126+
"select_options": {},
122127
},
123128
}
124129

@@ -157,6 +162,7 @@ def test_generate_load_mapping_from_declarations__polymorphic_lookups(
157162
"sf_object": "Account",
158163
"table": "Account",
159164
"fields": ["Name", "Description"],
165+
"select_options": {},
160166
},
161167
"Insert Contact": {
162168
"sf_object": "Contact",
@@ -165,11 +171,13 @@ def test_generate_load_mapping_from_declarations__polymorphic_lookups(
165171
"lookups": {
166172
"AccountId": {"table": ["Account"], "key_field": "AccountId"}
167173
},
174+
"select_options": {},
168175
},
169176
"Insert Lead": {
170177
"sf_object": "Lead",
171178
"table": "Lead",
172179
"fields": ["LastName", "Company"],
180+
"select_options": {},
173181
},
174182
"Insert Event": {
175183
"sf_object": "Event",
@@ -178,6 +186,7 @@ def test_generate_load_mapping_from_declarations__polymorphic_lookups(
178186
"lookups": {
179187
"WhoId": {"table": ["Contact", "Lead"], "key_field": "WhoId"}
180188
},
189+
"select_options": {},
181190
},
182191
}
183192

@@ -221,6 +230,7 @@ def test_generate_load_mapping_from_declarations__circular_lookups(
221230
},
222231
"sf_object": "Account",
223232
"table": "Account",
233+
"select_options": {},
224234
},
225235
"Insert Contact": {
226236
"sf_object": "Contact",
@@ -229,6 +239,7 @@ def test_generate_load_mapping_from_declarations__circular_lookups(
229239
"lookups": {
230240
"AccountId": {"table": ["Account"], "key_field": "AccountId"}
231241
},
242+
"select_options": {},
232243
},
233244
}, mf
234245

@@ -252,11 +263,13 @@ def test_generate_load_mapping__with_load_declarations(self, org_config):
252263
"sf_object": "Account",
253264
"api": DataApi.REST,
254265
"table": "Account",
266+
"select_options": {},
255267
},
256268
"Insert Contact": {
257269
"sf_object": "Contact",
258270
"api": DataApi.BULK,
259271
"table": "Contact",
272+
"select_options": {},
260273
},
261274
}, mf
262275

@@ -288,24 +301,28 @@ def test_generate_load_mapping__with_upserts(self, org_config):
288301
"Insert Account": {
289302
"sf_object": "Account",
290303
"table": "Account",
304+
"select_options": {},
291305
},
292306
"Upsert Account Name": {
293307
"sf_object": "Account",
294308
"table": "Account",
295309
"action": DataOperationType.UPSERT,
296310
"update_key": ("Name",),
297311
"fields": ["Name"],
312+
"select_options": {},
298313
},
299314
"Etl_Upsert Account AccountNumber_Name": {
300315
"sf_object": "Account",
301316
"table": "Account",
302317
"action": DataOperationType.ETL_UPSERT,
303318
"update_key": ("AccountNumber", "Name"),
304319
"fields": ["AccountNumber", "Name"],
320+
"select_options": {},
305321
},
306322
"Insert Contact": {
307323
"sf_object": "Contact",
308324
"table": "Contact",
325+
"select_options": {},
309326
},
310327
}, mf
311328

cumulusci/tasks/bulkdata/load.py

Lines changed: 85 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,90 @@ def _execute_step(
310310

311311
return step.job_result
312312

313+
def process_lookup_fields(self, mapping, fields, polymorphic_fields):
314+
"""Modify fields and priority fields based on lookup and polymorphic checks."""
315+
for name, lookup in mapping.lookups.items():
316+
if name in fields:
317+
# Get the index of the lookup field before removing it
318+
insert_index = fields.index(name)
319+
# Remove the lookup field from fields
320+
fields.remove(name)
321+
322+
# Do the same for priority fields
323+
lookup_in_priority_fields = False
324+
if name in mapping.select_options.priority_fields:
325+
# Set flag to True
326+
lookup_in_priority_fields = True
327+
# Remove the lookup field from priority fields
328+
del mapping.select_options.priority_fields[name]
329+
330+
# Check if this lookup field is polymorphic
331+
if (
332+
name in polymorphic_fields
333+
and len(polymorphic_fields[name]["referenceTo"]) > 1
334+
):
335+
# Convert to list if string
336+
if not isinstance(lookup.table, list):
337+
lookup.table = [lookup.table]
338+
# Polymorphic field handling
339+
polymorphic_references = lookup.table
340+
relationship_name = polymorphic_fields[name]["relationshipName"]
341+
342+
# Loop through each polymorphic type (e.g., Contact, Lead)
343+
for ref_type in polymorphic_references:
344+
# Find the mapping step for this polymorphic type
345+
lookup_mapping_step = next(
346+
(
347+
step
348+
for step in self.mapping.values()
349+
if step.table == ref_type
350+
),
351+
None,
352+
)
353+
if lookup_mapping_step:
354+
lookup_fields = lookup_mapping_step.get_load_field_list()
355+
# Insert fields in the format {relationship_name}.{ref_type}.{lookup_field}
356+
for field in lookup_fields:
357+
fields.insert(
358+
insert_index,
359+
f"{relationship_name}.{lookup_mapping_step.sf_object}.{field}",
360+
)
361+
insert_index += 1
362+
if lookup_in_priority_fields:
363+
mapping.select_options.priority_fields[
364+
f"{relationship_name}.{lookup_mapping_step.sf_object}.{field}"
365+
] = f"{relationship_name}.{lookup_mapping_step.sf_object}.{field}"
366+
367+
else:
368+
# Non-polymorphic field handling
369+
lookup_table = lookup.table
370+
371+
if isinstance(lookup_table, list):
372+
lookup_table = lookup_table[0]
373+
374+
# Get the mapping step for the non-polymorphic reference
375+
lookup_mapping_step = next(
376+
(
377+
step
378+
for step in self.mapping.values()
379+
if step.table == lookup_table
380+
),
381+
None,
382+
)
383+
384+
if lookup_mapping_step:
385+
relationship_name = polymorphic_fields[name]["relationshipName"]
386+
lookup_fields = lookup_mapping_step.get_load_field_list()
387+
388+
# Insert the new fields at the same position as the removed lookup field
389+
for field in lookup_fields:
390+
fields.insert(insert_index, f"{relationship_name}.{field}")
391+
insert_index += 1
392+
if lookup_in_priority_fields:
393+
mapping.select_options.priority_fields[
394+
f"{relationship_name}.{field}"
395+
] = f"{relationship_name}.{field}"
396+
313397
def configure_step(self, mapping):
314398
"""Create a step appropriate to the action"""
315399
bulk_mode = mapping.bulk_mode or self.bulk_mode or "Parallel"
@@ -370,85 +454,7 @@ def configure_step(self, mapping):
370454
for field in describe_result["fields"]
371455
if field["type"] == "reference"
372456
}
373-
374-
# Loop through each lookup to get the corresponding fields
375-
for name, lookup in mapping.lookups.items():
376-
if name in fields:
377-
# Get the index of the lookup field before removing it
378-
insert_index = fields.index(name)
379-
# Remove the lookup field from fields
380-
fields.remove(name)
381-
382-
# Check if this lookup field is polymorphic
383-
if (
384-
name in polymorphic_fields
385-
and len(polymorphic_fields[name]["referenceTo"]) > 1
386-
):
387-
# Convert to list if string
388-
if not isinstance(lookup.table, list):
389-
lookup.table = [lookup.table]
390-
# Polymorphic field handling
391-
polymorphic_references = lookup.table
392-
relationship_name = polymorphic_fields[name][
393-
"relationshipName"
394-
]
395-
396-
# Loop through each polymorphic type (e.g., Contact, Lead)
397-
for ref_type in polymorphic_references:
398-
# Find the mapping step for this polymorphic type
399-
lookup_mapping_step = next(
400-
(
401-
step
402-
for step in self.mapping.values()
403-
if step.sf_object == ref_type
404-
),
405-
None,
406-
)
407-
408-
if lookup_mapping_step:
409-
lookup_fields = (
410-
lookup_mapping_step.get_load_field_list()
411-
)
412-
# Insert fields in the format {relationship_name}.{ref_type}.{lookup_field}
413-
for field in lookup_fields:
414-
fields.insert(
415-
insert_index,
416-
f"{relationship_name}.{lookup_mapping_step.sf_object}.{field}",
417-
)
418-
insert_index += 1
419-
420-
else:
421-
# Non-polymorphic field handling
422-
lookup_table = lookup.table
423-
424-
if isinstance(lookup_table, list):
425-
lookup_table = lookup_table[0]
426-
427-
# Get the mapping step for the non-polymorphic reference
428-
lookup_mapping_step = next(
429-
(
430-
step
431-
for step in self.mapping.values()
432-
if step.sf_object == lookup_table
433-
),
434-
None,
435-
)
436-
437-
if lookup_mapping_step:
438-
relationship_name = polymorphic_fields[name][
439-
"relationshipName"
440-
]
441-
lookup_fields = (
442-
lookup_mapping_step.get_load_field_list()
443-
)
444-
445-
# Insert the new fields at the same position as the removed lookup field
446-
for field in lookup_fields:
447-
fields.insert(
448-
insert_index, f"{relationship_name}.{field}"
449-
)
450-
insert_index += 1
451-
457+
self.process_lookup_fields(mapping, fields, polymorphic_fields)
452458
else:
453459
action = mapping.action
454460

@@ -503,9 +509,6 @@ def _stream_queried_data(self, mapping, local_ids, query):
503509
pkey = row[0]
504510
row = list(row[1:]) + statics
505511

506-
# Replace None values in row with empty strings
507-
row = [value if value is not None else "" for value in row]
508-
509512
if mapping.anchor_date and (date_context[0] or date_context[1]):
510513
row = adjust_relative_dates(
511514
mapping, date_context, row, DataOperationType.INSERT

cumulusci/tasks/bulkdata/mapping_parser.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,16 +124,19 @@ def split_update_key(cls, val):
124124
def validate_priority_fields(cls, values):
125125
select_options = values.get("select_options")
126126
fields_ = values.get("fields_", {})
127+
lookups = values.get("lookups", {})
127128

128129
if select_options and select_options.priority_fields:
129130
priority_field_names = set(select_options.priority_fields.keys())
130131
field_names = set(fields_.keys())
132+
lookup_names = set(lookups.keys())
131133

132134
# Check if all priority fields are present in the fields
133135
missing_fields = priority_field_names - field_names
136+
missing_fields = missing_fields - lookup_names
134137
if missing_fields:
135138
raise ValueError(
136-
f"Priority fields {missing_fields} are not present in 'fields'"
139+
f"Priority fields {missing_fields} are not present in 'fields' or 'lookups'"
137140
)
138141

139142
return values

0 commit comments

Comments
 (0)