Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BDI/BGE: Update FLS to check only populated fields for DI & target objects #4155

Merged
merged 23 commits into from
Apr 12, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0bbec29
BDI only runs FLS on populated fields. BGE only runs FLS on core and …
psadie-sfdo Mar 29, 2019
74ea7de
Add flag when batch is from BGE
psadie-sfdo Mar 29, 2019
eefb9c7
Fix populated field collection
psadie-sfdo Mar 29, 2019
88e7121
Merge b421aed5dd243f6914ce150edbdaf4907e707eed into feature/prince-bg…
mrbelvedere Apr 2, 2019
c9e0ae3
Merge 30eeb7dea3a1540825684b07d6432c683097991b into feature/prince-bg…
mrbelvedere Apr 2, 2019
5e6c546
Merge b379f89fe2c39c7508e6a6495f5a951518cb5c32 into feature/prince-bg…
mrbelvedere Apr 2, 2019
280831c
Merge ca4a469c3f51e21f1277b61dff6995329aaa6e6d into feature/prince-bg…
mrbelvedere Apr 2, 2019
955dd72
Merge 7f23e7a75e80ed2355e5f836be45093d144d3400 into feature/prince-bg…
mrbelvedere Apr 2, 2019
e0c0021
Merge 54844544616722345853f612a005f0b18bcf0e21 into feature/prince-bg…
mrbelvedere Apr 2, 2019
271efbc
Fix BGE calls in BDI and add check for relationship fields in describ…
psadie-sfdo Apr 3, 2019
cd1348b
Merge branch 'feature/prince-bge-bdi-fls-bug-fix' of github.com:Sales…
psadie-sfdo Apr 3, 2019
80d28e7
Merge 91b0a8165e801568a5757b3c9d88957113b63a27 into feature/prince-bg…
mrbelvedere Apr 4, 2019
6faa986
Fix permission error on BGE config wizard
psadie-sfdo Apr 4, 2019
b9d0732
Remove fields user doesn't have permissions to access in Config Wizard
psadie-sfdo Apr 4, 2019
eef472b
moved relationship type check from UTIL_Describe to BDI_DataImportSer…
Apr 5, 2019
2b6048f
Fix reference in loop
psadie-sfdo Apr 5, 2019
ab143c8
Add required fields to relevant data import fields
psadie-sfdo Apr 6, 2019
019123b
Update FLS check, throw error only on DI fields where user has no acc…
psadie-sfdo Apr 8, 2019
35312f8
Merge 4f450d057003d224b34044c74679b7ec6dd1d521 into feature/prince-bg…
mrbelvedere Apr 8, 2019
66b6569
Remove redundant FLS call
psadie-sfdo Apr 8, 2019
b3e05db
Update populated list string case and update condition readability fo…
psadie-sfdo Apr 8, 2019
7d5d2a1
Merge a6a07568afca91c86bc09f914b04f09bde3cde19 into feature/prince-bg…
mrbelvedere Apr 10, 2019
15fd82e
Merge f796c33bde37d2378c63b4de804398502ef3c4ff into feature/prince-bg…
mrbelvedere Apr 10, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/classes/BDI_ContactService.cls
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ public with sharing class BDI_ContactService {
private Map<String, String> mapDIFieldToC1Field {
get {
if (mapDIFieldToC1Field == null) {
mapDIFieldToC1Field = BDI_DataImportService.mapFieldsForDIObject('Contact1', 'Contact', BDI_DataImportService.listStrDataImportFields);
mapDIFieldToC1Field = BDI_DataImportService.mapFieldsForDIObject(
'Contact1', 'Contact', BDI_DataImportService.relevantDataImportFields
);
}
return mapDIFieldToC1Field;
}
Expand All @@ -85,7 +87,9 @@ public with sharing class BDI_ContactService {
private Map<String, String> mapDIFieldToC2Field {
get {
if (mapDIFieldToC2Field == null) {
mapDIFieldToC2Field = BDI_DataImportService.mapFieldsForDIObject('Contact2', 'Contact', BDI_DataImportService.listStrDataImportFields);
mapDIFieldToC2Field = BDI_DataImportService.mapFieldsForDIObject(
'Contact2', 'Contact', BDI_DataImportService.relevantDataImportFields
);
}
return mapDIFieldToC2Field;
}
Expand Down
7 changes: 4 additions & 3 deletions src/classes/BDI_DataImportBatch_SEL.cls
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public with sharing class BDI_DataImportBatch_SEL {
return [SELECT Id,
Name,
Account_Custom_Unique_ID__c,
Active_Fields__c,
Batch_Description__c,
Batch_Process_Size__c,
Contact_Custom_Unique_ID__c,
Expand All @@ -53,13 +54,13 @@ public with sharing class BDI_DataImportBatch_SEL {
Donation_Matching_Implementing_Class__c,
Donation_Matching_Rule__c,
Donation_Date_Range__c,
Process_Using_Scheduled_Job__c,
GiftBatch__c,
Last_Processed_On__c,
Process_Using_Scheduled_Job__c,
Records_Failed__c,
Records_Successfully_Processed__c,
Post_Process_Implementing_Class__c,
Run_Opportunity_Rollups_while_Processing__c,
GiftBatch__c
Run_Opportunity_Rollups_while_Processing__c
FROM DataImportBatch__c
WHERE Id in :setBatchId];
}
Expand Down
53 changes: 47 additions & 6 deletions src/classes/BDI_DataImportService.cls
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,14 @@ global with sharing class BDI_DataImportService {
* @return DataImportBatch__c
*/
private static DataImportBatch__c loadBatch(Id batchId) {

List<DataImportBatch__c> dataImportBatches = BDI_DataImportBatch_SEL.selectByIds(new Set<Id>{ batchId });
if (dataImportBatches.size() == 1) {
return dataImportBatches[0];
DataImportBatch__c currentBatch = dataImportBatches[0];
if (!String.isBlank(currentBatch.Active_Fields__c)) {
relevantDataImportFields = BGE_BatchGiftEntry_UTIL.getDataImportFields(batchId, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the thing that made me the most uncomfortable about the original PR, but I am thinking we may be able to avoid it - applications calling BDI (like BGE) should ideally not be defining BDI behaviors, else we could build up all these conditionals and exceptions built in for each application that's calling into it. HOWEVER, I think we may be able to avoid this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ptewson-sfdo - Would moving this logic (along with other the field collection/map building) from the BGE util to the BDI_DataImportService class make sense? The one thing that immediately sticks out to me is that the getActiveFieldNamesFromBatch method from BGE_BatchGiftEntry_UTIL relies on the BGE_ConfigurationWizard_CTRL.BGEField class. If the above is acceptable, would moving BGEField into its own file make sense so we don't rely on the config wizard controller in BDI here as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We believe we can pull the call to BGE_BatchGiftEntry_UTIL.getDataImportFields(batchId, false) entirely - these fields would only be used for FLS when the help text maps to a target object field and then the check would be on the target field. None of these fields have default mappings or map logically to target fields and would not be checked anyway, and we can remove an unneeded dependency on BGE by pulling this out.

batchFromBGE = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to avoid references to "BGE" if we can describe this property more generically ... "batchDefinesActiveFieldList" or somethign

}
return currentBatch;
} else {
throw(new BDIException(String.format(Label.bdiInvalidBatchId, new List<String>{ batchId })));
}
Expand Down Expand Up @@ -489,12 +493,26 @@ global with sharing class BDI_DataImportService {
this.listDI = dataImports;
this.diSettings = dataImportSettings;

// list of populated data import fields
Set<String> populatedDataImportFieldsSet = new Set<String>();

// first set a clean state for each DI
for (DataImport__c dataImport : dataImports) {
dataImport.Status__c = null;
dataImport.ImportedDate__c = null;
dataImport.ApexJobId__c = null;
dataImport.FailureInformation__c = null;

// collect populated DI fields to use for later mapping
if (batchFromBGE != true) {
Map<String, Object> populatedFieldsMap = dataImport.getPopulatedFieldsAsMap();
List<String> populatedFields = new List<String>(populatedFieldsMap.keySet());
populatedDataImportFieldsSet.addAll(populatedFields);
}
}

if (batchFromBGE != true && populatedDataImportFieldsSet != null && populatedDataImportFieldsSet.size() > 0) {
relevantDataImportFields = new List<String>(populatedDataImportFieldsSet);
}

// do any performance optimizations to avoid unnecessary code
Expand Down Expand Up @@ -674,6 +692,28 @@ global with sharing class BDI_DataImportService {
}
}

/*******************************************************************************************************
* @description flag to let us know if we came from BGE
*/
global static Boolean batchFromBGE { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid global!


/*******************************************************************************************************
* @description list of the fields in the Data Import object that we care about for FLS checks because
* they are present in the Batch we are processing
* This list gets set in the loadBatch method.
* If no batch is loaded, we use the full list of all DI fields listStrDataImportFields
*/
global static List<String> relevantDataImportFields {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid global!

get {
if (relevantDataImportFields == null || relevantDataImportFields.isEmpty()) {
return listStrDataImportFields;
} else {
return relevantDataImportFields;
}
}
set;
}

/*******************************************************************************************************
* @description list of all the fields in the Data Import object
*/
Expand Down Expand Up @@ -940,7 +980,7 @@ global with sharing class BDI_DataImportService {
}

// get our mapping of household custom fields
Map<String, String> mapDIFieldToHHField = mapFieldsForDIObject('Household', 'Account', listStrDataImportFields);
Map<String, String> mapDIFieldToHHField = mapFieldsForDIObject('Household', 'Account', relevantDataImportFields);

// bail out if no custom fields
if (mapDIFieldToHHField.size() == 0) {
Expand Down Expand Up @@ -1491,7 +1531,7 @@ global with sharing class BDI_DataImportService {
get {
if (mapDIFieldToA1Field == null) {
mapDIFieldToA1Field = BDI_DataImportService.mapFieldsForDIObject(
'Account1', 'Account', BDI_DataImportService.listStrDataImportFields
'Account1', 'Account', BDI_DataImportService.relevantDataImportFields
);
}
return mapDIFieldToA1Field;
Expand All @@ -1507,7 +1547,7 @@ global with sharing class BDI_DataImportService {
if (mapDIFieldToA2Field == null) {
mapDIFieldToA2Field =
BDI_DataImportService.mapFieldsForDIObject(
'Account2', 'Account', BDI_DataImportService.listStrDataImportFields
'Account2', 'Account', BDI_DataImportService.relevantDataImportFields
);
}
return mapDIFieldToA2Field;
Expand Down Expand Up @@ -1563,7 +1603,8 @@ global with sharing class BDI_DataImportService {
}

Map<String, String> mapDIFieldToAddrField =
mapFieldsForDIObject('Address', UTIL_Namespace.StrTokenNSPrefix('Address__c'), listStrDataImportFields);
mapFieldsForDIObject('Address', UTIL_Namespace.StrTokenNSPrefix('Address__c'), relevantDataImportFields);

List<Address__c> addressesToInsert = new List<Address__c>();
List<DataImport__c> dataImportsToInsert = new List<DataImport__c>();
Map<String, Address__c> mapHHAddrKeyToAddrNew = new Map<String, Address__c>();
Expand Down
4 changes: 2 additions & 2 deletions src/classes/BDI_Donations.cls
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public class BDI_Donations {
if (dataImportFieldToOpportunityField == null) {
dataImportFieldToOpportunityField = BDI_DataImportService.mapFieldsForDIObject(
'Opportunity', 'Opportunity',
BDI_DataImportService.listStrDataImportFields
BDI_DataImportService.relevantDataImportFields
);
}
return dataImportFieldToOpportunityField;
Expand All @@ -127,7 +127,7 @@ public class BDI_Donations {
if (dataImportFieldToPaymentField == null) {
dataImportFieldToPaymentField = BDI_DataImportService.mapFieldsForDIObject(
'Payment', 'npe01__OppPayment__c',
BDI_DataImportService.listStrDataImportFields);
BDI_DataImportService.relevantDataImportFields);
}
return dataImportFieldToPaymentField;
}
Expand Down
4 changes: 2 additions & 2 deletions src/classes/BDI_MatchDonations.cls
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public with sharing class BDI_MatchDonations implements BDI_IMatchDonations {
get {
if (mapDIFieldToOppField == null) {
mapDIFieldToOppField = BDI_DataImportService.mapFieldsForDIObject('Opportunity', 'Opportunity',
BDI_DataImportService.listStrDataImportFields);
BDI_DataImportService.relevantDataImportFields);
}
return mapDIFieldToOppField;
}
Expand All @@ -64,7 +64,7 @@ public with sharing class BDI_MatchDonations implements BDI_IMatchDonations {
get {
if (mapDIFieldToPmtField == null) {
mapDIFieldToPmtField = BDI_DataImportService.mapFieldsForDIObject('Payment', 'npe01__OppPayment__c',
BDI_DataImportService.listStrDataImportFields);
BDI_DataImportService.relevantDataImportFields);
// special case Donation fields we defaultly map to Opp fields, but we will use with Payments too!
// note that these two work for matching rules.
mapDIFieldToPmtField.put(UTIL_Namespace.StrTokenNSPrefix('Donation_Amount__c').toLowercase(), 'npe01__Payment_Amount__c');
Expand Down
102 changes: 102 additions & 0 deletions src/classes/BGE_BatchGiftEntry_UTIL.cls
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,108 @@
*/
public with sharing class BGE_BatchGiftEntry_UTIL {

/*******************************************************************************************************
* @description returns a list of DataImport__c fields the Batch Gift Entry UI needs in SOQL
* @param batchId the batch for which to get all fields for soql
* @return List<String> list of DataImport__c field api names
*/
public static List<String> getDataImportFields(Id batchId) {
return getDataImportFields(batchId, true);
}

/*******************************************************************************************************
* @description returns a list of DataImport__c fields the Batch Gift Entry UI needs in SOQL
* @param batchId the batch for which to get all fields for soql
* @param includeRelationshipFields whether to include relationship fields in the returned list
* @return List<String> list of DataImport__c field api names
*/
public static List<String> getDataImportFields(Id batchId, Boolean includeRelationshipFields) {

List<String> fields = getCoreDataImportFields(includeRelationshipFields);
fields.addAll(getActiveFieldNamesFromBatch(batchId));

return fields;
}

/*******************************************************************************************************
* @description Returns the subset of DataImport__c fields that are part of every batch
* @param includeRelationshipFields whether to include relationship fields in the returned list
* @return list of DataImport__c field api names
*/
private static List<String> getCoreDataImportFields(Boolean includeRelationshipFields) {
List<String> fields = new List<String> {
'Id',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason that Donation_Amount__c, Donation_Date__c, and NPSP_Data_Import_Batch__c aren't included in this list?

Copy link
Contributor Author

@psadie-sfdo psadie-sfdo Apr 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, these get added in by the getActiveFieldNamesFromBatch method as these are always required and are also considered "selected" fields in the BGE.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nm let's leave this alone

'Account1Imported__c',
'Contact1Imported__c',
'Donation_Donor__c',
'DonationImported__c',
'DonationImportStatus__c',
'FailureInformation__c',
'NPSP_Data_Import_Batch__c',
'PaymentImported__c',
'PaymentImportStatus__c',
'Status__c'
};

if (includeRelationshipFields) {
fields.addAll(new List<String> {
'Account1Imported__r.Name',
'Contact1Imported__r.Name',
'DonationImported__r.Name',
'PaymentImported__r.Name'
});
}

// Maybe create new method in UTIL_Namespace for lists
List<String> namespacedFields = new List<String>();
for(String fieldName : fields) {
namespacedFields.add(UTIL_Namespace.StrAllNSPrefix(fieldName));
}

return namespacedFields;
}

/*******************************************************************************************************
* @description parses Active_Fields__c for the list of user-defined fields included in the batch config
* @param batchId: ID of the NPSP_Data_Import_Batch__c
* @return list of field api names
*/
public static List<String> getActiveFieldNamesFromBatch(Id batchId) {
DataImportBatch__c batch = [SELECT Active_Fields__c FROM DataImportBatch__c WHERE Id = :batchId];
String activeFieldsJSON = batch.Active_Fields__c;
List<String> activeFieldNames = new List<String>();

if (activeFieldsJSON != null) {
List<BGE_ConfigurationWizard_CTRL.BGEField> activeFields =
(List<BGE_ConfigurationWizard_CTRL.BGEField>)JSON.deserialize(
activeFieldsJSON,
List<BGE_ConfigurationWizard_CTRL.BGEField>.class
);
for (BGE_ConfigurationWizard_CTRL.BGEField field : activeFields) {
activeFieldNames.add(field.name);
if (field.type == 'reference') {
DescribeFieldResult lookupDFR = UTIL_Describe.getFieldDescribe(
UTIL_Namespace.StrTokenNSPrefix('DataImport__c'),
field.name.toLowerCase()
);
String referencedObjectName = lookupDFR.getReferenceTo()[0].getDescribe().name;
activeFieldNames.add(field.name.substringBefore('__c') + '__r.' + UTIL_Describe.getNameField(referencedObjectName));
}
}
}

return activeFieldNames;
}

/*******************************************************************************************************
* @description checks for read, create, and edit FLS for a given field
* @param dfr DescribeFieldResult of the field to check
* @return Boolean
*/
public static Boolean canUpdateField(DescribeFieldResult dfr) {
return dfr.isCreateable() && dfr.isUpdateable();
}

/*******************************************************************************************************
* @description Returns the relevant Batch fields for soql
* @return list of field API names
Expand Down
38 changes: 6 additions & 32 deletions src/classes/BGE_DataImportBatchEntry_CTRL.cls
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,10 @@ public with sharing class BGE_DataImportBatchEntry_CTRL {
String activeFieldsJSON = batch.Active_Fields__c;
if (activeFieldsJSON != null) {
List<BGE_ConfigurationWizard_CTRL.BGEField> activeFields =
(List<BGE_ConfigurationWizard_CTRL.BGEField>)JSON.deserialize(activeFieldsJSON,
List<BGE_ConfigurationWizard_CTRL.BGEField>.class);
(List<BGE_ConfigurationWizard_CTRL.BGEField>)JSON.deserialize(
activeFieldsJSON,
List<BGE_ConfigurationWizard_CTRL.BGEField>.class
);

Map<String, Schema.DescribeFieldResult> fieldMap = UTIL_Describe.getAllFieldsDescribe(
UTIL_Namespace.StrTokenNSPrefix('DataImport__c')
Expand Down Expand Up @@ -520,7 +522,7 @@ public with sharing class BGE_DataImportBatchEntry_CTRL {
* @return Query string
*/
public static String getDataImportQuery(Id batchId, List<Id> dataImportIds, Integer numRows, Integer offset) {
List<String> dataImportFields = getDataImportFields(batchId);
List<String> dataImportFields = BGE_BatchGiftEntry_UTIL.getDataImportFields(batchId);

String query = 'SELECT ' + String.join(dataImportFields,', ') +
' FROM DataImport__c' +
Expand Down Expand Up @@ -567,42 +569,14 @@ public with sharing class BGE_DataImportBatchEntry_CTRL {
};
}

/*******************************************************************************************************
* @description reads the Active_Fields__c field to get active configured fields for the batch
* @param batchId: ID of the NPSP_Data_Import_Batch__c
* @return list of field API names
*/
private static List<String> getActiveFieldNamesFromBatch(Id batchId) {

DataImportBatch__c batch = [SELECT Active_Fields__c FROM DataImportBatch__c WHERE Id = :batchId];
String activeFieldsJSON = batch.Active_Fields__c;
List<String> activeFieldNames = new List<String>();

if (activeFieldsJSON != null) {
List<BGE_ConfigurationWizard_CTRL.BGEField> activeFields =
(List<BGE_ConfigurationWizard_CTRL.BGEField>)
JSON.deserialize(activeFieldsJSON, List<BGE_ConfigurationWizard_CTRL.BGEField>.class);
for (BGE_ConfigurationWizard_CTRL.BGEField field : activeFields) {
activeFieldNames.add(field.name);
if (field.type == 'reference') {
DescribeFieldResult lookupDFR = UTIL_Describe.getFieldDescribe(UTIL_Namespace.StrTokenNSPrefix('DataImport__c'), field.name.toLowerCase());
String referencedObjectName = lookupDFR.getReferenceTo()[0].getDescribe().name;
activeFieldNames.add(field.name.substringBefore('__c') + '__r.' + UTIL_Describe.getNameField(referencedObjectName));
}
}
}

return activeFieldNames;
}

/*******************************************************************************************************
* @description returns a list of DataImport__c fields the Batch Gift Entry UI needs in SOQL
* @return List<String> list of DataImport__c field api names
*/
private static List<String> getDataImportFields(Id batchId) {

List<String> fields = getDataImportFields();
fields.addAll(getActiveFieldNamesFromBatch(batchId));
fields.addAll(BGE_BatchGiftEntry_UTIL.getActiveFieldNamesFromBatch(batchId));
return fields;
}

Expand Down