Skip to content
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

HTTP Binding for providing historical events. #892

Open
RoboPhred opened this issue Apr 15, 2020 · 29 comments
Open

HTTP Binding for providing historical events. #892

RoboPhred opened this issue Apr 15, 2020 · 29 comments
Labels
Defer to TD 2.0 EventAffordance Topics related to Event Affordances Has Use Case Potential The use case can be extracted and explained Selected for Use Case The issue is relevant for the work and should move to an use case

Comments

@RoboPhred
Copy link

I am looking at implementing Events for a TD-providing project with HTTP bindings. The event affoardance seem to cover subscribing to a stream of events, but is there any mechanism for requesting an array of past events?

For context, I have a project that originally started following the Mozilla WOT spec, and I am now adapting it to cover the W3C TD spec instead. Mozilla specifies the event endpoint as an array of past events with timestamps, and this is something I would like to be able to model with a TD.

@egekorkan
Copy link
Contributor

Just an idea: You can have a property called "pastEvents" as an array type and the items can like this:

// pastEvents property
{
  "type": "array",
  "items": {
    "type": "string", // or more like the date time information
    "@type":"EventAffordance"
  }
}

I am not very sure since this would mean that you would expect the "forms" of events in the items as well. Maybe you should use "td:hasNotificationSchema". Any opinions @vcharpenay ?

@sebastiankb sebastiankb added the EventAffordance Topics related to Event Affordances label Apr 17, 2020
@RoboPhred
Copy link
Author

Providing the events through a property is workable, and this technique could also cover the missing ActionRequest concept from the Mozilla WOT spec.

It would be nice to have a stronger contract for this though, possibly by a "gethistoricalevents" op form. This seems to be similar in scope to "readmultipleproperties" as an optional extension onto the affoardance.

@egekorkan
Copy link
Contributor

egekorkan commented Apr 17, 2020

We have discussed this as well but for us this is specific for an implementation and should not be integrated into the standard. Given further interest from other implementations and members, it can be in the future.

@benfrancis
Copy link
Member

You can have a property called "pastEvents"

I always try to restrict the use of properties for actual physical properties of a device which cause a change of state in the real world, and avoid using them to represent things like internal firmware settings or data logs. Using properties for event logs or action queues feels like a hack to shoehorn an API/protocol that can't currently be expressed via the Thing Description into the TD Information Model.

The HTTP Protocol Binding currently describes how to get and set properties and invoke actions, but not how to receive events or cancel or query the state of an action request.

I think this behaviour could be defined for HTTP and WebSockets through an extended HTTP Protocol Binding or default profile, and a WebSocket sub-protocol.

@benfrancis
Copy link
Member

benfrancis commented Apr 17, 2020

See also, section 2.5 of the WG Charter.

Complex Interactions

Many use cases require the implementation of complex interactions that span multiple protocol transactions. An example would be the initiation, monitoring, and possible cancellation of ongoing actions. Another would be managing and accessing historical queues for events and actions.

In fact such complex interactions can be implemented with the more basic features already included in the WoT Thing Description and in particular can be supported in a self-describing way with hypermedia controls. Hypermedia controls can also be used to clarify what remediary actions can be taken when an error occurs.

However, there are many possible ways to implement and use such complex interactions. To better support the goals of interoperability, it would be useful to precisely define the most common complex interactions.

This work item will document the behavior of complex interactions, including:

  1. The ability to initiate, monitor, and cancel ongoing actions.
  2. Support for action and event queues.
  3. Error recovery via hypermedia responses.

@egekorkan
Copy link
Contributor

The action queue are indeed something we are working on. I was not aware of the event queue statement of the working charter, so this is my bad. Just a question though, how does the Consumer use the information provided in the past events?

@zolkis
Copy link

zolkis commented Apr 17, 2020

Let's not mix the data model of a specific Thing with the general WoT interaction model.
It's not clear whether the use case is
a) to fetch past Event interactions (and require a WoT mechanism for that)
b) or to fetch data since the last read in a time series.

