Skip to content

Commit 8b7e826

Browse files
authored
Merge pull request #94 from eclipse-thingweb/explore-directory-variables
feat!: add initial support for query parameters to exploreDirectory method
2 parents 7adfed6 + 17334a9 commit 8b7e826

File tree

4 files changed

+155
-8
lines changed

4 files changed

+155
-8
lines changed

example/directory_discovery.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2024 Contributors to the Eclipse Foundation. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
//
5+
// SPDX-License-Identifier: BSD-3-Clause
6+
7+
// ignore_for_file: avoid_print
8+
9+
import "package:dart_wot/binding_http.dart";
10+
import "package:dart_wot/core.dart";
11+
12+
Future<void> main(List<String> args) async {
13+
final servient = Servient(
14+
clientFactories: [
15+
HttpClientFactory(),
16+
],
17+
);
18+
19+
final wot = await servient.start();
20+
// FIXME(JRKhb): The "things" property currently points to "localhost",
21+
// preventing this example from working
22+
final url = Uri.parse("https://zion.vaimee.com/.well-known/wot");
23+
24+
final thingDiscovery = await wot.exploreDirectory(url);
25+
26+
await for (final thingDescription in thingDiscovery) {
27+
print(thingDescription);
28+
}
29+
}

lib/src/core/implementation/wot.dart

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,17 @@ class WoT implements scripting_api.WoT {
111111

112112
@override
113113
Future<scripting_api.ThingDiscoveryProcess> exploreDirectory(
114-
Uri url, [
114+
Uri url, {
115115
scripting_api.ThingFilter? filter,
116-
]) async {
116+
int? offset,
117+
int? limit,
118+
scripting_api.DirectoryPayloadFormat? format,
119+
}) async {
120+
// TODO(JKRhb): Add support for the collection format.
121+
if (format == scripting_api.DirectoryPayloadFormat.collection) {
122+
throw ArgumentError('Format "$format" is currently not supported.');
123+
}
124+
117125
final thingDescription = await requestThingDescription(url);
118126

119127
if (!thingDescription.isValidDirectoryThingDescription) {
@@ -124,8 +132,14 @@ class WoT implements scripting_api.WoT {
124132

125133
final consumedDirectoryThing = await consume(thingDescription);
126134

127-
final interactionOutput =
128-
await consumedDirectoryThing.readProperty("things");
135+
final interactionOutput = await consumedDirectoryThing.readProperty(
136+
"things",
137+
uriVariables: {
138+
if (offset != null) "offset": offset,
139+
if (limit != null) "limit": limit,
140+
if (format != null) "format": format.toString(),
141+
},
142+
);
129143
final rawThingDescriptions = await interactionOutput.value();
130144

131145
if (rawThingDescriptions is! List<Object?>) {

lib/src/core/scripting_api/wot.dart

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,35 @@ import "discovery/thing_filter.dart";
1313
import "exposed_thing.dart";
1414
import "types.dart";
1515

16+
/// Enumeration for specifying the value of the `format` query parameter when
17+
/// using the `exploreDirectory` discovery method.
18+
///
19+
/// See [section 7.3.2.1.5] of the [WoT Discovery] specification for more
20+
/// information.
21+
///
22+
/// [WoT Discovery]: https://www.w3.org/TR/2023/REC-wot-discovery-20231205
23+
/// [section 7.3.2.1.5]: https://www.w3.org/TR/2023/REC-wot-discovery-20231205/#exploration-directory-api-things-listing
24+
enum DirectoryPayloadFormat {
25+
/// Indicates that an array of Thing Descriptions should be returned.
26+
///
27+
/// This is the default value.
28+
array,
29+
30+
/// Indicates that an collection of Thing Descriptions should be returned.
31+
collection,
32+
;
33+
34+
@override
35+
String toString() {
36+
switch (this) {
37+
case array:
38+
return "array";
39+
case collection:
40+
return "collection";
41+
}
42+
}
43+
}
44+
1645
/// Interface for a [WoT] runtime.
1746
///
1847
/// See WoT Scripting API specification,
@@ -39,9 +68,12 @@ abstract interface class WoT {
3968
/// [ThingDescription] objects for Thing Descriptions that match an optional
4069
/// [filter] argument of type [ThingFilter].
4170
Future<ThingDiscoveryProcess> exploreDirectory(
42-
Uri url, [
71+
Uri url, {
4372
ThingFilter? filter,
44-
]);
73+
int? offset,
74+
int? limit,
75+
DirectoryPayloadFormat? format,
76+
});
4577

4678
/// Discovers [ThingDescription]s from a given [url] using the specified
4779
/// [method].

test/core/discovery_test.dart

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ final directoryTestUri2 = Uri.parse("$testUriScheme://[::4]/.well-known/wot");
2121
final directoryTestThingsUri2 = Uri.parse("$testUriScheme://[::4]/things");
2222
final directoryTestUri3 = Uri.parse("$testUriScheme://[::5]/.well-known/wot");
2323
final directoryTestThingsUri3 = Uri.parse("$testUriScheme://[::5]/things");
24+
final directoryTestUri4 = Uri.parse("$testUriScheme://[::6]/.well-known/wot");
25+
final directoryTestThingsUri4 = Uri.parse(
26+
"$testUriScheme://[::3]/things?offset=2&limit=3&format=array",
27+
);
2428

2529
const validTestTitle1 = "Test TD 1";
2630
const validTestThingDescription = '''
@@ -49,6 +53,26 @@ final directoryThingDescription1 = '''
4953
},
5054
"properties": {
5155
"things": {
56+
"uriVariables": {
57+
"offset": {
58+
"title": "Number of TDs to skip before the page",
59+
"type": "number",
60+
"default": 0
61+
},
62+
"limit": {
63+
"title": "Number of TDs in a page",
64+
"type": "number"
65+
},
66+
"format": {
67+
"title": "Payload format",
68+
"type": "string",
69+
"enum": [
70+
"array",
71+
"collection"
72+
],
73+
"default": "array"
74+
}
75+
},
5276
"forms": [
5377
{
5478
"href": "$directoryTestThingsUri1"
@@ -128,8 +152,9 @@ class _MockedProtocolClient implements ProtocolClient {
128152
}
129153

130154
@override
131-
Future<Content> readResource(Form form) async {
132-
final href = form.href;
155+
Future<Content> readResource(AugmentedForm form) async {
156+
final href = form.resolvedHref;
157+
133158
if (href == directoryTestThingsUri1) {
134159
return "[$validTestThingDescription]".toContent("application/td+json");
135160
}
@@ -142,6 +167,10 @@ class _MockedProtocolClient implements ProtocolClient {
142167
return invalidTestThingDescription2.toContent("application/td+json");
143168
}
144169

170+
if (href == directoryTestThingsUri4) {
171+
return "[$validTestThingDescription]".toContent("application/ld+json");
172+
}
173+
145174
throw StateError("Encountered an unknown URI $href.");
146175
}
147176

@@ -167,6 +196,10 @@ class _MockedProtocolClient implements ProtocolClient {
167196
return directoryThingDescription3.toDiscoveryContent(url);
168197
}
169198

199+
if (url == directoryTestUri4) {
200+
return directoryThingDescription1.toDiscoveryContent(url);
201+
}
202+
170203
throw StateError("Encountered invalid URL.");
171204
}
172205

@@ -377,5 +410,44 @@ void main() {
377410
await thingDiscoveryProcess.stop();
378411
expect(thingDiscoveryProcess.done, true);
379412
});
413+
414+
test("should support the experimental query parameters API", () async {
415+
final servient = Servient(
416+
clientFactories: [
417+
_MockedProtocolClientFactory(),
418+
],
419+
);
420+
421+
final wot = await servient.start();
422+
final thingDiscoveryProcess = await wot.exploreDirectory(
423+
directoryTestUri4,
424+
offset: 2,
425+
limit: 3,
426+
format: DirectoryPayloadFormat.array,
427+
);
428+
429+
var counter = 0;
430+
await for (final thingDescription in thingDiscoveryProcess) {
431+
counter++;
432+
expect(thingDescription.title, validTestTitle1);
433+
}
434+
expect(counter, 1);
435+
expect(thingDiscoveryProcess.done, true);
436+
});
437+
438+
test(
439+
'should currently not support the "collection" format when using the '
440+
"experimental query parameters API", () async {
441+
final servient = Servient();
442+
final wot = await servient.start();
443+
444+
expect(
445+
() async => await wot.exploreDirectory(
446+
directoryTestUri4,
447+
format: DirectoryPayloadFormat.collection,
448+
),
449+
throwsArgumentError,
450+
);
451+
});
380452
});
381453
}

0 commit comments

Comments
 (0)