← back to Fentucci Naturals
scripts/reconcile-width-backfill.js
43 lines
// TK-34 width reconcile: set global.width="36 inches" + drop the Needs-Width tag
// on the 504 Fentucci/Tokiwa DRAFTs, so TK-00058's Needs-Width guard stops
// reverting them and go-live.py can trickle-activate. DRAFT products only —
// nothing customer-facing flips here. Idempotent. --commit to write (dry by default).
const fs = require('fs'), { execSync } = require('child_process');
const TOK = fs.readFileSync('/Users/macstudio3/Projects/secrets-manager/.env', 'utf8')
.split('\n').find(l => l.startsWith('SHOPIFY_ADMIN_TOKEN=')).split('=')[1];
const STORE = 'designer-laboratory-sandbox.myshopify.com', API = '2024-10';
const DRY = !process.argv.includes('--commit');
const WIDTH = '36 inches';
const sleep = ms => new Promise(r => setTimeout(r, ms));
async function gql(q, v) { for (let a = 0; a < 4; a++) { try {
const r = await fetch(`https://${STORE}/admin/api/${API}/graphql.json`, { method: 'POST',
headers: { 'X-Shopify-Access-Token': TOK, 'Content-Type': 'application/json' },
body: JSON.stringify({ query: q, variables: v }) });
const j = await r.json(); if (j.errors && a < 3) { await sleep(1500); continue; } return j;
} catch { await sleep(1500); } } return {}; }
// product IDs of the exact batch (DRAFT · image · quote-tag · fentucci)
const rows = execSync(`psql "host=/tmp dbname=dw_unified" -tAc "SELECT shopify_product_id FROM tokiwa_catalog WHERE shopify_product_id IS NOT NULL"`)
.toString().trim().split('\n').map(s => s.trim()).filter(Boolean);
(async () => {
console.log(`mode=${DRY ? 'DRY' : 'COMMIT'} candidates=${rows.length} width="${WIDTH}"`);
let set = 0, untagged = 0, skip = 0, err = 0;
for (const pid of rows) {
const gid = `gid://shopify/Product/${pid}`;
if (DRY) { set++; continue; }
// set width metafield
const mr = await gql(`mutation($mfs:[MetafieldsSetInput!]!){metafieldsSet(metafields:$mfs){userErrors{message}}}`,
{ mfs: [{ ownerId: gid, namespace: 'global', key: 'width', type: 'single_line_text_field', value: WIDTH }] });
const me = mr.data?.metafieldsSet?.userErrors || [];
if (me.length) { err++; console.log(` ERR width ${pid}: ${JSON.stringify(me)}`); } else set++;
// drop Needs-Width tag
const tr = await gql(`mutation($id:ID!,$t:[String!]!){tagsRemove(id:$id,tags:$t){userErrors{message}}}`,
{ id: gid, t: ['Needs-Width'] });
if (!(tr.data?.tagsRemove?.userErrors || []).length) untagged++;
if (set % 50 === 0) console.log(` ...${set}/${rows.length}`);
await sleep(250);
}
console.log(`DONE ${DRY ? '(DRY)' : ''}: width_set=${set} untagged=${untagged} skip=${skip} err=${err}`);
})();