まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:api

Google Fit は Google のヘルスケア関連プラットフォームと、その SDK を提供しています:
https://developers.google.com/fit/

2020062600


Google Fit SDK は Android 標準アプリである Google Fit を操作するものです。簡単に言えば「Android の歩数カウント情報を取得する」ための SDK といえます。

この SDK の1つがブラウザ用の JavaScript で提供されており、ウェブアプリケーションに組み込んで使うことが可能です。データを取得するまでを実際に作ってみたので、その準備手順を含めてまとめてみました。


【Google APIs Console へのプロジェクト登録】
この Google Fit SDK を使う上で最初に必要な作業は Google APIs Console へプロジェクトを登録して Client ID を取得することです(SDK は Client ID を指定して動かすことになります)。
https://console.developers.google.com/flows/enableapi?apiid=fitness&pli=1

Fitness API を使うことになるアプリケーションプロジェクトとして、既存のプロジェクトを流用するか新たにプロジェクトを作成します。そして「使用する API」は「Fitness API」を選択し、「使用する場所」は「ウェブサーバー」を選択、更に「アクセスするデータの種類」は「ユーザーデータ」を選択します。最後に「必要な認証情報」をクリックして、クライアント ID を取得します:
2020062601


次の画面で OAuth 認証用のドメイン登録をします。「承認済みの JavaScript 生成元」と「承認済みのリダイレクト URI」に以下のアプリを動かす URI を指定します。ローカルで動作確認する場合であれば両方に "http://localhost:8080" を追加して保存します※:
2020062602

※以下で紹介するサンプルを https://localhost:8080/ という URL にアクセスして参照する場合の設定です。異なるホスト名やポート番号を使う場合は適宜変更してください。

これで Google APIs Console 側の準備は完了です。



【サンプルアプリケーションで動作確認】
動作確認用の Node.js 向けサンプルアプリケーションを以下に公開しておきました。Node.js がインストール済みの環境であれば実際に動かして動作を確認することが可能です:
https://github.com/dotnsf/google_fit


zip をダウンロードして展開するか、git clone して、自分の PC 内にプロジェクトファイルを展開します。実質的に public/index.html ファイルを 8080 番ポートで公開するだけの Node.js アプリケーションです。

実際に動作させる前に、public/index.html ファイルをテキストエディタで開き、8行目の client_id 変数の文字列値(初期値は 'your_client_id' )を上記で取得したクライアント ID の文字列値に書き換えて保存します:
2020062609


これでサンプルアプリケーションを動作させる準備ができました。以下のコマンドを実行してアプリケーションを稼働状態にします:
$ cd google_fit

$ npm install

$ node app


稼働状態になったアプリケーションは 8080 番ポートでリクエストを待ち受けています。ウェブブラウザで http://localhost:8080/ にアクセスしてみます。正しく動作していると最初に OAuth 認証が実行されるので、Android スマホで利用しているものと同じ Google アカウントでログインします:
2020062604


Google アカウントを選択してログインし、正しく処理が実行されると(このコードでは)2019/03/01 以降の歩数記録を取得します。何種類かの dataSource ごとの記録が表示されるはずです:
2020062608


どれか1つの dataSource をクリックすると、その dataSource に関して更に詳しく日時やその時の歩数が表示されます:
2020062609


↑自分は Android をメインスマホにしていないため、歩数が少ない・・・



実際に使っているコードは OAuth 認証のための authorize 関数と、そのコールバック先で実行している dataSource 一覧のフェッチ、そして各 dataSource ごとの歩数カウントのためのデータセットのフェッチです。JavaScript 自体はかなりシンプルになっていると感じました。



(参考)
https://qiita.com/feb19/items/383848119ba1bdfe5110


マンホール蓋の位置情報付きポータルサイトであるマンホールマップは、Twitter の OAuth ログインによってユーザー認証を行い、ログイン後にマンホール画像を投稿できる機能が有効になります。が、ログインしなくても投稿済み情報を参照できるようになっています。


この参照できる情報には投稿されたマンホール画像やその位置情報(地図)だけでなく、投稿者の情報も含まれています。投稿者の名前だけでなく、Twitter アイコン(プロフィール画像)も表示されます:
2020060401



【起こった問題】
正確にはどのタイミングからだったのかわからないのですが、先日からマンホールマップにこの Twitter アイコンが表示されなくなる、という現象が起こっていました。


マンホールマップ側に変更がない状況で表示されていたものが表示されなくなった。ということは Twitter で何かあった?一時的な障害であることを祈っていたのですが1日経過しても状況は変わりませんでした。もしかして Twitter API が変更になったのかなあ、、という可能性も考えたのですが、明確なアナウンス類を見つけることができませんでした。ただ 2019 年に発表されたこの変更予告を見逃していて、もしかしたら今年の5月末くらいに実装からも外れて・・・みたいな感じで関係あったのかなあ(未確認)・・・という感じ:
https://twittercommunity.com/t/upcoming-changes-to-user-object-and-get-users-suggestions-endpoints/124732/2

2020060404



これまでのマンホールマップでの Twitter アイコン取得方法と同じ方法(後述)を解説していたこちらのページの中でも画像が表示されているべき(と思われる)箇所の画像が表示されていません。つまりマンホールマップにだけ発生している現象というわけではなく、このページでも自分と同じ現象が起こっているように感じました:
Twitter APIで取得したプロフィール画像のサイズを変更する


(↓この部分、本来は画像が表示されている??)
2020060403



ともあれ詳しい状況はわからないままですが、表示されなくなった Twitter アイコンを復活させるべく行った緊急対応の顛末を記録しておきます。結論として現時点(2020/06/04)で応急処置的にアイコンが表示されるよう復活しています。



【これまでの Twitter アイコン取得方法】
もう公式ドキュメントには記載されておらず、どこにこの方法について言及したドキュメントがあったのかも覚えてないのですが、これまでは以下のように HTTP API を2回使って Twitter アイコン画像の URL を取得していました:

(1)GET https://twitter.com/screen_name/profile_image にアクセス(screen_name 部はアイコンを表示したい人の Twitter アカウントの表示名※)
(2)(1)の結果得られる JSON オブジェクトから request.path の値を取り出す( "/12345/67890.jpg" みたいな値が取り出される)。
(3)HTML で <img src="https://pbs.twitter.com(request.path の値)"/> などのようにするとアイコン画像が表示できる


※Twitter アカウントの表示名とは、アカウントのページを開いた時のこの部分(赤枠)のことです:
2020060402



これまでのマンホールマップではこの方法で投稿者の Twitter アイコン画像を表示していました。上述の「ここでも自分と同じ現象が起こっている・・・」と書いたリンク先でも同じ手法が紹介されていますが、マンホールマップ同様に表示できなくなっているようでした。

調査した結果を記載しておくと、上述の方法で(1)と(2)は結果を得ることができているのですが、(3)で指定する URL にブラウザでアクセスすると 404 エラー(存在しない URL)となりました:
2020060402


ただ、では(1)と(2)が成功しているのかというと、どうも怪しいと思っています。(1)を実行した結果得られる(2)の値は以前は実行するまで推測できないパス名だったのですが、いま実行すると '/screen_name/profile_image' という結果となりました(つまり(1)を実行した時のパスがそのまま返ってくる)。この時点で以前のアイコンが正しく表示出来ていた頃とは結果が異なっているので、もしかすると(1)が以前のように動かなくなっていることが、アイコンが表示されない直接の原因なのかもしれない、、と推測しています。


いずれにしても、この API はもう使えなくなっちゃったのかな・・・ 結論として後述の応急手当的な対応はできたのですが、正直、今でもこの方法に戻せるなら戻して使いたいと思っています。理由も含めて後述します。


【現時点での正攻法(Twitter API を使う方法)】
上で紹介した方法以外に、(ちゃんと)Twitter API ドキュメントで紹介されている方法を使ってアイコン画像を取得する(正攻法的な)方法もあります。逆にいうと上述の方法は現在の Twitter API ドキュメントを探しても見つからない、ある種の「裏ワザ」的な方法だったりします(苦笑)。

正攻法というのは、具体的には GET /users/show API を使う方法です:
https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-users-show

2020060403


この API を実行する際のパラメータでアイコンを取得するユーザーの screen_name を指定します。すると指定したユーザーの情報を得ることができ、その中にはプロフィール画像(アイコン)の URL が含まれているので、その値を使うことでアイコン画像を表示することができる、というものです。


