X

Step Up to Modern Cloud Development

OSvC BUI Extension - How to create a library extension

Edson Ferreira Da Silva Junior
Senior Product Development Manager

Library is an existing extension type that you can find as part of BUI Extensibility Framework. If you are not familiar with the library concept, it is a collection of non-volatile resources or implementations of behavior that can be invoked from other programs (in our case, across extensions that share the same resources or behaviors). For example, your extension project requires a common behavior such as a method for authentication, global variable, a method for trace/log, and others. In this case, a library is a useful approach because it can wrap all common methods in a single extension that can be invoked from others, it prevents your project from inconsistently repeated methods over different extensions. The following benefits can be observed when this approach is used:

  1. centralized maintenance of core methods;
  2. reduced size of other extensions, which might improve the time of download content;
  3. standardized methods;
  4. and others...
 

Before we can get further, let's see what this sample code delivers. 

  • Library
    • myLibrary.js: This file includes a common implementation of behavior such as a method to trace, to return authentication credentials and to execute ROQL queries.
  • myGlobalHeaderMenu
    • init.html: This file is initializing the required js. ** if you have experience with require.js, probably you are thinking why not use require.js. We can work with require.js in another post. Although, the library concept is still needed.
    • js/myGlobalHeaderMenu.js: This file is creating our user interface extension. We want to see a Menu Header with thumbs-ups icon like we did before. As it is a sample code, we want to have something simple to trigger our methods implemented as a library and see it in action.
 

The global header menu is invoking a trace log and ROQL Query function that was implemented as part of the library sample code. When the thumbs-up is clicked a ROQL Query Statement( "select count(*) from accounts") is passed as a parameter to a function that was implemented as part of the library. The result is presented by another library behavior which was defined to trace any customization. In order to have the trace log function on, we've implemented a local storage item (localStorage.setItem('debugMyExtension',true);) as you can see in the animation gif below.

 

It will make more sense in the next session of this post where you can read the code line with comments to understand the logic under the hood. For now, let's see what you should expect when this sample code is uploaded to your site.

 

Demo Sample Code

 

Here are the sample codes to create a ‘Global Header Menu’ and a ‘Library.’ Please, download from attachment and add each one of the add-is as Agent Browser UI Extension, then select Console for the myGlobalHeaderMenu extension, and init.html as the init file. Lastly, upload myLibrary.js as a new extension (the extension name should be Utilities), then select library as extension type

 

Library

 

Here is the code line implemented in myLibrary.js. Read the code line with comments for a better understanding.

 
/* As mentioned in other posts, we want to keep the app name and app version consistent for each extension. 
Later, it will help us to better troubleshoot and read the logs provided by BUI Extension Log Viewer.*/

var appName = "UtilityExtension";
var appVersion = "1.0";

/*We have created this function in order to troubleshoot our extensions. 
You don't want to have your extension tracing for all agents, so in this sample code, we are using a local storage 
to check whether the trace mode is on or off. In order to have the trace on, with the console object opened 
set a local item as follow; localStorage.setItem('debugMyExtension',true);*/

let myExtensions2log = function(e){
    if (localStorage.getItem('debugMyExtension') == 'true')
        window.console.log("[My Extension Log]: " + e);
}


/* Authentication is required to connect to Oracle Service Cloud APIs. This function returns
the current session token and the REST API end-point, you don't want to have this information hard-coded.*/

let myAuthentication = new Promise(function(resolve, reject){
    ORACLE_SERVICE_CLOUD.extension_loader.load(appName,appVersion).then(function(extensionProvider){
                extensionProvider.getGlobalContext().then(function(globalContext){
                _urlrest = globalContext.getInterfaceServiceUrl("REST");
                _accountId = globalContext.getAccountId();

                                globalContext.getSessionToken().then(
                                   function(sessionToken){
                    resolve({'sessionToken': sessionToken,'restEndPoint': _urlrest, 'accountId': _accountId});
                });

                        });
                });
});

/* This function will receive a ROQL statement and will return the result object. 
With this function, other extensions can send a ROQL statement and receive a JSON object as result.*/

let myROQLQuery = function(param){
    return new Promise(function(resolve, reject){
        var xhr = new XMLHttpRequest();

        myAuthentication.then(function(result){

            xhr.open("GET", result['restEndPoint'] + "/connect/latest/queryResults/?query=" + param, true);
            xhr.setRequestHeader("Authorization", "Session " + result['sessionToken']);
            xhr.setRequestHeader("OSvC-CREST-Application-Context", "UtilitiesExtension");
            xhr.onload = function(e) {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        var obj = JSON.parse(xhr.responseText);                                                                         
                        resolve(obj);
                    } else {
                        reject('myROQLQuery from Utilities Library has failed');
                    }
                  }
            }
            xhr.onerror = function (e) {
                console.error(xhr.statusText);
              };
            xhr.send();
        });

    });
}

 

myGlobalHeaderMenu

 

init.html

This is the init.html file. The important part here is to understand the src path. If you are not familiar with "src path" here is a quick explanation. Notice that each extension resides in a directory and the idea is to work with directory paths.

 

/   = Root directory

.   = This location

..  = Up a directory

./  = Current directory

../ = Parent of current directory

../../ = Two directories backwards

 

In our case, it is ./../[Library Extension Name]/[Library File name]  -> "./../Utilities/myLibrary.js"

 
<!--This HTML file was created to make a call on the required files to run this extension-->

<!--myLibrary is the first extension to be called. This file has the common resources that is needed to run the second .js file-->
<script src="./../Utilities/myLibrary.js"></script>

<!--myGlobalHeaderMenu is the main extension which will create the Global Header Menu and call myLibrary for dependet resources-->
<script src="./js/myGlobalHeaderMenu.js"></script>

 

 

 

js/myGlobalHeaderMenu.js

 
let myHeaderMenu = function(){
    ORACLE_SERVICE_CLOUD.extension_loader.load("GlobalHeaderMenuItem", "1.0").then(function (sdk) {
        sdk.registerUserInterfaceExtension(function (IUserInterfaceContext) {
            IUserInterfaceContext.getGlobalHeaderContext().then(function (IGlobalHeaderContext) {
                IGlobalHeaderContext.getMenu('').then(function (IGlobalHeaderMenu) {
                    var icon = IGlobalHeaderMenu.createIcon("font awesome");
                    icon.setIconClass("fas fa-thumbs-up");
                    IGlobalHeaderMenu.addIcon(icon);
                    IGlobalHeaderMenu.setHandler(function (IGlobalHeaderMenu) {
                        myROQLQuery("select count(*) from accounts").then(function(result){
                            result["items"].forEach(function(rows){
                                rows["rows"].forEach(function(value){
                                    myExtensions2log(value); 
                                })                                
                            });                                                                                    
                        });
                    });
                    IGlobalHeaderMenu.render();
                });
            });
        });
    });
}

myHeaderMenu();
 

We hope that you find this post useful. We encourage you to try the sample code from this post and let us know what modifications you have made to enhance it. What other topics would you like to see next? Let us know in the comments below.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha
Oracle

Integrated Cloud Applications & Platform Services