← back to Dw Pairs Well

tools/pull-mfr-sku.js

34 lines

// READ-ONLY: bulk-pull custom.manufacturer_sku for all ACTIVE products → tools/mfr-map.jsonl
// Uses the full-access token (write_products implies read). No writes. $0 (Shopify API).
const https=require('https');
const STORE='designer-laboratory-sandbox.myshopify.com', API='2024-10';
const fs=require('fs'), cp=require('child_process');
const TOK=cp.execSync(`grep -rhoE 'shpat_[a-f0-9]{32}' ~/Projects/secrets-manager/.env ~/Projects/Designer-Wallcoverings/DW-Agents ~/Projects/Designer-Wallcoverings/shopify 2>/dev/null | sort -u`).toString().split('\n').find(t=>t.startsWith('shpat_307')&&t.endsWith('a43b'));
if(!TOK){console.error('no full-access token');process.exit(1);}
function gql(q){return new Promise((res,rej)=>{const body=JSON.stringify({query:q});const r=https.request({host:STORE,path:`/admin/api/${API}/graphql.json`,method:'POST',headers:{'X-Shopify-Access-Token':TOK,'Content-Type':'application/json','Content-Length':Buffer.byteLength(body)}},resp=>{let d='';resp.on('data',c=>d+=c);resp.on('end',()=>{try{res(JSON.parse(d))}catch(e){rej(d.slice(0,300))}})});r.on('error',rej);r.write(body);r.end();});}
(async()=>{
  const start=await gql(`mutation{bulkOperationRunQuery(query:"""
    { products(query:"status:active") { edges { node { id handle vendor
        metafield(namespace:"custom",key:"manufacturer_sku"){ value }
        variants(first:1){ edges { node { sku } } } } } } }
  """){ bulkOperation{ id status } userErrors{ field message } } }`);
  const ue=start.data?.bulkOperationRunQuery?.userErrors;
  if(ue&&ue.length){console.error('userErrors',JSON.stringify(ue));process.exit(1);}
  console.log('bulk op started:',start.data.bulkOperationRunQuery.bulkOperation.id);
  // poll
  let url=null, tries=0;
  while(tries++<120){
    await new Promise(r=>setTimeout(r,5000));
    const cur=await gql(`{ currentBulkOperation{ status objectCount url errorCode } }`);
    const o=cur.data.currentBulkOperation;
    process.stdout.write(`\r  ${o.status} objects=${o.objectCount||0}   `);
    if(o.status==='COMPLETED'){url=o.url;console.log('\n COMPLETED objects=',o.objectCount);break;}
    if(o.status==='FAILED'){console.error('\n FAILED',o.errorCode);process.exit(1);}
  }
  if(!url){console.error('\n timed out');process.exit(1);}
  // download JSONL
  await new Promise((res,rej)=>{const f=fs.createWriteStream('tools/mfr-map.jsonl');https.get(url,r=>{r.pipe(f);f.on('finish',()=>{f.close();res();});}).on('error',rej);});
  const n=fs.readFileSync('tools/mfr-map.jsonl','utf8').trim().split('\n').filter(Boolean).length;
  console.log('saved tools/mfr-map.jsonl lines=',n);
})().catch(e=>{console.error('ERR',e);process.exit(1);});