【正攻法(Twitter API を使う方法)では駄目な理由】
しかしこの方法はマンホールマップでの実装には向かない大きな理由があります。それはこの API はアクセストークンを使って実行する必要があるのですが、ユーザーがログインしていないとアクセストークンが取得できないのです。つまりユーザーがログインする前や、そもそもログインしないユーザーからするとアクセストークンを取得するタイミングがないので、この API を使うことが出来ず、アイコンを表示できないのでした。

マンホールマップは上述のようにログインしなくても参照できることを謳ってきていた背景もあり、Twitter 側に仕様変更があったとしても、急にマンホールマップにもログインを必須にすることに抵抗を感じてしまいました。。 そんな背景もあり、この正攻法での方法は採用していません。


【応急処置による現在の Twitter アイコン取得方法】
で、結論としてはこの方法を採用しているのですが、では改めてユーザーのログインなしにどうやって Twitter アイコンを取得しているか、を紹介します。あくまで応急手当的な内容であり、実行効率はよくない(のでできれば早く修正したい)ことを自覚している方法であることをご了承ください。

実現方法を簡単に言うと「Twitter にログイン済みの別プロセスを使って API を実行する」方法です。以下でもう少し詳しく紹介しますが、その前に twurl というコマンドラインツールを紹介します。
https://github.com/twitter/twurl

2020060404



twurl (読み方わからず・・)は Twitter 謹製の Twitter API 専用 curl といった位置づけのオープンソース製品です。各種 Twitter API を必要であればパラメータを付けてコマンドラインから実行し、その結果を標準出力に取り出すことができます。このツールも実行前に OAuth によるログインが必要ですが、ログインを1度実行しておけば、そのあとの API 実行は続けて行うことができます。標準の curl でも Twitter API を実行することは可能ですが、パラメータとして与える署名の計算が毎回面倒だったりするわけです。一方 twurl を使うとそのあたりは裏でバッチリやってくれるらしく、必要最小限のパラメータだけを渡して実行することができる、というものです。

例えば上述した正攻法による API で紹介した GET /users/show API を使ってユーザー情報を取得する場合であれば、セットアップ後にこういったコマンドを実行します(curl と同様ですが、"-X GET" は既定値なので省略可能です):
$ twurl -X GET /1.1/users/show.json?screen_name=dotnsf

これで screen_name が dotnsf(僕のアカウントです)であるユーザーの情報を取得することができます。結果は JSON で得ることができますが、この中の "profile_image_url_https" キーの値がそのユーザーの Twitter アイコン画像を表示するための URL 、ということになります:
$ twurl -X GET /1.1/users/show.json?screen_name=dotnsf
{
  "id": XXXX....,
  "id_str": "XXXX....",
    :
  "profile_image_url_https": "https://pbs.twitter.com/AAAAAAAA/BBBBB.jpg",
    :
}

この値が目的の URL 文字列です。したがって twurl コマンドを使うと目的の文字列を取得することができることがわかりました。


で、今回の API 代替方法というのは、この twurl を外部プロセスで実行して対象 Twitter ユーザーのアイコン画像の URL を取り出し、この URL を使ってアイコンを表示する、という方法です。外部プロセスを使うのがちとダサいのですが、一方でこの方法なら Node.js 実行前に twurl のセットアップを済ませておけば、Twitter API 実行のためのウェブアプリケーション側でのログインは不要となるので、参照目的のユーザーであっても(ログインしなくても)アイコンが表示される、ということが実現できるようになります。

マンホールマップのソースコードは現時点においては非公開ですが、この該当部分だけを別アプリケーションにして github で公開しています:
https://github.com/dotnsf/twurl


このアプリケーションは上述の twurl を別プロセスで動かして目的の URL を取得する、という内容を Node.js で実現するサンプルです。おそらく他のプログラミング言語であっても(外部コマンドを実行して、その結果を取得する機能があれば)実現できると思っています。

サンプルアプリの使い方ですが、まずアプリケーションを実行する前に twurl をセットアップする必要があります。詳しくは twurl の github 内にも記載されていますが、一応こちらでも(日本語で)以下に紹介します。

まず twurl は Ruby Gem を使ってインストールします。そのため Ruby(& Gem) 環境が必要です。Ruby がインストールされていない環境の場合はインストールしておきましょう。 Ubuntu 環境下であればこんな感じで:
$ sudo apt-get install ruby

