-
Notifications
You must be signed in to change notification settings - Fork 8
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
Action semantics #81
Comments
Discussion in arch call on 17.6. Needs a hypermedia format for an action model. See Stevens – Unix network programming Actions can return a JSON object with multiple fields: containing "id", “status” and “cancel” endpoints and a notification endpoint to which to subscribed to for status change notifications. Cancel and notification could be optional? TD needs a way to communicate action capabilities, e.g. cancellable, notification support etc. Output data schema of actions describe the capabilities, i.e. if they don't define a cancel endpoint an action is not cancellable. Failure responses – protocol independent Action status: Success, failed, ongoing, (not responding – on a gateway / proxy) Link an action to a status object and an event endpoint? TD has no links. |
As I understand it this would mean that all cancelable actions would require two separate interaction affordances in a Thing Description, e.g.
To explain, the way that this works in the Web Thing REST API is that a Invoke an action
Query the status of an action
Cancel an action
A GET on the top level Action resource returns a list (queue) of all the pending List action requests
This approach doesn't require two separate interaction affordances per action. I would suggest something along these lines for async actions in the Core Profile. There is a proposal in w3c/wot-thing-description#302 (comment) regarding how to represent some of these types of operations ( The payload of the responses could be simplified from the Web Thing API by removing the object wrapper with the name of the action, since this is not strictly needed. (The reason it's there in the Web Thing API is that there's also a top level For synchronous actions I assume that there would be no dynamically created |
So my thought here is that the affordances for cancel, etc. would not have to be in the TD. This is hard anyway for dynamic resources. The original idea of the hypermedia approach (first proposed something like three years ago, and note it is in our charter to better nail it down) was that an "Action Description" would be returned by an action invocation and it would have a set of links in it for (dynamic) interactions that could be done to follow up on an action invocation. At a minimum support for checking status, requesting cancellation (if possible, so would be optional) and subscribing to a notification of a status change (also optional, just in case the endpoint can't deal with events, but the alternative is polling the status which is not efficient). Anyway, the original proposal was to use a special case of a TD as an "Action Description" which would indeed allow a lot of flexibility, but would also be complicated. So my proposal is to keep things simple and just return a JSON object from an action invocation which would have a set of pre-defined entries. To make this concrete, when you invoke an action the "output" object (which, BTW, would be described in the TD's "output" data schema for the action) would look something like
We would prescriptively define in the profile spec how each of these in turn would work (replacing a TD-like Action Description, basically, with normative specifications). For instance, for "status" we would indicate what values could be returned (one of a small set of strings, for instance) and how the protocol would work ("GET" on HTTP, for instance). Same for Notification. Note that you would be able to see from the TD whether or not an action is cancellable, etc. just by looking at the output data schema. We could write a Thing Model for Actions to define all this if we wanted to get fancy but would not require the Thing to return it. HOWEVER, in the meeting we all agreed that we should definitely start with the low-hanging fruit here and start by a least defining synchronous actions. Then only once that is done should we look at how to deal with async actions (and that means we need some way to distinguish the two). We also discussed a number of alternatives to the above, but cluttering the TD with a bunch of extra properties and events for each action does not not really seem like a good idea. We also thought that maybe additional "ops" for actions like "notify" and "cancel" might go into the TD spec later, and wanted something consistent with that (possible) evolution of the TD. Taking that approach in the profile spec now though is not feasible. |
@benfrancis BTW, I admit to typing up the above before reading all the details of your posts (I only had 5m between meetings). Skimming what you posted it seems we might be close to being on the same page. I will read your posts more carefully and post a followup soon. |
Please also check w3c/wot-thing-description#899 From my point of view:
|
Also regarding the very first comment: w3c/wot-thing-description#890 |
We discussed a proposal during the vF2F, slides are here: |
Below is a sketch of a proposal for how the action operations could work in the Protocol Binding section of the WoT Core Profile. Note: I could personally live without the This proposal includes support for both synchronous and asynchronous action status responses. My suggestion is that web things can choose which type of response to send. Consumers MUST accept both types of response to the initial
|
I like @benfrancis proposal. There two points, which I like to discuss:
|
@sebastiankb wrote:
I agree this could be inefficient for large inputs, as with the One argument for including the input data in the body of the dynamically created resource is that it can then neatly be updated with a PUT request in the
E.g. PATCH /things/lamp/actions/fade/123e4567-e89b-12d3-a456-426655 HTTP/1.1
Host: mythingserver.com
Content-Type: application/json
Accept: application/json
{
"input": {
"level": 50,
"duration": 5
}
{ If we decide we don't need the Is there some other way we can mitigate the issue of large inputs? @mmccool suggested just including a hash for example. Could we truncate large values? How do other hypermedia systems and APIs deal with that issue?
I wondered this too. I concluded that given there's no way to guarantee that all actions can be completed within an HTTP timeout period, it's still useful for the consumer to know if the invoked action is still pending or running when the HTTP response comes back, even if a dynamic resource is not created to track its status. |
How about introducing a sub-resource where the input parameters of the invoked action can be queried. E.g., GET /things/lamp/actions/fade/123e4567-e89b-12d3-a456-426655/input HTTP/1.1
Host: mythingserver.com
Accept: application/json The response can look like: HTTP/1.1 200 OK
Content-Type: application/json
{
"level": 50,
"duration": 5
} The advantages are, that the client can decide to check the input parameters and the usual queryaction response will be more compact.
I had quick look into XML-RPC. If everything is ok, simply the return value is provided without a status code in the response message. If something went wrong, the response message is different with a detailed error message. We could also do this by the usage of the additionalResponse feature in the TD. What do you think? |
Another alternative would be to make input optional. Consumers can even know it ahead of time by checking the Do you see any downsides? |
I suggest we implement the decision from the architecture call and create a PR with the sections of the current proposal that we agreed upon in the call, i.e. to include invoke, query and cancel into the draft. @benfrancis - We can extend the branch/PR of #88 and evolve it, or do you prefer to create a separate PR? This discussion about input parameters and whether it is optional in the response is very useful and should be continued in the next architecture/profile call. We can then incrementally refine and clarify these questions. A JSON schema would be very helpful to have a proposal that we can agree on and can include into the spec. |
Yes, sure! So what I had in mind was something like this: {
// A TD action description
"newAction" :{
"title": "newAction",
"description": "",
"input": {
"type": "object",
"properties": {
"type": "object",
"property": {
"foo": {
"type": "string"
}
}
}
},
"output": {
// according to what is described above an ActionStatus can be described with this schema
"type": "object",
"properties": {
"input" : {
"type": "object",
"property": {
"foo": {"type":"string"}
}
},
"output": {
"type": "string" // The actual output of the action. it can be anything
},
"status": {
"type": "string",
"enum": [
"pending",
"running",
"completed",
"failed"
]
},
"error": {
"type": "object",
"description": "An error object according to RFC 7807",
"properties": {
"type": { "type": "string"},
"title": { "type": "string"},
"status": { "type": "string"},
"detail": { "type": "string"},
"instance": { "type": "string"}
}
}
},
"required": [ "status", "input" ] // here I know that the response will have the input field
},
"forms": []
}
} As you can see using the required array I can state that the input it will be always returned in the response for the {
"output": {
// according to what is described above an ActionStatus can be described with this schema
"type": "object",
"properties": {
// no more input defined
"output": {
"type": "string" // The actual output of the action. it can be anything
},
"status": {
"type": "string",
"enum": [
"pending",
"running",
"completed",
"failed"
]
},
"error": {
"type": "object",
"description": "An error object according to RFC 7807",
"properties": {
"type": { "type": "string"},
"title": { "type": "string"},
"status": { "type": "string"},
"detail": { "type": "string"},
"instance": { "type": "string"}
}
}
},
"required": [ "status" ]
},
} Note: the JSON schema might not be accurate to the spec defined by @benfrancis, it is meant to be just mean to explain my previous comment. We can describe further during the call and maybe refining it inside a PR. |
Please see #89 for a first draft of specification text to describe |
@relu91 wrote:
It's probably worth noting at this stage that I'd ideally like to get to a point where a Web Thing conformant with the Core Profile could provide a very simple Thing Description like... {
"@context": "https://www.w3.org/2019/wot/td/v1",
"id": "urn:ex:thing",
"title": "My lamp",
"profile": "https://www.w3.org/2021/wot/profile/core",
"security": { ... },
"actions": {
"fade": {
"input": {
"type": "number",
"description": "duration in ms"
},
"forms": [ { "href": "/fade" } ]
} ...then a conformant Consumer would see that the Web Thing supports the Core Profile and by applying all the defaults defined in the profile specification would arrive at a much more comprehensive canonical Thing Description much like the one you have provided above, or the one in w3c/wot-thing-description#302 (comment) with the full set of operations defined. This would mean that Web Things which support the Core Profile don't have to worry about all the complexities of dealing with multiple forms declarative protocol bindings for dynamic resources and can just provide a single HTTP endpoint for an action affordance which is then expanded out into the full set of operations for free. I see this as an extension of the current set of defaults in the Thing Description specification. |
I completely agree - simplicity is one of the primary goals of the profile spec. |
Regarding the simplicity argument of @mlagally , this does not make an implementation simpler, only its non canonical TD |
@benfrancis wrote:
Currently the Thing Description specification puts no constraints on the protocols that Web Things may use or the complexity of their protocol bindings, which makes it effectively impossible to implement a Consumer that can support any Web Thing. If we accepted that a Consumer which implements support for the Core Profile does not have to support Web Things which don't conform with the profile, then actually it could drastically simplify implementations. This is because although it may be possible to expand a simplified TD into a more complex canonical TD with declarative protocol bindings describing every little detail, Consumers would not necessarily need to support other declarative protocol bindings which don't conform with the profile. e.g. a Consumer conformant with the Core Profile may support a |
There are two fundamentally different approaches to acotions:
synchronous actions
These are the baseline and need to be supported in any case.
We have to define the set of error conditions and a way to communicate / signal a timeout.
This should not be too hard.
asynchronous actions
It is easy to create a can of worms with race conditions if we don't get it right and mess up the design.
This can get arbitrarily complex, if we think of non-atomic transactions, rollbacks, conflicting actions etc.
For these I suggest the following approach:
An action can return a "status" object, which can be used to query (i.e. poll) whether the action has been completed and returns the result.
The caller has only "read-only" access on this object - there's no way to cancel an action using this object.
If an action should be cancellable, a separate "cancel_" can be defined by the TD,
which does the right thing.
The text was updated successfully, but these errors were encountered: