diff --git a/src/frontend/src/controllers/API/queries/api-keys/index.ts b/src/frontend/src/controllers/API/queries/api-keys/index.ts index e59061a6a..382a910fd 100644 --- a/src/frontend/src/controllers/API/queries/api-keys/index.ts +++ b/src/frontend/src/controllers/API/queries/api-keys/index.ts @@ -1 +1,3 @@ +export * from "./use-delete-api-key"; +export * from "./use-get-api-keys"; export * from "./use-post-add-api-key"; diff --git a/src/frontend/src/controllers/API/queries/api-keys/use-delete-api-key.ts b/src/frontend/src/controllers/API/queries/api-keys/use-delete-api-key.ts new file mode 100644 index 000000000..a0ec09204 --- /dev/null +++ b/src/frontend/src/controllers/API/queries/api-keys/use-delete-api-key.ts @@ -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 = ( + options, +) => { + const { mutate } = UseRequestProcessor(); + + const deleteApiKeyFn = async (payload: IDeleteApiKey): Promise => { + const res = await api.delete(`${getURL("API_KEY")}/${payload.keyId}`); + return res.data; + }; + + const mutation = mutate(["useDeleteApiKey"], deleteApiKeyFn, options); + + return mutation; +}; diff --git a/src/frontend/src/controllers/API/queries/api-keys/use-get-api-keys.ts b/src/frontend/src/controllers/API/queries/api-keys/use-get-api-keys.ts new file mode 100644 index 000000000..850fa7b91 --- /dev/null +++ b/src/frontend/src/controllers/API/queries/api-keys/use-get-api-keys.ts @@ -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; +} + +export const useGetApiKeysQuery: useQueryFunctionType< + undefined, + IApiQueryResponse +> = (_, options) => { + const { query } = UseRequestProcessor(); + + const getApiKeysFn = async () => { + return await api.get(`${getURL("API_KEY")}/`); + }; + + const responseFn = async () => { + const { data } = await getApiKeysFn(); + return data; + }; + + const queryResult = query(["useGetApiKeysQuery"], responseFn, { ...options }); + + return queryResult; +}; diff --git a/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/hooks/use-api-keys.tsx b/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/hooks/use-api-keys.tsx deleted file mode 100644 index 2bdbe33f9..000000000 --- a/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/hooks/use-api-keys.tsx +++ /dev/null @@ -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, - 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; diff --git a/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/hooks/use-handle-delete-key.tsx b/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/hooks/use-handle-delete-key.tsx deleted file mode 100644 index 0d642dfbf..000000000 --- a/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/hooks/use-handle-delete-key.tsx +++ /dev/null @@ -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; diff --git a/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx index af5280f72..73b484d5f 100644 --- a/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx @@ -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([]); + 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() {
{ 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} />
diff --git a/src/frontend/tests/end-to-end/promptModalComponent.spec.ts b/src/frontend/tests/end-to-end/promptModalComponent.spec.ts index 37a8fbb6e..0053b5adf 100644 --- a/src/frontend/tests/end-to-end/promptModalComponent.spec.ts +++ b/src/frontend/tests/end-to-end/promptModalComponent.spec.ts @@ -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();