Skip to content

Instantly share code, notes, and snippets.

@zolkis
Last active February 14, 2019 08:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zolkis/555441aebe1874392a00be50a8228f91 to your computer and use it in GitHub Desktop.
Save zolkis/555441aebe1874392a00be50a8228f91 to your computer and use it in GitHub Desktop.
WoT Scripting ideas

Check the comments below on how we arrived to this variant.

typedef DOMString PropertyFragment;  // serialized TD Property
// includes InteractionPattern and DataSchema
//   InteractionPattern contains: title, titles, description, descriptions, forms, security, uriVariables
//   DataSchema contains: type, unit,  enum, oneOf, const, readOnly, writeOnly
//       repeated (from InteractionPattern): [ title, titles, description, descriptions ]

typedef DOMString ActionFragment;  // serialized TD Action
//  includes InteractionPattern + input, output, safe, idempotent

typedef DOMString EventFragment;  // serialized TD Event
// includes InteractionPattern + data, subscription, cancellation

[Constructor(PropertyFragment init)]
interface ThingProperty: EventTarget {
  Promise<any> read();
  Promise<void> write(any value);
  readonly attribute object metadata;  // get TD members
  attribute EventHandler onchange;  // simple event
};

[Constructor(ActionFragment init)]
interface ThingAction {
  Promise<any> invoke(optional any parameters);
  readonly attribute object metadata;  // get TD members
};

[Constructor(EventFragment init)]
interface ThingEvent: EventTarget {
  readonly attribute object metadata;  // get TD members
  attribute EventHandler onnotification;
};

typedef DOMString ThingFragment;  // serialized Thing Description

[Constructor(optional ThingFragment init)]
interface ConsumedThing: EventTarget {
  Promise<void> updateMetadata(USVString url);  // former fetch()
  readonly attribute object metadata;  // get TD members
  // expose the WoT interactions explicitly
  ThingProperty getProperty(DOMString name);
  ThingAction getAction(DOMString name);
  ThingEvent getEvent(DOMString name);
  attribute EventHandler onchange;  // simple event for TD change
};

typedef (ThingFragment or ConsumedThing) ThingModel;  
typedef (PropertyFragment or ThingProperty) PropertyModel;
typedef (ActionFragment or ThingAction) ActionModel;
typedef (EventFragment or ThingEvent) EventModel;

[Constructor(ThingModel init)]
interface ExposedThing: ConsumedThing {
  ExposedThing addProperty(DOMString name, PropertyModel property, optional any initValue);
  ExposedThing setPropertyReadHandler(DOMString name, PropertyReadHandler readHandler);
  ExposedThing setPropertyWriteHandler(DOMString name, PropertyWriteHandler writeHandler);
  ExposedThing removeProperty(DOMString name);

  ExposedThing addAction(DOMString name, ActionModel init, ActionHandler action);
  ExposedThing removeAction(DOMString name);
  ExposedThing setActionHandler(DOMString name, ActionHandler action);

  ExposedThing addEvent(DOMString name, EventModel event);
  ExposedThing removeEvent(DOMString name);  
  void emitEvent(DOMString name, any data);

  Promise<void> expose();
  Promise<void> destroy();

  Promise<void> register(USVString directory);
  Promise<void> unregister(USVString directory);
};

callback PropertyReadHandler = Promise<any>();
callback PropertyWriteHandler = Promise<void>(any value);
callback ActionHandler = Promise<any>(any parameters);

dictionary ThingFilter {
  (DiscoveryMethod or DOMString) method = "any";
  USVString? url;
  USVString? query;
  ThingFragment? fragment;
};

[Constructor(optional ThingFilter filter)]
interface ThingDiscovery: EventTarget {
  void start();
  void stop();
  attribute EventHandler onfound;
  attribute EventHandler onerror;
  attribute EventHandler oncomplete;
};

interface WOT {
  Promise<void> start(optional WotOptions options);
  void stop();
  Promise<ThingDescription> fetch(USVString url);
};
@zolkis
Copy link
Author

zolkis commented Jan 11, 2019

