mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-11 03:38:38 +00:00
fix(ui): resolve TanStack Query lint findings
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
f2be1d26d1
commit
360267533f
15 changed files with 159 additions and 46 deletions
|
|
@ -40,7 +40,7 @@ export function GuardrailDetail({ guardrailId, onBack, accessToken = null, start
|
|||
error: detailError,
|
||||
} = useGuardrailsUsageDetail(guardrailId, { accessToken, startDate, endDate });
|
||||
const { data: logsData, isLoading: logsLoading } = useQuery({
|
||||
queryKey: ["guardrails-usage-logs", guardrailId, logsPage, logsPageSize],
|
||||
queryKey: ["guardrails-usage-logs", guardrailId, logsPage, logsPageSize, startDate, endDate],
|
||||
queryFn: () =>
|
||||
getGuardrailsUsageLogs(accessToken!, {
|
||||
guardrailId,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,14 @@ export const useMCPServerHealth = () => {
|
|||
);
|
||||
|
||||
return {
|
||||
...query,
|
||||
data: query.data,
|
||||
error: query.error,
|
||||
isError: query.isError,
|
||||
isFetching: query.isFetching,
|
||||
isLoading: query.isLoading,
|
||||
isSuccess: query.isSuccess,
|
||||
refetch: query.refetch,
|
||||
status: query.status,
|
||||
recheckServerHealth,
|
||||
recheckingServerIds,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -621,6 +621,30 @@ describe("useSelectedTeamModels", () => {
|
|||
expect(modelAvailableCall).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("keeps team model results in separate cache entries", async () => {
|
||||
const teamOneResponse = { data: [{ ...mockProxyModel, id: "team-1-model" }] };
|
||||
const teamTwoResponse = { data: [{ ...mockProxyModel, id: "team-2-model" }] };
|
||||
(modelAvailableCall as any).mockResolvedValueOnce(teamOneResponse).mockResolvedValueOnce(teamTwoResponse);
|
||||
|
||||
const { result, rerender } = renderHook(({ teamID }: { teamID: string }) => useSelectedTeamModels(teamID), {
|
||||
wrapper,
|
||||
initialProps: { teamID: "team-1" },
|
||||
});
|
||||
|
||||
await waitFor(() => expect(result.current.data).toEqual(teamOneResponse));
|
||||
|
||||
rerender({ teamID: "team-2" });
|
||||
|
||||
await waitFor(() => expect(result.current.data).toEqual(teamTwoResponse));
|
||||
expect(modelAvailableCall).toHaveBeenCalledTimes(2);
|
||||
expect(
|
||||
queryClient
|
||||
.getQueriesData<AllProxyModelsResponse>({ queryKey: ["selectedTeamModels", "list"] })
|
||||
.map(([, data]) => data?.data[0]?.id)
|
||||
.sort(),
|
||||
).toEqual(["team-1-model", "team-2-model"]);
|
||||
});
|
||||
|
||||
it("should handle error when modelAvailableCall fails", async () => {
|
||||
const errorMessage = "Failed to fetch team models";
|
||||
const testError = new Error(errorMessage);
|
||||
|
|
|
|||
|
|
@ -247,7 +247,12 @@ export const useModelHub = () => {
|
|||
export const useAllProxyModels = () => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
return useQuery<AllProxyModelsResponse>({
|
||||
queryKey: allProxyModelsKeys.list({}),
|
||||
queryKey: allProxyModelsKeys.list({
|
||||
filters: {
|
||||
userId: userId ?? "",
|
||||
userRole: userRole ?? "",
|
||||
},
|
||||
}),
|
||||
queryFn: async () => await modelAvailableCall(accessToken!, userId!, userRole!, true, null, true, false, "expand"),
|
||||
enabled: Boolean(accessToken && userId && userRole),
|
||||
});
|
||||
|
|
@ -256,7 +261,12 @@ export const useAllProxyModels = () => {
|
|||
export const useUserModels = (): UseQueryResult<string[]> => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
return useQuery<string[]>({
|
||||
queryKey: userModelsKeys.list({}),
|
||||
queryKey: userModelsKeys.list({
|
||||
filters: {
|
||||
userId: userId ?? "",
|
||||
userRole: userRole ?? "",
|
||||
},
|
||||
}),
|
||||
queryFn: async () => {
|
||||
const response = await modelAvailableCall(accessToken!, userId!, userRole!);
|
||||
return response["data"].map((model: { id: string }) => model.id);
|
||||
|
|
@ -268,7 +278,13 @@ export const useUserModels = (): UseQueryResult<string[]> => {
|
|||
export const useSelectedTeamModels = (teamID: string | null) => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
return useQuery<AllProxyModelsResponse>({
|
||||
queryKey: selectedTeamModelsKeys.list({}),
|
||||
queryKey: selectedTeamModelsKeys.list({
|
||||
filters: {
|
||||
teamID: teamID ?? "",
|
||||
userId: userId ?? "",
|
||||
userRole: userRole ?? "",
|
||||
},
|
||||
}),
|
||||
queryFn: async () => await modelAvailableCall(accessToken!, userId!, userRole!, true, teamID!),
|
||||
enabled: Boolean(accessToken && userId && userRole && teamID),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -116,6 +116,36 @@ describe("useTeams", () => {
|
|||
expect(fetchTeams).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("keeps team list results scoped to the authorized user and role", async () => {
|
||||
const otherUserTeams = [mockTeams[1]];
|
||||
vi.mocked(fetchTeams).mockResolvedValueOnce(mockTeams).mockResolvedValueOnce(otherUserTeams);
|
||||
|
||||
const { result, rerender } = renderHook(() => useTeams(), { wrapper });
|
||||
|
||||
await waitFor(() => expect(result.current.data).toEqual(mockTeams));
|
||||
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "test-access-token",
|
||||
userId: "another-user-id",
|
||||
userRole: "Internal User",
|
||||
token: "test-token",
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
rerender();
|
||||
|
||||
await waitFor(() => expect(result.current.data).toEqual(otherUserTeams));
|
||||
expect(fetchTeams).toHaveBeenCalledTimes(2);
|
||||
expect(
|
||||
queryClient
|
||||
.getQueriesData<Team[]>({ queryKey: ["teams", "list"] })
|
||||
.map(([, teams]) => teams?.[0]?.team_id)
|
||||
.sort(),
|
||||
).toEqual(["team-1", "team-2"]);
|
||||
});
|
||||
|
||||
it("should handle error when fetchTeams fails", async () => {
|
||||
const errorMessage = "Failed to fetch teams";
|
||||
const testError = new Error(errorMessage);
|
||||
|
|
@ -398,7 +428,10 @@ describe("useTeam", () => {
|
|||
});
|
||||
|
||||
it("should use initialData from teams list cache when available", async () => {
|
||||
queryClient.setQueryData(["teams", "list", { params: {} }], mockTeams);
|
||||
queryClient.setQueryData(
|
||||
["teams", "list", { params: { filters: { userId: "test-user-id", userRole: "Admin" } } }],
|
||||
mockTeams,
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useTeam("team-1"), { wrapper });
|
||||
|
||||
|
|
@ -413,7 +446,10 @@ describe("useTeam", () => {
|
|||
});
|
||||
|
||||
it("should return undefined initialData when teamId is not in cache", () => {
|
||||
queryClient.setQueryData(["teams", "list", { params: {} }], mockTeams);
|
||||
queryClient.setQueryData(
|
||||
["teams", "list", { params: { filters: { userId: "test-user-id", userRole: "Admin" } } }],
|
||||
mockTeams,
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useTeam("non-existent-team"), { wrapper });
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,12 @@ export const teamKeys = createQueryKeys("teams");
|
|||
export const useTeams = (): UseQueryResult<Team[]> => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
return useQuery<Team[]>({
|
||||
queryKey: teamKeys.list({}),
|
||||
queryKey: teamKeys.list({
|
||||
filters: {
|
||||
userId: userId ?? "",
|
||||
userRole: userRole ?? "",
|
||||
},
|
||||
}),
|
||||
queryFn: async () => await fetchTeams(accessToken!, userId, userRole, null),
|
||||
enabled: Boolean(accessToken),
|
||||
});
|
||||
|
|
@ -159,7 +164,7 @@ export const useAllTeams = (): UseQueryResult<Team[]> => {
|
|||
};
|
||||
|
||||
export const useTeam = (teamId?: string) => {
|
||||
const { accessToken } = useAuthorized();
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
const queryClient = useQueryClient();
|
||||
return useQuery<Team>({
|
||||
queryKey: teamKeys.detail(teamId!),
|
||||
|
|
@ -177,7 +182,14 @@ export const useTeam = (teamId?: string) => {
|
|||
initialData: () => {
|
||||
if (!teamId) return undefined;
|
||||
|
||||
const teams = queryClient.getQueryData<Team[]>(teamKeys.list({}));
|
||||
const teams = queryClient.getQueryData<Team[]>(
|
||||
teamKeys.list({
|
||||
filters: {
|
||||
userId: userId ?? "",
|
||||
userRole: userRole ?? "",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return teams?.find((team) => team.team_id === teamId);
|
||||
},
|
||||
|
|
@ -202,6 +214,7 @@ export const useInfiniteTeams = (pageSize: number = 50, search?: string, organiz
|
|||
...(search && { search }),
|
||||
...(organizationId && { organizationId }),
|
||||
...(userId && { userId }),
|
||||
isAdmin: isAdmin ? "true" : "false",
|
||||
},
|
||||
}),
|
||||
queryFn: async ({ pageParam }) => {
|
||||
|
|
|
|||
|
|
@ -112,15 +112,15 @@ export function ActivityScope({
|
|||
},
|
||||
});
|
||||
};
|
||||
const discoveryScope: ActivitySelection = {
|
||||
source: value.source,
|
||||
service: "",
|
||||
filters: [],
|
||||
lookback_hours: value.lookback_hours,
|
||||
};
|
||||
const discoveryOptions = {
|
||||
queryKey: ["lens-activity-options", value.source, value.lookback_hours, asOf, accessToken],
|
||||
queryFn: () => load(discoveryScope),
|
||||
queryFn: () =>
|
||||
load({
|
||||
source: value.source,
|
||||
service: "",
|
||||
filters: [],
|
||||
lookback_hours: value.lookback_hours,
|
||||
}),
|
||||
staleTime: 60000,
|
||||
enabled: validWindow,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ const MCPToolsViewer = ({
|
|||
error: mcpToolsError,
|
||||
refetch: refetchTools,
|
||||
} = useQuery({
|
||||
queryKey: ["mcpTools", serverId, passthroughHeaders, oauthToken],
|
||||
queryKey: ["mcpTools", serverId, passthroughHeaders, oauthToken, userID],
|
||||
queryFn: async () => {
|
||||
if (!accessToken) throw new Error("Access Token required");
|
||||
const result = await listMCPTools(accessToken, serverId, buildCustomHeaders());
|
||||
|
|
|
|||
|
|
@ -244,17 +244,19 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
|
|||
|
||||
return await userListCall(
|
||||
accessToken,
|
||||
userIdFilter ? [userIdFilter] : null,
|
||||
pagination.pageIndex + 1,
|
||||
pagination.pageSize,
|
||||
userListQueryFilters.userId ? [userListQueryFilters.userId] : null,
|
||||
userListQueryFilters.page,
|
||||
userListQueryFilters.pageSize,
|
||||
null,
|
||||
userRoleFilter ?? null,
|
||||
teamFilter ?? null,
|
||||
ssoUserIdFilter ?? null,
|
||||
sortBy,
|
||||
sortOrder,
|
||||
orgAdminOrgIds ? orgAdminOrgIds.map((o) => o.organization_id) : null,
|
||||
searchFilter,
|
||||
userListQueryFilters.role ?? null,
|
||||
userListQueryFilters.team ?? null,
|
||||
userListQueryFilters.ssoUserId ?? null,
|
||||
userListQueryFilters.sortBy,
|
||||
userListQueryFilters.sortOrder,
|
||||
userListQueryFilters.orgAdminOrgIds
|
||||
? userListQueryFilters.orgAdminOrgIds.map((organization) => organization.organization_id)
|
||||
: null,
|
||||
userListQueryFilters.search,
|
||||
);
|
||||
},
|
||||
enabled: Boolean(accessToken && token && userRole && userID),
|
||||
|
|
|
|||
|
|
@ -30,7 +30,11 @@ export const useAutoRouterAvailability = (accessToken: string, body: Request, en
|
|||
queryFn: ({ signal }) =>
|
||||
apiClient.post<Availability>("/auto_router/availability", {
|
||||
accessToken,
|
||||
body: { ...body, complexity_router_config: JSON.parse(debounced) },
|
||||
body: {
|
||||
team_id: body.team_id,
|
||||
saved_model_id: body.saved_model_id,
|
||||
complexity_router_config: JSON.parse(debounced),
|
||||
},
|
||||
signal,
|
||||
}),
|
||||
enabled: enabled && Boolean(accessToken),
|
||||
|
|
@ -53,8 +57,10 @@ export const useAutoRouterAvailability = (accessToken: string, body: Request, en
|
|||
return query.data.error ?? null;
|
||||
};
|
||||
return {
|
||||
...query,
|
||||
data: query.data,
|
||||
isError: query.isError,
|
||||
isPending: query.isPending || (query.isFetching && !query.isFetchedAfterMount),
|
||||
refetch: query.refetch,
|
||||
isChecking,
|
||||
saveBlockedReason: saveBlockedReason(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -246,20 +246,19 @@ const LogsPanel: React.FC<Props> = ({ accessToken, userId }) => {
|
|||
const [page, setPage] = useState(1);
|
||||
const [selectedLog, setSelectedLog] = useState<LogRow | null>(null);
|
||||
|
||||
const startDate = getStartMoment(timeRange).utc().format("YYYY-MM-DD HH:mm:ss");
|
||||
const endDate = moment().utc().format("YYYY-MM-DD HH:mm:ss");
|
||||
|
||||
const logsCallOptions = {
|
||||
accessToken,
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
page,
|
||||
page_size: PAGE_SIZE,
|
||||
params: { user_id: userId, sort_by: "startTime", sort_order: "desc" as const },
|
||||
};
|
||||
const logsQueryOptions = {
|
||||
queryKey: [LOGS_QUERY_KEY, accessToken, userId, timeRange, page],
|
||||
queryFn: () => uiSpendLogsCall(logsCallOptions),
|
||||
queryFn: () => {
|
||||
const logsCallOptions = {
|
||||
accessToken,
|
||||
start_date: getStartMoment(timeRange).utc().format("YYYY-MM-DD HH:mm:ss"),
|
||||
end_date: moment().utc().format("YYYY-MM-DD HH:mm:ss"),
|
||||
page,
|
||||
page_size: PAGE_SIZE,
|
||||
params: { user_id: userId, sort_by: "startTime", sort_order: "desc" },
|
||||
};
|
||||
return uiSpendLogsCall(logsCallOptions);
|
||||
},
|
||||
enabled: !!accessToken && !!userId,
|
||||
placeholderData: keepPreviousData,
|
||||
};
|
||||
|
|
@ -270,10 +269,14 @@ const LogsPanel: React.FC<Props> = ({ accessToken, userId }) => {
|
|||
const totalPages = logs?.total_pages ?? 0;
|
||||
const total = logs?.total ?? 0;
|
||||
|
||||
const detailStartDate = selectedLog ? moment(selectedLog.startTime).utc().format("YYYY-MM-DD HH:mm:ss") : "";
|
||||
const { data: detailData, isLoading: isDetailLoading } = useQuery({
|
||||
queryKey: [LOGS_QUERY_KEY, "detail", accessToken, selectedLog?.request_id, selectedLog?.startTime],
|
||||
queryFn: () => uiSpendLogDetailsCall(accessToken, selectedLog!.request_id, detailStartDate),
|
||||
queryFn: () =>
|
||||
uiSpendLogDetailsCall(
|
||||
accessToken,
|
||||
selectedLog!.request_id,
|
||||
moment(selectedLog!.startTime).utc().format("YYYY-MM-DD HH:mm:ss"),
|
||||
),
|
||||
enabled: !!accessToken && !!selectedLog,
|
||||
});
|
||||
const details = detailData as LogDetails | undefined;
|
||||
|
|
|
|||
|
|
@ -91,11 +91,11 @@ const TIME_RANGE_OPTIONS: { value: TimeRange; label: string }[] = [
|
|||
|
||||
const UsagePanel: React.FC<Props> = ({ accessToken, userId }) => {
|
||||
const [timeRange, setTimeRange] = useState<TimeRange>("30d");
|
||||
const { start, end } = getDateRange(timeRange);
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: [USAGE_QUERY_KEY, accessToken, userId, timeRange],
|
||||
queryFn: () => {
|
||||
const { start, end } = getDateRange(timeRange);
|
||||
const request = {
|
||||
accessToken,
|
||||
startTime: start,
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ export default function RequestLogsPanel({ accessToken, token, userRole, userID,
|
|||
const { data: selectedKeyInfo } = useQuery(keyInfoQueryOptions);
|
||||
|
||||
const urlLogQueryOptions: UseQueryOptions<LogEntry | null> = {
|
||||
queryKey: ["logs", "byId", urlLogId, accessToken],
|
||||
queryKey: ["logs", "byId", urlLogId, accessToken, startTime, endTime, isCustomDate],
|
||||
queryFn: async () => {
|
||||
if (urlLogId === null) return null;
|
||||
const window = formatLogsWindow(startTime, endTime, isCustomDate);
|
||||
|
|
|
|||
|
|
@ -161,6 +161,9 @@ describe("useLogFilterLogic", () => {
|
|||
["startTime", { startTime: "2025-02-02T00:00:00" }],
|
||||
["columnFilters", { columnFilters: [{ id: LOG_FILTER_IDS.TEAM_ID, value: "team-2" }] }],
|
||||
["excludeInternalHealthChecks", { excludeInternalHealthChecks: true }],
|
||||
["token", { token: "other-token" }],
|
||||
["userRole", { userRole: "Internal User" }],
|
||||
["userID", { userID: "user-2" }],
|
||||
])("refetches when %s changes", async (_label, nextProps) => {
|
||||
const { rerender } = renderHook((props: HookOverrides) => useLogFilterLogic({ ...defaultProps, ...props }), {
|
||||
wrapper,
|
||||
|
|
|
|||
|
|
@ -155,6 +155,9 @@ export function useLogFilterLogic({
|
|||
sortOrder,
|
||||
excludeInternalHealthChecks,
|
||||
sessionCursor,
|
||||
token,
|
||||
userRole,
|
||||
userID,
|
||||
],
|
||||
queryFn: async () => {
|
||||
if (!accessToken || !token || !userRole || !userID) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue