Source: resource-enumerate/modules/resource-enumerate.js

/**
 * @summary resource-enumerate
 * @version 3.0.0
 * @since 1.0.0
 * @author Arian Khosravi <arian.khosravi@aofl.com>
 */
import {environmentTypeEnumerate} from '@aofl/server-environment';
import {Middleware} from '@aofl/middleware';
import {ApiRequest} from '@aofl/api-request';


/**
 * Resource enumerate is a special case API call that returns the interface of
 * the API code in a single endpoint. The response can contain server time,
 * paths to resources, and other information based on application needs. An
 * advantage of this technique is that it eliminates the need to hardcode paths\urls.
 *
 * @memberof module:@aofl/resource-enumerate
 */
class ResourceEnumerate {
  /**
   * Creates an instance of ResourceEnumerate.
   *
   * @param {String} environment
   */
  constructor(environment) {
    this.environment = environment;
    this.ready = new Promise((resolve) => { this.resolve = resolve; });
    this.middlewareInstance = new Middleware('before', 'after');
    this.apiRequestInstance = new ApiRequest();
  }

  /**
   *
   *
   * @readonly
   * @type {String}
   */
  static get NAMESPACE() {
    return 'resource-enumerate';
  }

  /**
   * init() updates apis.url based on supplied config object.
   *
   * @param {Object} {apis, developmentConfig, stageConfig}
   * @param {Boolean} [skipEnvironmentCheck=true] skip dev/stage config process
   * @return {Promise}
   */
  async init({apis, developmentConfig, stageConfig}, skipEnvironmentCheck = false) {
    this.apis = apis;

    for (const apiNs in this.apis) {
      /* istanbul ignore next */
      if (!Object.hasOwnProperty.call(this.apis, apiNs)) continue;
      const api = this.apis[apiNs];
      this.apiRequestInstance.addFormatter(apiNs, {
        pack() {
          return api.requestOptions || {};
        },
        unpack(response) {
          return response.json();
        }
      });
    }

    if (skipEnvironmentCheck === false && this.environment !== environmentTypeEnumerate.PROD) {
      await this.updateApis(developmentConfig, stageConfig);
      this.resolve();
      return this.ready;
    }

    this.resolve();
    return this.ready;
  }

  /* istanbul ignore next */
  /**
   * before hook. Allows to execute some logic right before the network call is made.
   *
   * @param {function} fn
   */
  before(fn) {
    this.middlewareInstance.use(fn, 'before');
  }

  /* istanbul ignore next */
  /**
   * after hook. Allows to execute some logic immediately after the network call is made.
   *
   * @param {Function} fn
   */
  after(fn) {
    this.middlewareInstance.use(fn, 'after');
  }

  /**
   * get() returns a promise that once resolved contains the payload from the resource enumerate
   * call.
   *
   * @param {String} apiNs
   * @param {Boolean} _fromCache
   * @return {Promise}
   */
  async get(apiNs, _fromCache = true) {
    await this.ready;
    const api = this.apis[apiNs];
    let fromCache = _fromCache;

    if (typeof api === 'undefined') {
      throw new TypeError(`${apiNs} is not a valid api namespace.`);
    }

    if (!fromCache || (typeof api.invalidateCache === 'function' && api.invalidateCache.call(null))) {
      this.apiRequestInstance.clearCache(ResourceEnumerate.NAMESPACE);
      fromCache = false;
    }

    const cacheManager = this.apiRequestInstance.getCacheManager(ResourceEnumerate.NAMESPACE);
    const re = cacheManager.getItem(api.url + JSON.stringify(''));
    const request = {
      namespace: apiNs,
      cached: re !== null
    };

    await this.middlewareInstance.iterateMiddleware(request, 'before');
    let reResp = null;

    if (re !== null) {
      reResp = re;
    } else {
      reResp = this.apiRequestInstance
        .request(api.url, '', apiNs, fromCache, ResourceEnumerate.NAMESPACE);
    }

    return reResp.then((response) => this.middlewareInstance.iterateMiddleware(request, 'after', response));
  }

  /**
   * updateApis() updates the api url of each namespace based on the config functions.
   *
   * @private
   * @param {*} developmentConfig
   * @param {*} stageConfig
   * @return {Promise}
   */
  updateApis(developmentConfig, stageConfig) {
    let configFn = developmentConfig;
    if (this.environment === environmentTypeEnumerate.STAGE) {
      configFn = stageConfig;
    }

    return configFn()
      .then((configModule) => {
        for (const apiNs in this.apis) {
        /* istanbul ignore next */
          if (!Object.hasOwnProperty.call(this.apis, apiNs)) continue;
          const api = this.apis[apiNs];
          let variables = {};
          if (typeof api[this.environment + 'Variables'] === 'function') {
            variables = api[this.environment + 'Variables'].call(null);
          }
          api.url = configModule.default(apiNs, variables);
        }
      });
  }
}

export {
  ResourceEnumerate
};