This blog was written by Deep Mandal from the Oracle Cloud Engineering team.

In this blog we'll show you how to create a reusable component that will help developers to integrate OneDrive File Picker SDK with Oracle Visual Builder. The component helps to pick and download files from OneDrive and perform file-based operations as per business requirement. Using this component users can select multiple files from OneDrive and as a call back it provides a list of the selected file data to VB which can be used for different use cases as per Business Requirements i.e., Download, Print, Send to Another REST Service etc.


Pre-requisites

There are a couple of pre-requisites to be done before you can use the component:
•    Azure Application to be configured for Application (Client) ID, which is required for File Picker SDK. (see steps later in this blog).
•    The JS SDK to be added to the VB index.html file. Use OneDrive File Picker SDK documentation for more details. Simply add the following line to the head section in the index.html file of your app.

<script type="text/javascript" src="https://js.live.net/v7.2/OneDrive.js"></script>

 


Process


Below is the detailed process of the component:

The Process


 
Process Step Details
•    This component exposes a button to the UI. The button name and icon class are passed as attributes to the component.
•    On Click of the button, OneDrive File Picker will be opened as a dialog.
•    Inside the dialog user will be asked to enter their authentication details for OneDrive login. 
•    On successful login, users will be landed to their OneDrive Folder. In case if SSO enabled between VB and OneDrive, users will be automatically landed to their OneDrive Folder without authentication page.
•    Users choose files and submit from the File Picker, then File Picker returns selected file data to the component and the component in term triggers an event to VB with the file data.
•    The files data can be used by the developer to implement the business logic as per the requirement.


Technical Details


Input Parameters

Name Type Writeback
Azure Application Id
Application ID of the Azure Application created for OneDrive File Picker.
String True
Error Data
This variable returns any error response received from OneDrive File Picker.  
Object True
Cancelled Flag
This variable returns true if user has cancelled and closed the File Picker.
Boolean True
Success Data
This variable returns success response received from OneDrive File Picker. 
Object True


Events

Name Bubbles Cancellable Details
Files Selected
This event sends all selected files data to the caller of this component.
True False  { 'files': <<Files Array>> }

 

Creating the Component

You can create the oj-onedrive component directly in Visual Builder under the resources->component of your application click to create a new component.

Create Component

To learn more about the role of each one of the files check out this blog.

Here is the content of the files for the component:

oj-onedrive-view.html

<oj-button chroming="solid" display="icons"   disabled="[[properties.disableButton]]" class="oj-button-sm"  on-oj-action="[[clickListener1]]">Add From Onedrive </oj-button>

oj-onedrive-viewmodel.js

define([
  'knockout',
  'ojs/ojcontext'
], function (
  ko,
  ojContext
) {
  'use strict';

  function ojOnedriveComponentModel(context) {
    var self = this;

    // At the start of your viewModel constructor.
    var busyContext = ojContext.getContext(context.element).getBusyContext();
    var options = { 'description': 'Component Startup - Waiting for data' };
    self.busyResolve = busyContext.addBusyState(options);
    self.composite = context.element;
    self.properties = context.properties;

    // Example observable.
    self.messageText = ko.observable('Hello from Example Component');

    self.clickListener1 = (event, data, bindingContext) => {
      try {
        var odOptions = {
          clientId: self.properties.azureApplicationId,
          action: "download",
          multiSelect: true,
          advanced: {},
          success: function (files) {
            //self.properties.successData = files;
            onSuccess(files);
          },
          cancel: function () {
            self.properties.cancelFlag = true;
          },
          error: function (error) {
            self.properties.errorData = 'Unhandled Exception FilePicker: ' + JSON.stringify(error);
          }
        };
        OneDrive.open(odOptions);
      } catch (e) {
        self.properties.errorData = 'Unhandled Exception FilePicker: ' + JSON.stringify(e);
      }

    };

    function onSuccess(files) {
      var dataFetchPromises = [];
      files.value.forEach(function(item){
        dataFetchPromises.push(new Promise(function (resolve, reject) {
          fetch(item["@microsoft.graph.downloadUrl"], {
            method: 'GET'
          }).then(response => {
            const reader = response.body.getReader();
            return new ReadableStream({
              start(controller) {
                return pump();
                function pump() {
                  return reader.read().then(({ done, value }) => {
                    // When no more data needs to be consumed, close the stream
                    if (done) {
                      controller.close();
                      return;
                    }
                    // Enqueue the next data chunk into our target stream
                    controller.enqueue(value);
                    return pump();
                  });
                }
              }
            });
          })
            .then(stream => new Response(stream))
            .then(response => response.blob())
            .then(blob => {
              resolve({
                name : item["name"],
                fileReference : blob
              });
            })
            .catch(error => {
              reject(error);
              //self.properties.errorData = 'Unhandled Exception while fetching File Data: ' +JSON.stringify(error);
              // handle the error
            });
        }));
      });

      Promise.all(dataFetchPromises).then(values => {
        var eventParams = {
          'bubbles': true,
          'cancelable': false,
          'detail': {
            'files': values
          }
        };
        self.properties.successData = 'Success';
        self.composite.dispatchEvent(new CustomEvent('filesSelected', eventParams));
      });
    };

    // Once all startup and async activities have finished, relocate if there are any async activities.
    self.busyResolve();
  }

  // Lifecycle methods - uncomment and implement if necessary.
  // XxOjOnedriveComponentModel.prototype.activated = function(context){
  // };

  // XxOjOnedriveComponentModel.prototype.connected = function(context){
  // };

  // XxOjOnedriveComponentModel.prototype.bindingsApplied = function(context){
  // };

  // XxOjOnedriveComponentModel.prototype.propertyChanged = function(propertyChangedContext){
  // };

  // XxOjOnedriveComponentModel.prototype.disconnected = function(element){
  // };

  return ojOnedriveComponentModel;
});

 

