From 24ba353a104caa8cef964db84a623488adc2964e Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 18:13:53 -0300 Subject: [PATCH] Added table of API Keys at settings page --- .../src/constants/alerts_constants.tsx | 2 + src/frontend/src/constants/constants.ts | 7 +- src/frontend/src/pages/ApiKeysPage/index.tsx | 7 +- .../SettingsPage/pages/ApiKeysPage/index.tsx | 234 +++++++++--------- 4 files changed, 129 insertions(+), 121 deletions(-) diff --git a/src/frontend/src/constants/alerts_constants.tsx b/src/frontend/src/constants/alerts_constants.tsx index c3e3ba946..ebc7543d5 100644 --- a/src/frontend/src/constants/alerts_constants.tsx +++ b/src/frontend/src/constants/alerts_constants.tsx @@ -23,6 +23,7 @@ export const USER_EDIT_ERROR_ALERT = "Error on edit user"; export const USER_ADD_ERROR_ALERT = "Error when adding new user"; export const SIGNIN_ERROR_ALERT = "Error signing in"; export const DEL_KEY_ERROR_ALERT = "Error on delete key"; +export const DEL_KEY_ERROR_ALERT_PLURAL = "Error on delete keys"; export const UPLOAD_ERROR_ALERT = "Error uploading file"; export const WRONG_FILE_ERROR_ALERT = "Invalid file type"; export const UPLOAD_ALERT_LIST = "Please upload a JSON file"; @@ -54,6 +55,7 @@ export const USER_DEL_SUCCESS_ALERT = "Success! User deleted!"; export const USER_EDIT_SUCCESS_ALERT = "Success! User edited!"; export const USER_ADD_SUCCESS_ALERT = "Success! New user added!"; export const DEL_KEY_SUCCESS_ALERT = "Success! Key deleted!"; +export const DEL_KEY_SUCCESS_ALERT_PLURAL = "Success! Keys deleted!"; export const FLOW_BUILD_SUCCESS_ALERT = `Flow built successfully`; export const SAVE_SUCCESS_ALERT = "Changes saved successfully!"; diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts index b0a0b55c5..ce9bf4d37 100644 --- a/src/frontend/src/constants/constants.ts +++ b/src/frontend/src/constants/constants.ts @@ -613,11 +613,8 @@ export const FETCH_ERROR_DESCRIPION = export const SIGN_UP_SUCCESS = "Account created! Await admin activation. "; -export const API_PAGE_PARAGRAPH_1 = - "Your secret API keys are listed below. Please note that we do not display your secret API keys again after you generate them."; - -export const API_PAGE_PARAGRAPH_2 = - "Do not share your API key with others, or expose it in the browser or other client-side code."; +export const API_PAGE_PARAGRAPH = + "Your secret API keys are listed below. Do not share your API key with others, or expose it in the browser or other client-side code."; export const API_PAGE_USER_KEYS = "This user does not have any keys assigned at the moment."; diff --git a/src/frontend/src/pages/ApiKeysPage/index.tsx b/src/frontend/src/pages/ApiKeysPage/index.tsx index c971042d0..4e271825e 100644 --- a/src/frontend/src/pages/ApiKeysPage/index.tsx +++ b/src/frontend/src/pages/ApiKeysPage/index.tsx @@ -22,8 +22,7 @@ import { DEL_KEY_SUCCESS_ALERT, } from "../../constants/alerts_constants"; import { - API_PAGE_PARAGRAPH_1, - API_PAGE_PARAGRAPH_2, + API_PAGE_PARAGRAPH, API_PAGE_USER_KEYS, LAST_USED_SPAN_1, LAST_USED_SPAN_2, @@ -128,9 +127,7 @@ export default function ApiKeysPage() { API keys

- {API_PAGE_PARAGRAPH_1} -
- {API_PAGE_PARAGRAPH_2} + {API_PAGE_PARAGRAPH}

diff --git a/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx index c6eb048d7..96eca02d5 100644 --- a/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/ApiKeysPage/index.tsx @@ -2,149 +2,154 @@ import IconComponent from "../../../../components/genericIconComponent"; import { Button } from "../../../../components/ui/button"; import { ColDef, ColGroupDef, SelectionChangedEvent } from "ag-grid-community"; -import { useEffect, useState } from "react"; +import { useContext, useEffect, useRef, useState } from "react"; import AddNewVariableButton from "../../../../components/addNewVariableButtonComponent/addNewVariableButton"; import Dropdown from "../../../../components/dropdownComponent"; import ForwardedIconComponent from "../../../../components/genericIconComponent"; import TableComponent from "../../../../components/tableComponent"; import { Badge } from "../../../../components/ui/badge"; import { Card, CardContent } from "../../../../components/ui/card"; -import { deleteGlobalVariable } from "../../../../controllers/API"; +import { + deleteApiKey, + deleteGlobalVariable, + getApiKey, +} from "../../../../controllers/API"; import useAlertStore from "../../../../stores/alertStore"; import { useGlobalVariablesStore } from "../../../../stores/globalVariablesStore/globalVariables"; import { cn } from "../../../../utils/utils"; +import { + API_PAGE_PARAGRAPH, + LAST_USED_SPAN_1, + LAST_USED_SPAN_2, +} from "../../../../constants/constants"; +import TableAutoCellRender from "../../../../components/tableAutoCellRender"; +import { + DEL_KEY_SUCCESS_ALERT, + DEL_KEY_ERROR_ALERT, + DEL_KEY_SUCCESS_ALERT_PLURAL, + DEL_KEY_ERROR_ALERT_PLURAL, +} from "../../../../constants/alerts_constants"; +import { AuthContext } from "../../../../contexts/authContext"; +import { ApiKey } from "../../../../types/components"; +import SecretKeyModal from "../../../../modals/secretKeyModal"; export default function ApiKeysPage() { - const globalVariablesEntries = useGlobalVariablesStore( - (state) => state.globalVariablesEntries, - ); - const removeGlobalVariable = useGlobalVariablesStore( - (state) => state.removeGlobalVariable, - ); - const globalVariables = useGlobalVariablesStore( - (state) => state.globalVariables, - ); + const [loadingKeys, setLoadingKeys] = useState(true); + const [selectedRows, setSelectedRows] = useState([]); + const setSuccessData = useAlertStore((state) => state.setSuccessData); const setErrorData = useAlertStore((state) => state.setErrorData); - const getVariableId = useGlobalVariablesStore((state) => state.getVariableId); - - const BadgeRenderer = (props) => { - return props.value !== "" ? ( -
- - {props.value} - -
- ) : ( -
- ); - }; - - const [rowData, setRowData] = useState< - { - type: string | undefined; - id: string; - name: string; - default_fields: string | undefined; - }[] - >([]); + const { userData } = useContext(AuthContext); + const [userId, setUserId] = useState(""); + const keysList = useRef([]); useEffect(() => { - const rows: Array<{ - type: string | undefined; - id: string; - name: string; - default_fields: string | undefined; - }> = []; - if (globalVariablesEntries === undefined) return; - globalVariablesEntries.forEach((entrie) => { - const globalVariableObj = globalVariables[entrie]; - rows.push({ - type: globalVariableObj.type, - id: globalVariableObj.id, - default_fields: (globalVariableObj.default_fields ?? []).join(", "), - name: entrie, - }); - }); - setRowData(rows); - }, [globalVariables]); + getKeys(); + }, [userData]); - const DropdownEditor = ({ options, value, onValueChange }) => { + function getKeys() { + setLoadingKeys(true); + if (userData) { + getApiKey() + .then((keys: [ApiKey]) => { + keysList.current = keys["api_keys"].map((apikey: ApiKey) => ({ + ...apikey, + last_used_at: apikey.last_used_at ?? "Never", + })); + setUserId(keys["user_id"]); + setLoadingKeys(false); + }) + .catch((error) => { + setLoadingKeys(false); + }); + } + } + + function resetFilter() { + getKeys(); + } + + function handleDeleteKey() { + Promise.all(selectedRows.map((selectedRow) => deleteApiKey(selectedRow))) + .then((res) => { + 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"]], + }); + }); + } + + function lastUsedMessage() { return ( - -
-
+
+ + {LAST_USED_SPAN_1} +

{LAST_USED_SPAN_2} +
+
); - }; - // Column Definitions: Defines the columns to be displayed. - const [colDefs, setColDefs] = useState<(ColDef | ColGroupDef)[]>([ + } + + const columnDefs = [ { headerCheckboxSelection: true, checkboxSelection: true, showDisabledCheckboxes: true, - headerName: "Variable Name", + headerName: "Name", field: "name", + cellRenderer: TableAutoCellRender, flex: 2, - }, //This column will be twice as wide as the others - { - field: "type", - cellRenderer: BadgeRenderer, - cellEditor: DropdownEditor, - cellEditorParams: { - options: ["Generic", "Credential"], - }, - flex: 1, - editable: false, }, - // { - // field: "value", - // cellEditor: "agLargeTextCellEditor", - // flex: 2, - // editable: false, - // }, { - headerName: "Apply To Fields", - field: "default_fields", + headerName: "Key", + field: "api_key", + cellRenderer: TableAutoCellRender, + flex: 1, + }, + { + headerName: "Created", + field: "created_at", + cellRenderer: TableAutoCellRender, + flex: 1, + }, + { + headerName: "Last Used", + field: "last_used_at", + cellRenderer: TableAutoCellRender, + flex: 1, + }, + { + headerName: "Total Uses", + field: "total_uses", + cellRenderer: TableAutoCellRender, flex: 1, - editable: false, resizable: false, }, - ]); - - const [selectedRows, setSelectedRows] = useState([]); - - async function removeVariables() { - const deleteGlobalVariablesPromise = selectedRows.map(async (row) => { - const id = getVariableId(row); - const deleteGlobalVariables = deleteGlobalVariable(id!); - await deleteGlobalVariables; - }); - Promise.all(deleteGlobalVariablesPromise) - .then(() => { - selectedRows.forEach((row) => { - removeGlobalVariable(row); - }); - }) - .catch(() => { - setErrorData({ - title: `Error deleting global variables.`, - }); - }); - } + ]; return (

- Global Variables + API Keys

-

- Manage global variables and assign them to fields. -

+

{API_PAGE_PARAGRAPH}

- + - +
@@ -177,14 +189,14 @@ export default function ApiKeysPage() { overlayNoRowsTemplate="No data available" onSelectionChanged={(event: SelectionChangedEvent) => { setSelectedRows( - event.api.getSelectedRows().map((row) => row.name), + event.api.getSelectedRows().map((row) => row.id), ); }} rowSelection="multiple" suppressRowClickSelection={true} pagination={true} - columnDefs={colDefs} - rowData={rowData} + columnDefs={columnDefs} + rowData={keysList.current} />