RE: An updated draft of the schema.org/Action proposal

On Monday, January 27, 2014 6:16 PM, Sam Goto wrote:
> On Fri, Jan 24, 2014 at 8:47 AM, Markus Lanthaler wrote:
> > On Friday, January 24, 2014 2:24 AM, Sam Goto wrote:
> > > Here are a few demos I'm writing to validate this model with a few
> > > real world use cases that are relevant:
> > >
> > > http://code.sgo.to/crawler/yaap.html#url=http://code.sgo.to
> >
> > Wow! Cool stuff! Are these documents available somewhere to download? I
> > mean in the form of a single archive or a Git/SVN/whatever repository?
> > That would make it simpler for me to go through all the examples.
> 
> The "crawler" part of this demo is here (super messy/hacky, sorry):
> 
> https://github.com/samuelgoto/samuelgoto.github.io/tree/master/src

Thanks


> The "client/service/provider" part of the demo is currently in a private
> repo, but I'd be happy to share if there is interest.

That would be helpful as I could more easily look at all the representations you created without having to crawl the whole API.

 
> > I know the ActionHandler stuff is there for Android support etc. but
> > wouldn't it be possible to avoid it (as discussed previously) and thus
> > eliminate one level of nesting?
>
> I don't think so. There is some extra metadata that is needed to invoke
> actions on android (e.g. the application package name. without it, any
> application can filter to catch a specific url pattern) and windows (you
> need things like ms-AppFamilyName, ms-AppId and ms-MinVersion to kick off
> a native windows app).
>
> This also needs to work with iOS, so I don't think we can avoid action
> handlers.

Hmm... maybe that info could be moved into the description of the operation!? Similar to how the HTTP method is there.


> On a related note, this is quite interesting too: http://appurl.org and
> I'm really glad to see things like that popping up.

Right, that's what I meant. I works by defining custom URL schemes and would thus integrate nicely. Your app defines a proprietary URL scheme it is able to handle and whenever a URL using that scheme is opened the app gets invoked. This is similar to skype: or tel: links on the Web today. 
 


>> > I think SupportedClasses make sense, but I'm still unhappy with
>> > complexity and verbosity of large API specifications when you get into
>> > the "nested requirements case" (which gmail is addressing via a "path-
>> > like" syntax with things like "review.reviewBody" to refer to the
>> > "reviewBody" property inside "review") ... Take food orders as an
>> > example:
>> >
>> > http://code.sgo.to/crawler/yaap.html#url=http://code.sgo.to/restaurants/123/orders
>>
>> Good point. The question here, I think, is really whether you want to
>> embrace linked data principles or not. If you do, you are able to define
>> your classes with their supported properties somewhere and then reference
>> them from properties by using their URLs. Obviously clients then need to
>> fetch those documents on the fly (or cache them locally). If you want/need
>> to define everything inline it becomes trickier. A possibility would be to
>> create new classes on the fly which flatten these structures but that's
>> not very elegant either. But honestly, I don't think this nesting is
>> really a problem. It's quite symmetric and developers are used to these
>> things already. In HTML, e.g., you create similar nestings all the time
>> (think of list with sub-lists e.g.)
>
> Right. The extra hop might be a deal breaker for a few applications, like
> gmail actions. I understand your point, I just think that we need to make
> the simple case simple.

Why do you consider it as a "deal breaker"? Is

  "expects": {
     "supportedProperties":  [ ... ]
  }

really that much more complex than

  "expects": [ ... ]

 
>> > It ends up with a quite large/verbose/complex set of requirements and
>> > expectations, and I'm trying to find ways to compress that into
>> > something cleaner for the simple case.
>> >
>> > Here is another example that I explored a bit deeper:
>> >
>> > http://code.sgo.to/crawler/yaap.html#url=http://code.sgo.to/hotels/123
>> 
>> Nice, concise example (the operation type should be something like
>> BookAction though, not SearchAction).
>
> In this example, the user is "searching" for an available reservation/room
> against the "reservations" list attached to a hotel. Once you find one
> (which you do if you click on the SearchAction), you can ReserveAction an
> individual LodgingReservation.
>
> So, you SearchAction against an ItemList (collection) of
> LodingReservation. Separately, you
> ReserveAction/ConfirmAction/CancelAction a LodgingReservation that is
> available.
>
> Does that make sense?

Hmm... yeah. The "Creates a new reservation for this hotel" description of SearchAction was confusing me. I think I wouldn't model it that way though. I find it a bit unnatural to *search* for a LodgingReservation I haven't made yet. I would rather use a AvailabilityEnquiryAction or something similar.
 

