mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 20:35:24 +00:00
wip on better error handling
This commit is contained in:
parent
1d1205f471
commit
5c624cda4f
6 changed files with 61 additions and 30 deletions
|
|
@ -48,6 +48,10 @@ export const sew = async <T>(fn: () => Promise<T>): Promise<T | ServiceError> =>
|
|||
Sentry.captureException(e);
|
||||
logger.error(e);
|
||||
|
||||
if (e instanceof ServiceErrorException) {
|
||||
return e.serviceError;
|
||||
}
|
||||
|
||||
if (e instanceof Error) {
|
||||
return unexpectedError(e.message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ export const SearchBar = ({
|
|||
indentWithTab={false}
|
||||
autoFocus={autoFocus ?? false}
|
||||
/>
|
||||
<div className="flex flex-row items-center gap-1">
|
||||
<div className="flex flex-row items-center gap-1 ml-1">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import { CodePreviewPanel } from "./codePreviewPanel";
|
|||
import { FilterPanel } from "./filterPanel";
|
||||
import { useFilteredMatches } from "./filterPanel/useFilterMatches";
|
||||
import { SearchResultsPanel, SearchResultsPanelHandle } from "./searchResultsPanel";
|
||||
import { ServiceErrorException } from "@/lib/serviceError";
|
||||
|
||||
interface SearchResultsPageProps {
|
||||
searchQuery: string;
|
||||
|
|
@ -79,7 +80,7 @@ export const SearchResultsPage = ({
|
|||
useEffect(() => {
|
||||
if (error) {
|
||||
toast({
|
||||
description: `❌ Search failed. Reason: ${error.message}`,
|
||||
description: `❌ Search failed. Reason: ${error instanceof ServiceErrorException ? error.serviceError.message : error.message}`,
|
||||
});
|
||||
}
|
||||
}, [error, toast]);
|
||||
|
|
@ -184,7 +185,7 @@ export const SearchResultsPage = ({
|
|||
<div className="flex flex-col items-center justify-center h-full gap-2">
|
||||
<AlertTriangleIcon className="h-6 w-6" />
|
||||
<p className="font-semibold text-center">Failed to search</p>
|
||||
<p className="text-sm text-center">{error.message}</p>
|
||||
<p className="text-sm text-center">{error instanceof ServiceErrorException ? error.serviceError.message : error.message}</p>
|
||||
</div>
|
||||
) : (
|
||||
<PanelGroup
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
'use client';
|
||||
|
||||
import { RepositoryInfo, SearchRequest, SearchResultFile, SearchStats, StreamedSearchResponse } from '@/features/search';
|
||||
import { useState, useCallback, useRef, useEffect } from 'react';
|
||||
import { ServiceErrorException } from '@/lib/serviceError';
|
||||
import { isServiceError } from '@/lib/utils';
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
interface CacheEntry {
|
||||
files: SearchResultFile[];
|
||||
|
|
@ -132,6 +134,14 @@ export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegex
|
|||
});
|
||||
|
||||
if (!response.ok) {
|
||||
// Check if this is a service error response
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (contentType?.includes('application/json')) {
|
||||
const errorData = await response.json();
|
||||
if (isServiceError(errorData)) {
|
||||
throw new ServiceErrorException(errorData);
|
||||
}
|
||||
}
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
|
|
@ -249,7 +259,7 @@ export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegex
|
|||
...prev,
|
||||
isStreaming: false,
|
||||
timeToSearchCompletionMs,
|
||||
error: error as Error,
|
||||
error: error instanceof Error ? error : null,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ import {
|
|||
import { parser as _parser } from '@sourcebot/query-language';
|
||||
import { PrismaClient } from '@sourcebot/db';
|
||||
import { SINGLE_TENANT_ORG_ID } from '@/lib/constants';
|
||||
import { ServiceErrorException } from '@/lib/serviceError';
|
||||
import { StatusCodes } from 'http-status-codes';
|
||||
import { ErrorCode } from '@/lib/errorCodes';
|
||||
|
||||
// Configure the parser to throw errors when encountering invalid syntax.
|
||||
const parser = _parser.configure({
|
||||
|
|
@ -61,35 +64,47 @@ export const parseQuerySyntaxIntoIR = async ({
|
|||
},
|
||||
prisma: PrismaClient,
|
||||
}): Promise<QueryIR> => {
|
||||
// First parse the query into a Lezer tree.
|
||||
const tree = parser.parse(query);
|
||||
|
||||
// Then transform the tree into the intermediate representation.
|
||||
return transformTreeToIR({
|
||||
tree,
|
||||
input: query,
|
||||
isCaseSensitivityEnabled: options.isCaseSensitivityEnabled ?? false,
|
||||
isRegexEnabled: options.isRegexEnabled ?? false,
|
||||
onExpandSearchContext: async (contextName: string) => {
|
||||
const context = await prisma.searchContext.findUnique({
|
||||
where: {
|
||||
name_orgId: {
|
||||
name: contextName,
|
||||
orgId: SINGLE_TENANT_ORG_ID,
|
||||
try {
|
||||
// First parse the query into a Lezer tree.
|
||||
const tree = parser.parse(query);
|
||||
|
||||
// Then transform the tree into the intermediate representation.
|
||||
return transformTreeToIR({
|
||||
tree,
|
||||
input: query,
|
||||
isCaseSensitivityEnabled: options.isCaseSensitivityEnabled ?? false,
|
||||
isRegexEnabled: options.isRegexEnabled ?? false,
|
||||
onExpandSearchContext: async (contextName: string) => {
|
||||
const context = await prisma.searchContext.findUnique({
|
||||
where: {
|
||||
name_orgId: {
|
||||
name: contextName,
|
||||
orgId: SINGLE_TENANT_ORG_ID,
|
||||
}
|
||||
},
|
||||
include: {
|
||||
repos: true,
|
||||
}
|
||||
},
|
||||
include: {
|
||||
repos: true,
|
||||
});
|
||||
|
||||
if (!context) {
|
||||
throw new Error(`Search context "${contextName}" not found`);
|
||||
}
|
||||
|
||||
return context.repos.map((repo) => repo.name);
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof SyntaxError) {
|
||||
throw new ServiceErrorException({
|
||||
statusCode: StatusCodes.BAD_REQUEST,
|
||||
errorCode: ErrorCode.FAILED_TO_PARSE_QUERY,
|
||||
message: `Failed to parse query "${query}" with message: ${error.message}`,
|
||||
});
|
||||
|
||||
if (!context) {
|
||||
throw new Error(`Search context "${contextName}" not found`);
|
||||
}
|
||||
|
||||
return context.repos.map((repo) => repo.name);
|
||||
},
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -34,4 +34,5 @@ export enum ErrorCode {
|
|||
API_KEY_NOT_FOUND = 'API_KEY_NOT_FOUND',
|
||||
INVALID_API_KEY = 'INVALID_API_KEY',
|
||||
CHAT_IS_READONLY = 'CHAT_IS_READONLY',
|
||||
FAILED_TO_PARSE_QUERY = 'FAILED_TO_PARSE_QUERY',
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue