Skip to content

Commit 89fe292

Browse files
authored
Merge pull request #106 from eclipse-thingweb/output-value
feat!: improve InteractionOutput implementation
2 parents 457a113 + a4af92b commit 89fe292

File tree

6 files changed

+144
-10
lines changed

6 files changed

+144
-10
lines changed

lib/src/core/exceptions.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
//
55
// SPDX-License-Identifier: BSD-3-Clause
66

7+
export "exceptions/web_idl.dart";
8+
79
/// Base class for custom exceptions defined in `dart_wot`.
810
base class DartWotException implements Exception {
911
/// Constructor.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
import "../exceptions.dart";
8+
9+
/// Indicates that an I/O read operation failed.
10+
///
11+
/// Corresponds with the Web IDL exception type [NotReadableError].
12+
///
13+
/// [NotReadableError]: https://webidl.spec.whatwg.org/#notreadableerror
14+
final class NotReadableException extends DartWotException {
15+
/// Instantiates a new [NotReadableException] with the given [message].
16+
NotReadableException(super.message);
17+
18+
@override
19+
String get exceptionType => "NotReadableException";
20+
}

lib/src/core/implementation/interaction_output.dart

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import "dart:typed_data";
88

99
import "../definitions/data_schema.dart";
1010
import "../definitions/form.dart";
11+
import "../exceptions.dart";
1112
import "../scripting_api.dart" as scripting_api;
1213
import "content.dart";
1314
import "content_serdes.dart";
@@ -16,17 +17,24 @@ import "content_serdes.dart";
1617
class InteractionOutput implements scripting_api.InteractionOutput {
1718
/// Creates a new [InteractionOutput] based on a [Content] object.
1819
///
19-
/// A [ContentSerdes] object has to be passed for decoding the raw
20-
/// payload contained in the [Content] object.
20+
/// A [_contentSerdes] object has to be passed for decoding the raw
21+
/// payload contained in the [_content] object.
22+
///
23+
/// In contrast to the interface definition in the
24+
/// [Scripting API specification], [_form] is defined as non-nullable here,
25+
/// since other parts of the code never pass a `null` value as an argument for
26+
/// this parameter.
27+
///
28+
/// [Scripting API specification]: https://w3c.github.io/wot-scripting-api/#the-interactionoutput-interface
2129
InteractionOutput(
2230
this._content,
23-
this._contentSerdes, [
31+
this._contentSerdes,
2432
this._form,
2533
this._schema,
26-
]) : _data = _content.body;
34+
) : _data = _content.body;
2735

2836
final Content _content;
29-
final Form? _form;
37+
final Form _form;
3038
final DataSchema? _schema;
3139
final Stream<List<int>> _data;
3240

@@ -38,6 +46,10 @@ class InteractionOutput implements scripting_api.InteractionOutput {
3846

3947
@override
4048
Future<ByteBuffer> arrayBuffer() async {
49+
if (dataUsed) {
50+
throw NotReadableException("Data has already been read");
51+
}
52+
4153
_dataUsed = true;
4254
return _content.byteBuffer;
4355
}
@@ -52,8 +64,11 @@ class InteractionOutput implements scripting_api.InteractionOutput {
5264
return existingValue.value;
5365
}
5466

55-
// TODO(JKRhb): Should a NotReadableError be thrown if schema is null?
56-
// C.f. https://w3c.github.io/wot-scripting-api/#the-value-function
67+
if (schema == null) {
68+
throw NotReadableException(
69+
"Can't convert data to a value because no DataSchema is present.",
70+
);
71+
}
5772

5873
final value = await _contentSerdes.contentToValue(
5974
_content,
@@ -72,5 +87,5 @@ class InteractionOutput implements scripting_api.InteractionOutput {
7287
DataSchema? get schema => _schema;
7388

7489
@override
75-
Form? get form => _form;
90+
Form get form => _form;
7691
}

lib/src/core/scripting_api/interaction_output.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,19 @@ abstract interface class InteractionOutput {
3030

3131
/// Asyncronously creates a [ByteBuffer] representation of the value of
3232
/// of the [InteractionOutput].
33+
///
34+
/// Follows the algorithm defined for the `arrayBuffer()` function in the
35+
/// Scripting API specification.
36+
///
37+
/// [algorithm]: https://w3c.github.io/wot-scripting-api/#the-arraybuffer-function
3338
Future<ByteBuffer> arrayBuffer();
3439

3540
/// The parsed value of the [InteractionOutput].
41+
///
42+
///
43+
/// Follows the algorithm defined for the `arrayBuffer()` function in the
44+
/// Scripting API specification.
45+
///
46+
/// [algorithm]: https://w3c.github.io/wot-scripting-api/#the-value-function
3647
Future<Object?> value();
3748
}

test/core/exceptions_test.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ void main() {
2929
DiscoveryException("test").toString(),
3030
"DiscoveryException: test",
3131
);
32+
33+
expect(
34+
NotReadableException("test").toString(),
35+
"NotReadableException: test",
36+
);
3237
});
3338
});
3439
}

test/core/interaction_output_test.dart

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ void main() {
2424
]),
2525
);
2626

27-
final interactionOutput = InteractionOutput(content, contentSerdes);
27+
final interactionOutput = InteractionOutput(
28+
content,
29+
contentSerdes,
30+
Form(Uri.parse("http://example.org")),
31+
const DataSchema(),
32+
);
2833

2934
final value1 = await interactionOutput.value();
3035
expect(value1, inputValue);
@@ -46,13 +51,89 @@ void main() {
4651
]),
4752
);
4853

49-
final interactionOutput = InteractionOutput(content, contentSerdes);
54+
final interactionOutput = InteractionOutput(
55+
content,
56+
contentSerdes,
57+
Form(Uri.parse("http://example.org")),
58+
const DataSchema(),
59+
);
5060

5161
final value1 = await interactionOutput.value();
5262
expect(value1, inputValue);
5363

5464
final value2 = await interactionOutput.value();
5565
expect(value1, value2);
5666
});
67+
68+
test(
69+
"throw a NotReadableException when calling the arrayBuffer() method "
70+
"twice", () async {
71+
final contentSerdes = ContentSerdes();
72+
final content = Content(
73+
"text/plain",
74+
const Stream.empty(),
75+
);
76+
77+
final interactionOutput = InteractionOutput(
78+
content,
79+
contentSerdes,
80+
Form(Uri.parse("http://example.org")),
81+
const DataSchema(),
82+
);
83+
84+
await interactionOutput.arrayBuffer();
85+
86+
final result = interactionOutput.arrayBuffer();
87+
await expectLater(
88+
result,
89+
throwsA(
90+
isA<NotReadableException>(),
91+
),
92+
);
93+
});
94+
});
95+
96+
test(
97+
"throw a NotReadableException in the value() method when no schema is "
98+
"defined", () async {
99+
final contentSerdes = ContentSerdes();
100+
final content = Content(
101+
"text/plain",
102+
const Stream.empty(),
103+
);
104+
105+
final interactionOutput = InteractionOutput(
106+
content,
107+
contentSerdes,
108+
Form(Uri.parse("http://example.org")),
109+
null,
110+
);
111+
112+
final result = interactionOutput.value();
113+
await expectLater(
114+
result,
115+
throwsA(
116+
isA<NotReadableException>(),
117+
),
118+
);
119+
});
120+
121+
test("allow accessing the form field", () async {
122+
final contentSerdes = ContentSerdes();
123+
final content = Content(
124+
"text/plain",
125+
const Stream.empty(),
126+
);
127+
128+
final uri = Uri.parse("http://example.org");
129+
130+
final interactionOutput = InteractionOutput(
131+
content,
132+
contentSerdes,
133+
Form(uri),
134+
const DataSchema(),
135+
);
136+
137+
expect(interactionOutput.form.href, uri);
57138
});
58139
}

0 commit comments

Comments
 (0)