Source: shopify/product/index.js

import { ShopIdService } from 'cpb-api';
import { isArray } from 'cpb-common';
import Datastore from 'cpb-datastore';
import Shopify from 'cpb-shopify';

/**
 * @module cpb-shopify/product
 */

/**
 * ### Product Attributes to request from Shopify
 * @readonly DEFAULT_PRODUCT_FIELDS
 */
const DEFAULT_PRODUCT_FIELDS = [
    'admin_graphql_api_id',
    'body_html', // body_html is displayed as 64characters on the frontend
    'created_at',
    'handle',
    'id',
    'image',
    'images',
    'options',
    'product_type',
    'published_at',
    'status',
    'tags',
    'title',
    'updated_at',
    'variants',
    'vendor',
  ],
  DEFAULT_SHOPIFY_PAGE_SIZE = 200,
  EXCLUDED_PRODUCT_FIELDS = ['options', 'variants', 'admin_graphql_api_id'];

/**
 * ### Product Data from Shopify
 */
export const Product = {
  settings: {
    kind: 'shopify_products',
    fields: DEFAULT_PRODUCT_FIELDS,
    excludedFields: EXCLUDED_PRODUCT_FIELDS,
  },
  /**
   * ### Save products to the datastore
   * @param {!object|object[]} data - product data to save
   * @param {!string} shopName - shopify bottom-level domain of the filestore lowercased
   * @param {!number} shopId
   * @return {Promise<void|*>}
   */
  async save({ data, shopName, shopId } = {}) {
    if (!data) throw new TypeError('!data');
    if (!shopId) throw new TypeError('!shopId');
    if (!shopName) throw new TypeError('!shopName');

    const promises = [];
    if (!isArray(data)) data = [data];

    for (const product of data) {
      product.shopID = shopId;
      product.shopName = shopName;
      product.datastore_updated_at = new Date();
      // resolving  Error: 3 INVALID_ARGUMENT: The value of property "body_html" is longer than 1500 bytes.
      // https://stackoverflow.com/questions/44373051/google-datastore-1500-byte-property-limit-for-embedded-entities
      product.body_html = (product.body_html || '').substring(0, 1450);

      const productData = { ...product };
      delete productData.config;

      //      console.debug({ SAVE: productData });
      promises.push(
        Datastore.save({
          kind: this.settings.kind,
          data: productData,
          key: Datastore.datastore.key([this.settings.kind, [productData.shopName, productData.id].join()]),
          useEmulator: process.env.USE_EMULATOR_DATASTORE,
        }),
      );
    }
    return Promise.all(promises);
  },
  /**
   * ### Get Shopify Product List Iteratively
   * Shopify Page Size is in `env.DEFAULT_SHOPIFY_PAGE_SIZE`. The default value is `250`.
   * @param {!string} shopName
   * @param {?number} id - product id
   * @param {?number[]|string} [ids] - product ids
   * @param {?string[]} [fields=DEFAULT_PRODUCT_FIELDS] - product attributes to include
   * @param {?object} settings - shopify api call settings
   * @property {?number} [settings.limit=DEFAULT_SHOPIFY_PAGE_SIZE] - pagination size
   * returns {Promise<*[][]|(*[]|*)[]>} [products, ?error] - [Shopify Products, Error]
   * @returns {Promise<Array()|*>} [products, ?error] - [Shopify Products, Error]
   * @async
   */
  async list({ shopName, id, ids = [], fields = [] } = {}, settings = { limit: DEFAULT_SHOPIFY_PAGE_SIZE }) {
    if (!shopName) throw new TypeError('!shopName');
    if (typeof ids === 'string') ids = ids.split(',');
    if (id) ids.push(id);
    if (isArray(ids) && ids.length) settings.ids = ids.join(',');
    settings.fields = [...DEFAULT_PRODUCT_FIELDS, ...fields].join(',');

    let pagination = {
      limit: (settings.limit || DEFAULT_SHOPIFY_PAGE_SIZE) > DEFAULT_SHOPIFY_PAGE_SIZE ? DEFAULT_SHOPIFY_PAGE_SIZE : settings.limit,
    };
    if (settings.ids) pagination.ids = settings.ids;
    pagination.fields = [...DEFAULT_PRODUCT_FIELDS, ...fields].join(',');

    const products = [],
      { id: shopId, name: shopNameDb } = await ShopIdService.getIdName(shopName),
      connection = await Shopify.connect({ shopName: shopNameDb });

    try {
      do {
        const page = await connection.product.list(pagination);
        pagination = page.nextPageParameters;
        products.push(...page);
      } while (pagination);

      if (products.length) this.save({ data: products, shopId, shopName: shopNameDb }).catch(console.error);

      for (const product of products) for (const attr of EXCLUDED_PRODUCT_FIELDS) {
        const variant = product.variants[0];
        if(!fields.includes(attr)) delete product[attr];
        if(attr === 'variants') product.variants = [variant];
      };
      return [products, undefined];
    } catch (error) {
      console.error(`[shopify/product/list][${shopNameDb}][${error.code}] NO_SHOPIFY_CONNECTION`, { error });
      return [[], error];
    }
  },

  /**
   *  ### Shopify Products Count
   * @param {string} shopName
   * @returns {Promise<number>} - products count
   */
  async count({ shopName }) {
    let connection;
    try {
      connection = await Shopify.connect({ shopName });
    } catch (error) {
      console.error(`[${shopName}] ERROR_CONNECT [${error.code}] `, { error });
      return 0;
    }
    return await connection.product.count();
  },
};
export default Product;