diff --git a/analysis_options.yaml b/analysis_options.yaml index a0480da..2ef22c9 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -3,5 +3,6 @@ analyzer: linter: rules: - annotate_overrides + - directives_ordering - empty_constructor_bodies - empty_statements diff --git a/changelog.md b/changelog.md index 87cde3a..351cb38 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ # Changelog -## unreleased +## 3.3.0 +- added a `close()` method to the `Analytics` class - change our minimum SDK from `1.24.0-dev` to `1.24.0` stable ## 3.2.0 diff --git a/example/ga.dart b/example/ga.dart index bf0807e..ad64249 100644 --- a/example/ga.dart +++ b/example/ga.dart @@ -28,4 +28,8 @@ main(List args) async { await ga.sendTiming('writeDuration', 123); await ga.sendEvent('create', 'consoleapp', label: 'Console App'); print('pinged ${ua}'); + + await ga.waitForLastPing(); + + ga.close(); } diff --git a/lib/src/usage_impl.dart b/lib/src/usage_impl.dart index c0b9ae5..df529e3 100644 --- a/lib/src/usage_impl.dart +++ b/lib/src/usage_impl.dart @@ -214,6 +214,9 @@ class AnalyticsImpl implements Analytics { return f; } + @override + void close() => postHandler.close(); + @override String get clientId => properties['clientId'] ??= new Uuid().generateV4(); @@ -291,4 +294,7 @@ abstract class PersistentProperties { */ abstract class PostHandler { Future sendPost(String url, Map parameters); + + /// Free any used resources. + void close(); } diff --git a/lib/src/usage_impl_html.dart b/lib/src/usage_impl_html.dart index 0072dc3..c15cde6 100644 --- a/lib/src/usage_impl_html.dart +++ b/lib/src/usage_impl_html.dart @@ -30,10 +30,13 @@ class AnalyticsHtml extends AnalyticsImpl { } } +typedef Future HttpRequestor(String url, + {String method, sendData}); + class HtmlPostHandler extends PostHandler { - final Function mockRequestor; + final HttpRequestor mockRequestor; - HtmlPostHandler({Function this.mockRequestor}); + HtmlPostHandler({this.mockRequestor}); @override Future sendPost(String url, Map parameters) { @@ -43,12 +46,16 @@ class HtmlPostHandler extends PostHandler { parameters['vp'] = '${viewportWidth}x$viewportHeight'; String data = postEncode(parameters); - var request = mockRequestor == null ? HttpRequest.request : mockRequestor; - return request(url, method: 'POST', sendData: data).catchError((e) { + HttpRequestor requestor = + mockRequestor == null ? HttpRequest.request : mockRequestor; + return requestor(url, method: 'POST', sendData: data).catchError((e) { // Catch errors that can happen during a request, but that we can't do // anything about, e.g. a missing internet connection. }); } + + @override + void close() {} } class HtmlPersistentProperties extends PersistentProperties { diff --git a/lib/src/usage_impl_io.dart b/lib/src/usage_impl_io.dart index 9ad366a..6e82d41 100644 --- a/lib/src/usage_impl_io.dart +++ b/lib/src/usage_impl_io.dart @@ -77,16 +77,21 @@ class IOPostHandler extends PostHandler { final String _userAgent; final HttpClient mockClient; - IOPostHandler({HttpClient this.mockClient}) : _userAgent = _createUserAgent(); + HttpClient _client; + + IOPostHandler({this.mockClient}) : _userAgent = _createUserAgent(); @override Future sendPost(String url, Map parameters) async { String data = postEncode(parameters); - HttpClient client = mockClient != null ? mockClient : new HttpClient(); - client.userAgent = _userAgent; + if (_client == null) { + _client = mockClient != null ? mockClient : new HttpClient(); + _client.userAgent = _userAgent; + } + try { - HttpClientRequest req = await client.postUrl(Uri.parse(url)); + HttpClientRequest req = await _client.postUrl(Uri.parse(url)); req.write(data); HttpClientResponse response = await req.close(); response.drain(); @@ -95,6 +100,9 @@ class IOPostHandler extends PostHandler { // anything about, e.g. a missing internet connection. } } + + @override + void close() => _client?.close(); } JsonEncoder _jsonEncoder = new JsonEncoder.withIndent(' '); diff --git a/lib/usage.dart b/lib/usage.dart index 52e9f46..e79701c 100644 --- a/lib/usage.dart +++ b/lib/usage.dart @@ -164,9 +164,15 @@ abstract class Analytics { * users won't want their CLI app to pause at the end of the process waiting * for Google analytics requests to complete. This method allows CLI apps to * delay for a short time waiting for GA requests to complete, and then do - * something like call `exit()` explicitly themselves. + * something like call `dart:io`'s `exit()` explicitly themselves (or the + * [close] method below). */ Future waitForLastPing({Duration timeout}); + + /// Free any used resources. + /// + /// The [Analytics] instance should not be used after this call. + void close(); } enum AnalyticsOpt { @@ -313,6 +319,9 @@ class AnalyticsMock implements Analytics { @override Future waitForLastPing({Duration timeout}) => new Future.value(); + @override + void close() {} + Future _log(String hitType, Map m) { if (logCalls) { print('analytics: ${hitType} ${m}'); diff --git a/pubspec.yaml b/pubspec.yaml index 95d6a44..564ba2e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ # BSD-style license that can be found in the LICENSE file. name: usage -version: 3.2.0+1 +version: 3.3.0 description: A Google Analytics wrapper for both command-line, web, and Flutter apps. homepage: https://github.com/dart-lang/usage author: Dart Team diff --git a/readme.md b/readme.md index 8e1adae..931f91f 100644 --- a/readme.md +++ b/readme.md @@ -52,9 +52,15 @@ have completed, or until the specified duration has elapsed. So, CLI apps can do something like: ```dart -analytics.waitForLastPing(timeout: new Duration(milliseconds: 500)).then((_) { - exit(0); -}); +await analytics.waitForLastPing(timeout: new Duration(milliseconds: 200)); +analytics.close(); +``` + +or: + +```dart +await analytics.waitForLastPing(timeout: new Duration(milliseconds: 200)); +exit(0); ``` ## Using the API diff --git a/test/all.dart b/test/all.dart index 393640a..9f93051 100644 --- a/test/all.dart +++ b/test/all.dart @@ -5,15 +5,15 @@ library usage.all_test; import 'hit_types_test.dart' as hit_types_test; -import 'usage_test.dart' as usage_test; -import 'usage_impl_test.dart' as usage_impl_test; import 'usage_impl_io_test.dart' as usage_impl_io_test; +import 'usage_impl_test.dart' as usage_impl_test; +import 'usage_test.dart' as usage_test; import 'uuid_test.dart' as uuid_test; void main() { hit_types_test.defineTests(); - usage_test.defineTests(); - usage_impl_test.defineTests(); usage_impl_io_test.defineTests(); + usage_impl_test.defineTests(); + usage_test.defineTests(); uuid_test.defineTests(); } diff --git a/test/src/common.dart b/test/src/common.dart index 2ace7eb..7799394 100644 --- a/test/src/common.dart +++ b/test/src/common.dart @@ -57,4 +57,7 @@ class MockPostHandler extends PostHandler { } Map get last => sentValues.last; + + @override + void close() {} } diff --git a/test/web_test.dart b/test/web_test.dart index 2ddc853..8d91ae9 100644 --- a/test/web_test.dart +++ b/test/web_test.dart @@ -6,6 +6,7 @@ library usage.web_test; import 'dart:async'; +import 'dart:html'; import 'package:test/test.dart'; import 'package:usage/src/usage_impl_html.dart'; @@ -61,7 +62,7 @@ void defineWebTests() { class MockRequestor { int sendCount = 0; - Future request(String url, {String method, String sendData}) { + Future request(String url, {String method, sendData}) { expect(url, isNotEmpty); expect(method, isNotEmpty); expect(sendData, isNotEmpty);