Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add nodes to upgrade status #763

Merged
merged 17 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions public/locales/en-US/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -416,9 +416,10 @@
"uri": "URI",
"owner": "Owner",
"other_chain_destination": "Other Chain Destination",
"%_of_total_validators": "% of Total Validators",
"%_of_total_nodes_validators": "% of Total Nodes & Validators",
"version_display": "Version: {{version}}",
"validators_count": "# of Validators: {{count}}",
"validators_count": "# of Validators: {{vals_count}}",
"nodes_count": "# of Nodes: {{nodes_count}}",
"current_stable_version": "Current Stable Version",
"stable_version": "{{stableVersion}}",
"nftoken_minter": "NFT Minter",
Expand Down
2 changes: 1 addition & 1 deletion public/locales/ja-JP/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@
"uri": "URI",
"owner": "所有者",
"other_chain_destination": null,
"%_of_total_validators": "全バリデータの割合",
"%_of_total_nodes_validators": null,
"version_display": "バージョン: {{version}}",
"validators_count": "バリデータの数: {{count}}",
"current_stable_version": "現在の安定バージョン",
Expand Down
134 changes: 100 additions & 34 deletions src/containers/Network/BarChartVersion.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { t } from 'i18next'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
Expand All @@ -9,19 +10,20 @@ import {
TooltipProps,
Label,
ResponsiveContainer,
Legend,
Text,
Cell,
} from 'recharts'
import { Loader } from '../shared/components/Loader'
import {
GREY,
BLUE,
RED,
GREEN,
PURPLE,
GREY_600,
GREY_800,
isEarlierVersion,
GREEN_500,
PURPLE_500,
GREEN_800,
PURPLE_700,
GREY_0,
GREY_400,
} from '../shared/utils'
import './css/barchart.scss'

Expand All @@ -30,6 +32,10 @@ interface Props {
stableVersion: string | null
}

interface LegendProps {
stableVersion: string | null
}

