いろんなMacがある
すぐ忘れちゃう。 同じようなアルファベットの並びがあるとなんだかわからなくなる。 フルスペルなんて1mmも覚えられない。そんな自分への記録です。
MacintoshのMac
Appleが作ってるりんごマークのついたパソコンです。
Media Access ControlのMACアドレス
ネットワーク上でパソコンとかスマホとか機械を識別するための番号です。 機械の個人番号のようなもので、基本的には世界で1つの番号です。
# コマンドだとこんな感じで確認できます。 $ ifconfig en0 ether en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 options=400<CHANNEL_IO> ether 12:34:56:ab:cd:ef #<<< ここがMACアドレス
このMACアドレスがあるので広い広いネットワークの中でもどの器械(パソコンとか)にどの器械から通信しているかわかるようになります。
Message Authentication CodeのMAC
メッセージ認証をする時にデータと一緒に送られてくる検証用のデータです。
メッセージ(message)を認証(authentication)するコード(code)なので、日本語で「メッセージ認証コード」、英語で「Message Authentication Code」、略して「MAC」です。 悪い人が送ったデータじゃないことを確かめるために使うデータです。
例えば、GitHubのWebhookを使うと送られてくる情報にこんな感じでくっついてきます。
{ ...省略... "headers": { ...省略... "x-hub-signature": "sha1=sha1のHMAC値", "x-hub-signature-256": "sha256=sha256のHMAC値" }, ...省略... "body": "データの本体", ...省略... }
LambdaのPythonでGitHubのWebhookから送られてくるHAMC値をチェックする
LambdaのPythonでGitHubのWebhookから送られてくるHAMC値をチェックするコード
# -*- coding: utf-8 -*- from __future__ import print_function import json, hmac, hashlib, base64, urllib def checkHmac(event) -> bool: """GitHubのWebhookから送られてくるHAMC値をチェックする""" # 送信者と受信者で秘密情報であるSecretの値 secret = 'GitHubのWebhookで設定したSecretの値(直書きしないで環境変数とかSecrets Managerから取得すべし)' # GitHubから送られてきた情報にあったHMAC値 sent_hmac = event['headers']['x-hub-signature-256'] print('SHA-256ハッシュ関数用のHMAC値:' + sent_hmac) # Secretの値をbytes型に変換する secret_bytes = bytes(secret, 'utf-8') if (event['isBase64Encoded']): # bodyをbase64でデコードしてbytes型にする body_bytes = base64.b64decode(event['body']) else: # bodyをbytes型に変換する body_bytes = bytes(event['body'], 'utf-8') # 「Secret + メッセージ + ハッシュ関数」でHMAC値を作る created_hmac = 'sha256=' + hmac.new(secret_bytes, body_bytes, hashlib.sha256).hexdigest() print('自分で作ったHMAC値:' + created_hmac) # 2つのHMAC値が同じであったら改竄されていないと判断する return hmac.compare_digest(created_hmac, sent_hmac) def lambda_handler(event, context): if checkHmac(event): print('HMAC値で認証できた!') # base64デコードした上で文字列にする b64decd = base64.b64decode(event['body']).decode() # URLデコードして「payload=」を削除する urldecd = urllib.parse.unquote(b64decd).lstrip('payload=') # 辞書型にする body = json.loads(urldecd) # そして処理で使う print(body) else: print('へんなHMAC値がGitHubから送られてきた!')
ことの発端は試験勉強
- じみに続けている情報処理試験の勉強でメッセージ認証を勉強していた。
- そういえば以前Backlogの課題にGitHubのコミットを連携するをやった時にGitHubのWebhookでHMAC値を使ったのを思い出して、今一度やってみようと思った。
- 手頃にGitHubでWebhookを設定
- AWSでAPI GatewayとLambdaをノリで作ってみた。
- WebhookからきたHMAC値と自作したHMAC値が一致しない!
GitHubのWebhookが送ってくるbodyがへんだ
print(event)
してCloudWatch Logsをみたら・・・bodyが・・・肉眼で読めない・・・。
{ "version": "2.0", ... "body": "cGF5bG9hZD0lN0IlMjJ...=", "isBase64Encoded": true }
アルファベットと数字と最後にある=
からBase64エンコードされてるっぽい・・・前にやった時は普通に読めたのに・・・・。
ペイロードがBase 64でエンコードされている
じっと見つめると "isBase64Encoded": true
と最後に書いてある・・・なにこれ?
Lambda プロキシ統合の場合、API Gateway は、次のようにクライアントリクエスト全体をバックエンド Lambda 関数の入力 event パラメータにマッピングします。
...省略...
"isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encoded"
訳すと「適用可能なリクエスト・ペイロードがBase 64でエンコードされているかどうかを示す真偽フラグ」。
HMAC値を作るにはbodyをBase64デコードします。
# 「cGF5bG9hZD0lN0IlMjJ...=」なbodyをBase64デコードしてbytes型にしてあげる body_bytes = base64.b64decode(event['body']) # bytes型にしたbodyを使ってHMAC値を作ろう created_hmac = 'sha256=' + hmac.new(secret_bytes, b64decd, hashlib.sha256).hexdigest()
WebhookからきたHMAC値は X-Hub-Signature
ではなくX-Hub-Signature-256
を使うのがお勧めです。
{ ...省略... "headers": { ...省略... "x-hub-signature": "sha1=sha1のHMAC値", "x-hub-signature-256": "sha256=sha256のHMAC値" }, ...省略... }
ヘッダ | 説明 |
---|---|
X-Hub-Signature | このヘッダは、webhook が secret で設定されている場合に送信されます。 これはリクエスト本文の HMAC hex digest であり、SHA-1 ハッシュ関数と HMAC key としての secret を使用して生成されます。 X-Hub-Signature は、既存の統合との互換性のために提供されているため、より安全な X-Hub-Signature-256 を代わりに使用することをお勧めします。 |
X-Hub-Signature-256 | このヘッダは、webhook が secret で設定されている場合に送信されます。 これはリクエスト本文の HMAC hex digest であり、SHA-256 ハッシュ関数と HMAC key としての secret を使用して生成されます。 |
表の出典 : webhook イベントとペイロード - GitHub Docs
isBase64EncodedがFalseの時も考えておきます。
isBase64Encoded
がFalse
だとBase64エンコードはされなさそうなので処理の分岐を作っておきます。
if (event['isBase64Encoded']): # bodyをbase64でデコードしてbytes型にする body_bytes = base64.b64decode(event['body']) else: # bodyをbytes型に変換する body_bytes = bytes(event['body'], 'utf-8')
処理でBodyの内容を使う時はさらに加工して使うといいでしょう。
# base64デコードした上で文字列にする b64decd = base64.b64decode(event['body']).decode() # こんな感じになる >>> payload=%7B%22ref%22%3A%22refs%2Fheads%2Fmaster%22%2C%22before... print(b64decd) # URLデコードして「payload=」を削除する urldecd = urllib.parse.unquote(b64decd).lstrip('payload=') # こんな感じになる >>> {'ref': 'refs/heads/master', 'before... print(urldecd) # 辞書型にすると使いやすい(と思う) body = json.loads(urldecd)
付加データを付き合わせてなりすましを見破るHMAC
前回の勉強内容
勉強のきっかけになった問題
令和2年度秋(2020-10)の情報処理安全確保支援士試験午後1の問題にHMACについての問題が出題されました。
そして、大敗を喫しました・・・ので勉強します。
なりすまし対策としてデータが改竄されていないかを検証します。
「勉強のきっかけになった問題」では、「会員番号をバーコードで表示するアプリのなりすまし対策」にメッセージ認証を用いることになっています。
「他人のバーコードを会員番号から推測して表示」「他人の会員番号を盗んでバーコードを生成」しちゃってなりすしできちゃうので対策をするわけです。
メッセージ認証は、検証用のデータをメッセージに添付する方法です。
ネットワークを伝わっている間にデータが改竄されても検出できるように以下の流れで検証を行います。
- メッセージをやりとりする送信者と受信者で秘密情報(共有鍵など)を共有する
- 送信者がメッセージ本文から秘密情報などをごにょごにょ使って検証用のデータを作る
- 送信者は、メッセージ本文と検証用のデータを受信者に送る
- 受信者がメッセージ本文から秘密情報などをごにょごにょ使って検証用のデータを作る
- 受信者は、送られてきた検証用のデータと自分で作った検証用のデータを比較する
- 2つの検証用のデータが同じであったら改竄されていないと判断する
検証用のデータのことをMACといいます。
- 英語 : Message Authentication Code
- 日本語 : メッセージ認証符号
送信者Aが,受信者Bと共有している鍵を用いて,メッセージからメッセージ認証符号を生成し,そのメッセージ認証符号とメッセージを受信者Bに送信する。このとき,メッセージとメッセージ認証符号を用いて,受信者Bができることはどれか。
答. メッセージの改ざんがないことを判定できる。
HMACは、ハッシュ関数を使って作られたMACのことです。
- 英語 : Hash-based Message Authentication Code
メッセージ本文 + 共有鍵 = MAC メッセージ本文 + 共有鍵 + ハッシュ関数 = HMAC
みたいな感じです。
MAC(Message Authentication Code)は、通信内容の改ざんの有無を検証し、完全性を保証するために通信データから生成される固定長のビット列です。 MACの生成には、共通鍵暗号を用いたもの(DES-MACやAES-MAC)とハッシュ関数を用いたもの(HMAC)がありますが、設問ではブロック暗号を用いてMACを生成しているので共通鍵暗号を用いた方式であることがわかります。
GitHubのWebhookでもHMACを使います。
以前、Backlogの課題にGitHubのコミットを連携する方法 - ponsuke_tarou’s blogをやった時にHMACを使いました。
- GitHubでWebhookを登録する時に合わせて登録するSecretが「メッセージをやりとりする送信者と受信者で秘密情報」になります。
- GitHubは、情報と一緒に検証用のデータを送ってくれます。
- BODY部分のメッセージ本文とSecret(秘密情報)とハッシュ関数でHMAC値を作ります。
- 2つのHMAC値が同じであったら改竄されていないと判断する(下記Pythonのコード参照)
{ ...省略... "headers": { ...省略... "x-hub-signature": "sha1=検証データ", "x-hub-signature-256": "sha256=検証データ" }, ...省略... "body": "BODYに送ってくれた情報がある" ...省略... }
# -*- coding: utf-8 -*- from __future__ import print_function import json, hmac, hashlib def lambda_handler(event, context): # 送信者と受信者で秘密情報であるSecretの値 secret = '3m@6vC5Y_mNwhhA' # GitHubから送られてきた情報にあったHMAC値 sent_hmac = event['headers']['x-hub-signature-256'] print('SHA-256ハッシュ関数用のHMAC値:' + sent_hmac) # Secretの値をbytes型に変換する secret_bytes = bytes(secret, 'utf-8') if (event['isBase64Encoded']): # bodyをbase64でデコードしてbytes型にする body_bytes = base64.b64decode(event['body']) else: # bodyをbytes型に変換する body_bytes = bytes(event['body'], 'utf-8') # 「Secret + メッセージ + ハッシュ関数」でHMAC値を作る created_hmac = 'sha256=' + hmac.new(secret_bytes, body_bytes, hashlib.sha256).hexdigest() print('自分で作ったHMAC値:' + created_hmac) # 2つのHMAC値が同じであったら改竄されていないと判断する if created_hmac == sent_hmac: print('HMAC値で認証できた!') else: print('認証できないHMAC値がGitHubから送られてきた!')
上記は以下の投稿で紹介しているコードを基にしていますので、ご興味のある方はご参照ください。 ponsuke-tarou.hatenablog.com
次回の勉強内容
はじめてのGoogle Analytics Reporting APIをNode.jsでちょっとだけ使ってみる
以前の投稿でGoogle AnalyticsとGoogle Cloud Platformを設定してGoogle Analytics Reporting APIを使えるようにしました。
今回は、自分のQiitaのページにアナリティクスを設定、Qiitaページ用のサービスアカウントを作ったので、その情報をNode.jsを使ってAPIで取得してみたいと思います。 ちなみに、Node.jsは未経験です、ずぶのど素人です。
準備する
環境 : Windows10 Pro バージョン1909
- Node.jsをインストールする
- 参考 : Node.jsをインストールする - Qiita
- 今回インストールしたバージョンは「v10.23.1」でした
- yarnをインストールする
- 参考 : yarnをインストールする - Qiita
- 今回インストールしたバージョンは「1.22.10」
- 作業用のディレクトリを作成する(ここ以降は下記のコードを参照)
- yarnの初期化する
- GoogleAPIのNode.js用ライブラリgoogle-api-nodejs-clientをインストールする
- Google Cloud Platformで作成したサービスアカウントの鍵ファイル(json)を格納するためのディレクトリを作成する
- 鍵ファイル(json)を格納する
# 作業用のディレクトリを作成する $ mkdir google-analytics-api $ cd google-analytics-api/ # yarnの初期化する $ yarn init ...省略... Done in 93.28s. # GoogleAPIのNode.js用ライブラリgoogle-api-nodejs-clientをインストールする $ yarn add googleapis yarn add v1.22.10 info No lockfile found. [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh packages... success Saved lockfile. success Saved 31 new dependencies. info Direct dependencies └─ googleapis@68.0.0 info All dependencies ├─ abort-controller@3.0.0 ...省略... Done in 7.96s. # Google Cloud Platformで作成したサービスアカウントの鍵ファイル(json)を格納するためのディレクトリを作成する $ mkdir config # 鍵ファイル(json)を格納する $ ls config/ client_secrets.json
コードを書く
Google APIでの認証については、Analytics Reporting API - 承認 | アナリティクス Reporting API v4が参考になります。
analytics.js
というファイルを作って以下のコードを書きました。
リクエストの内容についてはメソッド: reports.batchGet | アナリティクス Reporting API v4 | Google Developersを見ながらしてする値を決めました。
const { google } = require('googleapis') const KEY_FILE = './config/client_secrets.json' // 鍵ファイルのパスを指定する const VIEW_ID = 'GoogleアナリティクスのViewID' /** * Auth2.0認証をしてクライアントを取得する * @returns クライアント */ async function getAnalyticsreportingClient() { const client = await google.auth.getClient({ keyFile: KEY_FILE, scopes: 'https://www.googleapis.com/auth/analytics.readonly' }) const analyticsreporting = google.analyticsreporting({ version: 'v4', auth: client }) return analyticsreporting } /** Google Analyticsから情報を取得する */ async function getReporting() { const client = await getAnalyticsreportingClient() // 取得期間をyyyy-MM-dd形式で指定 let dateRanges = [{startDate: '2021-03-19', endDate: '2021-03-30'}] const res = await client.reports.batchGet({ requestBody: { reportRequests: [ // 指定期間でのPV数TOP3のページを取得するリクエスト { viewId: VIEW_ID, dateRanges: dateRanges, dimensions: [{name: 'ga:pagePath'}], // ページ単位の情報を取得する metrics: [{expression: 'ga:pageviews'}, {expression: 'ga:avgTimeOnPage'}], // PV数と平均ページ滞在時間を取得する orderBys: {fieldName: 'ga:pageviews', sortOrder: 'DESCENDING'}, // PV数の降順でソートする pageSize: 3 // 3件分取得する }, // 直近1週間での平均ページ滞在時間TOP3のページを取得するリクエスト { viewId: VIEW_ID, dateRanges: dateRanges, dimensions: [{name: 'ga:sourceMedium'}], // ページ単位の情報を取得する metrics: [{expression: 'ga:bounceRate'}, {expression: 'ga:avgTimeOnPage'}], // 直帰率と平均ページ滞在時間を取得する orderBys: {fieldName: 'ga:avgTimeOnPage', sortOrder: 'DESCENDING'}, // 平均ページ滞在時間の降順でソートする pageSize: 3 // 3件分取得する } ] } }) // コンソールに取得内容を表示 console.log(JSON.stringify(res.data)) } getReporting()
Reporting APIを呼び出す
早速、ターミナルから実行してみます。本当はレスポンスが1行なのですが、見にくいので改行などを入れています。
$ node analytics.js {"reports":[ { "columnHeader":{"dimensions":["ga:pagePath"],"metricHeader":{"metricHeaderEntries":[{"name":"ga:pageviews","type":"INTEGER"},{"name":"ga:avgTimeOnPage","type":"TIME"}]}}, "data":{"rows":[ {"dimensions":["/ponsuke0531/items/4629626a3e84bcd9398f"],"metrics":[{"values":["1631","387.46666666666664"]}]}, {"dimensions":["/ponsuke0531/items/df51a784b5ff48c97ac7"],"metrics":[{"values":["1160","601.5056179775281"]}]}, {"dimensions":["/ponsuke0531/items/edf2eee638202aa7f61f"],"metrics":[{"values":["999","401.8253968253968"]}]} ], "totals":[{"values":["35429","382.3464974141984"]}], "rowCount":499, "minimums":[{"values":["1","0.0"]}], "maximums":[{"values":["1631","1679.0"]}]}, "nextPageToken":"3" }, { "columnHeader":{"dimensions":["ga:sourceMedium"],"metricHeader":{"metricHeaderEntries":[{"name":"ga:bounceRate","type":"PERCENT"},{"name":"ga:avgTimeOnPage","type":"TIME"}]}}, "data":{"rows":[ {"dimensions":["27.94.140.223:8080 / referral"],"metrics":[{"values":["0.0","1335.0"]}]}, {"dimensions":["nekorokkekun.hatenablog.com / referral"],"metrics":[{"values":["0.0","996.0"]}]}, {"dimensions":["zenn.dev / referral"],"metrics":[{"values":["0.0","909.0"]}]} ], "totals":[{"values":["86.22115384615384","382.3464974141984"]}], "rowCount":69, "minimums":[{"values":["0.0","0.0"]}], "maximums":[{"values":["100.0","1335.0"]}]}, "nextPageToken":"3" } ]}
はじめてのGoogle Analytics Reporting APIをPythonとCloud9でちょっとだけ使ってみる
以前の投稿でGoogle AnalyticsとGoogle Cloud Platformを設定してGoogle Analytics Reporting APIを使えるようにしました。
そのAPIを簡単に呼び出してみたいと思います。
今回は、どんなものが取得できるかをみてみたいだけなので簡易にReporting APIのドキュメントにあるクイックスタートのコードとAWSのCloud9を使います。
Pythonをそれほど知りません、が、大丈夫。なぜなら「実装」と呼べるほどのことはしません。ちょっと設定するだけです。
準備する
- はじめてのアナリティクス Reporting API v4: サービス アカウント向け Python クイックスタートからPythonのコード(HelloAnalytics.py)をダウンロードする
- Google AnalyticsでView IDを確認する
- Google Analyticsへログインする
- 管理 > 対象のアカウント選択 > 対象のプロパティ選択 > [ビューの設定]で画面を開く
- [基本設定] > [ビュー ID]に表示されたView IDをメモする
- AWSでCloud9の環境を作成する
Cloud9でコードを設定する
- Cloud9の環境を起動して表示する
- [File] > [Upload Local Files...]で以下のファイルをアップロードする
- サービスアカウントの鍵ファイル(json)
- ダウンロードしたクイックスタートのコードファイル(HelloAnalytics.py)
- HelloAnalytics.pyを開く
- 上のほうにある以下の値を書き換える
- <REPLACE_WITH_JSON_FILE> : サービスアカウントの鍵ファイル名(HelloAnalytics.pyと違う場所に配置した場合はそのパス)
- <REPLACE_WITH_VIEW_ID> : Google AnalyticsのView ID
ライブラリをインストールする
GoogleのAPIを呼び出すために必要なライブラリをインストールします。
Cloud9を使っている場合は必ず--user
オプションを使用して自分の使うPythonに対してインストールします。
忘れるとModuleNotFoundError: No module named 'apiclient'となることがあるので注意してください。
# 認証に使うライブラリをインストールする $ python -m pip install --user --upgrade oauth2client # Google APIを使うためのライブラリをインストールする $ python -m pip install --user --upgrade google-api-python-client
Reporting APIを呼び出す
あとはコードを実行するだけです。
HelloAnalytics.pyを表示した状態で画面上部の[Run]ボタンを押下すればGoogle AnalyticsからAPIで取得した情報が表示されます。
これを基にメソッド: reports.batchGetを見ながらコードのパラメータを変えてどんな情報が取得できるかを試していきたいと思います。
Google Analytics Reporting APIでの発生したリクエスト数を確認する
Google APIには、リクエスト数の制限があります。
Google アナリティクスは数多くのサイトで使用されています。Google では、処理能力を超える負荷がシステムにかからないようにすること、システム リソースが均等に配分されるようにすることを目的に、API リクエストに制限と割り当てを設けています。この制限と割り当ては変更されることがあります。
Analytics Reporting APIもタダで使えますが、リクエスト数に制限があるので制限を超えるとエラーコードが返却されるようになります。
Google Cloud Platformのプロジェクトでは「割り当て(1 日あたりの API リクエスト数など)」というものが設定できます。 割り当ての詳細は、割り当ての操作 | ドキュメント | Google Cloudを参照してください。
Analytics Reporting APIのリクエスト数制限
API リクエストの制限と割り当て - Google Developersと上記割り当て(割り当ては初期値のまま)ではリクエスト数の単位がバラバラしていてわかりにくかったのでそれぞれの情報を表にして集めてみました。
単位 | リクエスト数上限 | 参照元 |
---|---|---|
プロジェクト | 50,000件/日 | [APIとサービス]の割り当て & API リクエストの制限と割り当て |
プロジェクト | 1,200件/分 | [APIとサービス]の割り当て |
プロジェクト | 2,000件/100秒 | API リクエストの制限と割り当て |
ユーザー 1 人につきプロジェクトごとで | 100件/100秒(1,000 件まで引き上げ可能) 10件/秒 |
API リクエストの制限と割り当て |
ユーザー | 600件/分 | [APIとサービス]の割り当て |
ビュー | 10,000件/日(引き下げ不可) | API リクエストの制限と割り当て |
プロジェクトごとのリクエストの失敗(プロファイルあたり) | 10件/時 かつ 50件/日 | Reporting API のリクエスト エラーについて - API リクエストの制限と割り当て |
表を見ていて気が付いたのは「1日単位の制限」を守っても「1秒や100秒単位の制限」を守らないとエラーになってしまうということでした。特定の時間にどっとリクエストがくるとエラーになる可能性があるのですね。
Google Analytics Reporting APIでの発生したリクエスト数を確認します。
リクエスト数に制限があるので、APIを使うアプリケーションを作る場合にどのくらいリクエスト数が発生するかを確認したいと思いました。
Google APIのリクエスト数は[APIとサービス]の[ダッシュボード]で確認できます。
API 指標を表示する最も簡単な方法は、Google Cloud Console の API ダッシュボードを使用することです。すべての API の使用状況の概要を表示することも、特定の API の使用状況を詳細に調べることもできます。
- Google Cloud Platformにログインする
- 左上メニュー > [APIとサービス] > [ダッシュボード]で画面遷移する
各APIのリクエスト数を[割り当て]で詳しく確認します。
[ダッシュボード]ではAPI毎の「任意期間での合計リクエスト数」を確認できましたが、使っている特定APIのリクエスト数をもっと詳しく確認したい、そのんな時のことです。 例えば、今回はGoogle Analytics Reporting APIについて詳しく見てみます。
- [ダッシュボード]画面一覧表から[Analytics Reporting API]リンクで画面遷移する
- 左メニュー > [割り当て]で画面遷移する
- [Requests]の右にある「v」で詳細を表示 > プルダウンで[Requests1分あたり]を選択 > 表示期間を選択
- 表示される「平均」の期間は「表示期間」によって変わるようなので細かく見る場合は注意する
各ユーザーのリクエスト数を[指標]で確認します。
[割り当て]画面の下には制限数の一覧表があります。
「Requests 1 分あたり /ユーザー」という行がとても気になります。
そこで探し回っていると、[割り当て]と同じくAnalytics Reporting APIの[指標]をから各ユーザーのリクエスト数が確認できました。
- [ダッシュボード]画面一覧表から[Analytics Reporting API]リンクで画面遷移する
- 左メニュー > [指標]で画面遷移する
- [Credentials]プルダウンで見たいユーザーを選択 > [OK]ボタン > 表示期間を選択
1回のAPI呼出しにリクエストを複数指定してもリクエストは1回?
Analytics Reporting APIでは、1回のAPI呼出しにリクエストを5つまで含めることができます。
リクエストは最大で 5 つ送信できます。すべてのリクエストには、同じ dateRanges、viewId、segments、samplingLevel、および cohortGroup が含まれている必要があります。
メソッド: reports.batchGet | アナリティクス Reporting API v4 | Google Developers
1回のAPI呼出しにリクエストを複数指定するとカウントアップされるリクエスト回数は何回なのでしょうか?
let dateRanges = [{startDate: '2021-03-29', endDate: '2021-03-30'}] const res = await client.reports.batchGet({ requestBody: { reportRequests: [ { viewId: VIEW_ID, dateRanges: dateRanges, dimensions: [{name: 'ga:pagePath'}], metrics: [{expression: 'ga:pageviews'}, {expression: 'ga:avgTimeOnPage'}], }, { viewId: VIEW_ID, dateRanges: dateRanges, dimensions: [{name: 'ga:sourceMedium'}], metrics: [{expression: 'ga:bounceRate'}, {expression: 'ga:avgTimeOnPage'}], }, { viewId: VIEW_ID, dateRanges: dateRanges, dimensions: [{name: 'ga:source'}], metrics: [{expression: 'ga:pageviews'}], }, { viewId: VIEW_ID, dateRanges: dateRanges, dimensions: [{name: 'ga:browser'}], metrics: [{expression: 'ga:pageviews'}], }, { viewId: VIEW_ID, dateRanges: dateRanges, dimensions: [{name: 'ga:country'}], metrics: [{expression: 'ga:pageviews'}], } ] } })
なんと1件しかカウントアップされませんでした。同じ期間などを条件にするのであればまとめてAPI呼出ししたほうがお得なのかもしれません。
とはいえGoogleのドキュメントにある以下の記載は気になるところです。
★注: バッチ処理で複数の Reporting API リクエストを 1 回のリクエストにまとめても、定められた割り当て量を超えるリクエストを実行することはできません。
「バッチ処理」のリンク先がGoogle アナリティクス Management APIのGoogle アナリティクス API リクエストのバッチ処理になっているのでManagement APIを使った時のことなのかもしれませんが、実装の時には気をつけるポイントになりそうです。(Google アナリティクス Management APIを知らないのですが・・・)
はじめてのGoogle Analytics Reporting APIをちょっとだけ使ってみる
Google AnalyticsからAPIで情報を取得することになりました。
しかし、Google AnalyticsもGoogle APIも使ったことがないので全く分かりません。
ここでは、調べながらはてなブログの情報をGoogle Analytics Reporting APIで取得するまでをやってみます。
Google Analyticsの設定をする
まずは、情報の基となるGoogle Analyticsを設定します。 今回は、このponsuke_taro's blogをGoogle Analyticsに設定して情報を蓄積できるようにします。
はてなブログ用のデータ解析をユニバーサル アナリティクスプロパティで作成する
参考 : Google Analyticsを導入する - はてなブログ ヘルプ
ユニバーサル アナリティクスは前世代の Google アナリティクスです。ユニバーサル アナリティクス プロパティと Google アナリティクス 4 プロパティとでは、使用できるレポートに違いがあります。
- (ない場合)Google アカウントの作成でアカウントを作成する
- お客様のビジネスに適した分析ツールとソリューション - Google アナリティクス を表示する
- [無料で利用する]ボタンで次の画面へ遷移し、[測定を開始]ボタンで設定画面を表示する
- 「アカウント名」に何のアクセス解析かわかる名前を設定して[次へ]ボタンで進む
- 「プロパティ名」などの入力項目を設定して登録する
- レポートのタイムゾーン」「通貨」 : 日本に設定する
- [プロパティの設定]の下にある[詳細オプションを表示]リンクでユニバーサル アナリティクスプロパティの設定欄を表示して以下を設定して[次へ]ボタンで進む
- ユニバーサル アナリティクス プロパティの作成 : ON
- ウェブサイトの URL : 登録するはてなブログのURL
- ユニバーサル アナリティクスのプロパティのみを作成する : ON
- [ビジネス情報]部分は任意で入力して[作成]ボタンで作成する
- [プロパティ]で作成したプロパティを選択 > [トラッキング情報] > [トラッキングコード] > [トラッキングID]をメモしておく
はてなブログにトラッキングIDを設定する
- はてなブログにログインしてダッシュボード管理画面を表示する
- [設定] > [詳細設定] > [解析ツール] > [Google Analytics 埋め込み]にメモしたGoogle AnalyticsのトラッキングIDを入力する
- ページ下の[変更する]ボタンで変更を確定する
Google Cloud PlatformでAPI使えるようにする
作成したはてなブログ用のデータ解析に溜まった情報をGoogle Cloud Platform(以降GCP)のAPIで取得できるように設定していきます。
Google Analytics Reporting APIを有効化する
まずは、Google Analytics Reporting APIを使えるように設定します。
- Google Cloud Platformにログインする
- (ない場合)プロジェクトを作成する
- GCP画面上部でプロジェクトを選択 > [APIとサービス] > [ライブラリ]
- 「Google Analytics API」を検索して、「Google Analytics Reporting API」を選択する
- [有効にする]ボタンでAPIを有効化する
3つ候補に出てきたAPIの違いがいまいちわからないので頑張った概要の和訳を記録として書いておきます。
API名 | 概要の頑張った和訳 |
---|---|
Google Analytics API | Analyticsの設定およびレポートデータへのアクセスを提供します。 |
Google Analytics Reporting API | Google Analyticsでレポートデータにアクセスするための最も高度なプログラム的方法です。Google AnalyticsレポートAPIを使用すると、カスタムダッシュボードを構築してGoogle Analyticsデータを表示したり、複雑なレポートタスクを自動化して時間を節約したり、Google Analyticsデータを他のビジネスアプリケーションと統合したりすることができます。 |
Google Analytics Data API | Google Analyticsのレポートデータにアクセスします。 |
サービスアカウントを作成する
APIを利用するサービスアカウントを作成します。
サービス アカウントは IAM によって管理されるもので、人間のユーザー以外のものを指しています。App Engine アプリの実行や Compute Engine インスタンスとのやり取りなど、アプリケーション自体がリソースにアクセスする場合や、アクションを独自に実行する必要がある場合を対象としています。
- GCP画面上部でプロジェクトを選択 > サイドメニュー[APIとサービス] > [認証情報]
- [認証情報を作成]ボタン > [サービス アカウント]
- 「サービス アカウント名」「サービス アカウント ID」に任意の値を入力して[完了]ボタンでアカウントを作成する
- 一覧にある作成したアカウントの[操作] > [詳細を管理]で詳細画面を表示
- [キー]タブ > [鍵を追加] > [新しい鍵を作成]
- [キーのタイプ]で「JSON」を選択 > [作成]ボタンで作成して鍵ファイルをダウンロードする
- ここでダウンロードする鍵ファイルは「再作成できない」「鍵があればサービスを使えてしまう」ので超大切に保管する
Google Analyticsで作成したサービスアカウントに権限を設定する
作成したアカウントがGoogle Analyticsにあるはてなブログの情報を取得できるように権限を設定します。
- Google Analyticsにログインする
- 管理 > 対象のアカウント選択 > [アカウントユーザーの管理]
- 右上の[+]ボタン > [ユーザーを追加]
- 以下を設定して[追加]ボタンで追加する
- メールアドレス : 作成したサービスアカウントのサービスアカウントID(
@
以降も入力する) - 権限 : 表示と分析
- メールアドレス : 作成したサービスアカウントのサービスアカウントID(
Google Analytics Reporting APIを呼び出す
早速、Google Analytics Reporting APIを呼び出してみます。
PythonとCloud9でちょっとだけ使ってみる
メトリクスとディメンションって何?
レポートの作成 | アナリティクス Reporting API v4 | Google Developersで基本の使い方を見ていくとまずはmetrics
とdimensions
なるものに引っかかりました。
Web分析をやっている人には疑問に思わないことなのでしょうが、Google AnalyticsはおろかWeb分析的なことをやったことがないので「メトリクス」「ディメンション」が何なのかわかりません。
アナリティクスのレポートは、すべてディメンションと指標の組み合わせに基づいて構成されます。
ディメンションはデータの属性です。たとえば、ディメンション「市区町村」はセッションの性質を表し、「横浜」、「川崎」などセッションが発生した市区町村を指定します。ディメンション「ページ」は、閲覧されたページの URL を表します。
指標はデータを定量化したものです。指標「セッション」はセッションの合計数です。指標「ページ/セッション」は、セッションあたりの平均閲覧ページ数です。
「メトリクス=量」「ディメンション=量の単位」みたいな感じです(正確には違うけど)。
「日単位のPV数を取得したい」だと「日」がディメンションで「PV数」がメトリクス・・・的な。
HelloAnalytics.pyのget_report
メソッドにあるパラメータをこんな感じに変えると・・・
※. ここのコードははじめてのGoogle Analytics Reporting APIをPythonとCloud9でちょっとだけ使ってみる - ponsuke_tarou’s blogで使ったものを基にしています。
# ...省略... 'reportRequests': [ { 'viewId': VIEW_ID, 'dateRanges': [{'startDate': '7daysAgo', 'endDate': 'today'}], # メトリクスにPVを指定 'metrics': [{'expression': 'ga:pageviews'}], # ディメンションに日にちを指定 'dimensions': [{'name': 'ga:date'}] }] # ...省略...
こんな感じで「日単位のPV数」が取得できました。
ga:date: 20210316 Date range: 0 ga:pageviews: 411 ga:date: 20210317 Date range: 0 ga:pageviews: 845 ga:date: 20210318 Date range: 0 ga:pageviews: 787 ga:date: 20210319 Date range: 0 ga:pageviews: 498
メトリクスとディメンションで指定する値の意味が分からなくなりそうなので使ったものはメモしていきます。
dimensions | 意味 | レスポンス例 |
---|---|---|
ga:date | 日付 | 20210322 |
ga:pagePath | ページ | "/ponsuke0531/items/edf2eee638202aa7f61f" |
ga:sourceMedium | 参照元 | "zenn.dev / referral" |
ga:dimension{インデックス番号} | カスタム ディメンション |
どこかに日本語で一覧があったらいいのに・・・。
metrics | 意味 | type | レスポンス例 |
---|---|---|---|
ga:pageviews | ページビュー数 | INTEGER | 1631 |
ga:avgTimeOnPage | 平均ページ滞在時間(秒) | TIME | "387.46666666666664" |
ga:bounceRate | 直帰率 | PERCENT | "0.0" |
Google APIには、リクエスト数の制限がありますので気をつけましょう。
遊びで使っているだけならいいのですが、お仕事で使う場合などにはAPIへのリクエスト数の制限がありますので気をつけましょう。
Node.jsでちょっとだけ使ってみる
1回のAPI呼出しで5こまでリクエストを送信できます
各リクエストには、別々のレスポンスが返されます。リクエストは最大で 5 つ送信できます。すべてのリクエストには、同じ dateRanges、viewId、segments、samplingLevel、および cohortGroup が含まれている必要があります。
リクエストの本文 | メソッド: reports.batchGet | アナリティクス Reporting API v4 | Google Developers
こんな感じで1回のAPI呼出しで2このリクエストを送信してみました。
※. ここのコードははじめてのGoogle Analytics Reporting APIをNode.jsでちょっとだけ使ってみる - ponsuke_tarou’s blogで使ったものを基にしています。
let dateRanges = [{startDate: '2021-03-19', endDate: '2021-03-30'}] // 1回のAPI呼出し const res = await client.reports.batchGet({ requestBody: { reportRequests: [ // 1こ目のリクエスト { viewId: VIEW_ID, dateRanges: dateRanges, dimensions: [{name: 'ga:pagePath'}], metrics: [{expression: 'ga:pageviews'}, {expression: 'ga:avgTimeOnPage'}], orderBys: {fieldName: 'ga:pageviews', sortOrder: 'DESCENDING'}, pageSize: 3 }, // 2こ目のリクエスト { viewId: VIEW_ID, dateRanges: dateRanges, dimensions: [{name: 'ga:sourceMedium'}], metrics: [{expression: 'ga:bounceRate'}, {expression: 'ga:avgTimeOnPage'}], orderBys: {fieldName: 'ga:avgTimeOnPage', sortOrder: 'DESCENDING'}, pageSize: 3 } ] } })
レスポンスも2こ分返ってきました。(レスポンスは手ごろに改行しています。)
{"reports":[ { "columnHeader":{"dimensions":["ga:pagePath"],"metricHeader":{"metricHeaderEntries":[{"name":"ga:pageviews","type":"INTEGER"},{"name":"ga:avgTimeOnPage","type":"TIME"}]}}, "data":{"rows":[ {"dimensions":["/ponsuke0531/items/4629626a3e84bcd9398f"],"metrics":[{"values":["1631","387.46666666666664"]}]}, {"dimensions":["/ponsuke0531/items/df51a784b5ff48c97ac7"],"metrics":[{"values":["1160","601.5056179775281"]}]}, {"dimensions":["/ponsuke0531/items/edf2eee638202aa7f61f"],"metrics":[{"values":["999","401.8253968253968"]}]} ], "totals":[{"values":["35429","382.3464974141984"]}], "rowCount":499, "minimums":[{"values":["1","0.0"]}], "maximums":[{"values":["1631","1679.0"]}]}, "nextPageToken":"3" }, { "columnHeader":{"dimensions":["ga:sourceMedium"],"metricHeader":{"metricHeaderEntries":[{"name":"ga:bounceRate","type":"PERCENT"},{"name":"ga:avgTimeOnPage","type":"TIME"}]}}, "data":{"rows":[ {"dimensions":["27.94.140.223:8080 / referral"],"metrics":[{"values":["0.0","1335.0"]}]}, {"dimensions":["nekorokkekun.hatenablog.com / referral"],"metrics":[{"values":["0.0","996.0"]}]}, {"dimensions":["zenn.dev / referral"],"metrics":[{"values":["0.0","909.0"]}]} ], "totals":[{"values":["86.22115384615384","382.3464974141984"]}], "rowCount":69, "minimums":[{"values":["0.0","0.0"]}], "maximums":[{"values":["100.0","1335.0"]}]}, "nextPageToken":"3" } ]}
試しにリクエストの部分をコピペして6こリクエストを送信したらちゃんと以下のエラーになりました。
$ node analytics.js (node:7580) UnhandledPromiseRejectionWarning: Error: There are too many requests in the batch request. The max allowed is 5 at Gaxios._request (C:\path\google-analytics-api\node_modules\gaxios\build\src\gaxios.js:127:23) at process._tickCallback (internal/process/next_tick.js:68:7) (node:7580) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:7580) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
JavaとSpringBootでちょっとだけ使ってみる(現在奮闘中)
さて、実際に使いたい環境でもやってみようと思ったら・・・そもそもPythonは使っていない・・・本当にぽんすけですね。 ということで今度はJavaを使ってSpring Bootの環境でやってみたいと思います。
- Spring Bootのプロジェクトを用意する
- クライアント ライブラリをインストールする
SpringのRestTemplateでAPIを呼び出す時にクエリパラメータをくっつける
RestTemplateでAPIを使うのにパラメータの設定方法を試しました。
はじめてSpring BootでRestTemplateを使ってAPIを呼び出すことになりました。
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations
HTTP リクエストを実行する同期クライアント。JDK HttpURLConnection、Apache HttpComponents などの基盤となる HTTP クライアントライブラリ上でシンプルなテンプレートメソッド API を公開します。 RestTemplate は、頻度の低いケースをサポートする一般化された exchange および execute メソッドに加えて、HTTP メソッドによる一般的なシナリオのテンプレートを提供します。
クエリパラメータは、必須でない場合に設定したりしなかったりと可変になりますが、どんな方法がいいのでしょう? クエリパラメータを設定するにはどんな方法がいいのか試してみることにしました。
e-StatのAPIをサンプルに使います。
サンプルに呼び出すAPIは、政府統計の総合窓口(e-Stat)−API機能の統計表情報取得を使います。
パラメータには以下の値を設定します。
パラメータ名 | 意味 | 設定する値 |
---|---|---|
appId | アプリケーションID | e-Statのサイトで取得したアプリケーションID |
openYears | 公開年月 | 202102 |
statsField | 統計分野 | 0204(人口移動) |
limit | データ取得件数 | 1 |
ControllerをでAPIを呼び出しちゃいます。
Serviceとかで呼ぶ方がいいと思いますが、とにかくパラメータをくっつける練習としてControllerでAPIを呼び出します。
package com.example.demo.controller; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import com.fasterxml.jackson.databind.JsonNode; @RestController @RequestMapping("/estat") public class EstatController { private static final String URL = "http://api.e-stat.go.jp/rest/3.0/app/json"; /** アプリケーションID. */ private String appId = "02b...e-Statのサイトで取得したアプリケーションID"; /** 公開年月. */ private String openYears = "202102"; /** 統計分野. */ private String statsField = "0204"; /** データ取得件数. */ private String limit = "1"; @GetMapping("/getStatsList") public JsonNode getStatsList() { RestTemplate restTemplate = new RestTemplate(); ResponseEntity<JsonNode> response = // .....ここに呼び出しの処理を書いていきます......... JsonNode jsonNode = response.getBody(); return jsonNode; } }
クエリパラメータをくっつける
- 環境
パラメータを1つ1つ設定する
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException 指定された URL で GET を実行して、エンティティを取得します。レスポンスは変換され、ResponseEntity に保存されます。 パラメーター: url - URL responseType - 戻り値の型 uriVariables - テンプレートを展開する変数
上記のgetForEntityを使って書きました。
@GetMapping("/getStatsList") public JsonNode getStatsList() { RestTemplate restTemplate = new RestTemplate(); // 1. getForEntityでクエリパラメータ一つ一つを設定して、APIを呼び出す. ResponseEntity<JsonNode> response = restTemplate.getForEntity( URL + "/getStatsList?appId={appId}&openYears={openYears}&statsField={statsField}&limit={limit}", JsonNode.class, appId, openYears, statsField, limit); JsonNode jsonNode = response.getBody(); return jsonNode; }
UriComponentsBuilderでパラメータを設定する
public <T> ResponseEntity<T> exchange(RequestEntity<?> entity, Class<T> responseType) throws RestClientException 指定された RequestEntity で指定されたリクエストを実行し、レスポンスを ResponseEntity として返します。通常、たとえば RequestEntity の静的ビルダーメソッドと組み合わせて使用されます。 パラメーター: entity - リクエストに書き込むエンティティ responseType - 戻り値の型
UriComponentsBuilderでパラメータを設定して、上記のexchangeでAPIを呼び出しました。
queryParamでパラメータ名と値を設定する
public UriComponentsBuilder queryParam(String name, Object... values) 指定されたクエリパラメーターを追加します。パラメーター名と値の両方に、後で値から展開される URI テンプレート変数を含めることができます。値が指定されていない場合、結果の URI にはクエリパラメーター名のみが含まれます。"?foo=bar" の代わりに "?foo"。 パラメーター: name - クエリパラメーター名 values - クエリパラメーター値
UriComponentsBuilderのqueryParamを使ってクエリパラメータを設定しました。
@GetMapping("/getStatsList") public JsonNode getStatsList() throws URISyntaxException { RestTemplate restTemplate = new RestTemplate(); // 1. エンドポイントからUriComponentsBuilderを作成する. UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(URL + "/getStatsList"); // 2. クエリパラメータを設定して文字列化する. String uri = builder.queryParam("appId", appId) .queryParam("openYears", openYears) .queryParam("statsField", statsField) .queryParam("limit", limit) .toUriString(); // 3. パラメータを設定したURLからRequestEntityを作成する. RequestEntity<Void> requestEntity = RequestEntity.get(new URI(uri)).build(); // 4. exchangeでAPIを呼び出す. ResponseEntity<JsonNode> response = restTemplate.exchange(requestEntity, JsonNode.class); JsonNode jsonNode = response.getBody(); return jsonNode; }
queryParamsでMultiValueMapを使って設定する
public UriComponentsBuilder queryParams(@Nullable MultiValueMap<String, String> params) 複数のクエリパラメーターと値を追加します。 パラメーター: params - パラメーター
UriComponentsBuilderのqueryParamsを使ってクエリパラメータを設定しました。
1つのキーに複数の値を設定できるMap(org.springframework.util.MultiValueMap)
MultiValueMapはよく使うMapとはちょっと違うもののようです。
@GetMapping("/getStatsList") public JsonNode getStatsList() throws URISyntaxException { RestTemplate restTemplate = new RestTemplate(); // 1. エンドポイントからUriComponentsBuilderを作成する. UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(URL + "/getStatsList"); // 2. Mapにクエリパラメータを設定する. MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.add("appId", appId); params.add("openYears", openYears); params.add("statsField", statsField); params.add("limit", limit); // 3. Mapでクエリパラメータを設定して文字列化する. String uri = builder.queryParams(params).toUriString(); // ...これ以降は「queryParamでパラメータ名と値を設定する」と同じ...
AWSのSecrets Managerに大切な情報を登録する
認証に使うAPIキーやWebhookに設定したSecretなどをLambda関数で使うことがあります。これらは大切な情報なのでSecrets Managerに登録して、Lambda関数から取得して使うようにします。
- AWSのコンソール > [Secrets Manager]
-
- シークレットの種類: APIキーや各種ユーザ情報などは「その他のシークレット」を選択
- シークレットのペア: 大切な情報の名前(シークレットキー)と大切な情報の値を設定、複数設定してもOK
- 暗号化キー: DefaultEncryptionKey
- [保存]ボタンでシークレットを作成する
AWSのSecrets Managerを使った事例
Lambdaの実行権限とAPI Gatewayを作成する
ポリシーがよくわからないのでちょっと勉強します。
IAMのポリシーを作成する
- AWSマネジメントコンソールにある[IAM] > サイドメニューの[ポリシー]
- [ポリシーの作成]ボタンで作成画面を開きます。
- [JSON]タブを開いて権限の設定内容を入力します。
- この設定は作成後でも変更できます。
- 権限の対象となる
Resource
はワイルドカード*
で広く設定すると簡単ですが、セキュリティを意識して細かく狭めて設定します。
- [次のステップ:タグ]ボタンから次の画面でタグを設定(設定しなくてもOK) > [次のステップ:確認]ボタン
- 確認画面へ遷移して[名前(必須)]と[説明(任意)]を入力します。
- [ポリシーの作成]ボタンでポリシーを作成して一覧画面に戻ります。
IAMのロールを作成する
- AWSマネジメントコンソールにある[IAM] > サイドメニューの[ロール]
- [ロールの作成]ボタンで作成画面を開きます。
- [AWSサービス] > [Lambda]を選択後に[次のステップ: アクセス権限]ボタンで次の画面を開きます。
- [Attach アクセス権限ポリシー]の[ポリシーのフィルタ]で作成したポリシーを検索して選択後に[次のステップ: タグ]ボタンで次の画面を開きます。
- [タグの追加 (オプション)]は任意なので設定せずに[次のステップ: 確認]ボタンで確認画面を開きます。
- [ロール名]を入力して[ロールの作成]ボタンでロールを作成して一覧画面に戻ります。
Lambda関数を作る
実装を後回しにして関数だけ作ります。
- [AWS マネジメントコンソール]から[Lambda]の画面を開く
- [関数の作成]ボタンで作成画面を表示する
- [一から作成]を選択して以下を設定して[関数の作成]ボタンで関数を作成する
- 関数名 : 任意の名前
- ランタイム : 使いたいもの
- 実行ロール : 既存のロールを使用する
- 既存のロール : 作成したロールを選択
コードには初期コードがあるのでそのまま。実装はAPI Gateway作成後にやります。
import json def lambda_handler(event, context): # TODO implement return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
API Gatewayを作成する
- 参考
作成する種類は上記の参考リンクを読んで決めました。
HTTP APIで作成する
- [HTTP API]の[構築]ボタンで次の画面へ
- [統合を追加] > [Lambda] > [Lambda 関数]で作成したLambda関数を選択
- [API 名]に任意の名前を設定して[次へ]ボタンで[ルートを設定]画面へ
- 以下を設定して[次へ]ボタンで[ステージを定義]画面へ
- メソッド : POST
- リソースパス :
/{Lambda関数名}
- 統合ターゲット : {Lambda関数名}
- [ステージを追加]ボタンで以下を追加して[次へ]ボタンで[確認して作成]画面へ
- ステージ名 :
$default
- 自動デプロイ : ON
- ステージ名 :
- [作成]ボタンで作成する
- 「{Lambda関数名}のステージ」一覧の[URLを呼び出す]列に「呼び出しURL」が表示される
- curlコマンドを使ってAPI Gatewayを呼び出してAPI GatewayがLambda関数を呼び出せることを確認する
curlコマンドのオプション | 意味 |
---|---|
-X | HTTPメソッドを指定する |
-H | HTTPヘッダを指定する |
# Lambda関数の初期コードに書いてある「Hello from Lambda!」が返却される $ curl -X POST -H 'Content-Type:application/json' {呼び出しURL}/{リソースパス} % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed } 100 20 100 20 0 0 50 0 --:--:-- --:--:-- --:--:-- 50"Hello from Lambda!" # 失敗例) 「リソースパス」をくっつけ忘れると「Not Found」になるので注意 $ curl -X POST -H 'Content-Type:application/json' {呼び出しURL} % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 23 100 23 0 0 287 0 --:--:-- --:--:-- --:--:-- 291{"message":"Not Found"}
Cloud9のPython環境を作る
最初「Cloud9を作成する」と書いて思いました。Cloud9を作っているのはAWSでぽんすけにそんな能力は塵ほどもありません。作るのは「Cloud9の環境」です。
Cloud9の環境を作る
- AWSのマネジメントコンソール > [Cloud9] > [Create enviroment]ボタン
- [Name]に環境名を入力 > [Next step]ボタン
- 設定項目を選択(VPCやSubnetも設定できる) > [Next step]ボタン
- 内容を確認 > [Create enviroment]ボタン > しばし待つ
- できた!
設定項目例
設定項目 | 設定値 | 参考になりそうなサイト |
---|---|---|
Environment type | Create a new EC2 instance for environment (direct access) | |
Instance type | t2.micro (1 GiB RAM + 1 vCPU) | |
Platform | Amazon Linux 2 下の表を参考に決める |
Amazon Linux は何系のディストリビューションに該当するのか – Amazon Web Service(AWS)導入開発支援 |
Cost-saving setting | After 30 minutes(default) | 【AWS Cloud9 の使い方】最初に覚えておくべき機能まとめ | 初心者向け完全無料プログラミング入門 |
Platform | 似たようなOS | デフォルトのPython |
---|---|---|
Amazon Linux | RHEL5-6 / CentOS5-6 | Python3.6 |
Amazon Linux 2 | RHEL7 / CentOS7 | Python3.7 |
Ubuntu Server | Ubuntu | わかったら書く |
Pythonとpipのバージョンを設定する
Cloud9の環境によってPythonやpipのバージョンは違うので、使いたいバージョンに設定していきます。
# Pythonのバージョンは3.8がいいな $ python -V Python 3.7.9 # pipも古いな・・・・ $ python -m pip -V pip 9.0.3 from /usr/lib/python3.7/site-packages (python 3.7)
pyenv使ってバージョンをPython3.8にする
参考 : pyenvのインストール、使い方、pythonのバージョン切り替えできない時の対処法 - Qiita
# pyenvをGitHubからCloneする $ git clone https://github.com/pyenv/pyenv.git ~/.pyenv Cloning into '/home/ec2-user/.pyenv'... remote: Enumerating objects: 67, done. remote: Counting objects: 100% (67/67), done. remote: Compressing objects: 100% (54/54), done. remote: Total 18814 (delta 26), reused 20 (delta 7), pack-reused 18747 Receiving objects: 100% (18814/18814), 3.80 MiB | 16.20 MiB/s, done. Resolving deltas: 100% (12760/12760), done. # バージョンを確認する $ ~/.pyenv/bin/pyenv --version pyenv 1.2.23 # .bashrcに $ vi ~/.bashrc # PATHを通す & pythonの実行パスの差し替えられるように設定して、 $ cat ~/.bashrc | grep pyenv export PATH="$HOME/.pyenv/bin:$PATH" eval "$(pyenv init -)" # 反映する $ source ~/.bashrc # PATHを確認して $ printenv PATH | sed -e 's/:/:\n/g' | grep pyenv /home/ec2-user/.pyenv/shims: /home/ec2-user/.pyenv/bin: # バージョンを確認する $ pyenv --version pyenv 1.2.23 # インストールできるPython3.8を確認する $ pyenv install -l | grep 3.8. 3.8.0 3.8-dev 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 miniconda-3.8.3 miniconda3-3.8.3 miniconda3-3.8-4.8.2 miniconda3-3.8-4.8.3 miniconda3-3.8-4.9.2 # Python3.8.7をインストールする $ pyenv install 3.8.7 Downloading Python-3.8.7.tar.xz... -> https://www.python.org/ftp/python/3.8.7/Python-3.8.7.tar.xz Installing Python-3.8.7... WARNING: The Python bz2 extension was not compiled. Missing the bzip2 lib? Installed Python-3.8.7 to /home/ec2-user/.pyenv/versions/3.8.7 # Yumをアップデートして、 $ sudo yum -y update ...省略... Complete! # PythonのインストールでWARNINGになった不足なものをインストールする $ sudo yum -y install bzip2 Loaded plugins: extras_suggestions, langpacks, priorities, update-motd 229 packages excluded due to repository priority protections Package bzip2-1.0.6-13.amzn2.0.2.x86_64 already installed and latest version Nothing to do # Python3.8に切り替える $ pyenv versions * system (set by /home/ec2-user/.pyenv/version) 3.8.7 $ pyenv global 3.8.7 # 使いたいバージョンに設定できた! $ python -V Python 3.8.7 $ python -m pip -V pip 20.2.3 from /home/ec2-user/.pyenv/versions/3.8.7/lib/python3.8/site-packages/pip (python 3.8)
プロジェクト設定をする
- 歯車マーク > [Preferences] > [Project Settings]
- [Code Editor (Ace)]
- [Soft Tabs]:ONにするとタブキーで指定された数のスペースが挿入される
- [On Save, Strip Whitespace]:ONにすると保存でファイルから不要なスペースとタブを削除する
- [Python Support]
- [Enable advanced (jedi) Python code completion]:ONにするとコードが補完を有効になる
基本のライブラリをインストールする
どこでも使いそうな以下をインストールしておきます。
ライブラリ | 概要 | 参考になるサイト |
---|---|---|
wheel | Pythonのパッケージの形式であるwheel(ホイール) | Wheel vs Egg — Python Packaging User Guide ドキュメント |
ikp3db | Python3でデバックするのに使う | ikp3db · PyPI |
$ python -m pip install wheel Collecting wheel Downloading wheel-0.36.2-py2.py3-none-any.whl (35 kB) Installing collected packages: wheel Successfully installed wheel-0.36.2 $ python -m pip install ikp3db Collecting ikp3db Using cached ikp3db-1.4.1.tar.gz (24 kB) Building wheels for collected packages: ikp3db Building wheel for ikp3db (setup.py) ... done Created wheel for ikp3db: filename=ikp3db-1.4.1-cp38-cp38-linux_x86_64.whl size=40854 sha256=ce8b91f29f673387c3b155c9c0d519803df67e34d7eaec552d11038c401203d2 Stored in directory: /home/ec2-user/.cache/pip/wheels/32/db/4b/66c26f1ee8960dc38bf6d62a4d79eb91d8e360e7b1a7ca2137 Successfully built ikp3db Installing collected packages: ikp3db Successfully installed ikp3db-1.4.1 $ python -m pip list Package Version ---------- ------- ikp3db 1.4.1 pip 20.2.3 setuptools 49.2.1 wheel 0.36.2
Lambda関数をインポートする
Lambda関数をCloud9で実装していくためにCloud9の外部で作ったLambda関数をインポートします。
AWS Resourcesを使う場合
[AWS Resources]が表示されない場合は、設定をすることで表示することができます。Cloud9からAWS Resources消失 - Qiitaでやり方を確認してください。
- [AWS Resources] > [Lambda] > [Remote Functions] > 対象のLambda関数を選択して右クリック > [import...]
AWS Toolkitを使う場合
- [AWS] > 対象のリージョン > [Lambda]
Lambda関数用にモジュールをインストールする
Lambda関数用にモジュールをインストールする場合は、関数のディレクトリないにインストールします。そうすれば、インストールしたモジュールもろともLambda関数をデプロイすることができます。
# Lambdaのディレクトリに移動する $ cd backlog_none_assignee/ # 場所を指定してパッケージをインストールする(ここではrequests) $ python -m pip install --target=./ requests # 仮想環境にインストールされているパッケージを確認する $ pip freeze certifi==2020.12.5 chardet==4.0.0 idna==2.10 requests==2.25.1 urllib3==1.26.3
とにかくやってみる!Vue.jsに突入し隊
※.このページの内容はとにかく急いで勉強しているので、正確でないことが多々あります。こんな奴いるんだ程度に見てください。
何が何だかわからないので、とにかくやってみます。
初めてすぎて何が何だかわかんない!から公式サイトの説明をそのまま実行してみます。
まずは、さっと基本情報をみておきます。
Node.jsは、JavaScriptをサーバでも動かせるすごい環境です。
Node.js はスケーラブルなネットワークアプリケーションを構築するために設計された非同期型のイベント駆動の JavaScript 環境です。
Vue.jsは、ユーザーインターフェイスを構築するためのフレームワークです。
Vue (発音は /vju:/、view と同様) は、ユーザーインターフェイスを構築するためのプログレッシブフレームワークです。他のモノリシックなフレームワークとは異なり、Vue は少しずつ適用していけるように設計されています。中核となるライブラリはビュー層だけに焦点を当てており、使い始めるのも、他のライブラリや既存のプロジェクトに統合することも容易です。
プログレッシブフレームワークは、必要な時に必要な分だけ使えるようにしたフレームワークです。
プログレッシブフレームワークは,必要になった時に問題解決するライブラリを適宜導入して問題を解決するという姿勢を持っています。最初に始めるときは小さく,大規模になるにつれて適切なライブラリやツールを導入することで大きく対応できる柔軟性を持ちます。不必要な学習コストが発生することもありません。
第1回 プログレッシブフレームワーク Vue.js:Vue.js入門 ―最速で作るシンプルなWebアプリケーション|gihyo.jp … 技術評論社
Vue.jsをとにかくやってみる
とにかく突貫でやるので、Vue.jsはインストールせずにCDNを使います。
プロトタイピングや学習を目的とする場合は、以下のようにして最新バージョンを利用できます:
<script src="https://unpkg.com/vue@next"></script>
はじめに
はじめに | Vue.jsをやってみます。
ここで新しい属性が出てきました。v-bind 属性はディレクティブと呼ばれます。ディレクティブは Vue によって提供された特別な属性であることを示すために v- 接頭辞がついています。
ひたすら読んで書き写して実行して各ディレクティブを体感しました。
ディレクディブ | 意味 | 参考サイト |
---|---|---|
v-bind | レンダリングされた DOM に特定のリアクティブな振る舞いを与える | |
v-on | イベントリスナをアタッチしてインスタンスのメソッドを呼び出す | |
v-model | フォームの入力とアプリケーションの状態を双方向にバインディングする | |
v-if | ブロックを条件に応じて描画 | 条件付きレンダリング — Vue.js |
v-for="要素 in 配列" | 配列に基づいて、アイテムのリストを描画 | リストレンダリング — Vue.j |
アプリケーションインスタンス
data
のプロパティは、インスタンスが作成時に存在した場合にのみ リアクティブ (reactive) です。次のように新しいプロパティを代入すると:
vm.b = 'hi'
b
への変更はビューへの更新を引き起こしません。あるプロパティがのちに必要であることがわかっているが、最初は空または存在しない場合は、なんらかの初期値を設定する必要があります。
インスタンスのライフサイクル表がありました。頑張って覚えていきます。
初めてPostmanを使ってみた記録
- Postmanをインストールする
- リクエストを整理するためのCollectionsを作成する
- 環境変数を作成する
- 認証情報をリクエストにくっつける
- リクエストをAPIにおくる
- セッションを設定する(ここは結果としてうまくいきませんでした、またいつか挑戦したいです。)
Postmanをインストールする
リクエストを整理するためのCollectionsを作成する
これからPostmanを使っていくといろんなリクエストを作ることになります。そんなたくさんのリクエストを整理するためのフォルダのようなものがCollectionです。
Postman's collection folders make it easy to keep your API requests and elements organized.
- [Cllections] > [+New Collection] > [Name]にAPIがわかるような名前を指定 > [Create]ボタンでCollectionを作成する
- 作成したCollectionの右にある[...] > [Add Folder] > [Name]にローカル環境がわかるような名前を指定 > [Create]ボタンでフォルダを作成する
Collectionsにリクエストを追加する
- 環境
- Windows10 Pro バージョン1909
- Postmat v7.36.1
APIにつなぐには認証が必要です。認証に何を使うかAPIによって異なります。初めてのリクエストのサンプルとして、認証してトークンを取得します。
- 作成したフォルダの右にある[...] > [Add Request] > [Name]にやることがわかるような名前を指定 > [Save to {フォルダ名}]ボタンで作成する
- APIに合わせて[Parameters]や[Headers]を設定する
- [Send]ボタンでリクエストをおくると[Body]にレスポンスが表示される
環境変数を作成する
APIのURLやらユーザ名やらリクエストにはよく使うものがあります。そんなものは環境変数にして便利に使えるようにします。
- URLやパラメータの値などで
{{変数名}}
の形式で記載すると設定した値が利用される
なお、INITIAL VALUE と CURRENT VALUE の違いですが、Postman のサーバに情報が送られるか否かになります。
チームでテストしていたり、複数台の PC でテストするときは INITIAL VALUE を使うと便利なのかもしれません。 ですが、検証用 PC は 1台しかないですし、私はボッチですしおすし。(・ω・) セキュリティを高めるためにもっ!CURRENT VALUE を使っています!!! セキュリティを高めるためだからねっ!(大事なことは二度言うスタイル)
任意のCllection専用に環境変数を設定できます。
Postman は変数を使うことができます。変数には Global/Collection/Environment 変数があり、Global は Postman 全体で利用できる変数。Collection は Collection レベルで参照できる変数。Environment は各 Environment(タブ)で利用できる変数です。
- 作成したCollectionの右にある[...] > [Edit]でCollectionの設定ダイアログを開く
- [Variables]タブでCollection専用の環境変数を設定する
認証情報をリクエストにくっつける
APIにつなぐときに毎回認証情報が必要となることはよくあります。その認証情報をリクエストに自動でくっつけられるようにします。
リクエスト単位で認証を設定することもできますが、API単位でCollectionを作って、API単位で設定することもできます。どちらも[Authorization]タブで設定します。
ヘッダーにBearerトークンをくっつける
先ほどのように取得したトークンをリクエストに設定して認証できるようにします。
Postman を複数環境で使う場合にアクセストークン取得方法を簡略化する | Developers.IOを参考に設定していきます。
Bearerトークンを直接設定することもできるようですが、トークンの期限切れとなった時に手動で再設定するのは面倒くさいのでOAuth2.0で設定しました。
リクエスト単位ではなくCollection単位で認証を設定した場合は、各リクエストの[Authorization]タブの[Type]には「Inherit auth from parent」を設定します。
パラメータにAPIキーをくっつける
認証情報によってはパラメータにくっつけることもあります。
例えば政府統計の総合窓口(e-Stat)のAPIではアプリケーションIDをappId
リクエストパラメータに毎回指定する必要があります。
各APIを利用するには、アプリケーションIDを必ず指定する必要があります。利用登録を行い、アプリケーションIDを取得して下さい。
Collection共通の認証設定としてe-StatのAPIへのリクエストにアプリケーションIDをくっつけます。
リクエストをAPIにおくる
ここまでで覚えたことを使っていろんなAPIを使ってみます。
パスパラメータを指定する
ここではBacklogAPIのプロジェクトの状態一覧の取得 | Backlog Developer API | Nulabをやってみます。
パスパラメータはAPIのエンドポイントのパスに設定されるパラメータです。
URL /api/v2/projects/:projectIdOrKey/statuses
「プロジェクトの状態一覧の取得」の場合はprojectIdOrKey
がパスパラメータになります。
Parameter Name | Type | Description |
---|---|---|
projectIdOrKey | String | Project ID or Project Key |
- (事前準備)使っているBacklogでAPIキーを発行する
- Postmanを起動する
- Backlog用にCollectionsを作成する
- URLとして
{{URL}}/projects/:projectIdOrKey/statuses
を入力するとパスパラメータの入力欄が表示される - パスパラメータに取得したいプロジェクトIDを指定する
- [Send]ボタンでプロジェクトの状態一覧の取得 | Backlog Developer API | Nulabの「レスポンスボディ」みたいなのが取れるか確認する
配列形式のクエリパラメータを設定する
Backlog APIを使って課題一覧の取得 | Backlog Developer API | Nulabをやってみます。
パラメータで配列を指定することがあります。例えばプロジェクトIDの配列する場合にはパラメータ名がprojectId
ではなくprojectId[]
となり[]
がくっ付きます。
今回使ってみる「課題一覧の取得」ではクエリパラメータのに配列を指定します。
以下は「課題一覧の取得」クエリパラメータの一部です(出典 : 課題一覧の取得 | Backlog Developer API | Nulab)。[]
がついているのが配列で、[]
がついてないのが配列でないものです。
パラメーター名 | 型 | 内容 |
---|---|---|
projectId[] | 数値 | プロジェクトのID |
issueTypeId[] | 数値 | 種別のID |
...省略... | ...省略... | ...省略... |
keyword | 文字列 | 検索キーワード |
- 使っているBacklogでAPIキーを発行する
- Postmanを起動し、Backlog用にCollectionsを作成して環境変数や認証情報を設定する
- 「課題一覧の取得」用のGETリクエストをCollectionに作成する
- Query Paramsの[KEY]に
projectId[]
を、[VALUE]に1つ目のプロジェクトIDを入力する - 次の行に[KEY]に
projectId[]
を、[VALUE]に2つ目のプロジェクトIDを入力する - 他のクエリパラメータを設定する
- [Send]ボタンで課題一覧の取得 | Backlog Developer API | Nulabの「レスポンスボディ」みたいなのが取れるか確認する
セッションを設定する(ここは結果としてうまくいきませんでした、またいつか挑戦したいです。)
呼び出すAPIによっては、画面でログインしてそのセッションを設定て呼び出さねばならないことがあります。そんなときのやり方をやってみました。
Postman InterceptorをChromeに追加する
4. Interceptor(https://www.getpostman.com/docs/capture)
Interceptor機能はChromeを利用して、ブラウザ内で発生したリクエストを自動でPostman Historyに登録する機能です。
- Postman Interceptor - Chrome ウェブストアを表示する
- ピンマークを押下するChromeのツールバーにアイコンが表示される
PostmanでリクエストとCookieをキャプチャする
- 右上にある衛星アイコンでダイアログを表示 > [Cokies] > [Install Interceptor Bridge]でInterceptor Bridgeをインストールする
- [Request] > [Source] > 「Interceptor」選択 > [Capture Requests] > ON > [Save Requests to] > 設定対象にしたいCollectionなどを設定
- [Cokies] > [Capture cookies] > ON > [Domains] > 対象のドメインを入力 > [Add Domain]ボタンで追加
画面からログインしてリクエストを送る
Sublime TextにターミナルとGit環境を作る
Windows
近いうちにやってみよう
Mac
Sublime Textでターミナルを使えるようにする
候補のプラグイン | 難点 | 紹介しているサイト |
---|---|---|
TerminalView | clear コマンドが使えない |
SublimeText3でターミナルを扱うプラグイン「TerminalView」 | プログラマーを目指す 「中卒」 男のブロ |
Terminal | 外部でターミナルを開く WindowsはPowerShellになる・・・GitBashがいい |
Sublime Textから手軽にターミナルを開くことができるパッケージ「Terminal」の使い方 |
TerminalViewをインストールする
Sublime Textで完結したいのでTerminalViewをインストールしてみます。
Command + Shift + P
> 「install」を入力すると候補が出る > [Package Control: Install Package]選択- 「TerminalView」を入力すると候補が出る > [TerminalView]選択してインストールする
- ターミナルタブを起動する
-
Command + Shift + P
> 「terminal」を入力すると候補が出る > [Terminal View: Open Bash Terminal]選択 - ターミナルタブが起動する
ショートカットキーを設定する
デフォルトのままだとコピペのショートカットが使えないのでCommand + C
とCommand + V
のように設定します。
- [Sublime Text] > [Preferences] > [Package Setting] > [TerminalView] > [Keybindings]
- Userに以下を貼り付けて保存する
[ {"keys": ["command+n"], "command": "new_file", "context": [{"key": "setting.terminal_view"}]}, {"keys": ["command+v"], "command": "terminal_view_paste", "context": [{"key": "setting.terminal_view"}]}, {"keys": ["command+c"], "command": "terminal_view_copy", "context": [{"key": "setting.terminal_view"}]}, ]
ターミナル上でのショートカットが以下表のように変更されます。
ショートカット | 意味 |
---|---|
Command + N | 新規ファイルを開く |
Command + V | 貼り付け |
Command + C | コピー |
シェルをBashからZshに変える
TerminalViewのデフォルトはシェルがBashになっています。
使っているMacのターミナルはZshになっているので、合わせてZshにすることで設定(.zshrc)も同じものを読み込めます。
- Zshの場所を確認する
- Sublime Textで
Command + Shift + P
> 「terminal」を入力すると候補が出る > [Terminal View: Palette Commands]選択 - User側に以下を記載 > 保存
Command + Shift + P
> 「zsh」を入力すると候補が出る > [Terminal View: Open Zsh Terminal]選択- ターミナルタブがZshで起動するようになる
[ { "caption": "Terminal View: Open Zsh Terminal", "command": "terminal_view_open", "args" : {"title": "Terminal (zsh)", "cmd": "/bin/zsh -l"}, }, ]
ターミナルにGitのブランチを表示できるようにする
【macOS Catalina】MacのターミナルにGitブランチ名を表示させる | とむじそブログを参考にGitのブランチを表示できるようにします。
今回、こんな感じにしてみました。
# Gitのブランチをターミナルに表示する autoload -Uz vcs_info setopt prompt_subst zstyle ':vcs_info:git:*' check-for-changes true zstyle ':vcs_info:git:*' stagedstr "%F{magenta}!" zstyle ':vcs_info:git:*' unstagedstr "%F{yellow}+" zstyle ':vcs_info:*' formats "%F{cyan}%c%u[%b]%f" zstyle ':vcs_info:*' actionformats '[%b|%a]' precmd () { vcs_info } # ターミナルの表示形式設定 PROMPT=' %F{green}%~%f:%B$vcs_info_msg_0_%b $%f '
解決したい問題
- 実行した内容が画面からはみ出したら見られない・・・スクロールとかできない・・・不便。
- 全角文字が見えない入力できない・・・不便
何?Swaggerって?
お仕事で「APIの仕様書をすわっがーで作ってね」って言われました、既存の仕様書はYAMLファイルになっているのですが・・・何?Swaggerって?
- Swaggerは、OpenAPI仕様に基づいてREST APIの仕様書作成から構築を助けてくれるツールです。
- Serversを書く
- 認証を書く
- 共通で使うオブジェクトや構造の定義を書く
- リクエストを書く
- レスポンスを書く
- YAMLファイルに保存する
Swaggerは、OpenAPI仕様に基づいてREST APIの仕様書作成から構築を助けてくれるツールです。
Swagger は RESTful APIを構築するためのオープンソースのフレームワークのことです。「Open API Initiative」という団体がRESTful APIのインターフェイスの記述をするための標準フォーマットを推進していて、その標準フォーマットがSwaggerです。Swaggerには多くの便利なツールが提供されていることもあり、多くのメリットを享受できそうです。
仕様書のフォーマット?
Swaggerは、OpenAPI仕様(以下OAS)と言われる、REST APIを定義するための標準仕様にもとづいて構築された一連のオープンソースツールです。REST APIの設計、構築、文書化、および使用に役立つ機能を提供します。
フォーマットだけじゃなくて、仕様書から構築までできるらしいです。
OpenAPIは、REST APIの記述フォーマットです。
What Is OpenAPI?
OpenAPI Specification (formerly Swagger Specification) is an API description format for REST APIs.
(省略)
What Is Swagger?
Swagger is a set of open-source tools built around the OpenAPI Specification that can help you design, build, document and consume REST APIs.
About Swagger Specification | Documentation | Swagger - swagger.io
弱い英語力から以下程度に解釈しました。
Swagger3.0は、OpenAPI?
OpenAPI は Swagger 3.0
Swagger 3.0 から OpenAPI に名前が変わったため、 OpenAPI 3.0 は Swagger 3.0 でもあります。
もともと他にも API 周りのインターフェース定義ができるルールが存在してたのですが、 Swagger が晴れて標準となったようです。 他には API Blueprint などがあるようですが、僕もそこまで詳しくはありません。 OpenAPI の仕様の、現時点での最新は
3.0.2
です。 仕様書はこちらにあります。https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md
Swaggerは、2から3でなにやらずいぶん変わったようですが・・・初めてなのでとにかくSwagger3.0を使えるようになることを優先します。
ブラウザで仕様書を見て書けます。
ツールをインストールしたり環境を構築したりしなくてもすぐに使えそうです。
- Swagger Editorをブラウザで表示する
- [File] > [Import file] > 既存の仕様書をインポートする
- 仕様書に定義されたインターフェースが横に見やすく表示された!
- 左のYMLを修正すると右側に反映される
実行もできます。
呼出し先が起動していれば、必要な情報を入力して実行することもできました。
GitにあるSwaggerのファイルをプレビューできるChrome拡張機能があるらしいです。
Serversを書く
APIのサーバを定義します。 サーバには、本番環境やらテスト環境など複数を定義できます。
認証を書く
試しに「OAuth2.0で取得したBearerのトークンをヘッダにAuthorizationで設定する」みたいに書こうとした・・・ら、なんか言われました。
Header parameters name "Authorization" are ignore. Use the `securitySchemes` and `security` sections instead to define authorization.
どうやら、ヘッダやパラメータに「Authorization」を設定しても無視されるようです。
You have used a restricted value as the name of a header parameter. The values Accept, Content-Type, and Authorization are restricted values and should not be used as the header name. A header with any of these values as the header name is ignored.
Header parameter with the name 'Authorization' is ignored - apisecurity.io
では、どう書くのか?調べながら書いてみました。
Bearer スキームを書く
トークンを利用した認証・認可 API を実装するとき Authorization: Bearer ヘッダを使っていいのか調べた - Qiitaを読むとやりたいことは「Bearer スキーム」というものでした。
- OpenAPI (Swagger) 3.0 で Bearer トークンの使用を定義する | Articles | Riotz.works
- Bearer Authentication - swagger.io
共通で使うオブジェクトや構造の定義を書く
APIのパラメータやレスポンスで共通のオブジェクトや構造を使うことがあります。
そんな共通のオブジェクトや構造は、components
配下に定義して$ref
を使うことで参照できます。
Components Section
Often, multiple API operations have some common parameters or return the same response structure. To avoid code duplication, you can place the common definitions in the global components section and reference them using $ref.
リクエストを書く
各リクエストのエンドポイントは、paths
配下に定義していきます。
paths: /{エンドポイント}: summary: {短い説明文} description: > {長い説明文、 ここにはMarkdownで複数行にわたって書くことができます。}
type
に定義するデータ型は、Data Typesに記載されているものを使用します。
type | 意味 | 参考になりそうなサイト |
---|---|---|
string | 文字列 | |
number | 整数と浮動小数点付き数値 | 浮動小数点って何? - Qiita |
integer | 整数 | |
boolean | 真偽値 | |
array | 配列 | |
object | オブジェクト |
パラメータを定義する
Describing Parameters
In OpenAPI 3.0, parameters are defined in the parameters section of an operation or path. To describe a parameter, you specify its name, location (in), data type (defined by either schema or content) and other attributes, such as description or required.
パラメータといっても種類があります。基本の書き方は同じで- in
に指定する値を変えて書き分けます。各パラメータがどんな感じのものかはSwaggerの上記ドキュメントページにサンプルがあってわかりやすいです。
パラメータの種類 | inに指定する値 | 参考になりそうなサイト |
---|---|---|
パスパラメータ | path | |
クエリパラメータ | query | |
ヘッダパラメータ | header | HTTP ヘッダー - HTTP | MDN |
Cookieパラメータ | cookie |
フォーマットはこんな感じです。
parameters: - in: pathかqueryかheaderかcookie name: パラメータ名やヘッダのフィールド名 schema: type: パラメータの型 enum: - パラメータの値が特定の値しか受付ない場合に指定する example: パラメータの例 required: 必須かどうかをboolで指定、パスパラメータは省略できないのでtrueを指定する description: パラメータの説明文 allowEmptyValue: パラメータ名の指定のみで値がなくてもいいかどうかをbookで指定する
レスポンスを書く
Describing Responses - swagger.ioを参照すると、
各レスポンスは以下のようにresponses
配下にHTTPステータスコード毎に定義していくようです。
responses: {ステータスコード}: description: {説明} content: {メディアタイプ}: schema: # ここからレスポンスボディを定義する type: {レスポンスボディのタイプ「object」「array」} properties: {プロパティ名}: type: {プロパティのタイプ} description: {プロパティの説明}
レスポンスのexampleを複数定義する
APIのレスポンスで同じHTTPステータスにexampleを複数定義したいということがあります。そんな場合の書き方です。
Adding Examples - swagger.ioを参考にするとフォーマットはこんな感じです。
paths: #...省略... responses: 'HTTPステータスコード': #...省略... examples: # <<<複数定義する場合は「example」ではなく「s」をつけて「examples」になります。 examples1: summary: 説明文、例えば「データがある場合」 value: # ここ以降にレスポンスの例を記載します。 examples2: summary: 説明文、例えば「データがない場合」 value: # ここ以降にレスポンスの例を記載します。
同じHTTPステータスで、exampleだけではなく返却するオブジェクトを複数定義することもできるようです。
Swaggerで、とあるapiのレスポンスにおいて、「同じステータスコードを返すんだけれど、bodyの内容が違う場合がある」時、SwaggerのoneOfという書き方で対応できます。(swagger3.0以上だったはず)
YAMLファイルに保存する
ブラウザ上で書いたものをローカルにダウンロードしてYAMLファイルとして保存します。