diff --git a/apps/api/package.json b/apps/api/package.json index 585cee2..0d158fc 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -17,7 +17,8 @@ "db:migrate:rollback": "bun run src/database/migrate.ts rollback", "db:migrate:up": "bun run src/database/migrate.ts up", "db:migrate:down": "bun run src/database/migrate.ts down", - "db:migrate:reset": "bun run src/database/migrate.ts reset && bun run db:types:gen" + "db:migrate:reset": "bun run src/database/migrate.ts reset && bun run db:types:gen", + "db:seed": "bun run src/database/seed.ts" }, "dependencies": { "@blade-and-brawn/calculator": "workspace:*", diff --git a/apps/api/src/database/seed.ts b/apps/api/src/database/seed.ts new file mode 100644 index 0000000..4262ba6 --- /dev/null +++ b/apps/api/src/database/seed.ts @@ -0,0 +1,80 @@ +import { + DEFAULT_STANDARDS_DATA, + DEFAULT_STANDARDS_PARAMS, + StandardsDataSchema, + StandardsParamsSchema, +} from "@blade-and-brawn/calculator"; +import { Value } from "@sinclair/typebox/value"; +import { db } from "./db"; +import { env, log } from "../util"; + +const DEFAULT_NAME = "Default"; + +async function seedStandardsDataset(): Promise { + const existing = await db.selectFrom("standards_datasets") + .select(["id"]) + .where("name", "=", DEFAULT_NAME) + .executeTakeFirst(); + if (existing) { + log.info({ id: existing.id }, "default standards dataset already seeded, skipping"); + return existing.id; + } + + Value.Assert(StandardsDataSchema, DEFAULT_STANDARDS_DATA); + const result = await db.insertInto("standards_datasets") + .values({ name: DEFAULT_NAME, data: DEFAULT_STANDARDS_DATA }) + .returning("id") + .executeTakeFirstOrThrow(); + log.info({ id: result.id }, "seeded default standards dataset"); + return result.id; +} + +async function seedStandardsConfig(datasetId: string): Promise { + const existing = await db.selectFrom("standards_configs") + .select(["id"]) + .where("name", "=", DEFAULT_NAME) + .executeTakeFirst(); + if (existing) { + log.info({ id: existing.id }, "default standards config already seeded, skipping"); + return; + } + + const hasSelected = await db.selectFrom("standards_configs") + .select(["id"]) + .where("is_selected", "=", true) + .executeTakeFirst(); + + Value.Assert(StandardsParamsSchema, DEFAULT_STANDARDS_PARAMS); + const result = await db.insertInto("standards_configs") + .values({ + name: DEFAULT_NAME, + dataset_id: datasetId, + params: DEFAULT_STANDARDS_PARAMS, + is_selected: !hasSelected, + }) + .returning("id") + .executeTakeFirstOrThrow(); + log.info({ id: result.id, selected: !hasSelected }, "seeded default standards config"); +} + +(async () => { + const answer = prompt(`Seed the database (${env.DATABASE_URL}) with default standards data? (y/N)`); + if (answer?.trim().toLowerCase() !== "y") { + log.info("Aborted"); + await db.destroy(); + return; + } + + try { + const datasetId = await seedStandardsDataset(); + await seedStandardsConfig(datasetId); + log.info("seed finished"); + } + catch (err) { + log.error({ err }, "seed failed"); + process.exit(1); + } + finally { + await db.destroy(); + } +})(); diff --git a/packages/calculator/src/index.ts b/packages/calculator/src/index.ts index 3e24101..f7a917c 100644 --- a/packages/calculator/src/index.ts +++ b/packages/calculator/src/index.ts @@ -35,6 +35,7 @@ const metricPriority = (m: NumberMetric) => { }; export const DEFAULT_STANDARDS_DATA = defaultStandardsData as StandardsData; +export const DEFAULT_STANDARDS_PARAMS = defaultConfig as StandardsParams; export class LevelCalculator { Standards: Standards;