1+ import 'dart:convert' ;
2+
13import 'package:codelessly_api/codelessly_api.dart' ;
4+ import 'package:collection/collection.dart' ;
5+ import 'package:http/http.dart' ;
6+ import 'package:meta/meta.dart' ;
27
3- import '../constants .dart' ;
8+ import '../../codelessly_sdk .dart' ;
49import '../utils/debouncer.dart' ;
510
611/// A class that tracks statistics of various operations in the SDK.
712abstract class StatTracker {
13+ Uri ? serverUrl;
14+
815 /// The project ID to track the statistics for.
916 String ? projectId;
1017
1118 bool get didInitialize => projectId != null ;
1219
13- void init (String projectId) => this .projectId = projectId;
20+ @mustCallSuper
21+ void init ({
22+ required String projectId,
23+ required Uri serverUrl,
24+ }) {
25+ this .projectId = projectId;
26+ this .serverUrl = serverUrl;
27+ }
1428
1529 /// Tracks one document read operation.
1630 Future <void > trackRead (String label);
@@ -31,21 +45,50 @@ abstract class StatTracker {
3145 Future <void > trackCloudAction (ActionModel action);
3246}
3347
34- /// A [StatTracker] implementation that utilizes Firestore to track the
35- /// statistics.
36- final class FirestoreStatTracker extends StatTracker {
48+ /// A [StatTracker] implementation that sends the stats to Codelessly's server.
49+ final class CodelesslyStatTracker extends StatTracker {
50+ @override
51+ void init ({
52+ required String projectId,
53+ required Uri serverUrl,
54+ }) {
55+ super .init (projectId: projectId, serverUrl: serverUrl);
56+
57+ // Send a batch if stats were tracked while this wasn't initialized yet.
58+ if (statBatch.isNotEmpty && ! disabled) {
59+ sendBatch ();
60+ }
61+ }
3762
3863 /// The field name to track the number of each operation.
3964 final Map <String , int > statBatch = {};
4065
41- /// Debounces the batch sending of the stats to prevent spamming the Firestore
66+ /// Debounces the batch sending of the stats to prevent spamming the server
4267 /// with too many writes.
4368 final DeBouncer debouncer = DeBouncer (const Duration (seconds: 1 ));
4469
45- /// Sends the batch of stats to the Firestore.
70+ bool get disabled =>
71+ clientType == kCodelesslyEditor || projectId == null || serverUrl == null ;
72+
73+ /// Sends the batch of stats to the server.
4674 Future <void > sendBatch () => debouncer.run (
4775 () async {
48- // TODO: call api endpoint.
76+ // TODO(Saad): Use an HTTP client.
77+ post (
78+ serverUrl! ,
79+ body: jsonEncode ({
80+ 'projectId' : projectId,
81+ 'stats' : {
82+ for (final entry in statBatch.entries
83+ .whereNot ((entry) => entry.key == writesField))
84+ entry.key: entry.value,
85+
86+ // Account for this stat tracking operation as an additional write
87+ // operation.
88+ writesField: (statBatch[writesField] ?? 0 ) + 1 ,
89+ },
90+ }),
91+ );
4992 statBatch.clear ();
5093 },
5194 forceRunAfter: 20 ,
@@ -57,36 +100,48 @@ final class FirestoreStatTracker extends StatTracker {
57100
58101 @override
59102 Future <void > trackRead (String label) {
103+ if (disabled) return Future .value ();
104+
60105 incrementField ('$readsField /$label ' );
61106 return sendBatch ();
62107 }
63108
64109 @override
65110 Future <void > trackWrite (String label) {
111+ if (disabled) return Future .value ();
112+
66113 incrementField ('$writesField /$label ' );
67114 return sendBatch ();
68115 }
69116
70117 @override
71118 Future <void > trackBundleDownload () {
119+ if (disabled) return Future .value ();
120+
72121 incrementField (bundleDownloadsField);
73122 return sendBatch ();
74123 }
75124
76125 @override
77126 Future <void > trackFontDownload () {
127+ if (disabled) return Future .value ();
128+
78129 incrementField (fontDownloadsField);
79130 return sendBatch ();
80131 }
81132
82133 @override
83134 Future <void > trackAction (ActionModel action) {
135+ if (disabled) return Future .value ();
136+
84137 incrementField ('$actionsField /${action .type .name }' );
85138 return sendBatch ();
86139 }
87140
88141 @override
89142 Future <void > trackCloudAction (ActionModel action) {
143+ if (disabled) return Future .value ();
144+
90145 incrementField ('$cloudActionsField /${action .type .name }' );
91146 return sendBatch ();
92147 }
0 commit comments