← back to Dw Scrape Scheduler

run-vendor.js

47 lines

#!/usr/bin/env node
// Scrape ONE vendor via its feed adapter -> snapshot to data/snapshots/.
// PROOF STAGE: snapshot + count only, NO canonical dw_unified write yet (per-vendor
// staging-table mapping is the next staged increment). Idempotent, $0 local.
const fs = require('fs');
const path = require('path');

const ADAPTERS = { shopify: require('./adapters/shopify'), woo: require('./adapters/woo') };
const { writeStaging } = require('./lib/write-staging');
const SNAP_DIR = path.join(__dirname, 'data', 'snapshots');

// Guard against a broken/partial feed wiping a catalog's freshness: if a scrape
// returns implausibly few products, snapshot it but SKIP the DB write.
const MIN_PLAUSIBLE = 3;

async function runVendor(entry, { db = true } = {}) {
  const adapter = ADAPTERS[entry.adapter];
  if (!adapter) return { vendor: entry.vendor, ok: false, skipped: true, reason: `adapter '${entry.adapter}' not yet implemented` };
  const t0 = Date.now();
  try {
    const products = await adapter.scrape(entry.base_url);
    fs.mkdirSync(SNAP_DIR, { recursive: true });
    const stamp = new Date().toISOString().slice(0, 10).replace(/-/g, '');
    const file = path.join(SNAP_DIR, `${entry.vendor_code}-${stamp}.json`);
    fs.writeFileSync(file, JSON.stringify({ vendor_code: entry.vendor_code, scraped_at: new Date().toISOString(), count: products.length, products }, null, 0));
    let staged = null;
    if (db) {
      if (products.length < MIN_PLAUSIBLE) staged = { skipped: `only ${products.length} products — DB write skipped (feed suspect)` };
      else staged = await writeStaging(entry.vendor_code, products);
    }
    return { vendor: entry.vendor, ok: true, count: products.length, ms: Date.now() - t0, file, staged };
  } catch (e) {
    return { vendor: entry.vendor, ok: false, reason: e.message, ms: Date.now() - t0 };
  }
}

module.exports = { runVendor };

// CLI: node run-vendor.js <vendor_code>  (looks the entry up in the manifest)
if (require.main === module) {
  const code = process.argv[2];
  const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, 'data', 'manifest.json'), 'utf8'));
  const entry = [...manifest.tier_a, ...manifest.parked_headless].find(e => e.vendor_code === code);
  if (!entry) { console.error(`vendor_code '${code}' not in manifest tier_a/parked_headless`); process.exit(1); }
  runVendor(entry).then(r => { console.log(JSON.stringify(r)); process.exit(r.ok ? 0 : 1); });
}