Ruby Gem が導入されている環境で、以下のコマンドを実行すると twurl が導入されます:
$ sudo gem install twurl

これで(Ubuntu であれば /usr/local/bin/twurl として)twurl がインストールされました。次に twurl のセットアップが必要です。Twitter Developers にアプリを登録し、Consumer Key と Consumer Secret を取得した上で以下のコマンドを実行します:
$ twurl authorize --consumer-key (Consumer Key) --consumer-secret (Consumer Secret)

このコマンドを実行すると「この URL を開け」という指示が表示されます。その URL をブラウザで開くと認証用の文字列が表示され、それと同じ内容を実行中の twurl コマンドの入力待ちプロンプトに打ち込んで Enter キーを押します。正しい文字列が指定されていれば認証が完了し、twurl コマンドが使えるようになります。試しに上述の GET /users/show コマンドを実行するなどしてみてください:
$ twurl /users/show?screen_name=dotnsf
(screen_name が dotnsf であるユーザーのプロフィール情報が表示されます)

これで twurl を実行する準備が整いました。あとは Node.js のアプリケーションからこのコマンドを外部呼び出しで実行し、その結果を取得・解析してプロフィール画像の URL へリダイレクトさせることで目的の機能が実現できそうです。


twurl のセットアップができた所で、改めてこのサンプルアプリケーションを動かしてみます:
https://github.com/dotnsf/twurl


このサンプルアプリケーションの app.js ファイルは以下のようになっています:
//.  app.js
var express = require( 'express' ),
    app = express();
var { execSync } = require( 'child_process' );
var settings = require( './settings' );

app.get( '/', function( req, res ){
  var screen_name = req.query.screen_name;
  if( screen_name ){
    try{
      var stdout = execSync( settings.twurl_command + ' /1.1/users/show.json?screen_name=' + screen_name );
      var obj = JSON.parse( stdout.toString() );
      var profile_image_url_https = obj.profile_image_url_https;
      res.redirect( profile_image_url_https );
    }catch( e ){
      return res.status( 403 ).send( { status: false, error: e } );
    }
  }else{
    return res.status( 403 ).send( { status: false, error: 'No screen_name provided.' } );
  }
});

var port = process.env.PORT || 8080;
app.listen( port );
console.log( "server starting on " + port + " ..." );

そして別ファイルである settings.js は以下のような設定にしています:
//. settings.js
exports.twurl_command = '/usr/local/bin/twurl';


簡単に解説しておくと、GET / をハンドルする API だけが定義されています。そしてこの API では screen_name というパラメータを受け取って、
$ /usr/local/bin/twurl /users/show.json?screen_name=(パラメータの値)

を同期的に実行し(twurl のパスは settings.js 内に記載)、実行結果が stdout 変数に格納されます。そして格納された結果を JSON パースした上で "profile_image_url_https" の値を取り出して、その URL にリダイレクトする、という処理を行っています。 これによってパラメータで指定した screen_name を持つ Twitter ユーザーのプロフィールアイコン画像を表示する、という処理を実現しています。

実際にサンプルアプリケーションを動かして、ブラウザで http://localhost:8080/?screen_name=dotnsf を実行した結果がこちらです:
2020060401


http://localhost:8080/?screen_name=dotnsf にアクセスすると、twurl の実行結果に含まれる URL に転送され、screen_name パラメータで指定した Twitter ユーザー(この場合であれば @dotnsf)のプロフィールアイコン画像が表示できました。twurl のセットアップ時に consumer_key と consumer_secret を指定してはいますが、そのあとのアプリケーション実行時には Twitter の OAuth ログインなしに Twitter API を実行して結果を取得することができています。

実用上においては、Twitter アイコンを表示するたびに twurl を使うこの一連の処理を実行するのは重すぎるので、一度取得した結果をキャッシュしておくなどの対処が必要になると思っています。ただこのやりかたで一応、挙動としては最低限の目的を達成することができました。

というわけで、無事にマンホールマップで投稿者の Twitter アイコンは応急処置的に復活できています:
2020060405


比較的新しい Web API の1つである MediaDevices インターフェースを使って、HTML と JavaScript だけで(PCの)画面共有が実現できるようになりました。とりあえず使ってみるには PC のウェブブラウザ※で以下のサイトにアクセスしてみてください:
https://dotnsf.github.io/display_media_stream/