An Event interaction is meant to model a subscription based transient notification about a state change.
Data, unless very small and sparse, should be transmitted by other means.

I would not use Event interactions to represent/convey time series. Depending on network conditions are sampling frequency + data size, it would be a race. Modeling these with Action interactions seem more suitable, as it could be adapted better to the use case.

We could do proofs of concept with these and see what would be worth standardizing as WoT mechanism for handling streams.

I agree we should check if Actions need refinement. (We also discussed aspects of synchronicity vs atomicity in action sequences/transactions.)

@benfrancis
Copy link
Member

benfrancis commented Apr 17, 2020

how does the Consumer use the information provided in the past events?

Events are obviously tricky to map onto HTTP in general because it has no built-in push mechanism (e.g. like CoAP does). Therefore, in its REST API the Mozilla WebThings implementation provides events using a GET on the URL of an event resource which can be polled at intervals if necessary.

This GET request could just return the latest event, but then the client may miss some events. The response to the GET request therefore contains an array of recent events with timestamps, so the client can figure out what events have been emitted since the last GET. This is obviously an imperfect solution because it still requires the resource to be polled often enough to receive every event, or for the server to keep track of which clients have received which events. This is why there is also a separate push mechanism in the WebSocket API to receive events as they are emitted, but the event resource in the REST API provides a fallback for clients which don't support WebSockets.

There are of course other potential solutions to receiving events over HTTP like long-polling and Server Sent Events, but none of them are perfect and currently the HTTP Protocol Binding in the Thing Description specification doesn't specify any one solution as a default.

@benfrancis
Copy link
Member

benfrancis commented Apr 17, 2020

It's not clear whether the use case is
a) to fetch past Event interactions (and require a WoT mechanism for that)
b) or to fetch data since the last read in a time series.

Events can contain data. The format of these data is described by the data member of the EventAffordance.

I would not use Event interactions to represent/convey time series. Depending on network conditions are sampling frequency + data size, it would be a race. Modeling these with Action interactions seem more suitable, as it could be adapted better to the use case.

Rather than discuss this abstractly, here are some concrete examples of events in Mozilla's WoT capability schemas which have been mapped onto real devices:

  • AlarmEvent - Indicates an alarm has been triggered.
  • PressedEvent - Indicates a button has been single-pressed.
  • DoublePressedEvent - Indicates a button has been double-pressed.
  • LongPressedEvent - Indicates a button has been long-pressed.
  • OverheatedEvent - Indicates a device has exceeded its safe operating temperature (this event can contain the current temperature as data in its payload).

None of these events could be modelled as an action as far as I can tell, because they are events emitted by a device, not actions invoked upon the device.

The more common way to model a time series of data would be through properties (e.g. streaming temperature values from a temperature property over a WebSocket). But events can contain data in some cases too.

@RoboPhred
Copy link
Author

RoboPhred commented Apr 17, 2020

In my case, the use case is to fetch past interactions for display. For example, imagine a TD-speaking website using the HTTP binding wants to show the times when a motion detector has detected motion.

For an example of this out in the home automation world, OpenHab can provide a timeline of events. Home Assistant also provides timelines, graphs, and event counters (as seen in the first image of the readme).

This information is useful largely for its historical readings, as it would not be particularly useful if the user could only be told of motion detection that has happened while the website is open.

Admittedly, a property that contains an array of previous values could work here, but knowledge of historical event values is a useful construct. I feel it would be preferable to implement this as a concern of the event, but alternatively an extension that lets me describe a property as offering up historical data for a given event would also be acceptable. In the latter case, my library would auto-generate such properties for each event.

My justification for having this be part of EventAffordance is that, to represent events as a semantic concept, it should provide not only the ability to observe events but also to know of past occurrences of the event.

