Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions packages/cli_tools/lib/src/analytics/analytics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,21 @@ abstract class Analytics {
final Map<String, dynamic> properties = const {},
}) {
late final Future<void> pendingTrack;
pendingTrack = sendEvent(event: event, properties: properties)
.catchError((final _) {})
pendingTrack = _quietSendEvent(event: event, properties: properties)
.whenComplete(() => _pendingTracks.remove(pendingTrack));
_pendingTracks.add(pendingTrack);
}

/// Sends an event, swallowing any errors.
Future<void> _quietSendEvent({
required final String event,
required final Map<String, dynamic> properties,
}) async {
try {
await sendEvent(event: event, properties: properties);
} catch (_) {}
}

/// Send an event to the analytics service.
Future<void> sendEvent({
required final String event,
Expand Down
4 changes: 2 additions & 2 deletions packages/cli_tools/lib/src/analytics/mixpanel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class MixPanelAnalytics extends Analytics {
Future<void> sendEvent({
required final String event,
final Map<String, dynamic> properties = const {},
}) {
}) async {
final payload = jsonEncode({
'event': event,
'properties': {
Expand All @@ -68,7 +68,7 @@ class MixPanelAnalytics extends Analytics {
},
});

return http.post(
await http.post(
_endpoint,
body: 'data=$payload',
headers: {
Expand Down
4 changes: 2 additions & 2 deletions packages/cli_tools/lib/src/analytics/posthog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class PostHogAnalytics extends Analytics {
Future<void> sendEvent({
required final String event,
final Map<String, dynamic> properties = const {},
}) {
}) async {
final eventData = {
'api_key': _projectApiKey,
'event': event,
Expand All @@ -54,7 +54,7 @@ class PostHogAnalytics extends Analytics {
},
};

return http
await http
.post(
_endpoint,
headers: {'Content-Type': 'application/json'},
Expand Down
40 changes: 40 additions & 0 deletions packages/cli_tools/test/analytics/analytics_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,26 @@ void main() {
},
);

test(
'Given an analytics implementation whose sendEvent returns a future with a non-void reified type, '
'when the event send fails, '
'then the failure is ignored and flush completes.',
() async {
final analytics = NonVoidFutureAnalytics();

analytics.track(event: 'test');

final flush = expectLater(analytics.flush(), completes);
await flushEventQueue();

analytics.completers.single.completeError(
StateError('Failed to send event.'),
);

await expectLater(flush, completes);
},
);

test(
'Given compound analytics with a provider that fails to flush, '
'when flushing the compound analytics, '
Expand Down Expand Up @@ -79,6 +99,26 @@ class PendingAnalytics extends Analytics {
}
}

/// Returns futures from [sendEvent] whose reified type is not `Future<void>`.
///
/// This an example of how NOT to do it. In general don't try to be smart and
/// skip the async on a method returning `Future<void>`. This is just one pitfall
/// of many.
class NonVoidFutureAnalytics extends Analytics {
final completers = <Completer<String>>[];

@override
Future<void> sendEvent /* no async */ ({
required final String event,
final Map<String, dynamic> properties = const {},
}) {
// note the mismatch Future<void> vs Future<String>
final completer = Completer<String>();
completers.add(completer);
return completer.future; // no await
}
}

class FailingFlushAnalytics extends Analytics {
var didFlush = false;

Expand Down
Loading