※対応ブラウザはこちら: 
https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia#Browser_compatibility



対応ブラウザで上記 URL にアクセスすると、画面共有のダイアログが表示されます:
2020033101


何かひとつ(下図の例では「アプリケーションウィンドウ」タブの「タスクマネージャー」)選択して、「共有」ボタンをクリックすると、、
2020033102


選択したアプリケーションのウィンドウ内画面がストリームでウェブブラウザに表示されます。最初に「全画面」を選んでいればウィンドウ全体がブラウザ内に表示されます:
2020033103


この機能を停止するには共有ダイアログの「共有を停止」ボタンをクリックしてください:
2020033104


以下、上記デモのソースコードを紹介します。コードそのものはこちらで公開しています:
https://github.com/dotnsf/display_media_stream


上記デモは実質的に index.html ファイルだけで実現しています。つまり HTML と JavaScript だけで実現しています。まず HTML 部(<body>部)は以下の通り、かなりシンプルです:
<body>
  <!-- Video element (live stream) -->
  <div>
    <video autoplay playsinline id="video" width="640" height="480"></video>
  </div>
</body>

<div> 要素の中に <video> 要素が1つだけ、autoplay 属性と playsinline 属性がついた状態で存在しています。一応初期サイズの指定もしていますが後で JavaScript で修正します。

次に肝となる JavaScript 部はこちらです:
<script>
var localVideo = null;

function gotLocalMediaStream( mediaStream ){
  localVideo.srcObject = mediaStream;
}

function handleLocalMediaStreamError( error ){
  console.log( "navigator.getUserMedia error: ", error );
}


$(function(){
  //. 画面サイズ取得
  var sw = window.parent.screen.width;
  var sh = window.parent.screen.height;
  //sw : sh = x : 480; => x = 480 * sw / sh;
  var x = Math.floor( 480 * sw / sh );
  $('#video').css( { width: x } );

  var mediaStreamConstraints = { video: true };
  localVideo = document.querySelector( "video" );
  navigator.mediaDevices.getDisplayMedia( mediaStreamConstraints ).then( gotLocalMediaStream ).catch( handleLocalMediaStreamError );
});
</script>

実質的には $(function(){ ... }); 部分が最初に実行されます。まずは window.parent.screen にアクセスして実画面のサイズを取得し、その縦横割合に合わせて上述の video 要素をリサイズ(縦は 480 に固定して、横を同割合になるようリサイズ)します。

そしてこの画面共有を実現しているのはこの1行です:
  navigator.mediaDevices.getDisplayMedia( mediaStreamConstraints ).then( gotLocalMediaStream ).catch( handleLocalMediaStreamError );

mediaDevices インターフェースの getDisplayMedia メソッドを、{ video: true } というオブジェクトを引数に実行しています。これがカメラやマイクではなくディスプレイ画面のストリームを取得するための処理で、成功すると gotLocalMediaStream 関数がコールバックされます。

その gotLocalMediaStream 関数は以下のような内容になっています:
<script>
var localVideo = null;

function gotLocalMediaStream( mediaStream ){
  localVideo.srcObject = mediaStream;
}

getDisplayMedia メソッドで取得したメディアストリームを引数にコールバックされ、その値を video 要素として取得済みの localVideo 変数の srcObject 属性に代入しています。これだけで後はディスプレイ画面の動画ストリームが video 要素の中で自動再生(autoplay)されます。


mediaDevices インターフェースや getDisplayMedia メソッドがまだ限られたブラウザ(Chrome, FireFox, Edge)の比較的新しいバージョンでしか使えず、スマホ系ブラウザでは全滅という状況ではあるのですが、ネイティブアプリを使わなくてもブラウザの JavaScript だけでここまでできるようになっていたんですね。video 要素として使えるということはスクリーンショットとかいろいろ応用できそう・・・


Swagger を使うことで、インタラクティブに実行可能な REST API のドキュメントを作ることができます。比較的有名な所では Swagger がライブデモとして用意している「ペットストア・サービスの REST API」があります:
https://petstore.swagger.io/

2020012201



↑架空の「ペットストア・ウェブサービス」が存在している前提で、ペットやその画像を登録/更新したり、検索したり、、加えてペットストア自体の登録/管理をしたり、利用者となるユーザーの管理といったペットストア運営に必要と思われる最低限の機能の REST API が実行可能な状態で公開されています。


