Skip to content

API for next PlugFest (Prague 2018) #82

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mkovatsc opened this issue Nov 13, 2017 · 28 comments
Closed

API for next PlugFest (Prague 2018) #82

mkovatsc opened this issue Nov 13, 2017 · 28 comments

Comments

@mkovatsc
Copy link
Contributor

mkovatsc commented Nov 13, 2017

interface WoT {
    Observable<ConsumedThing> discover(optional ThingFilter filter);
    Promise<ThingDescription> fetchTD(USVString url);
    ConsumedThing             consume(ThingDescription td); // with fetch split out, we could make it synchrounous
    ExposedThing              expose(ThingTemplate init); // I think it was only symmetry to make it return a promise
};

typedef USVString ThingDescription;

// A full ThingTemplate aligned with ThingDescription requires pure JSON serialization of TD!
dictionary ThingTemplate {
    DOMString                   name;
    sequence<SemanticType>?     semanticTypes = []; // additional @types to "Thing" -- use case?
    sequence<SemanticMetadata>? metadata = []; // metadata fields in TD root (same level as 'name')
}

dictionary SemanticType {
    DOMString name;
    USVString context;
    DOMString prefix;
};

dictionary SemanticMetadata {
    SemanticType type;
    any          value;
};

interface ConsumedThing {
    readonly attribute DOMString name;
    readonly attribute ThingDescription td;
    Promise<any> readProperty(DOMString name); // aligned with 'writable'
    Promise<void> writeProperty(DOMString name, any value); // aligned with 'writable'
    Promise<any> invokeAction(DOMString name, any parameters);
    Observable onEvent(DOMString name);  // subscribe to events
    Observable onPropertyChange(DOMString name);  // subscribe to property change
    Observable onTDChange();  // subscribe to Thing Description change
};

interface ExposedThing: ConsumedThing {
    Promise<void> start(); // useful to make async or counter-productive?
    Promise<void> stop();
    Promise<void> register(USVString url); // convenience for Thing Directory
    Promise<void> unregister(USVString url); // convenience for Thing Directory
    Promise<void> emitEvent(DOMString eventName, any payload);
    
    void  addProperty(ThingPropertyInit property);  // throws on error
    void  removeProperty(DOMString name);  // throws on error
    void  addAction(ThingActionInit action);  // throws on error
    void  removeAction(DOMString name);  // throws on error
    void  addEvent(ThingEventInit event);  // throws on error
    void  removeEvent(DOMString name);  // throws on error
};

typedef DOMString ValueType;  // Linked Data JSON Schema

dictionary ThingPropertyInit {
    DOMString               name;
    ValueType               type; 
    any                     initValue;
    boolean?                writable = false;
    boolean?                observable = false;
    sequence<SemanticType>? semanticTypes = [];
    sequence<SemanticMetadata>? metadata = []; // metadata fields in TD root (same level as 'name')
    callback?               onRead = Promise<any> (any oldValue) = null; // programmatic value creation (Function does not work for initValue); Promise for forwarding
    callback?               onWrite = Promise<void> (any oldValue, any newValue) = null; // for applying state changes; Promise for forwarding
};

dictionary ThingActionInit {
    DOMString               name;
    ValueType               inputType; 
    ValueType               outputType;
    callback                action = Promise<any> (any input);
    sequence<SemanticType>? semanticTypes = [];
    sequence<SemanticMetadata>? metadata = []; // metadata fields in TD root (same level as 'name')
};

dictionary ThingEventInit {
    DOMString               name;
    ValueType               type; // Linked Data JSON Schema
    sequence<SemanticType>? semanticTypes = [];
    sequence<SemanticMetadata>? metadata = []; // metadata fields in TD root (same level as 'name')
};
@mkovatsc
Copy link
Contributor Author

#78 diffused a bit. Let's try to keep the first post here sticky and update it with our latest view to be eventually put into the next Working Draft.

Please comment with change requests.

@zolkis
Copy link
Contributor

zolkis commented Nov 13, 2017

#78 diffused a bit.

Please don't create issues just for hopping discussion from one issue to the other. We should continue #78 as there are unanswered questions there, with the context being there, not here.

I have fixed some typos/bugs in the proposal.
I added the ThingBuilder interface mixin.
Added definition for ValueType.

