techium

このブログは何かに追われないと頑張れない人たちが週一更新をノルマに技術情報を発信するブログです。もし何か調査して欲しい内容がありましたら、@kobashinG or @muchiki0226 までいただけますと気が向いたら調査するかもしれません。

Swagger Codegen で自動生成したクライアントSDKを使う(iOS編)

Swagger Codegen で自動生成したクライアントSDKを使う(iOS編)

REST API 実装 - techium にて grape-swagger で作成した JSON 定義を元に Swagger UI で Web API 仕様書を作成したが、Swagger の定義ファイルを使ってクライアント(iOS, Android など 色々 ) のソースコードを生成することもできる。

Swagger Codegen _ API Development Tools _ Swagger

自作した Web API に対し、クライアントソースコードを吐き出して実際に通信してみる。(ドキドキ

続きを読む

OAuthSwift で AccessToken 取得

OAuthSwift で Access Token 取得

Doorkeeper - awesome OAuth2 provider for your Rails app. を使った OAuth2 認可を実装したサーバーに対して iOS アプリから実際にアクセストークンの取得を試みる。

OSS でいくつか公開されているライブラリがあるが、GitHub スター数や、更新の活発さ等から以下を選択、使用してみたところ正常にアクセストークンを取得できた。

OAuthSwift/OAuthSwift/ Swift based OAuth library for iOS

Doorkeeper 側の設定

https://YOUR_DOMAIN/oauth/applications にアクセスし、対象のアプリケーションを選択する。

Callback urls にはこれまで local tests 用の urn:ietf:wg:oauth:2.0:oob しか入れていなかったので Edit 押下で Redirect URI に追記。

urn:ietf:wg:oauth:2.0:oob
sample-app://oauth-callback

なんか命名規則っぽいのはあるのかな。とりあえず付けた感。

Installation

iOS アプリ側に戻る。
今回パッケージ管理には Carthage を使用しているので Support Carthage の通りに。

How to

How to の通りに。

URL Schemes の追加。

f:id:kfurue:20170830074213p:plain

AppDelegate.swift に以下を追記。

import UIKit
import OAuthSwift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    
    func application(_ app: UIApplication,
                     open url: URL,
                     options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
        if (url.host == "oauth-callback") {
            OAuthSwift.handle(url: url)
        }
        return true
    }
・
・
・

とりあえず動作確認用なので、ViewController 上でお試し。

ViewController.swift に以下を追記。

import UIKit
import OAuthSwift

class ViewController: UIViewController {

    let oauthswift = OAuth2Swift(
        consumerKey:    "15afda6f72009b78571150d63f57bc690a436c8e1199c09bc2de05bc63c09f14",
        consumerSecret: "e590044244da414ece872de0ada52aed33d1cf889d365cb3da4dceed710668b8",
        authorizeUrl:   "https://fierce-wave-40771.herokuapp.com/oauth/authorize",
        accessTokenUrl: "https://fierce-wave-40771.herokuapp.com/oauth/token",
        responseType:   "code"
    )
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        let handle = oauthswift.authorize(
            withCallbackURL: URL(string: "sample-app://oauth-callback")!,
            scope: "", state:"hoge",
            success: { credential, response, parameters in
                print(credential.oauthToken)
                // Do your request
        },
            failure: { error in
                print(error.localizedDescription)
        }
        )

    }
・
・
・

実行すると、

  1. お試しアプリが起動
  2. (上記 ViewController に記載した OAuthSwift の処理で) Safari に自動遷移
  3. Safari 側でログイン処理を実施
  4. (Redirect URL に飛ぶ際 URL Schemes を使って)お試しアプリに自動遷移
  5. print(credential.oauthToken) の行でアクセストークンが取得できていることが確認できる

となる。

成功だ。

その他

テストについてちょっと考えていたが、このあたり Facebook はよくできているなと感じた。

テストユーザー - アプリ開発

まー Web API へのリクエストからしてそもそもモックにすることで unit test は可能か。

REST API 実装

REST API 実装

Rails チュートリアルで作成したアプリに REST API を実装するシリーズ。

  • Doorkeeper による OAuth2 認可
  • Grape による REST API 実装
  • Swagger(OpneAPI) を用いたドキュメンテーションの追加
  • Grape Entities を使ったドキュメント中のレスポンス定義

と進めてきたので、前回決めた API 設計に沿って実装していく。

続きを読む

REST API 設計

REST API 設計

Rails チュートリアルで作成したアプリに REST API を実装するシリーズ。

  • Doorkeeper による OAuth2 認可
  • Grape による REST API 実装
  • Swagger(OpneAPI) を用いたドキュメンテーションの追加
  • Grape Entities を使ったドキュメント中のレスポンス定義

と必要な要素は全て揃った感があるので、REST API の全容を決めていく。

続きを読む

Using Grape Entities

Using Grape Entities

Rails チュートリアルで作成したアプリに REST API を実装するシリーズ。

  • Doorkeeper による OAuth2 認可
  • Grape による REST API 実装
  • Swagger(OpneAPI) を用いたドキュメンテーションの追加

と進めてきて、今回は Grape Entities を使ってドキュメント中のレスポンス定義を明記してみる。

インストール

Using Grape Entities に沿って進める。

Grape::EntityインストールGrapeSwagger::Entityインストール

gem 'grape-entity'
gem 'grape-swagger-entity'
$ bundle

実装

  • Grape::Entity で、レスポンスを整形
  • GrapeSwagger::Entity で、ドキュメントの記載を整形

基本的にこうなる。

User API を実際に書き換えてみる。
今まではとりあえず以下のように User モデルの内容をそのまま返却していた。

module V1

  class Users < Grape::API
・
・
・
    resource :users do

      desc 'Return all users.'
      get do
        User.all
      end
・
・
・

実際のレスポンスはこんな感じ。

[
  {
    "id": 1,
    "name": "Example User",
    "email": "example@railstutorial.org",
    "created_at": "2017-07-22T02:26:19.028Z",
    "updated_at": "2017-07-22T02:26:19.028Z",
    "password_digest": "hogehoge",
    "remember_digest": null,
    "admin": true,
    "activation_digest": "hogehoge",
    "activated": true,
    "activated_at": "2017-07-22T02:26:18.922Z",
    "reset_digest": null,
    "reset_sent_at": null
  },
・
・
・
]

もはや何でもありで User データモデルの内容を全て返却している状態。
これを、

  • 自分が意図した属性のみ返却
  • 返却される値の定義をドキュメンテーション

とするのが今回の目標。

まずは Entity の定義から。

今回は全属性のうち、

  • id
  • name
  • email

を返却するよう実装する。(特に意図はないけどそれぐらいで良さそう?)
created_at も入れておけば「このユーザーはいつから利用を開始しているか」とか表示できてそれっぽいか。
まぁ必要になったら追加することにしよう。

app/api/v1/users.rb に以下を追記。

module V1
  module Entities
    class User < Grape::Entity
      expose :id, documentation: { type: 'integer', desc: 'User ID.', required: true }
      expose :name, documentation: { type: 'string', desc: 'User name.', required: true }
      expose :email, documentation: { type: 'string', desc: 'User email address', required: true }
    end

  end

  class Users < Grape::API
・
・
・

とりあえず型と詳細、optional かどうかの指定をセットしている。
(他にも設定できる内容はたくさんある。)

次に実際に処理を書き換える。

module V1

  class Users < Grape::API
・
・
・
    resource :users do

      desc 'Return all users.',
        entity: Entities::User
      get do
        user = User.all
        present user, with: Entities::User
      end
・
・
・

これだけ。
動かしてみる。

[
  {
    "id": 1,
    "name": "Example User",
    "email": "example@railstutorial.org"
  },
・
・
・
]

うん、意図通り。

レスポンス例が表示できて、

f:id:kfurue:20170815112736p:plain

各属性の詳細が表示できて、

f:id:kfurue:20170815112751p:plain

irb で AccessToken 発行まで進めておけば、Try it out! ボタンも正常に動作する。

f:id:kfurue:20170815112801p:plain

良い感じである。

その他

これで下地が全部整った感あるので、

  • REST API 全体設計
  • 実装

と進めていく。

microposts とか、followers とか? 全容はまだぼんやり。

既存 Rails アプリに追加した REST API ドキュメンテーションにOAuth2 認可情報を反映する

既存 Rails アプリに追加した REST API ドキュメンテーションにOAuth2 認可情報を反映する

回を追うごとにタイトルが長くなっていくのはご愛嬌。

Rails チュートリアルで作成したアプリに
- REST API を追加 - OAuth2 で保護 - Swagger でドキュメンテーション

と進めてきた。

前回の Swagger ドキュメンテーションではセキュリティの定義をしていないので追加していく。

Swagger の SecurityDefinition

OAuth 2

This page is in progress. Please check back later.

まぁオープンソースだしね、文句があるなら自分でパッチ送れや的な?

GrapeSwaggerRails の doorkeeper 連携

そもそも GrapeSwaggerRails が doorkeeper と連携できるんでは?と思って見てみたらやはり。

Integration with DoorKeeper

user.rb は上記例の通り。

has_one :token, -> { order 'created_at DESC' }, class_name: Doorkeeper::AccessToken, foreign_key: :resource_owner_id

swagger.rb は、以下のようにある。

GrapeSwaggerRails.options.before_action do |request|
  GrapeSwaggerRails.options.api_key_default_value = current_user.token.token
end

最終的な GrapeSwaggerRails.options.before_action は以下のようになった。

GrapeSwaggerRails.options.before_action do |request|
  GrapeSwaggerRails.options.app_url = request.protocol + request.host_with_port

  if (user_id = session[:user_id])
    current_user ||= User.find_by(id: user_id)
    if (current_user_token = current_user.token)
      GrapeSwaggerRails.options.api_key_default_value = current_user_token.token
    end
  else
    session[:forwarding_url] = request.fullpath
    redirect_to(GrapeSwaggerRails.options.app_url + '/login')
  end
end

さらに こちら を参考に、api_key の値をクエリパラメータでは無くヘッダーで渡すよう設定する。

GrapeSwaggerRails.options.api_auth     = 'bearer'
GrapeSwaggerRails.options.api_key_name = 'Authorization'
GrapeSwaggerRails.options.api_key_type = 'header'

これで、irb から AccessToken 取得まで進めておけば、そのユーザーでログインした(セッションが有効な)状態で https://host:port/swagger にアクセスすればアクセストークンを api_key として持った状態で Swagger UI が表示できる。
各 API の Try it out! ボタンも正常に動作する。

その他

irb 使わなくても、Swagger UI から認可のフローが実行できないものか。。?
ちょっと だいぶ調べて、できそうなんだけどうまくいかないので断念。

api/v1/root.rb に以下を追加したところ、Swagger の json には SecurityDefinition の定義もできているが、Authorize 用の UI が表示されない。

    add_swagger_documentation \
    info: {
      title: "SAMPLE APP",
      description: "This is the sample application for the tutorial.",
      contact_name: "kfurue",
      contact_email: "contact@example.com",
      contact_url: "http://example.com/contact",
      license: "the MIT License",
      license_url: "http://example.com/license"
    },
    security_definitions: {
      oauthAccessCode: {
        type: "oauth2",
        authorizationUrl: "https://host:port/oauth/authorize",
        tokenUrl: 'https://host:port/oauth/token',
        flow: "accessCode",
        scopes: {
          user: "User scope"
        }
      },
      oauthImplicit: {
        type: "oauth2",
        authorizationUrl: "https://host:port/oauth/authorize",
        flow: "implicit",
        scopes: {
          user: "User scope"
        }
      }
    }

GrapeSwaggerRails のせいか?と思い、grape-swagger で生成した JSON を元に、ローカルで Swagger UI を立ち上げてみるとなるほど、Authorization 用の UI は出てくる。 が、今度は

Validation Erroroauth2RedirectUri configuration is not passed. Oauth2 authorization cannot be performed.

などと表示されて認可フローは通せず。

まだまだ壁は多そうなのでここらで諦めよう。
やりたいことはできてるっぽいし?