Apex Coding Challenge Find Highest Frequency of Numbers

Problem: Find the number that has the highest frequency in a list of integers.

Input: 1,6,2,1,6,1

Output: 1 //because 1 occurs 3 times in the list

Option 1: Use Hashmap to iterate list

	List<Integer> nums = new List<Integer>{1,6,2,1,6,1};
		Map<Integer, Integer> numMap = new HashMap<>();
		for (Integer num : nums){
			if (numMap.containsKey(num)){
				Integer numFreq = numMap.get(num);
				numMap.put(num, numFreq+1);
			} else {
				numMap.put(num, 1);
		Integer biggestFreq = 0;
		Integer biggestVal = 0;
		for (Integer num : numMap.keySet()){
			if (numMap.get(num) > biggestFreq){
				biggestFreq = numMap.get(num);
				biggestVal = num;

Option 2: Use wrapper class with compare to sort wrapper

List<Integer> nums = new List<Integer>{1,6,2,1,6,1};

Map<Integer, NumFrequencyWrapper> numMap = new HashMap<>();
for (Integer num : nums){
	if (numMap.containsKey(num)){
		NumFrequencyWrapper numFreqWrapper = numMap.get(num);
		numMap.put(num, numFreqWrapper);
	} else {
		NumFrequencyWrapper numFrequencyWrapper = new NumFrequencyWrapper();
		numMap.put(num, numFrequencyWrapper);
List<NumFrequencyWrapper> frequencyWrapperList = new List(numMap.values());
Collections.sort(frequencyWrapperList, new Untitled.NumFrequencyWrapperCompare());

public class NumFrequencyWrapper {
	private Integer num;
	private Integer frequency;
	public void setNum(Integer num){
		this.num = num;
	public Integer getNum(){
		return num;
	public void setFrequency(Integer frequency){
		this.frequency = frequency;
	public Integer getFrequency(){
		return this.frequency;
public class NumFrequencyWrapperCompare implements Comparator<NumFrequencyWrapper>{
	public int compare(NumFrequencyWrapper a, NumFrequencyWrapper b) { 
		return  b.getFrequency() - a.getFrequency(); 

Option 3: Using buckets to group index of frequencies together

List<Integer> nums = new List<Integer>{1,6,2,1,6,1};
Integer returnNums = 2;
Map<Integer, Integer> numMap = new Map<Integer, Integer>();
for (Integer num : nums){
	if (numMap.containsKey(num)){
		Integer numFreq = numMap.get(num);
		numMap.put(num, numFreq+1);
	} else {
		numMap.put(num, 1);

Map<Integer, List<Integer>> mapOfBucketWithValues = new Map<Integer, List<Integer>>();
for (Integer num : numMap.keySet()){
	Integer numFrequency = numMap.get(num);
	if (mapOfBucketWithValues.containsKey(numFrequency)){
		List<Integer> existingIndexNum = mapOfBucketWithValues.get(numFrequency);
		mapOfBucketWithValues.put(numFrequency, existingIndexNum);
	} else {
		List<Integer> numList = new ArrayList<>();
		mapOfBucketWithValues.put(numFrequency, numList);

for (Integer k=nums.size(), returnedNums=0; 1<=k; k--){
	if (mapOfBucketWithValues.containsKey(k)){
		for (Integer numBucket : mapOfBucketWithValues.get(k)){
			if (returnedNums < returnNums){

Apex Clear all fields for a SObject record

The clearOutRecords would iterate all the fields passed as the currentRecord, then:
1. Exclude the fields as part of the fieldsToExcludeForClearOut Set and relationship fields
2. Check if the field is not null and updateable
3. Special logic to set fields to predefined values
4. Set all other fields to null
5. Return the SObject with fields as null

private static Set<String> fieldsToExcludeForClearOut = new Set<String>{'Cases', 'DoNotCall', 
'HasOptedOutOfFax', 'HasOptedOutOfEmail', 'LastName', 
'FirstName', 'Email', 'AccountId', 'CreatedDate',

    public SObject clearOutRecords(SObject currentRecord, String sObjectName){
      SObjectType objToken = Schema.getGlobalDescribe().get(sObjectName);
      DescribeSObjectResult objDef = objToken.getDescribe();
      Map<String, SObjectField> fieldsSobject = objDef.fields.getMap();
      Map<String, Object> fields = currentRecord.getPopulatedFieldsAsMap();
      Type classType = Type.forName(sObjectName);
      SObject mergedRecord = (SObject)JSON.deserialize('{}', classType);
      for (String field : fields.keySet()){
        if (!fieldsToExcludeForClearOut.contains(field) && !field.contains('__r')){
          if (currentRecord.get(field)!=null && fieldsSobject.get(field).getDescribe().isUpdateable()){
            if ('User_Status__c'.equals(field)){
              mergedRecord.put(field, 'Incomplete');
            } else if ('Is_Mail_Same_As_Home__c'.equals(field)){
              mergedRecord.put(field, false);
            } else {
              mergedRecord.put(field, null);
            } else if ('Id'.equals(field)){
            mergedRecord.put(field, currentRecord.get(field));
      return mergedRecord;

Initializing the clearOutRecords method

1. Query the fields that you would like to clear
2. Pass the Object to the clearOutRecords method

Contact queryContact = [Select Id, FirstName, LastName, Email, Birthdate, MailingState, Age__c from Contact where MailingState!=null limit 1 ];

Contact clearedOutContact = (Contact)App_Service.instance.clearOutRecords(queryContact, 'Contact');


Contact:{Id=0036300000TQZIwAAP, Birthdate=null, MailingState=null}

Salesforce Platform Events Streaming using Spring Boot

Create EmpConnector connection
The first thing is to create a configuration that on startup will connect to the EmpConnector and start listing for incoming events on a specific topic.

1. Set all your Bayeux Parameters (bayeuxParameters)
2. Create EmpConnector connection (empConnector)
3. Start listening to topic (startAndPublishAsyncEventToExchange)
4. Adding listeners to the topic (startAndPublishAsyncEventToExchange)

package com.app.core.events;

import com.app.core.service.IncomingRequestHandler;
import com.app.utils.AppPlanUtils;
import com.salesforce.emp.connector.BayeuxParameters;
import com.salesforce.emp.connector.EmpConnector;
import com.salesforce.emp.connector.TopicSubscription;
import com.salesforce.emp.connector.example.LoggingListener;
import org.cometd.bayeux.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import java.net.URL;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;

import static com.salesforce.emp.connector.LoginHelper.login;

public class SalesforceEventConfig {

    private static Logger itsLogger = LoggerFactory.getLogger(SalesforceEventConfig.class);

    private String username;

    private String password;

    private String token;

    private String url;

    private String version;

    protected IncomingRequestHandler itsReqHandler;

    private static final Integer WAIT_TIME = 2;
    private static final Integer START_UP_WAIT_TIME = 5;
    private static final String EVENT_NAME = "App_Events__e";

    public BayeuxParameters bayeuxParameters() throws Exception {
        String theUrlEnv = AppUtils.getConfigVar("SALESFORCE_URL");
        String passwordAndToken = password + token;
        if (theUrlEnv != null) {
            url = getNextToken(0, theUrlEnv);
            username = getNextToken(url.length() + version.length() + 2, theUrlEnv);
            passwordAndToken = theUrlEnv.substring(url.length() + version.length() + username.length() + 3);
            itsLogger.info("Found SALESFORCE_URL to parse. url={}, version={}, username={}", url, version, username);
            url = url.split("/services/Soap/u/")[0];

        BayeuxParameters bayeuxParams = getBayeuxParamWithSpecifiedAPIVersion(version);
        BayeuxParameters bayeuxParameters = login(new URL(url),username, passwordAndToken, bayeuxParams);
        return bayeuxParameters;

    public EmpConnector empConnector(BayeuxParameters bayeuxParameters){
        itsLogger.debug("BayeuxParameters url {} version {}", bayeuxParameters.host(), bayeuxParameters.version());
        EmpConnector empConnector = new EmpConnector(bayeuxParameters);
        itsLogger.debug("EmpConnector isConnected {} isHandshook {}", empConnector.isConnected(), empConnector.isHandshook());
        return empConnector;

    private static BayeuxParameters getBayeuxParamWithSpecifiedAPIVersion(String apiVersion) {
        BayeuxParameters params = new BayeuxParameters() {

            public String version() {
                return apiVersion;

            public String bearerToken() {
                return null;

        return  params;

    private String getNextToken(int startIdx, String origStr) {
        int theIdx = origStr.indexOf("|", startIdx);
        if (theIdx > 0) {
            return origStr.substring(startIdx, theIdx);
        } else {
            return origStr.substring(startIdx);

    public TopicSubscription startAndPublishAsyncEventToExchange(EmpConnector empConnector)  {
        TopicSubscription subscription = null;
        try {
            long replayFrom = EmpConnector.REPLAY_FROM_TIP;
            itsLogger.debug("Setup event consumer with replyFrom {}", replayFrom);

            SalesforceEventPayload eventPayload = new SalesforceEventPayload();
            Consumer<Map<String, Object>> consumer = event -> {

            LoggingListener loggingListener = new LoggingListener(true, true);
            itsLogger.debug("Adding event listeners");
            empConnector.addListener(Channel.META_CONNECT, loggingListener)
                    .addListener(Channel.META_HANDSHAKE, loggingListener)
                    .addListener(Channel.META_DISCONNECT, loggingListener)
                    .addListener(Channel.META_SUBSCRIBE, loggingListener)
                    .addListener(Channel.META_UNSUBSCRIBE, loggingListener)
                    .addListener(Channel.META_DISCONNECT, loggingListener)
                    .addListener(Channel.SERVICE, loggingListener);

            itsLogger.debug("Starting Event Bus");
            empConnector.start().get(START_UP_WAIT_TIME, TimeUnit.SECONDS);

            itsLogger.debug("Subscribing to event {}", EVENT_NAME);
            subscription = empConnector.subscribe("/event/" + EVENT_NAME, replayFrom, consumer).get(WAIT_TIME, TimeUnit.SECONDS);

            itsLogger.debug(String.format("Subscribed: %s", subscription));

        } catch (InterruptedException e) {
        } catch (ExecutionException e) {
        } catch (TimeoutException e) {

        return subscription;

Basic POJO to serialize events received for a specific topic

Note: as the ‘payload’ tag has some extra tags I remove them before serializing the payload to POJO.

package com.app.core.events;

import com.app.db.model.UserServiceRequestEvent;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.Serializable;
import java.util.Map;

public class SalesforceEventPayload implements Serializable{

    private static final String SALESFORCE_EVENT_KEY = "Event_Data__c=";

    private static Logger itsLogger = LoggerFactory.getLogger(SalesforceEventPayload.class);

    public SalesforceEventPayload() {}

    private UserServiceRequestEvent payload;

    public UserServiceRequestEvent getPayload() {
        return payload;

    public void setPayload(Map<String, Object> fieldMappings) {
        ObjectMapper theMapper = new ObjectMapper();
        theMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        theMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
        theMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
        theMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        theMapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
        if (fieldMappings.containsKey("payload")){
            String eventPayload = fieldMappings.get("payload").toString();
            String splitEventString = eventPayload.toString().split(SALESFORCE_EVENT_KEY)[1];
            String removeLastTwo = splitEventString.substring(1, splitEventString.length() - 2);
            String unescapeJson = StringEscapeUtils.unescapeJson(removeLastTwo);
                payload = theMapper.readValue(unescapeJson, UserServiceRequestEvent.class);
            } catch (JsonParseException e) {
                itsLogger.error("Event json parse exception {}", e.getMessage());
            } catch (JsonMappingException e) {
                itsLogger.error("Event json mapping exception {}", e.getMessage());
            } catch (IOException e) {
                itsLogger.error("Event io exception {}", e.getMessage());

Apex remove sensitive data from json

When you need to remove sensitive data from json before logging the following method will remove a predefined list of keywords

String bodyArgs = '{"name":"test", "ssn":"324234234", "email":"test@mail.com"}';
Object bodyObj = (Object)JSON.deserializeUntyped(bodyArgs);

Map<String, Object> mapObj = new Map<String, Object>();
if (bodyObj instanceof List<Object>){
	List<Object> lstObjs = (List<Object>)JSON.deserializeUntyped(bodyArgs);
    for (Object lstObj : lstObjs){
       Map<String,Object> parseLstObj = (Map<String,Object>)JSON.deserializeUntyped(JSON.serialize(lstObj));
} else {
	mapObj = (Map<String,Object>)JSON.deserializeUntyped(bodyArgs);

Map<String, String> newMappedValues = new Map<String, String>
System.debug(removeAttributes(mapObj, newMappedValues));
>>> Output: '{"name":"test"}'

removeAttributes method will iterate all the keys in the payload and remove the sensitive keys from the payload

private Set<String> removeSensitiveKeyValue = new Set<String>{'ssn', 'email', 'dob'};

public Map<String, String> removeAttributes(Map<String,Object> jsonObj, Map<String, String> mappedKeys)  {
	for(String key : jsonObj.keySet()) {
		if (removeSensitiveKeyValue.contains(key)){
		} else {
	      if(jsonObj.get(key) instanceof Map<String,Object>) {
	          removeAttributes((Map<String,Object>)jsonObj.get(key), mappedKeys);
	      } else if(jsonObj.get(key) instanceof List<Object>) {
	          for(Object listItem : (List<Object>)jsonObj.get(key)) {
	           if(listItem instanceof Map<String,Object>)  {
	        	removeAttributes((Map<String,Object>)listItem, mappedKeys);
	      } else {
			mappedKeys.put(key, String.valueOf(jsonObj.get(key)));
	return mappedKeys;

Apex Coding Challenge for Iterating Lists

Here is the problem statement:

Given an expectedSum value find the sum (n+ (n+1) = expectedValue) from a list that equals the expectedSum. I am using the following list


and looking for the sum to be 8 (3+5, 4+4)

*Note the list is sorted

Solution 1:

        Integer expectedSum = 8;
		List<Integer> listInts = Arrays.asList(1,3,4,4,5,9);
		for (Integer k=0; k<listInts.size();k++){
			for (Integer j=k+1; j<listInts.size();j++){
				if (listInts.get(k)+listInts.get(j)==expectedSum){
					System.debug(listInts.get(k) + ' + ' + listInts.get(j));

The above time complexity for a nested for loop is O(n^2)

Solution 2:

Do a binary search to search if the diff is contained in the list

		Integer expectedSum = 8;
		List<Integer> listInts = Arrays.asList(1,3,4,4,5,9);
		for (Integer listInt : listInts){ 
			Integer diffInt = expectedSum-listInt;
			if (binarySearch(listInts, diffInt)!=null){
				System.debug(listInt + " + " + diffInt);
	public static Integer binarySearch(List<Integer> listInts, Integer searchInt){
		Integer startPos = 0;
		Integer listSize = listInts.size() - 1;
		Integer mid;
		while(startPos <= listSize){
			if(listInts.get(mid) == searchInt){
				 return listInts.get(mid);
			else if(searchInt < listInts.get(mid)){
				listSize = mid - 1;
				startPos = mid + 1;
		return null;	 

the above time complexity for binary search is for each element in the array O(n log n) Returning null is not a good practice try to return another number or throw an exception.

Solution 3:

Check if the diff is contained in the list by using the .contains method

		Integer expectedSum = 8;
		List<Integer> listInts = Arrays.asList(1,3,4,4,5,9);

		for (Integer listInt : listInts){
			Integer diffInt =expectedSum-listInt;
			if (listInts.contains(diffInt)){
				System.debug(listInt + ' + ' + diffInt);

the above time complexity for loop is O(n)

Option 4

Start on either end of the array and move inwards when you find a solution, if the sum of the outer and inner element is bigger that the expectedSum move the maxPointer inwards, if it is smaller move the minPointer inwards.

        Integer expectedSum = 1;
		List<Integer> listInts = Arrays.asList(1,3,4,4,5,9);

		Integer maxPointer = listInts.size()-1;
		Integer minPointer = 0;
		for (Integer k=0; k<listInts.size();k++){
			if (expectedSum < listInts.get(maxPointer)){
			} else if (minPointer!=maxPointer){
				Integer sumPair =listInts.get(minPointer) + listInts.get(maxPointer);
				if ( sumPair == expectedSum){
					System.debug(listInts.get(minPointer) + " + " + listInts.get(maxPointer));
					minPointer +=1;
				} else if (sumPair < expectedSum){
					minPointer +=1;
				} else {

the above time complexity for loop is O(n)


4 + 4
5 + 3
3 + 5


How to do it with an unsorted list:

Iterate through the list and add the integer to the list, if the diff of the expected sum and integer is contained in the set then print it

		Integer expectedSum = 8;
		List<Integer> listInts = Arrays.asList(7,4,6,1,5,2,3);

		Set<Integer> intSetWithDiff = new HashSet<>();
		for (Integer listInt : listInts){
			Integer sumDiff = expectedSum - listInt;
			if (intSetWithDiff.contains(sumDiff)){
				System.debug(listInt + " + " + sumDiff);


1 + 7
2 + 6
3 + 5

Salesforce Object Level and Field Level Security Architecture

The Salesforce Security review require that both:

  1. Object Level (OLS)
  2. Field Level (FLS)

Security is applied for the following areas:

  1. Query of data – Selectors
  2. Triggers – Domains
  3. DML – Unit of Work

To check both (OLS) and FLS we can use fflib_SecurityUtils it has all the to methods to check if a user can access an object and also individual fields for that object. An Apex exception is thrown if the user does not have access to the given object and/or fields.

Example is when a community user tries to create an Account the following error occurs if OLS is enabled

fflib_SecurityUtils.CrudException: You do not have permission to insert Account
Class.fflib_SecurityUtils.checkObjectIsInsertable: line 309, column 1
Class.fflib_SObjectUnitOfWork.insertDmlByType: line 583, column 1
Class.fflib_SObjectUnitOfWork.doCommitWork: line 531, column 1
Class.fflib_SObjectUnitOfWork.commitWork: line 509, column 1
Class.App_Service_Test.testSecurityUOW: line 81, column 1

  1. Query of data – Selectors

The key purpose of this class is to make building dynamic SOQL queries safer and more robust than traditional string concatenation or String.format approaches. It also has an option to automatically check read security for the objects and fields given to it. Here are some of the methods we use to dynamically build all our queries.

Enable FLS check for queries before they run, by setting setEnforceFLS(true)

OR set the OLS and FLS when constructing a new Selector so it will be implement for all queries

OpportunitiesSelector oppsSelector = 
new OpportunitiesSelector(includeFieldSetFields, enforceObjectSecurity, enforceFLS);

2. Triggers – Domains

Domain classes has minimal functionality in it other than the routing of trigger events to the applicable virtual methods and object security enforcement.

Domain classes by OLS by default as part of it’s Configuration class:

3. DML – Unit of Work

This is not currently implemented to check the following:

1. Insert

  if (enforceOLS){

2. Update

 if (enforceOLS){

3. Delete 

if (enforceOLS){

Disadvantages of OLS and FLS implemented across your app

OLS – this is a check that is done to see if the user profile has access to do CRUD on the specific object. If this check is not done and user tries to do any of the CRUD operations it will just result in a Salesforce error. Doing this check on an object level is not too big of an overhead. If these checks are not done, Permission errors will be thrown as the profile does not have the correct permission.

FLS – this is not recommended as we have to iterate through every field of the object and check if the user has the privileges to read/update/insert/delete to that field. This slows down operations down drastically and only needs to be used in a few uses cases.

Possible solutions run security checks on app

Have a way to enable both OLS and FLS in your code then run through all your test and see if any break. If the break GREAT fix your Object Settings/FLS so that they work. When running in Production have a way to disable this as it may/will slow down your operations/services.

Apex Interview Question > FizzBuzz

Consider the following problem:

Write a short program that prints each number from 1 to 100 in one line

For each multiple of 3, print “Fizz” instead of the number.

For each multiple of 5, print “Buzz” instead of the number.

For numbers which are multiples of both 3 and 5, print “FizzBuzz” instead of the number.
Write a solution (or reduce an existing one) so it has as few characters as possible.

Solution 1: For loop with multiple if statements

String str = '';
for(Integer i = 1; i&amp;lt;=100; i++){
    else if(Math.mod(i, 3) != 0)
    str +=',';



Apex FeedItem Trigger Share to Community

When uploading a new FeedItem we want to share it to a specific community. First thing we need to do is share the FeedItem to a community by sharing it to the User. As the documentation states: Only feed items with a Group or User parent can set a NetworkId or a null value for NetworkScope.


NetworkId—The ID of the community in which the FeedItem is available. If left empty, the feed item is only available in the default community.

FeedItemTrigger Trigger

trigger FeedItemTrigger on FeedItem (after insert, before insert) {

FeedItemTrigger Domain Class

public class App_Domain_FeedItem extends fflib_SObjectDomain {

	public override void onBeforeInsert(){
		List<ContentDocumentLink> contentDocumentLinksList = new List<ContentDocumentLink>();
		for(FeedItem record : (List<FeedItem>) Records){
			record.Body = record.ParentId;
			record.NetworkScope = {NetworkId};
			record.ParentId = UserInfo.getUserId();

Now that the file is shared with the user we can give View sharing back to the original object using ContentDocumentLink by using the RelatedRecordId

public class App_Domain_FeedItem extends fflib_SObjectDomain {
public override void onAfterInsert(){
		List<ContentDocumentLink> contentDocumentLinksList = new List<ContentDocumentLink>();
		List<Documents__c> bpDocumentsList = new List<Documents__c>();

		Map<Id, Id> feedItemToRelatedRecordIdMap = new Map<Id, Id>();
		for(FeedItem record : (List<FeedItem>) Records){
			feedItemToRelatedRecordIdMap.put(record.Id, record.RelatedRecordId);

		Map<Id, Id> mapOfRelatedRecordContentVersionMap = new Map<Id, Id>();
		List<ContentVersion> contentDocumentVersions = [Select Id, ContentDocumentId from ContentVersion where id IN :feedItemToRelatedRecordIdMap.values()];
		for (ContentVersion contentDocumentVersion : contentDocumentVersions){
			mapOfRelatedRecordContentVersionMap.put(contentDocumentVersion.Id, contentDocumentVersion.ContentDocumentId);

		for(FeedItem record : (List<FeedItem>) Records){
			String bpDocumentId = record.Body;

			Id relatedRecordId = feedItemToRelatedRecordIdMap.get(record.Id);
			Id contentDocumentId = null;
			if (mapOfRelatedRecordContentVersionMap.containsKey(relatedRecordId)){
				 contentDocumentId = mapOfRelatedRecordContentVersionMap.get(relatedRecordId);

			if (contentDocumentId!=null){
				ContentDocumentLink cdl = new ContentDocumentLink(LinkedEntityId = bpDocumentId , ContentDocumentId=contentDocumentId	, shareType = 'V');

			Documents__c updateDocument = new Documents__c(Id=bpDocumentId);
			String formatDownloadUrl = '/CommunityApi/sfc/servlet.shepherd/version/download/{0}?asPdf=false&operationContext=CHATTER';
			updateDocument.Download_Url__c = String.format(formatDownloadUrl, new List<String>{contentDocumentId});
			updateDocument.File_Id__c = contentDocumentId;
			updateDocument.File_Name__c = record.Title;
			updateDocument.File_Size__c = record.ContentSize;
			updateDocument.File_Type__c = record.ContentType!=null ? mimeTypeMap.containsKey(record.ContentType.toLowerCase()) ? mimeTypeMap.get(record.ContentType.toLowerCase()) : record.ContentType  : record.ContentType;


		if (!contentDocumentLinksList.isEmpty()){
			insert contentDocumentLinksList;

		if (!bpDocumentsList.isEmpty()){
			update bpDocumentsList;

Now the Feeditem is shared both to the user and the object. It can be downloaded by the logged in user.

Apex Test Active Case Assignment Rules Assertion Fails

If you are testing active assignment rules you need to add it as an DMLOptions during testing else your assignment rule(s) will not get called and your assertion(s) may fail.

If you have one active Case Assignment Rule you can use dmlOpts.assignmentRuleHeader.useDefaultRule = true else you need to query for AssignmentRules. To query AssignmentRules you will need to have without sharing set as your class level access, else you will not get access to AssignmentRules.

If you have one method to insertCases you can make sure your assignmentRules run every time you execute a test class.

  public Id insertCase(Case bpCase){
      fflib_ISObjectUnitOfWork uow = Rest_App.UnitOfWork.newInstance();

      if (Test.isRunningTest()){
        Database.DMLOptions dmlOpts = new Database.DMLOptions();
         dmlOpts.assignmentRuleHeader.useDefaultRule = true;

      return bpCase.Id;

Here is the actual test class:

	@isTest(SeeAllData=true) static void testClientRequest(){
		RestRequest req = new RestRequest();
		RestResponse res = new RestResponse();
		Map<String, String> mapOfIds = App_Global_Test.setupCommunityUserReturnIds();

			System.runAs(new User(Id=mapOfIds.get('UserId'))){
				req.requestBody = Blob.valueOf('{"contactId":"'+mapOfIds.get('ContactId')+'", "type":"Client Request", "description":"Please help me with my account", "subject":"Client Request", "reason":"Client Request", "origin":"App"}');
				req.requestURI = '/v1/support/case';
				req.httpMethod = 'POST';
				RestContext.request = req;
				RestContext.response = res;

				System.assertEquals(res.statusCode, 200);

		List<Case> supportCase = [Select Id, Subject, Description, Type, Owner.Name from Case where ContactId=:mapOfIds.get('ContactId')];
		System.assertEquals(supportCase.size(), 1);
		System.assertEquals(supportCase.get(0).Subject, 'Client Request');
		System.assertEquals(supportCase.get(0).Type, 'Client Request');
		System.assertEquals(supportCase.get(0).Owner.Name, 'Client Services Queue');

Apex Run multiple batch jobs sequentially

We can run 3 batch jobs sequentially by incrementing the jobCounter and passing the integer (job index) into the batch scope

This can be increased to any amount of batch jobs, the problem I solved was able to update the Contact and disable Users in the same code running as different batch job.

As you cannot call @future in a batch method this solves by running each update in their own batch = transaction 👍🏻

global with sharing class App_Job_Account_Delete implements System.Schedulable, Database.Batchable<Integer>, Database.Stateful, Database.AllowsCallouts  {

    private List<JobError> jobErrors = new List<JobError>();

    global Integer jobCounter = 1;

    public void execute(SchedulableContext sc) {
        Database.executeBatch(this, jobCounter);

    public Integer[] start(Database.BatchableContext context) {
        return new Integer[] {jobCounter};

    public void execute(Database.BatchableContext context, Integer[] scope)     {
            if (isSandbox() || Test.IsRunningTest()){
                if(scope[0] == 1) {
                    //add your code here to run as part of 1st batch
                } else if (scope[0] == 2){
                    //add your code here to run as part of 2nd batch
                }  else if (scope[0] == 3){
                    //add your code here to run as part of 3rd batch
            } else {
                JobError jobError = new JobError();
                jobError.message = 'Environment Error: Job will only run on dev and automation environment';
                jobError.records = new List<SObject>();
        } catch (Exception ex){
            JobError jobError = new JobError();
            jobError.records = new List<SObject>();
            jobError.message = 'Exception: ' + ex.getTypeName() + ': ' + ex.getMessage()  + ' -- ' + ex.getCause();

    public void finish(Database.BatchableContext context){
        if (jobCounter<3){
            Database.executeBatch(this, jobCounter);

    public class JobError{
        public String message;
        public List<SObject> records;

    public void setJobError(List<JobError> jobErrors){
        this.jobErrors = jobErrors;

    public static Boolean isSandbox() {
        if ([SELECT IsSandbox FROM Organization LIMIT 1].IsSandbox && ('Dev'.equalsIgnoreCase(CustomSettings.Environment__c) || 'Automation'.equalsIgnoreCase(CustomSettings.Environment__c))){
            return true;
        } else {
            return false;

Run the code as

Id batchprocessid = Database.executeBatch(new App_Job_Account_Delete());
%d bloggers like this: