Skip to content

Commit

Permalink
fixup! Implement ExposedThing functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
JKRhb committed Jun 15, 2024
1 parent b3f6ab4 commit d41ecbd
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 79 deletions.
12 changes: 10 additions & 2 deletions example/exposed_thing/http_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import "package:dart_wot/core.dart";
String property = "hi :)";

void main() async {
final servient =
Servient.create(servers: [HttpServer(HttpConfig(port: 3000))]);
final servient = Servient.create(
clientFactories: [HttpClientFactory()],
servers: [HttpServer(HttpConfig(port: 3000))],
);

final wot = await servient.start();

Expand Down Expand Up @@ -50,4 +52,10 @@ void main() async {

// TODO: Should an incorrect data type be handled in some way?
});

final thingDescription = await wot
.requestThingDescription(Uri.parse("http://localhost:3000/test"));

print(thingDescription.toJson());
await servient.shutdown();
}
6 changes: 6 additions & 0 deletions lib/src/binding_coap/coap_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,10 @@ final class CoapServer implements ProtocolServer {
// TODO(JKRhb): implement stop
throw UnimplementedError();
}

@override
Future<void> destroyThing(ExposableThing thing) {
// TODO: implement destroyThing
throw UnimplementedError();
}
}
109 changes: 54 additions & 55 deletions lib/src/binding_http/http_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import "../../core.dart" hide ExposedThing;

import "http_config.dart";

const _thingsPath = "things";

