From 2c4e21c5cd5132f4e328d27b25c80cf47182eac6 Mon Sep 17 00:00:00 2001 From: Avneesh Hota Date: Thu, 12 Dec 2024 18:44:10 +0530 Subject: [PATCH 1/2] allow custom collection group names to be editable --- .../com/akto/action/ApiCollectionsAction.java | 31 ++++++++- apps/dashboard/src/main/resources/struts.xml | 29 ++++++++ .../src/apps/dashboard/pages/observe/api.js | 10 +++ .../observe/api_collections/ApiEndpoints.jsx | 66 ++++++++++++++++--- 4 files changed, 127 insertions(+), 9 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java b/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java index 37c1bce2b1..3d3bbcc58f 100644 --- a/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java @@ -216,7 +216,7 @@ public String fetchCollection() { private String collectionName; private boolean isValidApiCollectionName(){ - if (this.collectionName == null) { + if (this.collectionName == null || this.collectionName.length() == 0) { addActionError("Invalid collection name"); return false; } @@ -713,6 +713,35 @@ public String updateEnvType(){ return Action.ERROR.toUpperCase(); } + public String editCollectionName() { + if(!isValidApiCollectionName()){ + return ERROR.toUpperCase(); + } + + ApiCollection apiCollection = ApiCollectionsDao.instance.getMeta(apiCollectionId); + if (apiCollection == null) { + String errorMessage = "API collection not found"; + addActionError(errorMessage); + return Action.ERROR.toUpperCase(); + } + + if (apiCollection.getHostName() != null) { + String errorMessage = "Traffic API collection can't edited"; + addActionError(errorMessage); + return Action.ERROR.toUpperCase(); + } + + ApiCollectionsDao.instance.updateOne( + Filters.eq(ApiCollection.ID, apiCollectionId), + Updates.combine( + Updates.set(ApiCollection.NAME, collectionName), + Updates.set("displayName", collectionName) + ) + ); + + return Action.SUCCESS.toUpperCase(); + } + public List getApiCollections() { return this.apiCollections; } diff --git a/apps/dashboard/src/main/resources/struts.xml b/apps/dashboard/src/main/resources/struts.xml index c598911ef8..52dacf631e 100644 --- a/apps/dashboard/src/main/resources/struts.xml +++ b/apps/dashboard/src/main/resources/struts.xml @@ -7396,6 +7396,35 @@ + + + + + API_COLLECTIONS + READ_WRITE + User created an empty collection + + + 403 + false + ^actionErrors.* + + + API_COLLECTIONS + + + + 422 + false + ^actionErrors.* + + + 403 + false + ^actionErrors.* + + + diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js index f12b519b1e..783b539d5f 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js @@ -805,4 +805,14 @@ export default { }) }, + async editCollectionName(apiCollectionId, collectionName) { + return await request({ + url: '/api/editCollectionName', + method: 'post', + data: { + apiCollectionId, collectionName + } + }) + }, + } \ No newline at end of file diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiEndpoints.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiEndpoints.jsx index 0668b426cc..ff8f9d1490 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiEndpoints.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiEndpoints.jsx @@ -1,5 +1,5 @@ import PageWithMultipleCards from "../../../components/layouts/PageWithMultipleCards" -import { Text, HorizontalStack, Button, Popover, Modal, IndexFiltersMode, VerticalStack, Box, Checkbox } from "@shopify/polaris" +import { Text, HorizontalStack, Button, Popover, Modal, IndexFiltersMode, VerticalStack, Box, Checkbox, TextField } from "@shopify/polaris" import api from "../api" import { useEffect, useState } from "react" import func from "@/util/func" @@ -155,8 +155,7 @@ function ApiEndpoints(props) { const collectionsMap = PersistStore(state => state.collectionsMap) const allCollections = PersistStore(state => state.allCollections); - const pageTitle = collectionsMap[apiCollectionId] - + const [ pageTitle, setPageTitle] = useState(collectionsMap[apiCollectionId]) const [apiEndpoints, setApiEndpoints] = useState([]) const [apiInfoList, setApiInfoList] = useState([]) const [unusedEndpoints, setUnusedEndpoints] = useState([]) @@ -185,6 +184,9 @@ function ApiEndpoints(props) { const queryParams = new URLSearchParams(location.search); const selectedUrl = queryParams.get('selected_url') const selectedMethod = queryParams.get('selected_method') + const [isEditing, setIsEditing] = useState(false); + const [editableTitle, setEditableTitle] = useState(pageTitle); + // the values used here are defined at the server. const definedTableTabs = apiCollectionId === 111111999 ? ['All', 'New', 'High risk', 'No auth', 'Shadow'] : ( apiCollectionId === 111111120 ? ['All', 'New', 'Sensitive', 'High risk', 'Shadow'] : ['All', 'New', 'Sensitive', 'High risk', 'No auth', 'Shadow'] ) @@ -905,20 +907,68 @@ function ApiEndpoints(props) { ) ] + + const handleSaveClick = async () => { + api.editCollectionName(apiCollectionId, editableTitle).then((resp) => { + func.setToast(true, false, 'Collection name updated successfully!') + setPageTitle(editableTitle) + collectionsMap[apiCollectionId] = editableTitle + setIsEditing(false); + }).catch((err) => { + setEditableTitle(pageTitle) + setIsEditing(false); + }) + + }; + + const handleTitleChange = (value) => { + setEditableTitle(value); + }; + + const handleKeyDown = (event) => { + if (event.key === 'Enter') { + handleSaveClick(); + } else if (event.key === 'Escape') { + setIsEditing(false); + } + } + return (
- {isQueryPage ? apiEndpointTable : + {isQueryPage ? ( + apiEndpointTable + ) : ( - - + isEditing ? ( + +
handleKeyDown(e)}> + {editableTitle.length}/24 + )} + onKeyDown={handleKeyDown} + /> +
+
+ ) : ( +
{setIsEditing(true);} : undefined}> + + + +
+ ) } backUrl="/dashboard/observe/inventory" secondaryActions={secondaryActionsComponent} components={components} /> - } + )}
) From fc400cf3af52cea05aea92c2fc07976027601beb Mon Sep 17 00:00:00 2001 From: Avneesh Hota Date: Fri, 13 Dec 2024 12:44:00 +0530 Subject: [PATCH 2/2] fixed struts message and save persist store --- .../java/com/akto/action/ApiCollectionsAction.java | 2 +- apps/dashboard/src/main/resources/struts.xml | 2 +- .../pages/observe/api_collections/ApiEndpoints.jsx | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java b/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java index e37d709aa6..c9c684590d 100644 --- a/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java @@ -741,7 +741,7 @@ public String editCollectionName() { } if (apiCollection.getHostName() != null) { - String errorMessage = "Traffic API collection can't edited"; + String errorMessage = "Unable to modify the Traffic API collection"; addActionError(errorMessage); return Action.ERROR.toUpperCase(); } diff --git a/apps/dashboard/src/main/resources/struts.xml b/apps/dashboard/src/main/resources/struts.xml index 84ec850371..0d4d4c1876 100644 --- a/apps/dashboard/src/main/resources/struts.xml +++ b/apps/dashboard/src/main/resources/struts.xml @@ -7389,7 +7389,7 @@ API_COLLECTIONS READ_WRITE - User created an empty collection + User edited collection name 403 diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiEndpoints.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiEndpoints.jsx index 6b28535cae..25c60ad8c2 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiEndpoints.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiEndpoints.jsx @@ -154,6 +154,8 @@ function ApiEndpoints(props) { const setShowDetails = ObserveStore(state => state.setInventoryFlyout) const collectionsMap = PersistStore(state => state.collectionsMap) const allCollections = PersistStore(state => state.allCollections); + const setCollectionsMap = PersistStore(state => state.setCollectionsMap) + const setAllCollections = PersistStore(state => state.setAllCollections) const [ pageTitle, setPageTitle] = useState(collectionsMap[apiCollectionId]) const [apiEndpoints, setApiEndpoints] = useState([]) @@ -907,12 +909,24 @@ function ApiEndpoints(props) { ) ] + function updateCollectionName(list, apiCollectionId, newName) { + list.forEach(item => { + if (item.id === apiCollectionId) { + item.displayName = newName; + item.name = newName; + } + }); + } + const handleSaveClick = async () => { api.editCollectionName(apiCollectionId, editableTitle).then((resp) => { func.setToast(true, false, 'Collection name updated successfully!') setPageTitle(editableTitle) collectionsMap[apiCollectionId] = editableTitle + setCollectionsMap(collectionsMap) + updateCollectionName(allCollections, parseInt(apiCollectionId, 10), editableTitle) + setAllCollections(allCollections) setIsEditing(false); }).catch((err) => { setEditableTitle(pageTitle)