Skip to content

Commit

Permalink
feat: add basic CURIE expansion for additional fields
Browse files Browse the repository at this point in the history
  • Loading branch information
JKRhb committed May 21, 2022
1 parent cd32118 commit 8125436
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 28 deletions.
44 changes: 39 additions & 5 deletions lib/src/definitions/form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//
// SPDX-License-Identifier: BSD-3-Clause

import 'package:curie/curie.dart';

import 'credentials/credentials.dart';
import 'expected_response.dart';
import 'security/security_scheme.dart';
Expand Down Expand Up @@ -66,7 +68,7 @@ class Form {
}

/// Creates a new [Form] from a [json] object.
Form.fromJson(Map<String, dynamic> json) {
Form.fromJson(Map<String, dynamic> json, PrefixMapping prefixMapping) {
// TODO(JKRhb): Check if this can be refactored
if (json["href"] is String) {
_parsedJsonFields.add("href");
Expand Down Expand Up @@ -122,18 +124,49 @@ class Form {
}
}

_addAdditionalFields(json);
_addAdditionalFields(json, prefixMapping);
}

dynamic _getJsonValue(Map<String, dynamic> formJson, String key) {
_parsedJsonFields.add(key);
return formJson[key];
}

void _addAdditionalFields(Map<String, dynamic> formJson) {
String _expandCurieKey(String key, PrefixMapping prefixMapping) {
if (key.contains(":")) {
final prefix = key.split(":")[0];
if (prefixMapping.getPrefixValue(prefix) != null) {
key = prefixMapping.expandCurieString(key);
}
}
return key;
}

dynamic _expandCurieValue(dynamic value, PrefixMapping prefixMapping) {
if (value is String && value.contains(":")) {
final prefix = value.split(":")[0];
if (prefixMapping.getPrefixValue(prefix) != null) {
value = prefixMapping.expandCurieString(value);
}
} else if (value is Map<String, dynamic>) {
return value.map<String, dynamic>((key, dynamic oldValue) {
final newKey = _expandCurieKey(key, prefixMapping);
final dynamic newValue = _expandCurieValue(oldValue, prefixMapping);
return MapEntry<String, dynamic>(newKey, newValue);
});
}

return value;
}

void _addAdditionalFields(
Map<String, dynamic> formJson, PrefixMapping prefixMapping) {
for (final entry in formJson.entries) {
if (!_parsedJsonFields.contains(entry.key)) {
additionalFields[entry.key] = entry.value;
final String key = _expandCurieKey(entry.key, prefixMapping);
final dynamic value = _expandCurieValue(entry.value, prefixMapping);

additionalFields[key] = value;
}
}
}
Expand All @@ -148,7 +181,8 @@ class Form {
..securityDefinitions = securityDefinitions
..security = security
..scopes = scopes
..response = response;
..response = response
..additionalFields.addAll(additionalFields);
return copiedForm;
}

Expand Down
7 changes: 5 additions & 2 deletions lib/src/definitions/interaction_affordances/action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//
// SPDX-License-Identifier: BSD-3-Clause

import 'package:curie/curie.dart';

import '../data_schema.dart';
import '../form.dart';
import 'interaction_affordance.dart';
Expand All @@ -20,7 +22,8 @@ class Action extends InteractionAffordance {
Action(List<Form> forms) : super(forms);

/// Creates a new [Action] from a [json] object.
Action.fromJson(Map<String, dynamic> json) : super([]) {
parseAffordanceFields(json);
Action.fromJson(Map<String, dynamic> json, PrefixMapping prefixMapping)
: super([]) {
parseAffordanceFields(json, prefixMapping);
}
}
7 changes: 5 additions & 2 deletions lib/src/definitions/interaction_affordances/event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//
// SPDX-License-Identifier: BSD-3-Clause

import 'package:curie/curie.dart';

import '../data_schema.dart';
import '../form.dart';
import 'interaction_affordance.dart';
Expand All @@ -24,8 +26,9 @@ class Event extends InteractionAffordance {
Event(List<Form> forms) : super(forms);

/// Creates a new [Event] from a [json] object.
Event.fromJson(Map<String, dynamic> json) : super([]) {
parseAffordanceFields(json);
Event.fromJson(Map<String, dynamic> json, PrefixMapping prefixMapping)
: super([]) {
parseAffordanceFields(json, prefixMapping);
_parseEventFields(json);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//
// SPDX-License-Identifier: BSD-3-Clause

import 'package:curie/curie.dart';

import '../form.dart';

/// Base class for Interaction Affordances (Properties, Actions, and Events).
Expand Down Expand Up @@ -34,10 +36,10 @@ abstract class InteractionAffordance {
List<Form> augmentedForms = [];

/// Parses [forms] represented by a [json] object.
void _parseForms(Map<String, dynamic> json) {
void _parseForms(Map<String, dynamic> json, PrefixMapping prefixMapping) {
for (final formJson in json["forms"]) {
if (formJson is Map<String, dynamic>) {
forms.add(Form.fromJson(formJson));
forms.add(Form.fromJson(formJson, prefixMapping));
}
}
}
Expand All @@ -59,8 +61,9 @@ abstract class InteractionAffordance {
}

/// Parses the [InteractionAffordance] contained in a [json] object.
void parseAffordanceFields(Map<String, dynamic> json) {
_parseForms(json);
void parseAffordanceFields(
Map<String, dynamic> json, PrefixMapping prefixMapping) {
_parseForms(json, prefixMapping);

final dynamic title = json["title"];
if (title is String) {
Expand Down
7 changes: 5 additions & 2 deletions lib/src/definitions/interaction_affordances/property.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//
// SPDX-License-Identifier: BSD-3-Clause

import 'package:curie/curie.dart';

import '../data_schema.dart';
import '../form.dart';
import 'interaction_affordance.dart';
Expand Down Expand Up @@ -47,8 +49,9 @@ class Property extends InteractionAffordance implements DataSchema {
Property(List<Form> forms) : super(forms);

/// Creates a new [Property] from a [json] object.
Property.fromJson(Map<String, dynamic> json) : super([]) {
parseAffordanceFields(json);
Property.fromJson(Map<String, dynamic> json, PrefixMapping prefixMapping)
: super([]) {
parseAffordanceFields(json, prefixMapping);
parseDataSchemaJson(this, json);
rawJson = json;
}
Expand Down
44 changes: 35 additions & 9 deletions lib/src/definitions/thing_description.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import 'dart:convert';

import 'package:curie/curie.dart';

import 'context_entry.dart';
import 'interaction_affordances/action.dart';
import 'interaction_affordances/event.dart';
Expand All @@ -21,6 +23,12 @@ import 'security/psk_security_scheme.dart';
import 'security/security_scheme.dart';
import 'thing_model.dart';

const _validContextValues = [
"https://www.w3.org/2019/wot/td/v1",
"https://www.w3.org/2022/wot/td/v1.1",
"http://www.w3.org/ns/td"
];

/// Represents a WoT Thing Description
class ThingDescription {
/// The [String] representation of this [ThingDescription], if it was created
Expand All @@ -46,6 +54,9 @@ class ThingDescription {
/// The JSON-LD `@context`, represented by a [List] of [ContextEntry]s.
final List<ContextEntry> context = [];

/// Contains the values of the @context for CURIE expansion.
final prefixMapping = PrefixMapping();

/// A [Map] of [Property] Affordances.
final Map<String, Property> properties = {};

Expand Down Expand Up @@ -184,19 +195,33 @@ class ThingDescription {
if (contextJson is String || contextJson is Map<String, dynamic>) {
_parseContextListEntry(contextJson);
} else if (contextJson is List<dynamic>) {
var firstEntry = true;
for (final contextEntry in contextJson) {
_parseContextListEntry(contextEntry);
_parseContextListEntry(contextEntry, firstEntry: firstEntry);
if (contextEntry is String &&
_validContextValues.contains(contextEntry)) {
firstEntry = false;
}
}
}
}

void _parseContextListEntry(dynamic contextJsonListEntry) {
void _parseContextListEntry(dynamic contextJsonListEntry,
{bool firstEntry = false}) {
if (contextJsonListEntry is String) {
context.add(ContextEntry(contextJsonListEntry, null));
if (firstEntry && _validContextValues.contains(contextJsonListEntry)) {
prefixMapping.defaultPrefixValue = contextJsonListEntry;
}
} else if (contextJsonListEntry is Map<String, dynamic>) {
for (final mapEntry in contextJsonListEntry.entries) {
if (mapEntry.value is String) {
context.add(ContextEntry(mapEntry.value as String, mapEntry.key));
final dynamic value = mapEntry.value;
final key = mapEntry.key;
if (value is String) {
context.add(ContextEntry(value, key));
if (!key.startsWith("@") && Uri.tryParse(value) != null) {
prefixMapping.addPrefix(key, value);
}
}
}
}
Expand All @@ -217,25 +242,26 @@ class ThingDescription {
void _parseProperties(Map<String, dynamic> json) {
for (final property in json.entries) {
if (property.value is Map<String, dynamic>) {
properties[property.key] =
Property.fromJson(property.value as Map<String, dynamic>);
properties[property.key] = Property.fromJson(
property.value as Map<String, dynamic>, prefixMapping);
}
}
}

void _parseActions(Map<String, dynamic> json) {
for (final action in json.entries) {
if (action.value is Map<String, dynamic>) {
actions[action.key] =
Action.fromJson(action.value as Map<String, dynamic>);
actions[action.key] = Action.fromJson(
action.value as Map<String, dynamic>, prefixMapping);
}
}
}

void _parseEvents(Map<String, dynamic> json) {
for (final event in json.entries) {
if (event.value is Map<String, dynamic>) {
events[event.key] = Event.fromJson(event.value as Map<String, dynamic>);
events[event.key] =
Event.fromJson(event.value as Map<String, dynamic>, prefixMapping);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ dependencies:
typed_data: ^1.3.0
uri: ^1.0.0
uuid: ^3.0.5
curie: ^0.1.0
7 changes: 6 additions & 1 deletion test/core/consumed_thing_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ void main() {
test('Parse Interaction Affordances', () async {
final thingDescriptionJson = '''
{
"@context": ["http://www.w3.org/ns/td"],
"@context": [
"http://www.w3.org/ns/td",
{
"coap": "http://www.example.org/coap-binding#"
}
],
"title": "Test Thing",
"titles": {
"en": "Test Thing"
Expand Down
12 changes: 9 additions & 3 deletions test/core/definitions_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import 'dart:convert';

import 'package:curie/curie.dart';
import 'package:dart_wot/src/definitions/expected_response.dart';
import 'package:dart_wot/src/definitions/form.dart';
import 'package:test/test.dart';
Expand All @@ -17,6 +18,8 @@ void main() {
});

test('Form', () {
final prefixMapping = PrefixMapping(
defaultPrefixValue: "https://www.w3.org/2019/wot/td/v1");
final form = Form("https://example.org");

expect(form.href, "https://example.org");
Expand Down Expand Up @@ -51,7 +54,8 @@ void main() {
"test": "test"
}""");

final form3 = Form.fromJson(form3Json as Map<String, dynamic>);
final form3 =
Form.fromJson(form3Json as Map<String, dynamic>, prefixMapping);

expect(form3.href, "https://example.org");
expect(form3.contentType, "application/json");
Expand All @@ -70,7 +74,8 @@ void main() {
"scopes": "test"
}""");

final form4 = Form.fromJson(form4Json as Map<String, dynamic>);
final form4 =
Form.fromJson(form4Json as Map<String, dynamic>, prefixMapping);

expect(form4.security, ["test"]);
expect(form4.op, ["writeproperty"]);
Expand All @@ -80,7 +85,8 @@ void main() {
{
}""");

expect(() => Form.fromJson(form5Json as Map<String, dynamic>),
expect(
() => Form.fromJson(form5Json as Map<String, dynamic>, prefixMapping),
throwsArgumentError);
});
});
Expand Down

0 comments on commit 8125436

Please sign in to comment.