/// A [ProtocolServer] for the Hypertext Transfer Protocol (HTTP).
final class HttpServer implements ProtocolServer {
/// Create a new [HttpServer] from an optional [HttpConfig].
Expand Down Expand Up @@ -60,23 +58,44 @@ final class HttpServer implements ProtocolServer {

_things[thingId] = thing;

final router = Router();

final affordances = [
...thingDescription.actions?.entries ?? [],
...thingDescription.properties?.entries ?? [],
...thingDescription.events?.entries ?? [],
];
final router = Router()
..get("/$thingId", (request) {
const defaultContentType = "application/td+json";
return Response(
200,
body: _servient.contentSerdes
.valueToContent(
DataSchemaValue.tryParse(thingDescription.toJson()),
null,
defaultContentType,
)
.body,
headers: {
"Content-Type": defaultContentType,
},
);
});

final affordances = <MapEntry<String, InteractionAffordance>>[];

for (final affordanceMap in [
thingDescription.actions,
thingDescription.properties,
thingDescription.events,
]) {
affordanceMap?.entries.forEach(affordances.add);
}

for (final affordance in affordances) {
final affordanceKey = affordance.key;
final affordanceValue = affordance.value;

// TODO: Integrate URI variables here
final path = "/$thingId/$affordanceKey";
switch (affordanceValue) {
// TODO: Refactor
// TODO: Handle values from protocol bindings
case Property(:final readOnly, :final writeOnly):
final path = "/$thingId/$affordanceKey";
if (!writeOnly) {
router.get(path, (request) async {
final content = await thing.handleReadProperty(affordance.key);
Expand Down Expand Up @@ -110,7 +129,26 @@ final class HttpServer implements ProtocolServer {

// TODO: Handle observe
}
default:
case Action():
router.post(path, (request) async {
if (request is! Request) {
throw Exception();
}

final content = Content(
request.mimeType ?? "application/json",
request.read(),
);
await thing.handleWriteProperty(affordance.key, content);

return Response(
204,
);
});

// TODO: Handle observe
case Event():
// TODO: Implement
continue;
}
}
Expand Down Expand Up @@ -146,53 +184,14 @@ final class HttpServer implements ProtocolServer {
return router.call(request);
}

if (firstSegment == _thingsPath) {
return _handleThingRequest(request);
}

return Response.notFound("Not found.");
}

Future<Response> _handleThingRequest(Request request) async {
if (request.method != "GET") {
return Response(405, body: "Method not allowed");
}

final path = request.requestedUri.pathSegments.sublist(1).join("/");

final exposedThing = _things[path];

if (exposedThing == null) {
return Response.notFound("Not found.");
}

// TODO: Fix content negotiation
final acceptHeader = request.headers["Accept"]?[0];
final contentType = ["*/*", null].contains(acceptHeader)
? "application/td+json"
: acceptHeader;

final rawThingDescription = exposedThing.thingDescription.toJson();

final dataSchemaValue = DataSchemaValue.tryParse(rawThingDescription);

// FIXME: Thing Description is not generated correctly
final content = _servient.contentSerdes.valueToContent(
dataSchemaValue,
null,
contentType ?? "application/td+json",
);
@override
Future<void> destroyThing(ExposableThing thing) async {
final id = thing.thingDescription.id;

return Response(200, body: content.body);
_things.remove(id);
_routes.remove(id);
}

// Response _handleRequest(shelf.Request request) {
// final path = request.url.path;

// if (path.startsWith(_thingsPath)) {
// return _handleThingRequest(request);
// }

// return shelf.Response.notFound('Not found.');
// }
}
10 changes: 3 additions & 7 deletions lib/src/core/implementation/exposed_thing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@ class ExposedThing implements scripting_api.ExposedThing, ExposableThing {
}

@override
Future<void> destroy() {
// TODO(JKRhb): implement destroy
throw UnimplementedError();
Future<void> destroy() async {
_servient.destroyThing(this);
}

@override
Expand All @@ -57,10 +56,7 @@ class ExposedThing implements scripting_api.ExposedThing, ExposableThing {
}

@override
Future<void> expose() async {
// TODO: Refactor
return _servient.expose(this);
}
Future<void> expose() => _servient.expose(this);

@override
void setActionHandler(String name, scripting_api.ActionHandler handler) {
Expand Down
38 changes: 23 additions & 15 deletions lib/src/core/implementation/servient.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,13 @@ class InternalServient implements Servient {
}
_clientFactories.clear();

final serverStatuses = _servers.map((server) => server.stop()).toList();
final thingDestructionFutures =
[..._things.values].map((exposedThing) => exposedThing.destroy());
await Future.wait(thingDestructionFutures);

final serverStatuses = _servers.map((server) => server.stop());
await Future.wait(serverStatuses);
serverStatuses.clear();
_servers.clear();
}

void _cleanUpForms(Iterable<InteractionAffordance>? interactionAffordances) {
Expand Down Expand Up @@ -158,7 +162,7 @@ class InternalServient implements Servient {
///
/// Returns `false` if the [thing] has already been registered, otherwise
/// `true`.
bool addThing(ExposedThing thing) {
bool _addThing(ExposedThing thing) {
final id = thing.thingDescription.id!;
if (_things.containsKey(id)) {
return false;
Expand All @@ -168,18 +172,22 @@ class InternalServient implements Servient {
return true;
}

/// Returns an [ExposedThing] with the given [id] if it has been registered.
ExposedThing? thing(String id) => _things[id];
/// Destroys a previously exposed [thing].
///
/// Returns `true` if the [thing] was successfully destroyed.
bool destroyThing(ExposedThing thing) {
final id = thing.thingDescription.id;
if (!_things.containsKey(id)) {
return false;
}

/// Returns a [Map] with the [ThingDescription]s of all registered
/// [ExposedThing]s.
Map<String, ThingDescription> get thingDescriptions {
return _things.map((key, value) => MapEntry(key, value.thingDescription));
for (final server in _servers) {
server.destroyThing(thing);
}
_things.remove(id);
return true;
}

/// Returns a list of available [ProtocolServer]s.
List<ProtocolServer> get servers => _servers;

@override
void addServer(ProtocolServer server) {
_things.values.forEach(server.expose);
Expand All @@ -191,11 +199,11 @@ class InternalServient implements Servient {
bool removeServer(String scheme) {
// TODO: Refactor
final containsScheme =
servers.where((server) => server.scheme == scheme).isNotEmpty;
_servers.where((server) => server.scheme == scheme).isNotEmpty;

if (containsScheme) {
// TODO: "De-expose" the ExposedThings
servers.removeWhere((server) => server.scheme == scheme);
_servers.removeWhere((server) => server.scheme == scheme);

return true;
}
Expand Down Expand Up @@ -264,7 +272,7 @@ class InternalServient implements Servient {
final thingDescription = _expandExposedThingInit(init);

final newThing = ExposedThing(this, thingDescription);
if (addThing(newThing)) {
if (_addThing(newThing)) {
await expose(newThing);
return newThing;
}
Expand Down
3 changes: 3 additions & 0 deletions lib/src/core/protocol_interfaces/protocol_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ abstract interface class ProtocolServer {

/// Exposes a [thing].
Future<void> expose(ExposableThing thing);

/// Removes a [thing] from this server.
Future<void> destroyThing(ExposableThing thing);
}

0 comments on commit d41ecbd

Please sign in to comment.