mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: merge Limits + Usage into unified Spend page (#24093)
This commit is contained in:
@@ -7,8 +7,6 @@ import {
|
||||
cancelChatListRefetches,
|
||||
chatCostSummary,
|
||||
chatCostSummaryKey,
|
||||
chatCostUsers,
|
||||
chatCostUsersKey,
|
||||
chatDiffContentsKey,
|
||||
chatKey,
|
||||
chatMessagesKey,
|
||||
@@ -20,6 +18,7 @@ import {
|
||||
infiniteChats,
|
||||
interruptChat,
|
||||
invalidateChatListQueries,
|
||||
paginatedChatCostUsers,
|
||||
pinChat,
|
||||
promoteChatQueuedMessage,
|
||||
regenerateChatTitle,
|
||||
@@ -176,20 +175,6 @@ describe("invalidateChatListQueries", () => {
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("does not invalidate chatCostUsersKey", async () => {
|
||||
const queryClient = createTestQueryClient();
|
||||
|
||||
queryClient.setQueryData(chatCostUsersKey(undefined), {});
|
||||
queryClient.setQueryData(chatsKey, [makeChat("chat-1")]);
|
||||
|
||||
await invalidateChatListQueries(queryClient);
|
||||
|
||||
expect(
|
||||
queryClient.getQueryState(chatCostUsersKey(undefined))?.isInvalidated,
|
||||
"chatCostUsersKey should NOT be invalidated",
|
||||
).not.toBe(true);
|
||||
});
|
||||
|
||||
it("does not invalidate a different chat's queries", async () => {
|
||||
const queryClient = createTestQueryClient();
|
||||
const chatId = "chat-1";
|
||||
@@ -631,25 +616,42 @@ describe("chat cost query factories", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("builds a distinct users query key and forwards snake_case params", async () => {
|
||||
const params = {
|
||||
it("builds paginated cost users query with correct key and coerces empty username", async () => {
|
||||
const payload = {
|
||||
start_date: "2025-01-01",
|
||||
end_date: "2025-01-31",
|
||||
username: "alice",
|
||||
limit: 10,
|
||||
offset: 20,
|
||||
username: "",
|
||||
};
|
||||
vi.mocked(API.experimental.getChatCostUsers).mockResolvedValue(
|
||||
{} as TypesGen.ChatCostUsersResponse,
|
||||
);
|
||||
const result = paginatedChatCostUsers(payload);
|
||||
|
||||
const query = chatCostUsers(params);
|
||||
// queryPayload returns the original payload.
|
||||
const pageParams = {
|
||||
pageNumber: 2,
|
||||
limit: 25,
|
||||
offset: 25,
|
||||
searchParams: new URLSearchParams(),
|
||||
};
|
||||
expect(result.queryPayload(pageParams)).toEqual(payload);
|
||||
|
||||
expect(chatCostUsersKey(params)).toEqual(["chats", "costUsers", params]);
|
||||
expect(query.queryKey).toEqual(["chats", "costUsers", params]);
|
||||
expect(query.queryKey).not.toEqual(chatCostSummaryKey("me", params));
|
||||
await query.queryFn();
|
||||
expect(API.experimental.getChatCostUsers).toHaveBeenCalledWith(params);
|
||||
// queryKey includes the payload and page number.
|
||||
const key = result.queryKey({ ...pageParams, payload });
|
||||
expect(key).toEqual(["chats", "costUsers", payload, 2]);
|
||||
|
||||
// queryFn coerces empty username to undefined.
|
||||
// Cast needed because PaginatedQueryFnContext includes
|
||||
// react-query internal fields that aren't relevant here.
|
||||
await (
|
||||
result.queryFn as (params: Record<string, unknown>) => Promise<unknown>
|
||||
)({
|
||||
...pageParams,
|
||||
payload,
|
||||
});
|
||||
expect(API.experimental.getChatCostUsers).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ username: undefined, limit: 25, offset: 25 }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
} from "react-query";
|
||||
import { API } from "#/api/api";
|
||||
import type * as TypesGen from "#/api/typesGenerated";
|
||||
import type { UsePaginatedQueryOptions } from "#/hooks/usePaginatedQuery";
|
||||
|
||||
export const chatsKey = ["chats"] as const;
|
||||
export const chatKey = (chatId: string) => ["chats", chatId] as const;
|
||||
@@ -992,12 +993,6 @@ type ChatCostDateParams = {
|
||||
end_date?: string;
|
||||
};
|
||||
|
||||
type ChatCostUsersParams = ChatCostDateParams & {
|
||||
username?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
};
|
||||
|
||||
export const chatCostSummaryKey = (user = "me", params?: ChatCostDateParams) =>
|
||||
[...chatsKey, "costSummary", user, params] as const;
|
||||
|
||||
@@ -1007,14 +1002,33 @@ export const chatCostSummary = (user = "me", params?: ChatCostDateParams) => ({
|
||||
staleTime: 60_000,
|
||||
});
|
||||
|
||||
export const chatCostUsersKey = (params?: ChatCostUsersParams) =>
|
||||
[...chatsKey, "costUsers", params] as const;
|
||||
interface PaginatedChatCostUsersPayload {
|
||||
username: string;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
}
|
||||
|
||||
export const chatCostUsers = (params?: ChatCostUsersParams) => ({
|
||||
queryKey: chatCostUsersKey(params),
|
||||
queryFn: () => API.experimental.getChatCostUsers(params),
|
||||
staleTime: 60_000,
|
||||
});
|
||||
export function paginatedChatCostUsers(
|
||||
payload: PaginatedChatCostUsersPayload,
|
||||
): UsePaginatedQueryOptions<
|
||||
TypesGen.ChatCostUsersResponse,
|
||||
PaginatedChatCostUsersPayload
|
||||
> {
|
||||
return {
|
||||
queryPayload: () => payload,
|
||||
queryKey: ({ payload, pageNumber }) =>
|
||||
[...chatsKey, "costUsers", payload, pageNumber] as const,
|
||||
queryFn: ({ payload, limit, offset }) =>
|
||||
API.experimental.getChatCostUsers({
|
||||
start_date: payload.start_date,
|
||||
end_date: payload.end_date,
|
||||
username: payload.username || undefined,
|
||||
limit,
|
||||
offset,
|
||||
}),
|
||||
staleTime: 60_000,
|
||||
};
|
||||
}
|
||||
|
||||
const prInsightsKey = (params?: { start_date?: string; end_date?: string }) =>
|
||||
[...chatsKey, "prInsights", params] as const;
|
||||
|
||||
Reference in New Issue
Block a user