Accessing Beehive via JavaScript

The Beehive Developer Kit RESTful API can be embedded easily in JavaScript to allow custom access to Beehive to carry out tasks that are not immediately available through the current clients.

We have several examples in production to allow one-click Calendar entry creation and the ability to create a "Public" folder accesible from tools like Aria. With a simple bit of html to allow the entry of the necessary paramaters we can access a wide range of API capabilities. The links to the pages that explain how to use these tools are:

The way these JavaScripts work is similar so the best way to show how the system works is to walk through one of them.

The create "Public Folder" code does the following:

  1. Logs into Beehive.
  2. Gets the user's Personal Workspace.
  3. Gets the default Documents folder details.
  4. Creates a new sub-folder in this directory.
  5. Sets the necessary Access Controls to make it Publicly accessible.
  6. Logs out of Beehive.

The script relies on several standard Javascript libraries available via Google searches. They include JQuery-1.4.2.js as a helper library, Json2.js as a marshaller/unmarshaller and Base64.js to encode/decode the URLs. In addition there is a BeehiveUtils.js file that contains some of the functions we have in common for all JavaScript code, xmlHttpRequest setup, login calls etc. This is available from http://www.oracle.com/technetwork/middleware/beehive/javascript-bdk-094791.html#JAVASCRIPTBDKCLIENTS - the versions of some of the libraries on this page has been superseded and newer ones may be available. 

The basic way the API works can be eaily described in simple terms. There is a login, the setting of some parameters to prevent cross-scripting and then the system is open for business - calls can be made and returned in JSON format.

Login requires a username and password to be supplied and then the call can be created - making sure ti encode the username and passowed for BASIC authentication. The code snippet would be something like the following - it assumes the code is being run from the system containing the Beehive instance - hence we will ignore the cross domain issues that would occur otherwise. Note: If you have no access to the htdocs area of the Apache web server you could put this code in a workspace and call it from inside the Beehive system via the webDAV  URL.

                var xmlHttpRequest = bhUtils.getXMLHttpRequest();
                xmlHttpRequest.open("POST", "/comb/v1/d/session/login", false);

                // Base64 encode the username and password

                var str = Base64.encode(username + ":" + password);
                xmlHttpRequest.setRequestHeader("Authorization", "Basic " + str);
                xmlHttpRequest.setRequestHeader("Content-Type", "application/json");
                xmlHttpRequest.setRequestHeader("Accept", "application/json");
                xmlHttpRequest.send(null);

                if (xmlHttpRequest.status != 200) {
                    alert("Login Failed, possibly invalid username or password");
     init();
                }
                else {
                    anticsrf = this.bhUtils.deserializeJSON(xmlHttpRequest.responseText)
                    return anticsrf;
                }

The code does the following:

  1. Create a new instance of the xmlHttpRequest and open a POST call to the session/login method.
  2. Encode the username and password using the Base64 code.
  3. Set the necessary header settings for Authorization type and to make sure it expects and returns JSON formatted information.
  4. Make the call with no payload and then check for the successfull completion - normmaly an HTTP return code of 200.
  5. If the login is successful we then extract the antiCSRF token (All calls to POST, PUT, and DELETE method calls require an anti-CSRF token. This anti-CSRF token guards against cross-site request forgery (CSRF) attacks.) Note: GET calls do not need the token attached to the call as they are read-only operations.

We are now logged into Beehive and can get on with doing what we need to do to complete the application. We need to get the personal Workspace and as there is only one and it is assocuiated with the user who has just logged in we can use the "my/workspace" call to get the details - as we are navigating to an entity within an entity this will consist of the Enterprise ID of the intermediate object. This gives us the id of the object to further query for the real target.

The calls are similar but this time it is a GET call we setup in the xmlHttpRequest:

                var xmlHttpRequest = bhUtils.getXMLHttpRequest();
                xmlHttpRequest.open("GET", "/comb/v1/d/my/workspace", false);
                xmlHttpRequest.setRequestHeader("Content-Type", "application/json");
                xmlHttpRequest.setRequestHeader("Accept", "application/json");
               
                xmlHttpRequest.send(null);
               
                if (xmlHttpRequest.status != 200) {
     replaceResponse("");
                    alert("Failed to get Personal Workspace " +
                    xmlHttpRequest.responseText);
                }
                else {
                    var personalWorkspace = bhUtils.deserializeJSON(xmlHttpRequest.responseText);
                    parent = personalWorkspace.collabId;
                }

 So we have deserialized the response and then have grabbed the BeeId of the workspace to be used when we make the new folder - it will be the container of the folder as we want this at the top level.

    "collabId": {
        "beeType": "beeId",
        "resourceType": "wspr",
        "id": "334B:3BF0:wspr:97F82F8445F94BE8BCEA06D446E9276A000000000227"
    }

The EID is 334B:3BF0:wspr:97F82F8445F94BE8BCEA06D446E9276A000000000227  consist of 4 parts "334B" is the Enterprise Id, "3BF0" is the Organization Id, "wspr" is the entity type - in this case a Personal Workspace and "97F82F8445F94BE8BCEA06D446E9276A000000000227" is the Id of the object itself. There is another part that is the snapshot id used to check if the object has changed since it was last called - we don't need it so have not coded to retrieve it.  The complete EID uniquely identifies the object in the Beehive system and can then be used in calls like the HeterogeneousFolderCreator call (Heterogeneous Folders are those that can conatain mixed artifacts - emails documents folders etc. )

