diff --git a/README.md b/README.md index 3270bde0..2537f107 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,9 @@ import 'package:dart_wot/dart_wot.dart'; Future main(List args) async { final CoapClientFactory coapClientFactory = CoapClientFactory(); - final servient = Servient()..addClientFactory(coapClientFactory); + final servient = Servient( + protocolClients: [coapClientFactory] + ); final wot = await servient.start(); final thingDescriptionJson = ''' diff --git a/example/coap_discovery.dart b/example/coap_discovery.dart index 494cebe8..10e63882 100644 --- a/example/coap_discovery.dart +++ b/example/coap_discovery.dart @@ -30,7 +30,7 @@ Future handleThingDescription( } Future main(List args) async { - final servient = Servient()..addClientFactory(CoapClientFactory()); + final servient = Servient(clientFactories: [CoapClientFactory()]); final wot = await servient.start(); final uri = Uri.parse('coap://plugfest.thingweb.io:5683/testthing'); diff --git a/example/coap_dns_sd_discovery.dart b/example/coap_dns_sd_discovery.dart index 48013414..6dbb1357 100644 --- a/example/coap_dns_sd_discovery.dart +++ b/example/coap_dns_sd_discovery.dart @@ -12,7 +12,11 @@ void handleThingDescription(ThingDescription thingDescription) => print('Discovered TD with title "${thingDescription.title}".'); Future main(List args) async { - final servient = Servient()..addClientFactory(CoapClientFactory()); + final servient = Servient( + clientFactories: [ + CoapClientFactory(), + ], + ); final wot = await servient.start(); final uri = Uri.parse('_wot._udp.local'); diff --git a/example/coaps_readproperty.dart b/example/coaps_readproperty.dart index 0d323ce9..469a11f0 100644 --- a/example/coaps_readproperty.dart +++ b/example/coaps_readproperty.dart @@ -35,7 +35,12 @@ Future main(List args) async { ), pskCredentialsCallback: _pskCredentialsCallback, ); - final servient = Servient()..addClientFactory(coapClientFactory); + + final servient = Servient( + clientFactories: [ + coapClientFactory, + ], + ); final wot = await servient.start(); diff --git a/example/complex_example.dart b/example/complex_example.dart index 5868110a..b9b1e766 100644 --- a/example/complex_example.dart +++ b/example/complex_example.dart @@ -112,15 +112,19 @@ Future basicCredentialsCallback( } Future main() async { - const coapConfig = CoapConfig(blocksize: 64); - final CoapClientFactory coapClientFactory = CoapClientFactory( - coapConfig: coapConfig, + final coapClientFactory = CoapClientFactory( + coapConfig: const CoapConfig(blocksize: 64), ); - final HttpClientFactory httpClientFactory = + + final httpClientFactory = HttpClientFactory(basicCredentialsCallback: basicCredentialsCallback); - final servient = Servient() - ..addClientFactory(coapClientFactory) - ..addClientFactory(httpClientFactory); + + final servient = Servient( + clientFactories: [ + coapClientFactory, + httpClientFactory, + ], + ); final wot = await servient.start(); final thingDescription = ThingDescription(thingDescriptionJson); diff --git a/example/core_link_format_discovery.dart b/example/core_link_format_discovery.dart index 1cfcfbca..aad9a4b8 100644 --- a/example/core_link_format_discovery.dart +++ b/example/core_link_format_discovery.dart @@ -12,7 +12,7 @@ const propertyName = 'status'; const actionName = 'toggle'; Future main(List args) async { - final servient = Servient()..addClientFactory(CoapClientFactory()); + final servient = Servient(clientFactories: [CoapClientFactory()]); final wot = await servient.start(); diff --git a/example/example.dart b/example/example.dart index f81843b5..6afe1dba 100644 --- a/example/example.dart +++ b/example/example.dart @@ -23,14 +23,19 @@ Future basicCredentialsCallback( } Future main(List args) async { - final CoapClientFactory coapClientFactory = CoapClientFactory(); - final HttpClientFactory httpClientFactory = + final coapClientFactory = CoapClientFactory(); + final httpClientFactory = HttpClientFactory(basicCredentialsCallback: basicCredentialsCallback); - final MqttClientFactory mqttClientFactory = MqttClientFactory(); - final servient = Servient() - ..addClientFactory(coapClientFactory) - ..addClientFactory(httpClientFactory) - ..addClientFactory(mqttClientFactory); + final mqttClientFactory = MqttClientFactory(); + + final servient = Servient( + clientFactories: [ + coapClientFactory, + httpClientFactory, + mqttClientFactory, + ], + ); + final wot = await servient.start(); const thingDescriptionJson = ''' diff --git a/example/http_basic_authentication.dart b/example/http_basic_authentication.dart index 2b787b00..dff3a1b0 100644 --- a/example/http_basic_authentication.dart +++ b/example/http_basic_authentication.dart @@ -68,9 +68,14 @@ Future basicCredentialsCallback( /// Illustrates the usage of both the basic and the automatic security scheme, /// with a server supporting basic authentication. Future main(List args) async { - final HttpClientFactory httpClientFactory = - HttpClientFactory(basicCredentialsCallback: basicCredentialsCallback); - final servient = Servient()..addClientFactory(httpClientFactory); + final httpClientFactory = HttpClientFactory( + basicCredentialsCallback: basicCredentialsCallback, + ); + final servient = Servient( + clientFactories: [ + httpClientFactory, + ], + ); final wot = await servient.start(); final thingDescription = ThingDescription(thingDescriptionJson); diff --git a/lib/src/core/servient.dart b/lib/src/core/servient.dart index 7f0af8e9..40544724 100644 --- a/lib/src/core/servient.dart +++ b/lib/src/core/servient.dart @@ -37,13 +37,24 @@ class ServientException implements Exception { class Servient { /// Creates a new [Servient]. /// - /// A custom [contentSerdes] can be passed that supports other media types - /// than the default ones. + /// The [Servient] can be preconfigured with a [List] of + /// [ProtocolClientFactory]s. + /// However, it is also possible to dynamically [addClientFactory]s and + /// [removeClientFactory]s at runtime. + /// + /// If you want to support a custom media type not already included in the + /// [ContentSerdes] class, a custom [contentSerdes] object can be passed as an + /// argument. Servient({ + List? clientFactories, ServerSecurityCallback? serverSecurityCallback, ContentSerdes? contentSerdes, }) : contentSerdes = contentSerdes ?? ContentSerdes(), - _serverSecurityCallback = serverSecurityCallback; + _serverSecurityCallback = serverSecurityCallback { + for (final clientFactory in clientFactories ?? []) { + addClientFactory(clientFactory); + } + } final List _servers = []; final Map _clientFactories = {}; @@ -184,13 +195,21 @@ class Servient { List get clientSchemes => _clientFactories.keys.toList(growable: false); - /// Adds a new [clientFactory] to this [Servient.] + /// Adds a new [clientFactory] to this [Servient]. void addClientFactory(ProtocolClientFactory clientFactory) { for (final scheme in clientFactory.schemes) { _clientFactories[scheme] = clientFactory; } } + /// Removes a [ProtocolClientFactory] matching the given [scheme] from this + /// [Servient], if present. + /// + /// If a [ProtocolClientFactory] was removed, the method returns it, otherwise + /// the return value is `null`. + ProtocolClientFactory? removeClientFactory(String scheme) => + _clientFactories.remove(scheme); + /// Checks whether a [ProtocolClient] is avaiable for a given [scheme]. bool hasClientFor(String scheme) => _clientFactories.containsKey(scheme); diff --git a/test/binding_http/http_test.dart b/test/binding_http/http_test.dart index 175ae8e9..fd173bef 100644 --- a/test/binding_http/http_test.dart +++ b/test/binding_http/http_test.dart @@ -128,13 +128,14 @@ void main() { ]) async => bearerCredentialsStore[uri.host]; - final servient = Servient() - ..addClientFactory( + final servient = Servient( + clientFactories: [ HttpClientFactory( basicCredentialsCallback: basicCredentialsCallback, bearerCredentialsCallback: bearerCredentialsCallback, ), - ); + ], + ); final wot = await servient.start(); final consumedThing = await wot.consume(parsedTd); diff --git a/test/core/consumed_thing_test.dart b/test/core/consumed_thing_test.dart index 5e1bf26a..66b38962 100644 --- a/test/core/consumed_thing_test.dart +++ b/test/core/consumed_thing_test.dart @@ -284,7 +284,7 @@ void main() { final parsedTd = ThingDescription(thingDescriptionJson); - final servient = Servient()..addClientFactory(HttpClientFactory()); + final servient = Servient(clientFactories: [HttpClientFactory()]); final wot = await servient.start(); final uriVariables = {'value': 'SFRUUEJJTiBpcyBhd2Vzb21l'}; diff --git a/test/core/servient_test.dart b/test/core/servient_test.dart new file mode 100644 index 00000000..5563dc74 --- /dev/null +++ b/test/core/servient_test.dart @@ -0,0 +1,61 @@ +// Copyright 2023 Contributors to the Eclipse Foundation. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// SPDX-License-Identifier: BSD-3-Clause + +import 'package:dart_wot/dart_wot.dart'; +import 'package:test/test.dart'; + +const testUriScheme = 'test'; + +class MockedProtocolClientFactory implements ProtocolClientFactory { + @override + ProtocolClient createClient() { + throw UnimplementedError('Instantiating a client is not supported yet.'); + } + + @override + bool destroy() { + return true; + } + + @override + bool init() { + return true; + } + + @override + Set get schemes => {testUriScheme}; +} + +void main() { + group('Servient Tests', () { + test('Should accept a ProtocolClientFactory list as constructor argument', + () { + final servient = Servient( + clientFactories: [ + MockedProtocolClientFactory(), + ], + ); + + expect(servient.clientSchemes, [testUriScheme]); + expect(servient.hasClientFor(testUriScheme), true); + }); + + test( + 'Should allow for adding and removing a ProtocolClientFactory at runtime', + () { + final servient = Servient() + ..addClientFactory(MockedProtocolClientFactory()); + + expect(servient.hasClientFor(testUriScheme), true); + + servient.removeClientFactory(testUriScheme); + + expect(servient.hasClientFor(testUriScheme), false); + expect(servient.clientSchemes.length, 0); + }, + ); + }); +}