[misc][OpenSocial] OpenSocial RPC Protocol Specification と shindig snapshot 1.1 の実装が不一致な件

やっぱりJavaScript側全部作り直さないとだめかなー。さすがにいらいらしてきた*1。ので、DQ9でもやってから続きの実装をしようかと。


Gadget XMLのコード。shindig の ifr で処理させるもの。

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Hello World!">
    <Require feature="opensocial-0.9" />
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
    <div id="#content"></div>
    <script type="text/javascript">
     var req = opensocial.newDataRequest();
      req.add(req.newFetchPersonRequest('VIEWER'), 'viewer');
      req.send(function(response) {
      alert(response.get('viewer').getData().getDisplayName());
      });
    </script>
    ]]>
  </Content>
</Module>

で、これをfirebugsで接続見ると、

[{"method":"people.get","params":{"userId":["@viewer"],"groupId":"@self","fields":["id","name","thumbnailUrl"
,"id","displayName"]},"id":"viewer"}]

JSON-RPCのバッチリクエストがいくわけさ。まぁそうだよね、ってことで、JSON-RPCの規格に従って、

[
  {
   "id" : "viewer",
   "result":{
      "name":"Jane Doe",// 
      "displayName":"Jone Doe",
      "gender":"female",
      "id":"example.org:34KJDCSKJN2HHF0DW20394"
   }
  }
]

で返すようにRackのconfig.ruを書くが、全然動かない。

ShindigJavaScriptを読みあさるの巻。

// features/src/main/javascript/features/opensocial-jsonrpc/jsonrpccontainer.js
// line 156
    var sendResponse = function(result) {

これが、makeNonProxiedRequest のコールバックハンドラになっている。JSONをパースした結果はresult.data に入っているはず。

// features/src/main/javascript/features/opensocial-jsonrpc/jsonrpccontainer.js
// line 162
      result = result.data;

OK。こういう書き方大嫌いだけど我慢する。

// features/src/main/javascript/features/opensocial-jsonrpc/jsonrpccontainer.js
// line 167
      // Map from indices to ids.
      for (var i = 0; i < result.length; i++) {
        result[result[i].id] = result[i];
      }

配列を勝手にオブジェクトにするなー!これはひどい、が、我慢我慢。
この時点で、result を整理すると

result[0] ->
  {
   "id" : "viewer",
   "result":{
      "name":"Jane Doe",
      "displayName":"Jone Doe",
      "gender":"female",
      "id":"example.org:34KJDCSKJN2HHF0DW20394"
   }
  }
result["viewer"] ->
  {
   "id" : "viewer",
   "result":{
      "name":"Jane Doe",
      "displayName":"Jone Doe",
      "gender":"female",
      "id":"example.org:34KJDCSKJN2HHF0DW20394"
   }
  }

ってことか。意味がわからん。続いて、JSON-RPCのレスポンスオブジェクトを作る。

// features/src/main/javascript/features/opensocial-jsonrpc/jsonrpccontainer.js
// line 172
      for (var k = 0; k < requestObjects.length; k++) {
        var request = requestObjects[k];
        var response = result[k];

        if (request.key && response.id !== request.key) {
          throw "Request key(" + request.key +
              ") and response id(" + response.id + ") do not match";
        }

リクエストに対応するresultを取るってことですね。なんでrequest.idではなくてrequest.keyなのかは謎ですがね。

しかし、これは罠が。

        var rawData = response.data;
        var error = response.error;

いや、おい、response.data って。undefined なんですが。

JSON-RPC にも、ShindigJSON-RPCにもそんなこと書いてなくない?と思ってSPEC確かめる。

POST /rpc HTTP/1.1
Host: api.example.org
Authorization:
Content-Type: application/json
[
{
"method" : "people.get",
"id" : "myself"
},
{
"method" : "people.get",
"id" : "myfriends"
"params: {
"groupId" : "@friends"
}
}
]
HTTP/1.x 207 Multi-Status
Content-Type: application/json
[
{
"id" : "myself",
"result" : {
"id" : "example.org:34KJDCSKJN2HHF0DW20394",
"name" : { "unstructured" : "Jane Doe"},
"gender" : "female"
}
},
{
"id" : "myfriends"
"error" : {
"code" : 401
}
}
]
|

http://www.opensocial.org/Technical-Resources/opensocial-spec-v09/RPC-Protocol.html#personEx

result って書いているじゃん。0.8 のほうも同じ。困ったもんだ。仕方がないので、とりあえず、仕様よりも実装を優先して、

[
  {
   "id" : "viewer",
   "data":{
      "name":"Jane Doe",// 
      "displayName":"Jone Doe",
      "gender":"female",
      "id":"example.org:34KJDCSKJN2HHF0DW20394"
   }
  }
]

って返すようにしました。そしたら、最初に書いたgadget xml は動きました。

正解の config.ru を書いておくと、

# config.ru
require 'rubygems'
require 'json'

run proc{|env|
  obj = [
       {
         "id" => "viewer",
         "data" => {
           "id" => "example.org:34KJDCSKJN2HHF0DW20394",
           "name" => "Jane Doe",
           "displayName" => "Jone Doe",
           "gender" => "female"
         }
       }
      ]

  [200,
   {'Content-Type'=>'text/plain'},
   obj.to_json]
}

で、あとは、obj = のところを サーバーサイドのJSON-RPCインプリとして実装してあげれば良さそうです。

ってか、Shindig、リファレンスインプリのくせして、ソースが読みにくいように感じるのは自分だけなのかなー。

*1:しかし、snapshotなんだから文句いうなよ!というのはごもっともですねw