Map.get(key) returns null

apexcalloutdebuggingmap

I am working on a callout that returns info about the Asset object. Based on the response payload, I need to check whether a corresponding asset exists in SFDC or not. If an asset record exists and if the info between webservice response and salesforce asset is different, the existing asset needs to be updated. If not, a new asset needs to be created. I am doing upsert to perform the operation using an External Id field defined on the asset object. The way I have structured the code is as below.

  1. Defined a wrapper class called AssetTemplate
  2. Make a callout to the webservice based on a trigger insert. Get the webservice response and build a map<string uniqueId, AssetTemplate>
  3. Query the Asset object with the above map's keyset and loop through the asset records to build another map<string uniqueId, AssetTemplate>
  4. Loop through the keyset of the first map, get the value of assetTemplate from first and second map and compare the fields. If any of the fields is different, add the asset to a list to perform the upsert. If the field values are same, skip so that the asset does not get duplicate updates.

Most of the above seems to work fine. However, when I try to perform a get(uniqueId) on the sfdc asset map, it is returning null. As a result, the upsert operation is always updating the existing asset in salesforce based on the uniqueId from the webservice response.

Below is the code snippet.

**Wrapper Class**
**public class AssetTemplate {**
    public String assetName;
    public Id accountId;
    public Datetime effectiveDate;
    public String externalGUID;
}

**Service Class declarations and methods**
    **private static map<string, AssetTemplate> mapResponsesByUniqueProductId{**
    get{
        mapResponsesByUniqueProductId = (mapResponsesByUniqueProductId!=null) ? mapResponsesByUniqueProductId : new map<string,AssetTemplate>();
        return mapResponsesByUniqueProductId;
    }
    set;
}

//Builds a map of UniqueProductID and AssetTemplate from Assets from SFDC
    **private static map<string, AssetTemplate> mapSFDCAssetsByUniqueProductId{**
    get{
        mapSFDCAssetsByUniqueProductId = (mapSFDCAssetsByUniqueProductId!=null) ?  mapSFDCAssetsByUniqueProductId : new map<string,AssetTemplate>();
        return mapSFDCAssetsByUniqueProductId;
    }
    set;
}

**service class to process the webservice response. If webservice response is not null, loop thru response and build mapResponsesByUniqueProductId map.

        if(oprResponses != null && oprResponses.size() > 0) {
            
            //From the response, build the AssetTemplate and then process it.
            for(ServiceResponse oprResponse :oprResponses) {
                AssetTemplate responseTemplate = buildResponseTemplate(event, oprResponse);
                mapResponsesToProcess(event, responseTemplate);
            }
        }
    }
    try {
        processQueue();
    } catch(Exception e) {
    }
 }

//Method to build the AssetTemplate object with the event details and response from Pulled Pork.
**public static AssetTemplate buildResponseTemplate(OrgProduct__e event, ServiceResponse oprResponse) {**

    AssetTemplate template = new AssetTemplate();
    Timezone tz = UserInfo.getTimeZone();                                                                       //1.2
    

    if (event != null && oprResponse != null) {
        template.externalGUID = oprResponse.externalGUID;
        template.accountId = event.AccountId;
        template.assetName = oprResponse.product.longName;
        template.effectiveDate = (oprResponse.effectiveDate==null)?null:(date.newInstance(oprResponse.effectiveDate.yearGmt(),oprResponse.effectiveDate.monthGmt(),oprResponse.effectiveDate.dayGmt()));
    } else {
        template = null;
    }
    return template;
}

**public static void mapResponsesToProcess(OrgProduct__e event, AssetTemplate template) {**
    
    if (event != null && template != null) {
        string strUniqueEventToken = event.UniqueProductID__c;
        if (!mapResponsesByUniqueProductId.containsKey(strUniqueEventToken)) {
            mapResponsesByUniqueProductId.put(strUniqueEventToken, template);
        }
    }
    
}

