Skip to content

Produce() method should ignore instance specific information #156

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
egekorkan opened this issue Nov 20, 2018 · 21 comments
Closed

Produce() method should ignore instance specific information #156

egekorkan opened this issue Nov 20, 2018 · 21 comments

Comments

@egekorkan
Copy link
Contributor

At the current state, produce() method claims to accept a Thing Description that always contains instance specific information such as href's IP address and contenttype. It should be made clear that this is not possible since a server cannot replicate the IP address of another server or that this server may not be able to provide the same contenttype.

@zolkis
Copy link
Contributor

zolkis commented Nov 20, 2018

produce() method claims to accept a Thing Description always contains instance specific information such as href's IP address and contenttype.

Where is that claimed? The produce() algorithm doesn't. However, it is indeed missing some steps/information.

When creating a new Thing by consuming a TD and using the object to supply to produce(), it doesn't really harm if IP address and content type are provided as a Thing model, since the algorithm of the produce() method should state what are the minimum set of properties in a TD init object that should be there, and what happens if an IP address and content type are provided - the implementation may take it as a hint, but that information there will be overridden in the newly created ExposedThing object.

@egekorkan
Copy link
Contributor Author

Where is that claimed?

The function description has the following:

Accepts a model argument of type ThingModel

After looking at the definition of ThingModel:

The ThingModel type represents either a ThingFragment, or a ThingDescription.

And after looking at the ThingDescription definition:

Serialized representation of the Thing Description (a JSON-LD document).

So this means that Scripting API uses the same definition of the Thing Description as the Thing Description spec which in turn means that produce() method must be able to accept a normal TD and generate an Exposed Thing according to that.

the implementation may take it as a hint

I also don't see what this means :)

In my head, there has to be a clause/sentence saying that the forms of a TD are instance specific and that the produce() method can recreate an ExposedThing object with the same instance only if the instance is the same. This being a very rare case, we should make it possible to add instance specific information such as contenttype, method, (href). Since we may have multiple forms, there should be something like addForm() that can be called on an InteractionFragment which would push a Form into the array of forms.

@zolkis
Copy link
Contributor

zolkis commented Nov 20, 2018

This is a JavaScript API and a function can accept any initialization object or any of its property subset. The algorithm tells how to use the init object. Instance in this context means the JavaScript object. Multiple forms are passed as array property in the init object.

@egekorkan
Copy link
Contributor Author

Doesn't this imply that the meaning of the TD object as an argument can have two different meanings for two different implementations?

@zolkis
Copy link
Contributor

zolkis commented Nov 28, 2018

No, it's not a TD object yet, it is a JavaScript object that is used for initializating an ExposedThing object which represents a TD "object" in the same way a ConsumedThing represents a TD "object". One script might be different from another script of course, and may use different initialization objects. But they can use also the same ones, as the developer decides.

@egekorkan
Copy link
Contributor Author

egekorkan commented Nov 28, 2018

No, it's not a TD object yet

But the scripting API spec says that the argument is a serialized form of the TD defined in the TD spec.
I totally agree that ExposedThing represent an object like the ConsumedThing but I do not understand how the argument of expose(), which is allowed to be a TD specified by the TD spec can be "allowed" to be interpreted differently. Does this mean that I can provide a TD with a property called myProperty and the ExposedThing can have a property called megaProperty instead of myProperty? Where do we draw the line?

@zolkis
Copy link
Contributor

zolkis commented Nov 28, 2018

how the argument of expose()

ExposedThing.expose() does not accept arguments.

As the spec says, WoT.produce() accepts a string (serialized TD which is used for initializing the new ExposedThing object) or a dictionary object that is used for initializing the new ExposedThing object, i.e. its properties become the ExposedThing object properties and the other required properties in the TD spec are initialized with default values. Sorry I don't understand the source of your concern.

@egekorkan
Copy link
Contributor Author

Sorry, I meant produce() instead of expose() in my previous comment.

is used for initializing the new ExposedThing object, i.e. its properties become the ExposedThing object properties

