From db28a4ea2377cb53b59f6fd5df35d8fabb391498 Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:32:53 -0500 Subject: [PATCH 1/9] adding event for analyzer startup --- pkgs/dash_analytics/lib/src/enums.dart | 7 +++++- .../test/dash_analytics_test.dart | 23 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/pkgs/dash_analytics/lib/src/enums.dart b/pkgs/dash_analytics/lib/src/enums.dart index cab435abe2..d4dcd8666e 100644 --- a/pkgs/dash_analytics/lib/src/enums.dart +++ b/pkgs/dash_analytics/lib/src/enums.dart @@ -7,8 +7,13 @@ /// The [label] for each enum value is what will be logged, the [description] /// is here for documentation purposes enum DashEvent { + analyzerServerStarted( + label: 'analyzer-server-started', + description: 'Dart Analyzer Server Started', + toolOwner: DashTool.dartAnalyzer, + ), hotReloadTime( - label: 'hot_reload_time', + label: 'hot-reload-time', description: 'Hot reload duration', toolOwner: DashTool.flutterTools, ), diff --git a/pkgs/dash_analytics/test/dash_analytics_test.dart b/pkgs/dash_analytics/test/dash_analytics_test.dart index 81fc92b967..c69386cba5 100644 --- a/pkgs/dash_analytics/test/dash_analytics_test.dart +++ b/pkgs/dash_analytics/test/dash_analytics_test.dart @@ -657,15 +657,36 @@ $initialToolName=${ConfigHandler.dateStamp},$toolsMessageVersion // Regex pattern to match only letters or hyphens final RegExp toolLabelPattern = RegExp(r'^[a-zA-Z\-]+$'); bool valid = true; + final List invalidTools = []; for (DashTool tool in DashTool.values) { if (!toolLabelPattern.hasMatch(tool.label)) { valid = false; + invalidTools.add(tool); } } expect(valid, true, reason: 'All tool labels should have letters and hyphens ' - 'as a delimiter if needed'); + 'as a delimiter if needed; invalid tools below\n$invalidTools'); + }); + + test( + 'All DashEvents labels are made of characters that are letters or hyphens', + () { + // Regex pattern to match only letters or hyphens + final RegExp eventLabelPattern = RegExp(r'^[a-zA-Z\-]+$'); + bool valid = true; + final List invalidEvents = []; + for (DashEvent event in DashEvent.values) { + if (!eventLabelPattern.hasMatch(event.label)) { + valid = false; + invalidEvents.add(event); + } + } + + expect(valid, true, + reason: 'All event labels should have letters and hyphens ' + 'as a delimiter if needed; invalid events below\n$invalidEvents'); }); test('Check that log file is correctly persisting events sent', () { From f83f19cf2fc54477bb6ea819f8287992af55340a Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:50:15 -0500 Subject: [PATCH 2/9] switching from hyphens to underscore; GA4 restriction --- pkgs/dash_analytics/lib/src/enums.dart | 10 +++++----- .../dash_analytics/test/dash_analytics_test.dart | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pkgs/dash_analytics/lib/src/enums.dart b/pkgs/dash_analytics/lib/src/enums.dart index d4dcd8666e..943ed7b1a5 100644 --- a/pkgs/dash_analytics/lib/src/enums.dart +++ b/pkgs/dash_analytics/lib/src/enums.dart @@ -8,12 +8,12 @@ /// is here for documentation purposes enum DashEvent { analyzerServerStarted( - label: 'analyzer-server-started', + label: 'analyzer_server_started', description: 'Dart Analyzer Server Started', toolOwner: DashTool.dartAnalyzer, ), hotReloadTime( - label: 'hot-reload-time', + label: 'hot_reload_time', description: 'Hot reload duration', toolOwner: DashTool.flutterTools, ), @@ -31,14 +31,14 @@ enum DashEvent { /// Officially-supported clients of this package. /// -/// All [label] values should use a hyphen as a delimiter +/// All [label] values should use an underscore as a delimiter enum DashTool { flutterTools( - label: 'flutter-tools', + label: 'flutter_tools', description: 'Runs flutter applications from CLI', ), dartAnalyzer( - label: 'dart-analyzer', + label: 'dart_analyzer', description: 'Analyzes dart code in workspace', ); diff --git a/pkgs/dash_analytics/test/dash_analytics_test.dart b/pkgs/dash_analytics/test/dash_analytics_test.dart index c69386cba5..dc129c719b 100644 --- a/pkgs/dash_analytics/test/dash_analytics_test.dart +++ b/pkgs/dash_analytics/test/dash_analytics_test.dart @@ -652,10 +652,10 @@ $initialToolName=${ConfigHandler.dateStamp},$toolsMessageVersion }); test( - 'All DashTools labels are made of characters that are letters or hyphens', + 'All DashTools labels are made of characters that are letters or underscores', () { - // Regex pattern to match only letters or hyphens - final RegExp toolLabelPattern = RegExp(r'^[a-zA-Z\-]+$'); + // Regex pattern to match only letters or underscores + final RegExp toolLabelPattern = RegExp(r'^[a-zA-Z\_]+$'); bool valid = true; final List invalidTools = []; for (DashTool tool in DashTool.values) { @@ -666,15 +666,15 @@ $initialToolName=${ConfigHandler.dateStamp},$toolsMessageVersion } expect(valid, true, - reason: 'All tool labels should have letters and hyphens ' + reason: 'All tool labels should have letters and underscores ' 'as a delimiter if needed; invalid tools below\n$invalidTools'); }); test( - 'All DashEvents labels are made of characters that are letters or hyphens', + 'All DashEvents labels are made of characters that are letters or underscores', () { - // Regex pattern to match only letters or hyphens - final RegExp eventLabelPattern = RegExp(r'^[a-zA-Z\-]+$'); + // Regex pattern to match only letters or underscores + final RegExp eventLabelPattern = RegExp(r'^[a-zA-Z\_]+$'); bool valid = true; final List invalidEvents = []; for (DashEvent event in DashEvent.values) { @@ -685,7 +685,7 @@ $initialToolName=${ConfigHandler.dateStamp},$toolsMessageVersion } expect(valid, true, - reason: 'All event labels should have letters and hyphens ' + reason: 'All event labels should have letters and underscores ' 'as a delimiter if needed; invalid events below\n$invalidEvents'); }); From 9d0dd2e2c95f6196ea1945e12c1c2cf8132a64fe Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Wed, 15 Feb 2023 16:03:29 -0500 Subject: [PATCH 3/9] making eventData nullable for events that don't need it --- pkgs/dash_analytics/example/dash_analytics_example.dart | 2 +- pkgs/dash_analytics/lib/src/analytics.dart | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/dash_analytics/example/dash_analytics_example.dart b/pkgs/dash_analytics/example/dash_analytics_example.dart index c7ec99fe3f..b46f47cde5 100644 --- a/pkgs/dash_analytics/example/dash_analytics_example.dart +++ b/pkgs/dash_analytics/example/dash_analytics_example.dart @@ -25,7 +25,7 @@ void main() { print(analytics.telemetryEnabled); analytics.sendEvent( eventName: DashEvent.hotReloadTime, - eventData: {'time_ns': 345}, + eventData: {'time_ns': 345}, // Optional map to add relevant data ); print(analytics.logFileStats()); analytics.close(); diff --git a/pkgs/dash_analytics/lib/src/analytics.dart b/pkgs/dash_analytics/lib/src/analytics.dart index fa678c9424..ba5d074138 100644 --- a/pkgs/dash_analytics/lib/src/analytics.dart +++ b/pkgs/dash_analytics/lib/src/analytics.dart @@ -127,7 +127,7 @@ abstract class Analytics { /// API to send events to Google Analytics to track usage Future? sendEvent({ required DashEvent eventName, - required Map eventData, + Map eventData, }); /// Pass a boolean to either enable or disable telemetry and make @@ -243,7 +243,7 @@ class AnalyticsImpl implements Analytics { @override Future? sendEvent({ required DashEvent eventName, - required Map eventData, + Map eventData = const {}, }) { if (!telemetryEnabled) return null; @@ -290,7 +290,7 @@ class TestAnalytics extends AnalyticsImpl { @override Future? sendEvent({ required DashEvent eventName, - required Map eventData, + Map eventData = const {}, }) { if (!telemetryEnabled) return null; From f10526774bc2c3663b7d1bc354383e56894562a2 Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Wed, 15 Feb 2023 16:05:39 -0500 Subject: [PATCH 4/9] Update dash_analytics_example.dart --- pkgs/dash_analytics/example/dash_analytics_example.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkgs/dash_analytics/example/dash_analytics_example.dart b/pkgs/dash_analytics/example/dash_analytics_example.dart index b46f47cde5..5ff43decfc 100644 --- a/pkgs/dash_analytics/example/dash_analytics_example.dart +++ b/pkgs/dash_analytics/example/dash_analytics_example.dart @@ -23,9 +23,11 @@ void main() { print('###### START ###### $start'); print(analytics.telemetryEnabled); + // [eventData] is an optional map to add relevant data + // for the [eventName] being sent analytics.sendEvent( eventName: DashEvent.hotReloadTime, - eventData: {'time_ns': 345}, // Optional map to add relevant data + eventData: {'time_ns': 345}, ); print(analytics.logFileStats()); analytics.close(); From 9825399fb2493c4aa77e67df6c395cfa690e55f3 Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Wed, 15 Feb 2023 17:14:07 -0500 Subject: [PATCH 5/9] added test to check for limitations below - Events can have a maximum of 25 user properties - User property names must be 24 characters or fewer - User property values must be 36 characters or fewer (only for `tool` name) - Event names must be 40 characters or fewer, may only contain alpha-numeric characters and underscores, and must start with an alphabetic character --- .../test/dash_analytics_test.dart | 86 ++++++++++++++----- 1 file changed, 65 insertions(+), 21 deletions(-) diff --git a/pkgs/dash_analytics/test/dash_analytics_test.dart b/pkgs/dash_analytics/test/dash_analytics_test.dart index dc129c719b..566ac8a95d 100644 --- a/pkgs/dash_analytics/test/dash_analytics_test.dart +++ b/pkgs/dash_analytics/test/dash_analytics_test.dart @@ -654,8 +654,9 @@ $initialToolName=${ConfigHandler.dateStamp},$toolsMessageVersion test( 'All DashTools labels are made of characters that are letters or underscores', () { - // Regex pattern to match only letters or underscores - final RegExp toolLabelPattern = RegExp(r'^[a-zA-Z\_]+$'); + // Regex pattern to match only letters or underscores and must start + // with an alphabet char + final RegExp toolLabelPattern = RegExp(r'^[a-zA-Z]{1}[a-zA-Z\_]*$'); bool valid = true; final List invalidTools = []; for (DashTool tool in DashTool.values) { @@ -670,25 +671,6 @@ $initialToolName=${ConfigHandler.dateStamp},$toolsMessageVersion 'as a delimiter if needed; invalid tools below\n$invalidTools'); }); - test( - 'All DashEvents labels are made of characters that are letters or underscores', - () { - // Regex pattern to match only letters or underscores - final RegExp eventLabelPattern = RegExp(r'^[a-zA-Z\_]+$'); - bool valid = true; - final List invalidEvents = []; - for (DashEvent event in DashEvent.values) { - if (!eventLabelPattern.hasMatch(event.label)) { - valid = false; - invalidEvents.add(event); - } - } - - expect(valid, true, - reason: 'All event labels should have letters and underscores ' - 'as a delimiter if needed; invalid events below\n$invalidEvents'); - }); - test('Check that log file is correctly persisting events sent', () { final int numberOfEvents = max((kLogFileLength * 0.1).floor(), 5); @@ -871,4 +853,66 @@ $initialToolName=${ConfigHandler.dateStamp},$toolsMessageVersion expect(query2.flutterChannelCount, 1, reason: 'The first instance has flutter information initialized'); }); + + test('Payload sent to GA follows limitations', () { + // Link to limitations documentation + // https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag#limitations + + final Map userPropPayload = userProperty.preparePayload(); + + // Check that each key in the user property object is less than 24 chars and + // that we have less than 25 keys + const int maxUserPropLength = 24; + const int maxUserPropKeys = 25; + bool userPropLengthValid = true; + final List invalidUserProps = []; + for (String key in userPropPayload.keys.toList()) { + if (key.length > maxUserPropLength) { + userPropLengthValid = false; + invalidUserProps.add(key); + } + } + expect(userPropLengthValid, true, + reason: + 'The max length for each user prop is $maxUserPropLength chars\n' + 'The below keys are too long:\n$invalidUserProps'); + expect( + userPropPayload.keys.length < maxUserPropKeys, + true, + reason: 'There are too many keys in the UserProperty payload', + ); + + // Check that each event name is less than 40 chars and starts with + // an alphabetic character; the entire string has to be alphanumeric + // and underscores + final RegExp eventLabelPattern = + RegExp(r'^[a-zA-Z]{1}[a-zA-Z0-9\_]{0,39}$'); + bool eventValid = true; + final List invalidEvents = []; + for (DashEvent event in DashEvent.values) { + if (!eventLabelPattern.hasMatch(event.label)) { + eventValid = false; + invalidEvents.add(event); + } + } + + expect(eventValid, true, + reason: 'All event labels should have letters and underscores ' + 'as a delimiter if needed; invalid events below\n$invalidEvents'); + + // All dash tools must be under 36 characters + const int maxDashToolLength = 36; + bool dashToolLengthValid = true; + final List invalidTools = []; + for (DashTool tool in DashTool.values) { + if (tool.label.length > maxDashToolLength) { + dashToolLengthValid = false; + invalidTools.add(tool); + } + } + + expect(dashToolLengthValid, true, + reason: 'All dash tool labels have to be less than $maxDashToolLength\n' + 'The following are too long below\n$invalidTools'); + }); } From 25a1fb8e5cac1f702be192989c38eb5fb4bdb1b1 Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Wed, 22 Feb 2023 15:45:22 -0500 Subject: [PATCH 6/9] Moved creds + edit to tool regex pattern --- pkgs/dash_analytics/example/dash_analytics_example.dart | 2 -- pkgs/dash_analytics/lib/src/analytics.dart | 6 ++---- pkgs/dash_analytics/lib/src/config_handler.dart | 2 +- pkgs/dash_analytics/lib/src/constants.dart | 8 ++++++++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/pkgs/dash_analytics/example/dash_analytics_example.dart b/pkgs/dash_analytics/example/dash_analytics_example.dart index 5ff43decfc..9fa0b8fdb7 100644 --- a/pkgs/dash_analytics/example/dash_analytics_example.dart +++ b/pkgs/dash_analytics/example/dash_analytics_example.dart @@ -11,8 +11,6 @@ final String apiSecret = '4yT8__oER3Cd84dtx6r-_A'; // point of the tool final Analytics analytics = Analytics( tool: DashTool.flutterTools, - measurementId: measurementId, - apiSecret: apiSecret, flutterChannel: 'ey-test-channel', flutterVersion: 'Flutter 3.6.0-7.0.pre.47', dartVersion: 'Dart 2.19.0', diff --git a/pkgs/dash_analytics/lib/src/analytics.dart b/pkgs/dash_analytics/lib/src/analytics.dart index fb026c16bd..697423db67 100644 --- a/pkgs/dash_analytics/lib/src/analytics.dart +++ b/pkgs/dash_analytics/lib/src/analytics.dart @@ -25,8 +25,6 @@ abstract class Analytics { /// of the [Analytics] abstract class using the [LocalFileSystem] factory Analytics({ required DashTool tool, - required String measurementId, - required String apiSecret, String? flutterChannel, String? flutterVersion, required String dartVersion, @@ -48,8 +46,8 @@ abstract class Analytics { return AnalyticsImpl( tool: tool.label, homeDirectory: getHomeDirectory(fs), - measurementId: measurementId, - apiSecret: apiSecret, + measurementId: kGoogleAnalyticsMeasurementId, + apiSecret: kGoogleAnalyticsApiSecret, flutterChannel: flutterChannel, flutterVersion: flutterVersion, dartVersion: dartVersion, diff --git a/pkgs/dash_analytics/lib/src/config_handler.dart b/pkgs/dash_analytics/lib/src/config_handler.dart index 83ecd7d8c2..8f62c4dcfc 100644 --- a/pkgs/dash_analytics/lib/src/config_handler.dart +++ b/pkgs/dash_analytics/lib/src/config_handler.dart @@ -21,7 +21,7 @@ const String telemetryFlagPattern = r'^reporting=([0|1]) *$'; /// Example: /// flutter-tools=2022-10-26,1 const String toolPattern = - r'^([A-Za-z0-9]+-*[A-Za-z0-9]*)=([0-9]{4}-[0-9]{2}-[0-9]{2}),([0-9]+)$'; + r'^([A-Za-z0-9]+_*[A-Za-z0-9]*)=([0-9]{4}-[0-9]{2}-[0-9]{2}),([0-9]+)$'; class ConfigHandler { /// Regex pattern implementation for matching a line in the config file diff --git a/pkgs/dash_analytics/lib/src/constants.dart b/pkgs/dash_analytics/lib/src/constants.dart index 53a853692b..924deb0d79 100644 --- a/pkgs/dash_analytics/lib/src/constants.dart +++ b/pkgs/dash_analytics/lib/src/constants.dart @@ -55,6 +55,14 @@ reporting=1 /// will be located const String kDartToolDirectoryName = '.dart'; +/// The API secret associated with the GA4 instance's Measurement Protocol +const kGoogleAnalyticsApiSecret = 'Ka1jc8tZSzWc_GXMWHfPHA'; + +/// The measurement ID related to the GA4 instance +/// +/// Serves as an identifier for a web data stream +const kGoogleAnalyticsMeasurementId = 'G-04BXPVBCWJ'; + /// How many data records to store in the log file const int kLogFileLength = 2500; From 41f00d59ad28a14f5a78bcb8db900d3851161435 Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Wed, 22 Feb 2023 16:32:56 -0500 Subject: [PATCH 7/9] Update to match main branch regex --- pkgs/dash_analytics/lib/src/config_handler.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/dash_analytics/lib/src/config_handler.dart b/pkgs/dash_analytics/lib/src/config_handler.dart index 8f62c4dcfc..08033d85c0 100644 --- a/pkgs/dash_analytics/lib/src/config_handler.dart +++ b/pkgs/dash_analytics/lib/src/config_handler.dart @@ -19,9 +19,9 @@ const String telemetryFlagPattern = r'^reporting=([0|1]) *$'; /// from the configuration file /// /// Example: -/// flutter-tools=2022-10-26,1 +/// flutter_tools=2022-10-26,1 const String toolPattern = - r'^([A-Za-z0-9]+_*[A-Za-z0-9]*)=([0-9]{4}-[0-9]{2}-[0-9]{2}),([0-9]+)$'; + r'^([A-Za-z0-9]+[A-Za-z0-9_]*)=([0-9]{4}-[0-9]{2}-[0-9]{2}),([0-9]+)$'; class ConfigHandler { /// Regex pattern implementation for matching a line in the config file From 50598aea8834b3af6d85870d0cd9ef5eff23fb54 Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Mon, 27 Feb 2023 12:27:02 -0500 Subject: [PATCH 8/9] Clean up of constructor --- pkgs/dash_analytics/lib/src/analytics.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/dash_analytics/lib/src/analytics.dart b/pkgs/dash_analytics/lib/src/analytics.dart index 697423db67..ad7749b85f 100644 --- a/pkgs/dash_analytics/lib/src/analytics.dart +++ b/pkgs/dash_analytics/lib/src/analytics.dart @@ -25,9 +25,9 @@ abstract class Analytics { /// of the [Analytics] abstract class using the [LocalFileSystem] factory Analytics({ required DashTool tool, + required String dartVersion, String? flutterChannel, String? flutterVersion, - required String dartVersion, }) { // Create the instance of the file system so clients don't need // resolve on their own From 5b46a6948172aa2a255c12618c3e18f4f0322a9c Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Tue, 28 Feb 2023 12:12:43 -0500 Subject: [PATCH 9/9] Adding separator within event enums --- pkgs/dash_analytics/lib/src/enums.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkgs/dash_analytics/lib/src/enums.dart b/pkgs/dash_analytics/lib/src/enums.dart index 19642ca84b..84d1f67ca7 100644 --- a/pkgs/dash_analytics/lib/src/enums.dart +++ b/pkgs/dash_analytics/lib/src/enums.dart @@ -7,11 +7,14 @@ /// The [label] for each enum value is what will be logged, the [description] /// is here for documentation purposes enum DashEvent { + // Events for flutter_tools hotReloadTime( label: 'hot_reload_time', description: 'Hot reload duration', toolOwner: DashTool.flutterTools, ), + + // Events for language_server lintUsageCounts( label: 'lint_usage_counts', description: 'Number of times each lint is enabled',