diff --git a/public/locales/ca-CA/translations.json b/public/locales/ca-CA/translations.json
index a4b62daf5..d5e305960 100644
--- a/public/locales/ca-CA/translations.json
+++ b/public/locales/ca-CA/translations.json
@@ -445,8 +445,8 @@
"other_chain_destination": "Destinació a una altra xarxa",
"%_of_total_nodes_validators": "% total de nodes i validadors",
"version_display": "Versió: {{version}}",
- "validators_count": "# de Validadors: {{vals_count}}",
- "nodes_count": "# de Nodes: {{nodes_count}}",
+ "validator_count": "# de Validadors: {{val_count}}",
+ "node_count": "# de Nodes: {{node_count}}",
"current_stable_version": "Versió estable actual",
"stable_version": "{{stableVersion}}",
"nftoken_minter": "Encunyador NFT",
diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json
index c7e8e0642..369edfa7a 100644
--- a/public/locales/en-US/translations.json
+++ b/public/locales/en-US/translations.json
@@ -445,8 +445,8 @@
"other_chain_destination": "Other Chain Destination",
"%_of_total_nodes_validators": "% of Total Nodes & Validators",
"version_display": "Version: {{version}}",
- "validators_count": "# of Validators: {{vals_count}}",
- "nodes_count": "# of Nodes: {{nodes_count}}",
+ "validator_count": "# of Validators: {{val_count}}",
+ "node_count": "# of Nodes: {{node_count}}",
"current_stable_version": "Current Stable Version",
"stable_version": "{{stableVersion}}",
"nftoken_minter": "NFT Minter",
diff --git a/public/locales/es-ES/translations.json b/public/locales/es-ES/translations.json
index 4245651b0..bde04d337 100644
--- a/public/locales/es-ES/translations.json
+++ b/public/locales/es-ES/translations.json
@@ -441,8 +441,8 @@
"other_chain_destination": "Otro Destino de Cadena",
"%_of_total_nodes_validators": "% Total de Nodos y Validadores",
"version_display": "Versión: {{version}}",
- "validators_count": "# de Validadores: {{vals_count}}",
- "nodes_count": "# de Nodos: {{nodes_count}}",
+ "validator_count": "# de Validadores: {{val_count}}",
+ "node_count": "# de Nodos: {{node_count}}",
"current_stable_version": "Versión Estable Actual",
"stable_version": "{{stableVersion}}",
"nftoken_minter": "Acuñador NFT",
diff --git a/public/locales/fr-FR/translations.json b/public/locales/fr-FR/translations.json
index 3265e919e..9cbdc4293 100644
--- a/public/locales/fr-FR/translations.json
+++ b/public/locales/fr-FR/translations.json
@@ -443,8 +443,8 @@
"other_chain_destination": "Autre destination de chaîne",
"%_of_total_nodes_validators": "% des nœuds et des validateurs",
"version_display": "Version: {{version}}",
- "validators_count": "nb de Validateurs: {{vals_count}}",
- "nodes_count": "nb de Nœuds: {{nodes_count}}",
+ "validator_count": "nb de Validateurs: {{val_count}}",
+ "node_count": "nb de Nœuds: {{node_count}}",
"current_stable_version": "Version Stable Actuelle",
"stable_version": "{{stableVersion}}",
"nftoken_minter": "Créateur du NFT",
diff --git a/public/locales/ja-JP/translations.json b/public/locales/ja-JP/translations.json
index 76c396048..953a54eba 100644
--- a/public/locales/ja-JP/translations.json
+++ b/public/locales/ja-JP/translations.json
@@ -443,8 +443,8 @@
"other_chain_destination": null,
"%_of_total_nodes_validators": null,
"version_display": "バージョン: {{version}}",
- "validators_count": "バリデータの数: {{vals_count}}",
- "nodes_count": "ノードの数: {{nodes_count}}",
+ "validator_count": "バリデータの数: {{val_count}}",
+ "node_count": "ノードの数: {{node_count}}",
"current_stable_version": "現在の安定バージョン",
"stable_version": null,
"nftoken_minter": "NFT発行者",
diff --git a/public/locales/ko-KR/translations.json b/public/locales/ko-KR/translations.json
index 73951775e..2094a1fe5 100644
--- a/public/locales/ko-KR/translations.json
+++ b/public/locales/ko-KR/translations.json
@@ -441,8 +441,8 @@
"other_chain_destination": "다른 체인의 목적지",
"%_of_total_nodes_validators": "노드와 검증자의 총 비율",
"version_display": "버전: {{version}}",
- "validators_count": "검증자 수: {{vals_count}}",
- "nodes_count": "노드 수: {{nodes_count}}",
+ "validator_count": "검증자 수: {{val_count}}",
+ "node_count": "노드 수: {{node_count}}",
"current_stable_version": "현재 안정적인 버전",
"stable_version": "{{stableVersion}}",
"nftoken_minter": "NFT 발행자",
diff --git a/src/containers/Network/BarChartVersion.tsx b/src/containers/Network/BarChartVersion.tsx
index b9f63f6c4..b49b0ad13 100644
--- a/src/containers/Network/BarChartVersion.tsx
+++ b/src/containers/Network/BarChartVersion.tsx
@@ -51,13 +51,13 @@ const CustomTooltip = ({
{t('version_display', { version: label })}
- {t('validators_count', {
- vals_count: payload ? payload[0].payload.validatorsCount : 0,
+ {t('validator_count', {
+ val_count: payload?.[0]?.payload?.validatorCount ?? 0,
})}
- {t('nodes_count', {
- nodes_count: payload ? payload[0].payload.nodesCount : 0,
+ {t('node_count', {
+ node_count: payload?.[0]?.payload?.nodeCount ?? 0,
})}
@@ -162,7 +162,7 @@ const BarChartVersion = (props: Props) => {
/>
{
))}
{
- if (!validators) {
- return []
- }
+interface ValidatorStats {
+ validatorPercent: number
+ validatorCount: number
+}
- let totalVals = 0
- let totalNodes = 0
- interface aggregationTypes {
- validatorsCount: number
- nodesCount: number
- }
+interface ValidatorAggregation {
+ [label: string]: ValidatorStats
+}
- const aggregation: Record = {}
+interface NodeAggregation {
+ [label: string]: NodeStats
+}
+
+interface DataAggregation extends ValidatorStats, NodeStats {
+ label: string
+}
+
+export const aggregateValidators = (validators: ValidatorResponse[]) => {
+ let totalVals = 0
+ const aggregation: ValidatorAggregation = {}
validators?.forEach((validator) => {
if (!validator.signing_key) return
const version = validator.server_version
totalVals += 1
if (version) {
if (!aggregation[version]) {
- aggregation[version] = { validatorsCount: 0, nodesCount: 0 }
+ aggregation[version] = { validatorCount: 0, validatorPercent: 0 }
}
- aggregation[version].validatorsCount += 1
+ aggregation[version].validatorCount += 1
}
})
+ for (const label of Object.keys(aggregation)) {
+ aggregation[label].validatorPercent =
+ totalVals > 0 ? (aggregation[label].validatorCount / totalVals) * 100 : 0
+ }
+
+ return aggregation
+}
+export const aggregateNodes = (nodes: NodeResponse[]) => {
+ let totalNodes = 0
+ const aggregation: NodeAggregation = {}
nodes?.forEach((node) => {
const { version } = node
if (!node.node_public_key) return
totalNodes += 1
if (version) {
if (!aggregation[version]) {
- aggregation[version] = { validatorsCount: 0, nodesCount: 0 }
+ aggregation[version] = { nodeCount: 0, nodePercent: 0 }
}
- aggregation[version].nodesCount += 1
+ aggregation[version].nodeCount += 1
}
})
+ for (const label of Object.keys(aggregation)) {
+ aggregation[label].nodePercent =
+ totalNodes > 0 ? (aggregation[label].nodeCount / totalNodes) * 100 : 0
+ }
+
+ return aggregation
+}
- return Object.entries(aggregation)
- .map(([version, counts]) => ({
- label: version ? version.trim() : 'N/A',
- validatorsPercent:
- totalVals > 0 ? (counts.validatorsCount * 100) / totalVals : 0,
- validatorsCount: counts.validatorsCount,
- nodesPercent: totalNodes > 0 ? (counts.nodesCount * 100) / totalNodes : 0,
- nodesCount: counts.nodesCount,
+export const aggregateData = (
+ validatorAggregation: ValidatorAggregation,
+ nodeAggregation: NodeAggregation,
+): DataAggregation[] => {
+ const combinedAggregation: { [label: string]: ValidatorStats & NodeStats } =
+ {}
+ for (const label of Object.keys(validatorAggregation)) {
+ combinedAggregation[label] = {
+ validatorPercent: validatorAggregation[label].validatorPercent,
+ validatorCount: validatorAggregation[label].validatorCount,
+ nodePercent: 0,
+ nodeCount: 0,
+ }
+ }
+
+ for (const label of Object.keys(nodeAggregation)) {
+ if (!combinedAggregation[label]) {
+ combinedAggregation[label] = {
+ validatorPercent: 0,
+ validatorCount: 0,
+ nodePercent: nodeAggregation[label].nodePercent,
+ nodeCount: nodeAggregation[label].nodeCount,
+ }
+ } else {
+ combinedAggregation[label].nodePercent =
+ nodeAggregation[label].nodePercent
+ combinedAggregation[label].nodeCount = nodeAggregation[label].nodeCount
+ }
+ }
+
+ return Object.entries(combinedAggregation)
+ .map(([label, stats]) => ({
+ label,
+ ...stats,
}))
.sort((a, b) => (isEarlierVersion(a.label, b.label) ? -1 : 1))
}
@@ -110,7 +155,9 @@ export const UpgradeStatus = () => {
const [vList, setVList] = useState>({})
const [validations, setValidations] = useState([])
const [unlCount, setUnlCount] = useState(0)
- const [aggregated, setAggregated] = useState([])
+ const [validatorAggregation, setValidatorAggregation] =
+ useState({})
+ const [nodeAggregation, setNodeAggregation] = useState({})
const { t } = useTranslation()
const language = useLanguage()
const network = useContext(NetworkContext)
@@ -145,7 +192,7 @@ export const UpgradeStatus = () => {
)
const fetchData = () => {
- const validatorsReq = axios
+ axios
.get(`${process.env.VITE_DATA_URL}/validators/${network}`)
.then((resp) => resp.data.validators)
.then((validators: ValidatorResponse[]) => {
@@ -153,16 +200,16 @@ export const UpgradeStatus = () => {
validators.forEach((validator) => {
newValidatorList[validator.signing_key] = validator
})
-
setVList(newValidatorList)
setUnlCount(
validators.filter((validator) => Boolean(validator.unl)).length,
)
+ setValidatorAggregation(aggregateValidators(validators))
return Object.values(newValidatorList)
})
.catch((e) => Log.error(e))
- const nodesReq = axios
+ axios
.get(`${process.env.VITE_DATA_URL}/topology/nodes/${network}`)
.then((resp) => resp.data.nodes)
.then((allNodes) => {
@@ -188,12 +235,11 @@ export const UpgradeStatus = () => {
}
return 1
})
+
+ setNodeAggregation(aggregateNodes(nodes))
return nodes
})
.catch((e) => Log.error(e))
- Promise.all([validatorsReq, nodesReq]).then(([validators, nodes]) => {
- setAggregated(aggregateData(validators, nodes))
- })
}
const fetchStableVersion = () => {
@@ -250,9 +296,17 @@ export const UpgradeStatus = () => {
-
-
-
+ {Object.keys(validatorAggregation).length > 0 ||
+ Object.keys(nodeAggregation).length > 0 ? (
+
+
+
+ ) : (
+
+ )}
)
diff --git a/src/containers/Network/test/upgradeStatus.test.js b/src/containers/Network/test/upgradeStatus.test.js
index 79d15a0d4..28d43d480 100644
--- a/src/containers/Network/test/upgradeStatus.test.js
+++ b/src/containers/Network/test/upgradeStatus.test.js
@@ -1,84 +1,162 @@
-import { aggregateData } from '../UpgradeStatus'
+import { mount } from 'enzyme'
+import moxios from 'moxios'
+import WS from 'jest-websocket-mock'
+import { Route } from 'react-router'
+import i18n from '../../../i18n/testConfig'
+import { Network } from '../index'
+import SocketContext from '../../shared/SocketContext'
+import MockWsClient from '../../test/mockWsClient'
+import { QuickHarness } from '../../test/utils'
+import { NETWORK_ROUTE } from '../../App/routes'
+import {
+ aggregateData,
+ aggregateNodes,
+ aggregateValidators,
+} from '../UpgradeStatus'
-describe('UpgradeStatus test functions', () => {
- const undefinedValidatorsData = [
- {
- ledger_index: 74661353,
- ledger_hash:
- '613E298A8C0AEB816D16AA61952E0834BBD9B5E5677EA3E9A2413118EE074363',
+const undefinedValidatorsData = [
+ {
+ ledger_index: 74661353,
+ ledger_hash:
+ '613E298A8C0AEB816D16AA61952E0834BBD9B5E5677EA3E9A2413118EE074363',
+ },
+ {
+ master_key: 'nHUakYHufAvdx5XqTS2F4Pu7i8fQqDqpKqXN2kUGHhBFcG38GNqL',
+ signing_key: 'n9M38x7Sf7epp3gaxgcFxEtwkSc4w2ePb1SgfLiz9bVCr5Lvzrm8',
+ unl: false,
+ domain: 'gerty.one',
+ ledger_index: 74554449,
+ server_version: '1.9.4',
+ agreement_1hour: {
+ missed: 936,
+ total: 936,
+ score: '0.00000',
+ incomplete: false,
},
- {
- master_key: 'nHUakYHufAvdx5XqTS2F4Pu7i8fQqDqpKqXN2kUGHhBFcG38GNqL',
- signing_key: 'n9M38x7Sf7epp3gaxgcFxEtwkSc4w2ePb1SgfLiz9bVCr5Lvzrm8',
- unl: false,
- domain: 'gerty.one',
- ledger_index: 74554449,
- server_version: '1.9.4',
- agreement_1hour: {
- missed: 936,
- total: 936,
- score: '0.00000',
- incomplete: false,
- },
- agreement_24hour: {
- missed: 22338,
- total: 22338,
- score: '0.00000',
- incomplete: false,
- },
- agreement_30day: {
- missed: 263139,
- total: 535427,
- score: '0.50854',
- incomplete: false,
- },
- chain: 'chain.4',
- partial: false,
+ agreement_24hour: {
+ missed: 22338,
+ total: 22338,
+ score: '0.00000',
+ incomplete: false,
},
- ]
-
- const nodesData = [
- {
- node_public_key: 'n9JoeT8XKeBSR8y4D9aDz2PL1DD1j6LQwkRTbH2eFqeRmWYHj2Nw',
- networks: 'dev',
- complete_ledgers: '22085270-29882772',
- ip: '34.208.12.148',
- port: 2459,
- uptime: 1257336,
- version: '1.11.0-rc3',
- server_state: 'full',
- io_latency_ms: 1,
- load_factor_server: '256',
- inbound_count: 4,
- outbound_count: 9,
- lat: '45.82',
- long: '-119.73',
- country_code: 'US',
- country: 'United States',
- region: 'Oregon',
- region_code: 'OR',
- city: 'Boardman',
- postal_code: '97818',
- timezone: 'America/Los_Angeles',
+ agreement_30day: {
+ missed: 263139,
+ total: 535427,
+ score: '0.50854',
+ incomplete: false,
},
- ]
+ chain: 'chain.4',
+ partial: false,
+ },
+]
- it('aggregateData handle edge case', () => {
- expect(aggregateData(undefinedValidatorsData, nodesData)).toEqual([
+const nodesData = [
+ {
+ node_public_key: 'n9JoeT8XKeBSR8y4D9aDz2PL1DD1j6LQwkRTbH2eFqeRmWYHj2Nw',
+ networks: 'dev',
+ complete_ledgers: '22085270-29882772',
+ ip: '34.208.12.148',
+ port: 2459,
+ uptime: 1257336,
+ version: '1.11.0-rc3',
+ server_state: 'full',
+ io_latency_ms: 1,
+ load_factor_server: '256',
+ inbound_count: 4,
+ outbound_count: 9,
+ lat: '45.82',
+ long: '-119.73',
+ country_code: 'US',
+ country: 'United States',
+ region: 'Oregon',
+ region_code: 'OR',
+ city: 'Boardman',
+ postal_code: '97818',
+ timezone: 'America/Los_Angeles',
+ },
+]
+
+describe('UpgradeStatus test functions', () => {
+ it('aggregate data works with validators without keys', () => {
+ const validatorAggregate = aggregateValidators(undefinedValidatorsData)
+ expect(validatorAggregate).toEqual({
+ '1.9.4': { validatorCount: 1, validatorPercent: 100 },
+ })
+ const nodeAggregate = aggregateNodes(nodesData)
+ expect(nodeAggregate).toEqual({
+ '1.11.0-rc3': { nodeCount: 1, nodePercent: 100 },
+ })
+ expect(aggregateData(validatorAggregate, nodeAggregate)).toEqual([
{
label: '1.9.4',
- validatorsCount: 1,
- validatorsPercent: 100,
- nodesCount: 0,
- nodesPercent: 0,
+ validatorCount: 1,
+ validatorPercent: 100,
+ nodeCount: 0,
+ nodePercent: 0,
},
{
label: '1.11.0-rc3',
- validatorsCount: 0,
- validatorsPercent: 0,
- nodesCount: 1,
- nodesPercent: 100,
+ validatorCount: 0,
+ validatorPercent: 0,
+ nodeCount: 1,
+ nodePercent: 100,
},
])
})
})
+
+describe('UpgradeStatus renders', () => {
+ let server
+ let client
+ const WS_URL = 'ws://localhost:1234'
+ const createWrapper = () =>
+ mount(
+
+
+ } />
+
+ ,
+ )
+
+ beforeEach(async () => {
+ window.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+ }))
+ server = new WS(WS_URL, { jsonProtocol: true })
+ client = new MockWsClient(WS_URL)
+ await server.connected
+ moxios.install()
+ })
+
+ afterEach(async () => {
+ moxios.uninstall()
+ server.close()
+ client.close()
+ WS.clean()
+ })
+
+ it('renders without crashing', async () => {
+ const wrapper = createWrapper()
+ wrapper.unmount()
+ })
+
+ it('renders when nodes request errors', async () => {
+ moxios.stubRequest(`${process.env.VITE_DATA_URL}/validators/main`, {
+ status: 200,
+ response: { validators: undefinedValidatorsData },
+ })
+ moxios.stubRequest(`${process.env.VITE_DATA_URL}/topology/nodes/main`, {
+ status: 502,
+ })
+
+ const wrapper = createWrapper()
+ wrapper.update()
+ setTimeout(() => {
+ wrapper.update()
+ expect(wrapper.find('.barchart').length).toEqual(1)
+ })
+ wrapper.unmount()
+ })
+})