This call is a POST type with a payload so is yet another type we have not yet seen. the payloa is the metadata detail of the call and contains in this case the location we want the folder to be and the name. A quirk of the RESTful approach is that the Heterogeneous FolderCreator has a Heterogeneous Folder Update within it - in this case it is a place holder with no metedata of its own.

The POST call requires the antiCSRF token be attahced to the call and this is done by extending the URL as follows: + "?antiCSRF=" + urlEncode(anticsrf.token);

As we have a payload for the call - the previous ones have been null - we have to serialize the JSON content to allow them to be passed as the body of the http call. Once the call is made we await the response and if it is not successful check if it just because the folder already exists - if it does pop-up an alert to that effect.

We check the result and extract the id of the new folder as a check that it has been created successfully  and to use in future calls to set the Access Controls.

var resourceURI = "/comb/v1/d/afrh" + "?antiCSRF=" + urlEncode(anticsrf.token);
               
                var HeterogeneousFolderCreator = {
                    beeType: "heterogeneousFolderCreator",
                    name: name,
                    parent: parent,
                    updater: {
                        beeType: "heterogeneousFolderUpdater"
                    }
                };
               
                xmlHttpRequest.open("POST", resourceURI, false);
                xmlHttpRequest.setRequestHeader("Content-type", "application/json");
                xmlHttpRequest.setRequestHeader("Accept", "application/json");
                var payload = bhUtils.serializeJSON(HeterogeneousFolderCreator);
                xmlHttpRequest.send(payload);

                if (xmlHttpRequest.status != 201) {
                    var failure = bhUtils.deserializeJSON(xmlHttpRequest.responseText);
     if (failure.cause = "the specified entity already exists.") {
      replaceResponse("");
      alert("The Aria Public Folder already exists");
      return
     }
     else {
      replaceResponse("");
      alert("Create Folder Failed: " + xmlHttpRequest.responseText);
     }
                }
                else {
                    var result = bhUtils.deserializeJSON(xmlHttpRequest.responseText);
                    var folderEID = result.collabId.id;
                }

 

We have now created a folder called "Aria Public Folder" in the Personal Workspace - at the same level as the INBOX and the Documents folder.

We now have to open up this folder to a wider audience to allow it to be "Public". The way Beehive works limits the level of "Public" exposure as there is a caveat that to access any resource whatsoever in Beehive the user has to be an authenticated user of the system.

The "Public" piece is supplied by the membership of the ALL_USERS group - all registered members of the system are members of the ALL_USERS group and awarding this group acess rights effectively awards every member of the group with the Access right. So in our case we want to allow any member of the ALL_USERS group to Discover and Read the contents of the folder but deny them the right to Write, Delete or Execute any content. (Note: Discover is the ability to find the content using the search system).

To award any rights to a group we need to find the group Id and we are fortunate that the ALL_USERS group is special and can be found directly by using the "agrp" method. As we only want the id of the group to use in another call we don't need any other details so we use the EMPTY projection to return the minimum content from the call. The calls structure should be getting more familiar and we will dispense with the setting of the xmlHttpRequste properties and just show the call. The "agrp" call is a GET call so we don't need the antiCSRF token and we have set a projection by attaching "projection=EMPTY" to the URL.

xmlHttpRequest.open("GET", "/comb/v1/d/agrp?projection=EMPTY", false);

From the call we get the id of the group and then construct the Access Control request payload

                var allUsersGrpId = allUsersGrp.collabId.id;
               
                var acePayload = {
                    beeType: "accessControlFieldsUpdater",
                    localACL: [{
                        beeType: "ace",
                        accessTypes: {
                            beeType: "accessTypes",
                            denyAccessTypes: ["WRITE", "DELETE", "EXECUTE"],
                            grantAccessTypes: ["DISCOVER", "READ"]
                        },
                        accessor: {
                            beeType: "beeId",
                            id: allUsersGrpId
                        }
                    }]
                };

 

Notice we have to explicitly set the allow and deny settings to complete the picture - there is no assumption made in the call as to the default state being allow or deny - you have to spell it out. The ace mentioned in the call is an Access Control Entry and this is in the local Access Control List.

The call we use is a PUT so requires the antiCSRF token attached and we have added another URL set property to make sure we do not think a return call of 20* is a failure - certain web operations can return 201 and we don't want this to be misinterpreted so we set the "&suppress_20x_code=true" to translate these return codes to 200. Note: the "&" prefix  is used for the second and any subsequent entry after the first parameter which uses the "?" prefix  - this is standard Servlet syntax.

The complete call is:

xmlHttpRequest.open("PUT", "/comb/v1/d/afrh/ac/" + folderEID + "?antiCSRF=" + urlEncode(anticsrf.token) + "&suppress_20x_code=true", false);

We are passing in the folder EID and using the Access Control method of the heterogeneous folder method "afrh/ac/" to set the ACL.

That is the job done but we have to tidy up after us.

We now have to  log out of the system  to destroy the antiCSRF token. The logout call is a POST so need the antiCSRF but does not have a payload - the call setup looks like:


var logoutURI = "/comb/v1/d/logout" + "?antiCSRF=" + urlEncode(anticsrf.token);

xmlHttpRequest.open("POST", logoutURI, false);

xmlHttpRequest.send(null);


Now we are done.