Pushing this off on a property does seem a misuse of the concepts, as things like a list of previous motion detection cannot be said to be a part of the state of a motion detector. And while temperature and heating/cooling status can be said to be part of an air handler's state, having a property be an array of timestamps and previous states again seems closer to an event concern than one of state.

@benfrancis
Copy link
Member

benfrancis commented Apr 17, 2020

Here is an event log from the WebThings UI:

event_log

@zolkis
Copy link

zolkis commented Apr 17, 2020

This information is useful largely for its historical readings, as it would not be particularly useful if the user could only be told of motion detection that has happened while the website is open.

So we'd need a new interaction construct that would be able to describe a series of past Event interactions (presumably configured during subscribe).

That would make event subscription the "meta-action" to configure past events delivery, which could happen in two (perhaps configurable) ways,

  • in a single bulk transaction (array of Events)
  • Event by event starting from the oldest event configured/available.

For this we'd need to modify the contract of subscribeevent and specify that Events might be delivered in arrays.

@egekorkan
Copy link
Contributor

From the discussion, it seems that the use case is indeed present. In the new TD version we are planning to not introduce any breaking changes, so changing what subscribeevent means would not be ideal. Some ideas:

  • Creating a new op value for the events like @RoboPhred mentioned in the comment above. The problem here would be how to choose a limit on the history but it can be done via uriVariables. The default mapping for this would be an HTTP GET request in case of HTTP. Also, there would be an assertion that this data will be always an array. While we do this already with readallproperties, where it is specified to be always an array, I only partly like this solution since it is quite prescriptive, i.e. it constrains how the payload can look like. This also doesn't solve the getting the history of all events that Mozilla Webthings support, for which we would need a meta interaction like getallhistoricalevents. Furthermore, how would this be indicated outside of the forms? For all the other op value, we have something in the interaction level that gives the hint that one form will contain the operation, i.e. "readOnly":"false" gives the hint that there will be "op":"writeproperty" in one of the forms. This is not a must though.
  • Creating another meta-interaction in the root level of the TD like readallproperties. The problem would be linking this to an event in the interactions and I guess it would be more messy.

@zolkis
Copy link

zolkis commented Apr 17, 2020

In the new TD version we are planning to not introduce any breaking changes, so changing what subscribeevent means would not be ideal.

Adding options to subscribeevent would not be a break... without options it would work the same way as now. But indeed a separate op could be better.

@RoboPhred
Copy link
Author

RoboPhred commented Apr 17, 2020

Treating this as a new op seems to be the best option in my opinion.

I can add to the conversation around using subscribeevent though. From the viewpoint of a subscription, this reminds me of hot and cold observables, used in rx / rxjs. In short:

  • 'hot' is the concept of subscribing to an ongoing feed. You only get the new items, and new subscribers will not see items received by previous subscribers.
  • 'cold' is the concept of subscribing to the entire contents of the feed, as if you subscribed at the time of feed creation. Any new subscribers immediately receive every value emitted since the start.

In this case, coldStart / supportsColdStart might make a good option or hint property name to opt-in to receiving previous events when making a new subscription. This would still allow the subscription to continue to receive future events as well.

I still believe a new op might better serve this though. While I do intend to both receive past events and subscribe to new ones at the same time, it might be better to keep the two ideas separate. I am not fully convinced either way through.

The problem of the data format for a readhistoricalevents op is one I don't have answers for. For one, we would need to somehow pass along timestamp data, which might not come over when the event is in its subscription form. This means either wrapping each event in a timestamped envelope similar to the current Mozilla WOT implementation, or allowing the TD to specify a different schema for historical events. A third option could just be to leave events in the same data format, but trust that the event will provide the timestamp on its own, in both subscribe and historical mode.

A problem with both approaches is that devices presumably exist indefinitely, so the historical action list is unbounded. There should be a way to both limit the number of items the client asks for, and to explain to the client the limits on the device itself. Both of these could be done through either a hard count on the number of historical events, or by the age of the event. It may even be advisable to provide both limits on the same event (IE: TD declares an event supporting the previous 100 events over the past 2 weeks).

