From ecaa9b39c09cdd51761e9625f4822af84078626c Mon Sep 17 00:00:00 2001 From: Michal Iskierko Date: Wed, 10 Jul 2024 15:58:48 +0200 Subject: [PATCH] feat(@desktop/metrics): Initialize centralized metrics Issue #15446 --- src/app/boot/app_controller.nim | 8 +++ src/app_service/service/accounts/service.nim | 2 +- src/app_service/service/metrics/dto.nim | 34 +++++++++++ src/app_service/service/metrics/service.nim | 61 +++++++++++++++++++ src/backend/accounts.nim | 8 ++- .../Onboarding/stores/StartupStore.qml | 8 +++ .../Onboarding/views/KeysMainView.qml | 9 +++ 7 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 src/app_service/service/metrics/dto.nim create mode 100644 src/app_service/service/metrics/service.nim diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index 2aea6e8c96..c54842ea4f 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -34,6 +34,7 @@ import app_service/service/ens/service as ens_service import app_service/service/community_tokens/service as tokens_service import app_service/service/network_connection/service as network_connection_service import app_service/service/shared_urls/service as shared_urls_service +import app_service/service/metrics/service as metrics_service import app/modules/shared_modules/keycard_popup/module as keycard_shared_module import app/modules/startup/module as startup_module @@ -64,6 +65,7 @@ type localAccountSensitiveSettingsVariant: QVariant userProfileVariant: QVariant globalUtilsVariant: QVariant + metricsVariant: QVariant # Services generalService: general_service.Service @@ -101,6 +103,7 @@ type tokensService: tokens_service.Service networkConnectionService: network_connection_service.Service sharedUrlsService: shared_urls_service.Service + metricsService: metrics_service.MetricsService # Modules startupModule: startup_module.AccessInterface @@ -152,6 +155,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.settingsService = settings_service.newService(statusFoundation.events) result.appSettingsVariant = newQVariant(result.settingsService) result.notificationsManager = newNotificationsManager(statusFoundation.events, result.settingsService) + result.metricsService = metrics_service.newService() + result.metricsVariant = newQVariant(result.metricsService) # Global result.localAppSettingsVariant = newQVariant(singletonInstance.localAppSettings) @@ -313,6 +318,7 @@ proc delete*(self: AppController) = self.localAccountSensitiveSettingsVariant.delete self.userProfileVariant.delete self.globalUtilsVariant.delete + self.metricsVariant.delete self.accountsService.delete self.chatService.delete @@ -342,6 +348,7 @@ proc delete*(self: AppController) = self.tokensService.delete self.keycardService.delete self.networkConnectionService.delete + self.metricsService.delete proc disconnectKeychain(self: AppController) = for id in self.keychainConnectionIds: @@ -391,6 +398,7 @@ proc startupDidLoad*(self: AppController) = singletonInstance.engine.setRootContextProperty("localAppSettings", self.localAppSettingsVariant) singletonInstance.engine.setRootContextProperty("localAccountSettings", self.localAccountSettingsVariant) singletonInstance.engine.setRootContextProperty("globalUtils", self.globalUtilsVariant) + singletonInstance.engine.setRootContextProperty("metrics", self.metricsVariant) singletonInstance.engine.load(newQUrl("qrc:///main.qml")) # We need to init a language service once qml is loaded diff --git a/src/app_service/service/accounts/service.nim b/src/app_service/service/accounts/service.nim index ef28424113..22e95b1c70 100644 --- a/src/app_service/service/accounts/service.nim +++ b/src/app_service/service/accounts/service.nim @@ -130,7 +130,7 @@ QtObject: try: let response = status_account.openedAccounts(main_constants.STATUSGODIR) - self.accounts = map(response.result.getElems(), proc(x: JsonNode): AccountDto = toAccountDto(x)) + self.accounts = map(response.result{"accounts"}.getElems(), proc(x: JsonNode): AccountDto = toAccountDto(x)) return self.accounts diff --git a/src/app_service/service/metrics/dto.nim b/src/app_service/service/metrics/dto.nim new file mode 100644 index 0000000000..2afbcc03e4 --- /dev/null +++ b/src/app_service/service/metrics/dto.nim @@ -0,0 +1,34 @@ +import json, chronicles +include ../../common/json_utils + +logScope: + topics = "metrics" + +type CentralizedMetricsInfoDto* = object + enabled*: bool + userConfirmed*: bool + +proc toCentralizedMetricsInfoDto*(jsonObj: JsonNode): CentralizedMetricsInfoDto = + result = CentralizedMetricsInfoDto() + discard jsonObj.getProp("enabled", result.enabled) + discard jsonObj.getProp("userConfirmed", result.userConfirmed) + +type CentralizedMetricDto* = object + id*: string + userId*: string + eventName*: string + eventValue*: JsonNode + timestamp*: int64 + platform*: string + appVersion*: string + +proc toJsonNode*(self: CentralizedMetricDto): JsonNode = + result = %* { + "id": self.id, + "userId": self.userId, + "eventName": self.eventName, + "eventValue": self.eventValue, + "timestamp": self.timestamp, + "platform": self.platform, + "appVersion": self.appVersion, + } \ No newline at end of file diff --git a/src/app_service/service/metrics/service.nim b/src/app_service/service/metrics/service.nim new file mode 100644 index 0000000000..3c2c7789bc --- /dev/null +++ b/src/app_service/service/metrics/service.nim @@ -0,0 +1,61 @@ +import NimQml, json, chronicles, times +include ../../common/json_utils + +import backend/response_type +import status_go +import constants +import ./dto + +logScope: + topics = "metrics" + +QtObject: + type MetricsService* = ref object of QObject + + proc delete*(self: MetricsService) = + self.QObject.delete + + proc newService*(): MetricsService = + new(result, delete) + result.QObject.setup + + proc toggleCentralizedMetrics*(self: MetricsService, enabled: bool) {.slot.} = + try: + let payload = %* {"enabled": enabled} + let response = status_go.toggleCentralizedMetrics($payload) + let jsonObj = response.parseJson + if jsonObj.hasKey("error"): + error "toggleCentralizedMetrics", errorMsg=jsonObj["error"].getStr + except Exception: + discard + + proc isCentralizedMetricsEnabled*(self: MetricsService): bool {.slot.} = + try: + let response = status_go.centralizedMetricsInfo() + let jsonObj = response.parseJson + if jsonObj.hasKey("error"): + error "isCentralizedMetricsEnabled", errorMsg=jsonObj["error"].getStr + return false + let metricsInfo = toCentralizedMetricsInfoDto(jsonObj) + return metricsInfo.enabled + except Exception: + return false + + # for testing, needs to be discussed + proc addCentralizedMetric*(self: MetricsService) = + try: + var metric = CentralizedMetricDto() + metric.userId = "123456" + metric.eventName = "desktop-event" + metric.eventValue = parseJson("""{"action": "section-changed"}""") + metric.timestamp = now().toTime().toUnix() + metric.platform = hostOS + metric.appVersion = APP_VERSION + + let payload = %* {"metric": metric.toJsonNode} + let response = status_go.addCentralizedMetric($payload) + let jsonObj = response.parseJson + if jsonObj.hasKey("error"): + error "addCentralizedMetric", errorMsg=jsonObj["error"].getStr + except Exception: + discard \ No newline at end of file diff --git a/src/backend/accounts.nim b/src/backend/accounts.nim index ef291ec343..588b711050 100644 --- a/src/backend/accounts.nim +++ b/src/backend/accounts.nim @@ -1,4 +1,4 @@ -import json, json_serialization, chronicles, strutils +import json, json_serialization, chronicles, strutils, std/os import ./core, ../app_service/common/utils import ../app_service/service/wallet_account/dto/account_dto import ../app_service/service/accounts/dto/login_request @@ -260,9 +260,11 @@ proc createAccountFromPrivateKey*(privateKey: string): RpcResponse[JsonNode] = proc openedAccounts*(path: string): RpcResponse[JsonNode] = try: - let response = status_go.openAccounts(path) + let mixPanelAppId = getEnv("MIXPANEL_APP_ID") + let mixPanelToken = getEnv("MIXPANEL_TOKEN") + let payload = %* {"dataDir": path, "mixpanelAppId": mixPanelAppId, "mixpanelToken": mixPanelToken} + let response = status_go.initializeApplication($payload) result.result = Json.decode(response, JsonNode) - except RpcException as e: error "error doing rpc request", methodName = "openedAccounts", exception=e.msg raise newException(RpcException, e.msg) diff --git a/ui/app/AppLayouts/Onboarding/stores/StartupStore.qml b/ui/app/AppLayouts/Onboarding/stores/StartupStore.qml index e24b060022..21ec5ca20e 100644 --- a/ui/app/AppLayouts/Onboarding/stores/StartupStore.qml +++ b/ui/app/AppLayouts/Onboarding/stores/StartupStore.qml @@ -115,6 +115,14 @@ QtObject { return root.startupModuleInst.getSeedPhrase() } + function toggleCentralizedMetrics(enabled) { + metrics.toggleCentralizedMetrics(enabled) + } + + function isCentralizedMetricsEnabled() { + return metrics.isCentralizedMetricsEnabled() + } + function validateLocalPairingConnectionString(connectionString) { return root.startupModuleInst.validateLocalPairingConnectionString(connectionString) } diff --git a/ui/app/AppLayouts/Onboarding/views/KeysMainView.qml b/ui/app/AppLayouts/Onboarding/views/KeysMainView.qml index d839484b25..2e6b8bc16c 100644 --- a/ui/app/AppLayouts/Onboarding/views/KeysMainView.qml +++ b/ui/app/AppLayouts/Onboarding/views/KeysMainView.qml @@ -25,6 +25,7 @@ Item { if (button1.visible) { button1.forceActiveFocus() } + enableCentricMetrics.checked = root.startupStore.isCentralizedMetricsEnabled() } QtObject { @@ -301,12 +302,20 @@ Item { } } + StatusCheckBox { + id: enableCentricMetrics + text: qsTr("Enable centric metrics") + Layout.alignment: Qt.AlignHCenter + onToggled: root.startupStore.toggleCentralizedMetrics(checked) + } + Item { Layout.fillWidth: true Layout.fillHeight: true } } + states: [ State { name: Constants.startupState.welcomeOldStatusUser