This is an automated email from the ASF dual-hosted git repository.

kishoreg pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 02dd3e2  Adding Tenants, Instances, Tables, Segments count tiles and 
their respective pages (#6117)
02dd3e2 is described below

commit 02dd3e2f9dcb9df79f911fbe010a2d5e9db4721a
Author: Sanket Shah <shahsan...@users.noreply.github.com>
AuthorDate: Thu Oct 8 00:22:44 2020 +0530

    Adding Tenants, Instances, Tables, Segments count tiles and their 
respective pages (#6117)
    
    * Adding Tenants, Instances, Tables, Segments count tiles and their 
respective pages
    
    * removed segment count from homepage
---
 .../main/resources/app/components/Breadcrumbs.tsx  |   5 +
 .../src/main/resources/app/components/Header.tsx   |   4 +-
 .../app/components/Homepage/InstanceTable.tsx      |   2 +-
 .../app/components/Homepage/InstancesTables.tsx    |  34 +-----
 .../{TenantsTable.tsx => TenantsListing.tsx}       |  26 +----
 .../src/main/resources/app/components/Table.tsx    |  34 +++---
 .../src/main/resources/app/interfaces/types.d.ts   |  13 ++-
 .../src/main/resources/app/pages/HomePage.tsx      | 126 +++++++++++++++++++--
 .../InstanceListingPage.tsx}                       |  54 +++++----
 .../src/main/resources/app/pages/Query.tsx         |   2 +-
 .../pages/{Tenants.tsx => TablesListingPage.tsx}   |  65 +++++++----
 .../src/main/resources/app/pages/Tenants.tsx       |  40 ++++++-
 .../TenantsListingPage.tsx}                        |  50 ++++----
 .../src/main/resources/app/requests/index.ts       |  15 ++-
 pinot-controller/src/main/resources/app/router.tsx |   8 ++
 .../main/resources/app/utils/PinotMethodUtils.ts   |  74 ++++++++----
 16 files changed, 381 insertions(+), 171 deletions(-)

