0

I have exposed this Apex class as a webservice. So far so good, as I get the response when this Webservice is called from ARC or POstman.

@RestResource(urlMapping='/ProcessAccount/*') 

global with sharing class AccountManager {

    @HttpGet
    global static list<Account> getAccountById() {

       RestRequest req = RestContext.request;
       String recordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Supplier_Account').getRecordTypeId();
       String accountId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
       system.debug('The account is: ' +accountId);


       list<Account> result =  [SELECT ID, Supplier_ID__c,Name, BillingStreet, BillingCity, BillingState, BillingPostalCode, 
                                    BillingCountry, Phone, 
                                    RecordTypeId, CreatedDate,LastModifiedDate, SystemModstamp 
                                    FROM Account 
                                    WHERE RecordTypeId = :recordTypeId AND Id = :accountId limit 1];

        system.debug('Check the list size ' + result.size());
        return result;

    }


}

My question is, how can I add error handling in this class. How can i make it good enough to confidentially deploy to production along with the test class. ?

Thank You

3
  • I don't see any system exceptions that could conceivably be thrown from this code unless you were to manually delete the Record Type. What errors are you concerned about?
    – David Reed
    Nov 21, 2019 at 16:52
  • As always, Thank you @David Reed. I am concerned about any error that would prevent the response from being sent to the external app. Nov 21, 2019 at 17:06
  • You could use try/catch blocks to capture any potential exception. Also, you could have a custom object to store the exception log as a separate record for every transaction.
    – Arnold Jr.
    Nov 21, 2019 at 17:29

3 Answers 3

4

I don't believe that this code can throw any system exceptions at all. If no Accounts are found, or if the input is nonsensical, the class will simply return an empty list.

You need to add more code only if you want to provide a semantic response for the caller, such as "this request doesn't make sense", "no matching Account found", etc. In that case you would define your own error codes, check for the error state, and return some kind of composite response like

public class Response {
    List<Account> results;
    Integer errorCode;
}
1

I have created a valid test class for your code above. However, this is not bulkified. Also I would note that you are returning a list of accounts, however, you only seem to be passing in 1 Account Id.

The one error I can see you checking for is that the Id being passed in as the path, you may want to check to see if it is a valid Account Id. You could perform a check like this:

Id myId = Id.valueOf(accountId);
if(Schema.Account.SObjectType == myId.getSobjectType())
{
    //continue
}else {
    //report failure back
}
@isTest
public class TestAccountManager
{
    @isTest
    static void test1()
    {
        Account a = new Account();

        a.Supplier_ID__c = '123';
        a.Name = 'Test Account';
        a.BillingStreet = '123 Fake Street';
        a.BillingCity = 'Springfield';
        a.BillingState = 'NJ';
        a.BillingPostalCode = '12345';
        a.BillingCountry = 'US';
        a.Phone = '123-456-7890';
        a.RecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Supplier_Account').getRecordTypeId();

        insert a;

        RestRequest req = new RestRequest(); 
        RestResponse res = new RestResponse();

        req.requestURI = System.Url.getSalesforceBaseUrl().toExternalForm()+'services/apexrest/ProcessAccount/'+a.Id;
        req.httpMethod = 'GET';
        RestContext.request = req;
        RestContext.response = res;

        System.assertEquals(a.Id, AccountManager.getAccountById()[0].Id);
    }
}
6
  • 1
    @BrynaAnderson, Thank You. I dint mean to get help writing a test class. My apologies for not phrasing the question correctly. I do appreciate your help though, Nov 21, 2019 at 17:04
  • @user4947 understood, I added a check for sObjectType of your Id, this could possibly be a potential failure checkpoint Nov 21, 2019 at 17:06
  • Thank you!!. I appreciate your response. Nov 21, 2019 at 17:09
  • 1
    @BryanAnderson Won't binding that Id into the query just return an empty list if it's the wrong type? I don't believe that throws an exception.
    – David Reed
    Nov 21, 2019 at 17:36
  • 2
    I truly appreciate your help @BryanAnderson and DavidReed. I was just testing it, and you answered before I could even ask. Nov 21, 2019 at 17:48
1

You will need to build your own rest response wrapper for different possible scenarios. And if you do this way, salesforce will return the rest response regardlessly, so the method return type will be void.

A over simplified example,

@RestResource(urlMapping='/MyRestContextExample/*')
global with sharing class MyRestContextExample {

    @HttpGet
    global static void doGet() {
        RestRequest req = RestContext.request;
        RestResponse standardResponse = RestContext.response;
        String accountId =req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
        List<Account> result = [SELECT Id, Name, Phone, Website FROM Account WHERE Id = :accountId];

      if(result.isEmpty()){  
         //you can have own header or leave it
         standardResponse.addHeader('Content-Type', 'application/json');        
         standardResponse.statusCode = 404;
         standardResponse.responseBody = blob.valueOf('No record found') ;
       }
      else if(!result.isEmpty()){     
         standardResponse.addHeader('Content-Type', 'application/json');         
         standardResponse.statusCode = 200;
         standardResponse.responseBody = blob.valueOf(JSON.serializePretty(result));
      }
      ....etc

}

}

On top of that, if you have more complicated scenario for the return result, for example, instead of returning list of accounts, different list of Sobjects (Contacts, Cases) need to be return. You can build your own rest wrapper class.

Public class ResponseWrapper {
    Public List<Account> accs;
    Public List<Contact> cons;
    Public List<Case>   cases;
    ResponseWrapper(List<Account> para1,List<Contact> para2, List<Case> para3){
       this.accs = para1;
       this.cons = para2;
       this.cases = para3;
    }

}

Then just seralize it at the response body, A simplified example without null check

List<Account> accs = [Your SOQL];
List<Contact> cons = [Your SOQL];
List<Case> cases = [Your SOQL];
ResponseWrapper sample = new ResponseWrapper(accs,cons,cases)
standardResponse.responseBody = blob.valueOf(JSON.serializePretty(sample));
3
  • The result of a SOQL query is never null. The query as written above would throw an exception if no responsive records are found.
    – David Reed
    Nov 22, 2019 at 1:25
  • a bit of rush, code updated
    – Wayne Ni
    Nov 22, 2019 at 2:35
  • Thank you @WayneNi, much appreciated and thank you again DavidReed Nov 22, 2019 at 17:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.