@zolkis
Copy link

zolkis commented Apr 18, 2020

There should be a way to both limit the number of items the client asks for, and to explain to the client the limits on the device itself.

There could be subscription options to tell the client prefs, and server limitations could just manifest in how the events/data is delivered.

I still believe a new op might better serve this though.

A separate op would work if someone first subscribes to an Event and then would fetch event history - otherwise it's a race. I guess the separate op would deliver the events in bulk.

I am thinking we'd need that bulk delivery mechanism even for normal Events if we wanted to support reporting high frequency data, in order to avoid network+processing roundtrips, then expose as an iterable, rather than firing a local event every time - it all depends on reporting rate, sampling rate, sample size, network conditions and the capabilities of the client and server.
In theory, with subscribe options (including hot/cold subscriptions) and possibility of bulk delivery we could represent all these use cases with Events: simple events with small/sparse data, event history and high frequency data. It can be even made backward compatible.

But it would be a simpler solution to separate interactions with bulk delivery vs single delivery, so we may want to stick with the current assumption that Event interactions should be used for state change notifications, eventually carrying small and sparse data (like in the use cases @benfrancis quoted). History of such Events would then be supported by a separate op, with options.

@takuki
Copy link
Contributor

takuki commented May 8, 2020

For example, DDS has QoS of History to configure how many (or all) values should be retained.
Please see page 105 in DDS specification.
Some industrial applications find this useful to make analysis of past values when it started observing a property.

@sebastiankb
Copy link
Contributor

Beside of the technical discussion we should have "historical events" as a use case and shared it with the Arch TF. I will provide an issue for it.

@takuki
Copy link
Contributor

takuki commented May 20, 2020

There is a "Event Sourcing" pattern for historical events. (from Microsoft Azure documentation), which might provide an useful input to the use case discussion.

Event Sourcing

When to use this pattern

  • When you want to record events that occur, and be able to replay them to restore the state of a system, roll back changes, or keep a history and audit log. For example, when a task involves multiple steps you might need to execute actions to revert updates and then replay some steps to bring the data back into a consistent state.

This pattern might not be useful in the following situations:

  • Small or simple domains, systems that have little or no business logic, or nondomain systems that naturally work well with traditional CRUD data management mechanisms.
  • Systems where consistency and real-time updates to the views of the data are required.

@benfrancis
Copy link
Member

