Skip to content

Commit

Permalink
feat(webapp): add endpoints stats view
Browse files Browse the repository at this point in the history
  • Loading branch information
Torresmorah committed Mar 20, 2023
1 parent 432b9c9 commit bff30f6
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 0 deletions.
19 changes: 19 additions & 0 deletions webapp/src/gql/producer.gql.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,22 @@ export const EOSRATE_STATS_QUERY = gql`
}
}
`

export const FASTEST_ENDPOINTS_QUERY = gql`query($today: date){
endpoints: check_history_by_endpoint(limit: 5, order_by: {avg_time: asc, availability: desc}, where: {date: {_eq: $today}}) {
value
avg_time
availability
}
}`

export const HISTORY_ENDPOINTS_BY_PRODUCER_QUERY = gql`query($id: Int){
endpoints: check_history_by_endpoint(order_by: {value: asc, date: asc}, where: {producer_id: {_eq: $id}}) {
value
date
avg_time
availability
}
}`


84 changes: 84 additions & 0 deletions webapp/src/hooks/customHooks/useHealthCheckHistoryState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useState, useEffect } from 'react'
import { useLazyQuery } from '@apollo/client'

import {
FASTEST_ENDPOINTS_QUERY,
HISTORY_ENDPOINTS_BY_PRODUCER_QUERY,
PRODUCERS_QUERY,
} from '../../gql'

const useHealthCheckState = () => {
const [
loadEndpoints,
{ loading = true, data: { endpoints: fastestEndpoints } = {} },
] = useLazyQuery(FASTEST_ENDPOINTS_QUERY)
const [
loadProducers,
{ loading: loadingProducers = true, data: { producers } = {} },
] = useLazyQuery(PRODUCERS_QUERY)
const [
loadHistory,
{ loading: loadingHistory = true, data: { endpoints: history } = {} },
] = useLazyQuery(HISTORY_ENDPOINTS_BY_PRODUCER_QUERY)
const [producersNames, setProducersNames] = useState()
const [selected, setSelected] = useState()
const [historyData, setHistoryData] = useState()
const [statsAverage, setStatsAverage] = useState()

useEffect(() => {
const endpointFilter = {
_and: [{ type: { _in: ['ssl', 'api'] } }, { value: { _gt: '' } }],
}
loadProducers({
variables: {
where: { nodes: { endpoints: endpointFilter } },
endpointFilter,
limit: null,
},
})
loadEndpoints({ variables: { today: new Date() } })
}, [])

useEffect(() => {
if (!producers?.length) return

setProducersNames(
producers.map((producer) => ({
id: producer.id,
name: producer?.bp_json?.org?.candidate_name,
})),
)
setSelected(producers[0]?.id)
}, [producers])

useEffect(() => {
loadHistory({ variables: { id: selected } })
}, [selected])

useEffect(() => {
if (!history) return

const data = history.reduce((aux, curr) => {
const index = aux.findIndex((x) => x.name === curr.value)
if (index < 0) {
aux.push({ name: curr.value, data: [curr.avg_time],avg_time:curr.avg_time, availability: curr.availability})
} else {
aux[index].data.push(curr.avg_time)
aux[index].availability = aux[index].availability + curr.availability
aux[index].avg_time = aux[index].avg_time + curr.avg_time
}

return aux
}, [])
setHistoryData(data)
setStatsAverage(data.map(x=>({value:x.name,avg_time:x.avg_time/x.data.length,availability:x.availability/x.data.length})))

}, [history])

return [
{ fastestEndpoints, producersNames, historyData, statsAverage, selected, loading },
{ setSelected },
]
}

export default useHealthCheckState
51 changes: 51 additions & 0 deletions webapp/src/routes/EndpointsStats/EndpointStatsTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* eslint camelcase: 0 */
import React from 'react'
import { useTranslation } from 'react-i18next'
import Typography from '@mui/material/Typography'
import Table from '@mui/material/Table'
import TableBody from '@mui/material/TableBody'
import TableCell from '@mui/material/TableCell'
import TableContainer from '@mui/material/TableContainer'
import TableHead from '@mui/material/TableHead'
import TableRow from '@mui/material/TableRow'

import { makeStyles } from '@mui/styles'

import styles from './styles'

const useStyles = makeStyles(styles)

