mojo/discovery: Attachment support for Dart client
library and adding dart apptests.
Change-Id: I618585332d239c1ea0105202330e7e6871b7d472
diff --git a/.gitignore b/.gitignore
index 34b760f..9460c52 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,8 @@
/lib/gen/dart-pkg
/lib/gen/mojom
/packages
+/example/packages
+/test/packages
/.packages
/.pub
/pubspec.lock
diff --git a/Makefile b/Makefile
index 5d924fe..043c70c 100644
--- a/Makefile
+++ b/Makefile
@@ -98,7 +98,7 @@
$(call MOGO_TEST,-v vanadium/discovery/internal/...)
.PHONY: apptest
-apptest: mojoapptests $(DISCOVERY_BUILD_DIR)/discovery_apptests.mojo | mojo-env-check
+apptest: build mojoapptests | mojo-env-check
$(call MOJO_APPTEST,"mojoapptests")
$(DISCOVERY_BUILD_DIR)/discovery_apptests.mojo: $(V23_GO_FILES) | mojo-env-check
diff --git a/go/src/mojom/vanadium/discovery/discovery.mojom.go b/go/src/mojom/vanadium/discovery/discovery.mojom.go
index f4da926..39cdb12 100644
--- a/go/src/mojom/vanadium/discovery/discovery.mojom.go
+++ b/go/src/mojom/vanadium/discovery/discovery.mojom.go
@@ -50,6 +50,7 @@
return discovery_Name
}
+// TODO(rudominer) This should only be defined for top-level interfaces.
func (f *Discovery_ServiceFactory) ServiceDescription() service_describer.ServiceDescription {
return &Discovery_ServiceDescription{}
}
@@ -691,6 +692,7 @@
Create(request Closer_Request)
}
+// TODO(rudominer) This should only be defined for top-level interfaces.
func (f *Closer_ServiceFactory) ServiceDescription() service_describer.ServiceDescription {
return &Closer_ServiceDescription{}
}
@@ -928,6 +930,7 @@
Create(request ScanHandler_Request)
}
+// TODO(rudominer) This should only be defined for top-level interfaces.
func (f *ScanHandler_ServiceFactory) ServiceDescription() service_describer.ServiceDescription {
return &ScanHandler_ServiceDescription{}
}
@@ -1119,6 +1122,7 @@
Create(request Update_Request)
}
+// TODO(rudominer) This should only be defined for top-level interfaces.
func (f *Update_ServiceFactory) ServiceDescription() service_describer.ServiceDescription {
return &Update_ServiceDescription{}
}
diff --git a/lib/client_impl.dart b/lib/client_impl.dart
index 8c5a0ff..242a78e 100644
--- a/lib/client_impl.dart
+++ b/lib/client_impl.dart
@@ -3,74 +3,111 @@
// license that can be found in the LICENSE file.
part of discovery;
-typedef Future _StopFunction();
-
class _Client implements Client {
- final DiscoveryProxy _discoveryProxy = new DiscoveryProxy.unbound();
+ final mojom.DiscoveryProxy _discoveryProxy =
+ new mojom.DiscoveryProxy.unbound();
_Client(ConnectToServiceFunction cts, String url) {
cts(url, _discoveryProxy);
}
Future<Scanner> scan(String query) async {
- StreamController<ScanUpdate> onUpdate = new StreamController<ScanUpdate>();
- ScanHandlerStub handlerStub = new ScanHandlerStub.unbound();
+ StreamController<Update> onUpdate = new StreamController<Update>();
+
+ mojom.ScanHandlerStub handlerStub = new mojom.ScanHandlerStub.unbound();
handlerStub.impl = new _ScanHandler(onUpdate);
- DiscoveryStartScanResponseParams scanResponse =
- await _discoveryProxy.ptr.startScan(query, handlerStub);
+ mojom.DiscoveryScanResponseParams scanResponse =
+ await _discoveryProxy.ptr.scan(query, handlerStub);
+
if (scanResponse.err != null) {
throw scanResponse.err;
}
- Future stop() {
- return _discoveryProxy.ptr.stopScan(scanResponse.scanId);
- }
- return new _Scanner(stop, onUpdate.stream);
+ return new _Scanner(scanResponse.closer, onUpdate.stream);
}
- Future<Advertiser> advertise(Service service,
+ Future<Advertiser> advertise(Advertisement advertisement,
{List<String> visibility: null}) async {
- DiscoveryStartAdvertisingResponseParams advertiseResponse =
- await _discoveryProxy.ptr.startAdvertising(service, visibility);
+ mojom.Advertisement mAdvertisement = new mojom.Advertisement()
+ ..id = advertisement.id
+ ..interfaceName = advertisement.interfaceName
+ ..attributes = advertisement.attributes
+ ..attachments = advertisement.attachments
+ ..addresses = advertisement.addresses;
+
+ mojom.DiscoveryAdvertiseResponseParams advertiseResponse =
+ await _discoveryProxy.ptr.advertise(mAdvertisement, visibility);
if (advertiseResponse.err != null) {
throw advertiseResponse.err;
}
- Future stop() {
- return _discoveryProxy.ptr.stopAdvertising(advertiseResponse.instanceId);
- }
- return new _Advertiser(stop);
+ return new _Advertiser(advertiseResponse.closer);
}
}
class _Scanner implements Scanner {
- final Stream<ScanUpdate> onUpdate;
+ final Stream<Update> onUpdate;
- final _StopFunction _stop;
- _Scanner(this._stop, this.onUpdate) {}
+ final mojom.CloserProxy _closer;
+ _Scanner(this._closer, this.onUpdate) {}
Future stop() {
- return _stop();
+ return _closer.close();
}
}
class _Advertiser implements Advertiser {
- final _StopFunction _stop;
- _Advertiser(this._stop) {}
+ final mojom.CloserProxy _closer;
+ _Advertiser(this._closer) {}
Future stop() {
- return _stop();
+ return _closer.close();
}
}
-class _ScanHandler extends ScanHandler {
- StreamController<ScanUpdate> _onUpdate;
+class _ScanHandler extends mojom.ScanHandler {
+ StreamController<Update> _onUpdate;
_ScanHandler(this._onUpdate);
- update(ScanUpdate update) {
+ onUpdate(mojom.UpdateProxy mUpdate) async {
+ mojom.UpdateIsLostResponseParams isLostParams = await mUpdate.ptr.isLost();
+ bool isLost = isLostParams.lost;
+
+ mojom.UpdateGetAdvertisementResponseParams advertisementParams =
+ await mUpdate.ptr.getAdvertisement();
+
+ Future<List<int>> attachmentFetcher(String key) async {
+ var attachmentResponse = mUpdate.ptr.getAttachment(key);
+
+ if (!(attachmentResponse is mojom.UpdateGetAttachmentResponseParams)) {
+ throw new ArgumentError.value(key, 'key', 'Attachment does not exist');
+ }
+
+ ByteData data =
+ await mojo_core.DataPipeDrainer.drainHandle(attachmentResponse.data);
+ return data.buffer.asUint8List().toList();
+ }
+
+ mojom.Advertisement mAdvertisement = advertisementParams.ad;
+
+ Update update = new Update._internal(
+ isLost ? UpdateTypes.lost : UpdateTypes.found,
+ attachmentFetcher,
+ mAdvertisement.id,
+ mAdvertisement.interfaceName);
+
+ if (mAdvertisement.attributes != null) {
+ update.attributes = mAdvertisement.attributes;
+ }
+ if (mAdvertisement.addresses != null) {
+ update.addresses = mAdvertisement.addresses;
+ }
+ if (mAdvertisement.attachments != null) {
+ update._attachments = mAdvertisement.attachments;
+ }
_onUpdate.add(update);
}
}
diff --git a/lib/discovery.dart b/lib/discovery.dart
index fefb827..d70ba95 100644
--- a/lib/discovery.dart
+++ b/lib/discovery.dart
@@ -4,12 +4,12 @@
library discovery;
import 'dart:async';
+import 'dart:typed_data';
import 'package:mojo/bindings.dart' as bindings;
-import 'gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart';
+import 'package:mojo/core.dart' as mojo_core;
-export 'gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart'
- show Service, ScanUpdate, UpdateType;
+import './gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart' as mojom;
part 'client_impl.dart';
@@ -20,50 +20,49 @@
return new _Client(cts, url);
}
- /// Scan scans services that match the query and returns a scanner handle that
- /// includes streams of found and lost services.
+ /// 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.
///
- /// For example, the following code waits until finding the first service that matches the
+ /// For example, the following code waits until finding the first advertisement that matches the
/// query and then stops scanning.
///
/// Scanner scanner = client.scan('v.InterfaceName = "v.io/i" AND v.Attrs["a"] = "v"');
- /// Service firstFoundService = await scanner.onUpdate.firstWhere((update) => update.updateType == UpdateTypes.found).service;
+ /// Update firstFound = await scanner.onUpdate.firstWhere((update) => update.updateType == UpdateTypes.found);
/// scanner.stop();
///
- /// The query is a WHERE expression of a syncQL query against advertised services, where
- /// keys are InstanceIds and values are Services.
+ /// The query is a WHERE expression of a syncQL query against advertisements, where
+ /// keys are Ids and values are Advertisement.
///
/// SyncQL tutorial at:
/// https://github.com/vanadium/docs/blob/master/tutorials/syncql-tutorial.md
Future<Scanner> scan(String query);
- /// Advertise advertises the [Service] to be discovered by [scan].
+ /// Advertise the [Advertisement] to be discovered by [scan].
/// [visibility] is used to limit the principals that can see the advertisement.
/// An empty or null [visibility] means that there are no restrictions on visibility.
/// Advertising will continue until [stop] is called on the [Advertiser] handle.
///
- /// If service.InstanceId is not specified, a random unique identifier will be
- /// assigned to it. Any change to service will not be applied after advertising starts.
+ /// If advertisement.id is not specified, a random unique identifier will be
+ /// assigned to it. Any change to advertisement will not be applied after advertising starts.
///
/// It is an error to have simultaneously active advertisements for two identical
- /// instances (service.InstanceId).
+ /// instances (advertisement.id).
///
- /// For example, the following code advertises a service for 10 seconds.
+ /// For example, the following code advertises an advertisement for 10 seconds.
///
- /// Service service = new Service()
- /// ..interfaceName = 'v.io/i'
- /// ..attrs = {'a', 'v'};
- /// Advertiser advertiser = client.advertise(service);
+ /// Advertisement ad = new Advertisement('v.io/interfaceName', ['v.io/address']);
+ /// ad.attributes['a'] = 'v';
+ /// Advertiser advertiser = client.advertise(ad);
/// new Timer(const Duration(seconds: 10), () => advertiser.stop());
- Future<Advertiser> advertise(Service service,
+ Future<Advertiser> advertise(Advertisement advertisement,
{List<String> visibility: null});
}
/// Handle to a scan call.
abstract class Scanner {
- /// A stream of [Update] objects as services are found or lost by the scanner.
- Stream<ScanUpdate> get onUpdate;
+ /// A stream of [Update] objects as advertisements are found or lost by the scanner.
+ Stream<Update> get onUpdate;
/// Stops scanning.
Future stop();
@@ -74,3 +73,85 @@
/// Stops the advertisement.
Future stop();
}
+
+/// Advertisement represents a feed into advertiser to broadcast its contents
+/// to scanners.
+///
+/// A large advertisement may require additional RPC calls causing delay in
+/// discovery. We limit the maximum size of an advertisement to 512 bytes
+/// excluding id and attachments.
+class Advertisement {
+ /// Universal unique identifier of the advertisement.
+ /// If this is not specified, a random unique identifier will be assigned.
+ List<int> id = null;
+
+ /// Interface name that the advertised service implements.
+ /// E.g., 'v.io/v23/services/vtrace.Store'.
+ String interfaceName = null;
+
+ /// Addresses (vanadium object names) that the advertised service is served on.
+ /// E.g., '/host:port/a/b/c', '/ns.dev.v.io:8101/blah/blah'.
+ List<String> addresses = null;
+
+ /// Attributes as a key/value pair.
+ /// E.g., {'resolution': '1024x768'}.
+ ///
+ /// The key must be US-ASCII printable characters, excluding the '=' character
+ /// and should not start with '_' character.
+ Map<String, String> attributes = new Map<String, String>();
+
+ /// Attachments as a key/value pair.
+ /// E.g., {'thumbnail': binary_data }.
+ ///
+ /// Unlike attributes, attachments are for binary data and they are not queryable.
+ /// We limit the maximum size of a single attachment to 4K bytes.
+ ///
+ /// The key must be US-ASCII printable characters, excluding the '=' character
+ /// and should not start with '_' character.
+ Map<String, List<int>> attachments = new Map<String, List<int>>();
+
+ Advertisement(this.interfaceName, this.addresses);
+}
+
+/// Enum for different types of updates.
+enum UpdateTypes { found, lost }
+
+/// Update represents a discovery update.
+class Update {
+ // The update type.
+ UpdateTypes updateType;
+
+ // The universal unique identifier of the advertisement.
+ List<int> id = null;
+
+ // The interface name that the service implements.
+ String interfaceName = null;
+
+ // The addresses (vanadium object names) that the service
+ // is served on.
+ List<String> addresses = new List<String>();
+
+ // Returns the named attribute. An empty string is returned if
+ // not found.
+ Map<String, String> attributes = new Map<String, String>();
+
+ Map<String, List<int>> _attachments = new Map<String, List<int>>();
+ Function _attachmentFetcher;
+
+ /// Fetches an attachment on-demand from its source.
+ /// ArgumentError is thrown if not found.
+ ///
+ /// This may do RPC calls if the attachment is not fetched yet.
+ ///
+ /// Attachments may not be available when this update is for lost advertisement.
+ Future<List<int>> fetchAttachment(String key) async {
+ if (_attachments.containsKey(key)) {
+ return _attachments[key];
+ }
+
+ return _attachmentFetcher(key);
+ }
+
+ Update._internal(
+ this.updateType, this._attachmentFetcher, this.id, this.interfaceName);
+}
diff --git a/lib/gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart b/lib/gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart
index 20eb267..128c2b8 100644
--- a/lib/gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart
+++ b/lib/gen/dart-gen/mojom/lib/discovery/discovery.mojom.dart
@@ -3,12 +3,9 @@
// found in the LICENSE file.
library discovery_mojom;
-
import 'dart:async';
-
import 'package:mojo/bindings.dart' as bindings;
import 'package:mojo/core.dart' as core;
-import 'package:mojo/mojo/bindings/types/mojom_types.mojom.dart' as mojom_types;
import 'package:mojo/mojo/bindings/types/service_describer.mojom.dart' as service_describer;
@@ -158,69 +155,87 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeUint8Array(id, 8, bindings.kArrayNullable, 16);
-
- encoder0.encodeString(interfaceName, 16, false);
-
- if (addresses == null) {
- encoder0.encodeNullPointer(24, false);
- } else {
- var encoder1 = encoder0.encodePointerArray(addresses.length, 24, bindings.kUnspecifiedArrayLength);
- for (int i0 = 0; i0 < addresses.length; ++i0) {
+ try {
+ encoder0.encodeUint8Array(id, 8, bindings.kArrayNullable, 16);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "id of struct Advertisement: $e";
+ rethrow;
+ }
+ try {
+ encoder0.encodeString(interfaceName, 16, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "interfaceName of struct Advertisement: $e";
+ rethrow;
+ }
+ try {
+ if (addresses == null) {
+ encoder0.encodeNullPointer(24, false);
+ } else {
+ var encoder1 = encoder0.encodePointerArray(addresses.length, 24, bindings.kUnspecifiedArrayLength);
+ for (int i0 = 0; i0 < addresses.length; ++i0) {
+ encoder1.encodeString(addresses[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+ }
+ }
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "addresses of struct Advertisement: $e";
+ rethrow;
+ }
+ try {
+ if (attributes == null) {
+ encoder0.encodeNullPointer(32, true);
+ } else {
+ var encoder1 = encoder0.encoderForMap(32);
+ var keys0 = attributes.keys.toList();
+ var values0 = attributes.values.toList();
- encoder1.encodeString(addresses[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+ {
+ var encoder2 = encoder1.encodePointerArray(keys0.length, bindings.ArrayDataHeader.kHeaderSize, bindings.kUnspecifiedArrayLength);
+ for (int i1 = 0; i1 < keys0.length; ++i1) {
+ encoder2.encodeString(keys0[i1], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+ }
+ }
+
+ {
+ var encoder2 = encoder1.encodePointerArray(values0.length, bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize, bindings.kUnspecifiedArrayLength);
+ for (int i1 = 0; i1 < values0.length; ++i1) {
+ encoder2.encodeString(values0[i1], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+ }
+ }
}
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "attributes of struct Advertisement: $e";
+ rethrow;
}
-
- if (attributes == null) {
- encoder0.encodeNullPointer(32, true);
- } else {
- var encoder1 = encoder0.encoderForMap(32);
- int size0 = attributes.length;
- var keys0 = attributes.keys.toList();
- var values0 = attributes.values.toList();
-
- {
- var encoder2 = encoder1.encodePointerArray(keys0.length, bindings.ArrayDataHeader.kHeaderSize, bindings.kUnspecifiedArrayLength);
- for (int i1 = 0; i1 < keys0.length; ++i1) {
-
- encoder2.encodeString(keys0[i1], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+ try {
+ if (attachments == null) {
+ encoder0.encodeNullPointer(40, true);
+ } else {
+ var encoder1 = encoder0.encoderForMap(40);
+ var keys0 = attachments.keys.toList();
+ var values0 = attachments.values.toList();
+
+ {
+ var encoder2 = encoder1.encodePointerArray(keys0.length, bindings.ArrayDataHeader.kHeaderSize, bindings.kUnspecifiedArrayLength);
+ for (int i1 = 0; i1 < keys0.length; ++i1) {
+ encoder2.encodeString(keys0[i1], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
+ }
+ }
+
+ {
+ var encoder2 = encoder1.encodePointerArray(values0.length, bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize, bindings.kUnspecifiedArrayLength);
+ for (int i1 = 0; i1 < values0.length; ++i1) {
+ encoder2.encodeUint8Array(values0[i1], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, bindings.kNothingNullable, bindings.kUnspecifiedArrayLength);
+ }
}
}
-
- {
- var encoder2 = encoder1.encodePointerArray(values0.length, bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize, bindings.kUnspecifiedArrayLength);
- for (int i1 = 0; i1 < values0.length; ++i1) {
-
- encoder2.encodeString(values0[i1], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
- }
- }
- }
-
- if (attachments == null) {
- encoder0.encodeNullPointer(40, true);
- } else {
- var encoder1 = encoder0.encoderForMap(40);
- int size0 = attachments.length;
- var keys0 = attachments.keys.toList();
- var values0 = attachments.values.toList();
-
- {
- var encoder2 = encoder1.encodePointerArray(keys0.length, bindings.ArrayDataHeader.kHeaderSize, bindings.kUnspecifiedArrayLength);
- for (int i1 = 0; i1 < keys0.length; ++i1) {
-
- encoder2.encodeString(keys0[i1], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, false);
- }
- }
-
- {
- var encoder2 = encoder1.encodePointerArray(values0.length, bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize, bindings.kUnspecifiedArrayLength);
- for (int i1 = 0; i1 < values0.length; ++i1) {
-
- encoder2.encodeUint8Array(values0[i1], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i1, bindings.kNothingNullable, bindings.kUnspecifiedArrayLength);
- }
- }
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "attachments of struct Advertisement: $e";
+ rethrow;
}
}
@@ -307,12 +322,27 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeString(id, 8, false);
-
- encoder0.encodeUint32(actionCode, 16);
-
- encoder0.encodeString(msg, 24, false);
+ try {
+ encoder0.encodeString(id, 8, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "id of struct Error: $e";
+ rethrow;
+ }
+ try {
+ encoder0.encodeUint32(actionCode, 16);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "actionCode of struct Error: $e";
+ rethrow;
+ }
+ try {
+ encoder0.encodeString(msg, 24, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "msg of struct Error: $e";
+ rethrow;
+ }
}
String toString() {
@@ -400,17 +430,26 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeStruct(ad, 8, false);
-
- if (visibility == null) {
- encoder0.encodeNullPointer(16, true);
- } else {
- var encoder1 = encoder0.encodePointerArray(visibility.length, 16, bindings.kUnspecifiedArrayLength);
- for (int i0 = 0; i0 < visibility.length; ++i0) {
-
- encoder1.encodeString(visibility[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+ try {
+ encoder0.encodeStruct(ad, 8, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "ad of struct _DiscoveryAdvertiseParams: $e";
+ rethrow;
+ }
+ try {
+ if (visibility == null) {
+ encoder0.encodeNullPointer(16, true);
+ } else {
+ var encoder1 = encoder0.encodePointerArray(visibility.length, 16, bindings.kUnspecifiedArrayLength);
+ for (int i0 = 0; i0 < visibility.length; ++i0) {
+ encoder1.encodeString(visibility[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+ }
}
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "visibility of struct _DiscoveryAdvertiseParams: $e";
+ rethrow;
}
}
@@ -492,12 +531,27 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeUint8Array(instanceId, 8, bindings.kArrayNullable, 16);
-
- encoder0.encodeInterface(closer, 16, true);
-
- encoder0.encodeStruct(err, 24, true);
+ try {
+ encoder0.encodeUint8Array(instanceId, 8, bindings.kArrayNullable, 16);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "instanceId of struct DiscoveryAdvertiseResponseParams: $e";
+ rethrow;
+ }
+ try {
+ encoder0.encodeInterface(closer, 16, true);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "closer of struct DiscoveryAdvertiseResponseParams: $e";
+ rethrow;
+ }
+ try {
+ encoder0.encodeStruct(err, 24, true);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "err of struct DiscoveryAdvertiseResponseParams: $e";
+ rethrow;
+ }
}
String toString() {
@@ -571,10 +625,20 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeString(query, 8, false);
-
- encoder0.encodeInterface(handler, 16, false);
+ try {
+ encoder0.encodeString(query, 8, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "query of struct _DiscoveryScanParams: $e";
+ rethrow;
+ }
+ try {
+ encoder0.encodeInterface(handler, 16, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "handler of struct _DiscoveryScanParams: $e";
+ rethrow;
+ }
}
String toString() {
@@ -648,10 +712,20 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeInterface(closer, 8, true);
-
- encoder0.encodeStruct(err, 16, true);
+ try {
+ encoder0.encodeInterface(closer, 8, true);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "closer of struct DiscoveryScanResponseParams: $e";
+ rethrow;
+ }
+ try {
+ encoder0.encodeStruct(err, 16, true);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "err of struct DiscoveryScanResponseParams: $e";
+ rethrow;
+ }
}
String toString() {
@@ -839,8 +913,13 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeInterface(update, 8, false);
+ try {
+ encoder0.encodeInterface(update, 8, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "update of struct _ScanHandlerOnUpdateParams: $e";
+ rethrow;
+ }
}
String toString() {
@@ -967,8 +1046,13 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeBool(lost, 8, 0);
+ try {
+ encoder0.encodeBool(lost, 8, 0);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "lost of struct UpdateIsLostResponseParams: $e";
+ rethrow;
+ }
}
String toString() {
@@ -1096,8 +1180,13 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeUint8Array(id, 8, bindings.kNothingNullable, 16);
+ try {
+ encoder0.encodeUint8Array(id, 8, bindings.kNothingNullable, 16);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "id of struct UpdateGetIdResponseParams: $e";
+ rethrow;
+ }
}
String toString() {
@@ -1225,8 +1314,13 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeString(interfaceName, 8, false);
+ try {
+ encoder0.encodeString(interfaceName, 8, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "interfaceName of struct UpdateGetInterfaceNameResponseParams: $e";
+ rethrow;
+ }
}
String toString() {
@@ -1362,15 +1456,19 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- if (addresses == null) {
- encoder0.encodeNullPointer(8, false);
- } else {
- var encoder1 = encoder0.encodePointerArray(addresses.length, 8, bindings.kUnspecifiedArrayLength);
- for (int i0 = 0; i0 < addresses.length; ++i0) {
-
- encoder1.encodeString(addresses[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+ try {
+ if (addresses == null) {
+ encoder0.encodeNullPointer(8, false);
+ } else {
+ var encoder1 = encoder0.encodePointerArray(addresses.length, 8, bindings.kUnspecifiedArrayLength);
+ for (int i0 = 0; i0 < addresses.length; ++i0) {
+ encoder1.encodeString(addresses[i0], bindings.ArrayDataHeader.kHeaderSize + bindings.kPointerSize * i0, false);
+ }
}
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "addresses of struct UpdateGetAddressesResponseParams: $e";
+ rethrow;
}
}
@@ -1439,8 +1537,13 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeString(name, 8, false);
+ try {
+ encoder0.encodeString(name, 8, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "name of struct _UpdateGetAttributeParams: $e";
+ rethrow;
+ }
}
String toString() {
@@ -1508,8 +1611,13 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeString(attribute, 8, false);
+ try {
+ encoder0.encodeString(attribute, 8, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "attribute of struct UpdateGetAttributeResponseParams: $e";
+ rethrow;
+ }
}
String toString() {
@@ -1577,8 +1685,13 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeString(name, 8, false);
+ try {
+ encoder0.encodeString(name, 8, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "name of struct _UpdateGetAttachmentParams: $e";
+ rethrow;
+ }
}
String toString() {
@@ -1646,8 +1759,13 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeConsumerHandle(data, 8, false);
+ try {
+ encoder0.encodeConsumerHandle(data, 8, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "data of struct UpdateGetAttachmentResponseParams: $e";
+ rethrow;
+ }
}
String toString() {
@@ -1775,8 +1893,13 @@
void encode(bindings.Encoder encoder) {
var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
-
- encoder0.encodeStruct(ad, 8, false);
+ try {
+ encoder0.encodeStruct(ad, 8, false);
+ } on bindings.MojoCodecError catch(e) {
+ e.message = "Error encountered while encoding field "
+ "ad of struct UpdateGetAdvertisementResponseParams: $e";
+ rethrow;
+ }
}
String toString() {
@@ -1800,11 +1923,14 @@
class _DiscoveryServiceDescription implements service_describer.ServiceDescription {
- dynamic getTopLevelInterface([Function responseFactory]) => null;
+ dynamic getTopLevelInterface([Function responseFactory]) =>
+ responseFactory(null);
- dynamic getTypeDefinition(String typeKey, [Function responseFactory]) => null;
+ dynamic getTypeDefinition(String typeKey, [Function responseFactory]) =>
+ responseFactory(null);
- dynamic getAllTypeDefinitions([Function responseFactory]) => null;
+ dynamic getAllTypeDefinitions([Function responseFactory]) =>
+ responseFactory(null);
}
abstract class Discovery {
@@ -2079,8 +2205,13 @@
int get version => 0;
- service_describer.ServiceDescription get serviceDescription =>
- new _DiscoveryServiceDescription();
+ static service_describer.ServiceDescription _cachedServiceDescription;
+ static service_describer.ServiceDescription get serviceDescription {
+ if (_cachedServiceDescription == null) {
+ _cachedServiceDescription = new _DiscoveryServiceDescription();
+ }
+ return _cachedServiceDescription;
+ }
}
const int _Closer_closeName = 0;
@@ -2088,11 +2219,14 @@
class _CloserServiceDescription implements service_describer.ServiceDescription {
- dynamic getTopLevelInterface([Function responseFactory]) => null;
+ dynamic getTopLevelInterface([Function responseFactory]) =>
+ responseFactory(null);
- dynamic getTypeDefinition(String typeKey, [Function responseFactory]) => null;
+ dynamic getTypeDefinition(String typeKey, [Function responseFactory]) =>
+ responseFactory(null);
- dynamic getAllTypeDefinitions([Function responseFactory]) => null;
+ dynamic getAllTypeDefinitions([Function responseFactory]) =>
+ responseFactory(null);
}
abstract class Closer {
@@ -2262,8 +2396,6 @@
assert(_impl != null);
switch (message.header.type) {
case _Closer_closeName:
- var params = _CloserCloseParams.deserialize(
- message.payload);
var response = _impl.close(_CloserCloseResponseParamsFactory);
if (response is Future) {
return response.then((response) {
@@ -2303,8 +2435,13 @@
int get version => 0;
- service_describer.ServiceDescription get serviceDescription =>
- new _CloserServiceDescription();
+ static service_describer.ServiceDescription _cachedServiceDescription;
+ static service_describer.ServiceDescription get serviceDescription {
+ if (_cachedServiceDescription == null) {
+ _cachedServiceDescription = new _CloserServiceDescription();
+ }
+ return _cachedServiceDescription;
+ }
}
const int _ScanHandler_onUpdateName = 0;
@@ -2312,11 +2449,14 @@
class _ScanHandlerServiceDescription implements service_describer.ServiceDescription {
- dynamic getTopLevelInterface([Function responseFactory]) => null;
+ dynamic getTopLevelInterface([Function responseFactory]) =>
+ responseFactory(null);
- dynamic getTypeDefinition(String typeKey, [Function responseFactory]) => null;
+ dynamic getTypeDefinition(String typeKey, [Function responseFactory]) =>
+ responseFactory(null);
- dynamic getAllTypeDefinitions([Function responseFactory]) => null;
+ dynamic getAllTypeDefinitions([Function responseFactory]) =>
+ responseFactory(null);
}
abstract class ScanHandler {
@@ -2487,8 +2627,13 @@
int get version => 0;
- service_describer.ServiceDescription get serviceDescription =>
- new _ScanHandlerServiceDescription();
+ static service_describer.ServiceDescription _cachedServiceDescription;
+ static service_describer.ServiceDescription get serviceDescription {
+ if (_cachedServiceDescription == null) {
+ _cachedServiceDescription = new _ScanHandlerServiceDescription();
+ }
+ return _cachedServiceDescription;
+ }
}
const int _Update_isLostName = 0;
@@ -2502,11 +2647,14 @@
class _UpdateServiceDescription implements service_describer.ServiceDescription {
- dynamic getTopLevelInterface([Function responseFactory]) => null;
+ dynamic getTopLevelInterface([Function responseFactory]) =>
+ responseFactory(null);
- dynamic getTypeDefinition(String typeKey, [Function responseFactory]) => null;
+ dynamic getTypeDefinition(String typeKey, [Function responseFactory]) =>
+ responseFactory(null);
- dynamic getAllTypeDefinitions([Function responseFactory]) => null;
+ dynamic getAllTypeDefinitions([Function responseFactory]) =>
+ responseFactory(null);
}
abstract class Update {
@@ -2883,8 +3031,6 @@
assert(_impl != null);
switch (message.header.type) {
case _Update_isLostName:
- var params = _UpdateIsLostParams.deserialize(
- message.payload);
var response = _impl.isLost(_UpdateIsLostResponseParamsFactory);
if (response is Future) {
return response.then((response) {
@@ -2905,8 +3051,6 @@
}
break;
case _Update_getIdName:
- var params = _UpdateGetIdParams.deserialize(
- message.payload);
var response = _impl.getId(_UpdateGetIdResponseParamsFactory);
if (response is Future) {
return response.then((response) {
@@ -2927,8 +3071,6 @@
}
break;
case _Update_getInterfaceNameName:
- var params = _UpdateGetInterfaceNameParams.deserialize(
- message.payload);
var response = _impl.getInterfaceName(_UpdateGetInterfaceNameResponseParamsFactory);
if (response is Future) {
return response.then((response) {
@@ -2949,8 +3091,6 @@
}
break;
case _Update_getAddressesName:
- var params = _UpdateGetAddressesParams.deserialize(
- message.payload);
var response = _impl.getAddresses(_UpdateGetAddressesResponseParamsFactory);
if (response is Future) {
return response.then((response) {
@@ -3015,8 +3155,6 @@
}
break;
case _Update_getAdvertisementName:
- var params = _UpdateGetAdvertisementParams.deserialize(
- message.payload);
var response = _impl.getAdvertisement(_UpdateGetAdvertisementResponseParamsFactory);
if (response is Future) {
return response.then((response) {
@@ -3056,8 +3194,13 @@
int get version => 0;
- service_describer.ServiceDescription get serviceDescription =>
- new _UpdateServiceDescription();
+ static service_describer.ServiceDescription _cachedServiceDescription;
+ static service_describer.ServiceDescription get serviceDescription {
+ if (_cachedServiceDescription == null) {
+ _cachedServiceDescription = new _UpdateServiceDescription();
+ }
+ return _cachedServiceDescription;
+ }
}
diff --git a/mojoapptests b/mojoapptests
index fe6124e..bb0da47 100644
--- a/mojoapptests
+++ b/mojoapptests
@@ -7,4 +7,9 @@
"test-args": [],
"shell-args": [],
},
+ {
+ "test": "https://dart.test.mojo.v.io/discovery_apptests.dart",
+ "type": "dart",
+ "dart_strict_mode": True
+ }
]
diff --git a/mojoconfig b/mojoconfig
index 1aeabf8..1240d0c 100644
--- a/mojoconfig
+++ b/mojoconfig
@@ -11,5 +11,14 @@
]),
],
},
+ {
+ 'host': 'https://dart.test.mojo.v.io/',
+ 'port': 32001,
+ 'mappings': [
+ ('', [
+ '@{DISCOVERY_DIR}/test',
+ ]),
+ ],
+ },
],
}
diff --git a/pubspec.yaml b/pubspec.yaml
index a01a5cc..ff2bddf 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -2,11 +2,11 @@
description: Discovery is a discovery system for developers that makes it easy to advertise apps and scan for them. It works over MDNS and BLE.
homepage: https://github.com/vanadium/mojo.discovery
name: v23discovery
-version: 0.0.14
+version: 0.0.16
dependencies:
- mojo_sdk: 0.2.15
+ mojo_sdk: 0.2.17
dev_dependencies:
dart_style: any
- test: any
+ mojo_apptest: any
environment:
sdk: '>=1.12.0 <2.0.0'
diff --git a/test/discovery_apptests.dart b/test/discovery_apptests.dart
new file mode 100644
index 0000000..58038ec
--- /dev/null
+++ b/test/discovery_apptests.dart
@@ -0,0 +1,50 @@
+#!mojo mojo:dart_content_handler?strict=true
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+import 'dart:convert';
+
+import 'package:mojo_apptest/apptest.dart';
+import 'package:mojo/application.dart';
+
+import 'package:v23discovery/discovery.dart';
+
+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';
+
+ // Advertise
+ Client client1 = new Client(application.connectToService, mojoUrl);
+ Advertisement input = new Advertisement(
+ interfaceName, ['v.io/myAddress1', 'v.io/myAddress2']);
+ 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);
+
+ // Scan
+ Client client2 = new Client(application.connectToService, mojoUrl);
+ Scanner scanner = await client2.scan('v.InterfaceName = "$interfaceName"');
+ Update update = await scanner.onUpdate.first;
+
+ expect(update.updateType, equals(UpdateTypes.found));
+ expect(update.id, isNotEmpty);
+ expect(update.interfaceName, equals(interfaceName));
+ expect(update.addresses, equals(input.addresses));
+ expect(update.attributes, equals(input.attributes));
+ expect(UTF8.decode(await update.fetchAttachment('myAttachment')),
+ equals(attachmentContent));
+ expect(update.fetchAttachment('badAttachmentKey'), throwsArgumentError);
+
+ // Clean up
+ await advertiser.stop();
+ await scanner.stop();
+ });
+}