diff --git a/scripts/migrate-tags.ts b/scripts/migrate-tags.ts new file mode 100644 index 0000000..bebcdfa --- /dev/null +++ b/scripts/migrate-tags.ts @@ -0,0 +1,50 @@ +import { db } from "@/db"; +import { services, tags } from "@/db/schema"; +import { eq, and } from "drizzle-orm"; + +async function migrate() { + console.log("Starting tags migration: assigning 'Offerta' tag to services migrated from offer_services...\n"); + + const offerServices = await db + .select({ id: services.id }) + .from(services) + .where(eq(services.migrated_from, "offer_services")); + + let taggedCount = 0; + let skippedCount = 0; + + for (const service of offerServices) { + const existing = await db + .select() + .from(tags) + .where( + and( + eq(tags.entity_type, "services"), + eq(tags.entity_id, service.id), + eq(tags.name, "Offerta") + ) + ) + .limit(1); + + if (existing.length > 0) { + skippedCount++; + continue; + } + + await db.insert(tags).values({ + entity_type: "services", + entity_id: service.id, + name: "Offerta", + }); + taggedCount++; + } + + console.log(`Assigned 'Offerta' tag: ${taggedCount} services, ${skippedCount} already tagged`); + console.log("\nMigration complete. Run scripts/validate-tags-migration.ts next."); + process.exit(0); +} + +migrate().catch((err) => { + console.error("Migration failed:", err); + process.exit(1); +}); diff --git a/scripts/validate-tags-migration.ts b/scripts/validate-tags-migration.ts new file mode 100644 index 0000000..3c2d582 --- /dev/null +++ b/scripts/validate-tags-migration.ts @@ -0,0 +1,55 @@ +import { db } from "@/db"; +import { services, tags } from "@/db/schema"; +import { eq, and, sql } from "drizzle-orm"; + +async function validate() { + let failures = 0; + + const [offerServicesCount] = await db + .select({ n: sql`count(*)::int` }) + .from(services) + .where(eq(services.migrated_from, "offer_services")); + + const [offerTagCount] = await db + .select({ n: sql`count(*)::int` }) + .from(tags) + .where(and(eq(tags.entity_type, "services"), eq(tags.name, "Offerta"))); + + console.log(`offer_services-derived services: ${offerServicesCount.n} | Offerta tags: ${offerTagCount.n}`); + + if (offerServicesCount.n !== offerTagCount.n) { + console.log("FAIL: Offerta tag count does not match offer_services-derived services count"); + failures++; + } else { + console.log("PASS: all offer_services-derived services have the Offerta tag"); + } + + const orphanedTags = await db.execute(sql` + SELECT COUNT(*)::int AS n FROM tags t + WHERE t.entity_type = 'services' + AND NOT EXISTS (SELECT 1 FROM services s WHERE s.id = t.entity_id) + `); + const orphanedTagsCount = (orphanedTags as unknown as Array<{ n: number }>)[0]?.n ?? 0; + if (orphanedTagsCount > 0) { + console.log(`FAIL: ${orphanedTagsCount} tags reference non-existent services`); + failures++; + } else { + console.log("PASS: no orphaned tag references"); + } + + const tagCounts = await db.execute(sql` + SELECT entity_type, COUNT(*)::int AS n FROM tags GROUP BY entity_type + `); + const counts = tagCounts as unknown as Array<{ entity_type: string; n: number }>; + for (const row of counts) { + console.log(`INFO: ${row.n} tags for entity_type=${row.entity_type}`); + } + + console.log(`\n${failures === 0 ? "ALL CHECKS PASSED" : `${failures} CHECK(S) FAILED`}`); + process.exit(failures === 0 ? 0 : 1); +} + +validate().catch((err) => { + console.error("Validation failed:", err); + process.exit(1); +});