Source: api/product/idService.js

/**
 * @class module:cpb-api/product/ProductIdService
 * ### Provides methods for the retrieval of both **productID** and **productHandle** from either value
 *
 * While the **shopName** is being used elsewhere
 */
import { query } from 'cpb-datastore';

class ProductIdService {
  static #settings = {
    kinds: ['shopify_products'],
  };
  static #cache = new Map();
  state = {};
  /**
   * shopID->shopName map
   * @private @static @type {Map<number, string>}
   */
  static #IDS = new Map();
  /**
   * shopName->shopID map
   * @private @static @type {Map<string, number>}
   */
  static #HANDLES = new Map();

  /**
   * get shopID's map
   * @return {Map<number,string>}
   */
  get ids() {
    return ProductIdService.#IDS;
  }

  /**
   * get shopName's map
   * @return {Map<string,number>} ProductIdService.#HANDLES
   */
  get handles() {
    return ProductIdService.#HANDLES;
  }

  /**
   * load datastore data and populate maps
   * @return {Promise<ProductIdService>}
   * @static @async
   */
  static async #load(filter) {
    const order = 'handle',
      selector = ['id', 'handle', 'shopName', 'shopID'];
    for (const kind of ProductIdService.#settings.kinds) {
      const querySettings = { kind, filter, order },
        data = await query(querySettings).catch(console.error);

      for (const { id, handle } of data) {
        ProductIdService.#IDS.set(+id, handle);
        ProductIdService.#HANDLES.set(handle, +id);
      }
    }

    console.info(`[ProductIdService.load]:
        #IDS: ${ProductIdService.#IDS.size}
         #HANDLES: ${ProductIdService.#HANDLES.size}`);

    return ProductIdService;
  }

  /**
   * check if the supplied value is number
   * @param {*} val
   * @return {boolean} isNumber
   */
  static isNumber(val) {
    return !isNaN(+val) && +val == val;
  }

  /**
   *  get shopName from its shopID
   * @param {!number|string} id - shopID
   * @param {?boolean|undefined} [fetch] - load data from datastore
   * @return {Promise<string|undefined|Map<number,string>>} name - shopName
   */
  static async #getHandle(id, fetch) {
    if (fetch || !ProductIdService.#IDS.size) await ProductIdService.#load();
    if (!id) return ProductIdService.#IDS;
    return ProductIdService.#IDS.get(+id); // || ProductIdService.#HANDLES.get(id);
  }

  /**
   * get shopID from the shopName. If name is not given the entire map is returned
   * @param {string} name - shopName
   * @param {?boolean|undefined} [fetch] - fetch datastore data
   * @return {Promise<Map<string, number>|number|undefined>} shopID
   */
  static async #getId(name, fetch) {
    if (fetch || !ProductIdService.#HANDLES.size) await ProductIdService.#load();
    if (!name) return ProductIdService.#HANDLES;
    return ProductIdService.#HANDLES.get(name);
  }

  /**
   * call to static load method from instance
   * @return {Promise<ProductIdService>}
   */
  async load(filter = {}) {
    await ProductIdService.#load(filter);
    return this;
  }

  /**
   * call to static load method from instance
   * @param {string} name
   * @param fetch
   * @return {Promise<Map<string, number>|number|undefined>}
   */
  async getId(name, fetch) {
    return await ProductIdService.#getId(name, fetch);
  }

  /**
   * call to static load method from instance
   * @param {number} id
   * @param fetch
   * @return {Promise<string>} shopName
   */
  async getHandle(id, fetch) {
    return await ProductIdService.#getHandle(id, fetch);
  }

  /**
   * get <**productID**, **productHandle**> tuple from either **productID** or **productHandle**
   * @param {!number|string} idOrName
   * @param shop
   * @param fetch
   * @return {{name: string, id: number}}
   */
  async getIdHandle(idOrName, { shopID, shopName } = {}, fetch) {
    if (!idOrName) throw new TypeError('!idOrName');
    if (!shopName && !shopID) throw new TypeError('!shopName||!shopID');
    //    if (fetch) await this.load();
    let id, handle;
    if (ProductIdService.isNumber(idOrName) && idOrName.length > 6) {
      id = idOrName;
      handle = await ProductIdService.#getHandle(idOrName);
    } else {
      id = await ProductIdService.#getId(idOrName);
      handle = idOrName;
    }
    if (!id) {
      console.error('[cpb-api/product/ProductIdService][ERROR] missing id for ', { idOrName, id, handle });
      handle = null;
    }
    if (!handle) {
      console.error('[cpb-api/product/ProductIdService][ERROR] missing name for ', { idOrName, id, handle });
      id = null;
    }

    return Promise.resolve({ id, handle });
  }
}

export default new ProductIdService();