← back to Secrets Manager

coordonne-collab-audit/tag-collab.js

52 lines

#!/usr/bin/env node
// ADD-only tag applier for Coordonné collab smart-collections.
// Uses Shopify GraphQL tagsAdd — appends the tag, never strips existing tags.
// Usage: node tag-collab.js <match-file.json> "<Exact Tag>"
const fs = require('fs');
const path = require('path');

const ENV = fs.readFileSync(path.join(__dirname, '..', '.env'), 'utf8');
const TOKEN = (ENV.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m) || [])[1];
const DOMAIN = 'designer-laboratory-sandbox.myshopify.com';
const VER = '2024-10';
if (!TOKEN) { console.error('no token'); process.exit(1); }

const matchFile = process.argv[2];
const TAG = process.argv[3];
if (!matchFile || !TAG) { console.error('usage: tag-collab.js <file.json> "<tag>"'); process.exit(1); }

const rows = JSON.parse(fs.readFileSync(matchFile, 'utf8'));

async function gql(query, variables) {
  const r = await fetch(`https://${DOMAIN}/admin/api/${VER}/graphql.json`, {
    method: 'POST',
    headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, variables }),
  });
  return r.json();
}

const M = `mutation add($id: ID!, $tags: [String!]!) {
  tagsAdd(id: $id, tags: $tags) {
    node { id }
    userErrors { field message }
  }
}`;

(async () => {
  let ok = 0, err = 0, skipped = 0;
  const results = [];
  for (const row of rows) {
    const gid = row.shopify_id.startsWith('gid://') ? row.shopify_id : `gid://shopify/Product/${row.shopify_id}`;
    // ADD-only guard: skip if already carries the exact tag
    if (row.tags && row.tags.includes(`"${TAG}"`)) { skipped++; results.push({dw_sku: row.dw_sku, status: 'already-tagged'}); continue; }
    const res = await gql(M, { id: gid, tags: [TAG] });
    const ue = res?.data?.tagsAdd?.userErrors;
    if (ue && ue.length) { err++; results.push({dw_sku: row.dw_sku, status: 'error', errors: ue}); }
    else if (res.errors) { err++; results.push({dw_sku: row.dw_sku, status: 'gql-error', errors: res.errors}); }
    else { ok++; results.push({dw_sku: row.dw_sku, status: 'tagged'}); }
    await new Promise(r => setTimeout(r, 550)); // ~1.8/s, well under 2/s REST-equiv
  }
  console.log(JSON.stringify({ tag: TAG, total: rows.length, tagged: ok, already: skipped, errors: err, results }, null, 2));
})();