てくてくテック

気ままに開発のメモを書いていこうと思います。主にSwiftかと。

Kituraのチュートリアル(ToDoBackend)を試してみた

はじめに

こんにちは。toosaaです。

前回IBM製のフレームワークKituraのGetting Startedに従ってサーバーサイドのHello, World!を行いました。今回は、Getting Startedに続いてチュートリアルとして用意されているToDoBackendを試してみます。そして次回、その内容の一部を調査しようと思います。

動作環境

  • macOS High Sierra ver 10.13.3
  • Xcode ver 9.2
  • Homebrew 1.5.2

事前準備

ToDoBbackendに対して、todo-backend-js-specによるテストをグリーンにしていくことで開発していきます。テスト駆動ですね。そのため、ToDoBackendとtodo-backend-js-specの二つのプロジェクトを用意します。

  • $ git clone http://github.com/IBM/ToDoBackend
  • $ git clone http://github.com/TodoBackend/todo-backend-js-spec

試しにテストを実行してみる

  • $ cd path/to//todo-backend-js-spec
  • $open index.html
  • 起動したブラウザのページのtest target rootにhttp://localhost:8080をセット
  • run testsをクリック

これでテストを実行できます。おそらく、最初の行にthe api root responds to a GET (i.e. the server is up and accessible, CORS headers are set up)とエラーが出てると思います。サーバーを起動してないのだからエラー出て当然です。

ToDoBackendのサーバーを初期化する

  • $ cd path/to/ToDoBackend
  • $ mkdir ToDoServer
  • $ cd ToDoServer
  • $ kitura init
  • open ToDoServer.xcodeproj
  • ビルドスキームをToDoServerに変更(デフォルトではToDoServer-Packageになっているはず)
    • Xcode左上の停止ボタン(四角いやつ)の右
  • ⌘-Rで実行
  • 再度テストを実行する

おそらくまたthe api root responds to a GET (i.e. the server is up and accessible, CORS headers are set up)とエラーが出てると思います。 Cross Origin Resource Sharing (CORS) が有効になっていないからです。

Cross Origin Resource Sharing (CORS) を有効化する

  • CORSライブラリをPackage.swiftに追加する
    • (注)チュートリアルページではdepenciesの方にConfigurationとかあり、エラーになった
// Package.swift
let package = Package(
    name: "ToDoServer",
    dependencies: [
      .package(url: "https://github.com/IBM-Swift/Kitura.git", .upToNextMinor(from: "2.0.0")),
      .package(url: "https://github.com/IBM-Swift/HeliumLogger.git", .upToNextMinor(from: "1.7.1")),
      .package(url: "https://github.com/IBM-Swift/CloudEnvironment.git", from: "6.0.0"),
      .package(url: "https://github.com/RuntimeTools/SwiftMetrics.git", from: "2.0.0"),
      .package(url: "https://github.com/IBM-Swift/Health.git", from: "0.0.0"),
      //ここ追加
      .package(url: "https://github.com/IBM-Swift/Kitura-CORS", .upToNextMinor(from: "2.0.0")),
    ],
    targets: [
      .target(name: "ToDoServer", dependencies: [ .target(name: "Application"), "Kitura" , "HeliumLogger"]),
      //ここにKituraCORS追加
      .target(name: "Application", dependencies: [ "Kitura", "KituraCORS", "CloudEnvironment", "Health" , "SwiftMetrics",
                                                   ]),
      .testTarget(name: "ApplicationTests" , dependencies: [.target(name: "Application"), "Kitura","HeliumLogger" ])
    ]
)
  • packageをインストールするためにregenerateする
    • Xcodeを閉じる
    • $ cd path/to/ToDoBackend/ToDoServer
    • $ swift package generate-xcodeproj
    • $ open TodoServer.xcodeproj
  • CORSライブラリを使う
    • Sources/Application/Application.swiftを開く
    • import KituraCORSを追記
    • postInit()の中に下記のコードを追加
let options = Options(allowedOrigin: .all)
let cors = CORS(options: options)
router.all("/*", middleware: cors)

この状態で再度テストを行うと、the api root responds to a POST with the todo which was posted to itに変わっているはずです。(注)もし変更なかったら、テスト用のページを一旦戻りrun testsしてみると変わるかと思います。このテスト結果はPostリクエストの処理を実装してないためです。

Postリクエストの処理を実装する

  • Modelを生成する
    • XcodeのProjectNavigator上のApplicationフォルダに右クリック
    • New File...を選択する
    • 名前をModels.swiftにする
    • TargetsをApplicationだけにする(おそらくToDoServerPackageDescriptionにチェックがついている)。
      • 私の環境だと最初はTargetsを選択する領域が出てなかったのですが、何かのボタンを押したら出たと記憶しています
    • Create
    • 下記のコードを追加
public struct ToDo : Codable, Equatable {
    public var id: Int?
    public var user: String?
    public var title: String?
    public var order: Int?
    public var completed: Bool?
    public var url: String?
    
    public static func ==(lhs: ToDo, rhs: ToDo) -> Bool {
        return (lhs.title == rhs.title) && (lhs.user == rhs.user) && (lhs.order == rhs.order) && (lhs.completed == rhs.completed) && (lhs.url == rhs.url) && (lhs.id == rhs.id)
    }
}
  • ToDoアイテムを格納するArrayを作る(このチュートリアルではDBは使わない)

    • Sources/Application/Application.swiftを開く
    • let cloudEnv = CloundEnv()の下に下記のコードを追加する

      private var todoStore = [ToDo]()
      private var nextId :Int = 0
      private let workerQueue = DispatchQueue(label: "worker")
      
    • App Class内に下記メソッドを追加

      func execute(_ block: (() -> Void)) {
         workerQueue.sync {
           block()
         }
      }
      
  • / へのpostリクエストに対応するhandlerを登録する

    • postInit()に下記コードを追加する
router.post("/", handler: storeHandler)
  • App Classに下記のメソッドを追加する
func storeHandler(todo: ToDo, completion: (ToDo?, RequestError?) -> Void ) {
     var todo = todo
     if todo.completed == nil {
         todo.completed = false
     }
     todo.id = nextId
     todo.url = "http://localhost:8080/\(nextId)"
     nextId += 1
     execute {
         todoStore.append(todo)
     }
     completion(todo, nil)
 }

DELETE, GET, PATCHの実装もする

基本は、postと同じ感じなので割愛

終わりに

今回は、Kituraのチュートリアルの一つToDoBackendを試してみました。次回は、この中で調査したものについて書きます。