sourcebot/packages/web/src/features/search/searchApi.ts

91 lines
3.2 KiB
TypeScript

import { sew } from "@/actions";
import { getRepoPermissionFilterForUser } from "@/prisma";
import { withOptionalAuthV2 } from "@/withAuthV2";
import { PrismaClient, UserWithAccounts } from "@sourcebot/db";
import { createLogger, env, hasEntitlement } from "@sourcebot/shared";
import { QueryIR } from './ir';
import { parseQuerySyntaxIntoIR } from './parser';
import { SearchOptions } from "./types";
import { createZoektSearchRequest, zoektSearch, zoektStreamSearch } from './zoektSearcher';
const logger = createLogger("searchApi");
type QueryStringSearchRequest = {
queryType: 'string';
query: string;
options: SearchOptions;
}
type QueryIRSearchRequest = {
queryType: 'ir';
query: QueryIR;
// Omit options that are specific to query syntax parsing.
options: Omit<SearchOptions, 'isRegexEnabled' | 'isCaseSensitivityEnabled'>;
}
type SearchRequest = QueryStringSearchRequest | QueryIRSearchRequest;
export const search = (request: SearchRequest) => sew(() =>
withOptionalAuthV2(async ({ prisma, user }) => {
const repoSearchScope = await getAccessibleRepoNamesForUser({ user, prisma });
// If needed, parse the query syntax into the query intermediate representation.
const query = request.queryType === 'string' ? await parseQuerySyntaxIntoIR({
query: request.query,
options: request.options,
prisma,
}) : request.query;
const zoektSearchRequest = await createZoektSearchRequest({
query,
options: request.options,
repoSearchScope,
});
logger.debug(`zoektSearchRequest:\n${JSON.stringify(zoektSearchRequest, null, 2)}`);
return zoektSearch(zoektSearchRequest, prisma);
}));
export const streamSearch = (request: SearchRequest) => sew(() =>
withOptionalAuthV2(async ({ prisma, user }) => {
const repoSearchScope = await getAccessibleRepoNamesForUser({ user, prisma });
// If needed, parse the query syntax into the query intermediate representation.
const query = request.queryType === 'string' ? await parseQuerySyntaxIntoIR({
query: request.query,
options: request.options,
prisma,
}) : request.query;
const zoektSearchRequest = await createZoektSearchRequest({
query,
options: request.options,
repoSearchScope,
});
logger.debug(`zoektStreamSearchRequest:\n${JSON.stringify(zoektSearchRequest, null, 2)}`);
return zoektStreamSearch(zoektSearchRequest, prisma);
}));
/**
* Returns a list of repository names that the user has access to.
* If permission syncing is disabled, returns undefined.
*/
const getAccessibleRepoNamesForUser = async ({ user, prisma }: { user?: UserWithAccounts, prisma: PrismaClient }) => {
if (
env.EXPERIMENT_EE_PERMISSION_SYNC_ENABLED !== 'true' ||
!hasEntitlement('permission-syncing')
) {
return undefined;
}
const accessibleRepos = await prisma.repo.findMany({
where: getRepoPermissionFilterForUser(user),
select: {
name: true,
}
});
return accessibleRepos.map(repo => repo.name);
}