import { Initializer, api, config, log } from "actionhero"; import * as fetch from "node-fetch"; import { StockConverter } from "../converters/StockConverter"; import * as xml2json from 'xml2json'; import { promises as fs } from 'fs'; import { join, basename } from "path"; import { GraphQLClient } from 'graphql-request' export class imports extends Initializer { constructor() { super(); this.name = "imports"; this.loadPriority = 1003; this.startPriority = 1003; this.stopPriority = 1003; } async start() { const shops = await api.shopSettings.getInstalledShops(); for(var i = 0; i < shops.length; i++){ const { shop } = shops[i]; await api.imports.creatImportFolders(shop); } } async initialize() { const INVENTORY_QUERY = /* GraphQL */` query productVariants($numProducts: Int!, $cursor: String, $searchQuery: String!) { productVariants(query: $searchQuery, first:$numProducts, after:$cursor) { pageInfo { hasNextPage }, edges { cursor, node { ida, inventoryItem { id, sku, inventoryLevels(first:1) { edges { node { id, available, location { id } } } } } } } } }`; const INVENTORY_MUTATION = /* GraphQL */` mutation inventoryBulkAdjustQuantityAtLocation($inventoryItemAdjustments: [InventoryAdjustItemInput!]!, $locationId: ID!) { inventoryBulkAdjustQuantityAtLocation(inventoryItemAdjustments: $inventoryItemAdjustments, locationId: $locationId) { inventoryLevels { id } userErrors { field message } } }`; api.imports = { stockConverter : new StockConverter() } api.imports.creatImportFolders = async (shop) => { const dir = api.imports.getShopImportDir(shop); const archiveDir = join(dir, "archive"); const errorDir = join(dir, "error"); const processingDir = join(dir, "processing"); const dirExists = await api.utils.fs_IsFolder(dir); if (!dirExists){ await fs.mkdir(dir); } const archiveDirExists = await api.utils.fs_IsFolder(archiveDir); if (!archiveDirExists){ await fs.mkdir(archiveDir); } const errorDirExists = await api.utils.fs_IsFolder(errorDir); if (!errorDirExists){ await fs.mkdir(errorDir); } const processingDirExists = await api.utils.fs_IsFolder(processingDir); if (!processingDirExists){ await fs.mkdir(processingDir); } } api.imports.checkImports = async (shop) => { const dir = api.imports.getShopImportDir(shop); const files = await fs.readdir(dir); return files; } api.imports.processFile = async (path, shop) => { const xmlString = await fs.readFile(path); const data = JSON.parse(xml2json.toJson(xmlString)); await api.imports.importStock(data, shop); } api.imports.importStock = async (data, shop) => { const stock = api.imports.stockConverter.wintersteiger2Shopify(data); const { apiCode } = config.shopifyMiddleware; const endpoint =`https://${shop}/admin/api/${apiCode}/graphql.json`; const auth = await api.shopifyShops.getShop(shop); const searchQuery = api.imports.buildInventorySearchQuery(stock); const graphQLClient = new GraphQLClient(endpoint, { headers: { "X-Shopify-Access-Token": auth.token }, }) let hasNext = true; let cursor = null; let numProducts = 1; let i = 0 //break while loop while(hasNext && i < 100){ const inventoryData = await graphQLClient.rawRequest(INVENTORY_QUERY, {searchQuery, numProducts, cursor}); const delay = api.imports.getCostDelay(inventoryData.extensions.cost); await api.utils.sleep(delay); hasNext = inventoryData.data.productVariants.pageInfo.hasNextPage; cursor = inventoryData.data.productVariants.edges[inventoryData.data.productVariants.edges.length - 1].cursor const stockAjustments = await api.imports.buildStockAjustments(inventoryData.data, stock); if(stockAjustments.inventoryItemAdjustments && stockAjustments.inventoryItemAdjustments.length){ const responseData = await graphQLClient.request(INVENTORY_MUTATION, stockAjustments); } console.log(stockAjustments); i++; //protect against infinate loop } } api.imports.getCostDelay = (cost) => { const { currentlyAvailable, restoreRate } = cost.throttleStatus; const delay = ( cost.actualQueryCost - currentlyAvailable ) / restoreRate; return isNaN(delay) ? 0 : Math.max(0, delay); } api.imports.buildStockAjustments = (storeData, ezrentData) => { let locationId = ""; const inventoryItemAdjustments = []; const { edges } = storeData.productVariants; for(var i = 0; i < edges.length; i++){ const item = edges[i]; const { inventoryItem } = item.node; const { inventoryLevels, sku, id } = inventoryItem; const inventoryLevel = inventoryLevels.edges[0].node; const inventoryDelta = parseInt(ezrentData[sku]) - parseInt(inventoryLevel.available); if(isNaN(inventoryDelta)){ continue; } if(inventoryDelta != 0){ const adjustment = { "inventoryItemId": id, "availableDelta": inventoryDelta } inventoryItemAdjustments.push(adjustment); } locationId = inventoryLevel.location.id; } return { inventoryItemAdjustments, locationId } } api.imports.buildInventorySearchQuery = (data) => { var searchQuery = ""; var i = 0; for (const prop in data) { if (data.hasOwnProperty(prop)) { searchQuery += `sku:${prop}`; if(i < Object.keys(data).length - 1){ searchQuery += " OR "; } } i++; } return searchQuery; } api.imports.getShopImportDir = (shop) => { const { importPath } = config.shopifyMiddleware; return join(importPath, shop.replace(".myshopify.com", "")); } } }