GitHub GraphQL API v4 を Android で遊んでみる

  • 20
    Like
  • 0
    Comment

Apollo GraphQL Client for Android

https://github.com/apollographql/apollo-android

Apollo-Android is a GraphQL compliant client that generates Java models from standard GraphQL queries.

前置きなしにいきなり始めます。GraphQL から Java コードを生成してくれます。このライブラリを利用して GitHub API v4 を利用する手順を紹介します。

はじめる前に

Android プロジェクトを適当に作成して、必要なパーミッションとライブラリのインポートを行います。

<uses-permission android:name="android.permission.INTERNET" />

プロジェクトの build.gradle の dependencies に以下を追加。

dependencies {
    ...

    classpath 'com.apollographql.apollo:gradle-plugin:0.4.0'
}

モジュールの build.gradle に以下を追加(apollo プラグインは必ず andoird プラグインより下にします)。

apply plugin: 'com.android.application'
apply plugin: 'com.apollographql.android'

オプショナルで RxJava2 を利用できるようにするライブラリも追加。

dependencies {
    ...

    compile "io.reactivex.rxjava2:rxjava:2.1.1"
    compile "io.reactivex.rxjava2:rxandroid:2.0.1"
    compile "com.apollographql.apollo:apollo-rx2-support:0.4.0"
}

Apollo 利用の基本手順

  1. apollo-codegen をインストール
  2. schema.json をダウンロード
  3. GraphQL でクエリを作成
  4. Java コードを生成
  5. 生成したコードを使う
    • OkHttp で認証処理
    • ApolloClient を作成
    • メソッドの呼び出し

1. apollo-codegen をインストール

GraphQL から Java を生成するツールをインストールします。このステップは初回のみ必要です。

npm install -g apollo-codegen

必ず -g を付けて実行してください。グローバルにインストールしないと Android のプロジェクトからアクセスできないのでコードの生成ができません。

※npm がない人は Node.js をインストールしてください。

2. schema.json をダウンロード

Apollo でのコード生成には API の詳細が記述されている schema.json と実際のクエリを定義した ~.graphql ファイルが必要です。まず schema.json を GitHub からダウンロードします。GitHub API のエンドポイントにアクセスするにはアクセストークンが必要なので以下の手順で先に取得しておきます。

GitHub で自分のプロフィール画像をクリックして設定を開きます。画面左下にある Personal access tokens を選択すると Generate new token と云うボタンが出てくるので、そこでアクセストークンを生成できます。スコープは userrepo を使います。このデモではそれぞれユーザデータの取得、レポジトリの issue の更新に利用します。

※個人利用限定のトークンです。扱いに注意してください。アプリで実際に使う場合は OAuth で簡単にアクセストークンを取得することができます。

github_settings.png

アクセストークンの準備ができたら、apollo-codegen を使って schema.json をダウンロードします。

apollo-codegen download-schema https://api.github.com/graphql --output schema.json --header "Authorization: Bearer <access-token>"

1MB 程ある schema.json がコマンドを実行したディレクトリにダウンロードされます。

3. GraphQL でクエリを作成

ユーザ情報を抜き出すだけの簡単なクエリを作ります。初めて GitHub の GraphiQL を開いた時に出ているサンプルクエリに少し項目を追加したものです。Apollo はオペレーションとクエリ名を省略した形式の GraphQL をサポートしていないのでオペレーション名と適当なクエリ名を与える必要があります。処理はクエリなので query キーワードで、名前は Viewer と与えます。生成されるコードのクラス名はクエリ名 + オペレーション名になるので ViewerQuery になります。

query Viewer {
  viewer {
    login
    bio
    avatarUrl
    company
    location
  }
}

念のために GraphiQL でクエリを実行してみるとこんな感じ。

fig2.png

このクエリを API.graphql と云う名前のファイルに普通のテキスト形式で保存します(ファイル名の API の部分は何でも構いません。拡張子は必ず .graphql にします)。

4. コードの生成

上のステップで用意した schema.json と API.graphiql から Java コードを生成します。あらかじめ作って置いたプロジェクトの src/main に graphql と云うディレクトリを作成します。そこにアプリのパッケージ名に対応するディレクトリ構造を作成し、schema.json と API.graphql を配置します。生成されるクラス群はこのディレクトリ構造に従ったパッケージ名が与えられます。ここではアプリのルートパッケージに api と云うパッケージを追加した構造にします。Android Studio のパースペクティブを Project Files にして展開するとこんな感じになります。

dirs.png

この状態でアプリのビルドを行うと Apollo プラグインがコードを生成してくれます。生成されたコードは build/generated のディレクトリ以下にあります。

5. 生成したコードを使う

生成したコードは ApolloClient を介して呼び出すことができます。schema.json をダウンロードした時と同様に、bearer token が要求されるので ApolloClient に認証処理を追加する必要があります。ApolloClient は OkHttp を HTTP クライアントとして利用できるので、OkHttp の Authenticator (もしくはInterceptor) を使って認証できるようにします。

OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .authenticator(new Authenticator() {
            @Override
            public Request authenticate(Route route, okhttp3.Response response) throws IOException {
                return response.request().newBuilder()
                        .addHeader("Authorization", "Bearer <access-token>")
                        .build();
            }
        }).build();

ApolloClient を作成します。上で作った okHttpClient と API のエンドポイントを渡します。

ApolloClient apolloClient = ApolloClient.builder()
        .okHttpClient(okHttpClient)
        .serverUrl("https://api.github.com/graphql")
        .build();

先のステップで生成したコードで GitHub にリクエストを送ります。

