diff --git a/README.md b/README.md index 13deed1b..164a9d25 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Specification 'Web of Things (WoT) Scripting API' -Each commit here will sync it to the master, which will expose the content to http://w3c.github.io/wot-scripting-api/. +The main deliverable is the [WoT Scripting API Specification](./index.html). See the rendered version here: [http://w3c.github.io/wot-scripting-api/](http://w3c.github.io/wot-scripting-api/) + +See the [rationale.md](./rationale.md) for explanation on API design choices. + +See the [primer.md](./primer.md) for explaining and illustrating the usage of the API. To make contributions, please provide pull-requests to the html file, see [github help](https://help.github.com/articles/using-pull-requests/). diff --git a/index.html b/index.html index eb4d0cf2..4f074439 100644 --- a/index.html +++ b/index.html @@ -107,16 +107,16 @@ This document defines conformance criteria that apply to a single product: the UA (user agent) that implements the interfaces it contains.

- The UA may be implemented in the browser, or in a separate runtime environment. + This specification can be used for implementing the WoT Scripting API in multiple language bindings. Currently ECMAScript and TypeScript definitions are described in this document. For specifying bindings in other languages, extensions of this document may be created later.

- This specification can be used for implementing the WoT Scripting API in multiple language bindings. Currently ECMAScript and TypeScript definitions are described in this document. For specifying bindings in other languages, extensions of this document may be created later. + The UA may be implemented in the browser, or in a separate runtime environment, such as [Node.js](https://nodejs.org/en/) or small embedded runtimes such as the [JavaScript Runtime for Zephyr OS](https://www.zephyrproject.org/community/blog/introducing-javascript-runtime-zephyr-os).

Implementations that use ECMAScript executed in a browser to implement the APIs defined in this document MUST implement them in a manner consistent with the ECMAScript Bindings defined in the Web IDL specification [[!WEBIDL]].

- Implementations that use TypeScript or ECMAScript in a RunTime to implement the APIs defined in this document MUST implement them in a manner consistent with the TypeScript Bindings defined in the TypeScript specification [[!TYPESCRIPT]]. + Implementations that use TypeScript or ECMAScript in a runtime to implement the APIs defined in this document MUST implement them in a manner consistent with the TypeScript Bindings defined in the TypeScript specification [[!TYPESCRIPT]].

This document serves a general description of the WoT Scripting API. Language and runtime specific issues are discussed in separate extensions of this document. @@ -124,9 +124,6 @@

Terminology and conventions

-

- A WoT runtime or simply Runtime is defined as a software stack that manages the lifecycle of WoT application scripts (see also the [Wikipedia definition](https://en.wikipedia.org/wiki/Runtime_system)). In the case of ECMAScript and TypeScript scripts, it consists of an ECMAScript interpreter, script lifecycle management, and an operating system API that provides access to local and remote resources. A Runtime can be modeled as a virtual machine in the sense that it should be completely isolated from other execution environments on memory address space, storage address space (file system), network namespace, etc. A runtime may be for instance a Node.js environment, an embedded runtime based on a small JavaScript engine, or provided by a browser. -

The following terms are defined in [[!WOT-PRACTICES]]:

@@ -134,7 +131,7 @@
Thing
A physical or virtual entity that represents physicality, such as a device, a group of devices, a room, or a software stack that exposes WoT interfaces.
-
Thing Description
+
Thing Description
An RDF document (currently serialized in JSON-LD by default) that contains semantic and functional descriptions of a Thing: semantic context, interaction resources (such as properties, actions and events), communication (protocol, data serialization, bindings), and security related data (e.g. keys, certificates, policies etc).
WoT Interface
@@ -145,11 +142,18 @@
JSON-LD
A JSON document that is augmented with support for Linked Data by providing an @context property with a defining URI [[!JSON-LD]].
-

Consuming a Thing Description means parsing the Thing Description and building a resource model with the use of Protocol Bindings that can be used in a script for accessing and controlling the Thing. A Thing when starts up, consumes its own TD, then it exposes its WoT interface as a server. Note that a script on a Thing may consume other TDs and expose a combined interface of the consumed Things.

+

+ A WoT Runtime or WR is defined as a script execution environment that manages the lifecycle of WoT application scripts, implements a script interpreter, an event loop, a security enforcement point for access management and uses lower-level APIs to provide access to local and remote resources. A WR should be isolated from other execution environments on memory address space, storage address space (file system), network namespace, etc. +

+

+ In this version of the specification, a WR is assumed to run a single script that uses this API to define one or more Things that share a common event loop. Script deployment methods are out of scope of this version. In future versions, running multiple scripts (as modules) may be possible, and script deployment MAY be implemented using a manager Thing whose actions permit script lifecycle management operations. +

+ A Thing is represented either as a ConsumedThing (for Things obtained by discovery or retrieve operations) or ExposedThing (for Things created with this API inside the WR). All ExposedThings contained in a WR are serving external requests through the same event loop, and are said to be local Things to each other. All other Things that run in a different WR, even if on the same physical hardware, are said to be remote Things to each other. Note that there may also be local ConsumedThing objects as well. +

The terms URL and @@ -157,7 +161,7 @@

The following terms are defined in [[!HTML5]] and are used in the context of browser implementations: - + browsing context, @@ -169,9 +173,6 @@ incumbent settings object, - - script execution environment, - Document, @@ -210,7 +211,7 @@ Document objects are presented to the user. A given browsing context has a single WindowProxy object, but it can have many Document objects, with their associated - Window objects. The script execution environment + Window objects. The script execution context associated with the browsing context identifies the entity which invokes this API, which can be a web app, a web page, or an iframe. @@ -221,6 +222,8 @@ secure context is defined in [[!WEBAPPSEC]].

+ + script execution context, Promise, @@ -246,6 +249,8 @@

Introduction

As described in the [WoT Current Practices](http://w3c.github.io/wot/current-practices/wot-practices.html#vision), the Web of Things is made of Things that can describe their capabilities in a machine-interpretable format, the Thing Description (TD). By consuming a TD, a Thing creates a runtime resource model that allows accessing the Thing by an application. + + The overall WoT concepts are described in the [WoT Architecture](https://w3c.github.io/wot-architecture/) document.

@@ -253,9 +258,14 @@

The following scripting use cases are covered in this specification:

-
- -

Security and Privacy

- The trust model, attacker model, threat model and possible mitigation - proposals for the Wot Scripting API are presented in the [Security and Privacy document](http://w3c.github.io/wot-scripting-api/security-privacy.html). This section presents the chosen - security and privacy model through normative requirements to implementations. + The following use cases are being considered for next versions: +

- -

Chain of Trust

-

- Things discoverable and accessible in a WoT network SHOULD be identified and authenticated. -

-

- The integrity of WoT communication SHOULD be ensured by implementations. -

-
- -

Threat Model

-

- The main threats are summarized in the [Security and Privacy document](http://w3c.github.io/web-nfc/security-privacy.html#threats-and-possible-solutions). -

-

- In this specification the following threats are considered of the highest priority: -

-

-
- -

Security Mechanisms

-

Identification, Authentication, Authorization

-
- -

Transport-level security

-
- -

Application-level security

-
-
- -

Security policies

-

- This section summarizes the security policies which are specified as - normative requirements in the respective algorithms of this - specification. -

-

WoT Data Representation

+ WoT provides a unified representation for data exchange between Things, standardized in the [Wot Things Description](https://w3c.github.io/wot-thing-description/) specification. + Thing Descriptions are represented as dictionary objects in this API.

-

The WoT API

+

The WoT Scripting API

- The API object provides access obtain Things by discovery or creation. A Thing is represented either as a ConsumedThing or ExposedThing. + The API object represents an implementation of the WoT Runtime and provides functionality to obtain Things by discovery or creation.

The WoT object exposes only functions and has no internal state.

-

- Because of this, implementations MAY use a global object, or provide a constructor, or provide the API object, or by other means (e.g. by `require()`). Since the common denominator across various Runtime types would be to use a constructor, this would be recommended. As a consequence, the ConsumedThing and ExposedThing objects could also be constructed, complete with an asynchronous fetch functionality. -

- The WebIDL interfaces presented in this initial version of the document are based on the [[!WOT-PRACTICES]]. It is expected to be updated soon. + Browser implementations SHOULD use a namespace object such as `wot`, and [Node.js](https://nodejs.org/en/)-like runtimes MAY provide the API object through the [`require()`](https://nodejs.org/api/modules.html) or [`import`](http://www.ecma-international.org/ecma-262/6.0/#sec-imports) mechanism.

+    // [SecureContext]
+    // [NamespaceObject]
     interface WoT {
-      Promise<void> discover(ThingFilter filter, ThingDiscoveryCallback onfound);
-
-      Promise<ConsumedThing> retrieve((USVString or Dictionary) thingReference);
-
-      Promise<ExposedThing> createThing(ThingInit init);
+      Observable<ConsumedThing> discover(optional ThingFilter filter);
+      Promise<ConsumedThing> retrieve(USVString url);
+      Promise<ExposedThing> createLocalThing(ThingInit init);
     };
 
     dictionary ThingInit {
       DOMString name;
       USVString url;
-      object description;
+      Dictionary description;
     };
 
-    callback ThingDiscoveryCallback = void (ConsumedThing thing);
-  
-
- -

The Thing Client API

-
-    interface ConsumedThing {
-      readonly attribute DOMString name;
-      readonly attribute USVString url;
-      readonly attribute object description;
-
-      Promise<void> invokeAction(ThingAction action);
-      Promise<void> setProperty(ThingProperty property);
-      Promise<void> getProperty(ThingProperty property);
+    enum DiscoveryType { "any", "local", "nearby", "directory", "broadcast", "other" };
 
-      ConsumedThing addListener(DOMString eventName, ThingEventListener listener);
-      ConsumedThing removeListener(DOMString eventName, ThingEventListener listener);
-      ConsumedThing removeAllListeners(DOMString eventName);
+    dictionary ThingFilter: ThingInit {
+      (DiscoveryType or DOMString) type = "any";
     };
+  
- callback ThingEventListener = void (ConsumedThing thing); +

+ The discover() method returns an [Observable](https://github.com/tc39/proposal-observable) object that can be subscribed and unsubscribed to. +

+
+    let subscription = wot.discover().subscribe(
+      thing => { console.log("Found Thing " + thing.url); },
+      error => { console.log("Discovery finished because an error: " + error.message); },
+      () => { console.log("Discovery finished successfully");}
+    );
+
+    setTimeout(
+      () => { subscription.unsubscribe(); console.log("Discovery timeout"); },
+      5000);
   
+

+ Note that canceling a discovery (through unsubscribe) may not be successful in all cases, for instance when discovery is based on open ended broadcast requests. However, once `unsubscribe()` has been called, implementations MUST suppress further event handling ( i.e. further discoveries and errors) on the Observable. Also, a discovery error may not mean the end of the discovery process. However, in order to respect Observable semantics (error always terminates processing), implementations MUST close or suppress further event handling on the Observable. +

The Thing Server API

     interface ExposedThing {
-      ExposedThing addProperty(ThingProperty property);
-      ExposedThing addAction(ThingAction action);
-      ExposedThing addEvent(ThingEvent event);
 
-      Promise<void> register();
+      // define TD modifiers
+      ExposedThing addProperty(ThingPropertyInit property);
+      ExposedThing removeProperty(DOMString name);
+
+      ExposedThing addAction(ThingActionInit action);
+      ExposedThing removeAction(DOMString name);
+
+      ExposedThing addEvent(ThingEventInit event);
+      ExposedThing removeEvent(DOMString name);
+
+      Promise<void> register(optional USVString directory);
       Promise<void> unregister();
 
       Promise<void> start();
@@ -435,56 +407,188 @@
 
       Promise<void> emitEvent(DOMString eventName, any payload);
 
-      // define request handlers
-      ExposedThing onPropertyRetrieve(PropertyRetrieveHandler handler);
-      ExposedThing onPropertyUpdate(PropertyHandler handler);
+      // define request handlers (one per request type, so no events here)
 
-      ExposedThing onActionInvocation(ActionHandler handler);
+      ExposedThing onRetrieveProperty(PropertyRequestHandler handler);
+      ExposedThing onUpdateProperty(PropertyRequestHandler handler);
 
-      ExposedThing onObserve(DOMString event, ObserveHandler handler);
-      ExposedThing onUnobserve(DOMString event, UnobserveHandler handler);
+      ExposedThing onInvokeAction(ActionRequestHandler handler);
 
-      any onSemanticTranslate(Property property, SemanticType type);
+      ExposedThing onObserve(ObserveRequestHandler handler);
     };
 
     ExposedThing implements ConsumedThing;
 
-    callback PropertyRetrieveHandler = void (ThingProperty property, USVString url);
-    callback PropertyUpdateHandler = void (ThingProperty property, USVString url);
-    callback ActionHandler = void (ThingAction action, USVString url);
-    callback ObserveHandler = void (DOMString eventName, USVString url);
+    callback PropertyRequestHandler = any (PropertyRequest request);
+    callback ActionRequestHandler = any (ActionRequest request);
+    callback ObserveRequestHandler = any (ObserveRequest request);
+
+    dictionary PropertyRequest {
+        USVString from;
+        ThingPropertyInit property;
+        Dictionary options;
+    };
+
+    dictionary ActionRequest {
+        USVString from;
+        ThingActionInit action;
+        any inputData;
+    };
+
+    dictionary ObserveRequest {
+        USVString from;
+        ObserveType type;
+        boolean subscribe;
+        DOMString name;
+    };
+
+    enum ObserveType { "property", "action", "event", "td" };
 
     dictionary SemanticType {
       DOMString name;
       DOMString context;
     };
 
-    dictionary ThingAction {
+    dictionary ThingPropertyInit {
       DOMString name;
-      object inputType;
-      object outputType;
+      boolean configurable = true;
+      boolean enumerable = true;
+      boolean writable = true;
       SemanticType[] semanticTypes;
-      Function action;
-      sequence<any> parameters;
-      any returnValue;
+      Dictionary dataDescription;
+      any value;
     };
 
-    dictionary ThingProperty {
+    dictionary ThingEventInit {
       DOMString name;
-      object contentType;
       SemanticType[] semanticTypes;
-      any value;
-      boolean writable = false;
+      Dictionary outputDataDescription;
     };
 
-    dictionary ThingEvent {
+    dictionary ThingActionInit {
       DOMString name;
-      object payloadType;
+      Dictionary inputDataDescription;
+      Dictionary outputDataDescription;
       SemanticType[] semanticTypes;
+      Function action;
+    };
+  
+
+ +

The Thing Client API

+
+    interface ConsumedThing {
+      readonly attribute DOMString name;
+      readonly attribute USVString url;
+      readonly attribute Dictionary description;
+
+      Promise<any> invokeAction(DOMString name, sequence<any> parameters);
+      Promise<void> setProperty(DOMString name, any value);
+      Promise<any> getProperty(DOMString name);
+
+      ConsumedThing addListener(DOMString eventName, ThingEventListener listener);
+      ConsumedThing removeListener(DOMString eventName, ThingEventListener listener);
+      ConsumedThing removeAllListeners(DOMString eventName);
     };
+
+    callback ThingEventListener = void (Event event);
+
+    [Constructor(PropertyChangeEventInit init)]
+    interface PropertyChangeEvent: Event {
+        readonly attribute PropertyChangeEventInit data;
+    };
+
+    [Constructor(ActionInvocationEventInit init)]
+    interface ActionInvocationEvent: Event {
+        readonly attribute ActionInvocationEventInit data;
+    };
+
+    [Constructor(ThingDescriptionChangeEventInit init)]
+    interface ThingDescriptionChangeEvent: Event {
+        readonly attribute ThingDescriptionChangeEventInit data;
+    };
+
+    dictionary PropertyChangeEventInit {
+        DOMString name;
+        any value;
+        any oldValue;
+    };
+
+    dictionary ActionInvocationEventInit {
+        DOMString actionName;
+        any returnValue;
+    };
+
+    dictionary ThingDescriptionChangeEventInit {
+        TDChangeType type;
+        TDChangeMethod method;
+        DOMString name;
+        TDChangeData data;
+    };
+
+    enum TDChangeMethod { "add", "remove", "change" };
+    enum TDChangeType { "property", "action", "event" };
+    typedef (ThingPropertyInit or ThingActionInit or ThingEventInit) TDChangeData;
+
   
+

Security and Privacy

+

+ The trust model, attacker model, threat model and possible mitigation + proposals for the Wot Scripting API are presented in the WoT Security and Privacy document. + This section presents the chosen security and privacy model through normative requirements to implementations. +

+ +

Chain of Trust

+

+ Things discoverable and accessible in a WoT network SHOULD be identified and authenticated. +

+

+ The integrity of WoT communication SHOULD be ensured by implementations. +

+
+ +

Threat Model

+

+ The main threats are summarized in the WoT Security and Privacy document. +

+

+ In this specification the following threats are considered of the highest priority: +

    +
  • + Unauthorized gathering of user-sensitive information. +
  • +
  • + Vulnerability of the underlying protocols. +
  • +
  • + Protect the integrity of Things and Thing Descriptions. +
  • +
+

+
+ +

Security Mechanisms

+

Identification, Authentication, Authorization

+
+ +

Transport-level security

+
+ +

Application-level security

+
+
+ +

Security policies

+

+ This section summarizes the security policies which are specified as + normative requirements in the respective algorithms of this + specification. +

+
+
+

Changes

The following is a list of major changes to the document. For a complete list of changes, see the [github change log](https://github.com/w3c/wot-scripting-api/commits/master). You can also view the [recently closed bugs](https://github.com/w3c/wot-scripting-api/issues?page=1&state=closed). @@ -495,16 +599,15 @@

The following problems are being discussed and need most attention:

Acknowledgements

- The editors would like to thank Dave Raggett, Matthias Kovatsch, and Michael Koster for their - comments and guidance to this document. + The editors would like to thank Dave Raggett, Matthias Kovatsch, and Michael Koster for their comments and guidance to this document.

diff --git a/rationale.html b/rationale.html deleted file mode 100644 index 463bca04..00000000 --- a/rationale.html +++ /dev/null @@ -1,519 +0,0 @@ - - - - - rationale - - -

Rationale for Scripting API design

-

This document attempts to explain why various decision for W3C WoT(Web of Things) Scripting API were made the way they were.

-

1. Why Constructor is used instead of Factory?

-

It would be simpler to expose the WoT object with a constructor. Also, for ExposedThing and ConsumedThing we could provide constructors instead of factories. That would be more aligned with ECMAScript best practices (e.g. an ExposedThing object could be created for testing purposes, shaped locally, then exposed).

- diff --git a/rationale.md b/rationale.md index 69411e98..e7c98b9a 100644 --- a/rationale.md +++ b/rationale.md @@ -2,6 +2,41 @@ This document attempts to explain why various decision for [W3C WoT(Web of Things) Scripting API](https://w3c.github.io/wot-scripting-api/index.html) were made the way they were. -## 1\. Why Constructor is used instead of Factory? +## Using factories vs constructors +As discussed in [issue 3](https://github.com/w3c/wot-scripting-api/issues/3), and as suggested [here](https://github.com/w3c/wot-scripting-api/issues/3#issuecomment-283746764), the guidelines are these: -It would be simpler to expose the WoT object with a constructor. Also, for ExposedThing and ConsumedThing we could provide constructors instead of factories. That would be more aligned with ECMAScript best practices (e.g. an ExposedThing object could be created for testing purposes, shaped locally, then exposed). +> The root WoT object should not be constructible. It represents the UA's magic ability to discover things, similar to how the Navigator object represents the UA's magic ability to do a bunch of stuff. A namespace might be a good replacement here, if it truly has no state. + +> Avoid constructor overloads. A true constructor should be something that directly copies the given essential data into internal fields. If there is a way to infer the essential data from some other data, then that should be a factory. So maybe ThingDescription is the essential data, and if we can infer that from a name or URL, then factory should be used (perhaps static factory, e.g. ExposedThing.fromName()). + +> The idea of using a builder pattern (first create an X, then call X.expose() on it to turn into another object, or X-with-UA-magic) is rather unidiomatic in JavaScript. The better way to represent something without UA magic is just a dictionary. Having the same class represent two very distinct things is not great. + + +Resolutions: +- The browser implementations of the WoT Scripting API uses a namespace object `wot` in the browser. +- Non-browser implementations that use various runtimes may use either a namespace object `wot`, or an API object provided by the `require()` or `import()` or similar mechanisms. +- The `ConsumedThing` and `ExposedThing` objects are created by factory methods. + +## Discovery API + +Based on [WoT Current Practices](https://w3c.github.io/wot/current-practices/wot-practices.html#td-discovery), there are different discovery types: local (to the hardware), proximity based (such as BLE or NFC), registry (directory) based, and broadcast/multicast based. The discovery type is specific to the underlying protocol bindings. + +The discovery results may be filtered either at the source or at reception, by constraints made on the Thing Description. + +Based on [issue 16](https://github.com/w3c/wot-scripting-api/issues/16) there is a need to be able to tell the WoT Runtime to stop discovery (or in the case of open ended requests, suppress further discovery results). Therefore returning `Promise` was not an option any more, since cancellable `Promise`s were [dropped](https://github.com/tc39/proposal-cancelable-promises). + +Resolutions: +- Use [Observables](https://github.com/tc39/proposal-observable) for controlling the discovery process (subscribe, unsubscribe). +- Use a single filter definition that also contains a property for discovery type, defaulting to `"any"`. It is simpler and more intuitive to use than having a separate parameter for discovery type. Some of the discovery types, such as registry/directory based discovery also require another parameter for the address of the directory. This can be provided as a required property in the discovery filter, described in the discovery algorithm. + +## Server API (`ExposedThing`) +Scripts that define Exposed Things should ensure the following: +1. define properties, actions and events according to the Thing Description. +2. define request handler functions to implement the serving end for the Client API. + +## Client API (`ConsumedThing`) +Scripts that use the Client API are basically sending requests to servers in order to retrieve or update properties, invoke actions, and observe properties, actions and events. When the `ConsumedThing` is fetched, its Thing Description is also fetched, then client scripts can track changes by subscribing to events that signal TD changes. + +For browser compatibility of the Client API, events are used with the DOM convention: an Event (or Event sub-class) object is passed as an argument to the event listener, which contains a property with the event payload data, instead the Node.js convention where payload is directly passed to the event listener as arguments. + +There is no Client API for creating Things. ExposedThings can be created only locally through the Server API. However, a WoT Runtime may have a special management Thing exposed that would accept actions for installing, uninstalling, running and stopping scripts in that WoT Runtime. These scripts may create local ExposedThings, so the use case of remote Thing creation can be implemented.