Index: usage/lib/src/usage_impl.dart |
diff --git a/usage/lib/src/usage_impl.dart b/usage/lib/src/usage_impl.dart |
deleted file mode 100644 |
index fc2c8a7af79a1f82a2195b312c3229cb1a5c341f..0000000000000000000000000000000000000000 |
--- a/usage/lib/src/usage_impl.dart |
+++ /dev/null |
@@ -1,232 +0,0 @@ |
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-library usage_impl; |
- |
-import 'dart:async'; |
-import 'dart:math' as math; |
- |
-import 'uuid.dart'; |
-import '../usage.dart'; |
- |
-final int _MAX_EXCEPTION_LENGTH = 100; |
- |
-String postEncode(Map<String, dynamic> map) { |
- // &foo=bar |
- return map.keys.map((key) { |
- String value = '${map[key]}'; |
- return "${key}=${Uri.encodeComponent(value)}"; |
- }).join('&'); |
-} |
- |
-/** |
- * A throttling algorithim. This models the throttling after a bucket with |
- * water dripping into it at the rate of 1 drop per second. If the bucket has |
- * water when an operation is requested, 1 drop of water is removed and the |
- * operation is performed. If not the operation is skipped. This algorithim |
- * lets operations be peformed in bursts without throttling, but holds the |
- * overall average rate of operations to 1 per second. |
- */ |
-class ThrottlingBucket { |
- final int startingCount; |
- int drops; |
- int _lastReplenish; |
- |
- ThrottlingBucket(this.startingCount) { |
- drops = startingCount; |
- _lastReplenish = new DateTime.now().millisecondsSinceEpoch; |
- } |
- |
- bool removeDrop() { |
- _checkReplenish(); |
- |
- if (drops <= 0) { |
- return false; |
- } else { |
- drops--; |
- return true; |
- } |
- } |
- |
- void _checkReplenish() { |
- int now = new DateTime.now().millisecondsSinceEpoch; |
- |
- if (_lastReplenish + 1000 >= now) { |
- int inc = (now - _lastReplenish) ~/ 1000; |
- drops = math.min(drops + inc, startingCount); |
- _lastReplenish += (1000 * inc); |
- } |
- } |
-} |
- |
-abstract class AnalyticsImpl extends Analytics { |
- static const String _GA_URL = 'https://www.google-analytics.com/collect'; |
- |
- /// Tracking ID / Property ID. |
- final String trackingId; |
- |
- final PersistentProperties properties; |
- final PostHandler postHandler; |
- |
- final ThrottlingBucket _bucket = new ThrottlingBucket(20); |
- final Map<String, dynamic> _variableMap = {}; |
- |
- final List<Future> _futures = []; |
- |
- AnalyticsImpl(this.trackingId, this.properties, this.postHandler, |
- {String applicationName, String applicationVersion}) { |
- assert(trackingId != null); |
- |
- if (applicationName != null) setSessionValue('an', applicationName); |
- if (applicationVersion != null) setSessionValue('av', applicationVersion); |
- } |
- |
- bool get optIn => properties['optIn'] == true; |
- |
- set optIn(bool value) { |
- properties['optIn'] = value; |
- } |
- |
- bool get hasSetOptIn => properties['optIn'] != null; |
- |
- Future sendScreenView(String viewName) { |
- Map args = {'cd': viewName}; |
- return _sendPayload('screenview', args); |
- } |
- |
- Future sendEvent(String category, String action, {String label, int value}) { |
- if (!optIn) return new Future.value(); |
- |
- Map args = {'ec': category, 'ea': action}; |
- if (label != null) args['el'] = label; |
- if (value != null) args['ev'] = value; |
- return _sendPayload('event', args); |
- } |
- |
- Future sendSocial(String network, String action, String target) { |
- if (!optIn) return new Future.value(); |
- |
- Map args = {'sn': network, 'sa': action, 'st': target}; |
- return _sendPayload('social', args); |
- } |
- |
- Future sendTiming(String variableName, int time, {String category, |
- String label}) { |
- if (!optIn) return new Future.value(); |
- |
- Map args = {'utv': variableName, 'utt': time}; |
- if (label != null) args['utl'] = label; |
- if (category != null) args['utc'] = category; |
- return _sendPayload('timing', args); |
- } |
- |
- AnalyticsTimer startTimer(String variableName, {String category, String label}) { |
- return new AnalyticsTimer(this, |
- variableName, category: category, label: label); |
- } |
- |
- Future sendException(String description, {bool fatal}) { |
- if (!optIn) return new Future.value(); |
- |
- // In order to ensure that the client of this API is not sending any PII |
- // data, we strip out any stack trace that may reference a path on the |
- // user's drive (file:/...). |
- if (description.contains('file:/')) { |
- description = description.substring(0, description.indexOf('file:/')); |
- } |
- |
- if (description != null && description.length > _MAX_EXCEPTION_LENGTH) { |
- description = description.substring(0, _MAX_EXCEPTION_LENGTH); |
- } |
- |
- Map args = {'exd': description}; |
- if (fatal != null && fatal) args['exf'] = '1'; |
- return _sendPayload('exception', args); |
- } |
- |
- void setSessionValue(String param, dynamic value) { |
- if (value == null) { |
- _variableMap.remove(param); |
- } else { |
- _variableMap[param] = value; |
- } |
- } |
- |
- Future waitForLastPing({Duration timeout}) { |
- Future f = Future.wait(_futures).catchError((e) => null); |
- |
- if (timeout != null) { |
- f = f.timeout(timeout, onTimeout: () => null); |
- } |
- |
- return f; |
- } |
- |
- /** |
- * Anonymous Client ID. The value of this field should be a random UUID v4. |
- */ |
- String get _clientId => properties['clientId']; |
- |
- void _initClientId() { |
- if (_clientId == null) { |
- properties['clientId'] = new Uuid().generateV4(); |
- } |
- } |
- |
- // Valid values for [hitType] are: 'pageview', 'screenview', 'event', |
- // 'transaction', 'item', 'social', 'exception', and 'timing'. |
- Future _sendPayload(String hitType, Map args) { |
- if (_bucket.removeDrop()) { |
- _initClientId(); |
- |
- _variableMap.forEach((key, value) { |
- args[key] = value; |
- }); |
- |
- args['v'] = '1'; // protocol version |
- args['tid'] = trackingId; |
- args['cid'] = _clientId; |
- args['t'] = hitType; |
- |
- return _recordFuture(postHandler.sendPost(_GA_URL, args)); |
- } else { |
- return new Future.value(); |
- } |
- } |
- |
- Future _recordFuture(Future f) { |
- _futures.add(f); |
- return f.whenComplete(() => _futures.remove(f)); |
- } |
-} |
- |
-/** |
- * A persistent key/value store. An [AnalyticsImpl] instance expects to have one |
- * of these injected into it. There are default implementations for `dart:io` |
- * and `dart:html` clients. |
- * |
- * The [name] paramater is used to uniquely store these properties on disk / |
- * persistent storage. |
- */ |
-abstract class PersistentProperties { |
- final String name; |
- |
- PersistentProperties(this.name); |
- |
- dynamic operator[](String key); |
- void operator[]=(String key, dynamic value); |
-} |
- |
-/** |
- * A utility class to perform HTTP POSTs. An [AnalyticsImpl] instance expects to |
- * have one of these injected into it. There are default implementations for |
- * `dart:io` and `dart:html` clients. |
- * |
- * The POST information should be sent on a best-effort basis. The `Future` from |
- * [sendPost] should complete when the operation is finished, but failures to |
- * send the information should be silent. |
- */ |
-abstract class PostHandler { |
- Future sendPost(String url, Map<String, String> parameters); |
-} |