From 3934675f1595ed2fee2297d7d1cc96beaaa50c9e Mon Sep 17 00:00:00 2001 From: Andrew Kolos Date: Tue, 28 Jan 2025 08:49:54 -0800 Subject: [PATCH 1/3] Send `enabled_features` as an event parameter rather than a user property --- pkgs/unified_analytics/lib/src/analytics.dart | 7 +++- pkgs/unified_analytics/lib/src/event.dart | 1 - .../lib/src/user_property.dart | 3 -- pkgs/unified_analytics/lib/src/utils.dart | 6 ++- .../test/unified_analytics_test.dart | 40 ++++++++++++++----- 5 files changed, 41 insertions(+), 16 deletions(-) diff --git a/pkgs/unified_analytics/lib/src/analytics.dart b/pkgs/unified_analytics/lib/src/analytics.dart index cc8b4bc11..8b9fc11db 100644 --- a/pkgs/unified_analytics/lib/src/analytics.dart +++ b/pkgs/unified_analytics/lib/src/analytics.dart @@ -339,6 +339,7 @@ class AnalyticsImpl implements Analytics { final File _clientIdFile; final UserProperty _userProperty; final LogHandler _logHandler; + final String? _enabledFeatures; /// Tells the client if they need to show a message to the /// user; this will return true if it is the first time the @@ -406,8 +407,8 @@ class AnalyticsImpl implements Analytics { truncateStringToLength(io.Platform.operatingSystemVersion, 36), locale: io.Platform.localeName, clientIde: clientIde, - enabledFeatures: enabledFeatures, ), + _enabledFeatures = enabledFeatures, _configHandler = ConfigHandler( homeDirectory: homeDirectory, configFile: homeDirectory @@ -613,6 +614,7 @@ class AnalyticsImpl implements Analytics { eventName: event.eventName, eventData: event.eventData, userProperty: _userProperty, + enabledFeatures: _enabledFeatures, ); if (_enableAsserts) checkBody(body); @@ -654,6 +656,7 @@ class AnalyticsImpl implements Analytics { eventName: collectionEvent.eventName, eventData: collectionEvent.eventData, userProperty: _userProperty, + enabledFeatures: _enabledFeatures, ); _logHandler.save(data: body); @@ -664,6 +667,7 @@ class AnalyticsImpl implements Analytics { clientId: clientId, eventName: collectionEvent.eventName, eventData: collectionEvent.eventData, + enabledFeatures: _enabledFeatures, userProperty: _userProperty, ); @@ -774,6 +778,7 @@ class FakeAnalytics extends AnalyticsImpl { eventName: event.eventName, eventData: event.eventData, userProperty: _userProperty, + enabledFeatures: _enabledFeatures, ); if (_enableAsserts) checkBody(body); diff --git a/pkgs/unified_analytics/lib/src/event.dart b/pkgs/unified_analytics/lib/src/event.dart index ed3066596..b0d0a9f8d 100644 --- a/pkgs/unified_analytics/lib/src/event.dart +++ b/pkgs/unified_analytics/lib/src/event.dart @@ -569,7 +569,6 @@ final class Event { if (reloadVMTimeInMs != null) 'reloadVMTimeInMs': reloadVMTimeInMs, }; - // TODO: eliasyishak, add better dartdocs to explain each param /// Event that is emitted periodically to report the number of times each lint /// has been enabled. /// diff --git a/pkgs/unified_analytics/lib/src/user_property.dart b/pkgs/unified_analytics/lib/src/user_property.dart index f0e177d09..a607b07e9 100644 --- a/pkgs/unified_analytics/lib/src/user_property.dart +++ b/pkgs/unified_analytics/lib/src/user_property.dart @@ -21,7 +21,6 @@ class UserProperty { final String hostOsVersion; final String locale; final String? clientIde; - final String? enabledFeatures; final File sessionFile; @@ -43,7 +42,6 @@ class UserProperty { required this.hostOsVersion, required this.locale, required this.clientIde, - required this.enabledFeatures, required this.sessionFile, }); @@ -157,6 +155,5 @@ class UserProperty { 'host_os_version': hostOsVersion, 'locale': locale, 'client_ide': clientIde, - 'enabled_features': enabledFeatures, }; } diff --git a/pkgs/unified_analytics/lib/src/utils.dart b/pkgs/unified_analytics/lib/src/utils.dart index 41848a204..182facb6d 100644 --- a/pkgs/unified_analytics/lib/src/utils.dart +++ b/pkgs/unified_analytics/lib/src/utils.dart @@ -76,13 +76,17 @@ Map generateRequestBody({ required DashEvent eventName, required Map eventData, required UserProperty userProperty, + required String? enabledFeatures, }) => { 'client_id': clientId, 'events': >[ { 'name': eventName.label, - 'params': eventData, + 'params': { + ...eventData, + if (enabledFeatures != null) 'enabled_features': enabledFeatures, + }, } ], 'user_properties': userProperty.preparePayload() diff --git a/pkgs/unified_analytics/test/unified_analytics_test.dart b/pkgs/unified_analytics/test/unified_analytics_test.dart index 2e78df426..cd5b94210 100644 --- a/pkgs/unified_analytics/test/unified_analytics_test.dart +++ b/pkgs/unified_analytics/test/unified_analytics_test.dart @@ -498,7 +498,7 @@ void main() { # All other lines are configuration lines. They have # the form "name=value". If multiple lines contain # the same configuration name with different values, -# the parser will default to a conservative value. +# the parser will default to a conservative value. # DISABLING TELEMETRY REPORTING # @@ -548,7 +548,7 @@ reporting=1 # All other lines are configuration lines. They have # the form "name=value". If multiple lines contain # the same configuration name with different values, -# the parser will default to a conservative value. +# the parser will default to a conservative value. # DISABLING TELEMETRY REPORTING # @@ -632,7 +632,7 @@ ${initialTool.label}=$dateStamp,$toolsMessageVersion ); }); - test('Check that UserProperty class has all the necessary keys', () { + test('The UserProperty class has all the necessary keys', () { const userPropertyKeys = [ 'session_id', 'flutter_channel', @@ -645,7 +645,6 @@ ${initialTool.label}=$dateStamp,$toolsMessageVersion 'host_os_version', 'locale', 'client_ide', - 'enabled_features', ]; expect(analytics.userPropertyMap.keys.length, userPropertyKeys.length, reason: 'There should only be ${userPropertyKeys.length} keys'); @@ -826,6 +825,7 @@ ${initialTool.label}=$dateStamp,$toolsMessageVersion eventName: DashEvent.hotReloadTime, eventData: eventData, userProperty: analytics.userProperty, + enabledFeatures: 'enable-native-assets', ); // Checks for the top level keys @@ -833,8 +833,6 @@ ${initialTool.label}=$dateStamp,$toolsMessageVersion reason: '"client_id" is required at the top level'); expect(body.containsKey('events'), true, reason: '"events" is required at the top level'); - expect(body.containsKey('user_properties'), true, - reason: '"user_properties" is required at the top level'); // Regex for the client id final clientIdPattern = RegExp( @@ -846,14 +844,36 @@ ${initialTool.label}=$dateStamp,$toolsMessageVersion expect(clientIdPattern.hasMatch(body['client_id'] as String), true, reason: 'The client id is not properly formatted, ie ' '46cc0ba6-f604-4fd9-aa2f-8a20beb24cd4'); - expect( - (body['events'][0] as Map).containsKey('name'), true, + expect(body['events'][0] as Map, contains('name'), reason: 'Each event in the events array needs a name'); - expect( - (body['events'][0] as Map).containsKey('params'), true, + expect(body['events'][0] as Map, contains('params'), reason: 'Each event in the events array needs a params key'); }); + test( + 'The list of enabled features is included as an event parameter in every sent event', + () { + final eventData = { + 'time': 5, + 'command': 'run', + }; + + final Map body = generateRequestBody( + clientId: Uuid().generateV4(), + eventName: DashEvent.hotReloadTime, + eventData: eventData, + userProperty: analytics.userProperty, + enabledFeatures: 'enable-native-assets', + ); + + expect((body['events'][0] as Map)['params'], + contains('enabled_features')); + expect( + (body['events'][0] as Map)['params'] + ['enabled_features'], + 'enable-native-assets'); + }); + test('Check that log file is correctly persisting events sent', () { final int numberOfEvents = max((kLogFileLength * 0.1).floor(), 5); From d0bac9f46ec1b936318450e9ca444352399e7aa4 Mon Sep 17 00:00:00 2001 From: Andrew Kolos Date: Tue, 28 Jan 2025 09:54:28 -0800 Subject: [PATCH 2/3] make `enabled_features` into a reserved event parameter name --- pkgs/unified_analytics/lib/src/event.dart | 576 ++++++++++++---------- 1 file changed, 321 insertions(+), 255 deletions(-) diff --git a/pkgs/unified_analytics/lib/src/event.dart b/pkgs/unified_analytics/lib/src/event.dart index b0d0a9f8d..158ea975a 100644 --- a/pkgs/unified_analytics/lib/src/event.dart +++ b/pkgs/unified_analytics/lib/src/event.dart @@ -8,15 +8,27 @@ import 'enums.dart'; final class Event { final DashEvent eventName; - final Map eventData; + late final Map eventData; + + Event._({required this.eventName, required this.eventData}) { + if (eventData.containsKey('enabled_features')) { + throw ArgumentError.value( + eventData, + 'eventData', + 'The enabled_features key is reserved and cannot ' + "be used as part of an event's data.", + ); + } + } /// Event that is emitted whenever a user has opted in /// or out of the analytics collection. /// /// [status] - boolean value where `true` indicates user is opting in. Event.analyticsCollectionEnabled({required bool status}) - : eventName = DashEvent.analyticsCollectionEnabled, - eventData = {'status': status}; + : this._( + eventName: DashEvent.analyticsCollectionEnabled, + eventData: {'status': status}); /// Event that is emitted when an error occurs within /// `package:unified_analytics`, tools that are using this package @@ -35,12 +47,14 @@ final class Event { required String workflow, required String error, String? description, - }) : eventName = DashEvent.analyticsException, - eventData = { - 'workflow': workflow, - 'error': error, - if (description != null) 'description': description, - }; + }) : this._( + eventName: DashEvent.analyticsException, + eventData: { + 'workflow': workflow, + 'error': error, + if (description != null) 'description': description, + }, + ); /// This is for various workflows within the flutter tool related /// to iOS and macOS workflows. @@ -54,12 +68,14 @@ final class Event { required String workflow, required String parameter, String? result, - }) : eventName = DashEvent.appleUsageEvent, - eventData = { - 'workflow': workflow, - 'parameter': parameter, - if (result != null) 'result': result, - }; + }) : this._( + eventName: DashEvent.appleUsageEvent, + eventData: { + 'workflow': workflow, + 'parameter': parameter, + if (result != null) 'result': result, + }, + ); /// Event that is emitted periodically to report the performance of the /// analysis server's handling of a specific kind of notification from the @@ -78,12 +94,14 @@ final class Event { required String duration, required String latency, required String method, - }) : eventName = DashEvent.clientNotification, - eventData = { - 'duration': duration, - 'latency': latency, - 'method': method, - }; + }) : this._( + eventName: DashEvent.clientNotification, + eventData: { + 'duration': duration, + 'latency': latency, + 'method': method, + }, + ); /// Event that is emitted periodically to report the performance of the /// analysis server's handling of a specific kind of request from the client. @@ -138,19 +156,21 @@ final class Event { String? included, String? openWorkspacePaths, String? removed, - }) : eventName = DashEvent.clientRequest, - eventData = { - if (added != null) 'added': added, - 'duration': duration, - if (excluded != null) 'excluded': excluded, - if (files != null) 'files': files, - if (included != null) 'included': included, - 'latency': latency, - 'method': method, - if (openWorkspacePaths != null) - 'openWorkspacePaths': openWorkspacePaths, - if (removed != null) 'removed': removed, - }; + }) : this._( + eventName: DashEvent.clientRequest, + eventData: { + if (added != null) 'added': added, + 'duration': duration, + if (excluded != null) 'excluded': excluded, + if (files != null) 'files': files, + if (included != null) 'included': included, + 'latency': latency, + 'method': method, + if (openWorkspacePaths != null) + 'openWorkspacePaths': openWorkspacePaths, + if (removed != null) 'removed': removed, + }, + ); /// An event that reports when the code size measurement is run /// via `--analyze-size`. @@ -158,10 +178,12 @@ final class Event { /// [platform] - string identifier for which platform was run "ios", "apk", /// "aab", etc. Event.codeSizeAnalysis({required String platform}) - : eventName = DashEvent.codeSizeAnalysis, - eventData = { - 'platform': platform, - }; + : this._( + eventName: DashEvent.codeSizeAnalysis, + eventData: { + 'platform': platform, + }, + ); /// Event that is emitted periodically to report the number of times a given /// command has been executed. @@ -172,11 +194,13 @@ final class Event { Event.commandExecuted({ required int count, required String name, - }) : eventName = DashEvent.commandExecuted, - eventData = { - 'count': count, - 'name': name, - }; + }) : this._( + eventName: DashEvent.commandExecuted, + eventData: { + 'count': count, + 'name': name, + }, + ); /// Event to capture usage values for different flutter commands. /// @@ -228,52 +252,59 @@ final class Event { bool? runEnableImpeller, String? runIOSInterfaceType, bool? runIsTest, - }) : eventName = DashEvent.commandUsageValues, - eventData = { - 'workflow': workflow, - 'commandHasTerminal': commandHasTerminal, - if (buildBundleTargetPlatform != null) - 'buildBundleTargetPlatform': buildBundleTargetPlatform, - if (buildBundleIsModule != null) - 'buildBundleIsModule': buildBundleIsModule, - if (buildAarProjectType != null) - 'buildAarProjectType': buildAarProjectType, - if (buildAarTargetPlatform != null) - 'buildAarTargetPlatform': buildAarTargetPlatform, - if (buildApkTargetPlatform != null) - 'buildApkTargetPlatform': buildApkTargetPlatform, - if (buildApkBuildMode != null) 'buildApkBuildMode': buildApkBuildMode, - if (buildApkSplitPerAbi != null) - 'buildApkSplitPerAbi': buildApkSplitPerAbi, - if (buildAppBundleTargetPlatform != null) - 'buildAppBundleTargetPlatform': buildAppBundleTargetPlatform, - if (buildAppBundleBuildMode != null) - 'buildAppBundleBuildMode': buildAppBundleBuildMode, - if (createProjectType != null) 'createProjectType': createProjectType, - if (createAndroidLanguage != null) - 'createAndroidLanguage': createAndroidLanguage, - if (createIosLanguage != null) 'createIosLanguage': createIosLanguage, - if (packagesNumberPlugins != null) - 'packagesNumberPlugins': packagesNumberPlugins, - if (packagesProjectModule != null) - 'packagesProjectModule': packagesProjectModule, - if (packagesAndroidEmbeddingVersion != null) - 'packagesAndroidEmbeddingVersion': packagesAndroidEmbeddingVersion, - if (runIsEmulator != null) 'runIsEmulator': runIsEmulator, - if (runTargetName != null) 'runTargetName': runTargetName, - if (runTargetOsVersion != null) - 'runTargetOsVersion': runTargetOsVersion, - if (runModeName != null) 'runModeName': runModeName, - if (runProjectModule != null) 'runProjectModule': runProjectModule, - if (runProjectHostLanguage != null) - 'runProjectHostLanguage': runProjectHostLanguage, - if (runAndroidEmbeddingVersion != null) - 'runAndroidEmbeddingVersion': runAndroidEmbeddingVersion, - if (runEnableImpeller != null) 'runEnableImpeller': runEnableImpeller, - if (runIOSInterfaceType != null) - 'runIOSInterfaceType': runIOSInterfaceType, - if (runIsTest != null) 'runIsTest': runIsTest, - }; + }) : this._( + eventName: DashEvent.commandUsageValues, + eventData: { + 'workflow': workflow, + 'commandHasTerminal': commandHasTerminal, + if (buildBundleTargetPlatform != null) + 'buildBundleTargetPlatform': buildBundleTargetPlatform, + if (buildBundleIsModule != null) + 'buildBundleIsModule': buildBundleIsModule, + if (buildAarProjectType != null) + 'buildAarProjectType': buildAarProjectType, + if (buildAarTargetPlatform != null) + 'buildAarTargetPlatform': buildAarTargetPlatform, + if (buildApkTargetPlatform != null) + 'buildApkTargetPlatform': buildApkTargetPlatform, + if (buildApkBuildMode != null) + 'buildApkBuildMode': buildApkBuildMode, + if (buildApkSplitPerAbi != null) + 'buildApkSplitPerAbi': buildApkSplitPerAbi, + if (buildAppBundleTargetPlatform != null) + 'buildAppBundleTargetPlatform': buildAppBundleTargetPlatform, + if (buildAppBundleBuildMode != null) + 'buildAppBundleBuildMode': buildAppBundleBuildMode, + if (createProjectType != null) + 'createProjectType': createProjectType, + if (createAndroidLanguage != null) + 'createAndroidLanguage': createAndroidLanguage, + if (createIosLanguage != null) + 'createIosLanguage': createIosLanguage, + if (packagesNumberPlugins != null) + 'packagesNumberPlugins': packagesNumberPlugins, + if (packagesProjectModule != null) + 'packagesProjectModule': packagesProjectModule, + if (packagesAndroidEmbeddingVersion != null) + 'packagesAndroidEmbeddingVersion': + packagesAndroidEmbeddingVersion, + if (runIsEmulator != null) 'runIsEmulator': runIsEmulator, + if (runTargetName != null) 'runTargetName': runTargetName, + if (runTargetOsVersion != null) + 'runTargetOsVersion': runTargetOsVersion, + if (runModeName != null) 'runModeName': runModeName, + if (runProjectModule != null) 'runProjectModule': runProjectModule, + if (runProjectHostLanguage != null) + 'runProjectHostLanguage': runProjectHostLanguage, + if (runAndroidEmbeddingVersion != null) + 'runAndroidEmbeddingVersion': runAndroidEmbeddingVersion, + if (runEnableImpeller != null) + 'runEnableImpeller': runEnableImpeller, + if (runIOSInterfaceType != null) + 'runIOSInterfaceType': runIOSInterfaceType, + if (runIsTest != null) 'runIsTest': runIsTest, + }, + ); /// Event that is emitted on shutdown to report the structure of the analysis /// contexts created immediately after startup. @@ -322,20 +353,22 @@ final class Event { required int transitiveFileLineCount, required int transitiveFileUniqueCount, required int transitiveFileUniqueLineCount, - }) : eventName = DashEvent.contextStructure, - eventData = { - 'contextsFromBothFiles': contextsFromBothFiles, - 'contextsFromOptionsFiles': contextsFromOptionsFiles, - 'contextsFromPackagesFiles': contextsFromPackagesFiles, - 'contextsWithoutFiles': contextsWithoutFiles, - 'immediateFileCount': immediateFileCount, - 'immediateFileLineCount': immediateFileLineCount, - 'numberOfContexts': numberOfContexts, - 'transitiveFileCount': transitiveFileCount, - 'transitiveFileLineCount': transitiveFileLineCount, - 'transitiveFileUniqueCount': transitiveFileUniqueCount, - 'transitiveFileUniqueLineCount': transitiveFileUniqueLineCount, - }; + }) : this._( + eventName: DashEvent.contextStructure, + eventData: { + 'contextsFromBothFiles': contextsFromBothFiles, + 'contextsFromOptionsFiles': contextsFromOptionsFiles, + 'contextsFromPackagesFiles': contextsFromPackagesFiles, + 'contextsWithoutFiles': contextsWithoutFiles, + 'immediateFileCount': immediateFileCount, + 'immediateFileLineCount': immediateFileLineCount, + 'numberOfContexts': numberOfContexts, + 'transitiveFileCount': transitiveFileCount, + 'transitiveFileLineCount': transitiveFileLineCount, + 'transitiveFileUniqueCount': transitiveFileUniqueCount, + 'transitiveFileUniqueLineCount': transitiveFileUniqueLineCount, + }, + ); /// Event that is emitted when a Dart CLI command has been executed. /// @@ -349,12 +382,14 @@ final class Event { required String name, required String enabledExperiments, int? exitCode, - }) : eventName = DashEvent.dartCliCommandExecuted, - eventData = { - 'name': name, - 'enabledExperiments': enabledExperiments, - if (exitCode != null) 'exitCode': exitCode, - }; + }) : this._( + eventName: DashEvent.dartCliCommandExecuted, + eventData: { + 'name': name, + 'enabledExperiments': enabledExperiments, + if (exitCode != null) 'exitCode': exitCode, + }, + ); /// Event that is sent from DevTools for various different actions as /// indicated by the [eventCategory]. @@ -388,31 +423,33 @@ final class Event { String? ideLaunchedFeature, String? isWasm, CustomMetrics? additionalMetrics, - }) : eventName = DashEvent.devtoolsEvent, - eventData = { - 'screen': screen, - 'eventCategory': eventCategory, - 'label': label, - 'value': value, - - 'userInitiatedInteraction': userInitiatedInteraction, - - // Optional parameters - if (g3Username != null) 'g3Username': g3Username, - if (userApp != null) 'userApp': userApp, - if (userBuild != null) 'userBuild': userBuild, - if (userPlatform != null) 'userPlatform': userPlatform, - if (devtoolsPlatform != null) 'devtoolsPlatform': devtoolsPlatform, - if (devtoolsChrome != null) 'devtoolsChrome': devtoolsChrome, - if (devtoolsVersion != null) 'devtoolsVersion': devtoolsVersion, - if (ideLaunched != null) 'ideLaunched': ideLaunched, - if (isExternalBuild != null) 'isExternalBuild': isExternalBuild, - if (isEmbedded != null) 'isEmbedded': isEmbedded, - if (ideLaunchedFeature != null) - 'ideLaunchedFeature': ideLaunchedFeature, - if (isWasm != null) 'isWasm': isWasm, - if (additionalMetrics != null) ...additionalMetrics.toMap(), - }; + }) : this._( + eventName: DashEvent.devtoolsEvent, + eventData: { + 'screen': screen, + 'eventCategory': eventCategory, + 'label': label, + 'value': value, + + 'userInitiatedInteraction': userInitiatedInteraction, + + // Optional parameters + if (g3Username != null) 'g3Username': g3Username, + if (userApp != null) 'userApp': userApp, + if (userBuild != null) 'userBuild': userBuild, + if (userPlatform != null) 'userPlatform': userPlatform, + if (devtoolsPlatform != null) 'devtoolsPlatform': devtoolsPlatform, + if (devtoolsChrome != null) 'devtoolsChrome': devtoolsChrome, + if (devtoolsVersion != null) 'devtoolsVersion': devtoolsVersion, + if (ideLaunched != null) 'ideLaunched': ideLaunched, + if (isExternalBuild != null) 'isExternalBuild': isExternalBuild, + if (isEmbedded != null) 'isEmbedded': isEmbedded, + if (ideLaunchedFeature != null) + 'ideLaunchedFeature': ideLaunchedFeature, + if (isWasm != null) 'isWasm': isWasm, + if (additionalMetrics != null) ...additionalMetrics.toMap(), + }, + ); /// Event that contains the results for a specific doctor validator. /// @@ -435,14 +472,16 @@ final class Event { required bool partOfGroupedValidator, required int doctorInvocationId, String? statusInfo, - }) : eventName = DashEvent.doctorValidatorResult, - eventData = { - 'validatorName': validatorName, - 'result': result, - 'partOfGroupedValidator': partOfGroupedValidator, - 'doctorInvocationId': doctorInvocationId, - if (statusInfo != null) 'statusInfo': statusInfo, - }; + }) : this._( + eventName: DashEvent.doctorValidatorResult, + eventData: { + 'validatorName': validatorName, + 'result': result, + 'partOfGroupedValidator': partOfGroupedValidator, + 'doctorInvocationId': doctorInvocationId, + if (statusInfo != null) 'statusInfo': statusInfo, + }, + ); /// Generic event for all dash tools to use when encountering an /// exception that we want to log. @@ -452,11 +491,13 @@ final class Event { Event.exception({ required String exception, Map data = const {}, - }) : eventName = DashEvent.exception, - eventData = { - 'exception': exception, - ...Map.from(data)..removeWhere((key, value) => value == null), - }; + }) : this._( + eventName: DashEvent.exception, + eventData: { + 'exception': exception, + ...Map.from(data)..removeWhere((key, value) => value == null), + }, + ); /// Event that is emitted from the flutter tool when a build invocation /// has been run by the user. @@ -479,14 +520,16 @@ final class Event { String? command, String? settings, String? error, - }) : eventName = DashEvent.flutterBuildInfo, - eventData = { - 'label': label, - 'buildType': buildType, - if (command != null) 'command': command, - if (settings != null) 'settings': settings, - if (error != null) 'error': error, - }; + }) : this._( + eventName: DashEvent.flutterBuildInfo, + eventData: { + 'label': label, + 'buildType': buildType, + if (command != null) 'command': command, + if (settings != null) 'settings': settings, + if (error != null) 'error': error, + }, + ); /// Provides information about which flutter command was run /// and whether it was successful. @@ -504,19 +547,23 @@ final class Event { required String result, required bool commandHasTerminal, int? maxRss, - }) : eventName = DashEvent.flutterCommandResult, - eventData = { - 'commandPath': commandPath, - 'result': result, - 'commandHasTerminal': commandHasTerminal, - if (maxRss != null) 'maxRss': maxRss, - }; + }) : this._( + eventName: DashEvent.flutterCommandResult, + eventData: { + 'commandPath': commandPath, + 'result': result, + 'commandHasTerminal': commandHasTerminal, + if (maxRss != null) 'maxRss': maxRss, + }, + ); // TODO: eliasyishak, remove this or replace once we have a generic // timing event that can be used by potentially more than one DashTool Event.hotReloadTime({required int timeMs}) - : eventName = DashEvent.hotReloadTime, - eventData = {'timeMs': timeMs}; + : this._( + eventName: DashEvent.hotReloadTime, + eventData: {'timeMs': timeMs}, + ); /// Events to be sent for the Flutter Hot Runner. Event.hotRunnerInfo({ @@ -539,35 +586,38 @@ final class Event { int? scannedSourcesCount, int? reassembleTimeInMs, int? reloadVMTimeInMs, - }) : eventName = DashEvent.hotRunnerInfo, - eventData = { - 'label': label, - 'targetPlatform': targetPlatform, - 'sdkName': sdkName, - 'emulator': emulator, - 'fullRestart': fullRestart, - if (reason != null) 'reason': reason, - if (finalLibraryCount != null) 'finalLibraryCount': finalLibraryCount, - if (syncedLibraryCount != null) - 'syncedLibraryCount': syncedLibraryCount, - if (syncedClassesCount != null) - 'syncedClassesCount': syncedClassesCount, - if (syncedProceduresCount != null) - 'syncedProceduresCount': syncedProceduresCount, - if (syncedBytes != null) 'syncedBytes': syncedBytes, - if (invalidatedSourcesCount != null) - 'invalidatedSourcesCount': invalidatedSourcesCount, - if (transferTimeInMs != null) 'transferTimeInMs': transferTimeInMs, - if (overallTimeInMs != null) 'overallTimeInMs': overallTimeInMs, - if (compileTimeInMs != null) 'compileTimeInMs': compileTimeInMs, - if (findInvalidatedTimeInMs != null) - 'findInvalidatedTimeInMs': findInvalidatedTimeInMs, - if (scannedSourcesCount != null) - 'scannedSourcesCount': scannedSourcesCount, - if (reassembleTimeInMs != null) - 'reassembleTimeInMs': reassembleTimeInMs, - if (reloadVMTimeInMs != null) 'reloadVMTimeInMs': reloadVMTimeInMs, - }; + }) : this._( + eventName: DashEvent.hotRunnerInfo, + eventData: { + 'label': label, + 'targetPlatform': targetPlatform, + 'sdkName': sdkName, + 'emulator': emulator, + 'fullRestart': fullRestart, + if (reason != null) 'reason': reason, + if (finalLibraryCount != null) + 'finalLibraryCount': finalLibraryCount, + if (syncedLibraryCount != null) + 'syncedLibraryCount': syncedLibraryCount, + if (syncedClassesCount != null) + 'syncedClassesCount': syncedClassesCount, + if (syncedProceduresCount != null) + 'syncedProceduresCount': syncedProceduresCount, + if (syncedBytes != null) 'syncedBytes': syncedBytes, + if (invalidatedSourcesCount != null) + 'invalidatedSourcesCount': invalidatedSourcesCount, + if (transferTimeInMs != null) 'transferTimeInMs': transferTimeInMs, + if (overallTimeInMs != null) 'overallTimeInMs': overallTimeInMs, + if (compileTimeInMs != null) 'compileTimeInMs': compileTimeInMs, + if (findInvalidatedTimeInMs != null) + 'findInvalidatedTimeInMs': findInvalidatedTimeInMs, + if (scannedSourcesCount != null) + 'scannedSourcesCount': scannedSourcesCount, + if (reassembleTimeInMs != null) + 'reassembleTimeInMs': reassembleTimeInMs, + if (reloadVMTimeInMs != null) 'reloadVMTimeInMs': reloadVMTimeInMs, + }, + ); /// Event that is emitted periodically to report the number of times each lint /// has been enabled. @@ -578,11 +628,13 @@ final class Event { Event.lintUsageCount({ required int count, required String name, - }) : eventName = DashEvent.lintUsageCount, - eventData = { - 'count': count, - 'name': name, - }; + }) : this._( + eventName: DashEvent.lintUsageCount, + eventData: { + 'count': count, + 'name': name, + }, + ); /// Event that is emitted periodically to report the amount of memory being /// used. @@ -601,12 +653,14 @@ final class Event { required int rss, int? periodSec, double? mbPerSec, - }) : eventName = DashEvent.memoryInfo, - eventData = { - 'rss': rss, - if (periodSec != null) 'periodSec': periodSec, - if (mbPerSec != null) 'mbPerSec': mbPerSec - }; + }) : this._( + eventName: DashEvent.memoryInfo, + eventData: { + 'rss': rss, + if (periodSec != null) 'periodSec': periodSec, + if (mbPerSec != null) 'mbPerSec': mbPerSec + }, + ); /// Event that is emitted periodically to report the performance of plugins /// when handling requests. @@ -622,12 +676,14 @@ final class Event { required String duration, required String method, required String pluginId, - }) : eventName = DashEvent.pluginRequest, - eventData = { - 'duration': duration, - 'method': method, - 'pluginId': pluginId, - }; + }) : this._( + eventName: DashEvent.pluginRequest, + eventData: { + 'duration': duration, + 'method': method, + 'pluginId': pluginId, + }, + ); /// Event that is emitted periodically to report the frequency with which a /// given plugin has been used. @@ -643,12 +699,14 @@ final class Event { required int count, required String enabled, required String pluginId, - }) : eventName = DashEvent.pluginUse, - eventData = { - 'count': count, - 'enabled': enabled, - 'pluginId': pluginId, - }; + }) : this._( + eventName: DashEvent.pluginUse, + eventData: { + 'count': count, + 'enabled': enabled, + 'pluginId': pluginId, + }, + ); /// Event that is emitted when `pub get` is run. /// @@ -662,12 +720,14 @@ final class Event { required String packageName, required String version, required String dependencyType, - }) : eventName = DashEvent.pubGet, - eventData = { - 'packageName': packageName, - 'version': version, - 'dependencyType': dependencyType, - }; + }) : this._( + eventName: DashEvent.pubGet, + eventData: { + 'packageName': packageName, + 'version': version, + 'dependencyType': dependencyType, + }, + ); /// Event that is emitted on shutdown to report information about the whole /// session for which the analysis server was running. @@ -691,14 +751,16 @@ final class Event { required int duration, required String flags, required String parameters, - }) : eventName = DashEvent.serverSession, - eventData = { - 'clientId': clientId, - 'clientVersion': clientVersion, - 'duration': duration, - 'flags': flags, - 'parameters': parameters, - }; + }) : this._( + eventName: DashEvent.serverSession, + eventData: { + 'clientId': clientId, + 'clientVersion': clientVersion, + 'duration': duration, + 'flags': flags, + 'parameters': parameters, + }, + ); /// Event that is emitted periodically to report the number of times the /// severity of a diagnostic was changed in the analysis options file. @@ -710,11 +772,13 @@ final class Event { Event.severityAdjustment({ required String diagnostic, required String adjustments, - }) : eventName = DashEvent.severityAdjustment, - eventData = { - 'diagnostic': diagnostic, - 'adjustments': adjustments, - }; + }) : this._( + eventName: DashEvent.severityAdjustment, + eventData: { + 'diagnostic': diagnostic, + 'adjustments': adjustments, + }, + ); /// Event that is emitted by `package:unified_analytics` when /// the user takes action when prompted with a survey. @@ -726,11 +790,13 @@ final class Event { Event.surveyAction({ required String surveyId, required String status, - }) : eventName = DashEvent.surveyAction, - eventData = { - 'surveyId': surveyId, - 'status': status, - }; + }) : this._( + eventName: DashEvent.surveyAction, + eventData: { + 'surveyId': surveyId, + 'status': status, + }, + ); /// Event that is emitted by `package:unified_analytics` when the /// user has been shown a survey. @@ -738,10 +804,12 @@ final class Event { /// [surveyId] - the unique id for a given survey. Event.surveyShown({ required String surveyId, - }) : eventName = DashEvent.surveyShown, - eventData = { - 'surveyId': surveyId, - }; + }) : this._( + eventName: DashEvent.surveyShown, + eventData: { + 'surveyId': surveyId, + }, + ); /// Event that records how long a given process takes to complete. /// @@ -762,17 +830,15 @@ final class Event { required String variableName, required int elapsedMilliseconds, String? label, - }) : eventName = DashEvent.timing, - eventData = { - 'workflow': workflow, - 'variableName': variableName, - 'elapsedMilliseconds': elapsedMilliseconds, - if (label != null) 'label': label, - }; - - /// Private constructor to be used when deserializing JSON into an instance - /// of [Event]. - Event._({required this.eventName, required this.eventData}); + }) : this._( + eventName: DashEvent.timing, + eventData: { + 'workflow': workflow, + 'variableName': variableName, + 'elapsedMilliseconds': elapsedMilliseconds, + if (label != null) 'label': label, + }, + ); @override int get hashCode => Object.hash(eventName, jsonEncode(eventData)); From 8ac155e4a55244fb0a0c916500e04235b448a852 Mon Sep 17 00:00:00 2001 From: Andrew Kolos Date: Tue, 28 Jan 2025 09:58:35 -0800 Subject: [PATCH 3/3] bump version --- pkgs/unified_analytics/CHANGELOG.md | 3 +++ pkgs/unified_analytics/lib/src/constants.dart | 2 +- pkgs/unified_analytics/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pkgs/unified_analytics/CHANGELOG.md b/pkgs/unified_analytics/CHANGELOG.md index 7e42cbabc..5c7333c76 100644 --- a/pkgs/unified_analytics/CHANGELOG.md +++ b/pkgs/unified_analytics/CHANGELOG.md @@ -1,3 +1,6 @@ +## 7.0.2 +- Send `enabled_features` as an event parameter in all events rather than as a user property. + ## 7.0.1 - Fixed `UnsupportedError` thrown when Event.exception is called without providing a value for `args`. diff --git a/pkgs/unified_analytics/lib/src/constants.dart b/pkgs/unified_analytics/lib/src/constants.dart index 2be4d0a6c..c78c6c262 100644 --- a/pkgs/unified_analytics/lib/src/constants.dart +++ b/pkgs/unified_analytics/lib/src/constants.dart @@ -87,7 +87,7 @@ const int kMaxLogFileSize = 25 * (1 << 20); const String kLogFileName = 'dart-flutter-telemetry.log'; /// The current version of the package, should be in line with pubspec version. -const String kPackageVersion = '7.0.1'; +const String kPackageVersion = '7.0.2'; /// The minimum length for a session. const int kSessionDurationMinutes = 30; diff --git a/pkgs/unified_analytics/pubspec.yaml b/pkgs/unified_analytics/pubspec.yaml index f0a99f12c..6ef64751b 100644 --- a/pkgs/unified_analytics/pubspec.yaml +++ b/pkgs/unified_analytics/pubspec.yaml @@ -5,7 +5,7 @@ description: >- # LINT.IfChange # When updating this, keep the version consistent with the changelog and the # value in lib/src/constants.dart. -version: 7.0.1 +version: 7.0.2 # LINT.ThenChange(lib/src/constants.dart) repository: https://github.com/dart-lang/tools/tree/main/pkgs/unified_analytics issue_tracker: https://github.com/dart-lang/tools/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Aunified_analytics