← back to Rebel Walls Push
scripts/rollback-TK11248.js
58 lines
'use strict';
/* Rollback for the TK-11248 batch. Reverses whichever step was applied.
* --step=11238 restores Aries title from TK-11238-restore.json
* --step=10405 recreates the 12 deleted Default-Title variants from TK-10405-restore-map.json
* (Shopify assigns NEW variant ids; SKU/price/options restored)
* DRY-RUN by default; --apply to fire. Rollback is the undo of a recorded write,
* so it only needs --apply (no sentinel) — but it prints exactly what it will do first.
*
* Usage:
* node rollback-TK11248.js --step=11238 # dry-run
* node rollback-TK11248.js --step=11238 --apply # restore title
* node rollback-TK11248.js --step=10405 --apply # recreate 12 variants
*/
const fs = require('fs');
const path = require('path');
const { gqlRetry, sleep } = require('./_shop');
const DATA = path.join(__dirname, '..', 'data');
const argv = process.argv.slice(2);
const APPLY = argv.includes('--apply');
const STEP = (argv.find(x => x.startsWith('--step=')) || '').split('=')[1];
const PRODUCT_UPDATE = `mutation($input: ProductInput!){ productUpdate(input:$input){ product{ id title } userErrors{ field message } } }`;
const VARIANTS_CREATE = `mutation($productId: ID!, $variants:[ProductVariantsBulkInput!]!){
productVariantsBulkCreate(productId:$productId, variants:$variants){
productVariants{ id title sku price } userErrors{ field message } } }`;
async function rb11238() {
const r = JSON.parse(fs.readFileSync(path.join(DATA, 'TK-11238-restore.json'), 'utf8'));
console.log(`ROLLBACK TK-11238: product ${r.product_id} title -> "${r.restore_title}"`);
if (!APPLY) { console.log('[dry-run]'); return; }
const w = await gqlRetry(PRODUCT_UPDATE, { input: { id: r.product_gid, title: r.restore_title } }, 'rb-title');
const ue = w.json.data.productUpdate.userErrors;
console.log(ue && ue.length ? `userErrors ${JSON.stringify(ue)}` : `restored: "${w.json.data.productUpdate.product.title}"`);
}
async function rb10405() {
const m = JSON.parse(fs.readFileSync(path.join(DATA, 'TK-10405-restore-map.json'), 'utf8'));
console.log(`ROLLBACK TK-10405: recreate ${m.variants.length} Default-Title variants`);
for (const v of m.variants) {
// optionValues must map to the product's option; Default Title = single default option value.
const optionValues = (v.options && v.options.length)
? v.options.map(o => ({ optionName: o.name, name: o.value })) : [{ optionName: 'Title', name: 'Default Title' }];
console.log(` ${v.pid} recreate ${v.sku}@$${v.price}`);
if (!APPLY) { console.log(' [dry-run]'); continue; }
const w = await gqlRetry(VARIANTS_CREATE, { productId: v.product_gid,
variants: [{ price: v.price, inventoryItem: { sku: v.sku }, optionValues }] }, `rb-create:${v.pid}`);
const ue = w.json.data.productVariantsBulkCreate.userErrors;
console.log(ue && ue.length ? ` userErrors ${JSON.stringify(ue)}` : ` recreated ${w.json.data.productVariantsBulkCreate.productVariants.map(x=>x.id).join(',')}`);
await sleep(400);
}
}
(async () => {
console.log(`### rollback-TK11248 STEP=${STEP} APPLY=${APPLY}`);
if (STEP === '11238') await rb11238();
else if (STEP === '10405') await rb10405();
else { console.error('specify --step=11238 or --step=10405'); process.exit(1); }
console.log(APPLY ? 'rollback complete.' : 'dry-run complete, no writes.');
})().catch(e => { console.error('FATAL', e); process.exit(2); });