mirror of
https://github.com/withastro/astro.git
synced 2025-03-24 23:21:57 -05:00
Fix isDbError()
for remote errors (#11027)
* fix: use LibsqlError for remote db errors * chore: remove unused drizzle.ts * fix(test): return expected `error` object * fix: error detail formatting * feat(test): error messages with remote adapter * feat(test): add code to test body * chore: changeset
This commit is contained in:
parent
296cd7e229
commit
eb1d9a447b
7 changed files with 66 additions and 43 deletions
5
.changeset/tidy-cows-change.md
Normal file
5
.changeset/tidy-cows-change.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@astrojs/db": patch
|
||||
---
|
||||
|
||||
Fix `isDbError()` returning `false` for remote database errors. Astro will now return a `LibsqlError` in development and production.
|
|
@ -4,7 +4,7 @@ import type { LibSQLDatabase } from 'drizzle-orm/libsql';
|
|||
import { drizzle as drizzleLibsql } from 'drizzle-orm/libsql';
|
||||
import { type SqliteRemoteDatabase, drizzle as drizzleProxy } from 'drizzle-orm/sqlite-proxy';
|
||||
import { z } from 'zod';
|
||||
import { AstroDbError, safeFetch } from './utils.js';
|
||||
import { DetailedLibsqlError, safeFetch } from './utils.js';
|
||||
|
||||
const isWebContainer = !!process.versions?.webcontainer;
|
||||
|
||||
|
@ -65,7 +65,10 @@ export function createRemoteDatabaseClient(appToken: string, remoteDbURL: string
|
|||
const json = await res.json();
|
||||
remoteResult = remoteResultSchema.parse(json);
|
||||
} catch (e) {
|
||||
throw new AstroDbError(await getUnexpectedResponseMessage(res));
|
||||
throw new DetailedLibsqlError({
|
||||
message: await getUnexpectedResponseMessage(res),
|
||||
code: KNOWN_ERROR_CODES.SQL_QUERY_FAILED,
|
||||
});
|
||||
}
|
||||
|
||||
if (method === 'run') return remoteResult;
|
||||
|
@ -107,7 +110,10 @@ export function createRemoteDatabaseClient(appToken: string, remoteDbURL: string
|
|||
const json = await res.json();
|
||||
remoteResults = z.array(remoteResultSchema).parse(json);
|
||||
} catch (e) {
|
||||
throw new AstroDbError(await getUnexpectedResponseMessage(res));
|
||||
throw new DetailedLibsqlError({
|
||||
message: await getUnexpectedResponseMessage(res),
|
||||
code: KNOWN_ERROR_CODES.SQL_QUERY_FAILED,
|
||||
});
|
||||
}
|
||||
let results: any[] = [];
|
||||
for (const [idx, rawResult] of remoteResults.entries()) {
|
||||
|
@ -151,22 +157,25 @@ const KNOWN_ERROR_CODES = {
|
|||
};
|
||||
|
||||
const getUnexpectedResponseMessage = async (response: Response) =>
|
||||
`Unexpected response from remote database:\n(Status ${response.status}) ${await response.text()}`;
|
||||
`Unexpected response from remote database:\n(Status ${response.status}) ${await response.clone().text()}`;
|
||||
|
||||
async function parseRemoteError(response: Response): Promise<AstroDbError> {
|
||||
async function parseRemoteError(response: Response): Promise<DetailedLibsqlError> {
|
||||
let error;
|
||||
try {
|
||||
error = errorSchema.parse(await response.json()).error;
|
||||
error = errorSchema.parse(await response.clone().json()).error;
|
||||
} catch (e) {
|
||||
return new AstroDbError(await getUnexpectedResponseMessage(response));
|
||||
return new DetailedLibsqlError({
|
||||
message: await getUnexpectedResponseMessage(response),
|
||||
code: KNOWN_ERROR_CODES.SQL_QUERY_FAILED,
|
||||
});
|
||||
}
|
||||
// Strip LibSQL error prefixes
|
||||
let details =
|
||||
error.details?.replace(/.*SQLite error: /, '') ??
|
||||
`(Code ${error.code}) \nError querying remote database.`;
|
||||
let baseDetails = error.details?.replace(/.*SQLite error: /, '') ?? 'Error querying remote database.';
|
||||
// Remove duplicated "code" in details
|
||||
const details = baseDetails.slice(baseDetails.indexOf(':') + 1).trim();
|
||||
let hint = `See the Astro DB guide for query and push instructions: https://docs.astro.build/en/guides/astro-db/#query-your-database`;
|
||||
if (error.code === KNOWN_ERROR_CODES.SQL_QUERY_FAILED && details.includes('no such table')) {
|
||||
hint = `Did you run \`astro db push\` to push your latest table schemas?`;
|
||||
}
|
||||
return new AstroDbError(details, hint);
|
||||
return new DetailedLibsqlError({ message: details, code: error.code, hint });
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
// Drizzle utilities we expose directly from `astro:db`
|
||||
export {
|
||||
sql,
|
||||
eq,
|
||||
gt,
|
||||
gte,
|
||||
lt,
|
||||
lte,
|
||||
ne,
|
||||
isNull,
|
||||
isNotNull,
|
||||
inArray,
|
||||
notInArray,
|
||||
exists,
|
||||
notExists,
|
||||
between,
|
||||
notBetween,
|
||||
like,
|
||||
notIlike,
|
||||
not,
|
||||
asc,
|
||||
desc,
|
||||
and,
|
||||
or,
|
||||
} from 'drizzle-orm';
|
|
@ -1,3 +1,4 @@
|
|||
import { LibsqlError } from '@libsql/client';
|
||||
import { AstroError } from 'astro/errors';
|
||||
|
||||
const isWindows = process?.platform === 'win32';
|
||||
|
@ -25,6 +26,22 @@ export class AstroDbError extends AstroError {
|
|||
name = 'Astro DB Error';
|
||||
}
|
||||
|
||||
export class DetailedLibsqlError extends LibsqlError {
|
||||
name = 'Astro DB Error';
|
||||
hint?: string;
|
||||
|
||||
constructor({
|
||||
message,
|
||||
code,
|
||||
hint,
|
||||
rawCode,
|
||||
cause,
|
||||
}: { message: string; code: string; hint?: string; rawCode?: number; cause?: Error }) {
|
||||
super(message, code, rawCode, cause);
|
||||
this.hint = hint;
|
||||
}
|
||||
}
|
||||
|
||||
function slash(path: string) {
|
||||
const isExtendedLengthPath = path.startsWith('\\\\?\\');
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { expect } from 'chai';
|
||||
import { loadFixture } from '../../astro/test/test-utils.js';
|
||||
import { setupRemoteDbServer } from './test-utils.js';
|
||||
|
||||
const foreignKeyConstraintError =
|
||||
'LibsqlError: SQLITE_CONSTRAINT_FOREIGNKEY: FOREIGN KEY constraint failed';
|
||||
|
@ -25,18 +26,31 @@ describe('astro:db - error handling', () => {
|
|||
|
||||
it('Raises foreign key constraint LibsqlError', async () => {
|
||||
const json = await fixture.fetch('/foreign-key-constraint.json').then((res) => res.json());
|
||||
expect(json.error).to.equal(foreignKeyConstraintError);
|
||||
expect(json).to.deep.equal({
|
||||
message: foreignKeyConstraintError,
|
||||
code: 'SQLITE_CONSTRAINT_FOREIGNKEY',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('build', () => {
|
||||
describe('build --remote', () => {
|
||||
let remoteDbServer;
|
||||
|
||||
before(async () => {
|
||||
remoteDbServer = await setupRemoteDbServer(fixture.config);
|
||||
await fixture.build();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await remoteDbServer?.stop();
|
||||
});
|
||||
|
||||
it('Raises foreign key constraint LibsqlError', async () => {
|
||||
const json = await fixture.readFile('/foreign-key-constraint.json');
|
||||
expect(JSON.parse(json).error).to.equal(foreignKeyConstraintError);
|
||||
expect(JSON.parse(json)).to.deep.equal({
|
||||
message: foreignKeyConstraintError,
|
||||
code: 'SQLITE_CONSTRAINT_FOREIGNKEY',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,8 +11,8 @@ export const GET: APIRoute = async () => {
|
|||
});
|
||||
} catch (e) {
|
||||
if (isDbError(e)) {
|
||||
return new Response(JSON.stringify({ error: `LibsqlError: ${e.message}` }));
|
||||
return new Response(JSON.stringify({ message: `LibsqlError: ${e.message}`, code: e.code }));
|
||||
}
|
||||
}
|
||||
return new Response(JSON.stringify({ error: 'Did not raise expected exception' }));
|
||||
return new Response(JSON.stringify({ message: 'Did not raise expected exception' }));
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createServer } from 'node:http';
|
||||
import { createClient } from '@libsql/client';
|
||||
import { LibsqlError, createClient } from '@libsql/client';
|
||||
import { z } from 'zod';
|
||||
import { cli } from '../dist/core/cli/index.js';
|
||||
import { resolveDbConfig } from '../dist/core/load-file.js';
|
||||
|
@ -112,7 +112,10 @@ function createRemoteDbServer() {
|
|||
res.end(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: e.message,
|
||||
error: {
|
||||
code: e instanceof LibsqlError ? e.code : 'SQLITE_QUERY_FAILED',
|
||||
details: e.message,
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue