From 2712b1502f91f0811790d5a12dac86e66194d3a2 Mon Sep 17 00:00:00 2001 From: danielpeintner Date: Mon, 28 Jan 2019 14:49:12 +0100 Subject: [PATCH 1/2] feat: start adding readAll on thing level --- examples/scripts/counterReadAllProperties.js | 86 ++++++++++++++++++++ packages/binding-http/src/http-server.ts | 34 +++++++- packages/core/src/exposed-thing.ts | 5 +- 3 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 examples/scripts/counterReadAllProperties.js diff --git a/examples/scripts/counterReadAllProperties.js b/examples/scripts/counterReadAllProperties.js new file mode 100644 index 000000000..3625b5b97 --- /dev/null +++ b/examples/scripts/counterReadAllProperties.js @@ -0,0 +1,86 @@ +/******************************************************************************** + * Copyright (c) 2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const NAME_PROPERTY_COUNT = "count"; +const NAME_PROPERTY_LAST_CHANGE = "lastChange"; +const NAME_ACTION_INCREMENT = "increment"; +const NAME_ACTION_DECREMENT = "decrement"; +const NAME_ACTION_RESET = "reset"; + +let thing = WoT.produce({ + name: "counter", + description: "counter example Thing", + "@context": ["http://www.w3.org/ns/td", {"iot": "http://example.org/iot"}], + }); + +console.log("Produced " + thing.name); + +thing.addProperty( + NAME_PROPERTY_COUNT, + { + type: "integer", + description: "current counter value", + "iot:Custom": "example annotation", + observable: true, + writable: true + }, + 0); +thing.addProperty( + NAME_PROPERTY_LAST_CHANGE, + { + type: "string", + description: "last change of counter value", + observable: true, + writable: false + }, + (new Date()).toUTCString()); + +thing.addAction( + NAME_ACTION_INCREMENT, + {}, + () => { + console.log("Incrementing"); + return thing.properties[NAME_PROPERTY_COUNT].read().then( (count) => { + let value = count + 1; + thing.properties[NAME_PROPERTY_COUNT].write(value); + thing.properties[NAME_PROPERTY_LAST_CHANGE].write((new Date()).toUTCString()); + }); + }); + +thing.addAction( + NAME_ACTION_DECREMENT, + {}, + () => { + console.log("Decrementing"); + return thing.properties[NAME_PROPERTY_COUNT].read().then( (count) => { + let value = count - 1; + thing.properties[NAME_PROPERTY_COUNT].write(value); + thing.properties[NAME_PROPERTY_LAST_CHANGE].write((new Date()).toUTCString()); + }); + }); + +thing.addAction( + NAME_ACTION_RESET, + {}, + () => { + console.log("Resetting"); + thing.properties[NAME_PROPERTY_COUNT].write(0); + thing.properties[NAME_PROPERTY_LAST_CHANGE].write((new Date()).toUTCString()); + }); + +// test setting metadata +thing["support"] = "git://github.com/eclipse/thingweb.node-wot.git"; + +thing.expose().then( () => { console.info(thing.name + " ready"); } ); diff --git a/packages/binding-http/src/http-server.ts b/packages/binding-http/src/http-server.ts index c6bd1a787..ac7850355 100644 --- a/packages/binding-http/src/http-server.ts +++ b/packages/binding-http/src/http-server.ts @@ -33,6 +33,8 @@ export default class HttpServer implements ProtocolServer { public readonly scheme: "http" | "https"; + private readonly ALL_DIR = "all"; + private readonly ALL_PROPERTIES = "properties"; private readonly PROPERTY_DIR = "properties"; private readonly ACTION_DIR = "actions"; private readonly EVENT_DIR = "events"; @@ -163,6 +165,15 @@ export default class HttpServer implements ProtocolServer { for (let type of ContentSerdes.get().getOfferedMediaTypes()) { let base: string = this.scheme + "://" + address + ":" + this.getPort() + "/" + encodeURIComponent(name); + if(thing.propertiesReadAll && thing.propertiesReadAll != null) { + let href = base + "/" + this.ALL_DIR + "/" + encodeURIComponent(this.ALL_PROPERTIES); + let form = new TD.Form(href, type); + if(!thing.forms) { + thing.forms = []; + } + thing.forms.push(form); + } + for (let propertyName in thing.properties) { let href = base + "/" + this.PROPERTY_DIR + "/" + encodeURIComponent(propertyName); let form = new TD.Form(href, type); @@ -328,7 +339,28 @@ export default class HttpServer implements ProtocolServer { return; } - if (segments[2] === this.PROPERTY_DIR) { + if (segments[2] === this.ALL_DIR) { + if(this.ALL_PROPERTIES == segments[3]) { + if (req.method === "GET") { + // let value = "TODO"; + // let schema: WoT.DataSchema = {"type": "string"}; + // let content = ContentSerdes.get().valueToContent(value, schema); + // res.setHeader("Content-Type", content.type); + var obj: {[k: string]: any} = {}; + for (let key in thing.properties) { + obj[key] = "Todo"; + } + + res.writeHead(200); + res.end(JSON.stringify(obj)); // content.body); + } else { + res.writeHead(405); + res.end("Method Not Allowed"); + } + // resource found and response sent + return; + } + } else if (segments[2] === this.PROPERTY_DIR) { // sub-path -> select Property let property = thing.properties[segments[3]]; if (property) { diff --git a/packages/core/src/exposed-thing.ts b/packages/core/src/exposed-thing.ts index bf475de0c..42fd3850b 100644 --- a/packages/core/src/exposed-thing.ts +++ b/packages/core/src/exposed-thing.ts @@ -26,12 +26,11 @@ import Helpers from "./helpers"; import { Content } from "./protocol-interfaces"; export default class ExposedThing extends TD.Thing implements WoT.ExposedThing { - - //private restListeners: Map = new Map(); - security: Array; securityDefinitions: { [key: string]: WoT.Security }; + id: string; + name: string; base: string; /** A map of interactable Thing Properties with read()/write()/subscribe() functions */ From 01ceeece360ecf1f3cec5419b18954b7cacdc4f4 Mon Sep 17 00:00:00 2001 From: danielpeintner Date: Tue, 29 Jan 2019 10:19:12 +0100 Subject: [PATCH 2/2] feat: combine all properties reporting into one call (for http for now) --- packages/binding-http/src/http-server.ts | 37 +++++++++++++++++------- packages/core/src/exposed-thing.ts | 1 + packages/core/src/servient.ts | 3 +- packages/td-tools/src/td-parser.ts | 12 +++++--- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/packages/binding-http/src/http-server.ts b/packages/binding-http/src/http-server.ts index ac7850355..db7d2f264 100644 --- a/packages/binding-http/src/http-server.ts +++ b/packages/binding-http/src/http-server.ts @@ -165,7 +165,7 @@ export default class HttpServer implements ProtocolServer { for (let type of ContentSerdes.get().getOfferedMediaTypes()) { let base: string = this.scheme + "://" + address + ":" + this.getPort() + "/" + encodeURIComponent(name); - if(thing.propertiesReadAll && thing.propertiesReadAll != null) { + if(true) { // make reporting of all properties optional? let href = base + "/" + this.ALL_DIR + "/" + encodeURIComponent(this.ALL_PROPERTIES); let form = new TD.Form(href, type); if(!thing.forms) { @@ -342,17 +342,34 @@ export default class HttpServer implements ProtocolServer { if (segments[2] === this.ALL_DIR) { if(this.ALL_PROPERTIES == segments[3]) { if (req.method === "GET") { - // let value = "TODO"; - // let schema: WoT.DataSchema = {"type": "string"}; - // let content = ContentSerdes.get().valueToContent(value, schema); - // res.setHeader("Content-Type", content.type); - var obj: {[k: string]: any} = {}; - for (let key in thing.properties) { - obj[key] = "Todo"; + let obj: {[k: string]: any} = {}; + let promises = []; + + for (let key in thing.properties) { + let property = thing.properties[key].read(); + promises.push(property); } - res.writeHead(200); - res.end(JSON.stringify(obj)); // content.body); + Promise.all(promises) + .then((value) => { + let index = 0; + for (let key in thing.properties) { + // TODO proper contentType handling + // let property = thing.properties[key]; + // let content = ContentSerdes.get().valueToContent(value[index], property); + // obj[key] = content.body; + obj[key] = value[index]; + index++; + } + // res.setHeader("Content-Type", content.type); + res.writeHead(200); + res.end(JSON.stringify(obj)); + }) + .catch(err => { + console.error(`HttpServer on port ${this.getPort()} got internal error on read '${requestUri.pathname}': ${err.message}`); + res.writeHead(500); + res.end(err.message); + }); } else { res.writeHead(405); res.end("Method Not Allowed"); diff --git a/packages/core/src/exposed-thing.ts b/packages/core/src/exposed-thing.ts index 42fd3850b..3953008cd 100644 --- a/packages/core/src/exposed-thing.ts +++ b/packages/core/src/exposed-thing.ts @@ -32,6 +32,7 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing { id: string; name: string; base: string; + forms: Array; /** A map of interactable Thing Properties with read()/write()/subscribe() functions */ properties: { diff --git a/packages/core/src/servient.ts b/packages/core/src/servient.ts index 3100f9421..45736e7af 100644 --- a/packages/core/src/servient.ts +++ b/packages/core/src/servient.ts @@ -177,7 +177,8 @@ export default class Servient { console.log(`Servient exposing '${thing.name}'`); - // initiatlizing forms fields + // initializing forms fields + thing.forms = []; for (let name in thing.properties) { thing.properties[name].forms = []; } diff --git a/packages/td-tools/src/td-parser.ts b/packages/td-tools/src/td-parser.ts index 23733026a..6553b2da6 100644 --- a/packages/td-tools/src/td-parser.ts +++ b/packages/td-tools/src/td-parser.ts @@ -145,9 +145,13 @@ export function serializeTD(thing: Thing): string { copy.security = ["nosec_sc"]; } - if (!copy.properties || Object.keys(copy.properties).length === 0) { + if (copy.forms && copy.forms.length === 0) { + delete copy.forms; + } + + if (copy.properties && Object.keys(copy.properties).length === 0) { delete copy.properties; - } else { + } else if(copy.properties) { // add mandatory fields (if missing): observable, writeOnly, and readOnly for (let propName in copy.properties) { let prop = copy.properties[propName]; @@ -163,9 +167,9 @@ export function serializeTD(thing: Thing): string { } } - if (!copy.actions || Object.keys(copy.actions).length === 0) { + if (copy.actions && Object.keys(copy.actions).length === 0) { delete copy.actions; - } else { + } else if (copy.actions) { // add mandatory fields (if missing): idempotent and safe for (let actName in copy.actions) { let act = copy.actions[actName];