← back to Majilite Onboard

scripts/collection.js

34 lines

const fs=require('fs'),path=require('path');
const STORE='designer-laboratory-sandbox.myshopify.com';
const TOKEN=(fs.readFileSync(path.join(process.env.HOME,'Projects/secrets-manager/.env'),'utf8').match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)||[])[1].replace(/["']/g,'').trim();
const GQL=`https://${STORE}/admin/api/2024-10/graphql.json`;
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
async function gql(q,v){const r=await fetch(GQL,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v})});const j=await r.json();if(j.errors)throw new Error(JSON.stringify(j.errors).slice(0,200));return j.data;}
(async()=>{
  const desc='<p>The <strong>Metallic Specialties I</strong> collection from Majilite — <em>The Nytek®</em> engineered Nylon-Fiber-Matrix faux leather. 160 luxurious, breathable, durable metallic & neutral finishes for wall coverings, seating, flat surfaces and displays. PFAS-, PVC- and plasticizer-free. Sample swatches; sold by the yard (54&quot; wide).</p>';
  // idempotent: reuse if handle exists
  let d=await gql(`mutation($input:CollectionInput!){ collectionCreate(input:$input){ collection{ id handle title } userErrors{field message} } }`,
    {input:{ title:'Majilite Metallic Specialties I', handle:'majilite-metallic-specialties-i', descriptionHtml:desc,
      ruleSet:{ appliedDisjunctively:false, rules:[
        {column:'TAG', relation:'EQUALS', condition:'Metallic Specialties I'},
        {column:'VENDOR', relation:'EQUALS', condition:'Majilite'}
      ]}}});
  let ue=d.collectionCreate.userErrors;
  let col=d.collectionCreate.collection;
  if(ue&&ue.length){ console.log('create userErrors:',JSON.stringify(ue)); if(!col){console.log('aborting');return;} }
  console.log('collection:', col.id, col.handle, '·', col.title);
  // publish collection to Online Store + Google & YouTube
  const pd=await gql(`{ publications(first:20){ nodes{ id name } } }`);
  const targets=pd.publications.nodes.filter(p=>/online store|google/i.test(p.name));
  const pp=await gql(`mutation($id:ID!,$pubs:[PublicationInput!]!){ publishablePublish(id:$id, input:$pubs){ userErrors{field message} } }`,
    {id:col.id, pubs:targets.map(t=>({publicationId:t.id}))});
  console.log('published to:', targets.map(t=>t.name).join(', '), pp.publishablePublish.userErrors.length?JSON.stringify(pp.publishablePublish.userErrors):'ok');
  // smart-collection population is async; poll count
  for(let i=0;i<6;i++){ await sleep(2500);
    const c=await gql(`query($id:ID!){ collection(id:$id){ productsCount{count} } }`,{id:col.id});
    const n=c.collection.productsCount.count; console.log('  products in collection:', n);
    if(n>=160) break;
  }
  console.log('STOREFRONT: https://'+STORE+'/collections/'+col.handle);
})();