Skip to content

Commit

Permalink
feat: incremental search and namespace preview in search bar (#654)
Browse files Browse the repository at this point in the history
Signed-off-by: veds-g <guptavedant2312@gmail.com>
  • Loading branch information
veds-g committed Apr 3, 2023
1 parent 51a278e commit 0db3248
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 15 deletions.
1 change: 1 addition & 0 deletions server/apis/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ type Handler interface {
GetPipelineEdge(c *gin.Context)
GetPipelineWatermarks(c *gin.Context)
GetPipelineStatus(c *gin.Context)
ListNamespaces(c *gin.Context)
}
18 changes: 18 additions & 0 deletions server/apis/v1/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,24 @@ func (h *handler) GetPipelineStatus(c *gin.Context) {
c.JSON(http.StatusOK, l)
}

// ListNamespaces is used to provide all the namespaces that have numaflow pipelines running
func (h *handler) ListNamespaces(c *gin.Context) {
l, err := h.numaflowClient.Pipelines("").List(context.Background(), metav1.ListOptions{})
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
return
}
m := make(map[string]bool)
for _, pl := range l.Items {
m[pl.Namespace] = true
}
var namespaces []string
for k := range m {
namespaces = append(namespaces, k)
}
c.JSON(http.StatusOK, namespaces)
}

func daemonSvcAddress(ns, pipeline string) string {
return fmt.Sprintf("%s.%s.svc.cluster.local:%d", fmt.Sprintf("%s-daemon-svc", pipeline), ns, dfv1.DaemonServicePort)
}
1 change: 1 addition & 0 deletions server/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,5 @@ func v1Routes(r gin.IRouter) {
r.GET("/namespaces/:namespace/pipelines/:pipeline/vertices/:vertex/metrics", handler.GetVertexMetrics)
r.GET("/namespaces/:namespace/pipelines/:pipeline/watermarks", handler.GetPipelineWatermarks)
r.GET("/namespaces/:namespace/pipelines/:pipeline/status", handler.GetPipelineStatus)
r.GET("/namespaces", handler.ListNamespaces)
}
51 changes: 36 additions & 15 deletions ui/src/components/namespaces/Namespaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,43 @@ import ClearIcon from '@mui/icons-material/Clear'
import SearchIcon from '@mui/icons-material/Search';
import { useSystemInfoFetch } from "../../utils/fetchWrappers/systemInfoFetch";
import {notifyError} from "../../utils/error";
import { useNamespaceListFetch } from "../../utils/fetchWrappers/namespaceListFetch";

export function Namespaces() {
const [nsArr, setnsArr] = useState([]);
const [nsSearch, setNsSearch] = useState([]);
const [nsCookie, setNsCookie] = useState([]);
const [value, setValue] = useState("");
const [disableSearch, setDisableSearch] = useState(false);
const [namespace, setNamespace] = useState("");
const { systemInfo, error: systemInfoError } = useSystemInfoFetch();
const { namespaceList, error: namespaceListError } = useNamespaceListFetch();

useEffect(() => {
if (systemInfoError) {
notifyError([{
error: "Failed to fetch the namespace scope installation info",
error: "Failed to fetch the system info",
options: {toastId: "ns-scope", autoClose: false}
}]);
}
}, [systemInfoError])

useEffect(() => {
if (namespaceListError && systemInfo && systemInfo?.namespaced === false) {
notifyError([{
error: "Failed to fetch the available namespaces",
options: {toastId: "ns-search", autoClose: false}
}]);
}
}, [namespaceListError, systemInfo])

useEffect(() => {
if (systemInfo && systemInfo?.namespaced) {
setValue(systemInfo?.managedNamespace);
setNamespace(systemInfo?.managedNamespace);
setDisableSearch(true);
} else if (systemInfo && !systemInfo?.namespaced){
setDisableSearch(false);

// set namespace value in search box
let curr_ns = localStorage.getItem("curr_namespace");
if (!curr_ns) curr_ns = "";
Expand All @@ -52,7 +65,7 @@ export function Namespaces() {
if (!ns_list) ns_list = "";
const ns_arr = ns_list.split(",");
ns_arr.pop();
setnsArr(ns_arr);
setNsCookie(ns_arr);
}
}, [systemInfo])

Expand All @@ -62,27 +75,36 @@ export function Namespaces() {
setNamespace(namespaceVal);
if (namespaceVal !== "") {
let flag = 0;
for (let i = 0; i < nsArr.length; i++) {
if (namespaceVal === nsArr[i]) {
for (let i = 0; i < nsCookie.length; i++) {
if (namespaceVal === nsCookie[i]) {
flag = 1;
break;
}
}
if (flag === 1) {return;}
const arr = nsArr;
const arr = nsCookie;
arr.unshift(namespaceVal);
if (arr.length > 5) arr.pop();
setnsArr(arr);
setNsCookie(arr);
let ns_list = "";
for (let i = 0; i < nsArr.length; i++) ns_list += nsArr[i] + ",";
for (let i = 0; i < nsCookie.length; i++) ns_list += nsCookie[i] + ",";
localStorage.setItem("namespaces", ns_list);
}
};

const ns_List = [];
nsArr.forEach((namespace) => (
ns_List.push({label: namespace})
))
useEffect(() => {
if (namespaceList) {
setNsSearch(namespaceList);
}
}, [namespaceList]);

const nsList = [];
nsCookie.forEach((namespace) => {
nsList.push({label: namespace, type: "Previously Searched"})
})
nsSearch.forEach((namespace) => {
nsList.push({label: namespace, type: "Available Namespaces"})
})

const handleKeyPress = e => {
if (e.key === 'Enter') {
Expand All @@ -107,9 +129,9 @@ export function Namespaces() {
blurOnSelect
disableClearable
id="curr_ns"
options={ns_List}
options={nsList}
groupBy={(option) => option.type}
sx={{ width: 250, margin: "0 10px" }}
filterOptions={(x) => x}
value={value}
onChange={(e, v) => {
setValue(v.label);
Expand All @@ -120,7 +142,6 @@ export function Namespaces() {
params.inputProps.onKeyPress = handleKeyPress;
return <TextField
{...params}
autoComplete="off"
label="Namespace"
placeholder="enter a namespace"
InputLabelProps={{ shrink: true }}
Expand Down
33 changes: 33 additions & 0 deletions ui/src/utils/fetchWrappers/namespaceListFetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useEffect, useState } from "react";
import { useFetch } from "./fetch";

export const useNamespaceListFetch = () => {
const [namespaceList, setNamespaceList] = useState<string[] | undefined>(undefined);
const [loading, setLoading] = useState<boolean>(true);

const {
data,
loading: fetchLoading,
error,
} = useFetch(
`/api/v1/namespaces`
);

useEffect(() => {
if (fetchLoading) {
setLoading(true);
return;
}
if (error) {
setLoading(false);
return;
}
if (data) {
setNamespaceList(data);
setLoading(false);
return;
}
}, [data, fetchLoading]);

return { namespaceList, error, loading };
};

0 comments on commit 0db3248

Please sign in to comment.