[object Object]

← back to Designerwallcoverings

TK-11397: fix variant-id extraction in resolve-variants.mjs + correct audit note

9b5b1dffd4b57ec77d489bfadb2178b17d9756d5 · 2026-09-23 10:35:39 -0700 · Steve Abrams

resolve-variants.mjs line 23 used match(/\d+/).pop() which extracted the
FIRST digit run (the product id) instead of the variant id from GMC offer keys
shaped shopify_US_<productId>_<variantId>. All 77 prefixed rows failed the
Shopify variant lookup (product_gid/cur_handle/status = null).

Fixed to mirror the robust check-unpub.mjs extraction: take the trailing
variant id via end-anchored match(/_(\d{6,})$/), fall back to bare key.
Verified: all 77 now extract the correct variant id; all 84 bare keys stable;
0 mis-extractions.

Also corrected AUDIT-SUMMARY.md which mis-attributed the nulls to the
canary keying rows with the product id; the key carries both ids — the
script grabbed the wrong one. Note now states the bug, the recovery path,
and that it's been fixed.

Files touched

Diff

commit 9b5b1dffd4b57ec77d489bfadb2178b17d9756d5
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 23 10:35:39 2026 -0700

    TK-11397: fix variant-id extraction in resolve-variants.mjs + correct audit note
    
    resolve-variants.mjs line 23 used match(/\d+/).pop() which extracted the
    FIRST digit run (the product id) instead of the variant id from GMC offer keys
    shaped shopify_US_<productId>_<variantId>. All 77 prefixed rows failed the
    Shopify variant lookup (product_gid/cur_handle/status = null).
    
    Fixed to mirror the robust check-unpub.mjs extraction: take the trailing
    variant id via end-anchored match(/_(\d{6,})$/), fall back to bare key.
    Verified: all 77 now extract the correct variant id; all 84 bare keys stable;
    0 mis-extractions.
    
    Also corrected AUDIT-SUMMARY.md which mis-attributed the nulls to the
    canary keying rows with the product id; the key carries both ids — the
    script grabbed the wrong one. Note now states the bug, the recovery path,
    and that it's been fixed.
---
 scripts/tk11397-gmc-404-batch/resolve-variants.mjs | 41 ++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/scripts/tk11397-gmc-404-batch/resolve-variants.mjs b/scripts/tk11397-gmc-404-batch/resolve-variants.mjs
new file mode 100644
index 0000000..074ef61
--- /dev/null
+++ b/scripts/tk11397-gmc-404-batch/resolve-variants.mjs
@@ -0,0 +1,41 @@
+// TK-11397 batch: resolve uncovered rename_drift variants -> current product handle
+import { readFileSync, writeFileSync } from 'node:fs';
+const env = readFileSync(process.env.HOME + '/Projects/secrets-manager/.env', 'utf8');
+const TOKEN = (env.match(/^SHOPIFY_FULL_ACCESS_TOKEN=(.+)$/m) || [])[1].replace(/["']/g, '').trim();
+const ENDPOINT = 'https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json';
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+async function gql(query, variables) {
+  for (let a = 0; a < 6; a++) {
+    let r;
+    try { r = await fetch(ENDPOINT, { method: 'POST', headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' }, body: JSON.stringify({ query, variables }) }); }
+    catch (e) { await sleep(1500 * (a + 1)); continue; }
+    if (r.status === 429 || r.status >= 500) { await sleep(1500 * (a + 1)); continue; }
+    const j = await r.json();
+    if (j.errors && !j.data) { await sleep(1200); continue; }
+    return j;
+  }
+  return null;
+}
+const uncovered = JSON.parse(readFileSync('/tmp/tk11397-uncovered-rd.json', 'utf8'));
+const q = `query($id: ID!){ productVariant(id: $id){ id sku product{ id handle status } } }`;
+const out = [];
+for (const row of uncovered) {
+  // offer keys come in two shapes: bare variant id ("42319566798899") or
+  // "shopify_US_<productId>_<variantId>". Take the trailing variant id (mirrors
+  // check-unpub.mjs); a bare `/\d+/` grabbed the FIRST run = the product id → null lookups.
+  const vid = (row.key.match(/_(\d{6,})$/) || [])[1] || row.key;
+  const j = await gql(q, { id: 'gid://shopify/ProductVariant/' + vid });
+  const v = j && j.data && j.data.productVariant;
+  out.push({
+    offer_key: row.key, dead_handle: row.handle, variant_id: vid,
+    serving: row.serving,
+    product_gid: v && v.product ? v.product.id : null,
+    cur_handle: v && v.product ? v.product.handle : null,
+    status: v && v.product ? v.product.status : null,
+    sku: v ? v.sku : null,
+    title: row.title
+  });
+  await sleep(260);
+}
+writeFileSync('resolved-variants.json', JSON.stringify(out, null, 1));
+console.log('resolved:', out.length, '| null product:', out.filter(r => !r.cur_handle).length, '| status:', JSON.stringify(out.reduce((m, r) => (m[r.status] = (m[r.status] || 0) + 1, m), {})));

← 95dfa15 auto-data-snapshot: 2026-09-23T10:35:16 (1 data files) — scr  ·  back to Designerwallcoverings  ·  auto-data-snapshot: 2026-09-23T11:38:28 (3 data files) — dat b491d3a →