Web IDL of the current Working Draft: https://www.w3.org/TR/wot-scripting-api/

Let's redesign the interfaces one by one, first the interactions (Property, Action, Event).
Main question: how to handle TD and TD fragments:

  1. as DOMString (serialized JSON) and specify algorithm on how to create objects out of it
  2. as opaque objects (deserialized JSON)
  3. as objects/interfaces defined in the Scripting spec (copy definitions from the TD spec)
    • with WebIDL we cannot specify the @ types
    • with cannot mix WebIDL definitions with prose
      => we need to define these objects in prose (but then it's already done in the TD spec).

For now let's explore option 1: TD and TD fragments come as DOMString, they are used for initializing ThingProperty, ThingAction, ThingEvent and their members can be accessed by explicit methods as metadata.

typedef DOMString PropertyFragment;  // serialized TD Property
// includes InteractionPattern and DataSchema
//   InteractionPattern contains: title, titles, description, descriptions, forms, security, uriVariables
//   DataSchema contains: type, unit,  enum, oneOf, const, readOnly, writeOnly 
//       repeated (from InteractionPattern): [ title, titles, description, descriptions ]

typedef DOMString ActionFragment;  // serialized TD Action 
//  includes InteractionPattern + input, output, safe, idempotent

typedef DOMString EventFragment;  // serialized TD Event
// includes InteractionPattern + data, subscription, cancellation

[Constructor(PropertyFragment init)]
interface ThingProperty: EventTarget {
  Promise<any> read();
  Promise<void> write(any value);
  any getMetadata(DOMString key);  // get TD members
  attribute EventHandler onchange;  // simple event
};

[Constructor(ActionFragment init)]
interface ThingAction {
  Promise<any> invoke(optional any parameters);
  any getMetadata(DOMString key);  // get TD members
};

[Constructor(EventFragment init)]
interface ThingEvent: EventTarget {
  any getMetadata(DOMString key);  // get TD members
  attribute EventHandler ondata; 
};

Note that we don't any more need Observer pattern, nor ExposedEvent.

@zolkis
Copy link
Author

zolkis commented Jan 11, 2019

Let's revisit ConsumedThing here.

typedef DOMString ThingFragment;  // serialized Thing Description

[Constructor(ThingFragment init)]
interface ConsumedThing: EventTarget {
  object getMetadata();  // deserialized object, for introspection
  // expose the WoT interactions explicitly
  ThingProperty getProperty(DOMString name);
  ThingAction getAction(DOMString name);
  ThingEvent getEvent(DOMString name);
  attribute EventHandler onchange;  // simple event for TD change
};

@zolkis
Copy link
Author

zolkis commented Jan 11, 2019

ExposedThing is next. One can create an ExposedThing from an existing ConsumedThing (as a model), or from a TD fragment.

typedef (ThingFragment or ConsumedThing) ThingModel;  
typedef (PropertyFragment or ThingProperty) PropertyModel;
typedef (ActionFragment or ThingAction) ActionModel;
typedef (EventFragment or ThingEvent) EventModel;

[Constructor(ThingModel init)]
interface ExposedThing: ConsumedThing {
  ExposedThing addProperty(DOMString name, PropertyModel property, optional any initValue);
  ExposedThing setPropertyReadHandler(DOMString name, PropertyReadHandler readHandler);
  ExposedThing setPropertyWriteHandler(DOMString name, PropertyWriteHandler writeHandler);
  ExposedThing removeProperty(DOMString name);

  ExposedThing addAction(DOMString name, ActionModel init, ActionHandler action);
  ExposedThing removeAction(DOMString name);
  ExposedThing setActionHandler(DOMString name, ActionHandler action);

  ExposedThing addEvent(DOMString name, EventModel event);
  ExposedThing removeEvent(DOMString name);  
  void emitEvent(DOMString name, any data);

  Promise<void> expose();
  Promise<void> destroy();
};

callback PropertyReadHandler = Promise<any>();
callback PropertyWriteHandler = Promise<void>(any value);
callback ActionHandler = Promise<any>(any parameters);

@zolkis
Copy link
Author

zolkis commented Jan 11, 2019

Let's handle discovery next. Most intuitive would be to define it as a separate interface.

dictionary ThingFilter {
  (DiscoveryMethod or DOMString) method = "any";
  USVString? url;
  USVString? query;
  ThingFragment? fragment;
};

[Constructor(optional ThingFilter filter)]
interface ThingDiscovery: EventTarget {
  void start();
  void stop();
  attribute EventHandler onfound;
  attribute EventHandler onerror;
  attribute EventHandler oncomplete;
};

@zolkis
Copy link
Author

zolkis commented Jan 11, 2019

The main API object is the last.
Discovery objects can be constructed (former discover()).
ConsumedThing can be constructed (former consume()).
ExposedThing can be constructed (former produce()).
Former fetch() can be integrated into ConsumedThing.
Former register() and unregister() can be integrated into ExposedThing.

partial interface ConsumedThing {  // Note: constructor argument becomes optional
  Promise<void> updateMetadata(USVString url);  // former fetch()
};

partial interface ExposedThing {
  Promise<void> register(USVString directory);
  Promise<void> unregister(USVString directory);
};

How to consume a Thing now? Two ways:

  1. Construct an empty ConsumedThing and call updateMetadata(url) on it.
  2. Construct a ConsumedThing based on a ThingFragment obtained by some other API.

How to produce an ExposedThing now?

  1. Take an existing ConsumedThing as model, or
  2. Take a ThingFragment obtained by some other API and construct ExposedThing from that.

@zolkis
Copy link
Author

zolkis commented Jan 11, 2019

Updating the gist from the comments above.

@danielpeintner
Copy link

This proposals seems to be very close to the version we used to have before this current scripting/TD iteration.

  • Explicit calls for properties, actions, etc
  • special methods for metadata etc
  • ...

e.g., getMetadata() used to be a simple get(), correct?

The nice thing about the current solution is that one can parse a construct (e.g, a Thing TD) and gets also this getters and setters out of the box. The only missing part are handlers that need to get attached...

@zolkis, let's talk about it in the scripting call today.

@zolkis
Copy link
Author

zolkis commented Feb 14, 2019

Model definitions for ExposedThing addXxx() methods.

typedef object InteractionMetadata; // @id, @type, defined in prose

dictionary InteractionPattern {
  DOMString title;
  DOMString description;
  sequence<Form> forms;
  sequence<DataSchema> uriVariables;
};
Interaction includes InteractionMetadata;

dictionary PropertyModel: InteractionPattern {
  boolean observable;
};

dictionary ActionModel: InteractionPattern {
    DataSchema input;
    DataSchema output;
    boolean safe;
    boolean idempotent;
};

dictionary EventModel: InteractionPattern {
    DataSchema data;
    DataSchema subscriptionData;
    DataSchema cancellationData;
};

enum InteractionType { 
    "readproperty", "writeproperty", "observproperty",
    "readallproperties", "writeallproperties",
    "readmultipleproperties", "writemultipleproperties",
    "invokeaction", "subscribeevent", "unsubscribeevent",
};

dictionary Form {
    USVString href;
    DOMString contentType;
    ExpectedResponse response;
    sequence<InteractionType> op;
    DOMString subprotocol;
    sequence<DOMString> security;
    sequence<DOMString> scopes;
};

dictionary ExpectedResponse {
    DOMString contentType;
};

dictionary MultiLanguage {
    // what comes here???
};

dictionary Link {
    USVString href;
    DOMString type;
    DOMString rel;
    USVString anchor;
};

typedef object DataSchemaMetadata; // @context, @type, const, enum described in prose

enum DataSchemaType { "null", "boolean", "number", "integer", "string", "object", "array" };

dictionary DataSchema {
    DOMString title;
    DOMString description;
    DataSchemaType type;
    DOMString unit;
    sequence<DataSchema> oneOf;
    boolean readOnly;
    boolean writeOnly;
};
DataSchema includes DataSchemaMetadata;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment