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