refactor: api keys API (#2652)
* feat: create useDeleteApiKey hook to handle api keys delete * refactor: use mutation to handle delete api key * [autofix.ci] apply automated fixes * feat: create useGetApiKeys hook * refactor: use useGetApiKeysQuery hook to get api keys data * [autofix.ci] apply automated fixes * refactor: change interface name * refactor: remove unnused loading state and use react state instead of react ref to ensure component render * fix: multiple refreshs when auto_login=false --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
cffed2c9be
commit
6fa3c7b17d
7 changed files with 140 additions and 107 deletions
|
|
@ -1 +1,3 @@
|
|||
export * from "./use-delete-api-key";
|
||||
export * from "./use-get-api-keys";
|
||||
export * from "./use-post-add-api-key";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
import { useMutationFunctionType } from "@/types/api";
|
||||
import { api } from "../../api";
|
||||
import { getURL } from "../../helpers/constants";
|
||||
import { UseRequestProcessor } from "../../services/request-processor";
|
||||
|
||||
interface IDeleteApiKey {
|
||||
keyId: string;
|
||||
}
|
||||
|
||||
// add types for error handling and success
|
||||
export const useDeleteApiKey: useMutationFunctionType<IDeleteApiKey> = (
|
||||
options,
|
||||
) => {
|
||||
const { mutate } = UseRequestProcessor();
|
||||
|
||||
const deleteApiKeyFn = async (payload: IDeleteApiKey): Promise<any> => {
|
||||
const res = await api.delete(`${getURL("API_KEY")}/${payload.keyId}`);
|
||||
return res.data;
|
||||
};
|
||||
|
||||
const mutation = mutate(["useDeleteApiKey"], deleteApiKeyFn, options);
|
||||
|
||||
return mutation;
|
||||
};
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import { useDarkStore } from "@/stores/darkStore";
|
||||
import { useQueryFunctionType } from "@/types/api";
|
||||
import { api } from "../../api";
|
||||
import { getURL } from "../../helpers/constants";
|
||||
import { UseRequestProcessor } from "../../services/request-processor";
|
||||
|
||||
export interface IApiKeysDataArray {
|
||||
name: string;
|
||||
last_used_at: string | null;
|
||||
total_uses: number;
|
||||
is_active: boolean;
|
||||
id: string;
|
||||
api_key: string;
|
||||
user_id: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface IApiQueryResponse {
|
||||
total_count: number;
|
||||
user_id: string;
|
||||
api_keys: Array<IApiKeysDataArray>;
|
||||
}
|
||||
|
||||
export const useGetApiKeysQuery: useQueryFunctionType<
|
||||
undefined,
|
||||
IApiQueryResponse
|
||||
> = (_, options) => {
|
||||
const { query } = UseRequestProcessor();
|
||||
|
||||
const getApiKeysFn = async () => {
|
||||
return await api.get<IApiQueryResponse>(`${getURL("API_KEY")}/`);
|
||||
};
|
||||
|
||||
const responseFn = async () => {
|
||||
const { data } = await getApiKeysFn();
|
||||
return data;
|
||||
};
|
||||
|
||||
const queryResult = query(["useGetApiKeysQuery"], responseFn, { ...options });
|
||||
|
||||
return queryResult;
|
||||
};
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
import { getApiKey } from "../../../../../controllers/API";
|
||||
import { Users } from "../../../../../types/api";
|
||||
|
||||
const useApiKeys = (
|
||||
userData: Users | null,
|
||||
setLoadingKeys: (load: boolean) => void,
|
||||
keysList: React.MutableRefObject<never[]>,
|
||||
setUserId: (userId: string) => void,
|
||||
) => {
|
||||
const fetchApiKeys = () => {
|
||||
setLoadingKeys(true);
|
||||
getApiKey()
|
||||
.then((keys) => {
|
||||
keysList.current = keys["api_keys"].map((apikey) => ({
|
||||
...apikey,
|
||||
name: apikey.name && apikey.name !== "" ? apikey.name : "Untitled",
|
||||
last_used_at: apikey.last_used_at ?? "Never",
|
||||
}));
|
||||
setUserId(keys["user_id"]);
|
||||
setLoadingKeys(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoadingKeys(false);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
fetchApiKeys,
|
||||
};
|
||||
};
|
||||
|
||||
export default useApiKeys;
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
import {
|
||||
DEL_KEY_ERROR_ALERT,
|
||||
DEL_KEY_ERROR_ALERT_PLURAL,
|
||||
DEL_KEY_SUCCESS_ALERT,
|
||||
DEL_KEY_SUCCESS_ALERT_PLURAL,
|
||||
} from "../../../../../constants/alerts_constants";
|
||||
import { deleteApiKey } from "../../../../../controllers/API";
|
||||
|
||||
const useDeleteApiKeys = (
|
||||
selectedRows: string[],
|
||||
resetFilter: () => void,
|
||||
setSuccessData: (data: { title: string }) => void,
|
||||
setErrorData: (data: { title: string; list: string[] }) => void,
|
||||
) => {
|
||||
const handleDeleteKey = () => {
|
||||
Promise.all(selectedRows.map((selectedRow) => deleteApiKey(selectedRow)))
|
||||
.then(() => {
|
||||
resetFilter();
|
||||
setSuccessData({
|
||||
title:
|
||||
selectedRows.length === 1
|
||||
? DEL_KEY_SUCCESS_ALERT
|
||||
: DEL_KEY_SUCCESS_ALERT_PLURAL,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title:
|
||||
selectedRows.length === 1
|
||||
? DEL_KEY_ERROR_ALERT
|
||||
: DEL_KEY_ERROR_ALERT_PLURAL,
|
||||
list: [error?.response?.data?.detail],
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
handleDeleteKey,
|
||||
};
|
||||
};
|
||||
|
||||
export default useDeleteApiKeys;
|
||||
|
|
@ -1,3 +1,14 @@
|
|||
import {
|
||||
DEL_KEY_ERROR_ALERT,
|
||||
DEL_KEY_ERROR_ALERT_PLURAL,
|
||||
DEL_KEY_SUCCESS_ALERT,
|
||||
DEL_KEY_SUCCESS_ALERT_PLURAL,
|
||||
} from "@/constants/alerts_constants";
|
||||
import {
|
||||
IApiKeysDataArray,
|
||||
useDeleteApiKey,
|
||||
useGetApiKeysQuery,
|
||||
} from "@/controllers/API/queries/api-keys";
|
||||
import { SelectionChangedEvent } from "ag-grid-community";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import TableComponent from "../../../../components/tableComponent";
|
||||
|
|
@ -5,8 +16,6 @@ import { AuthContext } from "../../../../contexts/authContext";
|
|||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import ApiKeyHeaderComponent from "./components/ApiKeyHeader";
|
||||
import { getColumnDefs } from "./helpers/column-defs";
|
||||
import useApiKeys from "./hooks/use-api-keys";
|
||||
import useDeleteApiKeys from "./hooks/use-handle-delete-key";
|
||||
|
||||
export default function ApiKeysPage() {
|
||||
const [loadingKeys, setLoadingKeys] = useState(true);
|
||||
|
|
@ -15,29 +24,61 @@ export default function ApiKeysPage() {
|
|||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const { userData } = useContext(AuthContext);
|
||||
const [userId, setUserId] = useState("");
|
||||
const keysList = useRef([]);
|
||||
const [keysList, setKeysList] = useState<IApiKeysDataArray[]>([]);
|
||||
const { refetch } = useGetApiKeysQuery();
|
||||
|
||||
useEffect(() => {
|
||||
fetchApiKeys();
|
||||
}, [userData]);
|
||||
|
||||
const { fetchApiKeys } = useApiKeys(
|
||||
userData,
|
||||
setLoadingKeys,
|
||||
keysList,
|
||||
setUserId,
|
||||
);
|
||||
|
||||
function resetFilter() {
|
||||
fetchApiKeys();
|
||||
async function getApiKeysQuery() {
|
||||
const { data } = await refetch();
|
||||
if (data !== undefined) {
|
||||
const updatedKeysList = data["api_keys"].map((apikey) => ({
|
||||
...apikey,
|
||||
name: apikey.name && apikey.name !== "" ? apikey.name : "Untitled",
|
||||
last_used_at: apikey.last_used_at ?? "Never",
|
||||
}));
|
||||
setKeysList(updatedKeysList);
|
||||
setUserId(data["user_id"]);
|
||||
}
|
||||
}
|
||||
|
||||
const { handleDeleteKey } = useDeleteApiKeys(
|
||||
selectedRows,
|
||||
resetFilter,
|
||||
setSuccessData,
|
||||
setErrorData,
|
||||
);
|
||||
useEffect(() => {
|
||||
if (userData) {
|
||||
getApiKeysQuery();
|
||||
}
|
||||
}, [userData]);
|
||||
|
||||
function resetFilter() {
|
||||
getApiKeysQuery();
|
||||
}
|
||||
|
||||
const { mutate } = useDeleteApiKey();
|
||||
|
||||
function handleDeleteApi() {
|
||||
for (let i = 0; i < selectedRows.length; i++) {
|
||||
mutate(
|
||||
{ keyId: selectedRows[i] },
|
||||
{
|
||||
onSuccess: () => {
|
||||
resetFilter();
|
||||
setSuccessData({
|
||||
title:
|
||||
selectedRows.length === 1
|
||||
? DEL_KEY_SUCCESS_ALERT
|
||||
: DEL_KEY_SUCCESS_ALERT_PLURAL,
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
setErrorData({
|
||||
title:
|
||||
selectedRows.length === 1
|
||||
? DEL_KEY_ERROR_ALERT
|
||||
: DEL_KEY_ERROR_ALERT_PLURAL,
|
||||
list: [error?.response?.data?.detail],
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const columnDefs = getColumnDefs();
|
||||
|
||||
|
|
@ -45,14 +86,14 @@ export default function ApiKeysPage() {
|
|||
<div className="flex h-full w-full flex-col justify-between gap-6">
|
||||
<ApiKeyHeaderComponent
|
||||
selectedRows={selectedRows}
|
||||
fetchApiKeys={fetchApiKeys}
|
||||
fetchApiKeys={getApiKeysQuery}
|
||||
userId={userId}
|
||||
/>
|
||||
|
||||
<div className="flex h-full w-full flex-col justify-between">
|
||||
<TableComponent
|
||||
key={"apiKeys"}
|
||||
onDelete={handleDeleteKey}
|
||||
onDelete={handleDeleteApi}
|
||||
overlayNoRowsTemplate="No data available"
|
||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||
setSelectedRows(event.api.getSelectedRows().map((row) => row.id));
|
||||
|
|
@ -61,7 +102,7 @@ export default function ApiKeysPage() {
|
|||
suppressRowClickSelection={true}
|
||||
pagination={true}
|
||||
columnDefs={columnDefs}
|
||||
rowData={keysList.current}
|
||||
rowData={keysList}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -110,9 +110,8 @@ test("PromptTemplateComponent", async ({ page }) => {
|
|||
}
|
||||
|
||||
value =
|
||||
(await page
|
||||
.locator('//*[@id="textarea_str_edit_prompt1"]')
|
||||
.inputValue()) ?? "";
|
||||
(await page.locator('//*[@id="textarea_str_edit_prompt1"]').inputValue()) ??
|
||||
"";
|
||||
|
||||
if (value != "prompt_name_test_123123!@#!@#") {
|
||||
expect(false).toBeTruthy();
|
||||
|
|
@ -126,14 +125,14 @@ test("PromptTemplateComponent", async ({ page }) => {
|
|||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.getByTestId('textarea_str_edit_prompt1-ExternalLink').click();
|
||||
await page.getByTestId("textarea_str_edit_prompt1-ExternalLink").click();
|
||||
await page
|
||||
.getByTestId("text-area-modal")
|
||||
.fill("prompt_edit_test_12312312321!@#$");
|
||||
|
||||
await page.getByText("Finish Editing", { exact: true }).click();
|
||||
|
||||
await page.getByTestId('textarea_str_edit_prompt-ExternalLink').click();
|
||||
await page.getByTestId("textarea_str_edit_prompt-ExternalLink").click();
|
||||
await page
|
||||
.getByTestId("text-area-modal")
|
||||
.fill("prompt_edit_test_44444444444!@#$");
|
||||
|
|
@ -194,9 +193,8 @@ test("PromptTemplateComponent", async ({ page }) => {
|
|||
}
|
||||
|
||||
value =
|
||||
(await page
|
||||
.locator('//*[@id="textarea_str_edit_prompt1"]')
|
||||
.inputValue()) ?? "";
|
||||
(await page.locator('//*[@id="textarea_str_edit_prompt1"]').inputValue()) ??
|
||||
"";
|
||||
|
||||
if (value != "prompt_edit_test_12312312321!@#$") {
|
||||
expect(false).toBeTruthy();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue