From 6af6176776f603e12d9c0f2573199d49aac64a2f Mon Sep 17 00:00:00 2001 From: ashitsalesforce Date: Sat, 3 Feb 2024 10:15:45 -0800 Subject: [PATCH] polymorphic relationship mapping thru idlookup fields UI support for mapping of a polymorphic relationship through parents' idlookup fields if the possible parent sobject types are 5 or less. --- .../dataloader/client/PartnerClient.java | 15 +-- .../dataloader/dyna/SforceDynaBean.java | 27 ++-- .../ui/ForeignKeyExternalIdPage.java | 122 +++++++++++++----- 3 files changed, 110 insertions(+), 54 deletions(-) diff --git a/src/main/java/com/salesforce/dataloader/client/PartnerClient.java b/src/main/java/com/salesforce/dataloader/client/PartnerClient.java index 9028b54f..087d5fdc 100644 --- a/src/main/java/com/salesforce/dataloader/client/PartnerClient.java +++ b/src/main/java/com/salesforce/dataloader/client/PartnerClient.java @@ -793,28 +793,27 @@ public void setFieldReferenceDescribes() throws ConnectionException { continue; } - boolean haSingleParentObject = parentObjectNames.length == 1; if (parentObjectNames.length >= DescribeRefObject.MAX_PARENT_OBJECTS_IN_REFERENCING_FIELD) { childObjectField.setLabel(childObjectField.getLabel() + " (Id)"); } else { - processParentObjectArrayForLookupReferences(parentObjectNames, childObjectField, haSingleParentObject); + processParentObjectArrayForLookupReferences(parentObjectNames, childObjectField); } } } } - private void processParentObjectArrayForLookupReferences(String[] parentObjectNames, Field childObjectField, boolean haSingleParentObject) throws ConnectionException { + private void processParentObjectArrayForLookupReferences(String[] parentObjectNames, Field childObjectField) throws ConnectionException { for (int parentObjectIndex = 0; parentObjectIndex < parentObjectNames.length; parentObjectIndex++ ) { String parentObjectName = parentObjectNames[parentObjectIndex]; - processParentObjectForLookupReferences(parentObjectName, childObjectField, haSingleParentObject, parentObjectIndex, parentObjectNames.length); + processParentObjectForLookupReferences(parentObjectName, childObjectField, parentObjectIndex, parentObjectNames.length); } } - private void processParentObjectForLookupReferences(String parentObjectName, Field childObjectField, boolean haSingleParentObject, int parentObjectIndex, int totalParentObjects) throws ConnectionException { + private void processParentObjectForLookupReferences(String parentObjectName, Field childObjectField, int parentObjectIndex, int numParentTypes) throws ConnectionException { Field[] parentObjectFields = describeSObject(parentObjectName).getFields(); Map parentIdLookupFieldMap = new HashMap(); for (Field parentField : parentObjectFields) { - processParentFieldForLookupReference(parentField, childObjectField, haSingleParentObject, parentObjectIndex, totalParentObjects, parentIdLookupFieldMap); + processParentFieldForLookupReference(parentField, childObjectField, numParentTypes, parentObjectIndex, numParentTypes, parentIdLookupFieldMap); } if (!parentIdLookupFieldMap.isEmpty()) { DescribeRefObject describeRelationship = new DescribeRefObject(parentObjectName, childObjectField, parentIdLookupFieldMap); @@ -822,13 +821,13 @@ private void processParentObjectForLookupReferences(String parentObjectName, Fie } } - private void processParentFieldForLookupReference(Field parentField, Field childObjectField, boolean haSingleParentObject, int parentObjectIndex, int totalParentObjects, Map parentIdLookupFieldMap) { + private void processParentFieldForLookupReference(Field parentField, Field childObjectField, int numParentTypes, int parentObjectIndex, int totalParentObjects, Map parentIdLookupFieldMap) { if (!parentField.isIdLookup()) { return; } if (parentField.getType() == FieldType.id) { updateChildFieldLabelWithParentIdLabels(parentField, childObjectField, parentObjectIndex, totalParentObjects); - } else if (haSingleParentObject) { + } else if (numParentTypes <= DescribeRefObject.MAX_PARENT_OBJECTS_IN_REFERENCING_FIELD) { parentIdLookupFieldMap.put(parentField.getName(), parentField); } } diff --git a/src/main/java/com/salesforce/dataloader/dyna/SforceDynaBean.java b/src/main/java/com/salesforce/dataloader/dyna/SforceDynaBean.java index e2e47798..88042846 100644 --- a/src/main/java/com/salesforce/dataloader/dyna/SforceDynaBean.java +++ b/src/main/java/com/salesforce/dataloader/dyna/SforceDynaBean.java @@ -85,21 +85,24 @@ static public DynaProperty[] createDynaProps(DescribeSObjectResult describer, Co // NOTE: currently only fields with one reference are supported on the server FieldType fieldType = field.getType(); String relationshipName = field.getRelationshipName(); - if (fieldType == FieldType.reference && field.getReferenceTo().length == 1 && + if (fieldType == FieldType.reference && field.getReferenceTo().length <= DescribeRefObject.MAX_PARENT_OBJECTS_IN_REFERENCING_FIELD && relationshipName != null && relationshipName.length() > 0) { - DescribeRefObject parent = controller.getReferenceDescribes().getParentSObject(relationshipName); - if(parent != null) { - for(String refFieldName : parent.getParentObjectFieldMap().keySet()) { - // property name contains information for mapping - // add old format to dyna props - dynaProps.add(new DynaProperty( - RelationshipField.formatAsString(relationshipName, refFieldName), + for (String parentName : field.getReferenceTo()) { + RelationshipField relField = new RelationshipField(parentName, relationshipName); + DescribeRefObject parent = controller.getReferenceDescribes().getParentSObject(relField.toFormattedRelationshipString()); + if(parent != null) { + for(String refFieldName : parent.getParentObjectFieldMap().keySet()) { + // property name contains information for mapping + // add old format to dyna props + dynaProps.add(new DynaProperty( + RelationshipField.formatAsString(relationshipName, refFieldName), + SObjectReference.class)); + // add new format to dyna props + dynaProps.add(new DynaProperty( + RelationshipField.formatAsString(parent.getParentObjectName(), relationshipName, refFieldName), SObjectReference.class)); - // add new format to dyna props - dynaProps.add(new DynaProperty( - RelationshipField.formatAsString(parent.getParentObjectName(), relationshipName, refFieldName), - SObjectReference.class)); + } } } } diff --git a/src/main/java/com/salesforce/dataloader/ui/ForeignKeyExternalIdPage.java b/src/main/java/com/salesforce/dataloader/ui/ForeignKeyExternalIdPage.java index 39bc29dd..5432e450 100644 --- a/src/main/java/com/salesforce/dataloader/ui/ForeignKeyExternalIdPage.java +++ b/src/main/java/com/salesforce/dataloader/ui/ForeignKeyExternalIdPage.java @@ -31,6 +31,8 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; @@ -52,10 +54,11 @@ public class ForeignKeyExternalIdPage extends LoadPage { private final Map extIdSelections = new HashMap(); + private final Map parentSelections = new HashMap(); private Composite containerComp; private ScrolledComposite scrollComp; private ReferenceEntitiesDescribeMap referenceObjects; - private int numChildFieldsWithNonIdLookupFieldSelections = 0; + private boolean hasParentEntitiesWithIdLookupField = false; public ForeignKeyExternalIdPage(Controller controller) { @@ -85,7 +88,7 @@ private void createFkExtIdUi() { Composite comp = new Composite(scrollComp, SWT.NONE); scrollComp.setContent(comp); - GridLayout gridLayout = new GridLayout(2, false); + GridLayout gridLayout = new GridLayout(3, false); gridLayout.horizontalSpacing = 10; gridLayout.marginHeight = 20; gridLayout.verticalSpacing = 7; @@ -99,9 +102,12 @@ private void createFkExtIdUi() { scrollBar.setPageIncrement(20 * 5); extIdSelections.clear(); - this.numChildFieldsWithNonIdLookupFieldSelections = 0; + parentSelections.clear(); + this.hasParentEntitiesWithIdLookupField = false; if(referenceObjects != null) { - for(String relationshipName : referenceObjects.keySet()) { + List sortedRelationshipList = new ArrayList<>(referenceObjects.keySet()); + Collections.sort(sortedRelationshipList); + for(String relationshipName : sortedRelationshipList) { OperationInfo operation = controller.getConfig().getOperationInfo(); Field childField = referenceObjects.getParentSObject(relationshipName).getChildField(); boolean isCreateableOrUpdateable = true; @@ -123,7 +129,7 @@ private void createFkExtIdUi() { } if (isCreateableOrUpdateable) { createObjectExtIdUi(comp, relationshipName); - numChildFieldsWithNonIdLookupFieldSelections++; + hasParentEntitiesWithIdLookupField = true; } } } @@ -137,18 +143,53 @@ private void createFkExtIdUi() { * @param relationshipName */ private void createObjectExtIdUi(Composite comp, String relationshipName) { - Label labelExtId = new Label(comp, SWT.RIGHT); - DescribeRefObject extIdInfo = referenceObjects.getParentSObject(relationshipName); - labelExtId.setText(relationshipName); - labelExtId.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END)); + RelationshipField relField = new RelationshipField(relationshipName, false); - // Add the ext id dropdown - Combo extIdCombo = new Combo(comp, SWT.DROP_DOWN | SWT.READ_ONLY); + // Add parent dropdown + if (relField.getParentObjectName() == null) { + // shouldn't happen + return; + } + Combo parentCombo = this.parentSelections.get(relField.getRelationshipName()); + if (parentCombo == null) { + // first parent object + Label labelExtId = new Label(comp, SWT.RIGHT); + labelExtId.setText(relField.getRelationshipName() + ":"); + labelExtId.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END)); + + parentCombo = new Combo(comp, SWT.DROP_DOWN | SWT.READ_ONLY); + GridData parentData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + parentData.widthHint = 150; + parentCombo.setLayoutData(parentData); + parentCombo.add(relField.getParentObjectName()); + parentCombo.select(0); + parentCombo.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent event) { + String parentName = ((Combo)event.widget).getText(); + Combo parentLookupIdFieldCombo = extIdSelections.get(relField.toFormattedRelationshipString()); + parentLookupIdFieldCombo.removeAll(); + RelationshipField selectedRelationship = new RelationshipField(parentName, relField.getRelationshipName()); + populateParentLookupFieldCombo(parentLookupIdFieldCombo, selectedRelationship); + } + }); + parentSelections.put(relField.getRelationshipName(), parentCombo); + // Add the ext id dropdown + Combo extIdCombo = new Combo(comp, SWT.DROP_DOWN | SWT.READ_ONLY); - // The width comes out based on number of pixels, not characters - GridData extIdData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); - extIdData.widthHint = 150; - extIdCombo.setLayoutData(extIdData); + // The width comes out based on number of pixels, not characters + GridData extIdData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + extIdData.widthHint = 150; + extIdCombo.setLayoutData(extIdData); + populateParentLookupFieldCombo(extIdCombo, relField); + extIdSelections.put(relField.toFormattedRelationshipString(), extIdCombo); + } else { + parentCombo.add(relField.getParentObjectName()); + } + } + + private void populateParentLookupFieldCombo(Combo extIdCombo, RelationshipField relField) { + DescribeRefObject extIdInfo = referenceObjects.getParentSObject(relField.toFormattedRelationshipString()); // get object's ext id info & set combo box to list of external id fields // set the objects reference information @@ -156,8 +197,6 @@ private void createObjectExtIdUi(Composite comp, String relationshipName) { // add default selection "not selected" to the list to allow users to go back to it fieldList.add(Labels.getString("ForeignKeyExternalIdPage.defaultComboText")); UIUtils.setComboItems(extIdCombo, fieldList, Labels.getString("ForeignKeyExternalIdPage.defaultComboText")); - - extIdSelections.put(relationshipName, extIdCombo); } /** @@ -184,21 +223,17 @@ private Map saveExtIdData() { Map relatedFields = new HashMap(); // foreign key references (if any set) - Map extIdReferences = new HashMap(); - for(String relationshipName : extIdSelections.keySet()) { - Combo combo = extIdSelections.get(relationshipName); - String extIdFieldName = combo.getText(); + for(String relationshipNameInCombo : extIdSelections.keySet()) { + Combo combo = extIdSelections.get(relationshipNameInCombo); + String lookupFieldInParent = combo.getText(); + RelationshipField relationshipField = getSelectedParentSObjectForLookupField(relationshipNameInCombo, lookupFieldInParent); // make sure that the item selection has occurred and that the default text is not displayed anymore - if(extIdFieldName != null && extIdFieldName.length() > 0 - && ! extIdFieldName.equals(Labels.getString("ForeignKeyExternalIdPage.defaultComboText"))) { - DescribeRefObject refObjectInfo = referenceObjects.getParentSObject(relationshipName); - extIdReferences.put(relationshipName, RelationshipField.formatAsString(refObjectInfo.getParentObjectName(), extIdFieldName)); + if(relationshipField != null) { + DescribeRefObject refDescribe = referenceObjects.getParentSObject(relationshipField.toFormattedRelationshipString()); Field relatedField = new Field(); - Field parentField = refObjectInfo.getParentObjectFieldMap().get(extIdFieldName); - Field childField = refObjectInfo.getChildField(); - RelationshipField relField = new RelationshipField(relationshipName, false); - relField.setParentFieldName(parentField.getName()); - relatedField.setName(relField.toString()); + Field parentField = refDescribe.getParentObjectFieldMap().get(lookupFieldInParent); + Field childField = refDescribe.getChildField(); + relatedField.setName(relationshipField.toString()); String childFieldLabel = childField.getLabel(); String[] childFieldLabelParts = childFieldLabel.split(" \\(.+\\)$"); relatedField.setLabel(childFieldLabelParts[0] + " (" + parentField.getLabel() + ")"); @@ -206,14 +241,33 @@ private Map saveExtIdData() { relatedField.setUpdateable(childField.isUpdateable()); relatedField.setType(FieldType.reference); String[] refToArray = new String[1]; - refToArray[0] = refObjectInfo.getParentObjectName(); + refToArray[0] = refDescribe.getParentObjectName(); relatedField.setReferenceTo(refToArray); - relatedFields.put(relationshipName,relatedField); + relatedFields.put(relationshipField.toString(),relatedField); } } return relatedFields; } + + private RelationshipField getSelectedParentSObjectForLookupField(String relationshipNameInCombo, String selectedIdLookupField) { + if(selectedIdLookupField != null && selectedIdLookupField.length() > 0 + && ! selectedIdLookupField.equals(Labels.getString("ForeignKeyExternalIdPage.defaultComboText"))) { + RelationshipField possibleParentSObjectField = new RelationshipField(relationshipNameInCombo, false); + Combo parentSObjectCombo = this.parentSelections.get(possibleParentSObjectField.getRelationshipName()); + if (parentSObjectCombo == null) { + return null; + } + String actualParentSObjectName = parentSObjectCombo.getText(); + String fullRelationshipName = RelationshipField.formatAsString( + actualParentSObjectName + , possibleParentSObjectField.getRelationshipName() + , selectedIdLookupField); + RelationshipField actualParentSObjectField = new RelationshipField(fullRelationshipName, true); + return actualParentSObjectField; + } + return null; + } /* * (non-Javadoc) @@ -241,7 +295,7 @@ public void setPageComplete() { @Override protected OperationPage getNextPageOverride(){ setupPage(); - if (numChildFieldsWithNonIdLookupFieldSelections == 0) { + if (!hasParentEntitiesWithIdLookupField) { // nothing to display, go to the next page return (OperationPage)getNextPage(); } @@ -250,7 +304,7 @@ protected OperationPage getNextPageOverride(){ @Override protected OperationPage getPreviousPageOverride(){ - if (numChildFieldsWithNonIdLookupFieldSelections == 0) { + if (!hasParentEntitiesWithIdLookupField) { // nothing to display, go to the previous page return (OperationPage)getPreviousPage(); }