const EndpointsTable = ({endpoints, title}) => {
const { t } = useTranslation('EndpointsStatsRoute')
const classes = useStyles()

return (
<>
<Typography component="p" variant="h6">
{title}
</Typography>
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>{t('Endpoint')}</TableCell>
<TableCell align="right">{t('Average Availability')}</TableCell>
<TableCell align="right">{t('Average Response Time')}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{endpoints.map((item, index) => (
<TableRow key={index}>
<TableCell>{item.value}</TableCell>
<TableCell align="right">{`${item.availability}%`}</TableCell>
<TableCell align="right">{`${item.avg_time.toFixed(3)} s`}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
)
}

export default EndpointsTable
102 changes: 102 additions & 0 deletions webapp/src/routes/EndpointsStats/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* eslint camelcase: 0 */
import React from 'react'
import { useTranslation } from 'react-i18next'
import Card from '@mui/material/Card'
import CardContent from '@mui/material/CardContent'
import Typography from '@mui/material/Typography'
import LinearProgress from '@mui/material/LinearProgress'
import MenuItem from '@mui/material/MenuItem'
import Highcharts from 'highcharts'
import HighchartsReact from 'highcharts-react-official'
import moment from 'moment'
import Select from '@mui/material/Select'
import { makeStyles } from '@mui/styles'

import useHealthCheckState from '../../hooks/customHooks/useHealthCheckHistoryState'

import styles from './styles'
import EndpointsTable from './EndpointStatsTable'

const useStyles = makeStyles(styles)

const dates = []
for (let i = 29; i >= 0; i--) {
const d = moment().subtract(i, 'days').format('ll')
dates.push(d)
}

const options = {
xAxis: {
categories: dates,
},
credits: {
enabled: false,
},
title: {
text: 'Average Response Time',
},
yAxis: {
title: {
text: 'Time in seconds',
},
labels: {
format: '{text} s',
},
},
tooltip: {
pointFormat: '{series.name}: <b>{point.y} s<b>',
},
}

const EndpointsStats = () => {
const { t } = useTranslation('EndpointsStatsRoute')
const classes = useStyles()
const [{fastestEndpoints,producersNames,historyData,statsAverage,selected,loading},{setSelected}] = useHealthCheckState()

return (
<>
<Card className={classes.cardShadow}>
<CardContent>
{loading && <LinearProgress />}
{!loading && (
<EndpointsTable
title="Top 5 fastest endpoints by querying from Costa Rica"
endpoints={fastestEndpoints || []}
/>
)}
</CardContent>
</Card>
<Card className={`${classes.cardByProducer} ${classes.cardShadow}`}>
<CardContent>
<Typography component="p" variant="h6">
Endpoints stats by producer
</Typography>
<br />
{producersNames?.length && (
<Select value={selected} onChange={(event) => setSelected(event.target.value)}>
{producersNames.map(producer => (
<MenuItem key={producer.id} value={producer.id}>{producer.name}</MenuItem>
))}
</Select>
)}
{historyData && (
<HighchartsReact
highcharts={Highcharts}
options={{ ...options,xAxis: {
categories: dates.slice(dates.length-(historyData[0].data.length+1)),
}, series: historyData }}
/>
)}
{statsAverage && (
<EndpointsTable
title="List of endpoints"
endpoints={statsAverage}
/>
)}
</CardContent>
</Card>
</>
)
}

export default EndpointsStats
8 changes: 8 additions & 0 deletions webapp/src/routes/EndpointsStats/styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default (theme) => ({
cardShadow: {
boxShadow: '0px 1px 3px 1px rgba(0, 0, 0, 0.15) !important',
},
cardByProducer: {
marginTop: theme.spacing(8)
}
})
8 changes: 8 additions & 0 deletions webapp/src/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {

const Home = lazy(() => import('./Home'))
const CPUBenchmark = lazy(() => import('./CPUBenchmark'))
const EndpointsStats = lazy(() => import('./EndpointsStats'))
const BlockProducers = lazy(() => import('./BlockProducers'))
const RewardsDistribution = lazy(() => import('./RewardsDistribution'))
const Nodes = lazy(() => import('./Nodes'))
Expand Down Expand Up @@ -118,6 +119,13 @@ const defaultRoutes = [
path: '/cpu-benchmark',
exact: true,
},
{
name: 'endpointsStats',
icon: <ActivityIcon />,
component: EndpointsStats,
path: '/endpoints-stats',
exact: true,
},
{
name: 'ricardianContract',
icon: <InfoIcon />,
Expand Down

0 comments on commit bff30f6

Please sign in to comment.