Notes, questions:

  • We need to be able to justify API choices with use cases and examples.
  • why nullable sequences when default is empty array?
  • ThingTemplate is not clear. Please provide examples.
  • It is not clear why do we need to expose SemanticType and SemanticMetadata in a scripting API. Please provide examples / use cases.
  • ThingBuilder IMHO is not needed in first version of the API. I'd get the core API right first.

@danielpeintner
Copy link
Contributor

dictionary SemanticType {
    DOMString name;
    USVString context;
};

Propose an optional field prefix to add a way to indicate prefix mapping for the TD serialization.

dictionary SemanticType {
    DOMString name;
    USVString context;
    optional DOMString prefix;
};

Without it we end up with autogenerated prefixes such as
{"pfx0": "http://example.org/sensors#"}
instead of
{"sensor": "http://example.org/sensors#"}

Shall we add this to the proposal? If there is consensus I can do so and remove this comment to avoid confusion...

@mkovatsc
Copy link
Contributor Author

You could set up a temporary document containing the API definitions. #78 was not suitable during TPAC to understand what is the latest proposal. When the document is in place, you can close this issue.

@zolkis
Copy link
Contributor

zolkis commented Nov 13, 2017

Added prefix to SemanticType as Daniel proposed.

@danielpeintner
Copy link
Contributor

I would like to clarify the difference and need of

  • semanticTypes // additional @types to "Thing"
  • metadata // metadata fields besides the ones specified

currently described in ThingTemplate only.

Let's use a snippet of an example TD which shows the need for representing unit and reference metadata information.

"interaction": [
    {
      "@id": "val",
      "@type": ["Property","Temperature"],
      "unit": "celsius",
      "reference": "threshold",
      "name": "myTemp",
      "outputData": { "type": "number" },
      "writable": false,
      "observable": true,
      "link": [{
          "href" : "val",
          "mediaType": "application/json"
      }]
    }
]

Consequently we need the same metadata field in ThingTemplate, but also in ThingPropertyInit, ThingActionInit, and ThingEventInit.
Does that make sense for everyone?

@danielpeintner
Copy link
Contributor

typedef DOMString ValueType; // Linked Data JSON Schema

A possible "TypeSystem" definition for ValueType can be found here.

@danielpeintner
Copy link
Contributor

Minor comment. I assume name in ThingTemplate is meant to be optional as all the other fields. Correct?

@zolkis
Copy link
Contributor

zolkis commented Dec 11, 2017

To my understanding, name is user-given, and as such, optional.

@danielpeintner
Copy link
Contributor

The API currently lacks security aspects. We might need to align with Security in TD.

@zolkis
Copy link
Contributor

zolkis commented Dec 11, 2017

IMHO the API itself should be agnostic of security aspects. It is the algorithm specifications and implementation/runtime that should address and encapsulate security (e.g. secure contexts, required permissions, access policies etc).

At API (client code) level we want transparent interoperability between different security realms. Provisioning process (out of WG scope) and the runtime/API implementation (in scope) should take care of security setup and enforcement.

What comes to the Scripting spec, the only thing to make sure is that the algorithms later described in the spec do specify the mechanisms runtimes should support in order to enable security setup (by an external tool specific to the solution) and enforcement.

@danielpeintner
Copy link
Contributor

W.r.t. security I mean that we need at least a way to provide/define security requirements so that a ConsumedThing can provide the serialization of a TD (see readonly attribute ThingDescription td) with the according security information.

danielpeintner added a commit to thingweb/wot-typescript-definitions that referenced this issue Dec 11, 2017
* remove url attribute
* rename description to td
* rename getProperty to readProperty
* rename setProperty to writeProperty
* rename observe to getObservable and simplify parameters
* remove listeners
danielpeintner added a commit to thingweb/node-wot that referenced this issue Dec 11, 2017
Update ConsumedThing according to w3c/wot-scripting-api#82
* remove url attribute
* rename description to td
* rename getProperty to readProperty
* rename setProperty to writeProperty
* rename observe to getObservable and simplify parameters
* remove listeners
@mmccool
Copy link
Contributor

mmccool commented Dec 11, 2017

