Friday, September 5, 2014

WS-REST 2014 Keynote



This year I had the honor to be invited by an old friend to give a keynote at WS-REST at WWW in Seoul.

Apart from meeting lots of great people, it was a great opportunity to get introduced to so many exciting ideas.

I figure that it could be useful to post my presentation online, as well as some text about it in case anyone that wasn't there needed a reference. So here it goes:



* reading note: some of the parts of the talk I wrote about before, so instead of repeating myself, I embedded links to them. You want to de-reference them inline as you go along.

It went more of less like the following...

"I think we are living in exciting times.

You see, there is this interesting idea that most technologies go through this notion of a hype-cycle: a technology trigger happens, they go through a peak of inflated expectations a trough of disillusionments, the slope of enlightment and eventually lead to a plateau of productivity.

And it feels like Hypermedia APIs are going through all that but it is early enough that we can feel that we can influence where it goes. Sort of like being at Sun when Java was being designed or Bell Labs when UNIX was being sorted out.

Sort of like the person that proposed <a>s to be added to HTML: it feels obvious now, but oh boy, did that person enabled billion dollar businesses to be created!

But even before I talk about the technology triggers, let me first tell you where I come from.

My Rock Bottom

A few years ago I was working on gmail with the task of providing better visualizations of links on emails. We used to call that "mail intelligence" or something like that.



So, as any engineer would probably do, I gathered a set of examples (youtube links, flickr links, lala.com links, etc) and I went on writing regular expressions to detect them as code to talk to their individual APIs.

That launched and it was really cool.

The week it launched, my product manager came to my desk and said "wow, that was cool, can we do vimeo.com urls too?". And I said "yes, of course, let me just add that real quick".

A few days later, 3 new urls types requests came along. Ultimately, I had personally written over 13 regexes and API calls. I thought to myself: "I don't think this is going to scale" ... and my co-worker sitting next to me, noticing how busy I was, replied "do you think so?" :)

On my next project, I got smarter.

I was doing link posting for our social products.



When I was tasked to parse video urls I said: this is the common interface between google and video services. this is how you describe its affordances. We'll implement it once and onboard any video website.

That launched and it was really cool.

But then we needed to do photo websites too. And articles. And reviews. And all of the things that you could do with them. I had personally implemented over 25 of these individually before I stopped and asked asked myself again "I don't think this is going to scale" ... "do you think so?", said my co-worker smiling at me again :)

We didn't get to where we are because we are smart.

We got to where we are because we tried everything else and failed.

When the idea of crawling these links and parsing schema.org markup was brought to my attention, it felt like a breath of fresh air (well, in reality, I ignored it at first. it took me a few rounds for it to sink in).

But the idea was powerful: delegate. Instead of writing specific APIs, give webmasters the tools they needed to express what was on the link and find a way to degrade gracefully.

Folks started exploring what would be involved in adding verbs to schema.org to allow the representation of affordances. We asked questions like: what's the difference between purchase and buy? And how do you express what's involved in fulfilling an action?

But something was missing ... our foundation wasn't feeling as solid as we would like to.

The real measurement of code quality: WTF/min

On my way to the airport to a long trip, I picked up a book at a local bookstore. It was called RESTful Web Services.

By the time I landed, I had read it 3 times. For most of the time, I was cursing. In the right wtf/min rate.


I felt like I had finally found someone in the world that understood what challenges I was facing. We were coming from different ends (I was coming from the "web for humans i.e. webpages", and they were coming from the "web for computers i.e. APIs") but we could almost touch hands in the middle.

I went back excited to tell my co-workers about it. I told them:

- "Guys, our APIs suck, we are an L2 API provider at best!"
- "How could that be?", some asked. "We proud ourselves to provide solid APIs". "Well, how many levels are there even?".

Ah, that's so cool! Show me what your APIs look like!

... and I said: well, hummm, that's the issue, I can't. You know, I'm embarrassed about my APIs too :(

Here are the challenges that they are facing.

Lots of challenges, but these are exciting times."

Sam Goto, April 7th 2014 WWW/WS-REST @ Seoul.

That's, hum, cool. But what happened after that?

I've been busy getting stuff done :) Check this out.



Thursday, September 4, 2014

