← 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));
})();