Application integration APIs

This section describes ways in which you can expose your application's business logic functionality encapsulated within your Service layer.

Note

In some cases, Developer X is able to achieve such functionality through the standard Salesforce APIs. However, depending on the requirement, it might be easier and safer to call an API that exposes an existing business logic within the application. You can decide to do this for the same reasoning you would create a custom UI rather than expect the end users to utilize solely the standard UI (as using your objects directly requires them to understand the application's object schema in more detail).

Providing Apex application APIs

If your Service layer is developed and tested as robustly as possible following the guidelines discussed in the earlier chapter, it is worth considering exposing it to Developer X by simply updating the class, methods, members, properties, and any custom Apex types such as global. This part of the chapter discusses the approach in more detail, though it can also be applied to dedicated Apex classes built specifically to expose an API that is delegating to your Service layer classes:

global class ComplianceService 
{ 
  global static void verify(Set<Id> recordIds)
  {

Tip

Make sure that you apply the global modifier carefully as it is not easy to deprecate it after your package has been released. Also, be sure to apply global to aspects of your Service layer only, avoid exposing Domain or Selector layer classes or types. If you need to expose behavior in these classes, write a Service layer method to do so. Also, note that the ComplianceService.ICompliant interface has not had the global modifier applied, as this is an internal aspect to the service.

As shown earlier in this chapter, we have also applied the global modifier to the Apex exception class defined within the ComplianceService class.

To try out this new application API, we will perform the following steps and execute the sample Apex test code introduced below in your test development org:

  1. Apply the sample code for this chapter to your packaging org.
  2. Comment out the ComplianceService.report method (we will uncomment this shortly before performing another upload).
  3. Upload a release of the package and install it in your test development org.
  4. Open the Developer Console and create an Apex class called ApplicationAPITest.

If you inspect the ComplianceService class in your test development org from the Apex Classes page under the Setup menu, you can see the new API methods and types you've just exposed are now visible (without the code). This provides a basic means for Developer X to learn about your API, in addition to any documentation you provide. A portion of how this looks for ComplianceService is shown in the following screenshot.

You can also see a Version drop-down box, which allows Developer X to explore different versions of the API definition. Note that the version shown might differ from yours depending on how many uploads of the package you have done at this point in the book:

Providing Apex application APIs

All global Apex classes will also show the Security link next to them in the list of classes, in the subscriber org. This is so that the subscriber org administrator can control permissions around the execution of the API's through Profiles. Note this only applies to global controller methods custom or extension controller or Web Service calls by Developer X. You can also granted access through appropriate Permission Set's as described in an earlier chapter. Meaning if the user is assigned a Permission Set granting them access to a specific set of features in the application, accessing those through API is included as well.

If you want to control API permissions separately, create separate but functionality scoped permission sets:

Providing Apex application APIs

Calling an application API from Apex

The code used in this section uses Apex test methods (not included in the FormulaForce application). As an easy way to run API code examples, we will be using an Apex Test to illustrate the use of Apex APIs from the sample code included in this chapter. The two test methods we will look at call the compliance API after calling the season API to create some test data (which, of course, only exists on the database within the scope of the test execution).

In the following examples, the Driver records created by the SeasonService.createTestSeason API do not indicate that the drivers hold FIA Super License. Thus, the expectation is that the ComplianceService.verify method will throw an exception reporting this problem. The following example test code asserts this behavior purely for illustration purposes in this chapter:

@IsTest
private class ApplicationAPITest {
    @IsTest
    private static void demoComplianceVerifyAPI() {
        // Create some test data via the SeasonService
        fforce.SeasonService.createTestSeason();
        
        // Query Drivers to test compliance against
        Map<Id, fforce__Driver__c> driversById =
            new Map<Id, fforce__Driver__c>([select Id from fforce__Driver__c]);
        
        try {
            // Invoke the compliance engine for the drivers
            fforce.ComplianceService.verify(driversById.keySet());
            System.assert(false, 'Expected failures'),
        } 
        catch (fforce.ComplianceService.ComplianceException e) {
            // Check the results
            System.assertEquals('4.1', e.failures[0].complianceCode);
            System.assertEquals('Driver must have a FIA Super License.',e.failures[0].failureReason);
        }
    }
}

Note

The namespace used in this sample is fforce. Your namespace will differ. Replace the namespace to get the code sample to compile (you can also find this class in the sample code for this chapter).

Run the test to confirm whether the API is working as expected.

Tip

As you have used the same Service layer method as used by the rest of the application, your existing unit and integration tests around this class should suffice in providing you confidence in the robustness of this as an API to your application. If not, improving your testing around your Service layer benefits both external and internal callers equally.

Notice that when you click on the Show Dependencies button in the ApplicationAPITest class, it shows the API and Custom Objects being referenced from the package:

Calling an application API from Apex

Modifying and depreciating the application API

Once a global class method or member has been published by including it in a released package, it cannot be removed or changed. For example, try changing the ComplianceService.verify method by renaming it to verification (for example). When you attempt to save the class, you will receive the following error:

(ComplianceService) Global/WebService identifiers cannot be removed from managed application: Method: void verify(SET<Id>)  (Line: 1)

Tip

If you want to perform some testing or distribute your package while reserving the ability to make changes to your new APIs, upload your package as a beta until you are happy with your API definition. Note that you can of course change the behavior of your API (determined by the Apex code behind it) at any time, though this has to be done with care.

The @Depreciated annotation can be used to effectively remove from visibility the class, interface, method, or member that global has been applied to from the perspective of subsequent package version consumers. Developer X can still access the deprecated items by referencing an earlier version of the package through the class version settings shown via the Version Settings tab as described in more detail in the following sections:

Note

At the time of writing this, the @Depricated flag cannot be used in developer orgs that are unmanaged. In a later chapter, we will discuss a team development model that permits the development of your application through managing your code within Source Control system and separately managing developer orgs per developer. While this style of development has a number of benefits, it does exclude the use of the @Depricated flag, as such orgs are effectively unmanaged.

Versioning Apex API definitions

When saved, the ApplicationAPITest class, the Version Settings tab, and the Dependencies page reflects the fact that code within the class is referencing the ComplianceService and SeasonService classes from the FormulaForce package.

In the following screenshot, the version is shown v1.8 as this was the current version of the package when the Apex class was first created (this might be different in your case, depending on how many times you have uploaded the package so far):

Versioning Apex API definitions

In the following steps, we will release a new version of the package with a new Compliance API to explore how the platform provides Apex API versioning:

  1. In the packaging org, uncomment the ComplianceService.report method. This method works like the verify method but will not throw an exception; instead, it returns a full list of results including compliance passes and failures.
  2. Upload a new release of the package and install it into your test development org.
  3. In the test development org, open the Apex class ApplicationAPITest.

Attempt to add the following new test method and save the class:

    @IsTest
    private static void demoComplianceReportAPI() {
        // Create some test data via the SeasonService
        fforce.SeasonService.createTestSeason();
        
        // Query Drivers to test compliance against
        Map<Id, fforce__Driver__c> driversById =
            new Map<Id, fforce__Driver__c>(
               [select Id from fforce__Driver__c]);
            
        // Update the licenses for the drivers
        for(fforce__Driver__c driver : driversById.values())
            driver.fforce__FIASuperLicense__c = true;
        update driversById.values();
            
        // Invoke the compliance engine to verify the drivers
        List<fforce.ComplianceService.VerifyResult>reportResults =             fforce.ComplianceService.report(driversById.keySet()); 
            
        // Check the results
        System.assertEquals('4.1',reportResults[0].complianceCode);
        System.assertEquals(true, reportResults[0].passed);
        System.assertEquals(null,reportResults[0].failureReason);
    }

You should get an error, indicating that the method does not exist:

Compile Error: Package Visibility: Method is not visible: fforce.ComplianceService.report(SET<Id>) at line 21 column 13 

This is due to the package version information in the Apex class metadata of the test class still referencing the release of the package installed at the time the code was originally written. Upgrading the package does not change this. Open the Version Settings tab and change to the release you have just installed to fix this compile problem

Versioning Apex API definitions

The preceding steps have illustrated how the Apex runtime manages versioning of the Apex APIs you expose from your package. Thus, the requirement of versioning API definition described earlier has been provided by the platform for you. There is no need to manage this yourself, though you do need to be aware of how it works.

Finally, revisit the ComplianceService Apex class page from within your test development org and notice that the available versions of this class has also increased to include the version just uploaded. Toggle between them; you can see the changes that the API definition has undergone between the last two package releases we have just performed.

Select All Versions to get a full summary of which methods arrived at which package version. You can see in the following screenshot that the verify method was first available in v1.8 and the report method in v1.9; again, your version numbers might differ:

Versioning Apex API definitions

Versioning Apex API behavior

The API definition is not the only aspect that can undergo changes between releases. Even if the definition stays the same, the code behind it can change the behavior of the API. The Apex runtime provides a means for the packaged code to determine which version the calling code written by Developer X is expecting. This can be obtained by calling the System.requestVersion method. With this information, your packaged code can modify its behavior according to the expectations of the calling code.

As you release more versions of your package and the functionality grows, the management of this type of code becomes more complex. Also note that this feature is only available in the code located within the packaging org or specifically managed code. In the standard developer org, this method will throw a runtime exception. This essentially rules out its use if you plan on relocating your code outside the packaging org, for example, by following the team development model discussed in the last chapter of this book.

Another way of versioning behavior is by considering whether changes in your application logic are something that Developer X would want to opt in to or be immediately available to their existing code once the package is upgraded. Changes to fix bugs or minor improvements in the way information is processed are likely things you wish to be available, regardless of the API version. However, other changes such as the way values are calculated or the addition of significantly new processing would be something that might justify a new API method or a parameter for Developer X to modify their code to use. Much like in the UI, you will choose to add a new tab, page, or button.

By considering when to version the API behavior, you have put the choice as to when to accept new API behaviors in the hands of Developer X. Critically, by considering backwards compatibility you have not blocked upgrading to the latest version of your package due to integration failures. Spotting backwards compatibility regressions caused by behavior changes will get harder the more versions you release and the more supported versions you have in your customer base. Therefore making sure that you have a strong API-testing strategy becomes all the more critical.

Providing RESTful application APIs

So far we have focused on Developer X being an on-platform Apex developer and the ways in which you can provide application APIs via Apex. However, it is also possible that Developer X could be coding off-platform, needing to call your application APIs from Java or Microsoft .NET, or a mobile application. Perhaps they want to write a small dedicated mobile application to check the compliance for teams, drivers, or cars.

This part of the chapter will focus on exposing a REST API of the preceding compliance API for use by off-platform developers writing integrations with your application. Don't forget, as with the on-platform scenario Salesforce provides a number of standard Salesforce APIs via SOAP and REST, which by coding off-platform Developer X can use to create, read, update, and delete records held in your application's Custom Objects.

Note

Apex provides support to expose the application logic via web services (SOAP), through the use of the webservice keyword placed in your Apex classes. If you want to expose a SOAP API, the platform can generate the appropriate WSDLs once you have applied this keyword to methods on your Service classes. The design of services designed in this book happens to align quite well with the general requirements of web services.

Note that the afore mentioned @Depricated attribute cannot be used on Apex code exposed in this way. One thing to consider though is that the WSDLs generated are not versioned; they are only generated based on the code for the installed version of your package. Developer X cannot generate historic versions of it from previous package versions. While Apex methods that use the webservice keyword fall under the same restrictions as global, you cannot exclude new methods or fields in your web services by version.

Apex also provides support to create your own REST APIs, with some great reference documentation within the Apex Developer's Guide. However, what it doesn't cover are REST API design considerations, often referred to as RESTful.

Many mistakes are made in exposing REST APIs without first understanding what it means to be RESTful. Before we get into using your Service layer to deliver a REST API, let's learn more about being RESTful.

Key aspects of being RESTful

A full discussion and overview of RESTful API design could easily take up a full chapter. The following is a summary of the key RESTful design goals and how these apply to REST APIs delivered via Apex (some guidelines are handled for you by the platform). As we discuss this topic further, the use of the term operation represents the methods in the application's Service layer classes. This will be used to help expose the application's REST API but should not steer its design.

Tip

There is no one list of guidelines and some are open to interpretation. However, if you enter Designing a RESTful API in Google, you're sure to come up with some useful reading. I recommend that you focus on those articles that give insight as to how some of the larger services such as Facebook, LinkedIn, and Salesforce themselves are interpreting RESTful, such as http://www.apigee.com/.

What are your application resources?

In the RESTful world, your operations are associated with a resource that is expressed via Uniform Resource Identifier (URI). A resource can be a physical thing in your application such as a Custom Object, or a conceptual thing. This is different from the Service layer design that drove the Apex API we have looked at earlier, where operations are expressed by grouping them under functional areas of the application, which are then expressed by Apex Service classes and methods for each operation.

The Salesforce REST API uses Custom Objects to define physical resources the REST URI uses, allowing you to build a URI and use the various HTTP methods to perform the create, read, update, and delete operations. For example, to retrieve a record you can issue the following request via the Developer Workbench tool:

What are your application resources?

In cases where you are exposing more business process or task-orientated operations that are not data-centric, it can be difficult to decide what the resource name should actually be? So it is worth taking the time to plan out and agree with the whole product team what the key REST resources are in the application and sticking to them.

One operation that falls into this category in our FormulaForce package is the compliance report. This operates on a number of different Custom Object records to report compliance, such as the driver, car, and team. Should we define a URI that applies it to each of these physical resources individually, or create a new logical compliance resource?

I chose the latter in this chapter because it feels more extensible to do it this way, as it also leverages the compliance service in the application, which already supports adding new object types to the compliance checker. Thus, the REST API will support new object types without future changes to the API, as they are added to the compliance checker.

Salesforce dictates the initial parts of the URI used to define the resource and the developer gets to define the rest. For the compliance REST API, we will use this URI:

/services/apexrest/compliance

Once you package your Apex code implementing this REST API, you will find that the preceding URI needs to be qualified by your package namespace, so it will take on the format, shown as follows, from the point of view of Developer X, where fforce is my namespace. We will use this format of URI when we test the compliance REST API later in this chapter:

/services/apexrest/fforce/compliance

Mapping HTTP methods

Your application operations for a given resource URI should be mapped to one of the standard HTTP methods on which REST is based: GET, POST, PUT, PATCH, and DELETE. Choosing which to use and being consistent about its use throughout your REST API is vital. Again, there are many resources on the Internet that discuss this topic in much more detail. Here is a brief summary of my interpretation of those readings:

  • GET: This method is typically for operations that query the database and whose parameters are easily expressed in the URI structure and/or whose parameters fit well with this method.
  • PUT versus POST: These methods are typically for operations that create, update, or query the database and where parameters are more complex, for example lists or structures of information that cannot be reflected in URI parameters. There is also a consideration around the term Idempotent (check out Wikipedia for a full definition of this), which relates to whether or not the request can be repeated over and over, resulting in the same state in the database. As you dig deeper into the RESTful design guidelines, this term typically drives the decision over using PUT versus POST.
  • PATCH: This method is typically for operations that solely update existing information, such as a record or some other complex dataset spanning multiple records.
  • DELETE: This method is, of course, to delete existing information expressed in the URI.

We will use the POST HTTP method with the /services/apexrest/compliance resource URI to implement our REST API for the compliance verify operation, passing the list of records to verify as a part of the HTTP request body using the JSON data format.

Providing Apex REST application APIs

The following Apex class follows the naming convention [ResourceName]Resource as the class name and then defines the appropriate methods (whose names are not as important in this case), mapping them accordingly to the HTTP method for the URL mapping. This is included in the sample code of this chapter:

@RestResource(urlMapping='/compliance')
global with sharing class ComplianceResource {
  @HttpPost
  global static List<ComplianceService.VerifyResult> report(List<Id> Ids) {
    return ComplianceService.report(new Set<Id>(Ids));
  }
}

The following are some things to note about the preceding example, and in general about wrapping Service layer methods in Apex REST methods to create REST APIs:

  • The preceding example uses the VerifyResult class from ComplianceService. You may wish to create your own class scoped within the ComplianceResource class for further isolation.
  • The methods in this class should delegate to one and only one, applicable method in a Service class as per the general Service layer calling guidelines, to maintain a transaction scope and also Separation of Concerns.
  • It is not recommended that you map your Apex resource classes only to a specific Service class as your earlier RESTful API design might not match your Service class design, though it should still delegate to it.
  • Treat these classes like you would treat a UI controller, including only marshaling and error-handling logic as required.
  • Typically you don't need to catch exceptions from the Service layer (unlike Visualforce controller action method callers), as the Apex REST runtime catches the exception and the returns appropriate REST API error code and message. Avoid implementing your own REST API error handling.
  • The global modifier is still required. Keep in mind that the same packaging restrictions regarding changes to these methods apply.
  • It is possible for the Developer X Apex developer to call these methods from Apex, as they are global. However, this should be discouraged because these methods might reference the REST API context variables, which will not be initialized in a standard Apex context.
  • The Set Apex data type is not supported by the Apex REST methods. Unlike the standard, which has been established with the Service class methods, the Set Apex data type is not supported by the Apex REST methods. Therefore, you will need to convert the List<Id> REST method parameters to the Set<Id> collections for your Service method calls.
  • The ComplianceService.verify method throws an exception with additional information about the specific compliance failures. However, information in a custom Apex exception is not automatically serialized by the platform—only the error message is included. In this case, a custom response would be required to catch the exception and return the information.

Calling your Apex REST application APIs

The Salesforce Developer Workbench can be used to call the preceding REST API. Locate a Driver record without the FIA Super License field selected and use the following ID. Note that the URI shown in the following screenshot contains the package namespace, fforce. To try this out you need to replace this with your chosen package namespace:

Calling your Apex REST application APIs

Versioning Apex REST application APIs

The generally recognized way in which REST APIs are versioned is to include the version in the URI. Notice that v30.0 (the Salesforce platform version) was included in the standard Salesforce REST API example shown earlier, but no version was included in the URI for the REST API exposing the compliance report operation. The platform does not provide a facility for versioning Apex REST APIs in the same way as its own APIs.

Behavior versioning

The Salesforce REST API, and any Apex REST APIs exposed, can receive from a new HTTP header, such as x-sfdc-packageversion-fforce: 1.10. In the case of Apex REST APIs, this can be used to allow the packaged API code to implement behavior versioning. This can be used to set the version returned by System.requestVersion as described earlier in this chapter.

This header is optional; without it, the current package version installed is assumed. My preceding recommendations around the utilization of behavior versioning also apply here. Because the header is optional, its usefulness is diluted.

Definition versioning

The preceding HTTP header does not affect the visibility of the API's definition in the same way that the platform does for Apex API's as illustrated earlier in the chapter. One option to address this is to map a versioned URI to a specific Apex resource class per version, for example, to support version v1.0 and v2.0 of the compliance REST API. This approach allows you to expose URI's with your version included:

/service/apexrest/fforce/v1.0/compliance
/service/apexrest/fforce/v2.0/compliance

When versioning the API definition, you would need to also capture copies of the Apex data types used in the Service layer. This would isolate each REST API version from underlying changes of the Service layer as it evolves. The main coding overhead here is going to be marshaling between the Apex types defined in the Service layer (which are always the latest version) and those in the class implementing the REST API.

For example, to version the report API, we have to capture its types as inner Apex classes at the time the API class is created. The following example illustrates this for v1.0. You can see how the definition of the API can be encapsulated within the specific version of the Apex class denoted by the v1_0 suffix:

@RestResource(urlMapping='/v1.0/compliance')
global with sharing class ComplianceResource_v1_0 {
  global class VerifyResult {
    global Id recordId;
    global String complianceCode;
    global Boolean passed;
    global String failureReason;
  } 
  @HttpPost
  global static List<VerifyResult> report(List<Id> Ids) {
    List<VerifyResult> results = new List<VerifyResult>();
    for(ComplianceService.VerifyResult result : 
      ComplianceService.report(new Set<Id>(Ids)))
    results.add(makeVerifyResult(result));
    return results;
  }
  private static VerifyResultmakeVerifyResult(ComplianceService.VerifyResult verifyResult){
    VerifyResult restVerifyResult = new VerifyResult();
    restVerifyResult.recordId = verifyResult.recordId;
    restVerifyResult.complianceCode = verifyResult.complianceCode;
    restVerifyResult.passed = verifyResult.passed;
    restVerifyResult.failureReason = verifyResult.failureReason;
    return restVerifyResult;
  }
} 

While the version number you use can be anything you like, you may want to consider following your package version numbering sequence.

Versioning Apex REST APIs this way is not ideal due to the manual way in which the definition has to be versioned by code and the overhead in managing that code for each release of your package. However, without this approach, it is always the latest definition of the Apex REST API in your package that is exposed.

This means that after upgrades of your package callers receive new fields in responses they do not expect. There is also no way to deprecate Apex REST APIs. However due to the deletion restrictions over the global class members, there is some protection over developers in accidentally removing fields that would break existing requests.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.93.141