Skip to content

Commit

Permalink
refactor: api keys API (langflow-ai#2652)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
igorrCarvalho and autofix-ci[bot] authored Jul 29, 2024
1 parent cffed2c commit 6fa3c7b
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 103 deletions.
2 changes: 2 additions & 0 deletions src/frontend/src/controllers/API/queries/api-keys/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from "./use-delete-api-key";
export * from "./use-get-api-keys";
export * from "./use-post-add-api-key";
Original file line number Diff line number Diff line change
@@ -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;
};
Original file line number Diff line number Diff line change
@@ -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;
};

This file was deleted.

This file was deleted.

83 changes: 62 additions & 21 deletions src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
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";
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);
Expand All @@ -15,44 +24,76 @@ 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();

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"]);
}
}

useEffect(() => {
fetchApiKeys();
if (userData) {
getApiKeysQuery();
}
}, [userData]);

const { fetchApiKeys } = useApiKeys(
userData,
setLoadingKeys,
keysList,
setUserId,
);

function resetFilter() {
fetchApiKeys();
getApiKeysQuery();
}

const { handleDeleteKey } = useDeleteApiKeys(
selectedRows,
resetFilter,
setSuccessData,
setErrorData,
);
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();

return (
<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));
Expand All @@ -61,7 +102,7 @@ export default function ApiKeysPage() {
suppressRowClickSelection={true}
pagination={true}
columnDefs={columnDefs}
rowData={keysList.current}
rowData={keysList}
/>
</div>
</div>
Expand Down
14 changes: 6 additions & 8 deletions src/frontend/tests/end-to-end/promptModalComponent.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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!@#$");
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 6fa3c7b

Please sign in to comment.