Contenu connexe Similaire à appengine ja night #24 Google Cloud Endpoints and BigQuery (20) appengine ja night #24 Google Cloud Endpoints and BigQuery2. 自己紹介
Ryo Yamasaki
• Blog
• http://blog.vier.jp/
• Twitter
• @vierjp
• 仕事
• Javaでサーバーサイドの開発がメイン
• 前職はスマホ向けサービス事業者の技術リーダー
• Google App Engineを中心にCloud StorageやBigQuery等、
Google Cloud Platformのプロダクトを利用していました。
• 個人でAndroidアプリ公開してます
• WiFi Tunes Sync
2
5. Google Cloud Endpoints
通信周りの実装が不要になります
Endpointsがカバーする範囲
クライアント
サーバー側
GET・POSTパ
必要な値を リクエストを リクエストを
ラメーターを
取得
送信
受け取る
セット
リクエスト内
サーバー側
API
容に基づい
クライアント・ライブラリ
Framework
て処理
オブジェクト
取得した結 JsonをParse
レスポンスを をJson形式
果を基に処 してオブジェ
受け取る
に変換して
理を行う
クトにセット
返す
5
13. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
Androidクライアントを作る Androidクライアントを作ら
場合
ない場合
2.右クリックから
AppEngineの
「Generate
AppEngine
1.Androidプロジェクトを作 「Webアプリケーションプロ
Backend」
成
ジェクト」
でサーバー側プロジェクト
を作成
作成
13
14. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
@Api(name = "appData", version = "v1", description = "AppData API description v1")
public class AppDataV1Endpoint {
コード例
IDを指定してデータを1件取得する処理
@ApiMethod(name = "data.get", path = "data/get/{id}”)
public AppDataV1 get(String id) throws ServiceException {
@Named("id") String id) throws ServiceException {
try {
// Entityのget処理
Key key = KeyFactory.createKey("AppData", id);
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
Entity entity = ds.get(key);
// 返却するDTOを生成してEntityの値を詰め替える
・ポイント
AppDataV1 result = entity2Dto(entity); ・@Api、@ApiMethod、@Namedアノテーション
return result; ・例外をthrowして呼び出し元にエラーを通知する
} } catch (EntityNotFoundException e) {
// IDに対応するデータが見つからない場合のエラー処理
throw new NotFoundException("id:" + id + " is not found.");
} catch (Exception e) {
// 予期しないシステムエラーが発生した場合のエラー処理
throw new InternalServerErrorException(e);
}
}
14
15. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• 用意されているアノテーション
• @Api
• クラスに定義する
• API群の一つのバージョンを示す (ex. Google Drive API v1)
• 一つのAPIを複数バージョン定義する場合は別のクラスにする
• @ApiMethod
• メソッドに定義する
• 個別のAPIを示す (ex. Google Drive APIの「drive.files.list」)
• @Named
• メソッド引数に定義する
• @ApiMethodのPathで「引数との対応」を指定しないなら必須
• Nullableを指定するなら必須
• @Nullable
• メソッド引数に定義する
• メソッド引数の値としてNullを許可する場合に指定
15
16. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• @Api
@Api(name = "appData", version = "v1", description = "AppData API description v1”)
・JavaScript
gapi.client.appData.data.get({'id':$scope.id}).execute(function(response,rawResponseJson) {
// callback
});
・Android
AppData.Builder builder = new AppData.Builder(AndroidHttp.newCompatibleTransport(), new
GsonFactory(), null);
AppData appDataApi = CloudEndpointUtils.updateBuilder(builder).build();
AppDataV1 response = appDataApi.data().get(id).execute();
return response;
16
17. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• @ApiMethod
@ApiMethod(name = "data.get", path = "data/get/{id}”)
@ApiMethod(name = "data.put", path = "data/put”)
・ポイント
・nameはAPIExplorerに表示される名前や
クライアントライブラリのメソッド(関数)名に影響するので
ルールを決めてわかりやすい名前をつけると良いかと。
・Pathは内部的に実行されるAPIのPathに影響する。
→気にしないなら定義しなくてもOK
・JavaScript
gapi.client.appData.data.get({'id':$scope.id}).execute(function(response,rawResponseJson) {
// callback
});
・Android
AppData.Builder builder = new AppData.Builder(AndroidHttp.newCompatibleTransport(), new
GsonFactory(), null);
AppData appDataApi = CloudEndpointUtils.updateBuilder(builder).build();
AppDataV1 response = appDataApi.data().get(id).execute();
return response;
17
18. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
エラーの通知に使用できる例外クラス
BadRequestExcepRon
• 入力エラー等 (ステータスコード400)
UnauthorizedExcepRon
• 認証エラー (ステータスコード401)
ForbiddenExcepRon
• アクセス権限が無い (ステータスコード403)
NotFoundExcepRon
• 要求したデータが存在しない場合等 (ステータスコード404)
InternalServerErrorExcepRon
• システムエラー等 (ステータスコード500)
ServiceUnavailableExcepRon
• メンテナンス中 等 (ステータスコード503)
18
19. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• 例外クラスの自作
• com.google.api.server.spi.ServiceException
を継承
• 実装例
public class ConflictException extends ServiceException{
public ConflictException(String message){
super(409, message);
}
}
19
20. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• ポイント
• API名(@Apiのname)は小文字から始める
• 返り値としてリテラルは返せない。
• 返り値としてAppEngineのEntity型も返せない。
• 一覧取得処理でページングしたい場合は
CollectionResponse<T>を使うと楽。
• 以下はメソッド引数として定義すれば取得可能。
• javax.servlet.http.HttpServletRequest
• javax.servlet.ServletContext
20
21. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• Tips:JUnitでサーバー側の単体テスト
• 一般的なAppEngineアプリのクラスと同様に
単体テストコードを書くことができます。
• Slim3のAppEngineTestCaseを使うのが簡単。
(実装コード内でSlim3を使ってなくてもOK)
21
22. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• API ExplorerでAPIの動作確認
• 入力項目はフォームに自
動で反映されます
• 赤字は必須項目
• リクエスト内容を参照可能
• 実行結果も参照可能
• OAuth2で認証してAPIを実
行することも可能
22
23. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• Eclipseのプロジェクト右クリックから生成
• コマンドラインから生成
sh
appengine-‐java-‐sdk-‐1.7.5/bin/endpoints.sh
get-‐java-‐client-‐lib
jp.vier.sample.endpoints.sample.v1.SampleEndpoint
23
24. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• ポイント
• コードが正しくない場合にはクライアントライ
ブラリ生成時にエラーが発生する
• エラー内容は「エラー・ログ」(Error Log)
ビューに表示される
• Eclipseでよく使う「問題」 (Problems)ビューでは
無いので注意
24
25. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• JavaScript 初期化処理(with AngularJS)
<script type="text/javascript">
var ENDPOINTS_ROOT = 'https://[your_app_id].appspot.com/_ah/api';
function init() {
// EndpointsのAPIを使うための初期化処理
// (第一引数はサーバー側の@APIに定義したAPI名)
gapi.client.load(’appData', 'v1', function() {
// angular init
angular.bootstrap(document,['ui.bootstrap','my.filter']);
}, ENDPOINTS_ROOT);
}
</script>
<script src="https://apis.google.com/js/client.js?onload=init"></script>
25
26. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• JavaScript 関数実行(with AngularJS)
$scope.getAppData=function() {
// API実行
gapi.client.appData.data.get({'id':$scope.id}).execute(function(response) {
// レスポンスを受け取って処理する
$scope.appData = response;
$scope.$apply();
});
・JavaScriptの関数を呼び出すことでサーバーのAPIを実行して結果を取得できる。
・AngularJSでは値の変更を反映するために「$scope.$apply();」が必要。
(AngularJSで「$http.get」を使った場合と違い、自動で反映されない)
26
27. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• JavaScript エラーハンドリング(with AngularJS)
$scope.getAppData=function() {
// API実行
gapi.client.appData.data.get({'id':$scope.id}).execute(function(response,rawResponseJson) {
// for local
if(response===false){
var rawResponse = JSON.parse(rawResponseJson);
// エラーダイアログを表示する
$scope.openErrorModal(rawResponse[0].error.message);
$scope.$apply();
// for production
}else if(response.error && response.error.data && response.error.data.length > 0){
// エラーダイアログを表示する
$scope.openErrorModal(response.error.data[0].message);
$scope.$apply();
// success ・エラーメッセージとhttpのステータスコードを取得できる。
}else{ ・ローカル環境とProduction環境でエラー時の挙動が異なる。(バグ?)
$scope.appData = response; ・rawResponseJsonは本来不要だが、
$scope.$apply(); ローカル環境でエラーハンドリングする際に必要。
}
});
}
27
28. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• Android メソッド実行
AppData.Builder builder = new AppData.Builder(AndroidHttp.newCompatibleTransport(),
new GsonFactory(), null);
AppData appDataApi = CloudEndpointUtils.updateBuilder(builder).build();
AppDataV1 response = appDataApi.data().get(id).execute();
・生成されたクライアント・ライブラリを実行するだけで
サーバー側の処理の返り値を取得できます。
・通常AsyncTaskの中で実行することになるでしょう。
28
29. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• Android エラーハンドリング
@Override
protected
AppDataV1
doInBackground(Void...
unused)
{
try
{
AppData.Builder
builder
=
new
AppData.Builder(AndroidHbp.newCompaRbleTransport(),
new
GsonFactory(),
null);
AppData
appDataApi
=
CloudEndpointURls.updateBuilder(builder).build();
AppDataV1
response
=
appDataApi.data().get(id).execute();
return
response;
}
catch
(final
GoogleJsonResponseExcepRon
e)
{
handler.post(new
Runnable()
{
@Override
・サーバーから返されたエラーは
public
void
run()
{
GoogleJsonResponseExceptionとしてcatchして
//
サーバー側で例外インスタンス生成時に渡したメッセージを取得する
String
resMessage
=
e.getDetails().getMessage();
エラーメッセージを取得する。
Toast.makeText(context,
"Error:
"
+
resMessage,
Toast.LENGTH_LONG).show();
return;
・エラーメッセージの他にステータスコードも取得できる。
}
・IOExceptionは通信エラーとして処理する。
});
return
null;
}
catch
(IOExcepRon
e)
{
//
通信エラーのケース
handler.post(new
Runnable()
{
@Override
public
void
run()
{
Toast.makeText(context,
“通信エラーが発生しました。ネットワーク状態を確認してください。",
Toast.LENGTH_LONG).show();
return;
}
});
return
null;
}
}
29
30. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
• ポイント
• 関数やメソッドを実行するだけで、内部的に
サーバー側のAPIを実行して返り値を取得
できます。
• クライアント側の実装方法はGoogleが提供
するAPIの使い方とほとんど同じなので、
困ったときはGoogle APIの使い方をググる
と良いかもしれません。
• Endpointsの情報に比べると情報がたくさんあり
ます。
30
31. Google Cloud Endpoints
Eclipseプロジェクトの バックエンドの API
Explorerで クライアントライ クライアント側
作成
実装
動作確認
ブラリの生成
の実装
Tips:Androidの実機からローカルサーバーに接続してテ
ストする方法
• Android側はCloudEndpointUtilsを使う
• 「Generate AppEngine Backend」をすると生成される。
• 使い方は 前述の「Android メソッド実行」を参照。
• CloudEndpointUtils#LOCAL_ANDROID_RUNをtrueにする。
• サーバー側はデバッグ設定を変更
• デバッグの構成 -> 引数タブ -> 「プログラムの引数」に
「--address=0.0.0.0 」を追加する。
31
34. Google Cloud Endpoints
OAuth2: サーバー側の実装例
/** Web用ClientId */
private static final String CLIENT_ID_WEB = "10*********09-7ufji**********k2a91.apps.googleusercontent.com";
/** Android用ClientId */
private static final String CLIENT_ID_ANDROID = "10*********09-
o4e8d**********gt2ts3.apps.googleusercontent.com";
/** Android用Audience */
private static final String AUDIENCE_ANDROID = CLIENT_ID_WEB;
@ApiMethod( OAuth2認証を利用する場合はClientIDの定義は必須。
ここに定義したClientID以外で認証して取得したTokenでアクセスした
name = "data.put", 場合、引数「user」はnullになる。(認証してないものとして扱われる)
path = "data/put", Androidで認証してアクセスする場合のみ「audiences」の指定が必要。
clientIds = {CLIENT_ID_WEB, CLIENT_ID_ANDROID },
audiences = AUDIENCE_ANDROID)
public AppDataV2 put(AppDataV2 appDataV2, User user) throws ServiceException {
// 認証チェック
com.google.appengine.api.users.User型の引数を定
義すると、
if (user == null) { OAuth2認証済みの場合に
throw new UnauthorizedException(“ログインが必要です。"); Userクラスにユーザー情報がセットされる。
}
// データの登録処理 認証チェックはuserがnullかどうかで判定し、
}
UnauthorizedExceptionをthrowする。
*実際には定数は別のクラスに定義した方が複数バージョンの対応等で使い勝手が良いです。
34
35. Google Cloud Endpoints
OAuth2: JavaScriptの実装例
var
mainCtrl
=
funcRon($scope)
{
var
CLIENT_ID
=
'10*********09-‐7uri**********k2a91.apps.googleusercontent.com';
var
SCOPES
=
'hbps://www.googleapis.com/auth/userinfo.email’;
//
OAuthライブラリをloadして自動で認証を試みる
・Web用のClient_IDを使用
gapi.client.load('oauth2',
'v2',
funcRon(response)
{
・scopeは最低限「userinfo.email」が必要
$scope.signin(true,
$scope.userAuthed);
});
・認証処理の実行
//
OAuth認証関数
・画面表示時に「可能であれば自動認証」する
$scope.signin=funcRon(mode)
{
際は「immediate:
true」にする。
gapi.auth.authorize({client_id:
CLIENT_ID,
・ボタン押下等で明示的に認証する場合は
scope:
SCOPES,
immediate:
mode,
「immediate:
false」にする。
response_type:
'token
id_token'},
$scope.userAuthed);
};
・認証処理完了時のcallbackで
//
認証後にユーザー情報を取得するcallback関数
$scope.userAuthed
=
funcRon()
{
ユーザー情報の取得とTokenの設定を行う。
var
request
=
gapi.client.oauth2.userinfo.get().execute(funcRon(response)
{
if
(!response.code)
{
var
token
=
gapi.auth.getToken();
・サインアウトは「gapi.auth.setToken(null);」でOK。
token.access_token
=
token.id_token;
gapi.auth.setToken(token);
//
User情報を取得
$scope.user=response;
・明示的なSign
in処理の呼び出し部分
//
反映させる
$scope.$apply();
<a
ng-‐click="signin(false);">
Sign
In
</a>
}
});
}
}
35
36. Google Cloud Endpoints
OAuth2: Androidの実装例 (Credentialの管理)
private
staRc
final
String
CLIENT_ID
=
"10*********09-‐7uri**********k2a91.apps.googleusercontent.com";
・サーバー側でAudienceに指定したClient_IDを指定
private
GoogleAccountCredenRal
credenRal;
Credentialの定義
@Override
protected
void
onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.acRvity_main);
Credentialを生成する。
//
CredenRalを生成する
credenRal
=
GoogleAccountCredenRal.usingAudience(this,
"server:client_id:"
+
CLIENT_ID);
}
private
void
setAccountName(String
accountName)
{
認証情報をCredinRalにセットする。
//
CredenRalにアカウント名をセット
credenRal.setSelectedAccountName(accountName);
}
36
37. Google Cloud Endpoints
OAuth2: Androidの実装例 (アカウント選択)
//
「Googleアカウント選択用Picker」を表示する
private
void
chooseAccount()
{
startAcRvityForResult(credenRal.newChooseAccountIntent(),
REQUEST_ACCOUNT_PICKER);
}
@Override
protected
void
onAcRvityResult(int
requestCode,
int
resultCode,
Intent
data)
{
super.onAcRvityResult(requestCode,
resultCode,
data);
switch
(requestCode)
{
//
「Googleアカウント選択用Picker」の選択結果を取得する
case
REQUEST_ACCOUNT_PICKER:
if
(data
!=
null
&&
data.getExtras()
!=
null)
{
Bundle
bundle
=
data.getExtras();
String
accountName
=
bundle.getString(AccountManager.KEY_ACCOUNT_NAME);
if
(accountName
!=
null)
{
//
CredenRalにアカウント名をセット
setAccountName(accountName);
}
}
break;
}
}
37
38. Google Cloud Endpoints
OAuth2: Androidの実装例 (クライアントライブラリ実行)
AppData.Builder
builder
=
new
AppData.Builder(
AndroidHbp.newCompaRbleTransport(),
new
GsonFactory(),
credenRal);
AppData
appDataApi
=
CloudEndpointURls.updateBuilder(builder).build();
認証情報を使用してAPIを実行する場合には、
Credentialを引数に渡す。
(認証情報が不要な場合にはnullを渡せばOK)
38
39. Google Cloud Endpoints
OAuth2: サーバーに渡せるTokenは二種類ある
Id_token
access_token
公式サンプルが使用している形式
API
Explorerが使用している形式(*1)
ドキュメントに書かれている形式
ドキュメントに書かれてない形式(*2)
User情報からEmailアドレスと User情報からUserId、Email、
NickNameのみ取得できる (*3)
NickName、AuthDomainを取得できる
認証したユーザーの権限をサーバー 認証したユーザーの権限をサーバー
側で行使できない(たぶん)
側で行使できる(若干強引に)(*4)
Token生成に使用したClientIDのチェッ Id_token同様にチェックが行われる。
クが行われる(Token
swap
aback対策)
(ただしAPI
Explorerは無条件で許可)
*1 認証に関してはドキュメント通りのJS・Android実装とAPI Explorerで挙動が異なります。
*2 Endpointsのドキュメントに書かれていない方式なので継続的にサポートされるか不明。
*3 id_tokenを使った場合にUserIDを取得できない事に関してはissueが上げられて
Acceptedになっているのでバグとして修正されるかもしれません。
*4 Endpointsの機能としてTokenやCredentialを取得する仕組みは無いようです。
HttpServletRequest#getHeaderメソッドでaccess_token文字列を直接取得する必要があります。
各クライアント・Tokenタイプによる挙動に関する詳細は「http://goo.gl/44vNj」を参照。
39
41. Google Cloud Endpoints
参考リンク
• 公式ドキュメント
• http://goo.gl/6wp7R
• 公式サンプル (tictactoe)
• Android - http://goo.gl/xjX1B
• Backend & JS- http://goo.gl/V8MEV
• API Explorer
• https://developers.google.com/apis-explorer/#s/
• Google APIs Discovery Service
• https://developers.google.com/discovery/?hl=ja
• APIs Console
• https://code.google.com/apis/console/
• Blog @vierjp 「Google Cloud Endpointsを試してみた」
• http://blog.vier.jp/2013/02/google-cloud-endpoints-12.html
41
43. BigQuery
参考リンク
• BigQuery 公式ドキュメント
• https://developers.google.com/bigquery/
• BigQuery Query Reference (JOIN)
• http://goo.gl/LKEZq
• BigQuery Query Reference (GROUP BY)
• http://goo.gl/EI2aK
• Google Developers Blog (BigQuery gets big new
features to make data analysis even easier)
• http://goo.gl/Unwmq
• Blog @vierjp 「BigQueryの新機能 (2013/03/15)」
• http://blog.vier.jp/2013/03/bigquery-20130315.html
43