// TODO: figure out a better way to import this from recharts
// copied from https://github.com/recharts/recharts/blob/master/src/component/DefaultTooltipContent.tsx
type ValueType = number | string | Array<number | string>
Expand All @@ -47,7 +53,12 @@ const CustomTooltip = ({
<p className="label">{t('version_display', { version: label })}</p>
<p className="value">
{t('validators_count', {
count: payload ? payload[0].payload.count : 0,
vals_count: payload ? payload[0].payload.validatorsCount : 0,
})}
</p>
<p className="value">
{t('nodes_count', {
nodes_count: payload ? payload[0].payload.nodesCount : 0,
})}
</p>
</div>
Expand All @@ -56,22 +67,44 @@ const CustomTooltip = ({
return null
}

const renderLegend = (stableVersion: string | null, t: any) => (
<div className="legend">
<div className="legend-text">
<span>{t('current_stable_version')}:</span>
<span style={{ color: GREEN }}>
{' '}
{t('stable_version', { stableVersion })}{' '}
</span>
const CustomLegend = (props: LegendProps) => {
const { stableVersion } = props
return (
<div className="custom-legend">
<div className="legend-color">
<div className="segment">
<span className="icon vals" />
<span className="text">{t('validators')}</span>
</div>
<div className="segment">
<span className="icon nodes" />
<span className="text">{t('nodes')}</span>
</div>
</div>
<div className="legend-stable">
<div className="stable-text">
<span>{t('current_stable_version')}:</span>
<span className="stable" style={{ color: GREY_0 }}>
pdp2121 marked this conversation as resolved.
Show resolved Hide resolved
{' '}
{t('stable_version', { stableVersion })}{' '}
</span>
</div>
</div>
</div>
</div>
)
)
}

const stableColorCode = (dataLabel: string, stableVersion: string) => {
if (dataLabel === stableVersion) return GREEN
if (isEarlierVersion(dataLabel, stableVersion)) return RED
return BLUE
const stableColorCode = (
type: string,
dataLabel: string,
stableVersion: string,
) => {
if (dataLabel === stableVersion) {
if (type === 'validators') return GREEN_500
return PURPLE_500
}
if (type === 'validators') return GREEN_800
return PURPLE_700
}

const BarChartVersion = (props: Props) => {
Expand All @@ -80,6 +113,7 @@ const BarChartVersion = (props: Props) => {
const [showTooltips, setShowTooltips] = useState(false)
return (
<div className="barchart">
<CustomLegend stableVersion={stableVersion} />
<ResponsiveContainer height={532} width="95%">
<BarChart
data={data}
Expand All @@ -93,26 +127,40 @@ const BarChartVersion = (props: Props) => {
height={90}
tickLine={false}
minTickGap={-1}
stroke={GREY}
stroke={GREY_400}
tick={(e) => {
const {
payload: { value },
} = e
const color = value === stableVersion ? GREY_0 : GREY_400
e['fill'] = color
if (value === stableVersion)
return (
<Text {...e} style={{ fontWeight: 700 }}>
{value}
</Text>
)
return <Text {...e}>{value}</Text>
}}
interval={0}
/>
<YAxis
className="yAxis"
tickLine={false}
tickFormatter={(tick) => `${tick}%`}
stroke={GREY}
stroke={GREY_400}
>
<Label
className="y-label"
value={t('%_of_total_validators')}
value={t('%_of_total_nodes_validators')}
angle={-90}
position="insideTop"
dx={45}
dy={80}
dy={110}
/>
</YAxis>
<Bar
dataKey="value"
dataKey="validatorsPercent"
barSize={30}
fill={PURPLE}
radius={[4, 4, 0, 0]}
Expand All @@ -124,17 +172,35 @@ const BarChartVersion = (props: Props) => {
data.map((_entry, index) => (
<Cell
key={data[index].label}
fill={stableColorCode(data[index].label, stableVersion)}
fill={stableColorCode(
'validators',
data[index].label,
stableVersion,
)}
/>
))}
</Bar>
<Bar
dataKey="nodesPercent"
barSize={30}
fill={PURPLE}
radius={[4, 4, 0, 0]}
isAnimationActive={false}
onMouseOver={() => setShowTooltips(true)}
onMouseLeave={() => setShowTooltips(false)}
>
{stableVersion &&
data.map((_entry, index) => (
<Cell
key={data[index].label}
fill={stableColorCode(
'nodes',
data[index].label,
stableVersion,
)}
/>
))}
</Bar>
<Legend
verticalAlign="top"
content={() => renderLegend(stableVersion, t)}
wrapperStyle={{
textAlign: 'right',
}}
/>
<Tooltip
content={<CustomTooltip />}
cursor={false}
Expand Down
2 changes: 1 addition & 1 deletion src/containers/Network/Nodes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { useLanguage } from '../shared/hooks'
import { NodeData, NodeResponse } from '../shared/vhsTypes'
import NetworkContext from '../shared/NetworkContext'

const ledgerCompare = (a: NodeData, b: NodeData) => {
export const ledgerCompare = (a: NodeData, b: NodeData) => {
const aLedger = a.validated_ledger.ledger_index
const bLedger = b.validated_ledger.ledger_index
const compareVersion = isEarlierVersion(b.version, a.version) ? -1 : 1
Expand Down
99 changes: 83 additions & 16 deletions src/containers/Network/UpgradeStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,76 @@ import {
} from '../shared/utils'
import { useLanguage } from '../shared/hooks'
import Log from '../shared/log'
import { StreamValidator, ValidatorResponse } from '../shared/vhsTypes'
import {
NodeData,
NodeResponse,
StreamValidator,
ValidatorResponse,
} from '../shared/vhsTypes'
import NetworkContext from '../shared/NetworkContext'
import { ledgerCompare } from './Nodes'

interface DataAggregation {
label: string
value: number
count: number
validatorsPercent: number
validatorsCount: number
nodesPercent: number
nodesCount: number
}

export const aggregateData = (
validators: ValidatorResponse[],
nodes: NodeResponse[],
): DataAggregation[] => {
if (!validators) {
return []
}
let total = 0
const aggregation: Record<string, number> = {}
let totalVals = 0
let totalNodes = 0
interface aggregationTypes {
validatorsCount: number
nodesCount: number
}
const aggregation: Record<string, aggregationTypes> = {}
validators.forEach((validator) => {
if (validator.signing_key) {
pdp2121 marked this conversation as resolved.
Show resolved Hide resolved
total += 1
totalVals += 1
pdp2121 marked this conversation as resolved.
Show resolved Hide resolved
const version = validator.server_version
if (version) {
if (!aggregation[version]) {
aggregation[version] = 1
aggregation[version] = { validatorsCount: 1, nodesCount: 0 }
} else {
aggregation[version].validatorsCount += 1
}
}
}
})

nodes.forEach((node) => {
if (node.version?.includes('+')) {
node.version = node.version.split('+')[0]
}
if (node.node_public_key) {
pdp2121 marked this conversation as resolved.
Show resolved Hide resolved
pdp2121 marked this conversation as resolved.
Show resolved Hide resolved
totalNodes += 1
const { version } = node
if (version) {
if (!aggregation[version]) {
aggregation[version] = { validatorsCount: 0, nodesCount: 1 }
pdp2121 marked this conversation as resolved.
Show resolved Hide resolved
} else {
aggregation[version] += 1
aggregation[version].nodesCount += 1
}
}
}
})

return Object.entries(aggregation)
.map(([version, count]) => ({
.map(([version, counts]) => ({
label: version ? version.trim() : 'N/A',
value: total > 0 ? (count * 100) / total : 0,
count,
validatorsPercent:
totalVals > 0 ? (counts.validatorsCount * 100) / totalVals : 0,
validatorsCount: counts.validatorsCount,
nodesPercent: totalNodes > 0 ? (counts.nodesCount * 100) / totalNodes : 0,
nodesCount: counts.nodesCount,
}))
.sort((a, b) => (isEarlierVersion(a.label, b.label) ? -1 : 1))
}
Expand Down Expand Up @@ -93,10 +127,8 @@ export const UpgradeStatus = () => {
)

const fetchData = () => {
const url = `${process.env.VITE_DATA_URL}/validators/${network}`

axios
.get(url)
const validatorsReq = axios
.get(`${process.env.VITE_DATA_URL}/validators/${network}`)
.then((resp) => resp.data.validators)
.then((validators: ValidatorResponse[]) => {
const newValidatorList: Record<string, ValidatorResponse> = {}
Expand All @@ -108,9 +140,44 @@ export const UpgradeStatus = () => {
setUnlCount(
validators.filter((validator) => Boolean(validator.unl)).length,
)
setAggregated(aggregateData(Object.values(newValidatorList)))
return Object.values(newValidatorList)
})
.catch((e) => Log.error(e))

const nodesReq = axios
.get(`${process.env.VITE_DATA_URL}/topology/nodes/${network}`)
.then((resp) => resp.data.nodes)
.then((allNodes) => {
const nodes: NodeData[] = allNodes.map((node: NodeResponse) => ({
...node,
version: node.version?.startsWith('rippled')
? node.version.split('-')[1]
: node.version,
validated_ledger: {
ledger_index: node.complete_ledgers
? Number(node.complete_ledgers.split('-')[1])
: 0,
},
load_factor: node.load_factor_server
? Number(node.load_factor_server)
: null,
}))

nodes.sort((a: NodeData, b: NodeData) => {
if (a.server_state === b.server_state) {
return ledgerCompare(a, b)
}
if (a.server_state && !b.server_state) {
return -1
}
return 1
})
return nodes
})
.catch((e) => Log.error(e))
Promise.all([validatorsReq, nodesReq]).then(([validators, nodes]) => {
setAggregated(aggregateData(validators, nodes))
})
}

const fetchStableVersion = () => {
Expand Down
Loading
Loading