← back to Tk 11331 Exec

d3b_discontinue_rollback.mjs

37 lines

// TK-11331 D3b discontinue ROLLBACK — reads data/d3b-discontinue-restore.jsonl and reverses
// each recorded change: status -> old_status (ACTIVE for DRAFT flips / unarchive for JUNK),
// and removes the discontinue-review tag we added (only if we added it). PG mirror first, then
// Shopify authoritative, then verify. DRY-RUN by default; --apply to write.
import fs from 'fs';
import {execSync} from 'child_process';
import {gql,sleep} from './lib.mjs';
const APPLY=process.argv.includes('--apply');
const MAP=process.argv.find(a=>a.endsWith('.jsonl'))||'data/d3b-discontinue-restore.jsonl';
const psql=sql=>execSync(`psql -h /tmp -d dw_unified -Atc ${JSON.stringify(sql)}`,{encoding:'utf8'}).trim();
const pgLit=s=>"'"+String(s).replace(/'/g,"''")+"'";
const toPgArray=arr=>'{'+arr.map(t=>'"'+String(t).replace(/\\/g,'\\\\').replace(/"/g,'\\"')+'"').join(',')+'}';
const READ=`query($id:ID!){product(id:$id){status tags}}`;
const UPDATE=`mutation($in:ProductInput!){productUpdate(input:$in){product{id status tags} userErrors{field message}}}`;
const recs=fs.readFileSync(MAP,'utf8').trim().split('\n').filter(Boolean).map(l=>JSON.parse(l));
console.log(`${APPLY?'APPLY':'DRY-RUN'} rollback — ${recs.length} records from ${MAP}`);
let done=0,fail=0;
for(const r of recs){
  const p=(await gql(READ,{id:r.gid})).product;
  if(!p){ fail++; console.log(`  ERR ${r.dw_sku} not found`); continue; }
  const restoreTags=(p.tags||[]).filter(t=> r.tag_added? t!==r.tag_added : true);
  if(!APPLY){ console.log(`  would ${p.status}->${r.old_status} ${r.dw_sku}${r.tag_added?` -${r.tag_added}`:''}`); done++; continue; }
  try{ let sql=`update shopify_products set status=${pgLit(r.old_status)}`;
    if(r.tag_added) sql+=`, tags=${pgLit(toPgArray(restoreTags))}`;
    sql+=` where shopify_id=${pgLit(r.gid)};`; psql(sql);
  }catch(e){ fail++; console.log(`  PG-ERR ${r.dw_sku}`); continue; }
  const input={id:r.gid,status:r.old_status}; if(r.tag_added) input.tags=restoreTags;
  const res=await gql(UPDATE,{in:input});
  if(res.productUpdate.userErrors.length){ fail++; console.log(`  SHOP-ERR ${r.dw_sku}`,JSON.stringify(res.productUpdate.userErrors)); continue; }
  await sleep(250);
  const after=(await gql(READ,{id:r.gid})).product;
  if(after.status!==r.old_status){ fail++; console.log(`  VERIFY-FAIL ${r.dw_sku} status=${after.status}`); continue; }
  done++; console.log(`  OK ${r.dw_sku} -> ${after.status}`);
}
console.log(`\nrollback ${APPLY?'APPLIED':'DRY'} — done=${done} fail=${fail}`);
process.exit(fail?1:0);