diff --git a/pinot-controller/src/main/resources/app/components/Breadcrumbs.tsx 
b/pinot-controller/src/main/resources/app/components/Breadcrumbs.tsx
index 3249af6..b77104f 100644
--- a/pinot-controller/src/main/resources/app/components/Breadcrumbs.tsx
+++ b/pinot-controller/src/main/resources/app/components/Breadcrumbs.tsx
@@ -47,6 +47,11 @@ const LinkRouter = (props: LinkRouterProps) => (
 
 const breadcrumbNameMap: { [key: string]: string } = {
   '/': 'Home',
+  '/tenants': 'Tenants',
+  '/controllers': 'Controllers',
+  '/brokers': 'Brokers',
+  '/servers': 'Servers',
+  '/tables': 'Tables',
   '/query': 'Query Console',
   '/cluster': 'Cluster Manager',
   '/zookeeper': 'Zookeeper Browser'
diff --git a/pinot-controller/src/main/resources/app/components/Header.tsx 
b/pinot-controller/src/main/resources/app/components/Header.tsx
index 08817b0..89532a8 100644
--- a/pinot-controller/src/main/resources/app/components/Header.tsx
+++ b/pinot-controller/src/main/resources/app/components/Header.tsx
@@ -34,10 +34,10 @@ const Header = ({ highlightSidebarLink, 
showHideSideBarHandler, openSidebar, ...
   <AppBar position="static">
     <Box display="flex">
       <Box textAlign="center" marginY="12.5px" width={openSidebar ? 250 : 90} 
borderRight="1px solid rgba(255,255,255,0.5)">
-        <Link to="/"><Logo onClick={() => highlightSidebarLink(1)} 
fulllogo={openSidebar.toString()} /></Link>
+        <Link to="/" style={{color: '#ffffff'}}><Logo onClick={() => 
highlightSidebarLink(1)} fulllogo={openSidebar.toString()} /></Link>
       </Box>
       <Box display="flex" alignItems="center">
-        <Box marginY="auto" padding="0.25rem 0 0.25rem 1.5rem" display="flex">
+        <Box marginY="auto" padding="0.25rem 0 0.25rem 1.5rem" display="flex" 
style={{cursor: 'pointer'}}>
           <MenuIcon onClick={() => showHideSideBarHandler()} />
         </Box>
         <BreadcrumbsComponent {...props}/>
diff --git 
a/pinot-controller/src/main/resources/app/components/Homepage/InstanceTable.tsx 
b/pinot-controller/src/main/resources/app/components/Homepage/InstanceTable.tsx
index 3e2da1c..60b9018 100644
--- 
a/pinot-controller/src/main/resources/app/components/Homepage/InstanceTable.tsx
+++ 
b/pinot-controller/src/main/resources/app/components/Homepage/InstanceTable.tsx
@@ -25,7 +25,7 @@ import PinotMethodUtils from '../../utils/PinotMethodUtils';
 
 type Props = {
   name: string,
-  instances: string[],
+  instances: Array<String>,
   clusterName: string
 };
 
diff --git 
a/pinot-controller/src/main/resources/app/components/Homepage/InstancesTables.tsx
 
b/pinot-controller/src/main/resources/app/components/Homepage/InstancesTables.tsx
index bca24ca..6f09121 100644
--- 
a/pinot-controller/src/main/resources/app/components/Homepage/InstancesTables.tsx
+++ 
b/pinot-controller/src/main/resources/app/components/Homepage/InstancesTables.tsx
@@ -17,42 +17,16 @@
  * under the License.
  */
 
-import React, { useEffect, useState } from 'react';
+import React, {  } from 'react';
 import map from 'lodash/map';
-import AppLoader from '../AppLoader';
 import InstanceTable from './InstanceTable';
-import PinotMethodUtils from '../../utils/PinotMethodUtils';
 
-type DataTable = {
-  [name: string]: string[]
-};
-
-const Instances = () => {
-  const [fetching, setFetching] = useState(true);
-  const [instances, setInstances] = useState<DataTable>();
-  const [clusterName, setClusterName] = useState('');
-
-  const fetchData = async () => {
-    const result = await PinotMethodUtils.getAllInstances();
-    let clusterNameRes = localStorage.getItem('pinot_ui:clusterName');
-    if(!clusterNameRes){
-      clusterNameRes = await PinotMethodUtils.getClusterName();
-    }
-    setInstances(result);
-    setClusterName(clusterNameRes);
-    setFetching(false);
-  };
-  useEffect(() => {
-    fetchData();
-  }, []);
-
-  return fetching ? (
-    <AppLoader />
-  ) : (
+const Instances = ({instances, clusterName}) => {
+  return (
     <>
       {
         map(instances, (value, key) => {
-          return <InstanceTable key={key} name={key} instances={value} 
clusterName={clusterName} />;
+          return <InstanceTable key={key} name={`${key}s`} instances={value} 
clusterName={clusterName} />;
         })
       }
     </>
diff --git 
a/pinot-controller/src/main/resources/app/components/Homepage/TenantsTable.tsx 
b/pinot-controller/src/main/resources/app/components/Homepage/TenantsListing.tsx
similarity index 62%
copy from 
pinot-controller/src/main/resources/app/components/Homepage/TenantsTable.tsx
copy to 
pinot-controller/src/main/resources/app/components/Homepage/TenantsListing.tsx
index 0696961..d711109 100644
--- 
a/pinot-controller/src/main/resources/app/components/Homepage/TenantsTable.tsx
+++ 
b/pinot-controller/src/main/resources/app/components/Homepage/TenantsListing.tsx
@@ -17,31 +17,15 @@
  * under the License.
  */
 
-import React, { useEffect, useState } from 'react';
-import { TableData } from 'Models';
-import AppLoader from '../AppLoader';
+import React from 'react';
 import CustomizedTables from '../Table';
-import PinotMethodUtils from '../../utils/PinotMethodUtils';
 
-const TenantsTable = () => {
-  const [fetching, setFetching] = useState(true);
-  const [tableData, setTableData] = useState<TableData>({ records: [], 
columns: [] });
-
-  const fetchData = async () => {
-    const result = await PinotMethodUtils.getTenantsData();
-    setTableData(result);
-    setFetching(false);
-  };
-  useEffect(() => {
-    fetchData();
-  }, []);
-
-  return fetching ? (
-    <AppLoader />
-  ) : (
+const TenantsTable = ({tenantsData}) => {
+  
+  return (
     <CustomizedTables
       title="Tenants"
-      data={tableData}
+      data={tenantsData}
       addLinks
       isPagination
       baseURL="/tenants/"
diff --git a/pinot-controller/src/main/resources/app/components/Table.tsx 
b/pinot-controller/src/main/resources/app/components/Table.tsx
index 98b3e6c..7dac1f3 100644
--- a/pinot-controller/src/main/resources/app/components/Table.tsx
+++ b/pinot-controller/src/main/resources/app/components/Table.tsx
@@ -49,19 +49,20 @@ import TableToolbar from './TableToolbar';
 import SimpleAccordion from './SimpleAccordion';
 
 type Props = {
-  title?: string;
-  data: TableData;
-  noOfRows?: number;
-  addLinks?: boolean;
-  isPagination?: boolean;
+  title?: string,
+  data: TableData,
+  noOfRows?: number,
+  addLinks?: boolean,
+  isPagination?: boolean,
   cellClickCallback?: Function,
   isCellClickable?: boolean,
   highlightBackground?: boolean,
-  isSticky?: boolean
+  isSticky?: boolean,
   baseURL?: string,
   recordsCount?: number,
   showSearchBox: boolean,
-  inAccordionFormat?: boolean
+  inAccordionFormat?: boolean,
+  regexReplace?: boolean
 };
 
 const StyledTableRow = withStyles((theme) =>
@@ -140,7 +141,7 @@ const useStyles = makeStyles((theme) => ({
     textAlign: 'center',
   },
   link: {
-    color: 'inherit',
+    color: '#4285f4',
   },
   spacer: {
     flex: '0 1 auto',
@@ -249,7 +250,8 @@ export default function CustomizedTables({
   baseURL,
   recordsCount,
   showSearchBox,
-  inAccordionFormat
+  inAccordionFormat,
+  regexReplace
 }: Props) {
   const [finalData, setFinalData] = React.useState(Utils.tableFormat(data));
 
@@ -382,12 +384,18 @@ export default function CustomizedTables({
                   .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
                   .map((row, index) => (
                     <StyledTableRow key={index} hover>
-                      {Object.values(row).map((cell, idx) =>
-                        addLinks && !idx ? (
+                      {Object.values(row).map((cell, idx) =>{
+                        let url = baseURL;
+                        if(regexReplace){
+                          let regex = /\:.*?:/;
+                          let matches = baseURL.match(regex);
+                          url = baseURL.replace(matches[0], 
row[matches[0].replace(/:/g, '')]);
+                        }
+                        return addLinks && !idx ? (
                           <StyledTableCell key={idx}>
                             <NavLink
                               className={classes.link}
-                              to={`${baseURL}${cell}`}
+                              to={`${url}${cell}`}
                             >
                               {cell}
                             </NavLink>
@@ -401,7 +409,7 @@ export default function CustomizedTables({
                             {styleCell(cell.toString())}
                           </StyledTableCell>
                         )
-                      )}
+                      })}
                     </StyledTableRow>
                   ))
               )}
diff --git a/pinot-controller/src/main/resources/app/interfaces/types.d.ts 
b/pinot-controller/src/main/resources/app/interfaces/types.d.ts
index 4199174..7cc6211 100644
--- a/pinot-controller/src/main/resources/app/interfaces/types.d.ts
+++ b/pinot-controller/src/main/resources/app/interfaces/types.d.ts
@@ -20,7 +20,7 @@
 declare module 'Models' {
   export type TableData = {
     records: Array<Array<string | number | boolean>>;
-    columns: string[];
+    columns: Array<string>;
   };
 
   export type Tenants = {
@@ -117,4 +117,15 @@ declare module 'Models' {
 
   export type ZKConfig = Object;
   export type ZKOperationResponsne = any;
+
+  export type DataTable = {
+    [name: string]: Array<string>
+  };
+
+  export type BrokerList = Array<string>;
+
+  export type ServerList = {
+    ServerInstances: Array<string>,
+    tenantName: string
+  }
 }
diff --git a/pinot-controller/src/main/resources/app/pages/HomePage.tsx 
b/pinot-controller/src/main/resources/app/pages/HomePage.tsx
index bb7a8c3..5c2c69a 100644
--- a/pinot-controller/src/main/resources/app/pages/HomePage.tsx
+++ b/pinot-controller/src/main/resources/app/pages/HomePage.tsx
@@ -17,17 +17,129 @@
  * under the License.
  */
 
-import * as React from 'react';
-import { Grid } from '@material-ui/core';
-import TenantsTable from '../components/Homepage/TenantsTable';
+import React, {useState, useEffect} from 'react';
+import { Grid, makeStyles, Paper } from '@material-ui/core';
+import { TableData, DataTable } from 'Models';
+import AppLoader from '../components/AppLoader';
+import PinotMethodUtils from '../utils/PinotMethodUtils';
+import TenantsListing from '../components/Homepage/TenantsListing';
 import Instances from '../components/Homepage/InstancesTables';
 import ClusterConfig from '../components/Homepage/ClusterConfig';
+import { Link } from 'react-router-dom';
+
+const useStyles = makeStyles((theme) => ({
+  paper:{
+    padding: '10px 0',
+    color: '#4285f4',
+    borderRadius: 4,
+    marginBottom: 15,
+    textAlign: 'center',
+    backgroundColor: 'rgba(66, 133, 244, 0.1)',
+    borderColor: 'rgba(66, 133, 244, 0.5)',
+    borderStyle: 'solid',
+    borderWidth: '1px',
+    '& h2, h4': {
+      margin: 0,
+    },
+    '& h4':{
+      textTransform: 'uppercase',
+      letterSpacing: 1,
+      fontWeight: 600
+    },
+    '&:hover': {
+      borderColor: '#4285f4'
+    }
+  },
+  gridContainer: {
+    padding: 20,
+    backgroundColor: 'white',
+    maxHeight: 'calc(100vh - 70px)',
+    overflowY: 'auto'
+  },
+  paperLinks: {
+    textDecoration: 'none'
+  }
+}));
 
 const HomePage = () => {
-  return (
-    <Grid item xs style={{ padding: 20, backgroundColor: 'white', maxHeight: 
'calc(100vh - 70px)', overflowY: 'auto' }}>
-      <TenantsTable />
-      <Instances />
+  const classes = useStyles();
+
+  const [fetching, setFetching] = useState(true);
+  const [tenantsData, setTenantsData] = useState<TableData>({ records: [], 
columns: [] });
+  const [instances, setInstances] = useState<DataTable>();
+  const [clusterName, setClusterName] = useState('');
+  const [tables, setTables] = useState([]);
+
+  const fetchData = async () => {
+    const tenantsDataResponse = await PinotMethodUtils.getTenantsData();
+    const instanceResponse = await PinotMethodUtils.getAllInstances();
+    const tablesResponse = await 
PinotMethodUtils.getQueryTablesList({bothType: true});
+    const tablesList = [];
+    tablesResponse.records.map((record)=>{
+      tablesList.push(...record);
+    });
+    setTenantsData(tenantsDataResponse);
+    setInstances(instanceResponse);
+    setTables(tablesList);
+    let clusterNameRes = localStorage.getItem('pinot_ui:clusterName');
+    if(!clusterNameRes){
+      clusterNameRes = await PinotMethodUtils.getClusterName();
+    }
+    setClusterName(clusterNameRes);
+    setFetching(false);
+  };
+  useEffect(() => {
+    fetchData();
+  }, []);
+  
+  return fetching ? (
+    <AppLoader />
+  ) : (
+    <Grid item xs className={classes.gridContainer}>
+      <Grid container spacing={3}>
+        <Grid item xs={2}>
+          <Link to="/tenants" className={classes.paperLinks}>
+            <Paper className={classes.paper}>
+              <h4>Tenants</h4>
+              <h2>{tenantsData.records.length}</h2>
+            </Paper>
+          </Link>
+        </Grid>
+        <Grid item xs={2}>
+          <Link to="/controllers" className={classes.paperLinks}>
+            <Paper className={classes.paper}>
+              <h4>Controllers</h4>
+              <h2>{instances.Controller.length}</h2>
+            </Paper>
+          </Link>
+        </Grid>
+        <Grid item xs={2}>
+          <Link to="/brokers" className={classes.paperLinks}>
+            <Paper className={classes.paper}>
+              <h4>Brokers</h4>
+              <h2>{instances.Broker.length}</h2>
+            </Paper>
+          </Link>
+        </Grid>
+        <Grid item xs={2}>
+          <Link to="/servers" className={classes.paperLinks}>
+            <Paper className={classes.paper}>
+              <h4>Servers</h4>
+              <h2>{instances.Server.length}</h2>
+            </Paper>
+          </Link>
+        </Grid>
+        <Grid item xs={2}>
+          <Link to="/tables" className={classes.paperLinks}>
+            <Paper className={classes.paper}>
+              <h4>Tables</h4>
+              <h2>{tables.length}</h2>
+            </Paper>
+          </Link>
+        </Grid>
+      </Grid>
+      <TenantsListing tenantsData={tenantsData}/>
+      <Instances instances={instances} clusterName={clusterName}/>
       <ClusterConfig />
     </Grid>
   );
diff --git 
a/pinot-controller/src/main/resources/app/components/Homepage/InstancesTables.tsx
 b/pinot-controller/src/main/resources/app/pages/InstanceListingPage.tsx
similarity index 57%
copy from 
pinot-controller/src/main/resources/app/components/Homepage/InstancesTables.tsx
copy to pinot-controller/src/main/resources/app/pages/InstanceListingPage.tsx
index bca24ca..30ddf79 100644
--- 
a/pinot-controller/src/main/resources/app/components/Homepage/InstancesTables.tsx
+++ b/pinot-controller/src/main/resources/app/pages/InstanceListingPage.tsx
@@ -17,46 +17,54 @@
  * under the License.
  */
 
-import React, { useEffect, useState } from 'react';
-import map from 'lodash/map';
-import AppLoader from '../AppLoader';
-import InstanceTable from './InstanceTable';
-import PinotMethodUtils from '../../utils/PinotMethodUtils';
-
-type DataTable = {
-  [name: string]: string[]
-};
+import React, {useState, useEffect} from 'react';
+import { Grid, makeStyles } from '@material-ui/core';
+import _ from 'lodash';
+import { DataTable } from 'Models';
+import AppLoader from '../components/AppLoader';
+import PinotMethodUtils from '../utils/PinotMethodUtils';
+import Instances from '../components/Homepage/InstancesTables';
+
+const useStyles = makeStyles(() => ({
+  gridContainer: {
+    padding: 20,
+    backgroundColor: 'white',
+    maxHeight: 'calc(100vh - 70px)',
+    overflowY: 'auto'
+  },
+
+}));
+
+const InstanceListingPage = () => {
+  const classes = useStyles();
 
-const Instances = () => {
   const [fetching, setFetching] = useState(true);
   const [instances, setInstances] = useState<DataTable>();
   const [clusterName, setClusterName] = useState('');
 
   const fetchData = async () => {
-    const result = await PinotMethodUtils.getAllInstances();
+    const instanceResponse = await PinotMethodUtils.getAllInstances();
+    const instanceType = _.startCase(location.hash.split('/')[1].slice(0, -1));
+    setInstances(_.pick(instanceResponse, instanceType));
     let clusterNameRes = localStorage.getItem('pinot_ui:clusterName');
     if(!clusterNameRes){
       clusterNameRes = await PinotMethodUtils.getClusterName();
     }
-    setInstances(result);
     setClusterName(clusterNameRes);
     setFetching(false);
-  };
+  }
+
   useEffect(() => {
     fetchData();
   }, []);
 
   return fetching ? (
-    <AppLoader />
+    <AppLoader/>
   ) : (
-    <>
-      {
-        map(instances, (value, key) => {
-          return <InstanceTable key={key} name={key} instances={value} 
clusterName={clusterName} />;
-        })
-      }
-    </>
-  );
+    <Grid item xs className={classes.gridContainer}>
+      <Instances instances={instances} clusterName={clusterName}/>
+    </Grid>
+  )
 };
 
-export default Instances;
\ No newline at end of file
+export default InstanceListingPage;
\ No newline at end of file
diff --git a/pinot-controller/src/main/resources/app/pages/Query.tsx 
b/pinot-controller/src/main/resources/app/pages/Query.tsx
index 53fd15d..84a1b11 100644
--- a/pinot-controller/src/main/resources/app/pages/Query.tsx
+++ b/pinot-controller/src/main/resources/app/pages/Query.tsx
@@ -237,7 +237,7 @@ const QueryPage = () => {
   };
 
   const fetchData = async () => {
-    const result = await PinotMethodUtils.getQueryTablesList();
+    const result = await PinotMethodUtils.getQueryTablesList({bothType: 
false});
     setTableList(result);
     setFetching(false);
   };
diff --git a/pinot-controller/src/main/resources/app/pages/Tenants.tsx 
b/pinot-controller/src/main/resources/app/pages/TablesListingPage.tsx
similarity index 51%
copy from pinot-controller/src/main/resources/app/pages/Tenants.tsx
copy to pinot-controller/src/main/resources/app/pages/TablesListingPage.tsx
index f425f47..fe29f61 100644
--- a/pinot-controller/src/main/resources/app/pages/Tenants.tsx
+++ b/pinot-controller/src/main/resources/app/pages/TablesListingPage.tsx
@@ -17,50 +17,73 @@
  * under the License.
  */
 
-import React, { useState, useEffect } from 'react';
-import { Grid } from '@material-ui/core';
+import React, {useState, useEffect} from 'react';
+import { Grid, makeStyles } from '@material-ui/core';
 import { TableData } from 'Models';
-import { RouteComponentProps } from 'react-router-dom';
-import CustomizedTables from '../components/Table';
 import AppLoader from '../components/AppLoader';
 import PinotMethodUtils from '../utils/PinotMethodUtils';
+import CustomizedTables from '../components/Table';
 
-type Props = {
-  tenantName: string
-};
+const useStyles = makeStyles(() => ({
+  gridContainer: {
+    padding: 20,
+    backgroundColor: 'white',
+    maxHeight: 'calc(100vh - 70px)',
+    overflowY: 'auto'
+  },
 
-const TenantPage = ({ match }: RouteComponentProps<Props>) => {
+}));
+
+const TablesListingPage = () => {
+  const classes = useStyles();
 
-  const tenantName = match.params.tenantName;
-  const columnHeaders = ['Table Name', 'Reported Size', 'Estimated Size', 
'Number of Segments', 'Status'];
   const [fetching, setFetching] = useState(true);
+  const columnHeaders = ['Table Name', 'Tenant Name', 'Reported Size', 
'Estimated Size', 'Number of Segments', 'Status'];
+  const records = [];
   const [tableData, setTableData] = useState<TableData>({
     columns: columnHeaders,
     records: []
   });
 
   const fetchData = async () => {
-    const result = await PinotMethodUtils.getTenantTableData(tenantName);
-    setTableData(result);
-    setFetching(false);
-  };
+    const tenantsDataResponse = await PinotMethodUtils.getTenantsData();
+    let promiseArr = [];
+    tenantsDataResponse.records.map((tenantRecord)=>{
+      promiseArr.push(PinotMethodUtils.getTenantTableData(tenantRecord[0]));
+    });
+    Promise.all(promiseArr).then((results)=>{
+      results.map((result, index)=>{
+        const tenantName = tenantsDataResponse.records[index][0];
+        records.push(...result.records.map((record)=>{
+          record.splice(1,0,tenantName);
+          return record;
+        }));
+      });
+      setTableData({columns: columnHeaders, records});
+      setFetching(false);
+    });
+  }
+
   useEffect(() => {
     fetchData();
   }, []);
-  return (
-    fetching ? <AppLoader /> :
-    <Grid item xs style={{ padding: 20, backgroundColor: 'white', maxHeight: 
'calc(100vh - 70px)', overflowY: 'auto' }}>
+
+  return fetching ? (
+    <AppLoader/>
+  ) : (
+    <Grid item xs className={classes.gridContainer}>
       <CustomizedTables
-        title={tenantName}
+        title="Tables"
         data={tableData}
         isPagination
         addLinks
-        baseURL={`/tenants/${tenantName}/table/`}
+        baseURL={`/tenants/:Tenant Name:/table/`} // TODO
+        regexReplace={true}
         showSearchBox={true}
         inAccordionFormat={true}
       />
     </Grid>
-  );
+  )
 };
 
-export default TenantPage;
+export default TablesListingPage;
\ No newline at end of file
diff --git a/pinot-controller/src/main/resources/app/pages/Tenants.tsx 
b/pinot-controller/src/main/resources/app/pages/Tenants.tsx
index f425f47..9c87678 100644
--- a/pinot-controller/src/main/resources/app/pages/Tenants.tsx
+++ b/pinot-controller/src/main/resources/app/pages/Tenants.tsx
@@ -38,10 +38,16 @@ const TenantPage = ({ match }: RouteComponentProps<Props>) 
=> {
     columns: columnHeaders,
     records: []
   });
+  const [brokerData, setBrokerData] = useState([]);
+  const [serverData, setServerData] = useState([]);
 
   const fetchData = async () => {
-    const result = await PinotMethodUtils.getTenantTableData(tenantName);
-    setTableData(result);
+    const tenantData = await PinotMethodUtils.getTenantTableData(tenantName);
+    const brokersData = await PinotMethodUtils.getBrokerOfTenant(tenantName);
+    const serversData = await PinotMethodUtils.getServerOfTenant(tenantName);
+    setTableData(tenantData);
+    setBrokerData(brokersData);
+    setServerData(serversData);
     setFetching(false);
   };
   useEffect(() => {
@@ -59,6 +65,36 @@ const TenantPage = ({ match }: RouteComponentProps<Props>) 
=> {
         showSearchBox={true}
         inAccordionFormat={true}
       />
+      <Grid container spacing={2}>
+        <Grid item xs={6}>
+          <CustomizedTables
+            title="Brokers"
+            data={{
+              columns: ['Instance Name'],
+              records: [brokerData]
+            }}
+            isPagination
+            addLinks
+            baseURL={'/instance/'}
+            showSearchBox={true}
+            inAccordionFormat={true}
+          />
+        </Grid>
+        <Grid item xs={6}>
+          <CustomizedTables
+            title="Servers"
+            data={{
+              columns: ['Instance Name'],
+              records: [serverData]
+            }}
+            isPagination
+            addLinks
+            baseURL={'/instance/'}
+            showSearchBox={true}
+            inAccordionFormat={true}
+          />
+        </Grid>
+      </Grid>
     </Grid>
   );
 };
diff --git 
a/pinot-controller/src/main/resources/app/components/Homepage/TenantsTable.tsx 
b/pinot-controller/src/main/resources/app/pages/TenantsListingPage.tsx
similarity index 53%
rename from 
pinot-controller/src/main/resources/app/components/Homepage/TenantsTable.tsx
rename to pinot-controller/src/main/resources/app/pages/TenantsListingPage.tsx
index 0696961..0b78ce2 100644
--- 
a/pinot-controller/src/main/resources/app/components/Homepage/TenantsTable.tsx
+++ b/pinot-controller/src/main/resources/app/pages/TenantsListingPage.tsx
@@ -17,38 +17,46 @@
  * under the License.
  */
 
-import React, { useEffect, useState } from 'react';
+import React, {useState, useEffect} from 'react';
+import { Grid, makeStyles } from '@material-ui/core';
 import { TableData } from 'Models';
-import AppLoader from '../AppLoader';
-import CustomizedTables from '../Table';
-import PinotMethodUtils from '../../utils/PinotMethodUtils';
+import AppLoader from '../components/AppLoader';
+import PinotMethodUtils from '../utils/PinotMethodUtils';
+import TenantsListing from '../components/Homepage/TenantsListing';
+
+const useStyles = makeStyles(() => ({
+  gridContainer: {
+    padding: 20,
+    backgroundColor: 'white',
+    maxHeight: 'calc(100vh - 70px)',
+    overflowY: 'auto'
+  },
+
+}));
+
+const TenantsListingPage = () => {
+  const classes = useStyles();
 
-const TenantsTable = () => {
   const [fetching, setFetching] = useState(true);
-  const [tableData, setTableData] = useState<TableData>({ records: [], 
columns: [] });
+  const [tenantsData, setTenantsData] = useState<TableData>({ records: [], 
columns: [] });
 
   const fetchData = async () => {
-    const result = await PinotMethodUtils.getTenantsData();
-    setTableData(result);
+    const tenantsDataResponse = await PinotMethodUtils.getTenantsData();
+    setTenantsData(tenantsDataResponse);
     setFetching(false);
-  };
+  }
+
   useEffect(() => {
     fetchData();
   }, []);
 
   return fetching ? (
-    <AppLoader />
+    <AppLoader/>
   ) : (
-    <CustomizedTables
-      title="Tenants"
-      data={tableData}
-      addLinks
-      isPagination
-      baseURL="/tenants/"
-      showSearchBox={true}
-      inAccordionFormat={true}
-    />
-  );
+    <Grid item xs className={classes.gridContainer}>
+      <TenantsListing tenantsData={tenantsData}/>
+    </Grid>
+  )
 };
 
-export default TenantsTable;
+export default TenantsListingPage;
\ No newline at end of file
diff --git a/pinot-controller/src/main/resources/app/requests/index.ts 
b/pinot-controller/src/main/resources/app/requests/index.ts
index 373c9e7..60e5ad6 100644
--- a/pinot-controller/src/main/resources/app/requests/index.ts
+++ b/pinot-controller/src/main/resources/app/requests/index.ts
@@ -19,7 +19,8 @@
 
 import { AxiosResponse } from 'axios';
 import { TableData, Instances, Instance, Tenants, ClusterConfig, TableName, 
TableSize,
-  IdealState, QueryTables, TableSchema, SQLResult, ClusterName, ZKGetList, 
ZKConfig, ZKOperationResponsne
+  IdealState, QueryTables, TableSchema, SQLResult, ClusterName, ZKGetList, 
ZKConfig, ZKOperationResponsne,
+  BrokerList, ServerList
 } from 'Models';
 import { baseApi } from '../utils/axios-config';
 
@@ -56,8 +57,8 @@ export const getInstance = (name: string): 
Promise<AxiosResponse<Instance>> =>
 export const getClusterConfig = (): Promise<AxiosResponse<ClusterConfig>> =>
   baseApi.get('/cluster/configs');
 
-export const getQueryTables = (): Promise<AxiosResponse<QueryTables>> =>
-  baseApi.get('/tables');
+export const getQueryTables = (type?: string): 
Promise<AxiosResponse<QueryTables>> =>
+  baseApi.get(`/tables${type ? "?type="+type: ""}`);
 
 export const getTableSchema = (name: string): 
Promise<AxiosResponse<TableSchema>> =>
   baseApi.get(`/tables/${name}/schema`);
@@ -84,4 +85,10 @@ export const zookeeperPutData = (params: string): 
Promise<AxiosResponse<ZKOperat
   baseApi.put(`/zk/put?${params}`, null, { headers: { 'Content-Type': 
'application/json; charset=UTF-8', 'Accept': 'text/plain, */*; q=0.01' } });
 
 export const zookeeperDeleteNode = (params: string): 
Promise<AxiosResponse<ZKOperationResponsne>> =>
-  baseApi.delete(`/zk/delete?path=${params}`);
\ No newline at end of file
+  baseApi.delete(`/zk/delete?path=${params}`);
+
+export const getBrokerListOfTenant = (name: string): 
Promise<AxiosResponse<BrokerList>> =>
+  baseApi.get(`/brokers/tenants/${name}`);
+
+export const getServerListOfTenant = (name: string): 
Promise<AxiosResponse<ServerList>> =>
+  baseApi.get(`/tenants/${name}?type=server`);
diff --git a/pinot-controller/src/main/resources/app/router.tsx 
b/pinot-controller/src/main/resources/app/router.tsx
index d15b07d..86b700c 100644
--- a/pinot-controller/src/main/resources/app/router.tsx
+++ b/pinot-controller/src/main/resources/app/router.tsx
@@ -18,6 +18,9 @@
  */
 
 import HomePage from './pages/HomePage';
+import TenantsListingPage from './pages/TenantsListingPage';
+import InstanceListingPage from './pages/InstanceListingPage';
+import TablesListingPage from './pages/TablesListingPage';
 import TenantsPage from './pages/Tenants';
 import TenantPageDetails from './pages/TenantDetails';
 import QueryPage from './pages/Query';
@@ -28,6 +31,11 @@ import ZookeeperPage from './pages/ZookeeperPage';
 export default [
   { path: "/", Component: HomePage },
   { path: "/query", Component: QueryPage },
+  { path: "/tenants", Component: TenantsListingPage },
+  { path: "/controllers", Component: InstanceListingPage },
+  { path: "/brokers", Component: InstanceListingPage },
+  { path: "/servers", Component: InstanceListingPage },
+  { path: "/tables", Component: TablesListingPage },
   { path: "/tenants/:tenantName", Component: TenantsPage },
   { path: "/tenants/:tenantName/table/:tableName", Component: 
TenantPageDetails },
   { path: "/tenants/:tenantName/table/:tableName/:segmentName", Component: 
SegmentDetails },
diff --git a/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts 
b/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts
index 35a2c9e..165ab76 100644
--- a/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts
+++ b/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts
@@ -18,7 +18,7 @@
  */
 
 import _ from 'lodash';
-import { SQLResult } from 'Models';
+import { DataTable, SQLResult } from 'Models';
 import moment from 'moment';
 import {
   getTenants,
@@ -40,7 +40,9 @@ import {
   zookeeperGetListWithStat,
   zookeeperGetStat,
   zookeeperPutData,
-  zookeeperDeleteNode
+  zookeeperDeleteNode,
+  getBrokerListOfTenant,
+  getServerListOfTenant
 } from '../requests';
 import Utils from './Utils';
 
@@ -50,24 +52,28 @@ import Utils from './Utils';
 const getTenantsData = () => {
   return getTenants().then(({ data }) => {
     const records = _.union(data.SERVER_TENANTS, data.BROKER_TENANTS);
-    return {
+    let promiseArr = [];
+    let finalResponse = {
       columns: ['Tenant Name', 'Server', 'Broker', 'Tables'],
-      records: [
-        ...records.map((record) => [
-          record,
-          data.SERVER_TENANTS.indexOf(record) > -1 ? 1 : 0,
-          data.BROKER_TENANTS.indexOf(record) > -1 ? 1 : 0,
-          '-',
-        ]),
-      ],
+      records: []
     };
+    records.map((record)=>{
+      finalResponse.records.push([
+        record,
+        data.SERVER_TENANTS.indexOf(record) > -1 ? 1 : 0,
+        data.BROKER_TENANTS.indexOf(record) > -1 ? 1 : 0
+      ]);
+      promiseArr.push(getTenantTable(record));
+    });
+    return Promise.all(promiseArr).then((results)=>{
+      results.map((result, index)=>{
+        finalResponse.records[index].push(result.data.tables.length);
+      });
+      return finalResponse;
+    });
   });
 };
 
-type DataTable = {
-  [name: string]: string[];
-};
-
 // This method is used to fetch all instances on cluster manager home page
 // API: /instances
 // Expected Output: {Controller: ['Controller1', 'Controller2'], Broker: 
['Broker1', 'Broker2']}
@@ -82,7 +88,7 @@ const getAllInstances = () => {
       r[key] = [...(r[key] || []), a];
       return r;
     }, initialVal);
-    return groupedData;
+    return {"Controller": groupedData.Controller, ...groupedData};
   });
 };
 
@@ -142,14 +148,20 @@ const getClusterConfigData = () => {
 // This method is used to display table listing on query page
 // API: /tables
 // Expected Output: {columns: [], records: []}
-const getQueryTablesList = () => {
-  return getQueryTables().then(({ data }) => {
-    return {
+const getQueryTablesList = ({bothType = false}) => {
+  let promiseArr = bothType ? [getQueryTables('realtime'), 
getQueryTables('offline')] : [getQueryTables()];
+  
+  return Promise.all(promiseArr).then((results) => {
+    let responseObj = {
       columns: ['Tables'],
-      records: data.tables.map((table) => {
-        return [table];
-      }),
-    };
+      records:  []
+    }
+    results.map((result)=>{
+      result.data.tables.map((table)=>{
+        responseObj.records.push([table]);
+      });
+    });
+    return responseObj;
   });
 };
 
@@ -562,6 +574,18 @@ const deleteNode = (path) => {
   });
 };
 
+const getBrokerOfTenant = (tenantName) => {
+  return getBrokerListOfTenant(tenantName).then((response)=>{
+    return response.data;
+  });
+};
+
+const getServerOfTenant = (tenantName) => {
+  return getServerListOfTenant(tenantName).then((response)=>{
+    return response.data.ServerInstances;
+  });
+};
+
 export default {
   getTenantsData,
   getAllInstances,
@@ -583,5 +607,7 @@ export default {
   getZookeeperData,
   getNodeData,
   putNodeData,
-  deleteNode
+  deleteNode,
+  getBrokerOfTenant,
+  getServerOfTenant
 };


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org
For additional commands, e-mail: commits-h...@pinot.apache.org

Reply via email to