Regarding security, I have created an issue in the security TF repo and cross-referenced it to this one:
w3c/wot-security#59
If you have any specific security requirements please post something there. Regarding the API, I'm in particular wondering if we need some kind of opaque "provisioned security credentials" argument.

RESOLUTION: As discussed in the meeting on Dec 18, security data, like protocol bindings, need to be provided when the Thing is provisioned, eg when the Thing runtime is set up. The scripting API only deals with actions taken from "inside" a Thing, and so this setup is out of scope. However, for practical reasons, we do need to have an implementation that allows this information to be specified. Therefore, the node-wot API should be extended to support the definition of security metadata during setup, and this part of the API should be documented, but it should be made clear that this part of the node-wot API is non-normative.

@danielpeintner
Copy link
Contributor

Two more comments:

  1. I think the current ExposedThing interface misses extends ConsumedThing.

  2. I wonder whether we can simplify the current interfaces a bit. Currently we have a mixin interface ThingBuilder providing functionality to "build" a thing returning ExposedThing. ExposedThing includes again ThingBuilder.
    The consequence is very similar but wouldn't it be simpler to integrate ThingBuilder calls in ExposedThing directly. This means no ThingBuilder mixin interface and adding addProperty, removeProperty, and others to ExposedThing directly. What was the reason for the mixin interface in the first place? Do we expect ThingBuilder functionality elsewhere?

@danielpeintner
Copy link
Contributor

The signature of getObservable(DOMString name); // for Observable Property and Event in ConsumedThing limits its use to non-conflicting property/event names.

Question: Are we ok with this limitation ?

@mkovatsc
Copy link
Contributor Author

+1 to merge ThingBuilder into ExposedThing

@mkovatsc
Copy link
Contributor Author

getObservable() depends on the direction we take for the TD. If we split up into properties, actions, events, we need to ensure no collisions. Splitting into two functions would make sense.

We should look into the naming discussions in whatwg/dom#544 (shared by Zoltan).

@mkovatsc
Copy link
Contributor Author

Updated example in first comment with results from Scripting API call on 18 Dec 2017.

@zolkis
Copy link
Contributor

zolkis commented Dec 18, 2017

  1. ExposedThing should implement (not extend) ConsumedThing.
  2. ThingBuilder is already in ExposedThing. A "mixin" is a way to group functionality, there is no ThingBuilder object generated from WebIDL. It makes sense to handle functionality concerning building a Thing in a separate declaration group, until we get more experience which way we want to handle it.

@danielpeintner
Copy link
Contributor

Hi @zolkis,

Maybe some feedback provided during the call w.r.t. 2

The implementation functionality with or without "mixin" is essentially the same. Moreover, I think support for "mixin" is restricted to some languages. The feedback during the call was also that given the original ThingBuilder was about returning purely ExposedThings we can integrate it in ExposedThing.

Anyhow, let's put this topic on the agenda for next call again.

@zolkis
Copy link
Contributor

zolkis commented Dec 19, 2017

When you implement the API, in TS there is no ThingBuilder visible anywhere, you just implement all props/functions in it as it was part of ExposedThing. But in the spec, with WebIDL expression, having a mixin delineates the building functionality more clearly. So, there is no ThingBuilder, it is just one way to declare a group of functionality in WebIDL.

@zolkis
Copy link
Contributor

zolkis commented Jan 15, 2018

Making a separate comment on the following proposal (to be discussed on the call).
Reasons for change:

Changes:

  • ConsumedThing: replace "getObservable()" with "onEvent()" and "onPropertyChange()"
  • ExposedThing now inherits ConsumedThing (to be discussed: is there an ExposedThing that cannot consume?)
  • ExposedThing: add support for "lock for editing", separate ThingBuilder interface.
interface ConsumedThing {
    readonly attribute DOMString name;
    readonly attribute ThingDescription td;
    Promise<any> readProperty(DOMString name); // aligned with 'writable'
    Promise<void> writeProperty(DOMString name, any value); // aligned with 'writable'
    Promise<any> invokeAction(DOMString name, any parameters);
    // Observable getObservable(DOMString name);  
    Observable onEvent(DOMString name);  // subscribe to events
    Observable onPropertyChange(DOMString name);  // subscribe to property change
};

