Home Manual Reference Source

src/sap/pdms/ipro/api/IntentManager.js

/**
 * @typedef {string} IntentType An intent type
 * /

/**
 * @typedef {Object<IntentType, ActionMetadata>} ActionsMetadata
 * @desc The metadata of a collection of actions implemented in one action.js file.
 * @example <caption>Sample ActionsMetadata</caption>
 * {
 *  sampleAlertAction: {
 *    execute: function(options() {
 *      ...
 *    })
 *  }
 * }
 */

/**
 * @typedef {Object} ActionMetadata
 * @desc The metadata of an action.
 * @property {ActionExecution} execute - The execute function of an action
 * @property {!string} apiVersion - The API version of an action. This document describes API version "3.0.0". `apiVersion` adheres to [semantic versioning](http://semver.org/).
 */

/**
 * Intent manager
 */
export default class IntentManager {

  constructor() {
    this._remoteActionsMapPromise = null;
    this._adhocActionsMap = new Map();
  }

  /**
   * Registers an adhoc action
   * @param  {IntentType} intentType intent type
   * @param  {ActionMetadata} actionMetadata the metadata of the action implementing the intent
   * @return {void}
   */  
  addAdhocAction(intentType, actionMetadata) {
    this._adhocActionsMap.set(intentType, actionMetadata);
  }

  _fetchRemoteActionServiceEntries() {

    // if it's already being loaded just return the existing promise to unnecessary reloading and race conditions.
    if (this._remoteActionsMapPromise && this._remoteActionsMapPromise.isPending()) {
      return this._remoteActionsMapPromise;
    }
    if (this._remoteActionsMapPromise && this._remoteActionsMapPromise.isFulfilled()) {
      return Promise.resolve(this._remoteActionsMapPromise.value());
    }

    return this._remoteActionsMapPromise = Promise.resolve(jQuery.ajax({
      url: '/platform/service-catalog/api/v1/service-entries?serviceTypeId=com.sap.pdms.Action',
      dataType: 'json',
      method: 'GET'
    })).then(actionServiceEntries => {
      const actionMap = new Map();
      actionServiceEntries.forEach(action => {
        if (!actionMap.has(action.options.intentType)) {
          actionMap.set(action.options.intentType, [{serviceEntry: action}]);
        } else {
          actionMap.get(action.options.intentType).push({serviceEntry: action});
        }
      });
      return actionMap;
    }).catch(err => {
      throw new Error('Cannot get actions from service catalog because of: ' + err);
    });
  };

  /**
   * Executes an intent
   * @param  {IntentType} intentType intent type
   * @param  {Object} options the input options for a given intent
   * @return {Promise<Object>} Promise that resolves to a return object for a given intent
   */  
  executeIntent(intentType, options) {
    // adhoc actions take higher precedence
    if (this._adhocActionsMap.has(intentType)) {
      // there is an adhoc action
      return Promise.resolve(this._adhocActionsMap.get(intentType).execute(options));
    }
    return this._fetchRemoteActionServiceEntries()
      .then(actionMap => {
        if (!actionMap.has(intentType)) {
          // no implementation
          throw new Error("no implementation for intentType " + intentType);
        }
        // inject baseUrl if not already set
        if(!options.baseUrl) {
          options.baseUrl = actionMap.get(intentType)[0].serviceEntry.serviceUrl;
        }
        // use first entry in implementation array for now
        return loadRemoteActionEntryPoint(actionMap.get(intentType)[0].serviceEntry)
          .then(actionFactory => actionFactory.getMetadata()[intentType].execute(options));
      });
  }
  /** 
   * Determines whether an intent is defined for an intent manager 
   * @param  {IntentType} intentType intent type
   * @return {Promise<bool>} Promise that resolves to true (intent is defined) or false (inten is not defined)
   */  
  intentIsDefined(intentType) {
    if (this._adhocActionsMap.has(intentType)) {
      return Promise.resolve(true);
    }
    return this._fetchRemoteActionServiceEntries()
      .then(actionMap => {
        if (actionMap.get(intentType)) {
          return true;
        }
        else {
          return false;
        }
      });
  }
}

function loadRemoteActionEntryPoint(serviceEntry) {
  return SystemJS.import(serviceEntry.serviceUrl + '/action.js');
}