JavaScript Extension Development API for Oracle Visual Builder Cloud Service - Classic Applications

Source: extensions.dt/js/spi/ExtensionManager.js

define([], function() {

    'use strict';

    /**
     * This API module allows authors of ABCS extensions to define their behaviour.
     *
     * <p>The infrastructure calls its methods {@link extensions.dt/js/spi/ExtensionManager#initialise ExtensionManager.initialise}
     * and {@link extensions.dt/js/spi/ExtensionManager#destroy ExtensionManager.destroy} when the application
     * which holds the extension is opened or closed.
     * </p>
     *
     * <p>
     * It's guranteed that the infrastructure will never call {@link extensions.dt/js/spi/ExtensionManager#initialise ExtensionManager.initialise}
     * or {@link extensions.dt/js/spi/extensions.dt/js/spi/ExtensionManager#destroy ExtensionManager.destroy} method twice in row.
     * They can be called N times, but always altering.
     * </p>
     *
     * <p>A composite operation consisting from calls to the {@link extensions.dt/js/spi/ExtensionManager#initialise ExtensionManager.initialise}
     * or {@link extensions.dt/js/spi/ExtensionManager#destroy ExtensionManager.destroy}
     * methods must be idempotent. Which means that when the two methods are
     * called in sequence, the state of the system must be exactly the same as before the calls.</p>
     *
     * The module path is specified in the extension manifest under the "extensionManager" key.
     *
     * @AbcsExtension stable
     * @exports extensions.dt/js/spi/ExtensionManager
     * @constructor
     * @private
     * @version 16.3.1
     * @example <caption>Simple shared object extension</caption>
     * define([
     *       'core/js/api/SharedObjectRegistry'
     *   ], function (sharedObjectRegistry) {
     *       'use strict';
     *       function MyExtensionManager() {
     *       }
     *       MyExtensionManager._OBJECT_ID = 'com.mycompany.extensions.myExtension.message';
     *       MyExtensionManager.prototype.initialise = function () {
     *           sharedObjectRegistry.register(MyExtensionManager._OBJECT_ID, 'Ahoy!');
     *           return Promise.resolve();
     *       };
     *       MyExtensionManager.prototype.destroy = function () {
     *           sharedObjectRegistry.unregister(MyExtensionManager._OBJECT_ID);
     *           return Promise.resolve();
     *       };
     *       MyExtensionManager.prototype.generateRuntimeCode = function () {
     *           return Promise.resolve('function() {\n\
     *               return new Promise(function(f,r) {\n\
     *                   require([\'core/js/api/SharedObjectRegistry\'], function(sharedObjectRegistry) {\n\
     *                       sharedObjectRegistry.register(\'com.mycompany.extensions.myExtension.message\', \'Ahoy!\');\n\
     *                       f();\n\
     *                   }, r);\n\
     *               });\n\
     *           }\n');
     *       };
     *       return new MyExtensionManager();
     *   });
     */
    var ExtensionManager = function () {};

    /**
     * This method is responsible for initialisation of the extension.
     *
     * <p>Extension author may call any ABCS stable APIs here to "install" the extension
     * to the VM. For example {@link bop/js/spi/BOP BOP} extension typically registers its {@link bop/js/spi/entity/EntityProvider EntityProvider}
     * and {@link bop/js/spi/operation/OperationProvider OperationProvider} in this method. Custom UI component register its
     * creator, PIs, view generators etc. here.</p>
     *
     * <p>The method is called by the infrastructure when the application which owns
     * or refers the extension is initialised (opened).</p>
     *
     * <p>An array of {@link module:extensions.dt/js/spi/ExtensionManager.Dependency ExtensionManager.Dependency} objects is passed to the method.
     * Each of them represents one dependency to the extension from a dependent object.</p>
     *
     * <p>It is guaranteed that this method is called only after all dependencies which
     * this extension has have been resolved which means the corresponding dependency targets
     * (extensions or more generic shareable objects) are initialised.</p>
     *
     * <p>It's the responsibility of the extension itself to understand any dependencies parameters
     * and adjust its behaviour accordingly.</p>
     *
     * @AbcsExtension stable
     * @version 16.3.1
     * @param {module:extensions.dt/js/spi/ExtensionManager.Dependency[]} dependencies - array of Dependency
     * objects which the extension is target of.
     * @return {Promise} promise of the extension being initialised.
     *
     * @example <caption>Registration of a shared object</caption>
     * CurrentTimeExtensionManager.prototype.initialise = function (dependencies) {
     *    sharedObjectRegistry.register('com.company.abcs.extensions.currentTime', CurrentTime);
     *    return Promise.resolve();
     * };
     */
    ExtensionManager.prototype.initialise = function(dependencies) { //eslint-disable-line no-unused-vars
            AbcsLib.throwMustBeOverriddenError();
    };

    /**
     * This method is responsible for destroying of the extension.
     *
     * <p>Extension author may call any ABCS stable APIs here to remove any changes
     * which were previously done in the {@link extensions.dt/js/spi/ExtensionManager#initialise ExtensionManager.initialise}
     * method from the system.</p>
     *
     * <p>{@link bop/js/spi/BOP BOP} extension typically un-registers its {@link bop/js/spi/entity/EntityProvider EntityProvider}
     * and {@link bop/js/spi/operation/OperationProvider OperationProvider} here. Custom UI component un-register its
     * creator, PIs, view generators etc.</p>
     *
     * <p>The method is called by the infrastructure when the application which owns
     * or refers the extension is destroyed (closed).</p>
     *
     * <p>An array of {@link module:extensions.dt/js/spi/ExtensionManager.Dependency ExtensionManager.Dependency} objects is passed to the method.
     * Each of them represents one dependency to the extension from a dependent object.
     * The passed dependencies are identical to those which were previously passed
     * to the {@link extensions.dt/js/spi/ExtensionManager#initialise ExtensionManager.initialise}
     * method.</p>
     *
     * <p>This method is called only after all dependents which depends on this
     * extension were destroyed (the destroy sequence is reverse to the initialisation sequence).</p>
     *
     * <p>It's the responsibility of the extension itself to understand any dependencies parameters
     * and adjust its behaviour accordingly.</p>
     *
     * @AbcsExtension stable
     * @version 16.3.1
     * @param {module:extensions.dt/js/spi/ExtensionManager.Dependency[]} dependencies - array of Dependency
     * objects which the extension is target of.
     * @return {Promise} promise of the extension being destroyed.
     *
     * @example <caption>De-registration of a shared object</caption>
     * CurrentTimeExtensionManager.prototype.destroy = function (dependencies) {
     *    sharedObjectRegistry.unregister('com.company.abcs.extensions.currentTime');
     *    return Promise.resolve();
     * };
     */
    ExtensionManager.prototype.destroy = function(dependencies) { //eslint-disable-line no-unused-vars
        AbcsLib.throwMustBeOverriddenError();
    };

    /**
     * Generates a body of javascript function responsible for initialisation
     * of the extension in runtime.
     *
     * <p>The javascript code is placed to a global extensions/shareable objects initialiser
     * which runs when the ABCS application is initialised in runtime.</p>
     *
     * <p>If this extension has dependencies to other shareable objects,
     * these shareable objects will be initialised before the code returned
     * from this method runs.</p>
     *
     * <p>The returned code must contain a function body and the function
     * must return a Promise object. The promise must only be resolved when the
     * code finishes its work which means the extension is initialised
     * in the runtime environment.</p>
     *
     * <p>During the runtime application artefact generation, the extensions
     * are initialised according to the applications dependencies in
     * similar manner as in design time.</p>
     *
     * <p>Note: You may use several techniques to built the code in text form:
     * <ul>
     * <li>return the code as a string - simple variant, allows to easily generate
     * the code based on the input parameters passed via the dependencies, however
     * it's bit more error prone as you need to properly escape the content of the string.
     * <li>alternatively if your code is long and complex enough, you can keep it
     * as a separate file in the sources of the extension and then load that module
     * using the the requirejs text plugin. This way you can also easily make
     * the file a template and use some templating mechanism to resolve
     * templating variables. This method spares you the painful escaping
     * of the previous simple method.
     * <li>use Function.toString() - you can simply declare the required function
     * in code form (not as string) and then use the Function.toString() method
     * to convert the code to the required textual form.
     * </ul>
     *
     * @AbcsExtension stable
     * @version 16.3.1
     * @param {module:extensions.dt/js/spi/ExtensionManager.Dependency[]} dependencies - array of Dependency
     * objects which the extension is target of.
     * @returns {Promise<String>} promise of javascript code in text form
     *
     * @example <caption>Trivial synchronous initialisation code</caption>
     * ExtensionManager.prototype.generateRuntimeCode = function() {
     * return 'function () {\n\
     *    return Promise.resolve();\n\
     *  }\n';
     * }
     * @example <caption>Trivial synchronous initialisation code using the Function.toString() method of producing the code</caption>
     * ExtensionManager.prototype.generateRuntimeCode = function() {
     *      return function () {
     *          return Promise.resolve();
     *      }.toString();
     * }
     * @example <caption>More complex asynchronous initialisation code</caption>
     * ExtensionManager.prototype.generateRuntimeCode = function() {
     * return 'function () {\n\
     *    return new Promise(function (f, r) {\n\
     *        require([\'yourPackage/js/YourModule\'], function (YourModule) {\n\
     *           //if the initialisation itself is asynchronous don\'t forget the resolve the promise\n\
     *           //when the initialisation finishes, e.g.:\n\
     *           YourModule.getPromiseOfSomethingAsync().then(f).catch(r);\n\
     *        }, r);\n\
     *    });\n\
     *  }\n';
     * }
     * @example <caption>Using the separate runtime code template</caption>
     * define([
     *       'core/js/api/SharedObjectRegistry',
     *       'text!com.mycompany.extensions.myExtension/templates/RuntimeInitialiser.js'
     *       ], function (
     *           sharedObjectRegistry,
     *           runtimeInitialiserTemplate
     *           ) {
     *       'use strict';
     *       function MyExtensionManager() {
     *       }
     *       MyExtensionManager._OBJECT_ID = 'com.mycompany.extensions.myExtension.message';
     *       ...
     *       MyExtensionManager.prototype.generateRuntimeCode = function () {
     *           //possibly resolve templating variables used in the templates/RuntimeInitialiser.js file here
     *           return runtimeInitialiserTemplate;
     *       };
     *       return new MyExtensionManager();
     *  });
     */
    ExtensionManager.prototype.generateRuntimeCode = function(dependencies) { //eslint-disable-line no-unused-vars
        AbcsLib.throwMustBeOverriddenError();
    };

    /**
     * Defines a dependency between a dependent and an extension object.
     *
     * <p>An array of objects of this type is passed to the ExtensionManager's
     * methods by the infrastructure during the extension lifecycle.</p>
     *
     * <p>The basic format of the dependency is defined by the
     * {@link extensions.dt/js/spi/ExtensionManager~ExtensionDependencyFormat ExtensionDependencyFormat}
     * module.</p>
     *
     * <p>However the keys defined and described there are only the mandatory
     * keys which every dependency to an extension must possess.
     * Each individual extension can define its own "API" -- e.g. set of keys
     * in the dependency definition it understands. The implementation of
     * the corresponding ExtensionManager is then responsible for reading those
     * keys from the passed dependency object and adapt its behaviour accordingly.</p>
     *
     * @AbcsExtension stable
     * @version 16.3.1
     * @memberof module:extensions.dt/js/spi/ExtensionManager
     * @objectLiteral
     * @example <caption>Usage of the Dependency passed objects</caption>
     *  CurrentTimeExtensionManager.prototype.initialise = function (dependencies) {
     *      var timeZones = ['utc']; //default timezone (if noone specifies time zone explicitly in the dependency).
     *      (dependencies || []).forEach(function(dependency) {
     *          var timeZone = dependency.getDefinition().timeZone;
     *          if(timeZone) {
     *              timeZones.push(timeZone);
     *          }
     *      });
     *      timeZones.forEach(function(timeZone) {
     *          sharedObjectRegistry.unregister('com.company.abcs.extensions.currentTime.' + timeZone, new CurrentTime(timeZone));
     *      });
     *      return Promise.resolve();
     *  };
     */
    var Dependency = function() {
    };

    /**
     * Gets definition of a dependency to an extension.
     *
     * <p>The keys of the dependency description are defined in {@link extensions.dt/js/spi/ExtensionManager~ExtensionDependencyFormat ExtensionDependencyFormat}</p>
     *
     * @AbcsExtension stable
     * @version 16.3.1
     * @returns {Object}
     *
     * @example <caption>Sample dependency to an ABCS extension</caption>
     * {
     *      sourceType: 'application',
     *      sourceId: 'myApplication-1.0',
     *      extensionId: 'com.mycompany.abcs.extensions.myTwitterBOP'
     * }
     * @see {@link bop.dt/js/spi/BOPExtensionManager~BOPExtensionDependencyFormat BOP dependency definition format}
     */
    Dependency.prototype.getDefinition = function(){
    };

    return ExtensionManager;

});