Connect the Visual Builder plugin to the Oracle Fusion Field Service Plugin API

To use the Plugin API in a custom plugin, you must include the Oracle Fusion Field Service connector file in your plugin.

  1. Create the ofsc-connector.js file in the Resources > js folder in Visual Builder.
  2. Copy the following example code into the ofsc-connector.js file:
    /**
     * @licence
     * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
     * Oracle Technology Network Developer License Terms (https://www.oracle.com/downloads/licenses/production-modify-license.html)
     */
    "use strict";
    define([
     
    ], () => {
        const OFSC_API_VERSION = 1;
     
        class OfscConnector extends EventTarget {
            constructor() {
                super();
                window.addEventListener("message", this.onPostMessage.bind(this), false);
     
                this._currentCommunicationCallback = null;
                this._currentCommunicationPromise = null;
            }
     
            /**
             * @param {Object} dataToSend
             * @returns {Promise.<*>}
             */
            sendMessage(dataToSend) {
                if (this._currentCommunicationPromise) {
                    return Promise.reject(new Error('Communication channel is busy'));
                }
     
                this._currentCommunicationPromise = new Promise((resolve, reject) => {
     
                    const originUrl = this.constructor._getOriginUrl();
                    const origin = originUrl ? this.constructor._getOrigin(originUrl) : '*';
     
                    this._currentCommunicationCallback = (receivedData) => {
                        this._currentCommunicationCallback = null;
                        this._currentCommunicationPromise = null;
     
                        if (receivedData instanceof Error) {
                            return reject(receivedData);
                        }
     
                        if (receivedData.method && receivedData.method === 'error') {
                            return reject(receivedData);
                        }
     
                        return resolve(receivedData);
                    };
     
                    dataToSend.apiVersion = OFSC_API_VERSION;
     
                    parent.postMessage(dataToSend, origin);
     
                    this.dispatchEvent(new CustomEvent('debugMessageSent', { detail: dataToSend }));
                });
     
                return this._currentCommunicationPromise;
            }
     
     
            onPostMessage(event) {
                // Ignore internal JET messages
                if (event.source === window) {
                    return;
                }
     
                if (typeof event.data === 'undefined') {
                    this.dispatchEvent(new CustomEvent('debugIncorrectMessageReceived', { detail: "No data" }));
     
                    if (this._currentCommunicationCallback) {
                        this._currentCommunicationCallback(new Error('No data'));
                        return;
                    }
     
                    return false;
                }
     
                const data = this.constructor.parseJSON(event.data, null);
     
                if (data === null) {
                    if (this._currentCommunicationCallback) {
                        this._currentCommunicationCallback(new Error('Incorrect JSON'));
                        return;
                    }
                    this.dispatchEvent(new CustomEvent('debugIncorrectMessageReceived', { detail: event.data }));
     
                    return false;
                }
     
                this.dispatchEvent(new CustomEvent('debugMessageReceived', { detail: data }));
     
                if (this._currentCommunicationCallback) {
                    this._currentCommunicationCallback(data);
                } else {
                    this.dispatchEvent(new CustomEvent('messageFromOfs', { detail: data }));
                }
            }
     
            static generateCallId() {
                return window.btoa(String.fromCharCode.apply(null, window.crypto.getRandomValues(new Uint8Array(16))));
            }
     
            static _getOrigin(url) {
                if (typeof url === 'string' && url !== '') {
                    if (url.indexOf("://") > -1) {
                        return (window.location.protocol || 'https:') + url.split('/')[2];
                    } else {
                        return (window.location.protocol || 'https:') + url.split('/')[0];
                    }
                }
     
                return '';
            }
     
            static _getOriginUrl() {
                if (document.referrer) {
                    return document.referrer;
                }
     
                if  (document.location.ancestorOrigins && document.location.ancestorOrigins[0]) {
                    return document.location.ancestorOrigins[0];
                }
     
                return null;
            }
     
            static parseJSON(text, defaultValue = null) {
                try {
                    return JSON.parse(text);
                } catch (err) {
                    return defaultValue;
                }
            }
        }
     
        return OfscConnector;
    });
  3. Include a new file in the plugin.
    This screenshot shows the JSON tab in Visual Builder, where a new file is added:
    This screenshot shows a new file in the JSON tab of Visual Builder.
  4. Create a new Action Chain with the name initOFSConnector.
    This screenshot shows the new Action Chain, initOFSConnector:
    This screenshot shows the new Action Chain initOFSConnector.
  5. Add the following chain using the UI or Code tab:
    • UI tab: This screenshot shows the new Action Chain added to the plugin using the UI:
      This screenshot shows the new Action Chain added to the plugin using the UI.
    • Code tab:
      define([
        'vb/action/actionChain',
        'resources/js/ofsc-connector'
      ], (
        ActionChain,
        OfscConnector
      ) => {
        'use strict';
       
        class initOfsConnector extends ActionChain {
       
          /**
           * @param {Object} context
           */
          async run(context) {
            const { $page, $flow, $application, $constants, $variables } = context;
       
            const callFunction = await this.pluginStartAction(context);
          }
       
          /**
           * @private
           * @param {Object} context
           */
          async pluginStartAction(context) {
            const { $page, $flow, $application, $constants, $variables } = context;
           
            const ofscConnector = new OfscConnector();
       
            $application.variables.ofscConnector = ofscConnector;
       
            ofscConnector.sendMessage({
              method: 'ready',
              sendInitData: true
            }).then(this.onMessage.bind(this, $application));
          }
       
          /**
           * @private
           */
          onMessage($application, messageData) {
            if (!messageData) {
              return;
            }
       
            switch (messageData.method) {
              case 'init':
                this.initPlugin($application, messageData);
                break;
       
              case 'open':
                this.openPlugin($application, messageData);
                break;
       
              // default:
              //   console.log('method', messageData.method, 'data', messageData);
            }
          }
       
          /**
           * @private
           */
          initPlugin($application, jsonData) {
            this.sendInitEnd($application.variables.ofscConnector);
          }
       
          /**
           * @private
           */
          sendInitEnd(ofscConnector) {
            ofscConnector.sendMessage({ method: 'initEnd' });
          }
       
          /**
           * @private
           */
          openPlugin($application, jsonData) {
            this.setUserLocale(jsonData.user);
          }
       
          /**
           * @private
           */
          setUserLocale(user) {
            const currentLocale = window.localStorage.getItem('ofs_plugin_locale');
       
            const locale = user.locale || user.languageCode || window.navigator.language;
       
            if (currentLocale !== locale) {
              window.localStorage.setItem('ofs_plugin_locale', locale);
              window.location.href = this.getPluginUrl();
            }
       
          }
       
          /**
           * @private
           */
          getPluginUrl() {
            const url = window.location.href;
       
            if (url.endsWith('/')) {
              return url + 'index.html';
            }
       
            return url;
          }
       
        }
       
        return initOfsConnector;
      });