This is exactly what I mean and I think we are on the same point. If we say that ExposedThing is initialized with the properties of the argument TD string, then the forms field of the argument TD string should be present in the ExposedThing. This is what I mean by instance-specific information of a TD. Without instance specific information, we would simply get a TD template (Thing fragment) and the current Scripting API spec does not tell that we can actually accept "only" a TD template.

I have put only in quotes since if the exposer of that TD (that came in the argument) can fulfill instance specific requirements, its ExposedThing will have exactly the same values for every key. I think there has to be at least a note about this constraint.

@zolkis
Copy link
Contributor

zolkis commented Nov 28, 2018

If we say that ExposedThing is initialized with the properties of the argument TD string, then the forms field of the argument TD string should be present in the ExposedThing.

Not necessarily. The ExposedThing creation algorithm does not have to take all input as it is, instead it may take it as a hint when determining the init value of the property in the ExposedThing object, so implementations may override the provided init values under conditions specified in the produce() algorithm.

Indeed we need to extend/clarify the produce() algorithm how does it initialize the new ExposedThing based on the input (string or object).

@zolkis
Copy link
Contributor

zolkis commented Jun 26, 2019

Should be fixed by #174.

@zolkis zolkis closed this as completed Jun 26, 2019
@egekorkan
Copy link
Contributor Author

I am not sure if this is fixed now since there is still no mention of the following details:

so implementations may override the provided init values under conditions specified in the produce() algorithm.

Indeed we need to extend/clarify the produce() algorithm how does it initialize the new ExposedThing based on the input (string or object).

@zolkis zolkis reopened this Mar 11, 2020
@zolkis
Copy link
Contributor

zolkis commented Mar 11, 2020

I think there may be a misunderstanding about the intended work flow when creating an ExposedThing.

From the network API point of view, we see that a new Thing is created on a device that serves requests and exposes a TD.

How does that happen? A script gets [deployed and] executed on that device.
That script defines the Thing behaviour and uses the infrastructure provided by the scripting runtime (currently node-wot). A script can use a dictionary (like a pre-filled TD template) to initialize an ExposedThing object and also needs to define the callback methods that are invoked when requests come to the ExposedThing.
The expose() method is implemented in node-wot and is responsible to "create the TD" that is shared on and align it with the implementation details (protocol bindings etc).

That includes instantiating a request handler for each Form declared in the TD template object and making sure external requests be dispatched to the right request handler and Form.