アプリケーションを REST API ベースで作る際に、このような Swagger による API ドキュメントを用意しておくと、REST API の仕様が確認できるだけでなく、実際にパラメータを指定した上で実行したり、実行した時のデータのやりとりやその結果までを確認することができて便利です。REST API を作成した時にセットで作っておくと、開発後の運用時や機能追加、引き継ぎといった段階においても非常に役立つインタラクティブドキュメントだと思っています。


この Swagger ドキュメントは REST API 開発後に swagger.yaml (または swagger.json)というファイルを用意することで自動作成が可能になります。特定のルールに基づいて REST API の内容(ホスト、パス、メソッド、パラメータ、返り値、・・)を記述していくと、ドキュメントの UI は自動的に作成され、指定した通りに実行が可能になります(そして実際に実行された結果を確認することもできるようになります)。

なお上述のペットストア・サービス REST API の定義内容はこちらの Swagger Editor で確認できます:
https://editor.swagger.io/

2020012205


swagger.yaml の記述方法そのものについては本ブログエントリの範囲ではないので、興味ある方は別途調べていただきたいのですが、そこまで難しい内容ではないと感じています。例えば GET /users?limit=xx&offset=yy といった具合に2つの URL パラメータ(limit, offset)を指定可能(必須ではない)な GET の REST API が存在していて、これを Swagger ドキュメントに記述する場合は swagger.yaml ファイルに以下のように指定します:
  :
/users
  get
    parameters:
      - name: limit
        type: number
        in: query
        description: 取得するデータ数
      - name: offset
        type: number
        in: query
        description: 取得するデータのオフセット
  :

実際に Swagger ドキュメントとして生成すると、この部分は以下のような UI に変換されて指定可能になる、といった具合です:
2020012202



で、ここからが本エントリの本題です。この Swagger ドキュメント(の yaml ファイル)を記述する際の、特に「ファイルアップロード」の API を記述する場合のパラメータ指定方法が特殊でちとわかりにくいものでした。試行錯誤した上で実際に動く例を見つけたので、自分の備忘録も兼ねて以下に紹介します。

まず API そのものは単純なファイルアップロード(POST /file)と仮定します。受け取るパラメータはアップロードするファイル本体だけ、他のパラメータは(Swagger ドキュメント的には特別な部分ではないので)いったん無視します。Node.js + Express 環境であれば、以下のような内容で作られているようなものです:
//. multer モジュールを利用してアップロードファイルを受け取る
var express = require( 'express' ),
    multer = require( 'multer' ),
    router = express.Router(),

    :

//. multer モジュールの設定
router.use( multer( { dest: './tmp/' } ).single( 'file' ) );

    :

