mojo/discovery: global Discovery port to the Dart API.

Closes https://github.com/vanadium/issues/issues/1282

Change-Id: I5de052e7e99e588d36d4067d70e33304418944af
diff --git a/Makefile b/Makefile
index 7afd589..5f7a560 100644
--- a/Makefile
+++ b/Makefile
@@ -108,7 +108,7 @@
 
 # Cleanup
 clean:
-	rm -rf build
+	rm -rf gen
 	rm -rf lib/gen/dart-pkg
 	rm -rf lib/gen/mojom
 	rm -rf $(PACKAGE_MOJO_BIN_DIR)
diff --git a/lib/discovery.dart b/lib/discovery.dart
index f66f064..90b4f7f 100644
--- a/lib/discovery.dart
+++ b/lib/discovery.dart
@@ -16,10 +16,40 @@
 typedef void ConnectToServiceFunction(String url, bindings.ProxyBase proxy);
 
 abstract class Client {
+  /// Creates a local discovery service.
+  ///
+  /// Local discovery uses MDNS, BLE and other proximity-based solutions to
+  /// advertise and scan for local services.
   factory Client(ConnectToServiceFunction cts, String url) {
     return new _Client(cts, url);
   }
 
+  /// Creates a global discovery service.
+  ///
+  /// You can connect to a global discovery service that uses the Vanadium namespace
+  /// under [path]. Optionally you can set [ttl] (default is 120s) and
+  /// [scanInteral] (default is 90s) to control the time-to-live and scan intervals.
+  ///
+  /// Global discovery is an experimental work to see its feasibility and set the
+  /// long-term goal, and can be changed without notice.
+  factory Client.global(ConnectToServiceFunction cts, String url, String path,
+      {Duration ttl, Duration scanInteral}) {
+    Uri originalUri = Uri.parse(url);
+    Map<String, String> qs = new Map.from(originalUri.queryParameters);
+
+    if (path == null) {
+      throw new ArgumentError.notNull('path');
+    }
+    qs['global'] = path;
+    if (ttl != null) {
+      qs['mount_ttl'] = "${ttl.inSeconds}s";
+    }
+    if (scanInteral != null) {
+      qs['scan_interval'] = "${scanInteral.inSeconds}s";
+    }
+    return new Client(cts, originalUri.replace(queryParameters: qs).toString());
+  }
+
   /// Scan scans advertisements that match the query and returns a scanner handle that
   /// includes streams of found and lost advertisements.
   /// Scanning will continue until [stop] is called on the [Scanner] handle.
diff --git a/test/discovery_apptests.dart b/test/discovery_apptests.dart
index 58038ec..28b3ffa 100644
--- a/test/discovery_apptests.dart
+++ b/test/discovery_apptests.dart
@@ -10,28 +10,29 @@
 
 import 'package:v23discovery/discovery.dart';
 
+const String mojoUrl = 'https://mojo.v.io/discovery.mojo';
+const String interfaceName = 'v.io/myInterface';
+
 main(List args, Object handleToken) {
   runAppTests(handleToken, [discoveryApptests]);
 }
 
 discoveryApptests(Application application, String url) {
-  test('Advertise and Scan', () async {
-    const String mojoUrl = 'https://mojo.v.io/discovery.mojo';
-    const String interfaceName = 'v.io/myInterface';
-
+  test('Local Discovery - Advertise and Scan', () async {
     // Advertise
-    Client client1 = new Client(application.connectToService, mojoUrl);
-    Advertisement input = new Advertisement(
-        interfaceName, ['v.io/myAddress1', 'v.io/myAddress2']);
+    Client advertiseClient = new Client(application.connectToService, mojoUrl);
+    Advertisement input =
+        new Advertisement(interfaceName, ['/h1:123/x', '/h1:123/y']);
     input.attributes['myAttr1'] = 'myAttrValue1';
     input.attributes['myAttr2'] = 'myAttrValue2';
     String attachmentContent = new List.generate(500, (_) => 'X').join();
     input.attachments['myAttachment'] = UTF8.encode(attachmentContent);
-    Advertiser advertiser = await client1.advertise(input);
+    Advertiser advertiser = await advertiseClient.advertise(input);
 
     // Scan
-    Client client2 = new Client(application.connectToService, mojoUrl);
-    Scanner scanner = await client2.scan('v.InterfaceName = "$interfaceName"');
+    Client scanClient = new Client(application.connectToService, mojoUrl);
+    Scanner scanner =
+        await scanClient.scan('v.InterfaceName = "$interfaceName"');
     Update update = await scanner.onUpdate.first;
 
     expect(update.updateType, equals(UpdateTypes.found));
@@ -47,4 +48,36 @@
     await advertiser.stop();
     await scanner.stop();
   });
+
+  // TODO(aghassemi): Multiple mojo connections seem to hange in Dart
+  // Test passes on its own but not when combined with the test above.
+  // Disabling for now until the mojo issues is resolved.
+  // test('Global Discovery - Advertise and Scan', () async {
+  //   const String path = 'a/b';
+  //   const Duration ttl = const Duration(seconds: 10);
+  //   const Duration scanInterval = const Duration(seconds: 1);
+  //
+  //   // Advertise
+  //   // Gloabl discovery only supports address.
+  //   Client advertiseClient = new Client.global(
+  //       application.connectToService, mojoUrl, path,
+  //       ttl: ttl);
+  //   Advertisement input = new Advertisement('', ['/h1:123/x', '/h1:123/y']);
+  //   Advertiser advertiser = await advertiseClient.advertise(input);
+  //
+  //   // Scan
+  //   Client scanClient = new Client.global(
+  //       application.connectToService, mojoUrl, path,
+  //       scanInteral: scanInterval);
+  //   Scanner scanner = await scanClient.scan('');
+  //   Update update = await scanner.onUpdate.first;
+  //
+  //   expect(update.updateType, equals(UpdateTypes.found));
+  //   expect(update.id, isNotEmpty);
+  //   expect(update.addresses, equals(input.addresses));
+  //
+  //   // Clean up
+  //   await advertiser.stop();
+  //   await scanner.stop();
+  // });
 }