[object Object]

← back to Designer Wallcoverings

Scrub residual Newwall leak from metafields: global.Brand->real brand + delete junk custom.collection_name letter (74 products)

c03397290df083b0f07078adce83c11b3bdbe366 · 2026-06-26 10:24:50 -0700 · Steve

Files touched

Diff

commit c03397290df083b0f07078adce83c11b3bdbe366
Author: Steve <steve@designerwallcoverings.com>
Date:   Fri Jun 26 10:24:50 2026 -0700

    Scrub residual Newwall leak from metafields: global.Brand->real brand + delete junk custom.collection_name letter (74 products)
---
 shopify/scripts/scrub-newwall-metafields.js | 57 +++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/shopify/scripts/scrub-newwall-metafields.js b/shopify/scripts/scrub-newwall-metafields.js
new file mode 100644
index 00000000..34da4f14
--- /dev/null
+++ b/shopify/scripts/scrub-newwall-metafields.js
@@ -0,0 +1,57 @@
+#!/usr/bin/env node
+/**
+ * Scrub residual "Newwall" leak from metafields on the 74 remapped products:
+ *   - global.Brand "Newwall" -> real per-SKU brand (Coordonné / Tres Tintas)
+ *   - custom.collection_name single-letter junk (A/C/D/F = newwall.com browse nav) -> delete
+ * Targets the 74 by handle (vendor already fixed, so can't query vendor:Newwall).
+ * APPLY=1 to write. Brand map: /tmp/newwall_brand_map.tsv ; handles: /tmp/newwall_live.tsv
+ */
+const fs = require('path') && require('fs');
+const path = require('path');
+const ROOT = path.resolve(__dirname, '..', '..');
+const env = Object.fromEntries(fs.readFileSync(path.join(ROOT, '.env'), 'utf8').split('\n')
+  .filter(l => l.includes('=') && !l.trim().startsWith('#'))
+  .map(l => { const i = l.indexOf('='); return [l.slice(0, i).trim(), l.slice(i + 1).trim()]; }));
+const S = (env.SHOPIFY_STORE_DOMAIN || 'designer-laboratory-sandbox.myshopify.com').replace(/^https?:\/\//, '');
+const T = env.SHOPIFY_ADMIN_TOKEN;
+const APPLY = process.env.APPLY === '1';
+
+const brand = Object.fromEntries(fs.readFileSync('/tmp/newwall_brand_map.tsv', 'utf8').trim().split('\n')
+  .map(l => l.split('\t')).map(([k, v]) => [k.toUpperCase(), v]));
+const handles = fs.readFileSync('/tmp/newwall_live.tsv', 'utf8').trim().split('\n')
+  .map(l => l.split('\t')).map(([key, status, handle]) => ({ key: key.toUpperCase(), handle }));
+
+const gql = (q, v) => fetch(`https://${S}/admin/api/2024-01/graphql.json`,
+  { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Shopify-Access-Token': T }, body: JSON.stringify({ query: q, variables: v }) }).then(r => r.json());
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+(async () => {
+  let brandFixed = 0, collDeleted = 0, missing = 0;
+  for (const { key, handle } of handles) {
+    const b = brand[key];
+    if (!b) { missing++; console.log('SKIP no brand', handle); continue; }
+    const d = await gql(`query($h:String!){productByHandle(handle:$h){id metafields(first:60){edges{node{id namespace key value}}}}}`, { h: handle });
+    const p = d.data?.productByHandle;
+    if (!p) { console.log('SKIP not found', handle); continue; }
+    const mf = (p.metafields?.edges || []).map(e => e.node);
+    const brandMf = mf.find(m => m.namespace === 'global' && m.key === 'Brand');
+    const collMf = mf.find(m => m.namespace === 'custom' && m.key === 'collection_name');
+    const needBrand = brandMf && /newwall/i.test(brandMf.value);
+    const needColl = collMf && /^[A-Za-z]$/.test((collMf.value || '').trim());
+    if (!needBrand && !needColl) continue;
+    console.log(`${handle}: ${needBrand ? `Brand "${brandMf.value}"->"${b}" ` : ''}${needColl ? `del collection_name "${collMf.value}"` : ''}`);
+    if (APPLY) {
+      if (needBrand) {
+        const r = await gql(`mutation($m:[MetafieldsSetInput!]!){metafieldsSet(metafields:$m){userErrors{field message}}}`,
+          { m: [{ ownerId: p.id, namespace: 'global', key: 'Brand', type: 'single_line_text_field', value: b }] });
+        if ((r.data?.metafieldsSet?.userErrors || []).length) console.log('  !!brand', JSON.stringify(r.data.metafieldsSet.userErrors)); else brandFixed++;
+      }
+      if (needColl) {
+        const r = await gql(`mutation($id:ID!){metafieldDelete(input:{id:$id}){deletedId userErrors{field message}}}`, { id: collMf.id });
+        if ((r.data?.metafieldDelete?.userErrors || []).length) console.log('  !!coll', JSON.stringify(r.data.metafieldDelete.userErrors)); else collDeleted++;
+      }
+      await sleep(300);
+    }
+  }
+  console.log(`\n${APPLY ? 'APPLIED' : 'DRY RUN'} — Brand fixed: ${brandFixed}, collection_name deleted: ${collDeleted}, missing-brand: ${missing}`);
+})();

← 277d0d98 auto-save: 2026-06-26T10:14:48 (4 files) — pending-approval/  ·  back to Designer Wallcoverings  ·  Designtex: Sold Per Yard variant relabel + Bolt size (spec-t b1cc8522 →