Rx2Apollo.from(apolloClient.query(new ViewerQuery()).watcher())
        .subscribeOn(Schedulers.io())
        .subscribe(new DisposableObserver<Response<ViewerQuery.Data>>() {
            @Override
            public void onNext(Response<ViewerQuery.Data> dataResponse) {
                android.util.Log.d("graphql", "" + dataResponse.data());
            }

            @Override
            public void onError(Throwable e) {
                android.util.Log.d("graphql", "" + e);
            }

            @Override
            public void onComplete() {
                // no-op
            }
        });

成功するとこんな感じのレスポンスが返ってきます(生成されたコードにラップされる前の実際のレスポンスはJSONテキスト)。

D/graphql: Data{viewer=Viewer{__typename=User, login=mightyfrog, name=, company=, avatarUrl=https://avatars0.githubusercontent.com/u/208494?v=4, bio=Android developer @DeNA, createdAt=2010-02-22T16:04:03Z}}

引数を取るクエリを作ってみる

ユーザ名を指定してユーザデータを取得してみます。クエリはこんな感じ。

{
  user(login: "mightyfrog") {
    id
    login
    bio
    avatarUrl
    company
    createdAt
  }
}

これを変数が使える形式に書き換えます。

query User($login: String!) {
  user(login: $login) {
    id
    login
    bio
    avatarUrl
    company
    createdAt
  }
}

GraphiQL で試します。変数の値は QUERY VARIABLES に記述して送信します。

{
  "login": "mightyfrog"
}

fig3.png

変数が使えるように書き換えたクエリを、先に作った API.graphql にこのように追加します。

API.graphql
query Viewer {
  viewer {
    login
    name
    company
    avatarUrl
    bio
    createdAt
  }
}

query User($login: String!) {
  user(login: $login) {
    id
    login
    bio
    avatarUrl
    company
    createdAt
  }
}

プロジェクトをビルドすると新たに UserQuery クラスが生成されるので使ってみます。コンストラクタが引数を取るようになっています。

Rx2Apollo.from(apolloClient.query(new UserQuery("mightyfrog")).watcher())
        .subscribeOn(Schedulers.io())
        .subscribe(new DisposableObserver<Response<UserQuery.Data>>() {
            @Override
            public void onNext(Response<UserQuery.Data> dataResponse) {
                android.util.Log.d("graphql", "" + dataResponse.data());
            }

            @Override
            public void onError(Throwable e) {
                android.util.Log.d("graphql", "" + e);
            }

            @Override
            public void onComplete() {
                // no-op
            }
        });

こんな感じの結果が返ってきます。

D/graphql: Data{user=User1{__typename=User, id=MDQ6VXNlcjIwODQ5NA==, login=mightyfrog, bio=Android developer @DeNA, avatarUrl=https://avatars0.githubusercontent.com/u/208494?v=4, company=, createdAt=2010-02-22T16:04:03Z}}

データを更新してみる

GitHub の The Octocat のアカウントの Hello-World レポジトリの issue349 に絵文字のリアクションを付けてみます。API.graphql に以下を追加してコード生成します。

mutation AddReactionToIssue($subjectId: ID!, $content: ReactionContent!) {
  addReaction(input:{subjectId: $subjectId,content: $content}) {
    reaction {
      content
    }
    subject {
      id
    }
  }
}

GraphiQL で試すとこんな感じに。

fig4.png

下のクエリを使って issue 番号を取得すれば、自分のレポジトリでも試すことができます。

query FindIssueID {
  repository(owner: "octocat", name: "Hello-World") {
    issue(number: 349) {
      id
    }
  }
}

ビルドして生成されたコードで実行します。

Rx2Apollo.from(apolloClient.mutate(new AddReactionToIssueMutation("MDU6SXNzdWUyMzEzOTE1NTE=", ReactionContent.HEART)))
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new DisposableObserver<Response<AddReactionToIssueMutation.Data>>() {
            @Override
            public void onNext(Response<AddReactionToIssueMutation.Data> dataResponse) {
                mTextView.setText("" + dataResponse.data());
                android.util.Log.d("graphql", "" + dataResponse.data());
            }

            @Override
            public void onError(Throwable e) {
                android.util.Log.d("graphql", "" + e);
            }

            @Override
            public void onComplete() {
                // no-op
            }
        });

レスポンスはこんな感じ。

D/graphql: Data{addReaction=AddReaction{__typename=AddReactionPayload, reaction=Reaction{__typename=Reaction, content=HEART}, subject=Subject{__typename=Issue, id=MDU6SXNzdWUyMzEzOTE1NTE=}}}

issue349 のリアクションも更新されます。

reaction.png

終わりに

Apollo GraphQL Client for Android はアルファとして一般公開されてからまだ3ヶ月ほどしか経っていないのライブラリです。正直扱いがちょっと面倒なのと、パフォーマンス面も不明ですがこのデモ程度のことはちゃんとこなす事ができます。今回は使いませんでしたが alias、fragment、directive を使ったもっと複雑なクエリもサポートしています。Kotlin プラグインも用意されていて、Proguard への配慮や、Auto-Value 対応予定であったりと今後も期待が持てるライブラリです。

ここまで読んでいただけた人はきっと GraphQL 自体にもより興味が持てたのではないかと思います。GraphQL をもっと知りたい!って人は GitHub の GraphQL Overview と graphql.org の Introduction to GraphQL がオススメです。

shibuya.apk #17 で使った使う予定だったデモ GraphQL API Server はこちら:
https://github.com/mightyfrog/Simple-GraphQL-API-Server

参考資料