Jive Connects API - Client Application Configuration and API Usage

Version 2

    1.  Background

     

    Because a Jive application is executing in an Open Social container, it has access to a set of APIs for proxied requests to remote services. Proxying is required because browser sandbox rules allow an application direct  network access only to the host from which the application was loaded. In Open Social versions prior to 0.9, the primary proxy mechanism was  the gadgets.io.makeRequest API, which looked something like this:

     

    var params = {};
    params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
    params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
    gadgets.io.makeRequest("http://www.example.com", callback, params);

     

    Starting with Open Social version 0.9, a new family of API calls in the osapi.http package became available.  A low level call using this API looks like:


    osapi.http.get({
        'href' : 'http://www.example.com',
        'format' : 'json',
        'authz' : 'signed'
    }).execute(callback);


    The basic design pattern for the osapi.http package API includes:

     

    • A method for each commonly used HTTP verb (delete, get, head, post, put) that takes a params argument describing the characteristics of the request to be performed
    • Each of these methods returns an object of type osapi.Request
    • The request object supports an execute() method that accepts a callback function which is called when the HTTP operation completes
    • The callback method will receive a JavaScript object containing the response body
    • If  an error HTTP status (4xx/5xx) was returned by the remote server, the  response object handed to the callback function will have an error field, which contains codeand message fields containing the HTTP status, and error message, respectively.


    While the low level osapi.http package API provides a fully functional HTTP client API, in practice  they present an application developer with several complexities:

    • The  application must deal with absolute URLs to the remote service instance  to be contacted, which implies a requirement to manage configuration of  the base URL for each service.
    • The application must deal with  details of authentication, including a requirement to securely manage  user credentials for each service.


    Jive  Connects is designed to address these complexities, while still providing an API that has the same basic "feel" so that application developers familiar with osapi.httpwill be immediately able to leverage the provided APIs.


    2.  Terminology


    The descriptions of Jive Connects that follows uses several terms that have precise meanings, as follows: Service -- the base URL of a service to which a JAF application might wish to  connect.  The set of services available within a particular Jive  instance are made available by the administrator of that Jive instance via the Admin Console.  The description of each available service includes the following  properties:


    • authStyle -- the type of user authentication required by this service.  Valid options include:    
      • anonymous -- no authentication processing is required to proxy requests to this service
      • apikey -- this service requires an API key that is specific to each user,  present in either a request parameter or an HTTP header included in each  request.
      • basic -- this service requires HTTP Basic authentication, in a manner specified by RFC 2617.
      • apikey+basic -- a combination of both apikey and basic styles.
      • oauth2 -- OAuth 2.0 (currently compatible with Draft 10 of the spec)
    • enabled -- a boolean flag indicating whether access to this service is currently available or not.
    • headers  -- a set of name/value pairs that define HTTP headers which will  automatically be added on each proxied request to this service.
    • lenient -- a flag indicating that, if this is an SSL/TLS service, certificate checking of the remote server should accept certificates not signed by a recognized signing authority.  This should be used only in development/QA environments.
    • serviceURL -- the base URL of this service.  On each individual request, the client will have the opportunity to append a path element that is added to the end of this URL.
    • tags  -- a list of string tags used as identifiers for this service, which  are matched to tags specified in the application configuration as  described below.


    When a service is defined with an authentication style of apikey or apikey+basic, any occurrence of the literal string {apikey} in the serviceURL, or in the value of anyheader, will be replaced (in proxy requests) by the user's stored API key value for this service. When a service has an authentication style of oauth2, several additional properties must be configured for it:


    • Client ID -- the client identifier (might be called consumer identifier or application identifier) assigned to your service by the service provider (typically, each service provider will offer a web form on which you can register "applications").
    • Client Secret -- the client secret (might be callsed consumer secret or application secret) assigned to your service by the service provider.
    • Authentication URL -- the URL defined by the service provider that is used to authenticate a client (by popping up a login form on the remote service, followed by an explicit grant of permissions for the remote application to utilize this service).
    • Access Token URL -- the URL defined by the service provider that is used to request an access token.
    • Redirect Base URL -- Normally, you can leave this field blank (which will default to the URL of your Jive instance).  Some OAuth based services, such as salesforce.com, require a redirect URL that starts with "https" even if the rest of your Jive instance is not using SSL, so this field lets you override the default.

     

    Connection -- the configuration of a particular Service to a particular alias defined by a particular application.  The  description of each connection includes the following properties:

    • alias -- a string identifier (unique within a particular application) for this particular Connection to a particular Service.
    • enabled -- a boolean flag indicating whether access to this connection is currently available or not.
    • service -- information about the actual Service that was configured (at application installation) for this Connection.


    3.  Connection Definition

     

    An application declares that it wishes to use Jive Connects functionality by including an appropriate <Require> element in the gadget.xml file describing the application.  Embedded within this element are  parameters that describe the various connections to services that this  application requires.  The gadget.xml file for the example "Chordia" application looks like this:

     

    <?xml version="1.0" encoding="UTF-8"?>
    <Module>
        <ModulePrefs title="Quotes Approval"
                    author="Jive Software"
               description="Manage pending sales quotations">
            <Require feature="jive-connects-1.0.0">
                <Param name="alias:quotes|title:Quotes Service">jive:service://jivesoftware.com/quotes</Param>
            </Require>
            <Require feature="dynamic-height"/>
            <Require feature="osapi"/>
            <Require feature="views"/>
        </ModulePrefs>
        <Content view="home" type="html" href="quotes_home.html"/>
        <Content view="canvas" type="html" href="quotes_canvas.html"/>
    </Module>

     

    The <Require> element indicates that this application requires the availability of  the Jive Connects (version 1.0.0) API, along with the Open Social API,  and the standard Google Gadget features for dynamically adjusting the  height of a rendered application, and for supporting multiple views.  In  addition, the Jive Connects feature is further configured by a <Param> element for each connection that is needed, each with a unique alias.   The parameter name includes a "title" sub-element, which will be  displayed to the user during the application installation process  described below, and the value of this element  ("com.jivesoftware.api.quotes") identifies one or more tags used to  select possible matching services that have been configured within this Jive instance.

     

    4.  Application Installation and Configuration

     

    When an application that requires Jive Connects connections  is initially installed, Jive will examine each of the aliases that have been  declared in the gadget.xml file as described in the previous section.  The goal is to match up each alias against an existing Service that matches at least one of the specified tags.  When comparing the  tags specified by the application against the tags available on Service definitions that have been set up on this Jive instance, there will be one of three possible results for each defined alias:

     

    • More than one matching service is found -- the user will be offered a choice of which matching service should be chosen for this alias.
    • Exactly one matching service is found -- the user will be notified that the matching service has been selected automatically for this alias.
    • No matching service is found -- application installation and configuration will fail, because no available service matches the required tags.


    The first time that the application attempts to send a request to the connection for a particular alias,  Jive will determine whether or not the user installing this application  has previously recorded required credentials (based on the configured  authentication style) for the associated service.  If so, the existing credentials  will be used automatically (saving the user from the frustration of having to enter his or her credentials multiple times for multiple applications that use the same service).  Otherwise, the user will be requested to enter the required credentials once, and these credentials will be used from then on for all application access by this user to the same service.


    5.  Application APIs


    When  you include the Jive Connects feature in your application, the  following JavaScript APIs are automatically made available.


    5.1 Performing Requests to Configured Connections

     

    The  Jive Connects API defines the following methods to perform HTTP  requests against one of your configured connections.  There is one  method for each supported HTTP verb:


    • osapi.jive.connects.delete(params) -- Return a request instance that will execute an HTTP DELETE call.
    • osapi.jive.connects.get(params) -- Return a request instance that will execute an HTTP GET call.
    • osapi.jive.connects.head(params) -- Return a request instance that will execute an HTTP HEAD call.
    • osapi.jive.connects.post(params) -- Return a request instance that will execute an HTTP POST call.
    • osapi.jive.connects.put(params) -- Return a request instance that will execute an HTTP PUT call.

     

    The parameters that may be included in the params argument to each of these calls are as follows:


    • alias -- The application defined alias for the Jive Connects connection this request should contact.  This parameter is required.
    • body -- The request body for this request (POST or PUT requests only).
    • format -- The format of the expected request body ("text" or "json").  In the latter case, the response content element will be converted into a JavaScript object for you.  Default value is "json".
    • headers -- A hash of HTTP headers (key is header name, value is an array of  String values for this header name) to add to this request.  By default,  no headers are added.
    • href -- The partial path to be  appended to the service URL.  If present, must be an empty string ("")  or a path starting with a slash ("/") character.  Default value is "".
    • params -- A hash of request parameters to be added to the absolute URL (key is  parameter name, value is an array of String values for this parameter  name).  By default, no request parameters are added.
    • refreshInterval -- The maximum number of seconds that the proxy server can cache the  response to this request.  By default, no caching takes place.

     

    The request object returned from any of the methods described above will support an execute() method to perform the actual I/O for this request.  This method accepts acallback parameter, which will receive as its parameter a response object with the following properties:


    • content -- The response body, if any, as a string if format is "text", or as a JavaScript object if format is "json".
    • error -- If an error occurred on this request, this property will exist, and will contain subproperties code (an error code) and message (an explanatory message describing the problem).  If this property does not exist, the request was successful.
    • headers -- A hash of HTTP headers (key is header name, value is an array of  String values for this header name) that were returned with this  response.
    • status -- The HTTP status code returned by the remote service.

     

    Putting everything together, your application might include code like this:

     

    function loadQuotes() {
        osapi.jive.connects.get({
            alias : 'quotes',
            headers : { 'Accept' : [ 'application/json' ] },
            href : '/quotes'
        }).execute(function(response) {
            if (response.error) {
                // Deal with an error scenario ...
            }
            else {
                // response.content is an array of information for each quote
                var html = "";
                $.each(response.content, function(index, quote) {
                    html += ... // Generate appropriate table row HTML for each quote
                });
                $("#table-body").html(html);
                gadgets.window.adjustHeight();
            }
        })
    }

     

    In this example, we see several elements working together:

     

    • We want to use the Jive Connects connection with an alias "quotes" to perform this request.
    • Because the "format" parameter was omitted, the default value ("json") means we will be getting back a JavaScript object.
    • We  know that our service supports a "/quotes" path (to be appended to the  service URL), which will return us a JSON array containing information  about each available sales quote.
    • We do not specify anything related to authentication of the individual user ... that is handled for us by Jive Connects.

    5.2 Credentials Reconfiguration for a Connection

     

    When  the application end user first installs an application, he or she will  decide which service to use for each connection, but Jive will not ask  the user for credentials (for a service) until that service is first  accessed.  The need for credentials will be signaled by an HTTP 401  (Unauthorized) response returned to the application, so we need a way to  trigger collecting the user's credentials and re-performing the I/O  request.  To do so, let's expand the previous example slightly:

     

    function loadQuotes() {
        osapi.jive.connects.get({
            alias : 'quotes',
            headers : { 'Accept' : [ 'application/json' ] },
            href : '/quotes'
        }).execute(function(response) {
            if (response.error) {
                if (response.error.code == 401) {
                    // Request Jive to configure (or reconfigure) credentials for this connection
                    osapi.jive.connects.reconfigure("quotes", response, function(feedback) {
                        loadQuotes(); // Resubmit the failed request
                }
                else {
                    // Deal with an error scenario ...
                }
            }
            else {
                // response.content is an array of information for each quote
                var html = "";
                $.each(response.content, function(index, quote) {
                    html += ... // Generate appropriate table row HTML for each quote
                });
                $("#table-body").html(html);
                gadgets.window.adjustHeight();
            }
        })
    }

     

    If your service indicates authentication problems by  means other than an HTTP status code of 401, you will need to adapt the  conditional logic to trigger reconfiguration appropriately.

     

    5.3 Using Batch Requests

     

    Consistent with many other OpenSocial APIs that return request objects,  you may wish to improve performance by batching several requests  together and sending them once to the Jive server, and getting the  responses back all together.  This works for Jive Connects requests  also.  For example, assume we have accumulated an array of customerID  values from the quotes being presented on a particular page, and wish to  retrieve the associated customer information for those customers, but  just once per individual customerID in the case where a single customer  might have more than one associated quote.  For efficiency, we want to  gather all of these requests into a single batch request to the Jive  server, and process them all at once:

     

    // Return a request object to retrieve the specified customer by ID
    function loadCustomer(customerID) {
        return osapi.jive.connects.get({
            alias : 'quotes',
            headers : { 'Accept' : [ 'application/json' ] },
            href : '/customers/' + customerID
        });
    }

    // The customerIDs parameter is an array of unique customer ID values
    function loadCustomers(customerIDs) {
        var batch = osapi.newBatch();
        $.each(customerIDs, function(index, customerID) {
            // Add a batch request with access key "customerNNN" for each ID
            batch.add('customer' + customerID, loadCustomer(customerID));
        });
        // Perform all of the batched requests
        batch.execute(function(responses) {
            customers = [ ]; // "customers" is a global variable to receive the list
            // Iterate through each of the responses
            $.each(customerIDs, function(index, customerID) {
                var response = responses['customer' + customerID];
                // TODO - deal with response.error not being null
                customers.push(response.content);
            });
        });
    }

     

    The moving parts work together like this:

     

    • The application accumulates a list of the unique customerID values it wants to acquire customer information for, and calls loadCustomers().
    • The loadCustomers() method creates a batch of request objects to all be performed together, one per customer, using the loadCustomer(customerID) function to create each of the requests.
      • OpenSocial  batches identify the individual requests in a batch by a key that is  specified by the application.  Here, we are synthesizing keys like  "customerNNN", where "NNN" is the desired customer ID.
    • The batch is executed, and loadCustomers() iterates through the responses.  In the example above, we simply add  the customer information to a global array named "customers".


    As a very convenient extra feature, note that you can "mix and match" requests of various types in a single batch request:


    • Jive Connects API requests as illustrated above.
    • Jive Core API requests (using the low level interface) to access information from your Jive instance.
    • Any OpenSocial compatible request object, such as those returned by the osapi.http family of methods, or other OpenSocial functions that return request objects.

     

    5.4 Using Data Pipelining


    This feature is not yet implemented, but is planned. Sometimes,  it is desireable to use a simple HTML templating approach to process  information from remote services, instead of doing everything in  JavaScript.  OpenSocial defines a feature called data pipelining to support this requirement, and Jive Connects supports similar capabilities. Jive Connects defines a template tag, <jive:ConnectsRequest>, that can be included in the HTML that defines your view.  This tag takes parameters that are similar to the osapi.jive.connects.get() method described earlier:


    • alias -- The application defined alias for the Jive Connects connection this request should contact.  This parameter is required.
    • href -- The partial path to be appended to the service URL.  If present,  must be an empty string ("") or a path starting with a slash ("/")  character.  Default value is "".
    • key -- A variable name that may be used later in your template to refer to the retrieved data.

     

    An example of using this feature would have the following code embedded in the HTML of your view:

     

    <script type="text/os-data">
      <jive:ConnectsRequest key="customers" alias="quotes" href="/customers?offset=0&limit=5"/>
    </script>

     

    When  your view is loaded, Jive will ensure that a GET request to retrieve  the specified data has been completed before your application starts  executing, and you can refer to the retrieved information using the key value that you have specified later in the template.

     

    TODO - example of accessing the retrieved data.

     

    5.5 Acquiring Information About Configured Services

     

    Most of the time, the application developer can remain blissfully unaware of  the details of which service the end user chose to configure for each  of the defined aliases, at installation time.  On occasion, however, it  would be useful to allow the application more insight into which service  was selected.  A common use case for this is where the application has  been coded to adjust its behavior for two different versions of a  service API, but it needs to know the characteristics of the service  that was selected by the end user who installed the app.

     

    To  handle this use case, the application would define multiple tags in its  gadget.xml configuration parameters, one per supported version of the  API.  When the application is installed, the user will be offered any  service (accessible by that user) that matches any of the  specified tags.  Once the selection has been made, the application needs  to know which version of the API that particular service instance  supports.  This can be accomplished as follows:

     

    function loadConnection() {
        osapi.jive.connects.connection({
            alias : 'quotes'
        }).execute(function(connection) {
            // "connection" contains information about the configured connection for this alias
            // "connection.service.tags" is an array of the tags for the selected service
        });
    }

     

    GIven access to the tags for the selected service, the application can adapt its behavior to the service that was selected by the installing user.