feat(F-114): completed feature
This commit is contained in:
91
project/scripts/seed-legacy-categories.mjs
Normal file
91
project/scripts/seed-legacy-categories.mjs
Normal file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* seed-legacy-categories — F-114
|
||||
*
|
||||
* Imports the legacy OpenCart `oc_category_description` rows into the
|
||||
* current catalog and brand tables. Reads the legacy list from the
|
||||
* pure helper at `src/modules/categories/legacy/legacy-catalog` so the
|
||||
* mapping stays in sync with the tests.
|
||||
*
|
||||
* Idempotent: skips entries whose name (case-insensitive) already exists in
|
||||
* the target table, and uniquifies slugs against existing ones.
|
||||
*
|
||||
* Usage:
|
||||
* npm run seed:legacy-categories # apply
|
||||
* DATABASE_URL=... node scripts/seed-legacy-categories.mjs --dry-run
|
||||
*/
|
||||
import pg from 'pg';
|
||||
import { buildImportPlan } from '../dist/modules/categories/legacy/legacy-catalog.js';
|
||||
|
||||
const DATABASE_URL = process.env.DATABASE_URL;
|
||||
if (!DATABASE_URL) {
|
||||
console.error('DATABASE_URL is required');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const args = new Set(process.argv.slice(2));
|
||||
const dryRun = args.has('--dry-run');
|
||||
|
||||
const pool = new pg.Pool({ connectionString: DATABASE_URL, max: 1 });
|
||||
|
||||
try {
|
||||
const categories = await pool.query('SELECT id, name, slug FROM categories_categories');
|
||||
const brands = await pool.query('SELECT id, name, slug FROM brands_brands');
|
||||
const categoryNameIndex = new Set(categories.rows.map((r) => r.name.toLowerCase()));
|
||||
const brandNameIndex = new Set(brands.rows.map((r) => r.name.toLowerCase()));
|
||||
const slugIndex = new Set(
|
||||
[...categories.rows, ...brands.rows].map((r) => r.slug.toLowerCase()),
|
||||
);
|
||||
const parentBySlug = new Map(categories.rows.map((r) => [r.slug, r.id]));
|
||||
|
||||
const plan = buildImportPlan(categoryNameIndex, brandNameIndex, slugIndex);
|
||||
|
||||
// Validate that every parentSlug resolves to an existing category.
|
||||
const missingParents = new Set();
|
||||
for (const cat of plan.categories) {
|
||||
if (cat.parentSlug && !parentBySlug.has(cat.parentSlug)) missingParents.add(cat.parentSlug);
|
||||
}
|
||||
if (missingParents.size > 0) {
|
||||
console.error(`Missing parent categories: ${[...missingParents].join(', ')}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Planned: ${plan.categories.length} categories, ${plan.brands.length} brands`);
|
||||
if (dryRun) {
|
||||
console.log('DRY RUN — nothing will be written.');
|
||||
for (const cat of plan.categories) {
|
||||
console.log(` + category ${cat.name} (slug=${cat.slug}, parent=${cat.parentSlug ?? 'ROOT'})`);
|
||||
}
|
||||
for (const brand of plan.brands) {
|
||||
console.log(` + brand ${brand.name} (slug=${brand.slug})`);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
for (const cat of plan.categories) {
|
||||
const parentId = cat.parentSlug ? parentBySlug.get(cat.parentSlug) : null;
|
||||
await pool.query(
|
||||
`INSERT INTO categories_categories (parent_id, name, slug, is_parent)
|
||||
VALUES ($1, $2, $3, false)
|
||||
ON CONFLICT (slug) DO NOTHING`,
|
||||
[parentId, cat.name, cat.slug],
|
||||
);
|
||||
console.log(` + category ${cat.name} (slug=${cat.slug}, parent=${cat.parentSlug ?? 'ROOT'})`);
|
||||
}
|
||||
|
||||
for (const brand of plan.brands) {
|
||||
await pool.query(
|
||||
`INSERT INTO brands_brands (name, slug) VALUES ($1, $2)
|
||||
ON CONFLICT (slug) DO NOTHING`,
|
||||
[brand.name, brand.slug],
|
||||
);
|
||||
console.log(` + brand ${brand.name} (slug=${brand.slug})`);
|
||||
}
|
||||
|
||||
console.log(`\nDone. Inserted ${plan.categories.length} categories and ${plan.brands.length} brands.`);
|
||||
} catch (error) {
|
||||
console.error('Error:', error.message);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await pool.end();
|
||||
}
|
||||
Reference in New Issue
Block a user