//. POST /file として REST API を用意する
router.post( '/file', function( req, res ){
  //. アップロードされたファイルの情報を取り出す
  var filepath = req.file.path;
  var filetype = req.file.mimetype;

    :
    :


UI 側からこの API を呼ぶ時は、普通に以下の様な enctype="multipart/form-data" を指定した form の HTML を用意すれば実行できるものです:
<form method="POST" action="/file" enctype="multipart/form-data">
<input type="file" name="file"/>
</form>

↓こんな感じの見た目になって実行されるものです:
2020012203

このような挙動を行う API : POST /file を Swagger ドキュメントとして用意する場合の(パラメータ部分の)記述方法をどのようにするか、というのが今回のテーマでした。

ちょっと特殊な指定方法となりますが、結論としては以下のようになります:
  :
/file:
  post:
    consumes:
      - multipart/form-data
    parameters:
      - in: formData
        name: file
        type: file
        description: アップロードするファイル
        required: true
  :

特殊な部分を解説します。まず HTML の form 属性で enctype="multipart/form-data" 指定を行っていた部分ですが、上述のように consumes: で "multipart/form-data" を指定することで実現します。

またパラメータ部分ですが、POST のデータとして送信するので "in: formData" を指定します。またパラメータの種類として "type: file" も付与します。 加えて、このパラメータは API 実行時には必須指定パラメータとなるので "required: true" も付与します。swagger.yaml ではこのように指定することでファイルアップロード系の API を記述できます。

参考程度に、このように指定した swatter.yaml を実際に Swagger ドキュメントに変換して表示すると、以下のような画面になりました:
2020012204


期待通りの結果になりました。

業務やプライベートでウェブアプリを作る際に colorbox を使う機会が少なからずあります。jQuery に対応したモーダルダイアログの軽量プラグインで、メイン画面にオーバーラップするダイアログ画面を呼び出し、ダイアログを閉じるまではメイン画面が使えなくなります。HTML 的な言い方をすると、ボックス領域の表示/非表示を切り替えつつ、ここが表示されている間は他の要素にアクセスできなくさせたい場合などに便利です。

加えて、個人的によく Google MAPs API を使った地図アプリを作っています。他にも多くの地図 API はありますし、実際に使うこともありますが、ストリートビューなど他にはない機能もあって、マンホールマップを始め、多くのアプリで Google MAPs API を活用しています。

今回紹介するのはこの2つを組み合わせて使う方法、つまり colorbox のモーダルダイアログ内で Google MAPs の地図を表示する方法です。特に考慮することなく普通に(colorbox で表示する領域の中に Google MAPs の地図領域を定義しておくだけで)使えそうな気がしていたのですが、どうもこれらの初期化処理時における相性があまりよくないようで、ちょっとしたコツが必要だということがわかりました。

この「ちょっとしたコツ」の結論は
・colorbox のダイアログを表示して、
ダイアログの表示が完了した後で Google MAPs を初期化してダイアログ内に表示する
という順序で実現する必要がありそうでした。一般的には HTML ページの初期化時に Google MAPs を初期化することが多いと思うのですが、colorbox と組み合わせて使う場合は、その初期化タイミングをダイアログの表示後に(ダイアログの表示が完了したことを検知した上で動的に)行う必要がありそうでした。


以下は具体的な実現方法です。まず jQuery や colorbox、Google MAPs API の JavaScript や CSS を一通りロードします(Google MAPs API をロードする際に API KEY の取得と指定が必要です):
  :
<script src="//code.jquery.com/jquery-2.2.4.min.js"></script>
<script type="text/javascript" src="//maps.google.com/maps/api/js?key=(your api key)"></script>
<link href="/css/colorbox.css" rel="stylesheet"/>
<script src="/js/jquery.colorbox-min.js" type="text/javascript"></script>
  :

HTML 内に colorbox で表示するモーダルダイアログ部分(id="mapModal")を定義します。ここは初期状態では表示されていてはまずいので display:none のスタイル指定をしておきます。またモーダルダイアログ表示した際にこの id="map_canvas" のブロックの中で Google MAPs の地図を表示することになりますが、初期段階では何も定義しません:
  :
<div style="display:none">
  <div id="mapModal">
    <div id="map_canvas" style="width:100%; height:520px"></div>
  </div>
</div>
  :

ではこのブロックをモーダルダイアログ表示するための colorbox のアクションを作成します。以下の例では <span> で括られたエリア(id="span1")をクリックした時に colorbox のモーダルダイアログを 90% x 90% でインライン表示するようにしています。加えてこのモーダルダイアログが表示されきった直後に $('#map_canvas').modalMap() が実行されるように定義しています:
  :
<span href="#mapModal" id="span1">ここをクリック</span>

<script>
$(function(){
  $('#span1').colorbox({
    inline: true,
    transition: 'none',
    width: '90%',
    height: '90%',
    onComplete: function(){
      $('#map_canvas').modalMap();
    }
  });
});
</script>
  :

↑この時の <span> 要素に href 属性を付けて対象モーダルの id を指定するのが肝のようです。。

最後にこの modalMap() 関数を定義して、この中で動的に Google MAP を初期化して表示するまでのコードを記述します:
  :
<script>
$.fn.modalMap = function(){
  var latlng = new google.maps.LatLng( 35.690625, 139.699788 );
  var opt = {
    zoom: 8,
    center: latlng,
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    scaleControl: true
  };

  var map = new google.maps.Map( document.getElementById( 'map_canvas' ), opt );
  map.setCenter( latlng );

  var marker = new google.maps.Marker({
    position: latlng,
    map: map
  });
};
</script>
  :

これで <span> 部分をクリックすると colorbox のモーダルダイアログを出し、ダイアログ内で動的にGoogle MAP の地図を初期化&表示する、という一連のコードが完成です:
2019090801

  ↓

2019090802

(↑わかりにくいかもしれませんが、地図部分はモーダルダイアログ表示されています)



このページのトップヘ