diff --git a/models/session_response.go b/models/session_response.go index d68eb9911..e7f37b22d 100644 --- a/models/session_response.go +++ b/models/session_response.go @@ -59,6 +59,9 @@ type SessionResponse struct { // permissions Permissions map[string][]string `json:"permissions,omitempty"` + // server end point + ServerEndPoint string `json:"serverEndPoint,omitempty"` + // status // Enum: [ok] Status string `json:"status,omitempty"` diff --git a/portal-ui/src/screens/Console/Configurations/SiteReplication/AddReplicationSites.tsx b/portal-ui/src/screens/Console/Configurations/SiteReplication/AddReplicationSites.tsx index 1b2d702e7..3a03a2f60 100644 --- a/portal-ui/src/screens/Console/Configurations/SiteReplication/AddReplicationSites.tsx +++ b/portal-ui/src/screens/Console/Configurations/SiteReplication/AddReplicationSites.tsx @@ -20,8 +20,7 @@ import { Box, LinearProgress } from "@mui/material"; import { useNavigate } from "react-router-dom"; import { Button } from "mds"; import useApi from "../../Common/Hooks/useApi"; -import { AddIcon, ClustersIcon, RemoveIcon } from "../../../../icons"; -import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; +import { ClustersIcon } from "../../../../icons"; import PageHeader from "../../Common/PageHeader/PageHeader"; import BackLink from "../../../../common/BackLink"; import { IAM_PAGES } from "../../../../common/SecureComponent/permissions"; @@ -33,12 +32,10 @@ import { setSnackBarMessage, } from "../../../../systemSlice"; import { useAppDispatch } from "../../../../store"; -import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper"; - -type SiteInputRow = { - name: string; - endpoint: string; -}; +import { useSelector } from "react-redux"; +import { selSession } from "../../consoleSlice"; +import SRSiteInputRow from "./SRSiteInputRow"; +import { SiteInputRow } from "./Types"; const isValidEndPoint = (ep: string) => { let isValidEndPointUrl = false; @@ -49,30 +46,74 @@ const isValidEndPoint = (ep: string) => { } catch (err) { isValidEndPointUrl = false; } - if (isValidEndPointUrl || ep === "") { + if (isValidEndPointUrl) { return ""; } else { return "Invalid Endpoint"; } }; + +const isEmptyValue = (value: string): boolean => { + return value?.trim() === ""; +}; + +const TableHeader = () => { + return ( + + + Site Name + + Endpoint {"*"} + Access Key {"*"} + Secret Key {"*"} + + + ); +}; + +const SiteTypeHeader = ({ title }: { title: string }) => { + return ( + + + {title} + + + ); +}; + const AddReplicationSites = () => { const dispatch = useAppDispatch(); const navigate = useNavigate(); + const { serverEndPoint = "" } = useSelector(selSession); + + const [currentSite, setCurrentSite] = useState([ + { + endpoint: serverEndPoint, + name: "", + accessKey: "", + secretKey: "", + }, + ]); + const [existingSites, setExistingSites] = useState([]); - const [accessKey, setAccessKey] = useState(""); - const [secretKey, setSecretKey] = useState(""); - const [siteConfig, setSiteConfig] = useState([]); - const setDefaultNewRows = () => { - const defaultNewSites = existingSites?.length - ? [{ endpoint: "", name: "" }] - : [ - { endpoint: "", name: "" }, - { endpoint: "", name: "" }, - ]; - setSiteConfig(defaultNewSites); + const defaultNewSites = [ + { endpoint: "", name: "", accessKey: "", secretKey: "" }, + ]; + setExistingSites(defaultNewSites); }; const [isSiteInfoLoading, invokeSiteInfoApi] = useApi( @@ -85,17 +126,34 @@ const AddReplicationSites = () => { curSite = { ...curSite, isCurrent: true, + isSaved: true, }; - siteList.splice(foundIdx, 1, curSite); + + setCurrentSite([curSite]); + siteList.splice(foundIdx, 1); } siteList.sort((x: any, y: any) => { return x.name === curSiteName ? -1 : y.name === curSiteName ? 1 : 0; }); - setExistingSites(siteList); + + let existingSiteList = siteList.map((si: any) => { + return { + ...si, + accessKey: "", + secretKey: "", + isSaved: true, + }; + }); + + if (existingSiteList.length) { + setExistingSites(existingSiteList); + } else { + setDefaultNewRows(); + } }, (err: any) => { - setExistingSites([]); + setDefaultNewRows(); } ); @@ -108,21 +166,36 @@ const AddReplicationSites = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useEffect(() => { - setDefaultNewRows(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [existingSites]); - - const isAllEndpointsValid = - siteConfig.reduce((acc: string[], cv, i) => { - const epValue = siteConfig[i].endpoint; + const existingEndPointsValidity = existingSites.reduce( + (acc: string[], cv, i) => { + const epValue = existingSites[i].endpoint; const isEpValid = isValidEndPoint(epValue); if (isEpValid === "" && epValue !== "") { acc.push(isEpValid); } return acc; - }, []).length === siteConfig.length; + }, + [] + ); + + const isExistingCredsValidity = existingSites + .map((site) => { + return !isEmptyValue(site.accessKey) && !isEmptyValue(site.secretKey); + }) + .filter(Boolean); + + const { accessKey: cAccessKey, secretKey: cSecretKey } = currentSite[0]; + + const isCurCredsValid = + !isEmptyValue(cAccessKey) && !isEmptyValue(cSecretKey); + const peerEndpointsValid = + existingEndPointsValidity.length === existingSites.length; + const peerCredsValid = + isExistingCredsValidity.length === existingSites.length; + + let isAllFieldsValid = + isCurCredsValid && peerEndpointsValid && peerCredsValid; const [isAdding, invokeSiteAddApi] = useApi( (res: any) => { @@ -146,37 +219,181 @@ const AddReplicationSites = () => { ); const resetForm = () => { - setAccessKey(""); - setSecretKey(""); setDefaultNewRows(); + setCurrentSite((prevItems) => { + return prevItems.map((item, ix) => ({ + ...item, + accessKey: "", + secretKey: "", + name: "", + })); + }); }; const addSiteReplication = () => { - const existingSitesToAdd = existingSites?.map((es, idx) => { + const curSite: any[] = currentSite?.map((es, idx) => { return { - accessKey: accessKey, - secretKey: secretKey, + accessKey: es.accessKey, + secretKey: es.secretKey, name: es.name, endpoint: es.endpoint, }; }); - const newSitesToAdd = siteConfig.reduce((acc: any, ns, idx) => { - if (ns.endpoint) { - acc.push({ - accessKey: accessKey, - secretKey: secretKey, - name: ns.name || `dr-site-${idx}`, - endpoint: ns.endpoint, - }); - } - return acc; - }, []); + const newOrExistingSitesToAdd = existingSites.reduce( + (acc: any, ns, idx) => { + if (ns.endpoint) { + acc.push({ + accessKey: ns.accessKey, + secretKey: ns.secretKey, + name: ns.name || `dr-site-${idx}`, + endpoint: ns.endpoint, + }); + } + return acc; + }, + [] + ); - invokeSiteAddApi("POST", `api/v1/admin/site-replication`, [ - ...(existingSitesToAdd || []), - ...(newSitesToAdd || []), - ]); + const sitesToAdd = curSite.concat(newOrExistingSitesToAdd); + + invokeSiteAddApi("POST", `api/v1/admin/site-replication`, sitesToAdd); + }; + + const renderCurrentSite = () => { + return ( + + + + + + {currentSite.map((cs, index) => { + const accessKeyError = isEmptyValue(cs.accessKey) + ? "AccessKey is required" + : ""; + const secretKeyError = isEmptyValue(cs.secretKey) + ? "SecretKey is required" + : ""; + return ( + { + const filedValue = e.target.value; + if (fieldName !== "") { + setCurrentSite((prevItems) => { + return prevItems.map((item, ix) => + ix === index + ? { ...item, [fieldName]: filedValue } + : item + ); + }); + } + }} + showRowActions={false} + /> + ); + })} + + + ); + }; + + const renderPeerSites = () => { + return ( + + + + + + {existingSites.map((ps, index) => { + const endPointError = isValidEndPoint(ps.endpoint); + + const accessKeyError = isEmptyValue(ps.accessKey) + ? "AccessKey is required" + : ""; + const secretKeyError = isEmptyValue(ps.secretKey) + ? "SecretKey is required" + : ""; + + return ( + { + const filedValue = e.target.value; + setExistingSites((prevItems) => { + return prevItems.map((item, ix) => + ix === index ? { ...item, [fieldName]: filedValue } : item + ); + }); + }} + canAdd={true} + canRemove={index > 0 && !ps.isSaved} + onAddClick={() => { + const newRows = [...existingSites]; + //add at the next index + newRows.splice(index + 1, 0, { + name: "", + endpoint: "", + accessKey: "", + secretKey: "", + }); + + setExistingSites(newRows); + }} + onRemoveClick={(index) => { + setExistingSites( + existingSites.filter((_, idx) => idx !== index) + ); + }} + /> + ); + })} + + + ); }; return ( @@ -195,10 +412,7 @@ const AddReplicationSites = () => { display: "grid", padding: "25px", gap: "25px", - gridTemplateColumns: { - md: "2fr 1.2fr", - xs: "1fr", - }, + gridTemplateColumns: "1fr", border: "1px solid #eaeaea", }} > @@ -208,6 +422,18 @@ const AddReplicationSites = () => { {isSiteInfoLoading || isAdding ? : null} + + + Note: AccessKey and SecretKey values for every site is required + while adding or editing peer sites +
{ return addSiteReplication(); }} > - - - Note:{" "} - - Access Key and Secret Key should be same on all sites. - - - - - ) => { - setAccessKey(event.target.value); - }} - label="Access Key" - required={true} - value={accessKey} - error={accessKey === "" ? "Access Key is required." : ""} - data-test-id={"add-site-rep-acc-key"} - /> - - - ) => { - setSecretKey(event.target.value); - }} - error={secretKey === "" ? "Secret Key is required." : ""} - label="Secret Key" - value={secretKey} - data-test-id={"add-site-rep-sec-key"} - /> - + {renderCurrentSite()} - - - Peer Sites - - - - - - Site Name - - - Endpoint {"*"} - - - {existingSites?.map((si, index) => { - return ( - - - {}} - /> - - - {}} - /> - - - {" "} - - - ); - })} - - {siteConfig.map((sci, index) => { - let isDelDisabled = false; - - if (existingSites?.length && index === 0) { - isDelDisabled = true; - } else if (!existingSites?.length && index < 2) { - isDelDisabled = true; - } - - return ( - - - { - const nameTxt = e.target.value; - setSiteConfig((prevItems) => { - return prevItems.map((item, ix) => - ix === index ? { ...item, name: nameTxt } : item - ); - }); - }} - data-test-id={`add-site-rep-peer-site-${index}`} - /> - - - { - const epTxt = e.target.value; - setSiteConfig((prevItems) => { - return prevItems.map((item, ix) => - ix === index - ? { ...item, endpoint: epTxt } - : item - ); - }); - }} - data-test-id={`add-site-rep-peer-ep-${index}`} - /> - - - - -