schema.org actions implementations

... a random collection of screenshots/snippets of actions in practice.

Since it was announced earlier this year, I get often asked how and where schema.org/Actions are used in practice: what real life products it enables.

I was preparing a presentation for a talk to a local workshop, and I was asked for that exact same content, so I figure I'd just copy-paste it here and update it as I find more (most of the content is linked from here).

A caveat: some products launched before the final specification went out (i.e. the official documentation still contains older markup), so I updated the code snippets to the current state (for consistency).

So, here it goes (in no particular order).

Google Search Sitelinks Search Box

Official docs:

Here is what you write:
<script type="application/ld+json">
{
   "@context": "http://schema.org",
   "@type": "WebSite",
   "url": "https://www.example-petstore.com/",
   "potentialAction": {
     "@type": "SearchAction",
     "target": "https://host.example-petstore.com/search?q={search_term}",
     "query-input": "required name=search_term"
   }
}
</script>

This is what you get:



Google Knowledge Graph

Here is what you write:

http://insidesearch.blogspot.com/2014/06/find-music-on-google-and-start-playing.html
http://blog.sgo.to/2014/09/listen-action-in-practice.html

<div itemscope itemtype="http://schema.org/MusicGroup">
  <meta itemprop="description" content="Lady Gaga, an artist on Spotify">
  <meta itemprop="url"
      content="http://open.spotify.com/artist/1HY2Jd0NmPuamShAr6KMms" />
  <div itemprop="potentialAction" itemscope
      itemtype="http://schema.org/ListenAction">
    <meta itemprop="target"
        content="http://open.spotify.com/artist/1HY2Jd0NmPuamShAr6KMms" />
  </div>
</div>

And this is what you get:


Maps


Here is what you write:

https://developers.google.com/search/docs/data-types/local-businesses
<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Restaurant",
  "@id": "http://davescafe.example.com/",
  "name": "Dave's Cafe",
  "image": "http://davescafe.example.com/image.jpg",
  "address" :{
    "@type": "PostalAddress",
    "streetAddress": "123 William St",
    "addressLocality": "New York",
    "addressRegion": "NY",
    "postalCode": "10038",
    "addressCountry": "US"
  },
  "geo":{
    "@type": "GeoCoordinates",
    "latitude": 40.709312,
    "longitude": -74.007136
  },
  "telephone": "+19172423826",
  "potentialAction": {
    "@type": "OrderAction",
    "target": {
      "@type": "EntryPoint",
      "urlTemplate": "https://www.example.com/daves-cafe-new-york",
      "inLanguage": "en-US",
      "actionPlatform": [
        "http://schema.org/DesktopWebPlatform",
        "http://schema.org/IOSPlatform",
        "http://schema.org/AndroidPlatform"
      ]
    },
    "deliveryMethod": [
      "http://purl.org/goodrelations/v1#DeliveryModePickUp",
      "http://purl.org/goodrelations/v1#DeliveryModeOwnFleet"
    ],
  }
}
</script>



And here is what you get:




Gmail

Here is what you write:

https://developers.google.com/gmail/actions/reference/rsvp-action

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Event",
  "name": "Taco Night",
  "startDate": "2015-04-18T15:30:00Z",
  "endDate": "2015-04-18T16:30:00Z",
  "location": {
    "@type": "Place",
    "address": {
      "@type": "PostalAddress",
      "name": "Google",
      "streetAddress": "24 Willie Mays Plaza",
      "addressLocality": "San Francisco",
      "addressRegion": "CA",
      "postalCode": "94107",
      "addressCountry": "USA"
    }
  },
  "potentialAction": {
    "@type": "RsvpAction",
    "target": "http://mysite.com/rsvp?eventId=123"
    "attendance-input": "required"
  }
}</script>

And here is what you get:




Here is one example of an implementer:

https://github.com/blog/1891-view-issue-pull-request-buttons-for-gmail


Google Search App Indexing

Here is what you write for servers:


<script type="application/ld+json">
{
  "@context": "http://schema.org", 
  "@type": "WebPage", 
  "@id": "http://example.com/gizmos", 
  "potentialAction": {
    "@type": "ViewAction", 
    "target": "android-app://com.example.android/http/example.com/gizmos"
  }
}
</script>

And what you'd write for native android clients:


    // Define a title for your current page, shown in autocompletion UI
    final String title = "App Indexing API Title";

    // Call the App Indexing API view method
    AppIndex.AppIndexApi.view(mClient, this, APP_URI, TITLE, WEB_URL, null);

And this is what you get:


(link to original image)

Google Search Social App Activities

Here is what you write:


<script type="application/ld+json">
{
  "type": "http://schema.org/ListenAction",
  "object": {
    "@type": "MusicRecording",
    "url": "https://developers.google.com/+/web/snippet/examples/song",
    "name": "When Johnny Comes Marching Home",
  }
}
</script>

And this what you get:


Yandex Islands

This what you write:

https://help.yandex.com/webmaster/interactive-answers/buttons-description.xml
https://github.com/bobuk/islands/blob/master/interactive-answers-eng.md

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "CheckInAction",
  "object": {
    "@type": "Flight"
  },
  "target" : "http://www.example.com/check_in"
}
</script>

And here is what you get:



Microsoft's App Linking

Here is what you write:

http://msdn.microsoft.com/en-us/library/dn614166.aspx

<span itemscope itemtype="http://schema.org/WebPage"> 
  <span itemprop="potentialAction" itemscope
      itemtype="http://schema.org/ViewAction">
    <span itemprop="target" itemscope
        itemtype="http://schema.org/WindowsActionHandler"> 
    </span>
    <span itemprop="target" itemscope
        itemtype="http://schema.org/WindowsPhoneActionHandler">
    </span>
  </span>
</span>

And this is what you get:




What's next?

Holograms :) JK :) But I promise it will be exciting, stay tuned!

In the meantime, you might want to read what I'm up to (and my quest to make APIs suck less) if you are bored and care about the geeky details :)

I'll try to keep this page updated as I find more usages. If you know of cool ways your company/partners/webmasters are using http://schema.org/Action, drop me a line in the comments below and I'll make sure to add them here!


Listen Action in practice

A step towards distributed affordances.

While I was reading about distributed affordances on my way to WWW2014, I kept saying to myself: oh goodness, if only I could talk about what I've been up to :) Well, I guess now that I got this out of the door I can :)

The basic idea about distributed affordances is simple: presenting to the user affordances from external sources in a serendipitous way.

For example, for a specific book, what's the infrastructured needed to assist users with external services where the book can be "bought", "borrowed" or "discovered"? That is, how do we enable this sort of experience (granted, not a pretty mock, it was in a phd thesis just for context:)):

Baby steps

One small step towards that goal was to solve the first part: describing affordances externally and aggregating them.

Today, if you search for your favorite artist on google, you get a set of links to external applications where you can listen to them.


How it works isn't that complicated: affordances exist out there, webmasters expose them, google crawls and assist users.

Take for example the affordances of this page about Katy Perry on rdio.com. There is a clear call to action to listen to her songs.

The tricky part was: machines couldn't read that.


Enter ListenAction

The delta needed was the ability to organize affordances as well as attach them to resources. With those mechanisms handy, developers/webmasters have the language to write:

<div itemscope itemtype="http://schema.org/MusicGroup">
  <meta itemprop="description" content="Lady Gaga, an artist on Spotify">
  <meta itemprop="url"
      content="http://open.spotify.com/artist/1HY2Jd0NmPuamShAr6KMms" />
  <div itemprop="potentialAction" itemscope
      itemtype="http://schema.org/ListenAction">
    <meta itemprop="target"
        content="http://open.spotify.com/artist/1HY2Jd0NmPuamShAr6KMms" />
  </div>
</div>

And that becomes machine readable to consumers of this page. Consumers know this page is about an artist which affords being listened to.

Come again?

OK, that was probably a bit complicated to follow if you are new to the subject. Here is a much simpler way to put it:


More actions?

Now sure, this isn't entirely serendipitous because I cheated on the second step: I hard code ListenAction.

Sure, I'll take that. One step at a time.

Here [1, 2] are some other folks who imagined the same world.


(image from http://webofdata.wordpress.com/2012/07/08/schema-org-webintents-awesomeness/)

What's next?

Not sure yet, but it will be exciting! Stay tuned!