>>> One idea is to take advantage of the fact that most times you want to
>>> express whether a property is required or not (rather things like
>>> cardinality, min/max values, etc). For example, if we (a) break down
>>> http://schema.org/SupportedClass.supportedProperty-ies into
>>> requiredProperty and optionalProperty and (b) let it take
>>> http://schema.org/Property (in addition to
>>> http://schema.org/SupportedProperty, which no longer holds whether the
>>> property is required or not), it avoids having to instantiate a new
>>> SupportedProperty.
>>>
>>> In the complex case (e.g. requirements on cardinality, min/max values,
>>> enumerations, etc), requiredProperty and optionalProperty may still
>>> take a SupportedProperty in case it needs to be more specific.
>>
>> How would you know what it is? Go and dereference all of them and check
>> their types?
>
> schema.org properties can take multiple types. It could potentially take a
> SupportedProperty (in case you need to be more specific) as well as a
> Property (in case you don't have any extra requirements).

What I meant was that if you allow both, you can't use the short form as you
wouldn't know what that URL stands for:

  "requiredProperty": [
    "name",
    "dslkjwe"
  ]

Is "name" a Property or a SupportedProperty? What about "dslkjwe", i.e., something your app hasn't seen yet?
 


>> Here is what this
>> http://code.sgo.to/crawler/yaap.html#url=http://code.sgo.to/hotels/123
>> would look like with these constructs:
>>
>> {
>>   "@context": "http://schema.org",
>>   "@type": "Hotel",
>>   "@id": "http://localhost:5000/hotels/123",
>>   "name": "Hilton",
>>   "reservations": {
>>     "@type": "ItemList",
>>     "@id": "http://localhost:5000/hotels/123/reservations",
>>     "operation": {
>>       "@type": "SearchAction",
>>       "actionStatus": "proposed",
>>       "expects": [{
>>           "@type": "SupportedClass",
>>           "subClassOf": "LodgingReservation",
>>           "requiredProperty": [
>>              "http://schema.org/checkInDate",
>>              "http://schema.org/checkOutDate",
>>              "http://schema.org/numberOfAdults",
>>              "http://schema.org/numberOfChildren"
>
> Just sanity checking Markus: is this valid JSON-LD? i.e. is it legal to
> say that the previous statement [1] corresponds to the following [2]
> expanded statement?
> 
> [1]
> 
> "requiredProperty": [
>   "http://schema.org/checkInDate",]
> 
> [2]
> 
> "requiredProperty": [
>   {
>     @type: Property,
>     @id: "http://schema.org/checkInDate"
>   }
>  ]

Yeah, if you type-coerce (http://www.w3.org/TR/json-ld/#type-coercion) "requiredProperty" in your context to be of type "@id" that works. You can't infer though that "http://schema.org/checkInDate" is of type "Property". At least not without setting rdfs:range of "requiredProperty" to "Property" (and you generally don't do that in Schema.org AFAIK). So it is just equivalent to

  "requiredProperty": [
    {
      @id: "http://schema.org/checkInDate"
    }
   ]

 
>>>           ]
>>>       }],
>>>       "returns": "http://schema.org/ItemList",
>>>       "actionHandler": [
>>>         {
>>>           "@type": "HttpHandler",
>>>           "name": "Creates a new reservation for this hotel",
>>>           "httpMethod": "post",
>>>         }
>>>       ]
>>>     }
>>>   }
>>> }
>>
>>> Yeah, this looks much cleaner, but you cheated a bit by eliminating the
>>> actionHandler nesting :-)
>>
>> In our latest thinking (which I still need to put down on paper and
>> publish), we think that the "expects" and "returns" constructs should go
>> into the Action-level properties, because it may apply to multiple action
>> handlers (example: if you have an android app as well as a web API, you
>> don't want to repeat yourself what your API looks like).

Definitely makes sense. Can't we get rid of actionHandler then at the same time by moving that information to the operation itself as already proposed above. In other words, an operation would not only be an HttpOperation but also, e.g., an AndroidOperation at the same time. The application which invokes it extracts the necessary information directly from the operation.

  {
    "@type": ["SearchAction", "HttpOperation", "AndroidOperation" ],
    "httpMethod": "POST",
    "androidIntent": ...  <-- or something similar, you get the idea
  }

 
> >> Furthermore, as soon as you need to add a single property (not just
> >> cardinality but also a *label* or a *description*) you would end up with
> >> exactly the same nesting. Have a look at this variation:
> 
> Yep, in that case you'd want to use a SupportedProperty instead.

Again, how do you distinguish between them?

 
>>   {
>>     "@context": "http://schema.org",
>>     "@type": "Hotel",
>>     "@id": "http://localhost:5000/hotels/123",
>>     "name": "Hilton",
>>     "reservations": {
>>       "@type": "ItemList",
>>       "@id": "http://localhost:5000/hotels/123/reservations",
>>       "operations": {
>>         "@type": "BookAction",
>>         "name": "Creates a new reservation for this hotel",
>>         "method": "POST",
>>         "expects": [{
>>           "@type": "SupportedClass",
>>           "subClassOf": "LodgingReservation",
>>           "supportedProperty": [
>>              { "property": "checkInDate", "required": true },
>>              { "property": "checkOutDate", "required": true },
>>              { "property": "numberOfAdults", "required": true },
>>              { "property": "numberOfChildren", "required": true }
>>           ]
>>         }],
>>         "returns": "http://schema.org/ItemList"
>>       }
>>     }
>>   }
>> 
>> How do you find this version?
>
> Does this version require any "automatic-expansion" that you mentioned
> before? Or is this valid JSON-LD out of the box?

It's valid JSON-LD out of the box. With "expansion" I was talking about how a JSON-LD processor interprets a JSON-LD document. Internally, it expands terms to URLs.

See http://www.w3.org/TR/json-ld-api/#expansion
 

>> I removed the HttpHandler in the example. I know you probably need it to
>> decide whether to invoke the operation directly and showing the user just
>> the result or open a new browser window. That information could be
>> expressed as an operation type if needed so, "@type": [ "BookAction",
>> "WorkflowOperation" }. In lack of a better term I used WorkflowOperation
>> to express that this specific operation is part of a larger workflow and
>> that requires a new browser window to be opened so that the user can
>> complete the remaining steps.
>
> Handlers are not necessarily "workflow" specific. That is, they exist
> because invoking actions in different platforms is complicated (e.g. this
> is what it takes to invoke on android, and this is how you invoke on
> windows).

OK. What do you think about moving that information to the operation itself as suggested above? I haven't thought hard about it yet but I can't see any obvious disadvantages of doing so. Quite the contrary.


Cheers,
Markus


--
Markus Lanthaler
@markuslanthaler

Received on Tuesday, 28 January 2014 13:45:31 UTC