Salesforce Developer Apex Rest Exposing Apex

salesforce-developer-apex-rest

// Salesforce - Developer - REST - Expose Your Apex Class as a Web Service:

You can expose your Apex class methods as a REST or SOAP web service operation. 
By making your methods callable through the web, you allow third party 
applications (or your own external applications) to access your data.

Apex REST methods can be used in managed and unmanaged packages. When calling 
Apex REST methods that are contained in a managed package, you need to include 
the managed package namespace in the REST call URL. For example, if the class 
is contained in a managed package namespace called packageNamespace and the 
Apex REST methods use a URL mapping of /MyMethod/*, the URL used via REST to 
call these methods would be of the form 
https://instance.salesforce.com/services/apexrest/packageNamespace/MyMethod/.

Instead of using custom Apex code for REST and SOAP services, external 
applications can integrate with Salesforce by using Salesforce’s REST and SOAP 
APIs. These APIs let you create, update, and delete records. However, the 
advantage of using Apex web services is that Apex methods can encapsulate 
complex logic. This logic is hidden from the consuming application. Also, the 
Apex class operations can be faster than making individual API calls, because 
fewer roundtrips are performed between the client and the Salesforce servers. 
With an Apex web service call, there is only one request sent, and all 
operations within the method are performed on the server.

The security context under which Apex web service methods run differs from the 
security context of Salesforce APIs. Unlike Salesforce APIs, Apex web service 
methods run with system privileges and don’t respect the user’s object and field 
permissions. However, Apex web service methods enforce sharing rules when 
declared with the with sharing keyword.

All sObjects, standard or custom, come with a REST API, so we do not have to
create a REST API for each of our objects.  Apex REST is used for defining
custom interactions.  Apex REST is good for abstracting complex interactions
to a single call:

1. Inserting / updating multiple objects
2. Callouts to external systems
3. Custom search interfaces.

All custom Apex REST services are accessed in the same namespace.  For example,
https://na15.salesforce.com/services/apexrest/<custom_path> where <custom_path>
can be something like v1/joborders

Making your Apex class available as a REST web service is straightforward:

1. Define your class as global

2. Add the @RestResource annotation to the class

   @RestResource(urlMapping='/Account/*')
   global with sharing class MyRestResource {
       @HttpGet
       global static Account getRecord() {
           // Add your code
       }
   }

   As you can see, the class is annotated with 
   @RestResource(urlMapping='/Account/*). The base endpoint for Apex REST is 
   https://yourInstance.salesforce.com/services/apexrest/. The URL mapping is 
   appended to the base endpoint to form the endpoint for your REST service.
   For example, in the class example, the REST endpoint is 
   https://yourInstance.salesforce.com/services/apexrest/Account/

   The URL mapping is case-sensitive and can contain a wildcard character (*).

   We should think about versioning your API endpoints so that you can provide 
   upgrades in functionality without breaking existing code. You could create t
   wo classes specifying URL mappings of /Cases/v1/* and /Cases/v2/* to 
   implement this functionality.

3. Define methods as global static

4. Add annotations to the class and methods.

   @HttpGet: Reads or retrieves records (HTTP VERB: GET)
   @HttpPost: Create records (HTTP VERB: POST)
   @HttpDelete: Deletes records (HTTP VERB: DELETE)
   @HttpPut: Update existing records or create records. (HTTP VERB: PUT)
   @HttpPatch: Update (HTTP VERB: PATCH)

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

    @HttpGet
    global static Case getCaseById() {
        RestRequest request = RestContext.request;
        // grab the caseId from the end of the URL
        String caseId = request.requestURI.substring(
          request.requestURI.lastIndexOf('/')+1);
        Case result =  [SELECT CaseNumber,Subject,Status,Origin,Priority
                        FROM Case
                        WHERE Id = :caseId];
        return result;
    }

    @HttpPost
    global static ID createCase(String subject, String status,
        String origin, String priority) {
        Case thisCase = new Case(
            Subject=subject,
            Status=status,
            Origin=origin,
            Priority=priority);
        insert thisCase;
        return thisCase.Id;
    }   

    @HttpDelete
    global static void deleteCase() {
        RestRequest request = RestContext.request;
        String caseId = request.requestURI.substring(
            request.requestURI.lastIndexOf('/')+1);
        Case thisCase = [SELECT Id FROM Case WHERE Id = :caseId];
        delete thisCase;
    }     

    @HttpPut
    global static ID upsertCase(String subject, String status,
        String origin, String priority, String id) {
        Case thisCase = new Case(
                Id=id,
                Subject=subject,
                Status=status,
                Origin=origin,
                Priority=priority);
        // Match case by Id, if present.
        // Otherwise, create new case.
        upsert thisCase;
        // Return the case ID.
        return thisCase.Id;
    }

    @HttpPatch
    global static ID updateCaseFields() {
        RestRequest request = RestContext.request;
        String caseId = request.requestURI.substring(
            request.requestURI.lastIndexOf('/')+1);
        Case thisCase = [SELECT Id FROM Case WHERE Id = :caseId];
        // Deserialize the JSON string into name-value pairs
        Map<String, Object> params = (Map<String, Object>)JSON.deserializeUntyped(request.requestbody.tostring());
        // Iterate through each parameter field and value
        for(String fieldName : params.keySet()) {
            // Set the field and value on the Case sObject
            thisCase.put(fieldName, params.get(fieldName));
        }
        update thisCase;
        return thisCase.Id;
    }    

}

https://yourInstance.salesforce.com/<Record ID>.
// Salesforce - Developer - REST - Expose Your Apex Class as a Web Service - Testing:

Testing your Apex REST class is similar to testing any other Apex class.  Just 
call the class methods by passing in parameter values and then verify the 
results. For methods that don’t take parameters or that rely on information in 
the REST request, create a test REST request.

In general, here’s how you test Apex REST services. To simulate a REST request, 
create a RestRequest in the test method, and then set properties on the request 
as follows. You can also add params that you “pass” in the request to simulate 
URI parameters.

// Set up a test request
RestRequest request = new RestRequest();

// Set request properties
request.requestUri =
    'https://yourInstance.salesforce.com/services/apexrest/Cases/'
    + recordId;
request.httpMethod = 'GET';

// Set other properties, such as parameters
request.params.put('status', 'Working');

// more awesome code here....
// Finally, assign the request to RestContext if used
RestContext.request = request;

If the method you’re testing accesses request values through RestContext, assign 
the request to RestContext to populate it (RestContext.request = request;).

1. In the Developer Console, select File | New | Apex Class.

2. For the class name, enter CaseManagerTest and then click OK.

3. Replace the autogenerated code with the following class definition.

@IsTest
private class CaseManagerTest {

    @isTest static void testGetCaseById() {
        Id recordId = createTestRecord();
        // Set up a test request
        RestRequest request = new RestRequest();
        request.requestUri =
            'https://yourInstance.salesforce.com/services/apexrest/Cases/'
            + recordId;
        request.httpMethod = 'GET';
        RestContext.request = request;
        // Call the method to test
        Case thisCase = CaseManager.getCaseById();
        // Verify results
        System.assert(thisCase != null);
        System.assertEquals('Test record', thisCase.Subject);
    }

    @isTest static void testCreateCase() {
        // Call the method to test
        ID thisCaseId = CaseManager.createCase(
            'Ferocious chipmunk', 'New', 'Phone', 'Low');
        // Verify results
        System.assert(thisCaseId != null);
        Case thisCase = [SELECT Id,Subject FROM Case WHERE Id=:thisCaseId];
        System.assert(thisCase != null);
        System.assertEquals(thisCase.Subject, 'Ferocious chipmunk');
    }

    @isTest static void testDeleteCase() {
        Id recordId = createTestRecord();
        // Set up a test request
        RestRequest request = new RestRequest();
        request.requestUri =
            'https://yourInstance.salesforce.com/services/apexrest/Cases/'
            + recordId;
        request.httpMethod = 'GET';
        RestContext.request = request;
        // Call the method to test
        CaseManager.deleteCase();
        // Verify record is deleted
        List<Case> cases = [SELECT Id FROM Case WHERE Id=:recordId];
        System.assert(cases.size() == 0);
    }

    @isTest static void testUpsertCase() {
        // 1. Insert new record
        ID case1Id = CaseManager.upsertCase(
                'Ferocious chipmunk', 'New', 'Phone', 'Low', null);
        // Verify new record was created
        System.assert(Case1Id != null);
        Case case1 = [SELECT Id,Subject FROM Case WHERE Id=:case1Id];
        System.assert(case1 != null);
        System.assertEquals(case1.Subject, 'Ferocious chipmunk');
        // 2. Update status of existing record to Working
        ID case2Id = CaseManager.upsertCase(
                'Ferocious chipmunk', 'Working', 'Phone', 'Low', case1Id);
        // Verify record was updated
        System.assertEquals(case1Id, case2Id);
        Case case2 = [SELECT Id,Status FROM Case WHERE Id=:case2Id];
        System.assert(case2 != null);
        System.assertEquals(case2.Status, 'Working');
    }    

    @isTest static void testUpdateCaseFields() {
        Id recordId = createTestRecord();
        RestRequest request = new RestRequest();
        request.requestUri =
            'https://yourInstance.salesforce.com/services/apexrest/Cases/'
            + recordId;
        request.httpMethod = 'PATCH';
        request.addHeader('Content-Type', 'application/json');
        request.requestBody = Blob.valueOf('{"status": "Working"}');
        RestContext.request = request;
        // Update status of existing record to Working
        ID thisCaseId = CaseManager.updateCaseFields();
        // Verify record was updated
        System.assert(thisCaseId != null);
        Case thisCase = [SELECT Id,Status FROM Case WHERE Id=:thisCaseId];
        System.assert(thisCase != null);
        System.assertEquals(thisCase.Status, 'Working');
    }  

    // Helper method
    static Id createTestRecord() {
        // Create test record
        Case caseTest = new Case(
            Subject='Test record',
            Status='New',
            Origin='Phone',
            Priority='Medium');
        insert caseTest;
        return caseTest.Id;
    }          

}

4. Press CTRL+S to save.

5. Run all the tests in your org by selecting Test | Run All.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License