//Method to process the responses from Pulled Pork to perform the Upsert (Insert/Update).
**private static void processQueue() {**
    
    set<string> setProcessedAssets = new set<string>();
    List<Asset> lstAssetsToUpsert = new List<Asset>();
    
    //Build the map of assets with UniqueProductID as the key.
    if (mapResponsesByUniqueProductId != null && mapResponsesByUniqueProductId.size() > 0) {
        Map<string, AssetTemplate> mapSFDCAssetsByUniqueProductId = new Map<string, AssetTemplate>();
        for (Asset ast : [SELECT Id, Name, IsDeleted, AccountId, UniqueProductID__c, PDCT01_Id__c, InstallDate, PurchaseDate, Cancelled_Date__c, Manufacturer__c, Sales_Channel__c from Asset WHERE UniqueProductID__c = :mapResponsesByUniqueProductId.keySet()]) {
            if (!mapSFDCAssetsByUniqueProductId.containsKey(ast.UniqueProductID__c)) {
                AssetTemplate template = buildAssetTemplate(ast.UniqueProductID__c, ast);
                mapSFDCAssetsByUniqueProductId.put(ast.UniqueProductID__c, template);
            } else {
                System.debug(LoggingLevel.INFO,'[Service Class][processQueue]: Asset found in System. AssetTemplate = ' + mapSFDCAssetsByUniqueProductId.get(ast.UniqueProductId__c));
            }
        }
    }
    
    for (String uniqueProductID : mapResponsesByUniqueProductId.keySet()) {
        
        AssetTemplate assetTemplate = mapSFDCAssetsByUniqueProductId.get(uniqueProductID);  ***//AssetTemplate is null here and not sure why.***
        AssetTemplate responseTemplate = mapResponsesByUniqueProductId.get(uniqueProductID);    
        System.debug(LoggingLevel.INFO,'[Service Class][processQueue]: assetTemplate = ' + JSON.serializePretty(assetTemplate) + '\n');
        System.debug(LoggingLevel.INFO,'[Service Class][processQueue]: responseTemplate = ' + JSON.serializePretty(responseTemplate) + '\n');
        //See if the information between the response from Pulled Pork API and information from Salesforce are different. If they are different, load the response into the array to perform the upsert.
        if ((assetTemplate != null && responseTemplate != null &&
            assetTemplate.assetName != responseTemplate.assetName &&
            assetTemplate.effectiveDate != responseTemplate.effectiveDate &&
            assetTemplate.accountId != responseTemplate.accountId
           ) || (assetTemplate == null && responseTemplate != null) )
        {
            Asset ast  = new Asset();
            ast.Name = (assetTemplate != null) ? assetTemplate.assetName : responseTemplate.assetName;
            ast.UniqueProductID__c = responseTemplate.externalGUID;
            ast.AccountId = responseTemplate.accountId;
            ast.InstallDate = (responseTemplate.effectiveDate != null) ? responseTemplate.effectiveDate.date() : null;
            ast.RecordTypeId = internalAssetRecordTypeId;
            lstAssetsToUpsert.add(ast);
        }
    }
    
    if (lstAssetsToUpsert != null && lstAssetsToUpsert.size() > 0) {
        try {
            upsert lstAssetsToUpsert Asset.Fields.UniqueProductID__c;
        } catch(Exception ex) {
            for (Asset ast : lstAssetsToUpsert) {
                upsert ast Asset.Fields.UniqueProductID__c;
            }
        }
    }

//Method to construct the AssetTemplate from the Asset.
**public static AssetTemplate buildAssetTemplate(String strUniqueProductID, Asset ast) {**
    
    AssetTemplate template = new AssetTemplate();
    
    if (strUniqueProductID != null && ast != null) {
        template.externalGUID = ast.UniqueProductID__c;
        template.assetName = ast.Name;
        template.accountId = ast.AccountId;
        template.effectiveDate = ast.InstallDate;
        
    } else {
        template = null;
    }
    
    return template;
}

As mentioned earlier, in the processQueue method, I am trying to get the AssetTemplate from the webservice response and from salesforce assets. The AssetTemplate retrieved from webservice response looks fine. However, the AssetTemplate retrieved from Salesforce Asset is null though the map is populated with the AssetTemplate in the buildAssetTemplate method.

Can someone help?

Thanks in advance.

Best Answer

Took me a while to spot it, but you're shadowing your static class member variable (property) called mapSFDCAssetsByUniqueProductId with a block-scoped variable in the code below:

//Build the map of assets with UniqueProductID as the key.
if (mapResponsesByUniqueProductId != null && mapResponsesByUniqueProductId.size() > 0) {
    Map<string, AssetTemplate> mapSFDCAssetsByUniqueProductId = new Map<string, AssetTemplate>();

This block level variable goes out of scope at the end of that if statement block, and then when the following code uses the same name to access the map, it's getting the class property instead which is still an empty map at that point.

You just need to modify the above code so that it uses the same map instance:

//Build the map of assets with UniqueProductID as the key.
if (mapResponsesByUniqueProductId != null && mapResponsesByUniqueProductId.size() > 0) {
    mapSFDCAssetsByUniqueProductId = new Map<string, AssetTemplate>();
Related Topic