basho / riak_api

Riak Client APIs
27 stars 46 forks source link

Webmachine URL Map produces malformed JSON when listing resources. [JIRA: RIAK-2376] #102

Open pyrrho opened 8 years ago

pyrrho commented 8 years ago

tl;dr -- Running curl -XGET http://localhost:10018/ -H "Accept: application/json" returns malformed JSON.

Call Stack: https://github.com/basho/riak_api/blob/develop/src/riak_api_wm_urlmap.erl#L74-L75 https://github.com/basho/mochiweb/blob/master/src/mochijson.erl#L53-L56 https://github.com/basho/mochiweb/blob/master/src/mochijson.erl#L121-L122 https://github.com/basho/mochiweb/blob/master/src/mochijson.erl#L137-L153

I believe the Services variable in mochijson:encode({struct, Services}) should be transformed to aggregate URIs into tuples per Resource ({Resource, {Uri1, Uri2, ...} },) before being encoded.

Longer Version:

Manually prettified, the output I see running curl -XGET http://localhost:10018/ -H "Accept: application/json" on a 2.1.3 devrel is,

{
    "riak_kv_wm_bucket_type":"/types",
    "riak_kv_wm_buckets":"/buckets",
    "riak_kv_wm_buckets":"/riak",
    "riak_kv_wm_buckets":"/types",
    "riak_kv_wm_counter":"/buckets",
    "riak_kv_wm_crdt":"/types",
    "riak_kv_wm_index":"/buckets",
    "riak_kv_wm_index":"/types",
    "riak_kv_wm_keylist":"/buckets",
    "riak_kv_wm_keylist":"/types",
    "riak_kv_wm_link_walker":"/buckets",
    "riak_kv_wm_link_walker":"/riak",
    "riak_kv_wm_link_walker":"/types",
    "riak_kv_wm_mapred":"/mapred",
    "riak_kv_wm_object":"/buckets",
    "riak_kv_wm_object":"/riak",
    "riak_kv_wm_object":"/types",
    "riak_kv_wm_ping":"/ping",
    "riak_kv_wm_preflist":"/buckets",
    "riak_kv_wm_preflist":"/types",
    "riak_kv_wm_props":"/buckets",
    "riak_kv_wm_props":"/types",
    "riak_kv_wm_stats":"/stats",
    "riak_repl_wm_stats":"/riak-repl"
}

Note the multiple instances of e.g. "riak_kv_wm_buckets":. . . and "riak_kv_wm_object":. . .. In the case of duplicate names in JSON objects, the value associated with the last instance of a given name will be returned. As such, pushing the above through a JSON interpreter yields,

$ curl -XGET http://localhost:10018/ -H "Accept: application/json" | json_pp
{
   "riak_kv_wm_crdt" : "/types",
   "riak_kv_wm_link_walker" : "/types",
   "riak_kv_wm_preflist" : "/types",
   "riak_kv_wm_ping" : "/ping",
   "riak_kv_wm_index" : "/types",
   "riak_kv_wm_props" : "/types",
   "riak_kv_wm_object" : "/types",
   "riak_kv_wm_stats" : "/stats",
   "riak_kv_wm_counter" : "/buckets",
   "riak_repl_wm_stats" : "/riak-repl",
   "riak_kv_wm_buckets" : "/types",
   "riak_kv_wm_keylist" : "/types",
   "riak_kv_wm_bucket_type" : "/types",
   "riak_kv_wm_mapred" : "/mapred"
}

Rather than collapsing values down to a single string, we should very likely return a lists (e.g. "riak_kv_wm_buckets": ["/buckets", "/riak", "/types"],).

It should be well noted that systems that consume JSON output of the resources lists will need to be modified as well. For example This issue was found when working through the Python Client code, comparing versions 2.0.2 and 2.4.0. Specifically,

In the former case, the client is expecting a single specific string to be returned -- in this case, "/buckets". In the later, the fact that the single specific string was suddenly "/types" was essentially ignored. It would be reasonably easy to update these calls to look for a certain element in returned lists, and act according to their existence/absence.