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 01/10] 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 02/10] 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 03/10] 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 04/10] 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 05/10] 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 24beebf5cb9722dbed34f031c65f4a750f526bfc Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:37:57 -0500 Subject: [PATCH 06/10] Check for telemetry status after saving locally --- pkgs/dash_analytics/lib/src/analytics.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/dash_analytics/lib/src/analytics.dart b/pkgs/dash_analytics/lib/src/analytics.dart index fb026c16bd..d943978258 100644 --- a/pkgs/dash_analytics/lib/src/analytics.dart +++ b/pkgs/dash_analytics/lib/src/analytics.dart @@ -245,8 +245,6 @@ class AnalyticsImpl implements Analytics { required DashEvent eventName, Map eventData = const {}, }) { - if (!telemetryEnabled) return null; - // Construct the body of the request final Map body = generateRequestBody( clientId: _clientId, @@ -257,6 +255,8 @@ class AnalyticsImpl implements Analytics { _logHandler.save(data: body); + if (!telemetryEnabled) return null; + // Pass to the google analytics client to send return _gaClient.sendData(body); } From a19d8bd50ddba10bc5ea7990a126d748cf57b173 Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Tue, 28 Feb 2023 15:03:54 -0500 Subject: [PATCH 07/10] Empty commit From f35de81caf7d98e290b11bf4b5df152bd71c9522 Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Tue, 28 Feb 2023 15:20:01 -0500 Subject: [PATCH 08/10] Update enums.dart --- pkgs/dash_analytics/lib/src/enums.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/dash_analytics/lib/src/enums.dart b/pkgs/dash_analytics/lib/src/enums.dart index 84d1f67ca7..d43bf18f2d 100644 --- a/pkgs/dash_analytics/lib/src/enums.dart +++ b/pkgs/dash_analytics/lib/src/enums.dart @@ -59,7 +59,7 @@ enum DashEvent { /// Officially-supported clients of this package. /// -/// All [label] values should use an underscore as a delimiter +/// All [label] values should use an underscore as a delimiter. enum DashTool { flutterTools( label: 'flutter_tools', From eee9570dec1ba40d5f419371cf5cf9b84a295dda Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:39:40 -0500 Subject: [PATCH 09/10] Revert "Check for telemetry status after saving locally" This reverts commit 24beebf5cb9722dbed34f031c65f4a750f526bfc. --- pkgs/dash_analytics/lib/src/analytics.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/dash_analytics/lib/src/analytics.dart b/pkgs/dash_analytics/lib/src/analytics.dart index d943978258..fb026c16bd 100644 --- a/pkgs/dash_analytics/lib/src/analytics.dart +++ b/pkgs/dash_analytics/lib/src/analytics.dart @@ -245,6 +245,8 @@ class AnalyticsImpl implements Analytics { required DashEvent eventName, Map eventData = const {}, }) { + if (!telemetryEnabled) return null; + // Construct the body of the request final Map body = generateRequestBody( clientId: _clientId, @@ -255,8 +257,6 @@ class AnalyticsImpl implements Analytics { _logHandler.save(data: body); - if (!telemetryEnabled) return null; - // Pass to the google analytics client to send return _gaClient.sendData(body); } From 68a831bacf3a57f8ff5949be755b965676a78d82 Mon Sep 17 00:00:00 2001 From: eliasyishak <42216813+eliasyishak@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:41:14 -0500 Subject: [PATCH 10/10] Save event data locally before checking telemetry status on sendEvent --- pkgs/unified_analytics/lib/src/analytics.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/unified_analytics/lib/src/analytics.dart b/pkgs/unified_analytics/lib/src/analytics.dart index ad7749b85f..b3b5a3c47e 100644 --- a/pkgs/unified_analytics/lib/src/analytics.dart +++ b/pkgs/unified_analytics/lib/src/analytics.dart @@ -243,8 +243,6 @@ class AnalyticsImpl implements Analytics { required DashEvent eventName, Map eventData = const {}, }) { - if (!telemetryEnabled) return null; - // Construct the body of the request final Map body = generateRequestBody( clientId: _clientId, @@ -255,6 +253,8 @@ class AnalyticsImpl implements Analytics { _logHandler.save(data: body); + if (!telemetryEnabled) return null; + // Pass to the google analytics client to send return _gaClient.sendData(body); }