Skip to content

Commit

Permalink
Improved error logging from config (#10207)
Browse files Browse the repository at this point in the history
* fix: better error messaging on seed()

* chore: collection -> table for errors

* chore: changeset
  • Loading branch information
bholmesdev authored Feb 23, 2024
1 parent eb8bffe commit 5d4ff09
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 36 deletions.
5 changes: 5 additions & 0 deletions .changeset/young-pears-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@astrojs/db": patch
---

Improve error messaging when seeding invalid data.
18 changes: 14 additions & 4 deletions packages/db/src/core/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export const MISSING_PROJECT_ID_ERROR = `${red('▶ Directory not linked.')}
To link this directory to an Astro Studio project, run
${cyan('astro db link')}\n`;

export const STUDIO_CONFIG_MISSING_WRITABLE_COLLECTIONS_ERROR = (collectionName: string) => `${red(
`▶ Writable collection ${bold(collectionName)} requires Astro Studio or the ${yellow(
export const STUDIO_CONFIG_MISSING_WRITABLE_TABLE_ERROR = (tableName: string) => `${red(
`▶ Writable table ${bold(tableName)} requires Astro Studio or the ${yellow(
'unsafeWritable'
)} option.`
)}
Expand All @@ -34,10 +34,20 @@ export const MIGRATIONS_NOT_INITIALIZED = `${yellow(
'▶ No migrations found!'
)}\n\n To scaffold your migrations folder, run\n ${cyan('astro db sync')}\n`;

export const SEED_WRITABLE_IN_PROD_ERROR = (collectionName: string) => {
export const SEED_WRITABLE_IN_PROD_ERROR = (tableName: string) => {
return `${red(
`Writable tables should not be seeded in production with data().`
)} You can seed ${bold(
collectionName
tableName
)} in development mode only using the "mode" flag. See the docs for more: https://www.notion.so/astroinc/astrojs-db-README-dcf6fa10de9a4f528be56cee96e8c054?pvs=4#278aed3fc37e4cec80240d1552ff6ac5`;
};

export const SEED_ERROR = (tableName: string, error: string) => {
return `${red(`Error seeding table ${bold(tableName)}:`)}\n\n${error}`;
};

export const SEED_EMPTY_ARRAY_ERROR = (tableName: string) => {
// Drizzle error says "values() must be called with at least one value."
// This is specific to db.insert(). Prettify for seed().
return SEED_ERROR(tableName, `Empty array was passed. seed() must receive at least one value.`);
};
9 changes: 2 additions & 7 deletions packages/db/src/core/integration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import { DB_PATH } from '../consts.js';
import { createLocalDatabaseClient } from '../../runtime/db-client.js';
import { astroConfigWithDbSchema, type DBTables } from '../types.js';
import { type VitePlugin } from '../utils.js';
import {
STUDIO_CONFIG_MISSING_WRITABLE_COLLECTIONS_ERROR,
UNSAFE_WRITABLE_WARNING,
} from '../errors.js';
import { STUDIO_CONFIG_MISSING_WRITABLE_TABLE_ERROR, UNSAFE_WRITABLE_WARNING } from '../errors.js';
import { errorMap } from './error-map.js';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
Expand Down Expand Up @@ -76,9 +73,7 @@ function astroDBIntegration(): AstroIntegration {
const foundWritableCollection = Object.entries(tables).find(([, c]) => c.writable);
const writableAllowed = studio || unsafeWritable;
if (!writableAllowed && foundWritableCollection) {
logger.error(
STUDIO_CONFIG_MISSING_WRITABLE_COLLECTIONS_ERROR(foundWritableCollection[0])
);
logger.error(STUDIO_CONFIG_MISSING_WRITABLE_TABLE_ERROR(foundWritableCollection[0]));
process.exit(1);
}
// Using writable tables with the opt-in flag. Warn them to let them
Expand Down
71 changes: 46 additions & 25 deletions packages/db/src/core/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ import { bold } from 'kleur/colors';
import { type SQL, sql, getTableName } from 'drizzle-orm';
import { SQLiteAsyncDialect, type SQLiteInsert } from 'drizzle-orm/sqlite-core';
import type { AstroIntegrationLogger } from 'astro';
import type { DBUserConfig } from '../core/types.js';
import type {
ColumnsConfig,
DBUserConfig,
MaybeArray,
ResolvedCollectionConfig,
} from '../core/types.js';
import { hasPrimaryKey } from '../runtime/index.js';
import { isSerializedSQL } from '../runtime/types.js';
import { SEED_WRITABLE_IN_PROD_ERROR } from './errors.js';
import { SEED_EMPTY_ARRAY_ERROR, SEED_ERROR, SEED_WRITABLE_IN_PROD_ERROR } from './errors.js';

const sqlite = new SQLiteAsyncDialect();

Expand Down Expand Up @@ -51,40 +56,56 @@ export async function seedData({
logger?: AstroIntegrationLogger;
mode: 'dev' | 'build';
}) {
const dataFns = Array.isArray(data) ? data : [data];
try {
const dataFns = Array.isArray(data) ? data : [data];
for (const dataFn of dataFns) {
await dataFn({
seed: async ({ table, writable }, values) => {
if (writable && mode === 'build' && process.env.ASTRO_DB_TEST_ENV !== '1') {
(logger ?? console).error(SEED_WRITABLE_IN_PROD_ERROR(getTableName(table)));
process.exit(1);
seed: async (config, values) => {
seedErrorChecks(mode, config, values);
try {
await db.insert(config.table).values(values as any);
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
throw new Error(SEED_ERROR(getTableName(config.table), msg));
}
await db.insert(table).values(values as any);
},
seedReturning: async ({ table, writable }, values) => {
if (writable && mode === 'build' && process.env.ASTRO_DB_TEST_ENV !== '1') {
(logger ?? console).error(SEED_WRITABLE_IN_PROD_ERROR(getTableName(table)));
process.exit(1);
}
let result: SQLiteInsert<any, any, any, any> = db
.insert(table)
.values(values as any)
.returning();
if (!Array.isArray(values)) {
result = result.get();
seedReturning: async (config, values) => {
seedErrorChecks(mode, config, values);
try {
let result: SQLiteInsert<any, any, any, any> = db
.insert(config.table)
.values(values as any)
.returning();
if (!Array.isArray(values)) {
result = result.get();
}
return result;
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
throw new Error(SEED_ERROR(getTableName(config.table), msg));
}
return result;
},
db,
mode,
});
}
} catch (error) {
(logger ?? console).error(
`Failed to seed data. Did you update to match recent schema changes?`
);
(logger ?? console).error(error as string);
} catch (e) {
if (!(e instanceof Error)) throw e;
(logger ?? console).error(e.message);
}
}

function seedErrorChecks<T extends ColumnsConfig>(
mode: 'dev' | 'build',
{ table, writable }: ResolvedCollectionConfig<T, boolean>,
values: MaybeArray<unknown>
) {
const tableName = getTableName(table);
if (writable && mode === 'build' && process.env.ASTRO_DB_TEST_ENV !== '1') {
throw new Error(SEED_WRITABLE_IN_PROD_ERROR(tableName));
}
if (Array.isArray(values) && values.length === 0) {
throw new Error(SEED_EMPTY_ARRAY_ERROR(tableName));
}
}

Expand Down

0 comments on commit 5d4ff09

Please sign in to comment.