oj-onedrive-styles.css

 

xx-oj-onedrive:not(.oj-complete) {
  visibility: hidden;
}

xx-oj-onedrive {
  min-height: 50px;
  width: 50px;
}

 

loader.js

define([
    './oj-onedrive-viewmodel',
    'ojs/ojcomposite',
    'text!./oj-onedrive-view.html',
    'text!./component.json',
    'css!./oj-onedrive-styles'
], function (
    viewModel,
    Composite,
    view,
    metadata) {
        'use strict';

        Composite.register('oj-onedrive', {
            view: view,
            viewModel: viewModel,
            metadata: JSON.parse(metadata)
        });
    }
);

 

component.json

{
  "name": "oj-onedrive",
  "displayName": "oj-onedrive",
  "description": "A description of oj-onedrive.",
  "version": "1.0.0",
  "jetVersion": ">= 9.0.0",
  "properties": {
      "successData": {
        "description": "Assign variable of type Object to get Success Response",
        "displayName": "Success Data",
        "type": "Object",
        "writeback": true
      },
      "cancelFlag": {
        "description": "Assign variable of type Boolean to get Canceled Flag",
        "displayName": "Cancel Flag",
        "type": "boolean",
        "writeback": true
      },
      "errorData": {
        "description": "Assign variable of type Object to get Error Response",
        "displayName": "Error Data",
        "type": "Object",
        "writeback": true
      },
      "azureApplicationId": {
        "description": "Application Id from Azure",
        "displayName": "Azure Application Id",
        "type": "string",
        "writeback": false
      },
      "disableButton" : {
      "type" : "boolean"
    }
  },
  "events" : {
    "filesSelected" : {
      "description" : "The event that consuming views can use to recognize when the files selected",
      "bubbles" : true,
      "cancelable" : false,
      "detail" : {
        "files" : {"type" : "Object"}
      }
    }
  },
  "methods": {},
  "slots": {}
}

Once defined the component would be available in the component palette and you can add it to your page and set its properties.

Component in the IDE

Azure Setup

Azure Application Setup Guide for ‘Application Id’ Generation
Steps
•    Register an Application to Azure Cloud.

Register the app

•    Add web platform.

Web Platform

•    Add VB Application redirect URL 

Redirect URL
•    Add VB Instance URL

VB Instance URL

•    Get the Application ID.

Get app ID

 

UI Results

One button will be available to the UI

App UI

On Click of the button OneDrive File Picker will open in a dialog window and asks for login details.

OneDrive Login


After successful login, users will be able to see the OneDrive Files and Folders.

File Picker

On selection of the files, an Array of selected file references will be returned to VB.

File Results