So the questions in this issue are basically encapsulated by expose() implementation, but the developer needs to know the following:

  • what protocol bindings can be used on that device
  • what Forms to define in the TD template
  • what request handlers to define and how (also using the device's local HW API, i.e. local SW environment).

@egekorkan
Copy link
Contributor Author

I do agree that it happens in the end with the expose() method but the TD is given only in the produce() so it concerns it too. Basically, the developer needs to know the 3 bullet points you have listed but also needs a warning (by the implementation or Scripting API) that not having this information might result in a TD that is different than the one given to the produce() method.

Background information: In node-wot, all the forms in a TD that is given to the produce() method are ignored and replaced by node-wot's default forms.

@danielpeintner
Copy link
Contributor

Let me add an example. Suppose the following TD for produce() which reports a count value as SVG image

{
	"title": "counter",
	"@context": ["https://www.w3.org/2019/wot/td/v1"],
	"properties": {
		"countAsImage": {
			"description": "current counter value as SVG image",
			"forms": [{
				"contentType": "image/svg+xml"
			}]
		}
	}
}

In node-wot we used to replace all existing forms entries with the one created from the node-wot runtime enhanced with bindings (e.g., for http, coap etc).

Assuming one wants to report an SVG image one can do something like this

    thing.setPropertyReadHandler("countAsImage", () => {
        return thing.readProperty("count").then((count) => {
            return new Promise((resolve, reject) => {
                resolve("<svg xmlns='http://www.w3.org/2000/svg' height='30' width='200'>" +
                    "<text x='0' y='15' fill='black'>" + count + "</text>" +
                    "</svg>");
            });
        });
    });

Doing so failed in node-wot so far because the provided "contentType": "image/svg+xml" was essentially removed (the following PR eclipse-thingweb/node-wot#187 will resolve this).

Hence I think the Scripting API spec needs to specify (not sure though how) that at least contentType (maybe more?) needs to be respected.

Having said that, I think there are cases where the Scripting API does not work anymore. Imagine the case that for HTTP one wants to report image/svg+xml while for CoAP image/png.

The method setPropertyReadHandler() is not aware of any binding (and most likely should not even be).

@egekorkan
Copy link
Contributor Author

Having said that, I think there are cases where the Scripting API does not work anymore. Imagine the case that for HTTP one wants to report image/svg+xml while for CoAP image/png.

I agree it is not possible now but there is nothing inherently limiting this. Something similar happened on the Consumer side which is now fixed with defining the form index in the InteractionOptions. A similar solution could be done on the Consumer side too:

  1. Defining in setPropertyReadHandler some kind options. Here we cannot use the form index since the TD is unknown to the developer before the script runs. I would imagine an object where multiple requirements are specified and the handler calls the fallback only if all these requirements are satisfied, otherwise calls the default callback. Example:
thing.setPropertyReadHandler("myProperty", {"protocol":"http"}, () => { //maybe href with wildcards?
    // return something if the request is http
});

// need to define what happens in this case since normally the second setPropertyReadHandler would overwrite the previous one
thing.setPropertyReadHandler("myProperty", {"contentType":"image/jpeg"} , () => {
    // return a jpeg
});

thing.setActionHandler("myAction", {"protocol":"http","contentType":"application/json"}, () => {
   // do the following if the request is HTTP
    // parse JSON input
    // do and return something
});

Disadvantage: Cannot write scripts like "Do this if the request is coap and do that if the request is http"

  1. Accessing the content of the form that has triggered the handler from the callback. Then the developer would need to manually handle different options with if/else or switch statements. Example:
thing.setPropertyReadHandler("myProperty",  (formOptions) => { //maybe href with wildcards?
    if (formOptions.protocol == "http"){
        // return something if the request is http
    } else if (formOptions.contentType == "image/jpeg"){
        // return a jpeg
    }
});

thing.setActionHandler("myAction", (value,formOptions) => {
   // do the following if the request is HTTP and JSON
    if (formOptions.protocol == "http" && formOptions.contentType == "application/json"){
        // parse JSON input
        // do and return something
    }
});

I think this method is better.

@zolkis
Copy link
Contributor

zolkis commented Mar 12, 2020

I agree the second way is better.

@zolkis
Copy link
Contributor

zolkis commented Mar 12, 2020

I think we need to break down the produce() and expose() algorithms a little bit in more detail.

The init dictionary (TD template), together with the handler functions should be in full control to implement a TD interaction.

An app that implements the ExposedThing might want to use its own dependencies for bindings and HW APIs and we should provide a mechanism for that in Scripting implementations.

The implementation (node-wot) can make ExposedThing implementations easier, by providing some bindings by default and translating to bindings where DataSchema and some supported content types are provided in the TD init dict. However, we should make sure we document the mechanisms used in implementations for dispatching interaction requests.

The examples above are very good and I wonder is there any formal heuristic we could distill from best practices and make it a standard dispatching mechanism for request handling.

@danielpeintner
Copy link
Contributor

Not sure if it is a valid use case but at the moment it is possible to use the ExposedThing as a value container without the need to specify any handlers (i.e., some clients can write the image and other clients read the image)...

@zolkis
Copy link
Contributor

zolkis commented Jun 18, 2020

Should be clarified/solve by #218, please check.

@relu91
Copy link
Member

relu91 commented Feb 18, 2021

I think this issue resonates with the current PR about ExposedThingInit (#289 ). When merging we should consider closing this issue.

@relu91
Copy link
Member

relu91 commented Mar 17, 2021

Pr #289 was merged, please feel free to reopen the issue if the current spec is not sufficiently specific about how to produce an ExposedThing from an init object.

@relu91 relu91 closed this as completed Mar 17, 2021
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