Support for event queues is in the current charter and I'm conscious we haven't come up with a solution for this yet. This is also needed in order to make WebThings W3C compliant (see WebThingsIO/gateway#2806 and WebThingsIO/gateway#2807). This feature would be particularly useful for an HTTP WoT consumer which doesn't support a separate event mechanism like Server Sent Events or WebSockets, but is also useful for all consumers which want to get historical event data (e.g. which occurred before a WebSocket was opened).

Proposed Solution

readpastevents

What do people think about a readpastevents operation which fetches a log of recent event data for a given event? e.g.

"events": {
  "overheated": {
    "title": "Overheated",
    "type": "number",
    "unit": "degree celsius",
    "description": "The lamp has exceeded its safe operating temperature",
    "forms": [{
      "op": "readpastevents",
      "href": "https://foo.webthings.io/things/lamp/events/overheated"
    }]
  }
}

readallpastevents

There could also be a readallpastevents operation in the top level forms member of a thing description which gets a log of event data for all a thing's events. e.g.

"forms": [
  {
    "op": "readallpastevents",
    "href": "https://foo.webthings.io/things/lamp/events"
  }
[

Challenge 1: Operation naming

I'm open to suggestions on naming since readallpastevents is a bit ambiguous as to whether it's all event types or all event instances.

Challenge 2: Payload representation

Another challenge is how to describe the payload of such resources, since its desirable to include timestamps in the data.

Currently WebThings returns payloads like this for both event and events resources:

[
  {
    "overheated": {
      "data": 102,
      "timestamp": "2017-01-25T15:01:35+00:00"
    }
  },
  {
    "overheated": {
      "data": 101,
      "timestamp": "2017-01-24T13:02:45+00:00"
    }
  }
]

But one of the things we're trying to do in order to be W3C compliant is to remove these types of object wrappers which are described in the Web Thing API specification rather than declaratively inside the TD.

For the payloads returned for individual event affordances we could remove the event name from the payload as that is implicit, e.g.

[
  {
    "data": 102,
    "timestamp": "2017-01-25T15:01:35+00:00"
  },
  {
    "data": 101,
    "timestamp": "2017-01-24T13:02:45+00:00"
  }
]

We could then try to describe this payload declaratively in the TD, e.g.

{
  "events": {
    "overheated": {
      "title": "Overheated",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "data": {
            "type": "integer"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "unit": "degree celsius",
      "description": "The lamp has exceeded its safe operating temperature",
      "forms": [{
        "op": "readpastevents",
        "href": "https://foo.webthings.io/things/lamp/events/overheated"
      }]
    }
  }
}

A downside of describing the payload declaratively in the Thing Description in this way is that it makes the method of providing timestamps implementation specific which will be harder for consumers to make sense of. Additional semantic annotations might be needed to better explain the semantic meaning of the payloads.

For the readallpastevents operation we would need the event name for each event instance. That could use special text in the specification like readallproperties which wraps a separate event log for each event type in its own object keyed by object name. For our use case in WebThings of showing the user a log of all events, this would require more work because we'd have to merge and sort the lists, but it could do the job.

{
  "overheated": [
    {
      "data": 102,
      "timestamp": "2017-01-25T15:01:35+00:00"
    },
    {
      "data": 101,
      "timestamp": "2017-01-24T13:02:45+00:00"
    }
  ],
  "alarm": [
    {
      "timestamp": "2017-01-25T15:01:35+00:00"
    },
    {
      "timestamp": "2017-01-24T13:02:43+00:00"
    }
  ]
}

Challenge 3: Pagination

Another challenge is how to decide how many events to show at a time (WebThings Gateway just picks an arbitrary number), and whether pagination features are needed like are being discussed for the Directory Service API. And if so how those features would be described in the Thing Description.


I acknowledge that the above is all a bit complicated and is different to existing operations in that it describes historical data (there is no way to get past values of properties for example). The working group may therefore decide only to specify the readpastevents operation, or not to specify any new operations at all.

However, if no solution is found to this problem some consequences will be:

  1. There will be no obvious way for WoT consumers to consume events using only HTTP, it will always require an additional mechanism such as Server Sent Events or WebSockets. For HTTP-only consumers event metadata will be useless and this may force the developers of HTTP-only WoT producers to try to work around the problem by representing event style data as properties instead, breaking the WoT data model.
  2. It won't be possible to make existing features of WebThings W3C compliant, which means:
    1. Removing event log features from all 16 web thing libraries in the WebThings Framework
    2. Limiting event logs in WebThings Gateway to not display any historical data, only events emitted since the user started viewing the log (by listening to a WebSocket), or by using a separate API completely separate from the Web of Things

@egekorkan
Copy link
Contributor

Challenge 1: Operation naming

I think this can be done like readallproperties and readmultipleproperties operations we currently have so it would be clear whether we mean all events or a subset.

Challenge 2: Payload representation

I think this is a more difficult thing to solve.

A downside of describing the payload declaratively in the Thing Description in this way is that it makes the method of providing timestamps implementation specific which will be harder for consumers to make sense of. Additional semantic annotations might be needed to better explain the semantic meaning of the payloads.

This would also change the Data Schema for a single and historical data, i.e. the example you have would not describe the Data Schema of a single event (btw we wrap event data in a data object). However, this has given me the following idea (that I am not super satisfied of 😬 ):

{
	"events": {
		"overheated": {
			"data": {
				"type": "integer"
			},
			"historicalData": {
				"type": "array",
				"items": {
					"type": "object",
					"properties": {
						"data": {
							"$ref": "#/events/overheated/data"
						},
						"timestamp": {
							"type": "string",
							"format": "date-time"
						}
					}
				}
			},
			"forms": [{
				"op": "subscribeevent",
				"href": "https://foo.webthings.io/things/lamp/events/overheated",
				"subprotocol": "sse"
			}, {
				"op": "readpastevents",
				"href": "https://foo.webthings.io/things/lamp/events/overheated"
			}]
		}
	}
}

Still the timestamp key needs to be understood by each consumer but it doesn't have to look for a semantic tag everywhere and this would allow the historical data to be modeled as a custom object as well (not that it makes sense but it would be possible).

Regarding pagination, it might be also described in the historicalData object somehow?

Regarding your last points: I don't think that they should be removed, historical data is very useful for dashboards showing graph of a value over time. If no solution is found, the worst-case solution would be to define a property and link it to the events

@zolkis
Copy link

zolkis commented Mar 18, 2021

This makes sense, but not every device might [be able to] implement it.
What matters is that a TD can describe it when there is support for it.

About pagination, an iterator/cursor approach would work on a JS API level, but on protocol level it needs to be supported.
The interaction options could contain the usual parameters in this case,

  • the max number of past events in the reply,
  • whether a session is started (i.e. next call will give the next batch, instead of the most fresh one)
  • or ended.
    Sensible defaults could be a small number of events (e.g. the last 10), and no session.

I think that readallpastevents is a can of worms, but I see the theoretical gap without it :).

@relu91
Copy link
Member

relu91 commented Mar 19, 2021

I support the idea of introducing a new operation type for this goal, as long as we don't start to spawn one new operation type every time we found that something is missing. I like the approach because is protocol binding independent so that existing implementations do not need any new update, very nice 👍🏻 . Just to mention the possible impact points of the proposal:

  • Scripting API should map the new operation type with a dedicated function. Another positive aspect is that this change is backward compatible and thanks to feature dection should not be a big problem. Right, @zolkis?
  • Thing Description specification needs to be updated to describe the new op
  • Architecture document, in particular the form sub section

Now the bad news, this feature is not backward compatible. In the sense that a TD that has readpastevents would not validate the TD 1.0 schema. This would force us to wait until TD 2.0 😢. However, I might have an idea.

What if we define a meta/pseudo resource/propertyaffordance(?) called the EventLog that can be associated with an EventAffordace or a PropertyAffordance or at root level(optionally as @zolkis suggested). Then the EventLog can be read thanks to the newly introduced op type. Kinda following what @egekorkan is proposing. Here's a concrete example:

{
    "events": {
        "overheated": {
            "data": {
                "type": "integer"
            },
            "eventLog": {
                /* Event log implements dataschema
                so that we can describe it's content
                similarly to Ege's example */
                "forms":[
                    {
                        "op": "readeventlog",
                        "href": "https://foo.webthings.io/things/lamp/events/overheated/log"
                    }
                ]
            },
            "forms": [
                {
                    "op": "subscribeevent",
                    "href": "https://foo.webthings.io/things/lamp/events/overheated",
                    "subprotocol": "sse"
                }
            ]
        }
    },
    "eventLog":{
        "forms": [
            {
                "op": "readalleventlogs", // or simply reuse "readeventlog"
                "href": "https://foo.webthings.io/things/lamp/events/logs",
                "subprotocol": "sse"
            }
        ]
    }
}

We might even exploit this design pattern to solve #302 and other problems related to the state management of actions. The biggest pro right now is that it's backward compatible (i.e., an old client would just ignore it). I also kinda see it as a possible good pattern for future extestions, but what do people think? is it too verbose/complex?

@benfrancis
Copy link
Member

benfrancis commented Mar 19, 2021

@relu91 wrote:

I support the idea of introducing a new operation type for this goal, as long as we don't start to spawn one new operation type every time we found that something is missing.

I'm afraid this is an inevitable consequence of the form-based declarative protocol binding approach of the W3C TD spec. This list is only going to grow over time. For 1.1 we are already discussing:

  • readpastevents (or readeventlog)
  • readallpastevents (or readalleventlogs)
  • queryaction
  • updateaction
  • cancelaction

Now the bad news, this feature is not backward compatible. In the sense that a TD that has readpastevents would not validate the TD 1.0 schema.

Ugh, really? Are you sure this isn't backwards compatible? The normative text in section 5.3.4.2 says:

The list of possible operation types of a form is fixed. As of this version of the specification, it only includes the well-known types necessary to implement the WoT interaction model described in [WOT-ARCHITECTURE]. Future versions of the standard may extend this list but operations types SHOULD NOT be arbitrarily set by servients.

Therefore, whilst the list of operations is fixed in 1.0, consumers should be expecting that future versions of the specification may extend this list. It also doesn't say that operations MUST NOT be arbitrarily set by servients, only that they SHOULD NOT, so consumers would trip up on producers which don't follow this guidance.

The JSON Schema in section B does indeed assert that op must have oneOf a fixed enum of values, but that section is non-normative.

If the list of operations really can't be extended, then that was a significant oversight and I don't see how it's possible to fulfill may of the goals set out in the current charter like extending the Thing Description vocabulary, extending protocol binding vocabulary and complex interactions. If this is the case, and the version number is the problem, then rather than using workarounds like defining new nested forms inside interaction affordances (which is a clever solution, but is going to create a real mess over time) I'd advocate simply skipping to 2.0! The charter doesn't say "Web of Things (WoT) Thing Description 1.1", or anything about backwards compatibility, it just says "Web of Things (WoT) Thing Description (Update)".

@relu91
Copy link
Member

relu91 commented Mar 19, 2021

I'm afraid this is an inevitable consequence of the form-based declarative protocol binding approach of the W3C TD spec. This list is only going to grow over time.

I am not against it, as I said this approach is scalable and well integrated with the current model. It was just a warning to not be too permissive 😅 . I think it would be weird to discuss an operation that would be easily implemented using the existing mechanisms. We might need to describe a rationale that rules the new operation introduction.

The JSON Schema in section B does indeed assert that op must have oneOf a fixed enum of values, but that section is non-normative.

Yeah, I was referring to the JSON schema. Naively I thought that it would follow the normative sections, but as the text is more permissive I think we have no problem then!

If this is the case, and the version number is the problem, then rather than using workarounds like defining new nested forms inside interaction affordances (which is a clever solution, but is going to create a real mess over time) I'd advocate simply skipping to 2.0! The charter doesn't say "Web of Things (WoT) Thing Description 1.1", or anything about backwards compatibility, it just says "Web of Things (WoT) Thing Description (Update)".

Yep, the nested object was more like a temporary solution to be changed in 2.0, still it might be used as a means to the describe the log dataschema as @egekorkan is pointing out.

About Challenge 2:
I think the only way to assure a little bit of consistency is to define proper semantic tags. However, some implementations could use different structures, making it difficult to really use the event log in a consistent matter. We have similar problems with read/writeall/mutipleproperties as the TD spec does not really explain how the payload really should look like (please, correct me if am wrong).

About challenge 3:
I would say that without pagination reading the event log would be impractical. Therefore, this is a kinda blocking feature that we need to understand how to properly model. In practice, pagination support should provide a description of how an application would provide a page number and total count to the underlying protocol. The output representation might be less problematic cause usually pagination is used for the list of things and the eventLog would be already described as an array.

If we want to be declarative as in other places, this would imply to resume the idea of the nested object to describe the inputs. Would the eventlog support other parameters? Filtering? Should we just surrender to the idea that we cannot describe everything declarativly?

Following the declarative path, I would go with the definition of semantic tags (e.g., page and count or others) to indicate that those parameters are used for the pagination control. The advantage is that they could be used also in actions or properties.

@egekorkan
Copy link
Contributor

Now the bad news, this feature is not backward compatible. In the sense that a TD that has readpastevents would not validate the TD 1.0 schema. This would force us to wait until TD 2.0 cry.

Just on this point, backwards compatibility is very poorly defined, you can even ask the TD Task Force members and you will get different answers. If a TD that used to validate now does not, it is serious compatibility issue. However, a new TD does not have to be consumed by an old consumer without any problems. The @context value will change anyways and if the consumer was doing basic validation, it will fail. So we can add new op values :)

Regarding adding more and more operations, I think it is inevitable. Scripting API will have to cover those as well. Most programming APIs keep adding new functions/methods as well so maybe it will be seen badly. However, this should spice up the profile discussions ;)

@benfrancis
Copy link
Member

@relu91 wrote:

I think the only way to assure a little bit of consistency is to define proper semantic tags.

I agree.

However, some implementations could use different structures, making it difficult to really use the event log in a consistent matter. We have similar problems with read/writeall/mutipleproperties as the TD spec does not really explain how the payload really should look like (please, correct me if am wrong).

Actually the spec does define this:

"The data schema for each of these meta-interactions is constructed by combining the data schemas of each PropertyAffordance instance in a single ObjectSchema instance, where the properties Map of the ObjectSchema instance contains each data schema of the PropertyAffordances identified by the name of the corresponding PropertyAffordances instance."

In my example I applied the same rule to readallpastevents.

I would say that without pagination reading the event log would be impractical. Therefore, this is a kinda blocking feature that we need to understand how to properly model.

I like the approach being proposed for the Directory Service API in w3c/wot-discovery#130 (rendered here) which is based on the Linked Data Platform Paging specification using Link and Content-Range headers in responses. The actual payload just ends up being a JSON array because all the pagination metadata is contained in HTTP headers. It might be possible to describe this in a Thing Description but it's a bit of a push, plus it's HTTP specific and I'm not sure whether other protocols have an equivalent.

  "forms": [{
    "op": "readpastevents",
    "href": "https://foo.webthings.io/things/lamp/events/overheated?offset={offset}&limit={limit}",
    "htv:methodName": "GET",
    "response": {
      "htv:headers": [
        {
          "htv:fieldName": "Link"
        },
        {
          "htv:fieldName": "Content-Range"
        }
      ],
    },
  "uriVariables": {
    "offset": {
      "type": "number",
      "default": 0
    },
      "limit": {
      "type": "number",
      "default": 10 
    }
  }
}]

The above follows the WIP example from the Directory Service API, but I don't think it's complete because I think the htv:headers also needs to specify the meaning of the content of the Link and Content-Range headers (see https://www.w3.org/TR/wot-binding-templates/#example-21-http-vocabulary-example-for-header-options for how you describe the content of headers).

@benfrancis
Copy link
Member

To follow up on this, for the time being I think we've concluded not to try to describe the historical event log WebThings exposes in the Thing Description and to instead implement new Server-Sent Events endpoints which are described in the Thing Description instead. This will most likely follow the events API proposed for the WoT Core Profile in w3c/wot-profile#92

@sebastiankb
Copy link
Contributor

from today's TD call:

  • so far we have not found a generic approach about this topic
  • however, group see this as an important topic which should be addressed in the next version (TD 2.0)

@egekorkan egekorkan added Has Use Case Potential The use case can be extracted and explained Selected for Use Case The issue is relevant for the work and should move to an use case labels Jan 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Defer to TD 2.0 EventAffordance Topics related to Event Affordances Has Use Case Potential The use case can be extracted and explained Selected for Use Case The issue is relevant for the work and should move to an use case
Projects
None yet
Development

No branches or pull requests

7 participants