← back to Shopify Sample Shipping

migration-rollback.mjs

25 lines

// FULL UNDO for the samples-only migration: dissociate all sample-profile variants
// back to General, and re-create the General "Free Shipping (No Tracking)" $0-$cap band.
import {query} from './query.mjs';
import fs from 'node:fs';
const SAMPLE_PROFILE='gid://shopify/DeliveryProfile/96764067891';
const GENERAL='gid://shopify/DeliveryProfile/29033627699';
const mut=`mutation($id:ID!,$profile:DeliveryProfileInput!){deliveryProfileUpdate(id:$id,profile:$profile){userErrors{field message}}}`;
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
const chunk=(a,n)=>{const o=[];for(let i=0;i<a.length;i+=n)o.push(a.slice(i,i+n));return o;};
async function q(g,v){for(let a=0;a<6;a++){try{return await query(g,v);}catch(e){if(/THROTTL/i.test(e.message)){await sleep(2000*(a+1));continue;}throw e;}}throw Error('throttled');}
// 1) dissociate all variants on the sample profile
let cursor=null, ids=[];
while(true){const d=await q(`query($id:ID!,$c:String){deliveryProfile(id:$id){profileItems(first:250,after:$c){pageInfo{hasNextPage endCursor} nodes{variants(first:50){nodes{id}}}}}}`,{id:SAMPLE_PROFILE,c:cursor});
  const pi=d.deliveryProfile.profileItems; for(const n of pi.nodes)for(const v of n.variants.nodes)ids.push(v.id);
  if(!pi.pageInfo.hasNextPage)break; cursor=pi.pageInfo.endCursor;}
console.log('dissociating',ids.length,'variants back to General...');
for(const c of chunk(ids,100)){await q(mut,{id:SAMPLE_PROFILE,profile:{variantsToDissociate:c}});await sleep(300);}
// 2) re-create General free band if missing
const cap=(()=>{try{return JSON.parse(fs.readFileSync('verification/migration-general-band-snapshot.json')).cap||'45';}catch{return '45';}})();
const g=(await q(`query($id:ID!){deliveryProfile(id:$id){profileLocationGroups{locationGroup{id}locationGroupZones(first:20){nodes{zone{id name}methodDefinitions(first:30){nodes{name}}}}}}}`,{id:GENERAL})).deliveryProfile;
let has=false,lg,zone;for(const gr of g.profileLocationGroups)for(const z of gr.locationGroupZones.nodes){if(z.zone.name!=='Domestic')continue;lg=gr.locationGroup.id;zone=z.zone.id;if(z.methodDefinitions.nodes.some(m=>m.name==='Free Shipping (No Tracking)'))has=true;}
if(!has){await q(mut,{id:GENERAL,profile:{locationGroupsToUpdate:[{id:lg,zonesToUpdate:[{id:zone,methodDefinitionsToCreate:[{name:'Free Shipping (No Tracking)',active:true,rateDefinition:{price:{amount:'0',currencyCode:'USD'}},priceConditionsToCreate:[{operator:'GREATER_THAN_OR_EQUAL_TO',criteria:{amount:'0',currencyCode:'USD'}},{operator:'LESS_THAN_OR_EQUAL_TO',criteria:{amount:cap,currencyCode:'USD'}}]}]}]}]}});console.log('re-created General free band $0-$'+cap);}
else console.log('General free band already present');
console.log('ROLLBACK COMPLETE');