interface ExposedThing: ConsumedThing {
    Promise<void> start(); // useful to make async or counter-productive?
    Promise<void> stop();
    Promise<void> register(USVString url); // convenience for Thing Directory
    Promise<void> unregister(USVString url); // convenience for Thing Directory
    Promise<void> emitEvent(DOMString eventName, any payload);
    
    DynamicThing edit();  // locks the object for editing
};

interface DynamicThing: ExposedThing {
    ThingBuilder  addProperty(ThingPropertyInit property);  // throws on error
    ThingBuilder  removeProperty(DOMString name);  // throws on error
    ThingBuilder  addAction(ThingActionInit action);  // throws on error
    ThingBuilder  removeAction(DOMString name);  // throws on error
    ThingBuilder  addEvent(ThingEventInit event);  // throws on error
    ThingBuilder  removeEvent(DOMString name);  // throws on error
    void lock();  // finish editing the ExposedThing object
};

One way to use it:

try {
    thing = wot.expose({ name: "newThing"});
    thing.edit()
           .addProperty(...)
           .addAction(...)
           .addEvent(...);
           .lock();  // may also do re-registrations as well
} catch(e) {
}

@zolkis
Copy link
Contributor

zolkis commented Jan 15, 2018

As discussed in the call, I have updated the first (sticky) comments with:

  • add "onTDchange()", "onEvent()", "onPropertyChange()" to ConsumedThing,
  • simplify the update model for ExposedThing compared to the previous comment.

If we want to keep the edit lock mechanism, the editing functions need to be accessible only during that transaction. So IMHO we should keep the ThingBuilder interface (as above), which is always tied to one instance (URL) of ExposedThing.

But this version (outlined in the first comment) I used inline functions for editing and without the "state" property. If not in editing transactions, all editing methods will throw.

@mkovatsc @danielpeintner @knimura please take a look again at the previous comment, I edited for a simpler interface.

@zolkis
Copy link
Contributor

zolkis commented Jan 22, 2018

As discussed on today's call, removed the startEditing() and finish() methods from the first (sticky) comment. This is now the baseline for the API freeze for the next plugfest.

As a record,

  • We need the concept of a transaction of multiple changes (adding properties/actions/events together), but for that use case we will adapt the algorithms for start(), stop(). If needed, re-registration can be triggered manually.
  • By default, addProperty() etc will be a transaction (if multiple operations are needed in one transaction, first stop the Thing). For simple changes, the Thing does not need to be stopped from servicing requests.

Also, it was requested to keep the current way of exposing existing interactions explicitly and separately, e.g. not as a generic add("property", propertyInit) but as addProperty(propertyInit). Eventual later interactions (to the existing property/action/event) will be handled by additions to the API.

We will need to specify mechanisms for

zolkis added a commit to zolkis/wot-scripting-api that referenced this issue Jan 29, 2018
@zolkis
Copy link
Contributor

zolkis commented Jan 29, 2018

As I mentioned in the call, I would see value in splitting the API into two sections or documents, using partial interface declarations, as follows.

interface WoT {
    Observable<ConsumedThing> discover(optional ThingFilter filter);
    Promise<ThingDescription> fetchTD(USVString url);
    ConsumedThing consume(ThingDescription td);
    ExposedThing expose(dictionary init);  // ThingTemplate
};

typedef USVString ThingDescription; 

interface ConsumedThing {
    readonly attribute DOMString name;
    readonly attribute ThingDescription td;
    Promise<any> readProperty(DOMString name); // aligned with 'writable'
    Promise<void> writeProperty(DOMString name, any value); // aligned with 'writable'
    Promise<any> invokeAction(DOMString name, any parameters);
    Observable onEvent(DOMString name);  // subscribe to events
    Observable onPropertyChange(DOMString name);  // subscribe to property change
    Observable onTDChange();  // subscribe to Thing Description change
};

callback ThingEventListener = void (ThingEvent event);
callback PropertyChangeListener = void (PropertyChangeEvent change);
callback TDChangeListener = void (TDChangeEvent td);

interface ExposedThing: ConsumedThing {
    Promise<void> start(); // useful to make async or counter-productive?
    Promise<void> stop();
    Promise<void> register(USVString url); // convenience for Thing Directory
    Promise<void> unregister(USVString url); // convenience for Thing Directory
    Promise<void> emitEvent(DOMString eventName, any payload);

    void setActionHandler(DOMString actionName, Function action);
    void setPropertyReadHandler(DOMString propertyName, PropertyReadHandler readHandler);
    void setPropertyWriteHandler(DOMString propertyName, PropertyWriteHandler readHandler);
};

callback PropertyReadHandler = any (DOMString propertyName);
callback PropertyWriteHandler = void (DOMString propertyName, any value);

And the following would be either in the same document in a different section/chapter, or in a separate document, depending how long the total document gets when algorithm descriptions will be added. The following is just a vague set, but it's pretty big in itself.

partial interface ExposedThing {
    void  addProperty(ThingPropertyInit property);  // throws on error
    void  removeProperty(DOMString name);  // throws on error
    void  addAction(ThingActionInit action);  // throws on error
    void  removeAction(DOMString name);  // throws on error
    void  addEvent(ThingEventInit event);  // throws on error
    void  removeEvent(DOMString name);  // throws on error
};

// A full ThingTemplate aligned with ThingDescription requires pure JSON serialization of TD!
dictionary ThingTemplate {
    DOMString                   name;
    sequence<SemanticType>?     semanticTypes;
    sequence<SemanticMetadata>? metadata;
}

dictionary SemanticType {
    DOMString name;
    USVString context;
    DOMString prefix;
};

dictionary SemanticMetadata {
    SemanticType type;
    any          value;
};

dictionary ThingPropertyInit {
    DOMString               name;
    ValueDefinition         value; 
    boolean?                writable = false;
    boolean?                observable = false;
    sequence<SemanticType>? semanticTypes;
    sequence<SemanticMetadata>? metadata; 
};

dictionary ThingActionInit {
    DOMString               name;
    sequence<ValueDefinition>  inputTypes; 
    ValueDefinition               outputType;
    sequence<SemanticType>? semanticTypes;
    sequence<SemanticMetadata>? metadata ;
};

dictionary ThingEventInit {
    DOMString               name;
    ValueDefinition         value;  // data carried by the event
    sequence<SemanticType>? semanticTypes = [];
    sequence<SemanticMetadata>? metadata = [];
};

typedef DOMString ValueType;

interface ValueDefinition {  
    readonly attribute ValueType type;
    readonly attribute any defaultValue;
    readonly attribute any min;
    readonly attribute any max;
};

The ValueType is defined here, but we should adhere to ECMAScript or WebIDL.

ValueDefinition should reflect the TD type system and is to be discussed.

@danielpeintner
Copy link
Contributor

Hi @zolkis before discussing the splitting further (I am not sure that we need it) I think we need to agree on the overall concept again. In your latest comments you introduce callback handlers such as callback PropertyReadHandler or callback PropertyWriteHandler.

I don't think those are needed. The according handlers are to be passed in ThingPropertyInit (see onRead and onWrite). Do you have a different mean in mind?

@zolkis
Copy link
Contributor

zolkis commented Jan 30, 2018

At this time we should only split these parts into different chapters/sections in the same document. When we add the algorithms, we'll see how big the document gets and whether it will be needed to move the TD generation part out into another document (part of the same WG deliverable).

In your latest comments you introduce callback handlers such as callback PropertyReadHandler or callback PropertyWriteHandler. I don't think those are needed.

We have discussed in yesterday's scripting call that we need a way to bind interaction handler scripts to interactions described in the TD. One opposing argument (from @mkovatsc) against splitting was exactly how do we detach and specify these handlers when we create the TD by a script. At least we seem to agree that we do need the handlers.

So yes, they are needed, and they are there even now. However, currently they are passed in init dictionary members, which is a no-no (besides, they are not spec'd correctly there). They should be moved out from init dictionaries to be explicitly visible functionality at the ExposedThing object level. If we want one-time init, it can be specified by prose/algorithm, and in that case we move them to the second part (together with {add/remove}{Property/Action/Event}).

danielpeintner added a commit to thingweb/wot-typescript-definitions that referenced this issue Jan 31, 2018
…o onEvent, onPropertyChange, and onTDChange
zolkis added a commit that referenced this issue Feb 13, 2018
Update the API according to discussion in #82 and #78
@zolkis
Copy link
Contributor

zolkis commented Feb 14, 2018

Addressed by #86.

@zolkis zolkis closed this as completed Feb 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants