Skip to content

Update the API according to discussion in #82 and #78 #86

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

Merged
merged 14 commits into from
Feb 13, 2018

Conversation

zolkis
Copy link
Contributor

@zolkis zolkis commented Jan 29, 2018

Don't merge yet, this is work in progress.
For the rendered version of the tip of the PR, use this link.

Signed-off-by: Zoltan Kis [email protected]

…eDefinition, request handlers for ExposedThing

Signed-off-by: Zoltan Kis <[email protected]>
@mkovatsc
Copy link
Contributor

This PR introduces many completely new concepts that conflict with our long-lived discussion in #82, e.g.:

  • expose vs produce
  • typedef ThingModel and reinterpretation of ThingTemplate
  • ExposedThing without ConsumedThing interface (no control over the Thing)
  • Cut down Interaction Inits
  • dictionary ValueDefinition
  • Additional set*Handler() functions

Is this to be changed as part of the work-in-progress, or are you proposing this now?

@zolkis
Copy link
Contributor Author

zolkis commented Jan 31, 2018

We have addressed these points during the call. Now the proposal is more or less clean. The reasons for change are the following.

  1. expose vs produce
    This is mainly a name change, coming from the fact that the returned object (ExposedThing) is not yet an exposed Thing, but an object that needs to or might undergo changes such as adding properties/actions/events and request handlers. Please read the text, the current proposal allows creating an ExposedThing in 3 ways:
  • based on a model (name + semantic annotations) that needs to be modified by adding properties/actions/events/handlers
  • based on a TD, that needs to be added handlers
  • based on a ConsumedThing (actually its TD, but implementations may do optimizations), that needs to be added handlers.

Also, see #64 for reasoning on consume/produce pair of words. While I think "expose()" made sense when the returned object was ready to be started, this method is really just a factory for a half-baked exposed Thing. Perhaps we could rename ExposedThing, but we don't need to. This method could also be just a constructor for an ExposedThing instead of a factory method. That would be more clear, and also would permit better separation/grouping in the spec.

  1. ThingModel reflects the possibilities from above, including ThingTemplate (that remained the same as in API for next PlugFest (Prague 2018) #82).

  2. Actually a script has total control over an ExposedThing. It defines the thing, so cannot use it before it is defined. If ConsumedThing interface is needed, it needs to consume the ExposedThing's generated TD.

Cut down Interaction Inits

I am not sure what do you mean here. Perhaps 6. explains.

  1. ValueDefinition is an attempt to align with the TD spec. Before this a full ThingDescription was used for specifying types - which was wrong and confusing.

  2. The set*Handler() functions are needed for two reasons:

  • it is an antipattern to specify function in init dictionary members
  • they make explicit the decision a TD does not contain scripts. Before, if you wanted to create and ExposedThing based on a template/TD, and wanted to add code (that was not present in the template/TD) you first needed to remove e.g. a property/action, then re-add it with the handler mentioned in the init dict. This was just wrong. Now you can take a template/TD/ConsumedThing, replicate the TD-related structures and add the handlers on top. Also, it is clear at the top level that an ExposedThing must specify handlers, it is not any more hidden behind the members of a parameter of a function.

In fact, part of these changes are small, part of them are needed. The question is consistency, usability and alignment with the TD spec and other APIs. I believe this version is better (if not perfect) in that sense.

@mkovatsc
Copy link
Contributor

Mainly, we must not repeat the mishap of a broken (FP)WD, which was caused by ad hoc changes and introduction of unfinished, unconsolidated concepts. So why introduce these changes now and independently? The conclusion in the call was to write up what was discussed there.

  1. expose vs produce -> fine by me
    Okay, small rename to be more precise, but also potentially more confusing in regard to established terminology.
  2. ThingModel -> partly fine
    Ideally, ThingTemplate and ThingDescription would become the same; also only a subset of a TD is valid for exposing Things (e.g., not the binding information). Thus, ThingTemplate was introduced as intermediary solution. For now, we could allow for an object (ThingTemplate) or a string (ThingDescription), which is similar to an earlier version of the API.
    To put a ConsumedThing there seems to only fulfill a specific use case of optimizing proxy Thing creation and opens the path for the strange pattern of 3. This should not be introduced, definitely not for Prague because of the implications on the necessary implementation.
  3. ExposedThing control -> not fine by me
    It is pretty ugly to juggle two objects, an ExposedThing and a ConsumedThing, to realize a fundamental use case: implementing the internals of a Thing. The writeProperty() function of an ExposedThing is different from the one of ConsumedThing: an ExposedThing is under the local control of the code and directly manipulates the state, while a ConsumedThing only sends request to modify a remote Thing under control of someone else. It was convenient to let ExposedThing implement the interface of ConsumedThing, but this example here tells me, it would be clearer to fully specify both interfaces independently. This was also raised in one of the calls with a similar motiviation.
  4. Interaction Inits -> might be fine
    Weird that this comes up only now, as we are basically going back to the initial API (cf. onInvokeAction() etc.). This in itself might be a good sign, though.

@mkovatsc
Copy link
Contributor

@knimura and @danielpeintner please comment, so we can have a prepared call on Monday.

@zolkis
Copy link
Contributor Author

zolkis commented Jan 31, 2018

  1. expose vs produce -> fine by me

The remaining question is whether should we rather make it a constructor to ExposedThing instead of a factory method with disputed naming? Then we avoid the terminology discussion and have cleaner, more JS-like definition to create local ExposedThing objects. The only reason to keep the factory method would be if we wanted to create remote Things, but that is no longer a use case, as it can be done as a service.

The WebIDL would look like this:

[Constructor(optional ThingModel model)]
interface ExposedThing {
   //...
};
  1. ThingModel -> partly fine

I am also fine with removing ConsumedThing from the models. I was just thinking it could offer significant optimization opportunity when we want to re-expose a consumed thing, i.e. we don't need to fetch/consume a TD, then read the serialized form from the consumed thing and feed it to the ExposedThing factory that will parse it again. However, we could add this later, eventually.

  1. ExposedThing control -> not fine by me

I am also fine with ExposedThing extending ConsumedThing, but we should be clear about the use cases.
The ExposedThing will define the properties, actions and events, then how to update/read a property, how to call an action, when to emit an event etc. So why would it want to use ConsumedThing methods to call an action, update/read a property and attach event listeners? It is implementing and exposing them, not using them as a client, not until finished exposing. There are two stages here, 1. exposing, 2. using (after consuming). I thought to make that distinction more clear. Perhaps we need to think what is the best way to expose the ConsumedThing interface on an ExposedThing, and when.

  1. Interaction Inits -> might be fine

After playing with examples, I am pretty convinced now that this is the best way to go. I should add more example code to the spec.

@mkovatsc
Copy link
Contributor

  1. Postpone discussing the constructor idea after Prague or prepare options for a F2F breakout.
  2. Good, then let's limit to ThingTemplate dictionary or TD string. ConsumedThing.description can be passed and does not need a fetch.
  3. It's not about extending ConsumedThing, but implementing the interface, as we need similar functions to control the local state. Consuming usually happens in a remote runtume. The implementation of the functions will be fundamentally different, yet I don't think we will need different names. At least postpone this discussion.
  4. Let's bounce this of the other editors before the call.

@knimura
Copy link
Contributor

knimura commented Feb 2, 2018

  1. expose vs produce

No issues with the name change.

  1. ThingModel – what is ThingModel

Not clear the definition of ThingModel. I have no problem with ThingTemplate, if the definition of ThingTemplate is the complete subset of TD but just excluding binding information from it.

  1. “Actually a script has total control over an ExposedThing. It defines the thing, so cannot use it before it is defined. If ConsumedThing interface is needed, it needs to consume the ExposedThing's generated TD.”

I agree with ExposedThing extending ConsumedThing, because ExposedThing should have the same function as ConsumedThing. But maybe difficult to understand the notion of "inheriting client API of local thing is part of ExposedThing(ServerAPI)", so showing the use case would be needed.

  1. ValueDefinition is an attempt to align with the TD spec. Before this a full ThingDescription was used for specifying types - which was wrong and confusing.”

Totally agree.

  1. “The set*Handler() functions are needed for two reasons:
    If you wanted to create and ExposedThing based on a template/TD, and wanted to add code you first needed to remove e.g. a property/action, then re-add it with the handler mentioned in the init dict.

Agree. If ThingTemplate is subset of TD and ExposedThing is created from ThingTemplate, both removeProperty and addProperty must be called to setting handler. If we feel that going back to the initial API, introducing UpdateProperty may be one of the solution to avoid onRetrieveProperty or so.

@zolkis
Copy link
Contributor Author

zolkis commented Feb 2, 2018

Not clear the definition of ThingModel.

A Thing model (the term was taken from #64) is either a ThingTemplate (dictionary) or a ThingDescription (string).

I have changed back to ExposedThing implements ConsumedThing.

@zolkis
Copy link
Contributor Author

zolkis commented Feb 5, 2018

As discussed on the call, on request by @knimura , added a short informal section for Observables, and on request by @danielpeintner , removed optionality from some of the arguments and dictionary members.

@zolkis
Copy link
Contributor Author

zolkis commented Feb 5, 2018

As discussed in the call, added explanations on how handlers are working/implemented on ExposedThing. @mjkoster please take a look.

@zolkis zolkis force-pushed the master branch 2 times, most recently from ed6e690 to c69afdf Compare February 7, 2018 13:47
@danielpeintner
Copy link
Contributor

One comment/question: I noticed that PropertyWriteHandler gets only the new value. We used to have "oldValue" and "newValue". Any rational for this change?

@zolkis zolkis changed the title [WiP] Update the API according to discussion in #82 and #78 Update the API according to discussion in #82 and #78 Feb 7, 2018
@zolkis
Copy link
Contributor Author

zolkis commented Feb 7, 2018

WiP is removed, proof-reading can start.

@mkovatsc
Copy link
Contributor

mkovatsc commented Feb 7, 2018

One comment/question: I noticed that PropertyWriteHandler gets only the new value. We used to have "oldValue" and "newValue". Any rational for this change?

This is a quite strong concept change. As I mentioned, we had a reactive programming pattern before, which has at least two benefits:

  • Script developers do not need to care about proper and safe state management. Without setting a PropertyHandlers, we have a basic resource behavior (set state with PUT, read state with GET).
  • Better performance, as requests can be answered directly from the runtime space, no need to hand over to the scripting space (which was a bit problematic with our previous Java + Nashorn implementation and will be problematic for embedded runtimes).

Such a change must be explicitly discussed and is not fit for Prague.

@zolkis
Copy link
Contributor Author

zolkis commented Feb 7, 2018

There is an editor's Note in the spec stating:

Note that this function is invoked by implementations before the property is updated, so the client code in this function can invoke the readProperty() method to find out the old value of the property, if needed. Therefore the old value is not provided to this method.

So your use case seems to be solved.
Providing an old value would create confusion because what does it mean: which old value? It might be different than the value read by readProperty() in a given client.

@mkovatsc
Copy link
Contributor

mkovatsc commented Feb 7, 2018

We are discussing server side (ExposedThing) -- the client (ConsumedThing) has nothing to do with these handlers.

@mkovatsc
Copy link
Contributor

mkovatsc commented Feb 7, 2018

Okay, I see you already removed "client" from that note -- thanks! ;)

Have you checked that the handler code has always the right reference to the ExposedThing object to call readProperty(propertyName)? To me it is not obvious without testing, also function vs lambda...

@zolkis
Copy link
Contributor Author

zolkis commented Feb 7, 2018

Of course the handler code has the right reference to the ExposedThing object.

thing.setPropertyWriteHandler(value => {
  thing.readProperty(propertyName).then(oldValue => {
    // put handler code here
   if (oldValue < 5) {
      // update to value 
      gpio.port.write(0x02, value);
   } 
  });
}, propertyName);

…ons. Removed duplicated statements.

Signed-off-by: Zoltan Kis <[email protected]>
@w3c w3c deleted a comment from zolkis Feb 12, 2018
@w3c w3c deleted a comment from zolkis Feb 12, 2018
@w3c w3c deleted a comment from zolkis Feb 12, 2018
Copy link
Contributor

@danielpeintner danielpeintner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please find comments I got when doing a first read through. In general I think it is fine to merge and keep fixing/updating.

index.html Outdated
let discoveryFilter = {
method: "directory",
url: "http://directory.wotservice.org"
};
let subscription = wot.discover(discoveryFilter).subscribe(
thing => { console.log("Found Thing " + thing.url); },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A ConsumedThing does not have url field anymore. Example needs to be change to thing.name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

index.html Outdated
from a <a>Thing Description</a>, then adding request handlers;
</li>
<li>
from an existing <a>ConsumedThing</a> object, based on its <a>Thing Description</a>, then adding request handlers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The third option is a special case of option 2. Hence I propose to remove option 3 and change option 2 to

from a Thing Description (possibly also retrieved from a ConsumedThing), then adding request handlers;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

USVString url;
Dictionary description;
USVString query;
sequence&lt;Dictionary&gt; constraints;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AND and OR relationship for constraints seems to be very powerful but also complex at first. Moreover, the link to "the semantic metadata" in its description is very fuzzy. As I understand it, the key of the key-value pair is a reference to SemanticType. If so we might want to add an example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See Example 4. ("protocol" is made up for the example)

index.html Outdated
let subscription = wot.discover({ method: "nearby", description: {protocol: "BLE4.2"} }).subscribe(
<pre class="example" title="Same as above but with different Observable syntax">
let subscription = wot.discover({ method: "local" }).subscribe({
next: thing => { console.log("Found local Thing " + thing.url); },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See before: A ConsumedThing does not have url field anymore. Example needs to be change to thing.name

index.html Outdated
let subscription = wot.discover({
method: "nearby",
constraints: [{ protocol: "BLE-4.2" }, { protocol: "NFC"}]
}).subscribe(
thing => { console.log("Found nearby Thing " + thing.url); },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See before: A ConsumedThing does not have url field anymore. Example needs to be change to thing.name

index.html Outdated
dictionary ThingPropertyInit: SemanticAnnotations {
required DOMString name;
required ValueType type;
any value;
boolean configurable = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest TD draft (https://w3c.github.io/wot-thing-description/#property) supports observable and writable.

We need to check what will/should be the default value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

index.html Outdated
DOMString name;
dictionary ThingPropertyInit: SemanticAnnotations {
required DOMString name;
required ValueType type;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Align with TD draft (call it outputData) ?

index.html Outdated
Function action;
dictionary ThingActionInit: SemanticAnnotations {
required DOMString name;
ValueType inputDataDescription;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Align with TD draft, name it inputData and outputData?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inputData would imply this is data, but it is a description of input data.

index.html Outdated
ThingDescription dataDescription;
dictionary ThingEventInit: SemanticAnnotations {
required DOMString name;
ValueType dataDescription;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Align with TD draft, name it outputData?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

outputData is a bad name, usually events have "data" in JS. This API is for developers.

index.html Outdated
@@ -962,31 +837,46 @@ <h2>Examples</h2>
<pre class="example highlight" title="Create a new blank exposed Thing">
WoT.createExposedThing(thingDescription)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old API example. Should be WoT.produce without Promise and then etc

Also in addAction there is still old handler code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the catch. Done.

… discovery constraint matching.

Signed-off-by: Zoltan Kis <[email protected]>
@zolkis
Copy link
Contributor Author

zolkis commented Feb 13, 2018

There is still a lot of work needed on SemanticAnnotations and DataSchema, but for the moment this is good to be merged.

@zolkis
Copy link
Contributor Author

zolkis commented Feb 13, 2018

I get a ReSpec warning for undefined Observable, but it's defined. Someone please take another look and try to fix.

@danielpeintner
Copy link
Contributor

Merging is fine by me also!

Note: the ReSpec issue you are seeing relates to the observable attribute in "5.7.1 The ThingPropertyInit dictionary". The name collision seems to cause that issue. Renaming the attribute made the ReSpec issue disappear.

Let's try to look into after merging...

@zolkis zolkis merged commit 45666df into w3c:master Feb 13, 2018
@zolkis
Copy link
Contributor Author

zolkis commented Feb 13, 2018

This is merged now. Thank you all for the comments and reviews.

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

Successfully merging this pull request may close these issues.

4 participants