migrating Dropbox API v1 to v2

去年からさんざんアナウンスされつつも、夏休みの宿題のノリでまあ切羽詰まったらやればいいだろう…と思っていた、Dropbox の API が v1 から v2 に移行するというトピックにそろそろ対応したい。

移行のガイドとしてはこんな文書がある。そのとおりに進めればいいはずだ。たぶんきっと。

幸いなことに、wasaviがDropboxを始めとしたオンラインストレージとやり取りする際に使用するAPIはそれほど多くはない。

  • OAuth2で認証する
  • 任意のディレクトリの直下のファイル・ディレクトリを列挙する
  • 任意のパスからテキストを読み込む
  • 任意のパスへテキストを書き込む

てな感じなので順番に片付けていこう。まず認証に関しては単にエンドポイントが変わるだけなのでこれには問題はない。そして残りは全部面倒くさい。

ディレクトリの列挙。v1では、任意のディレクトリに対して[cci]metadata[/cci]APIをlistオプション付きで呼ぶと、そのディレクトリ自身のメタデータと併せて、一緒に直接の子供のメタデータのリストも得ることができた。ガイドによればこのAPIのv2における代替は[cci]files/get_metadata[/cci]なのだが。しかしlistオプション付きの場合はそれではまったく代替になり得なく、複数のAPIを組み合わせて使わなければならない。困ったことにこの辺はガイドにはさっぱり解説されていない。というわけでv1のlistオプション付きmetadata APIの代替になる処理を自前で書かざるを得ない。

まず任意のディレクトリ自身のメタデータを読み込むために、[cci]files/get_metadata[/cci]を呼ぶ。それが完了したら(ここは別に並列でもいいが)[cci]files/list_folder[/cci]を呼び子供のメタデータを得る。また、このAPIは一度に返される結果セットのサイズが決まっているようで、その呼び出しが結果セット全体の最後かどうかは不定だ。そのかわり呼び出しのたび最後の結果かを示すフラグ has_more を返すので、つまりこの後呼び出し側が責任を持って[cci]files/list_folder/continue[/cci]を複数回呼び出す必要がある。それから列挙対象のパスなどを与える方法が若干面倒でContent-Typeを[cci]application/x-www-form-urlencoded[/cci]ではなく[cci]application/json[/cci]にした上でJSONをPOSTしなければいけない。

なんだこれは。バージョンが進んだのにクライアント側でやらないといけない仕事が格段に増えている。ここまでの遷移を見てみると、個々のAPIができることがより制限され、かつその代替がよく解説されないという状況にしか見えない。どうにもいまいちだと言わざるを得ない。

ファイルの読み込み。[cci]files/download[/cci] を呼ぶのだが、読み込み対象のパスなどを与える方法が若干面倒。POST のボディではなく、[cci]Dropbox-API-Arg[/cci]なるヘッダに、JSONを文字列化したものを与える必要がある。それができない場合は、クエリ文字列として[cci]?arg=[/cci]てな感じで与える必要がある。XHRの場合何でも好き勝手なヘッダを追加することはできないのでクエリ文字列を使用することになる。で、読み込み結果はレスポンスボディとして帰ってくるのだが、メタデータは[cci]Dropbox-API-Result[/cci]なるヘッダにこれもやはりJSON形式で帰ってくる。

ファイルの書き込みにおける変更点は読み込みと同じ。v1では書き込みの際はPUTメソッドを使用した。これはこれで筋が通っている仕様だと思うけど、v2ではPOSTに変更されている。

というわけでAPIの使い勝手としてはv1のほうが勝ってたんじゃないかなあ…と思わなくもない。

それから、v1では多くのパラメータに汎用的に[cci]locale[/cci]を付加することができたがv2ではHTTPのヘッダ(Dropbox-Locale)に含めるように変更になったとか、全てのアクセスはPOSTメソッドでないと「いけない」(GETも受け付けるとかではない)などの細かな変更もある。メソッドはともかく、ロケールは難しい。前述の通りXHRでは任意のリクエストヘッダを送出できるわけではないからだ。X-Dropbox-Locale とかなら送れるんだけどなー。まあロケールは主にエラーメッセージとかに影響するはずなのでそれほど困るわけではないのだけど。

When I overwrite a directory as a file…

wasavi を起動し、dropbox や gdrive にすでにディレクトリとして保存されているパスに対して :write した場合、エラーになってもらわないと困る。もしも保存したパスがディレクトリで、なおかつそのディレクトリの下に多数のファイルが保持されている時、有無を言わさずそれらがすべて消去され指定のパスが同名の単なるファイルにすり替えられるとしたら、これは絶対に避けなければならないケースだ。

google drive の場合、ファイルを上書きするには先んじてそのファイルの ID を得なければならない。これはこれで面倒な作業ではあるけれど、しかしその過程でファイルの mime タイプが application/vnd.google-apps.folder かどうかを判断するチャンスがアプリケーション側に与えられるので問題ない(それを一つのトランザクションとして見なした場合、そんなゆるゆるなチェック体制でいいのか? とは思うけど)。

dropbox の場合、ファイルを更新するにはそのパスと内容を同時にアップロードするだけだ。dropbox で上記のクリティカルなケースがどうなるか試してみたところ、どうもサブディレクトリの下にファイルがある場合はエラーになり、完全に空のディレクトリの場合は単なるファイルへの上書きが成功するようだ。

それはそれで賢い振る舞いなのかもしれない。また、この辺の振る舞いは別に wasavi 側で規定できるわけでもない。

ということで、そういう動きを受け入れることにする。