-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* wip collection info tab * wip collection info tab * wip collection info tab, some refactoring, status indicator * added collection cluster info * snackbars * configs upd for tests, test for CollectionClusterInfo * cluster info refactoring * variant heading for CardHeader * format * bug fix
- Loading branch information
Showing
14 changed files
with
423 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,3 +37,4 @@ rules: | |
}, | ||
], | ||
} | ||
ignorePatterns: ['node_modules/', 'build/', '*.test.js', '*.test.jsx'] |
68 changes: 68 additions & 0 deletions
68
src/components/Collections/CollectionCluster/ClusterInfo.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Card, CardHeader, Table, TableBody } from '@mui/material'; | ||
import { CopyButton } from '../../Common/CopyButton'; | ||
import ClusterInfoHead from './ClusterInfoHead'; | ||
import ClusterShardRow from './ClusterShardRow'; | ||
|
||
const ClusterInfo = ({ collectionCluster, ...other }) => { | ||
const shards = [ | ||
...(collectionCluster.result?.local_shards || []), | ||
...(collectionCluster.result?.remote_shards || []), | ||
]; | ||
|
||
const shardRows = shards.map((shard) => ( | ||
<ClusterShardRow | ||
shard={shard} | ||
clusterPeerId={collectionCluster.result?.peer_id} | ||
key={shard.shard_id.toString() + (shard.peer_id || '')} | ||
/> | ||
)); | ||
|
||
return ( | ||
<Card variant="dual" {...other}> | ||
<CardHeader | ||
title={'Collection Cluster Info'} | ||
variant="heading" | ||
sx={{ | ||
flexGrow: 1, | ||
}} | ||
action={<CopyButton text={JSON.stringify(collectionCluster)} />} | ||
/> | ||
<Table> | ||
<ClusterInfoHead /> | ||
<TableBody>{shardRows}</TableBody> | ||
</Table> | ||
</Card> | ||
); | ||
}; | ||
|
||
ClusterInfo.defaultProps = { | ||
collectionCluster: { | ||
result: {}, | ||
}, | ||
}; | ||
|
||
ClusterInfo.propTypes = { | ||
collectionCluster: PropTypes.shape({ | ||
result: PropTypes.shape({ | ||
peer_id: PropTypes.number, | ||
local_shards: PropTypes.arrayOf( | ||
PropTypes.shape({ | ||
shard_id: PropTypes.number, | ||
state: PropTypes.string, | ||
}) | ||
), | ||
remote_shards: PropTypes.arrayOf( | ||
PropTypes.shape({ | ||
shard_id: PropTypes.number, | ||
peer_id: PropTypes.number, | ||
state: PropTypes.string, | ||
}) | ||
), | ||
}), | ||
}).isRequired, | ||
other: PropTypes.object, | ||
}; | ||
|
||
export default ClusterInfo; |
28 changes: 28 additions & 0 deletions
28
src/components/Collections/CollectionCluster/ClusterInfoHead.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { TableCell, TableHead, TableRow, Typography } from '@mui/material'; | ||
import React from 'react'; | ||
|
||
const ClusterInfoHead = () => { | ||
return ( | ||
<TableHead> | ||
<TableRow> | ||
<TableCell> | ||
<Typography variant="subtitle1" fontWeight={600}> | ||
Shard ID | ||
</Typography> | ||
</TableCell> | ||
<TableCell> | ||
<Typography variant="subtitle1" fontWeight={600}> | ||
Location | ||
</Typography> | ||
</TableCell> | ||
<TableCell> | ||
<Typography variant="subtitle1" fontWeight={600}> | ||
Status | ||
</Typography> | ||
</TableCell> | ||
</TableRow> | ||
</TableHead> | ||
); | ||
}; | ||
|
||
export default ClusterInfoHead; |
36 changes: 36 additions & 0 deletions
36
src/components/Collections/CollectionCluster/ClusterShardRow.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { TableCell, TableRow, Typography } from '@mui/material'; | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
const ClusterShardRow = ({ shard, clusterPeerId }) => { | ||
return ( | ||
<TableRow data-testid="shard-row"> | ||
<TableCell> | ||
<Typography variant="subtitle1" component={'span'} color="text.secondary"> | ||
{shard.shard_id} | ||
</Typography> | ||
</TableCell> | ||
<TableCell> | ||
<Typography variant="subtitle1" component={'span'} color="text.secondary"> | ||
{shard.peer_id ? `Remote (${shard.peer_id})` : `Local (${clusterPeerId ?? 'unknown'})`} | ||
</Typography> | ||
</TableCell> | ||
<TableCell> | ||
<Typography variant="subtitle1" component={'span'} color="text.secondary"> | ||
{shard.state} | ||
</Typography> | ||
</TableCell> | ||
</TableRow> | ||
); | ||
}; | ||
|
||
ClusterShardRow.propTypes = { | ||
shard: PropTypes.shape({ | ||
shard_id: PropTypes.number, | ||
peer_id: PropTypes.number, | ||
state: PropTypes.string, | ||
}).isRequired, | ||
clusterPeerId: PropTypes.number, | ||
}; | ||
|
||
export default ClusterShardRow; |
71 changes: 71 additions & 0 deletions
71
src/components/Collections/CollectionCluster/collectionCluster.test.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import '@testing-library/jest-dom'; | ||
import ClusterInfo from './ClusterInfo'; | ||
import ClusterShardRow from './ClusterShardRow'; | ||
|
||
const CLUSTER_INFO = { | ||
result: { | ||
peer_id: 5644950770669488, | ||
shard_count: 3, | ||
local_shards: [ | ||
{ | ||
shard_id: 0, | ||
points_count: 62223, | ||
state: 'Active', | ||
}, | ||
{ | ||
shard_id: 2, | ||
points_count: 65999, | ||
state: 'Active', | ||
}, | ||
], | ||
remote_shards: [ | ||
{ | ||
shard_id: 0, | ||
peer_id: 5255497362296823, | ||
state: 'Active', | ||
}, | ||
{ | ||
shard_id: 1, | ||
peer_id: 5255497362296823, | ||
state: 'Active', | ||
}, | ||
{ | ||
shard_id: 1, | ||
peer_id: 8741461806010521, | ||
state: 'Active', | ||
}, | ||
{ | ||
shard_id: 2, | ||
peer_id: 8741461806010521, | ||
state: 'Active', | ||
}, | ||
], | ||
shard_transfers: [], | ||
}, | ||
status: 'ok', | ||
time: 0.00002203, | ||
}; | ||
|
||
describe('collection cluster info', () => { | ||
it('should render ClusterShardRow with given data', () => { | ||
const shard = CLUSTER_INFO.result.remote_shards[0]; | ||
render( | ||
<table> | ||
<tbody> | ||
<ClusterShardRow shard={shard} clusterPeerId={CLUSTER_INFO.result.peer_id} /> | ||
</tbody> | ||
</table> | ||
); | ||
expect(screen.getByTestId('shard-row').children[0].children[0].textContent).toBe(shard.shard_id.toString()); | ||
expect(screen.getByText(`Remote (${shard.peer_id})`)).toBeTruthy(); | ||
expect(screen.getByText(shard.state)).toBeTruthy(); | ||
}); | ||
|
||
it('should render CollectionClusterInfo with given data', () => { | ||
const shardReplicasCount = CLUSTER_INFO.result.local_shards.length + CLUSTER_INFO.result.remote_shards.length; | ||
render(<ClusterInfo collectionCluster={CLUSTER_INFO} />); | ||
expect(screen.getByText('Collection Cluster Info')).toBeTruthy(); | ||
expect(screen.getAllByTestId('shard-row').length).toBe(shardReplicasCount); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import React, { memo, useEffect } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Box, Card, CardContent, CardHeader, Typography } from '@mui/material'; | ||
import { useClient } from '../../context/client-context'; | ||
import { DataGridList } from '../Points/DataGridList'; | ||
import { CopyButton } from '../Common/CopyButton'; | ||
import { Dot } from '../Common/Dot'; | ||
import ClusterInfo from './CollectionCluster/ClusterInfo'; | ||
import { useSnackbar } from 'notistack'; | ||
import { getSnackbarOptions } from '../Common/utils/snackbarOptions'; | ||
|
||
export const CollectionInfo = ({ collectionName }) => { | ||
const { enqueueSnackbar, closeSnackbar } = useSnackbar(); | ||
const { client: qdrantClient } = useClient(); | ||
const [collection, setCollection] = React.useState({}); | ||
const [clusterInfo, setClusterInfo] = React.useState(null); | ||
|
||
useEffect(() => { | ||
qdrantClient | ||
.getCollection(collectionName) | ||
.then((res) => { | ||
setCollection(() => { | ||
return { ...res }; | ||
}); | ||
}) | ||
.catch((err) => { | ||
enqueueSnackbar(err.message, getSnackbarOptions('error', closeSnackbar)); | ||
}); | ||
|
||
qdrantClient | ||
.api('cluster') | ||
.collectionClusterInfo({ collection_name: collectionName }) | ||
.then((res) => { | ||
setClusterInfo(() => { | ||
return { ...res.data }; | ||
}); | ||
}) | ||
.catch((err) => { | ||
enqueueSnackbar(err.message, getSnackbarOptions('error', closeSnackbar)); | ||
}); | ||
}, [collectionName]); | ||
|
||
return ( | ||
<Box pt={2}> | ||
<Card variant="dual"> | ||
<CardHeader | ||
title={'Collection Info'} | ||
variant="heading" | ||
sx={{ | ||
flexGrow: 1, | ||
}} | ||
action={<CopyButton text={JSON.stringify(collection)} />} | ||
/> | ||
<CardContent> | ||
<DataGridList | ||
data={collection} | ||
specialCases={{ | ||
status: ( | ||
<Typography variant="subtitle1" color="text.secondary"> | ||
{collection.status} <Dot color={collection.status} /> | ||
</Typography> | ||
), | ||
}} | ||
/> | ||
</CardContent> | ||
</Card> | ||
|
||
{clusterInfo && <ClusterInfo sx={{ mt: 5 }} collectionCluster={clusterInfo} />} | ||
</Box> | ||
); | ||
}; | ||
|
||
CollectionInfo.displayName = 'CollectionInfo'; | ||
|
||
CollectionInfo.propTypes = { | ||
collectionName: PropTypes.string.isRequired, | ||
}; | ||
|
||
export default memo(CollectionInfo); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { styled } from '@mui/material/styles'; | ||
|
||
/** | ||
* Status indicator dot. | ||
* @type {StyledComponent<MUIStyledCommonProps<Theme>, JSX.IntrinsicElements[string], {}>} | ||
*/ | ||
export const Dot = styled('div')( | ||
({ color }) => ` | ||
border-radius: 50%; | ||
background-color: ${color}; | ||
width: 10px; | ||
height: 10px; | ||
display: inline-block; | ||
` | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { JsonViewer } from '@textea/json-viewer'; | ||
import { useTheme } from '@mui/material/styles'; | ||
import { Divider, Grid, Typography } from '@mui/material'; | ||
|
||
/** | ||
* A list of key-value pairs, where the value is either a string or an object. | ||
* if the value is an object, it will be rendered as a JSON tree. | ||
* @param {Object} data - key-value pairs to render | ||
* @param {Object} specialCases - key-value pairs to render, where the value is JSX element | ||
* @return {unknown[]} - array of JSX elements | ||
*/ | ||
export const DataGridList = function ({ data = {}, specialCases = {} }) { | ||
const theme = useTheme(); | ||
const specialKeys = Object.keys(specialCases) || []; | ||
|
||
return Object.keys(data).map((key) => { | ||
return ( | ||
<div key={key}> | ||
<Grid container spacing={2}> | ||
<Grid item xs={3} my={1}> | ||
<Typography | ||
variant="subtitle1" | ||
sx={{ | ||
display: 'inline', | ||
wordBreak: 'break-word', | ||
fontWeight: 600, | ||
}} | ||
> | ||
{key} | ||
</Typography> | ||
</Grid> | ||
|
||
<Grid item xs={9} my={1}> | ||
{/* special cases */} | ||
{specialKeys?.includes(key) && specialCases[key]} | ||
|
||
{/* objects */} | ||
{typeof data[key] === 'object' && !specialKeys.includes(key) && ( | ||
<Typography variant="subtitle1"> | ||
{' '} | ||
<JsonViewer | ||
theme={theme.palette.mode} | ||
value={data[key]} | ||
displayDataTypes={false} | ||
defaultInspectDepth={0} | ||
rootName={false} | ||
/>{' '} | ||
</Typography> | ||
)} | ||
|
||
{/* other types of values */} | ||
{typeof data[key] !== 'object' && !specialKeys.includes(key) && ( | ||
<Typography variant="subtitle1" color="text.secondary" display={'inline'}> | ||
{'\t'} {data[key].toString()} | ||
</Typography> | ||
)} | ||
</Grid> | ||
</Grid> | ||
<Divider /> | ||
</div> | ||
); | ||
}); | ||
}; | ||
|
||
DataGridList.defaultProps = { | ||
data: {}, | ||
specialCases: {}, | ||
}; | ||
|
||
DataGridList.propTypes = { | ||
data: PropTypes.object.isRequired, | ||
specialCases: PropTypes.shape({ | ||
key: PropTypes.string, | ||
value: PropTypes.element, | ||
}), | ||
}; |
Oops, something went wrong.