feat: Add RPC statistics screen to settings' advanced view
Closes #13264
This commit is contained in:
parent
b38138cc71
commit
1ac52f5d38
|
@ -220,7 +220,7 @@ proc newModule*[T](
|
|||
result, events, accountsService, settingsService, stickersService,
|
||||
profileService, contactsService, aboutService, languageService, privacyService, nodeConfigurationService,
|
||||
devicesService, mailserversService, chatService, ensService, walletAccountService, generalService, communityService,
|
||||
networkService, keycardService, keychainService, tokenService
|
||||
networkService, keycardService, keychainService, tokenService, nodeService
|
||||
)
|
||||
result.stickersModule = stickers_module.newModule(result, events, stickersService, settingsService, walletAccountService,
|
||||
networkService, tokenService, keycardService)
|
||||
|
|
|
@ -23,6 +23,7 @@ import ../../../../app_service/service/community/service as community_service
|
|||
import ../../../../app_service/service/keycard/service as keycard_service
|
||||
import ../../../../app_service/service/keychain/service as keychain_service
|
||||
import ../../../../app_service/service/token/service as token_service
|
||||
import ../../../../app_service/service/node/service as node_service
|
||||
|
||||
import ./profile/module as profile_module
|
||||
import ./contacts/module as contacts_module
|
||||
|
@ -85,7 +86,8 @@ proc newModule*(delegate: delegate_interface.AccessInterface,
|
|||
networkService: network_service.Service,
|
||||
keycardService: keycard_service.Service,
|
||||
keychainService: keychain_service.Service,
|
||||
tokenService: token_service.Service
|
||||
tokenService: token_service.Service,
|
||||
nodeService: node_service.Service
|
||||
): Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
|
@ -112,7 +114,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface,
|
|||
privacyService, accountsService, walletAccountService, keychainService)
|
||||
|
||||
result.walletModule = wallet_module.newModule(result, events, accountsService, walletAccountService, settingsService,
|
||||
networkService, devicesService)
|
||||
networkService, devicesService, nodeService)
|
||||
|
||||
singletonInstance.engine.setRootContextProperty("profileSectionModule", result.viewVariant)
|
||||
|
||||
|
|
|
@ -2,22 +2,26 @@ import io_interface
|
|||
import app/core/eventemitter
|
||||
import app_service/service/wallet_account/service as wallet_account_service
|
||||
import app_service/service/devices/service as devices_service
|
||||
import app_service/service/node/service as node_service
|
||||
|
||||
type
|
||||
Controller* = ref object of RootObj
|
||||
delegate: io_interface.AccessInterface
|
||||
events: EventEmitter
|
||||
walletAccountService: wallet_account_service.Service
|
||||
nodeService: node_service.Service
|
||||
|
||||
proc newController*(
|
||||
delegate: io_interface.AccessInterface,
|
||||
events: EventEmitter,
|
||||
walletAccountService: wallet_account_service.Service,
|
||||
nodeService: node_service.Service
|
||||
): Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.walletAccountService = walletAccountService
|
||||
result.nodeService = nodeService
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
discard
|
||||
|
@ -29,3 +33,9 @@ proc init*(self: Controller) =
|
|||
|
||||
proc hasPairedDevices*(self: Controller): bool =
|
||||
return self.walletAccountService.hasPairedDevices()
|
||||
|
||||
proc getRpcStats*(self: Controller): string =
|
||||
return self.nodeService.getRpcStats()
|
||||
|
||||
proc resetRpcStats*(self: Controller) =
|
||||
self.nodeService.resetRpcStats()
|
||||
|
|
|
@ -57,3 +57,9 @@ method hasPairedDevices*(self: AccessInterface): bool {.base.} =
|
|||
|
||||
method onLocalPairingStatusUpdate*(self: AccessInterface, data: LocalPairingStatus) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getRpcStats*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method resetRpcStats*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
|
|
@ -15,6 +15,7 @@ import app_service/service/wallet_account/service as wallet_account_service
|
|||
import app_service/service/network/service as network_service
|
||||
import app_service/service/settings/service as settings_service
|
||||
import app_service/service/devices/service as devices_service
|
||||
import app_service/service/node/service as node_service
|
||||
|
||||
logScope:
|
||||
topics = "profile-section-wallet-module"
|
||||
|
@ -33,6 +34,7 @@ type
|
|||
accountsService: accounts_service.Service
|
||||
walletAccountService: wallet_account_service.Service
|
||||
devicesService: devices_service.Service
|
||||
nodeService: node_service.Service
|
||||
accountsModule: accounts_module.AccessInterface
|
||||
networksModule: networks_module.AccessInterface
|
||||
keypairImportModule: keypair_import_module.AccessInterface
|
||||
|
@ -44,11 +46,12 @@ proc newModule*(
|
|||
walletAccountService: wallet_account_service.Service,
|
||||
settingsService: settings_service.Service,
|
||||
networkService: network_service.Service,
|
||||
devicesService: devices_service.Service
|
||||
devicesService: devices_service.Service,
|
||||
nodeService: node_service.Service
|
||||
): Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
result.controller = controller.newController(result, events, walletAccountService)
|
||||
result.controller = controller.newController(result, events, walletAccountService, nodeService)
|
||||
result.view = newView(result)
|
||||
result.viewVariant = newQVariant(result.view)
|
||||
result.events = events
|
||||
|
@ -133,3 +136,9 @@ method hasPairedDevices*(self: Module): bool =
|
|||
method onLocalPairingStatusUpdate*(self: Module, data: LocalPairingStatus) =
|
||||
if data.state == LocalPairingState.Finished:
|
||||
self.view.emitHasPairedDevicesChangedSignal()
|
||||
|
||||
method getRpcStats*(self: Module): string =
|
||||
return self.controller.getRpcStats()
|
||||
|
||||
method resetRpcStats*(self: Module) =
|
||||
self.controller.resetRpcStats()
|
||||
|
|
|
@ -55,3 +55,8 @@ QtObject:
|
|||
QtProperty[bool] hasPairedDevices:
|
||||
read = getHasPairedDevices
|
||||
notify = hasPairedDevicesChanged
|
||||
|
||||
proc getRpcStats(self: View): string {.slot.} =
|
||||
return self.delegate.getRpcStats()
|
||||
proc resetRpcStats(self: View) {.slot.} =
|
||||
self.delegate.resetRpcStats()
|
||||
|
|
|
@ -114,3 +114,9 @@ method destroyKeypairImportPopup*(self: AccessInterface) {.base.} =
|
|||
|
||||
method hasPairedDevices*(self: AccessInterface): bool {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getRpcStats*(self: AccessInterface): string {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method resetRpcStats*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
|
|
@ -445,3 +445,9 @@ method hasPairedDevices*(self: Module): bool =
|
|||
proc onLocalPairingStatusUpdate*(self: Module, data: LocalPairingStatus) =
|
||||
if data.state == LocalPairingState.Finished:
|
||||
self.view.emitHasPairedDevicesChangedSignal()
|
||||
|
||||
method getRpcStats*(self: Module): string =
|
||||
return self.view.getRpcStats()
|
||||
|
||||
method resetRpcStats*(self: Module) =
|
||||
self.view.resetRpcStats()
|
||||
|
|
|
@ -233,3 +233,8 @@ QtObject:
|
|||
QtProperty[bool] walletReady:
|
||||
read = getWalletReady
|
||||
notify = walletReadyChanged
|
||||
|
||||
proc getRpcStats*(self: View): string {.slot.} =
|
||||
return self.delegate.getRpcStats()
|
||||
proc resetRpcStats*(self: View) {.slot.} =
|
||||
self.delegate.resetRpcStats()
|
||||
|
|
|
@ -69,3 +69,17 @@ QtObject:
|
|||
proc peerCount*(self: Service): int = self.peers.len
|
||||
|
||||
proc isConnected*(self: Service): bool = self.connected
|
||||
|
||||
proc getRpcStats*(self: Service): string =
|
||||
try:
|
||||
return status_node.getRpcStats()
|
||||
except Exception as e:
|
||||
let errDescription = e.msg
|
||||
error "error: ", errDescription
|
||||
|
||||
proc resetRpcStats*(self: Service) =
|
||||
try:
|
||||
status_node.resetRpcStats()
|
||||
except Exception as e:
|
||||
let errDescription = e.msg
|
||||
error "error: ", errDescription
|
||||
|
|
|
@ -114,7 +114,6 @@ proc hasGas*(self: Service, accountAddress: string, chainId: int, nativeGasSymbo
|
|||
if balance.account == accountAddress and balance.chainId == chainId:
|
||||
if(self.currencyService.parseCurrencyValue(nativeGasSymbol, balance.balance) >= requiredGas):
|
||||
return true
|
||||
break
|
||||
return false
|
||||
|
||||
proc getCurrency*(self: Service): string =
|
||||
|
|
|
@ -40,6 +40,20 @@ proc makePrivateRpcCall*(
|
|||
error "error doing rpc request", methodName = methodName, exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
||||
proc makePrivateRpcCallNoDecode*(
|
||||
methodName: string, inputJSON: JsonNode
|
||||
): string {.raises: [RpcException].} =
|
||||
if DB_BLOCKED_DUE_TO_PROFILE_MIGRATION:
|
||||
debug "DB blocked due to profile migration, unable to proceed with the rpc call", rpc_method=methodName
|
||||
raise newException(RpcException, "db closed due to profile migration")
|
||||
try:
|
||||
debug "NewBE_callPrivateRPCNoDecode", rpc_method=methodName
|
||||
result = status_go.callPrivateRPC($inputJSON)
|
||||
|
||||
except Exception as e:
|
||||
error "error doing rpc request", methodName = methodName, exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
||||
proc callPrivateRPCWithChainId*(
|
||||
methodName: string, chainId: int, payload = %* []
|
||||
): RpcResponse[JsonNode] {.raises: [RpcException, ValueError, Defect, SerializationError].} =
|
||||
|
@ -61,6 +75,16 @@ proc callPrivateRPC*(
|
|||
}
|
||||
return makePrivateRpcCall(methodName, inputJSON)
|
||||
|
||||
proc callPrivateRPCNoDecode*(
|
||||
methodName: string, payload = %* []
|
||||
): string {.raises: [RpcException].} =
|
||||
let inputJSON = %* {
|
||||
"jsonrpc": "2.0",
|
||||
"method": methodName,
|
||||
"params": %payload
|
||||
}
|
||||
return makePrivateRpcCallNoDecode(methodName, inputJSON)
|
||||
|
||||
proc migrateKeyStoreDir*(account: string, hashedPassword: string, oldKeystoreDir: string, multiaccountKeystoreDir: string)
|
||||
{.raises: [RpcException, ValueError, Defect, SerializationError].} =
|
||||
try:
|
||||
|
|
|
@ -14,3 +14,9 @@ proc wakuV2Peers*(): RpcResponse[JsonNode] {.raises: [Exception].} =
|
|||
|
||||
proc sendRPCMessageRaw*(inputJSON: string): string {.raises: [Exception].} =
|
||||
result = callPrivateRPCRaw(inputJSON)
|
||||
|
||||
proc getRpcStats*(): string {.raises: [Exception].} =
|
||||
result = callPrivateRPCNoDecode("rpcstats_getStats")
|
||||
|
||||
proc resetRpcStats*() {.raises: [Exception].} =
|
||||
discard callPrivateRPCNoDecode("rpcstats_reset")
|
||||
|
|
|
@ -293,6 +293,7 @@ StatusSectionLayout {
|
|||
|
||||
messagingStore: root.store.messagingStore
|
||||
advancedStore: root.store.advancedStore
|
||||
walletStore: root.store.walletStore
|
||||
sectionTitle: root.store.getNameForSubsection(Constants.settingsSubsection.advanced)
|
||||
contentWidth: d.contentWidth
|
||||
}
|
||||
|
|
|
@ -148,4 +148,12 @@ QtObject {
|
|||
function updateWatchAccountHiddenFromTotalBalance(address, hideFromTotalBalance) {
|
||||
accountsModule.updateWatchAccountHiddenFromTotalBalance(address, hideFromTotalBalance)
|
||||
}
|
||||
|
||||
function getRpcStats() {
|
||||
return root.walletModule.getRpcStats()
|
||||
}
|
||||
|
||||
function resetRpcStats() {
|
||||
root.walletModule.resetRpcStats()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ SettingsContentBase {
|
|||
|
||||
property MessagingStore messagingStore
|
||||
property AdvancedStore advancedStore
|
||||
property WalletStore walletStore
|
||||
|
||||
Item {
|
||||
id: advancedContainer
|
||||
|
@ -430,6 +431,14 @@ SettingsContentBase {
|
|||
Global.openPopup(changeNumberOfLogsArchived)
|
||||
}
|
||||
}
|
||||
|
||||
StatusSettingsLineButton {
|
||||
id: rpcStatsButton
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: 0
|
||||
text: qsTr("RPC statistics")
|
||||
onClicked: rpcStatsModal.open()
|
||||
}
|
||||
}
|
||||
|
||||
FleetsModal {
|
||||
|
@ -618,5 +627,12 @@ SettingsContentBase {
|
|||
onDecelerationChanged: root.advancedStore.setScrollDeceleration(value)
|
||||
onCustomScrollingChanged: root.advancedStore.setCustomScrollingEnabled(enabled)
|
||||
}
|
||||
|
||||
RPCStatsModal {
|
||||
id: rpcStatsModal
|
||||
|
||||
walletStore: root.walletStore
|
||||
title: rpcStatsButton.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
import QtQuick 2.15
|
||||
import QtQml 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQml.Models 2.15
|
||||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Popups.Dialog 0.1
|
||||
|
||||
import shared.controls 1.0
|
||||
import utils 1.0
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import "../stores"
|
||||
|
||||
|
||||
StatusDialog {
|
||||
id: root
|
||||
|
||||
implicitHeight: 610
|
||||
|
||||
property WalletStore walletStore
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
property int totalCalls
|
||||
property int totalFilteredCalls: countAggregator.value
|
||||
|
||||
function updateModel() {
|
||||
sourceModel.clear()
|
||||
|
||||
const jsonStatsRaw = root.walletStore.getRpcStats()
|
||||
const jsonStats = JSON.parse(jsonStatsRaw)
|
||||
if (jsonStats.result && jsonStats.result.methods) {
|
||||
const methods = jsonStats.result.methods
|
||||
d.totalCalls = jsonStats.result.total
|
||||
|
||||
for (const method in methods) {
|
||||
sourceModel.append({ method: method, count: methods[method] })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resetStats() {
|
||||
root.walletStore.resetRpcStats()
|
||||
updateModel()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
SearchBox {
|
||||
id: searchBox
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 16
|
||||
}
|
||||
|
||||
StatusListView {
|
||||
id: resultsListView
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 2
|
||||
|
||||
ListModel {
|
||||
id: sourceModel
|
||||
}
|
||||
|
||||
header: StatusListItem {
|
||||
title: qsTr("Total")
|
||||
statusListItemTitle.customColor: Theme.palette.directColor1
|
||||
statusListItemLabel.customColor: Theme.palette.directColor1
|
||||
label: qsTr("%1 of %2").arg(d.totalFilteredCalls).arg(d.totalCalls)
|
||||
enabled: false
|
||||
z: 3 // Above delegate (z=1) and above section.delegate (z = 2)
|
||||
}
|
||||
headerPositioning: ListView.OverlayHeader
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
sourceModel: sourceModel
|
||||
sorters: RoleSorter {
|
||||
roleName: "count"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
}
|
||||
|
||||
filters: FastExpressionFilter {
|
||||
function spellingTolerantSearch(data, searchKeyword) {
|
||||
const regex = new RegExp(searchKeyword.split('').join('.{0,1}'), 'i')
|
||||
return regex.test(data)
|
||||
}
|
||||
|
||||
enabled: !!searchBox.text
|
||||
expression: {
|
||||
searchBox.text
|
||||
let keyword = searchBox.text.trim().toUpperCase()
|
||||
return spellingTolerantSearch(model.method, keyword)
|
||||
}
|
||||
expectedRoles: ["method"]
|
||||
}
|
||||
}
|
||||
|
||||
SumAggregator {
|
||||
id: countAggregator
|
||||
|
||||
model: resultsListView.model
|
||||
roleName: "count"
|
||||
}
|
||||
|
||||
delegate: StatusListItem {
|
||||
title: model.method
|
||||
label: model.count
|
||||
enabled: false
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
d.updateModel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: StatusDialogFooter {
|
||||
leftButtons: ObjectModel {
|
||||
StatusButton {
|
||||
text: qsTr("Refresh")
|
||||
onClicked: {
|
||||
d.updateModel()
|
||||
}
|
||||
}
|
||||
StatusButton {
|
||||
text: qsTr("Reset")
|
||||
onClicked: {
|
||||
root.walletStore.resetRpcStats()
|
||||
d.updateModel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rightButtons: ObjectModel {
|
||||
CopyToClipBoardButton {
|
||||
id: copyToClipboardButton
|
||||
|
||||
onCopyClicked: root.walletStore.copyToClipboard(textToCopy)
|
||||
onPressed: function() {
|
||||
let copiedText = "Total" + '\t' + d.totalFilteredCalls + " of " + d.totalCalls + '\n' + '\n'
|
||||
for (let i = 0; i < resultsListView.model.count; i++) {
|
||||
const item = resultsListView.model.get(i)
|
||||
copiedText += item.method + '\t' + item.count + '\n'
|
||||
}
|
||||
textToCopy = copiedText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue