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

  • 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 '19 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. – user4947 Nov 21 '19 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 '19 at 17:29
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;
}
|improve this answer|||||
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);
    }
}
|improve this answer|||||
  • 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, – user4947 Nov 21 '19 at 17:04
  • @user4947 understood, I added a check for sObjectType of your Id, this could possibly be a potential failure checkpoint – Bryan Anderson Nov 21 '19 at 17:06
  • Thank you!!. I appreciate your response. – user4947 Nov 21 '19 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 '19 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. – user4947 Nov 21 '19 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));
|improve this answer|||||
  • 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 '19 at 1:25
  • a bit of rush, code updated – Wayne Ni Nov 22 '19 at 2:35
  • Thank you @WayneNi, much appreciated and thank you again DavidReed – user4947 Nov 22 '19 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.