feat: improve error messages (#1006)
This commit is contained in:
parent
9127e1be88
commit
8a7d3e9950
13 changed files with 370 additions and 174 deletions
|
|
@ -35,6 +35,7 @@ import {
|
||||||
import { after } from 'next/server';
|
import { after } from 'next/server';
|
||||||
import type { Chat } from '@/lib/db/schema';
|
import type { Chat } from '@/lib/db/schema';
|
||||||
import { differenceInSeconds } from 'date-fns';
|
import { differenceInSeconds } from 'date-fns';
|
||||||
|
import { ChatSDKError } from '@/lib/errors';
|
||||||
|
|
||||||
export const maxDuration = 60;
|
export const maxDuration = 60;
|
||||||
|
|
||||||
|
|
@ -67,7 +68,7 @@ export async function POST(request: Request) {
|
||||||
const json = await request.json();
|
const json = await request.json();
|
||||||
requestBody = postRequestBodySchema.parse(json);
|
requestBody = postRequestBodySchema.parse(json);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return new Response('Invalid request body', { status: 400 });
|
return new ChatSDKError('bad_request:api').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -77,7 +78,7 @@ export async function POST(request: Request) {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
if (!session?.user) {
|
if (!session?.user) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('unauthorized:chat').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const userType: UserType = session.user.type;
|
const userType: UserType = session.user.type;
|
||||||
|
|
@ -88,12 +89,7 @@ export async function POST(request: Request) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (messageCount > entitlementsByUserType[userType].maxMessagesPerDay) {
|
if (messageCount > entitlementsByUserType[userType].maxMessagesPerDay) {
|
||||||
return new Response(
|
return new ChatSDKError('rate_limit:chat').toResponse();
|
||||||
'You have exceeded your maximum number of messages for the day! Please try again later.',
|
|
||||||
{
|
|
||||||
status: 429,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const chat = await getChatById({ id });
|
const chat = await getChatById({ id });
|
||||||
|
|
@ -111,7 +107,7 @@ export async function POST(request: Request) {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (chat.userId !== session.user.id) {
|
if (chat.userId !== session.user.id) {
|
||||||
return new Response('Forbidden', { status: 403 });
|
return new ChatSDKError('forbidden:chat').toResponse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,10 +233,10 @@ export async function POST(request: Request) {
|
||||||
} else {
|
} else {
|
||||||
return new Response(stream);
|
return new Response(stream);
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (error) {
|
||||||
return new Response('An error occurred while processing your request!', {
|
if (error instanceof ChatSDKError) {
|
||||||
status: 500,
|
return error.toResponse();
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,13 +252,13 @@ export async function GET(request: Request) {
|
||||||
const chatId = searchParams.get('chatId');
|
const chatId = searchParams.get('chatId');
|
||||||
|
|
||||||
if (!chatId) {
|
if (!chatId) {
|
||||||
return new Response('id is required', { status: 400 });
|
return new ChatSDKError('bad_request:api').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
if (!session?.user) {
|
if (!session?.user) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('unauthorized:chat').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
let chat: Chat;
|
let chat: Chat;
|
||||||
|
|
@ -270,27 +266,27 @@ export async function GET(request: Request) {
|
||||||
try {
|
try {
|
||||||
chat = await getChatById({ id: chatId });
|
chat = await getChatById({ id: chatId });
|
||||||
} catch {
|
} catch {
|
||||||
return new Response('Not found', { status: 404 });
|
return new ChatSDKError('not_found:chat').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return new Response('Not found', { status: 404 });
|
return new ChatSDKError('not_found:chat').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chat.visibility === 'private' && chat.userId !== session.user.id) {
|
if (chat.visibility === 'private' && chat.userId !== session.user.id) {
|
||||||
return new Response('Forbidden', { status: 403 });
|
return new ChatSDKError('forbidden:chat').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const streamIds = await getStreamIdsByChatId({ chatId });
|
const streamIds = await getStreamIdsByChatId({ chatId });
|
||||||
|
|
||||||
if (!streamIds.length) {
|
if (!streamIds.length) {
|
||||||
return new Response('No streams found', { status: 404 });
|
return new ChatSDKError('not_found:stream').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const recentStreamId = streamIds.at(-1);
|
const recentStreamId = streamIds.at(-1);
|
||||||
|
|
||||||
if (!recentStreamId) {
|
if (!recentStreamId) {
|
||||||
return new Response('No recent stream found', { status: 404 });
|
return new ChatSDKError('not_found:stream').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const emptyDataStream = createDataStream({
|
const emptyDataStream = createDataStream({
|
||||||
|
|
@ -344,29 +340,22 @@ export async function DELETE(request: Request) {
|
||||||
const id = searchParams.get('id');
|
const id = searchParams.get('id');
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return new Response('Not Found', { status: 404 });
|
return new ChatSDKError('bad_request:api').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
if (!session?.user?.id) {
|
if (!session?.user) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('unauthorized:chat').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
const chat = await getChatById({ id });
|
const chat = await getChatById({ id });
|
||||||
|
|
||||||
if (chat.userId !== session.user.id) {
|
if (chat.userId !== session.user.id) {
|
||||||
return new Response('Forbidden', { status: 403 });
|
return new ChatSDKError('forbidden:chat').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const deletedChat = await deleteChatById({ id });
|
const deletedChat = await deleteChatById({ id });
|
||||||
|
|
||||||
return Response.json(deletedChat, { status: 200 });
|
return Response.json(deletedChat, { status: 200 });
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
return new Response('An error occurred while processing your request!', {
|
|
||||||
status: 500,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,23 @@ import {
|
||||||
getDocumentsById,
|
getDocumentsById,
|
||||||
saveDocument,
|
saveDocument,
|
||||||
} from '@/lib/db/queries';
|
} from '@/lib/db/queries';
|
||||||
|
import { ChatSDKError } from '@/lib/errors';
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const id = searchParams.get('id');
|
const id = searchParams.get('id');
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return new Response('Missing id', { status: 400 });
|
return new ChatSDKError(
|
||||||
|
'bad_request:api',
|
||||||
|
'Parameter id is missing',
|
||||||
|
).toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
if (!session?.user?.id) {
|
if (!session?.user) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('unauthorized:document').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const documents = await getDocumentsById({ id });
|
const documents = await getDocumentsById({ id });
|
||||||
|
|
@ -25,11 +29,11 @@ export async function GET(request: Request) {
|
||||||
const [document] = documents;
|
const [document] = documents;
|
||||||
|
|
||||||
if (!document) {
|
if (!document) {
|
||||||
return new Response('Not found', { status: 404 });
|
return new ChatSDKError('not_found:document').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.userId !== session.user.id) {
|
if (document.userId !== session.user.id) {
|
||||||
return new Response('Forbidden', { status: 403 });
|
return new ChatSDKError('forbidden:document').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.json(documents, { status: 200 });
|
return Response.json(documents, { status: 200 });
|
||||||
|
|
@ -40,13 +44,16 @@ export async function POST(request: Request) {
|
||||||
const id = searchParams.get('id');
|
const id = searchParams.get('id');
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return new Response('Missing id', { status: 400 });
|
return new ChatSDKError(
|
||||||
|
'bad_request:api',
|
||||||
|
'Parameter id is required.',
|
||||||
|
).toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
if (!session?.user?.id) {
|
if (!session?.user) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('not_found:document').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -62,7 +69,7 @@ export async function POST(request: Request) {
|
||||||
const [document] = documents;
|
const [document] = documents;
|
||||||
|
|
||||||
if (document.userId !== session.user.id) {
|
if (document.userId !== session.user.id) {
|
||||||
return new Response('Forbidden', { status: 403 });
|
return new ChatSDKError('forbidden:document').toResponse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,17 +90,23 @@ export async function DELETE(request: Request) {
|
||||||
const timestamp = searchParams.get('timestamp');
|
const timestamp = searchParams.get('timestamp');
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return new Response('Missing id', { status: 400 });
|
return new ChatSDKError(
|
||||||
|
'bad_request:api',
|
||||||
|
'Parameter id is required.',
|
||||||
|
).toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!timestamp) {
|
if (!timestamp) {
|
||||||
return new Response('Missing timestamp', { status: 400 });
|
return new ChatSDKError(
|
||||||
|
'bad_request:api',
|
||||||
|
'Parameter timestamp is required.',
|
||||||
|
).toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
if (!session?.user?.id) {
|
if (!session?.user) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('unauthorized:document').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const documents = await getDocumentsById({ id });
|
const documents = await getDocumentsById({ id });
|
||||||
|
|
@ -101,7 +114,7 @@ export async function DELETE(request: Request) {
|
||||||
const [document] = documents;
|
const [document] = documents;
|
||||||
|
|
||||||
if (document.userId !== session.user.id) {
|
if (document.userId !== session.user.id) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('forbidden:document').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const documentsDeleted = await deleteDocumentsByIdAfterTimestamp({
|
const documentsDeleted = await deleteDocumentsByIdAfterTimestamp({
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,28 @@
|
||||||
import { auth } from '@/app/(auth)/auth';
|
import { auth } from '@/app/(auth)/auth';
|
||||||
import { NextRequest } from 'next/server';
|
import type { NextRequest } from 'next/server';
|
||||||
import { getChatsByUserId } from '@/lib/db/queries';
|
import { getChatsByUserId } from '@/lib/db/queries';
|
||||||
|
import { ChatSDKError } from '@/lib/errors';
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
export async function GET(request: NextRequest) {
|
||||||
const { searchParams } = request.nextUrl;
|
const { searchParams } = request.nextUrl;
|
||||||
|
|
||||||
const limit = parseInt(searchParams.get('limit') || '10');
|
const limit = Number.parseInt(searchParams.get('limit') || '10');
|
||||||
const startingAfter = searchParams.get('starting_after');
|
const startingAfter = searchParams.get('starting_after');
|
||||||
const endingBefore = searchParams.get('ending_before');
|
const endingBefore = searchParams.get('ending_before');
|
||||||
|
|
||||||
if (startingAfter && endingBefore) {
|
if (startingAfter && endingBefore) {
|
||||||
return Response.json(
|
return new ChatSDKError(
|
||||||
'Only one of starting_after or ending_before can be provided!',
|
'bad_request:api',
|
||||||
{ status: 400 },
|
'Only one of starting_after or ending_before can be provided.',
|
||||||
);
|
).toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
if (!session?.user?.id) {
|
if (!session?.user) {
|
||||||
return Response.json('Unauthorized!', { status: 401 });
|
return new ChatSDKError('unauthorized:chat').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
const chats = await getChatsByUserId({
|
const chats = await getChatsByUserId({
|
||||||
id: session.user.id,
|
id: session.user.id,
|
||||||
limit,
|
limit,
|
||||||
|
|
@ -31,7 +31,4 @@ export async function GET(request: NextRequest) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return Response.json(chats);
|
return Response.json(chats);
|
||||||
} catch (_) {
|
|
||||||
return Response.json('Failed to fetch chats!', { status: 500 });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,22 @@
|
||||||
import { auth } from '@/app/(auth)/auth';
|
import { auth } from '@/app/(auth)/auth';
|
||||||
import { getSuggestionsByDocumentId } from '@/lib/db/queries';
|
import { getSuggestionsByDocumentId } from '@/lib/db/queries';
|
||||||
|
import { ChatSDKError } from '@/lib/errors';
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const documentId = searchParams.get('documentId');
|
const documentId = searchParams.get('documentId');
|
||||||
|
|
||||||
if (!documentId) {
|
if (!documentId) {
|
||||||
return new Response('Not Found', { status: 404 });
|
return new ChatSDKError(
|
||||||
|
'bad_request:api',
|
||||||
|
'Parameter documentId is required.',
|
||||||
|
).toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
if (!session || !session.user) {
|
if (!session?.user) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('unauthorized:suggestions').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const suggestions = await getSuggestionsByDocumentId({
|
const suggestions = await getSuggestionsByDocumentId({
|
||||||
|
|
@ -26,7 +30,7 @@ export async function GET(request: Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (suggestion.userId !== session.user.id) {
|
if (suggestion.userId !== session.user.id) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('forbidden:api').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.json(suggestions, { status: 200 });
|
return Response.json(suggestions, { status: 200 });
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,32 @@
|
||||||
import { auth } from '@/app/(auth)/auth';
|
import { auth } from '@/app/(auth)/auth';
|
||||||
import { getChatById, getVotesByChatId, voteMessage } from '@/lib/db/queries';
|
import { getChatById, getVotesByChatId, voteMessage } from '@/lib/db/queries';
|
||||||
|
import { ChatSDKError } from '@/lib/errors';
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const chatId = searchParams.get('chatId');
|
const chatId = searchParams.get('chatId');
|
||||||
|
|
||||||
if (!chatId) {
|
if (!chatId) {
|
||||||
return new Response('chatId is required', { status: 400 });
|
return new ChatSDKError(
|
||||||
|
'bad_request:api',
|
||||||
|
'Parameter chatId is required.',
|
||||||
|
).toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
if (!session || !session.user || !session.user.email) {
|
if (!session?.user) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('unauthorized:vote').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const chat = await getChatById({ id: chatId });
|
const chat = await getChatById({ id: chatId });
|
||||||
|
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return new Response('Chat not found', { status: 404 });
|
return new ChatSDKError('not_found:chat').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chat.userId !== session.user.id) {
|
if (chat.userId !== session.user.id) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('forbidden:vote').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const votes = await getVotesByChatId({ id: chatId });
|
const votes = await getVotesByChatId({ id: chatId });
|
||||||
|
|
@ -39,23 +43,26 @@ export async function PATCH(request: Request) {
|
||||||
await request.json();
|
await request.json();
|
||||||
|
|
||||||
if (!chatId || !messageId || !type) {
|
if (!chatId || !messageId || !type) {
|
||||||
return new Response('messageId and type are required', { status: 400 });
|
return new ChatSDKError(
|
||||||
|
'bad_request:api',
|
||||||
|
'Parameters chatId, messageId, and type are required.',
|
||||||
|
).toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
if (!session || !session.user || !session.user.email) {
|
if (!session?.user) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('unauthorized:vote').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const chat = await getChatById({ id: chatId });
|
const chat = await getChatById({ id: chatId });
|
||||||
|
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return new Response('Chat not found', { status: 404 });
|
return new ChatSDKError('not_found:vote').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chat.userId !== session.user.id) {
|
if (chat.userId !== session.user.id) {
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return new ChatSDKError('forbidden:vote').toResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
await voteMessage({
|
await voteMessage({
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { useEffect, useState } from 'react';
|
||||||
import useSWR, { useSWRConfig } from 'swr';
|
import useSWR, { useSWRConfig } from 'swr';
|
||||||
import { ChatHeader } from '@/components/chat-header';
|
import { ChatHeader } from '@/components/chat-header';
|
||||||
import type { Vote } from '@/lib/db/schema';
|
import type { Vote } from '@/lib/db/schema';
|
||||||
import { fetcher, generateUUID } from '@/lib/utils';
|
import { fetcher, fetchWithErrorHandlers, generateUUID } from '@/lib/utils';
|
||||||
import { Artifact } from './artifact';
|
import { Artifact } from './artifact';
|
||||||
import { MultimodalInput } from './multimodal-input';
|
import { MultimodalInput } from './multimodal-input';
|
||||||
import { Messages } from './messages';
|
import { Messages } from './messages';
|
||||||
|
|
@ -19,6 +19,7 @@ import type { Session } from 'next-auth';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
import { useChatVisibility } from '@/hooks/use-chat-visibility';
|
import { useChatVisibility } from '@/hooks/use-chat-visibility';
|
||||||
import { useAutoResume } from '@/hooks/use-auto-resume';
|
import { useAutoResume } from '@/hooks/use-auto-resume';
|
||||||
|
import { ChatSDKError } from '@/lib/errors';
|
||||||
|
|
||||||
export function Chat({
|
export function Chat({
|
||||||
id,
|
id,
|
||||||
|
|
@ -62,6 +63,7 @@ export function Chat({
|
||||||
experimental_throttle: 100,
|
experimental_throttle: 100,
|
||||||
sendExtraMessageFields: true,
|
sendExtraMessageFields: true,
|
||||||
generateId: generateUUID,
|
generateId: generateUUID,
|
||||||
|
fetch: fetchWithErrorHandlers,
|
||||||
experimental_prepareRequestBody: (body) => ({
|
experimental_prepareRequestBody: (body) => ({
|
||||||
id,
|
id,
|
||||||
message: body.messages.at(-1),
|
message: body.messages.at(-1),
|
||||||
|
|
@ -72,10 +74,12 @@ export function Chat({
|
||||||
mutate(unstable_serialize(getChatHistoryPaginationKey));
|
mutate(unstable_serialize(getChatHistoryPaginationKey));
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
if (error instanceof ChatSDKError) {
|
||||||
toast({
|
toast({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
description: error.message,
|
description: error.message,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import type { ArtifactKind } from '@/components/artifact';
|
||||||
import { generateUUID } from '../utils';
|
import { generateUUID } from '../utils';
|
||||||
import { generateHashedPassword } from './utils';
|
import { generateHashedPassword } from './utils';
|
||||||
import type { VisibilityType } from '@/components/visibility-selector';
|
import type { VisibilityType } from '@/components/visibility-selector';
|
||||||
|
import { ChatSDKError } from '../errors';
|
||||||
|
|
||||||
// Optionally, if not using email/pass login, you can
|
// Optionally, if not using email/pass login, you can
|
||||||
// use the Drizzle adapter for Auth.js / NextAuth
|
// use the Drizzle adapter for Auth.js / NextAuth
|
||||||
|
|
@ -45,8 +46,10 @@ export async function getUser(email: string): Promise<Array<User>> {
|
||||||
try {
|
try {
|
||||||
return await db.select().from(user).where(eq(user.email, email));
|
return await db.select().from(user).where(eq(user.email, email));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to get user from database');
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to get user by email',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,8 +59,7 @@ export async function createUser(email: string, password: string) {
|
||||||
try {
|
try {
|
||||||
return await db.insert(user).values({ email, password: hashedPassword });
|
return await db.insert(user).values({ email, password: hashedPassword });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create user in database');
|
throw new ChatSDKError('bad_request:database', 'Failed to create user');
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,8 +73,10 @@ export async function createGuestUser() {
|
||||||
email: user.email,
|
email: user.email,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create guest user in database');
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to create guest user',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,8 +100,7 @@ export async function saveChat({
|
||||||
visibility,
|
visibility,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save chat in database');
|
throw new ChatSDKError('bad_request:database', 'Failed to save chat');
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,8 +116,10 @@ export async function deleteChatById({ id }: { id: string }) {
|
||||||
.returning();
|
.returning();
|
||||||
return chatsDeleted;
|
return chatsDeleted;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to delete chat by id from database');
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to delete chat by id',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,7 +159,10 @@ export async function getChatsByUserId({
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
if (!selectedChat) {
|
if (!selectedChat) {
|
||||||
throw new Error(`Chat with id ${startingAfter} not found`);
|
throw new ChatSDKError(
|
||||||
|
'not_found:database',
|
||||||
|
`Chat with id ${startingAfter} not found`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredChats = await query(gt(chat.createdAt, selectedChat.createdAt));
|
filteredChats = await query(gt(chat.createdAt, selectedChat.createdAt));
|
||||||
|
|
@ -166,7 +174,10 @@ export async function getChatsByUserId({
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
if (!selectedChat) {
|
if (!selectedChat) {
|
||||||
throw new Error(`Chat with id ${endingBefore} not found`);
|
throw new ChatSDKError(
|
||||||
|
'not_found:database',
|
||||||
|
`Chat with id ${endingBefore} not found`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredChats = await query(lt(chat.createdAt, selectedChat.createdAt));
|
filteredChats = await query(lt(chat.createdAt, selectedChat.createdAt));
|
||||||
|
|
@ -181,8 +192,10 @@ export async function getChatsByUserId({
|
||||||
hasMore,
|
hasMore,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to get chats by user from database');
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to get chats by user id',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -191,8 +204,7 @@ export async function getChatById({ id }: { id: string }) {
|
||||||
const [selectedChat] = await db.select().from(chat).where(eq(chat.id, id));
|
const [selectedChat] = await db.select().from(chat).where(eq(chat.id, id));
|
||||||
return selectedChat;
|
return selectedChat;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to get chat by id from database');
|
throw new ChatSDKError('bad_request:database', 'Failed to get chat by id');
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,8 +216,7 @@ export async function saveMessages({
|
||||||
try {
|
try {
|
||||||
return await db.insert(message).values(messages);
|
return await db.insert(message).values(messages);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save messages in database', error);
|
throw new ChatSDKError('bad_request:database', 'Failed to save messages');
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,8 +228,10 @@ export async function getMessagesByChatId({ id }: { id: string }) {
|
||||||
.where(eq(message.chatId, id))
|
.where(eq(message.chatId, id))
|
||||||
.orderBy(asc(message.createdAt));
|
.orderBy(asc(message.createdAt));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to get messages by chat id from database', error);
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to get messages by chat id',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,8 +262,7 @@ export async function voteMessage({
|
||||||
isUpvoted: type === 'up',
|
isUpvoted: type === 'up',
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to upvote message in database', error);
|
throw new ChatSDKError('bad_request:database', 'Failed to vote message');
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -258,8 +270,10 @@ export async function getVotesByChatId({ id }: { id: string }) {
|
||||||
try {
|
try {
|
||||||
return await db.select().from(vote).where(eq(vote.chatId, id));
|
return await db.select().from(vote).where(eq(vote.chatId, id));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to get votes by chat id from database', error);
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to get votes by chat id',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,8 +303,7 @@ export async function saveDocument({
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save document in database');
|
throw new ChatSDKError('bad_request:database', 'Failed to save document');
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,8 +317,10 @@ export async function getDocumentsById({ id }: { id: string }) {
|
||||||
|
|
||||||
return documents;
|
return documents;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to get document by id from database');
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to get documents by id',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -319,8 +334,10 @@ export async function getDocumentById({ id }: { id: string }) {
|
||||||
|
|
||||||
return selectedDocument;
|
return selectedDocument;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to get document by id from database');
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to get document by id',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -346,10 +363,10 @@ export async function deleteDocumentsByIdAfterTimestamp({
|
||||||
.where(and(eq(document.id, id), gt(document.createdAt, timestamp)))
|
.where(and(eq(document.id, id), gt(document.createdAt, timestamp)))
|
||||||
.returning();
|
.returning();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
throw new ChatSDKError(
|
||||||
'Failed to delete documents by id after timestamp from database',
|
'bad_request:database',
|
||||||
|
'Failed to delete documents by id after timestamp',
|
||||||
);
|
);
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,8 +378,10 @@ export async function saveSuggestions({
|
||||||
try {
|
try {
|
||||||
return await db.insert(suggestion).values(suggestions);
|
return await db.insert(suggestion).values(suggestions);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save suggestions in database');
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to save suggestions',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -377,10 +396,10 @@ export async function getSuggestionsByDocumentId({
|
||||||
.from(suggestion)
|
.from(suggestion)
|
||||||
.where(and(eq(suggestion.documentId, documentId)));
|
.where(and(eq(suggestion.documentId, documentId)));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
throw new ChatSDKError(
|
||||||
'Failed to get suggestions by document version from database',
|
'bad_request:database',
|
||||||
|
'Failed to get suggestions by document id',
|
||||||
);
|
);
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -388,8 +407,10 @@ export async function getMessageById({ id }: { id: string }) {
|
||||||
try {
|
try {
|
||||||
return await db.select().from(message).where(eq(message.id, id));
|
return await db.select().from(message).where(eq(message.id, id));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to get message by id from database');
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to get message by id',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -424,10 +445,10 @@ export async function deleteMessagesByChatIdAfterTimestamp({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
throw new ChatSDKError(
|
||||||
'Failed to delete messages by id after timestamp from database',
|
'bad_request:database',
|
||||||
|
'Failed to delete messages by chat id after timestamp',
|
||||||
);
|
);
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -441,8 +462,10 @@ export async function updateChatVisiblityById({
|
||||||
try {
|
try {
|
||||||
return await db.update(chat).set({ visibility }).where(eq(chat.id, chatId));
|
return await db.update(chat).set({ visibility }).where(eq(chat.id, chatId));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to update chat visibility in database');
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to update chat visibility by id',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -470,10 +493,10 @@ export async function getMessageCountByUserId({
|
||||||
|
|
||||||
return stats?.count ?? 0;
|
return stats?.count ?? 0;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
throw new ChatSDKError(
|
||||||
'Failed to get message count by user id for the last 24 hours from database',
|
'bad_request:database',
|
||||||
|
'Failed to get message count by user id',
|
||||||
);
|
);
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -489,8 +512,10 @@ export async function createStreamId({
|
||||||
.insert(stream)
|
.insert(stream)
|
||||||
.values({ id: streamId, chatId, createdAt: new Date() });
|
.values({ id: streamId, chatId, createdAt: new Date() });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create stream id in database');
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to create stream id',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -505,7 +530,9 @@ export async function getStreamIdsByChatId({ chatId }: { chatId: string }) {
|
||||||
|
|
||||||
return streamIds.map(({ id }) => id);
|
return streamIds.map(({ id }) => id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to get stream ids by chat id from database');
|
throw new ChatSDKError(
|
||||||
throw error;
|
'bad_request:database',
|
||||||
|
'Failed to get stream ids by chat id',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
132
lib/errors.ts
Normal file
132
lib/errors.ts
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
export type ErrorType =
|
||||||
|
| 'bad_request'
|
||||||
|
| 'unauthorized'
|
||||||
|
| 'forbidden'
|
||||||
|
| 'not_found'
|
||||||
|
| 'rate_limit'
|
||||||
|
| 'offline';
|
||||||
|
|
||||||
|
export type Surface =
|
||||||
|
| 'chat'
|
||||||
|
| 'auth'
|
||||||
|
| 'api'
|
||||||
|
| 'stream'
|
||||||
|
| 'database'
|
||||||
|
| 'history'
|
||||||
|
| 'vote'
|
||||||
|
| 'document'
|
||||||
|
| 'suggestions';
|
||||||
|
|
||||||
|
export type ErrorCode = `${ErrorType}:${Surface}`;
|
||||||
|
|
||||||
|
export type ErrorVisibility = 'response' | 'log' | 'none';
|
||||||
|
|
||||||
|
export const visibilityBySurface: Record<Surface, ErrorVisibility> = {
|
||||||
|
database: 'log',
|
||||||
|
chat: 'response',
|
||||||
|
auth: 'response',
|
||||||
|
stream: 'response',
|
||||||
|
api: 'response',
|
||||||
|
history: 'response',
|
||||||
|
vote: 'response',
|
||||||
|
document: 'response',
|
||||||
|
suggestions: 'response',
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ChatSDKError extends Error {
|
||||||
|
public type: ErrorType;
|
||||||
|
public surface: Surface;
|
||||||
|
public statusCode: number;
|
||||||
|
|
||||||
|
constructor(errorCode: ErrorCode, cause?: string) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
const [type, surface] = errorCode.split(':');
|
||||||
|
|
||||||
|
this.type = type as ErrorType;
|
||||||
|
this.cause = cause;
|
||||||
|
this.surface = surface as Surface;
|
||||||
|
this.message = getMessageByErrorCode(errorCode);
|
||||||
|
this.statusCode = getStatusCodeByType(this.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toResponse() {
|
||||||
|
const code: ErrorCode = `${this.type}:${this.surface}`;
|
||||||
|
const visibility = visibilityBySurface[this.surface];
|
||||||
|
|
||||||
|
const { message, cause, statusCode } = this;
|
||||||
|
|
||||||
|
if (visibility === 'log') {
|
||||||
|
console.error({
|
||||||
|
code,
|
||||||
|
message,
|
||||||
|
cause,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Response.json(
|
||||||
|
{ code: '', message: 'Something went wrong. Please try again later.' },
|
||||||
|
{ status: statusCode },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json({ code, message, cause }, { status: statusCode });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMessageByErrorCode(errorCode: ErrorCode): string {
|
||||||
|
if (errorCode.includes('database')) {
|
||||||
|
return 'An error occurred while executing a database query.';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (errorCode) {
|
||||||
|
case 'bad_request:api':
|
||||||
|
return "The request couldn't be processed. Please check your input and try again.";
|
||||||
|
|
||||||
|
case 'unauthorized:auth':
|
||||||
|
return 'You need to sign in before continuing.';
|
||||||
|
case 'forbidden:auth':
|
||||||
|
return 'Your account does not have access to this feature.';
|
||||||
|
|
||||||
|
case 'rate_limit:chat':
|
||||||
|
return 'You have exceeded your maximum number of messages for the day. Please try again later.';
|
||||||
|
case 'not_found:chat':
|
||||||
|
return 'The requested chat was not found. Please check the chat ID and try again.';
|
||||||
|
case 'forbidden:chat':
|
||||||
|
return 'This chat belongs to another user. Please check the chat ID and try again.';
|
||||||
|
case 'unauthorized:chat':
|
||||||
|
return 'You need to sign in to view this chat. Please sign in and try again.';
|
||||||
|
case 'offline:chat':
|
||||||
|
return "We're having trouble sending your message. Please check your internet connection and try again.";
|
||||||
|
|
||||||
|
case 'not_found:document':
|
||||||
|
return 'The requested document was not found. Please check the document ID and try again.';
|
||||||
|
case 'forbidden:document':
|
||||||
|
return 'This document belongs to another user. Please check the document ID and try again.';
|
||||||
|
case 'unauthorized:document':
|
||||||
|
return 'You need to sign in to view this document. Please sign in and try again.';
|
||||||
|
case 'bad_request:document':
|
||||||
|
return 'The request to create or update the document was invalid. Please check your input and try again.';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 'Something went wrong. Please try again later.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusCodeByType(type: ErrorType) {
|
||||||
|
switch (type) {
|
||||||
|
case 'bad_request':
|
||||||
|
return 400;
|
||||||
|
case 'unauthorized':
|
||||||
|
return 401;
|
||||||
|
case 'forbidden':
|
||||||
|
return 403;
|
||||||
|
case 'not_found':
|
||||||
|
return 404;
|
||||||
|
case 'rate_limit':
|
||||||
|
return 429;
|
||||||
|
case 'offline':
|
||||||
|
return 503;
|
||||||
|
default:
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
lib/utils.ts
40
lib/utils.ts
|
|
@ -2,32 +2,44 @@ import type { CoreAssistantMessage, CoreToolMessage, UIMessage } from 'ai';
|
||||||
import { type ClassValue, clsx } from 'clsx';
|
import { type ClassValue, clsx } from 'clsx';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
import type { Document } from '@/lib/db/schema';
|
import type { Document } from '@/lib/db/schema';
|
||||||
|
import { ChatSDKError, type ErrorCode } from './errors';
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ApplicationError extends Error {
|
export const fetcher = async (url: string) => {
|
||||||
info: string;
|
const response = await fetch(url);
|
||||||
status: number;
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const { code, cause } = await response.json();
|
||||||
|
throw new ChatSDKError(code as ErrorCode, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetcher = async (url: string) => {
|
return response.json();
|
||||||
const res = await fetch(url);
|
};
|
||||||
|
|
||||||
if (!res.ok) {
|
export async function fetchWithErrorHandlers(
|
||||||
const error = new Error(
|
input: RequestInfo | URL,
|
||||||
'An error occurred while fetching the data.',
|
init?: RequestInit,
|
||||||
) as ApplicationError;
|
) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(input, init);
|
||||||
|
|
||||||
error.info = await res.json();
|
if (!response.ok) {
|
||||||
error.status = res.status;
|
const { code, cause } = await response.json();
|
||||||
|
throw new ChatSDKError(code as ErrorCode, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (typeof navigator !== 'undefined' && !navigator.onLine) {
|
||||||
|
throw new ChatSDKError('offline:chat');
|
||||||
|
}
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return res.json();
|
|
||||||
};
|
|
||||||
|
|
||||||
export function getLocalStorage(key: string) {
|
export function getLocalStorage(key: string) {
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ai-chatbot",
|
"name": "ai-chatbot",
|
||||||
"version": "3.0.22",
|
"version": "3.0.23",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbo",
|
"dev": "next dev --turbo",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { expect, test } from '../fixtures';
|
||||||
import { AuthPage } from '../pages/auth';
|
import { AuthPage } from '../pages/auth';
|
||||||
import { generateRandomTestUser } from '../helpers';
|
import { generateRandomTestUser } from '../helpers';
|
||||||
import { ChatPage } from '../pages/chat';
|
import { ChatPage } from '../pages/chat';
|
||||||
|
import { getMessageByErrorCode } from '@/lib/errors';
|
||||||
|
|
||||||
test.describe
|
test.describe
|
||||||
.serial('Guest Session', () => {
|
.serial('Guest Session', () => {
|
||||||
|
|
@ -201,7 +202,7 @@ test.describe('Entitlements', () => {
|
||||||
|
|
||||||
await chatPage.sendUserMessage('Why is the sky blue?');
|
await chatPage.sendUserMessage('Why is the sky blue?');
|
||||||
await chatPage.expectToastToContain(
|
await chatPage.expectToastToContain(
|
||||||
'You have exceeded your maximum number of messages for the day! Please try again later.',
|
getMessageByErrorCode('rate_limit:chat'),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { generateUUID } from '@/lib/utils';
|
import { generateUUID } from '@/lib/utils';
|
||||||
import { expect, test } from '../fixtures';
|
import { expect, test } from '../fixtures';
|
||||||
import { TEST_PROMPTS } from '../prompts/routes';
|
import { TEST_PROMPTS } from '../prompts/routes';
|
||||||
|
import { getMessageByErrorCode } from '@/lib/errors';
|
||||||
|
|
||||||
const chatIdsCreatedByAda: Array<string> = [];
|
const chatIdsCreatedByAda: Array<string> = [];
|
||||||
|
|
||||||
|
|
@ -14,8 +15,9 @@ test.describe
|
||||||
});
|
});
|
||||||
expect(response.status()).toBe(400);
|
expect(response.status()).toBe(400);
|
||||||
|
|
||||||
const text = await response.text();
|
const { code, message } = await response.json();
|
||||||
expect(text).toEqual('Invalid request body');
|
expect(code).toEqual('bad_request:api');
|
||||||
|
expect(message).toEqual(getMessageByErrorCode('bad_request:api'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Ada can invoke chat generation', async ({ adaContext }) => {
|
test('Ada can invoke chat generation', async ({ adaContext }) => {
|
||||||
|
|
@ -55,8 +57,9 @@ test.describe
|
||||||
});
|
});
|
||||||
expect(response.status()).toBe(403);
|
expect(response.status()).toBe(403);
|
||||||
|
|
||||||
const text = await response.text();
|
const { code, message } = await response.json();
|
||||||
expect(text).toEqual('Forbidden');
|
expect(code).toEqual('forbidden:chat');
|
||||||
|
expect(message).toEqual(getMessageByErrorCode('forbidden:chat'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Babbage cannot delete Ada's chat", async ({ babbageContext }) => {
|
test("Babbage cannot delete Ada's chat", async ({ babbageContext }) => {
|
||||||
|
|
@ -67,8 +70,9 @@ test.describe
|
||||||
);
|
);
|
||||||
expect(response.status()).toBe(403);
|
expect(response.status()).toBe(403);
|
||||||
|
|
||||||
const text = await response.text();
|
const { code, message } = await response.json();
|
||||||
expect(text).toEqual('Forbidden');
|
expect(code).toEqual('forbidden:chat');
|
||||||
|
expect(message).toEqual(getMessageByErrorCode('forbidden:chat'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Ada can delete her own chat', async ({ adaContext }) => {
|
test('Ada can delete her own chat', async ({ adaContext }) => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import type { Document } from '@/lib/db/schema';
|
import type { Document } from '@/lib/db/schema';
|
||||||
import { generateUUID } from '@/lib/utils';
|
import { generateUUID } from '@/lib/utils';
|
||||||
import { expect, test } from '../fixtures';
|
import { expect, test } from '../fixtures';
|
||||||
|
import { getMessageByErrorCode } from '@/lib/errors';
|
||||||
|
|
||||||
const documentsCreatedByAda: Array<Document> = [];
|
const documentsCreatedByAda: Array<Document> = [];
|
||||||
|
|
||||||
|
|
@ -12,8 +13,9 @@ test.describe
|
||||||
const response = await adaContext.request.get('/api/document');
|
const response = await adaContext.request.get('/api/document');
|
||||||
expect(response.status()).toBe(400);
|
expect(response.status()).toBe(400);
|
||||||
|
|
||||||
const text = await response.text();
|
const { code, message } = await response.json();
|
||||||
expect(text).toEqual('Missing id');
|
expect(code).toEqual('bad_request:api');
|
||||||
|
expect(message).toEqual(getMessageByErrorCode(code));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Ada cannot retrieve a document that does not exist', async ({
|
test('Ada cannot retrieve a document that does not exist', async ({
|
||||||
|
|
@ -26,8 +28,9 @@ test.describe
|
||||||
);
|
);
|
||||||
expect(response.status()).toBe(404);
|
expect(response.status()).toBe(404);
|
||||||
|
|
||||||
const text = await response.text();
|
const { code, message } = await response.json();
|
||||||
expect(text).toEqual('Not found');
|
expect(code).toEqual('not_found:document');
|
||||||
|
expect(message).toEqual(getMessageByErrorCode(code));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Ada can create a document', async ({ adaContext }) => {
|
test('Ada can create a document', async ({ adaContext }) => {
|
||||||
|
|
@ -118,8 +121,9 @@ test.describe
|
||||||
const response = await adaContext.request.delete(`/api/document`);
|
const response = await adaContext.request.delete(`/api/document`);
|
||||||
expect(response.status()).toBe(400);
|
expect(response.status()).toBe(400);
|
||||||
|
|
||||||
const text = await response.text();
|
const { code, message } = await response.json();
|
||||||
expect(text).toEqual('Missing id');
|
expect(code).toEqual('bad_request:api');
|
||||||
|
expect(message).toEqual(getMessageByErrorCode(code));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Ada cannot delete a document without specifying a timestamp', async ({
|
test('Ada cannot delete a document without specifying a timestamp', async ({
|
||||||
|
|
@ -132,8 +136,9 @@ test.describe
|
||||||
);
|
);
|
||||||
expect(response.status()).toBe(400);
|
expect(response.status()).toBe(400);
|
||||||
|
|
||||||
const text = await response.text();
|
const { code, message } = await response.json();
|
||||||
expect(text).toEqual('Missing timestamp');
|
expect(code).toEqual('bad_request:api');
|
||||||
|
expect(message).toEqual(getMessageByErrorCode(code));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Ada can delete a document by specifying id and timestamp', async ({
|
test('Ada can delete a document by specifying id and timestamp', async ({
|
||||||
|
|
@ -187,8 +192,9 @@ test.describe
|
||||||
);
|
);
|
||||||
expect(response.status()).toBe(403);
|
expect(response.status()).toBe(403);
|
||||||
|
|
||||||
const text = await response.text();
|
const { code, message } = await response.json();
|
||||||
expect(text).toEqual('Forbidden');
|
expect(code).toEqual('forbidden:document');
|
||||||
|
expect(message).toEqual(getMessageByErrorCode(code));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Ada's documents did not get updated", async ({ adaContext }) => {
|
test("Ada's documents did not get updated", async ({ adaContext }) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue