Filter Collection By Field Apex Invocable Method

PHOTO EMBED

Wed Sep 18 2024 14:21:44 GMT+0000 (Coordinated Universal Time)

Saved by @dannygelf #apex #flow #salesforce

global with sharing class BK_FilterCollectionByField {
    global class FilterException extends Exception {}
    @InvocableMethod(label='Filter Collection By Field')
    global static List<Result> filterRecordsByField(List<Request> requests) {
        List<Result> results = new List<Result>();
        for(Request request : requests) {
            // Validate inputs
            if (request.allowEmptyCollection != true && (request.inputRecords == null || request.inputRecords.isEmpty())) {
                throw new FilterException('Input record collection is required but empty.');
            }
            if (request.filterValues == null || request.filterValues.isEmpty()) {
                throw new FilterException('Filter values are required but empty.');
            }
            if (String.isBlank(request.fieldAPIName)) {
                throw new FilterException('Field to filter by is required but empty.');
            }
            List<SObject> filteredRecords = new List<SObject>();
            if (request.allowEmptyCollection == true && (request.inputRecords == null || request.inputRecords.isEmpty())) {
                // Return empty result if allowed
                filteredRecords = new List<SObject>{};
            } else {
                // Prepare the set or list for filter values based on case sensitivity
                Set<String> filterValuesSet = new Set<String>();
                if(request.caseSensitive == true) {
                    filterValuesSet.addAll(request.filterValues);
                } else {
                    for(String value : request.filterValues) {
                        if(value != null) {
                            filterValuesSet.add(value.toLowerCase());
                        }
                    }
                }
                // Filter records
                for(SObject record : request.inputRecords) {
                    Object fieldValueObj = record.get(request.fieldAPIName);
                    if(fieldValueObj != null) {
                        String fieldValue = String.valueOf(fieldValueObj);
                        if(request.caseSensitive == true) {
                            if(filterValuesSet.contains(fieldValue)) {
                                filteredRecords.add(record);
                            }
                        } else {
                            if(filterValuesSet.contains(fieldValue.toLowerCase())) {
                                filteredRecords.add(record);
                            }
                        }
                    }
                }
            }
            Result result = new Result();
            result.filteredRecords = filteredRecords;
            results.add(result);
        }
        return results;
    }
    global class Request {
        @InvocableVariable(label='Input Records' description='Collection of records to filter' required=true)
        global List<SObject> inputRecords;
        @InvocableVariable(label='Filter Values' description='Collection of string values to filter by' required=true)
        global List<String> filterValues;
        @InvocableVariable(label='Field API Name' description='API name of the field to filter by' required=true)
        global String fieldAPIName;
        @InvocableVariable(label='Allow Empty Collection' description='Allow the input record collection to be empty? Default is false')
        global Boolean allowEmptyCollection = false;
        @InvocableVariable(label='Case Sensitive' description='Perform case-sensitive matching? Default is false')
        global Boolean caseSensitive = false;
    }
    global class Result {
        @InvocableVariable(label='Filtered Records' description='Collection of records that match the filter criteria')
        global List<SObject> filteredRecords;
    }
}





Test class:
@isTest
private class BK_FilterCollectionByFieldTest {
    @isTest
    static void testFilterRecordsByField() {
        // Create sample accounts
        Account acc1 = new Account(Name = 'Test Account 1', AccountNumber = '123');
        Account acc2 = new Account(Name = 'Test Account 2', AccountNumber = '456');
        Account acc3 = new Account(Name = 'Test Account 3', AccountNumber = '789');
        insert new List<Account>{acc1, acc2, acc3};
        // Prepare request
        BK_FilterCollectionByField.Request request = new BK_FilterCollectionByField.Request();
        request.inputRecords = new List<SObject>{acc1, acc2, acc3};
        request.filterValues = new List<String>{'123', '789'};
        request.fieldAPIName = 'AccountNumber';
        request.caseSensitive = true;
        // Call the method
        List<BK_FilterCollectionByField.Result> results = BK_FilterCollectionByField.filterRecordsByField(
            new List<BK_FilterCollectionByField.Request>{request}
        );
        // Assert the results
        System.assertEquals(1, results.size(), 'Should return one result object');
        System.assertEquals(2, results[0].filteredRecords.size(), 'Should return two filtered records');
        Set<Id> expectedIds = new Set<Id>{acc1.Id, acc3.Id};
        Set<Id> resultIds = new Set<Id>();
        for(SObject sobj : results[0].filteredRecords) {
            resultIds.add(sobj.Id);
        }
        System.assertEquals(expectedIds, resultIds, 'Filtered records should match expected accounts');
    }
    @isTest
    static void testFilterRecordsByField_CaseInsensitive() {
        // Create sample accounts with varying case in AccountNumber
        Account acc1 = new Account(Name = 'Test Account 1', AccountNumber = 'abc');
        Account acc2 = new Account(Name = 'Test Account 2', AccountNumber = 'DEF');
        Account acc3 = new Account(Name = 'Test Account 3', AccountNumber = 'GHI');
        insert new List<Account>{acc1, acc2, acc3};
        // Prepare request
        BK_FilterCollectionByField.Request request = new BK_FilterCollectionByField.Request();
        request.inputRecords = new List<SObject>{acc1, acc2, acc3};
        request.filterValues = new List<String>{'Abc', 'def'};
        request.fieldAPIName = 'AccountNumber';
        request.caseSensitive = false;
        // Call the method
        List<BK_FilterCollectionByField.Result> results = BK_FilterCollectionByField.filterRecordsByField(
            new List<BK_FilterCollectionByField.Request>{request}
        );
        // Assert the results
        System.assertEquals(1, results.size(), 'Should return one result object');
        System.assertEquals(2, results[0].filteredRecords.size(), 'Should return two filtered records');
        Set<Id> expectedIds = new Set<Id>{acc1.Id, acc2.Id};
        Set<Id> resultIds = new Set<Id>();
        for(SObject sobj : results[0].filteredRecords) {
            resultIds.add(sobj.Id);
        }
        System.assertEquals(expectedIds, resultIds, 'Filtered records should match expected accounts');
    }
    @isTest
    static void testFilterRecordsByField_EmptyCollectionAllowed() {
        // Prepare request with empty inputRecords
        BK_FilterCollectionByField.Request request = new BK_FilterCollectionByField.Request();
        request.inputRecords = new List<SObject>();
        request.filterValues = new List<String>{'123', '789'};
        request.fieldAPIName = 'AccountNumber';
        request.allowEmptyCollection = true;
        // Call the method
        List<BK_FilterCollectionByField.Result> results = BK_FilterCollectionByField.filterRecordsByField(
            new List<BK_FilterCollectionByField.Request>{request}
        );
        // Assert the results
        System.assertEquals(1, results.size(), 'Should return one result object');
        System.assertEquals(0, results[0].filteredRecords.size(), 'Should return zero filtered records');
    }
    @isTest
    static void testFilterRecordsByField_ExceptionOnEmptyCollection() {
        // Prepare request with empty inputRecords
        BK_FilterCollectionByField.Request request = new BK_FilterCollectionByField.Request();
        request.inputRecords = new List<SObject>();
        request.filterValues = new List<String>{'123', '789'};
        request.fieldAPIName = 'AccountNumber';
        request.allowEmptyCollection = false;
        // Call the method and expect exception
        try {
            List<BK_FilterCollectionByField.Result> results = BK_FilterCollectionByField.filterRecordsByField(
                new List<BK_FilterCollectionByField.Request>{request}
            );
            System.assert(false, 'Expected an exception due to empty inputRecords');
        } catch (Exception ex) {
            System.assert(ex instanceof BK_FilterCollectionByField.FilterException, 'Expected a FilterException');
            System.assertEquals('Input record collection is required but empty.', ex.getMessage());
        }
    }
    @isTest
    static void testFilterRecordsByField_ExceptionOnEmptyFilterValues() {
        // Create sample accounts
        Account acc1 = new Account(Name = 'Test Account 1', AccountNumber = '123');
        insert acc1;
        // Prepare request with empty filterValues
        BK_FilterCollectionByField.Request request = new BK_FilterCollectionByField.Request();
        request.inputRecords = new List<SObject>{acc1};
        request.filterValues = new List<String>();
        request.fieldAPIName = 'AccountNumber';
        // Call the method and expect exception
        try {
            List<BK_FilterCollectionByField.Result> results = BK_FilterCollectionByField.filterRecordsByField(
                new List<BK_FilterCollectionByField.Request>{request}
            );
            System.assert(false, 'Expected an exception due to empty filterValues');
        } catch (Exception ex) {
            System.assert(ex instanceof BK_FilterCollectionByField.FilterException, 'Expected a FilterException');
            System.assertEquals('Filter values are required but empty.', ex.getMessage());
        }
    }
    @isTest
    static void testFilterRecordsByField_ExceptionOnEmptyFieldAPIName() {
        // Create sample accounts
        Account acc1 = new Account(Name = 'Test Account 1', AccountNumber = '123');
        insert acc1;
        // Prepare request with empty fieldAPIName
        BK_FilterCollectionByField.Request request = new BK_FilterCollectionByField.Request();
        request.inputRecords = new List<SObject>{acc1};
        request.filterValues = new List<String>{'123'};
        request.fieldAPIName = '';
        // Call the method and expect exception
        try {
            List<BK_FilterCollectionByField.Result> results = BK_FilterCollectionByField.filterRecordsByField(
                new List<BK_FilterCollectionByField.Request>{request}
            );
            System.assert(false, 'Expected an exception due to empty fieldAPIName');
        } catch (Exception ex) {
            System.assert(ex instanceof BK_FilterCollectionByField.FilterException, 'Expected a FilterException');
            System.assertEquals('Field to filter by is required but empty.', ex.getMessage());
        }
    }
}
content_copyCOPY

Needed to filter collection by a list of ids... standard Filter element in Flows doesn't have "IN" operator (API version 62)