Goで実装してGoogle Cloud FunctionsからBigQueryへデータを追加する

#当記事の内容
Google Cloud FunctionsをGoで実装してHTTPリクエストを受け取りBigQueryへデータを追加する方法を記載します。

#開発環境
 - Google Cloud Shellを使用
 - 以下のコマンドを実施済

```shell
$ sudo gcloud components update && gcloud components install beta
```
 - Google Cloud Functions APIは有効化済
 - Go:1.11

#事前準備
##BigQueryのテーブル作成
始めにBigQueryのテーブルを準備します。
テーブルの定義は以下の通りです。

```names.json
[
 {
   "description": "名前",
   "name": "NAME",
   "type": "String",
   "mode": "Required"
 },
 {
   "description": "時刻",
   "name": "DATETIME",
   "type": "Timestamp",
   "mode": "Nullable"
 }
]
```

`bq`コマンドを使ってテーブルを作成します。

```shell-session
$ bq mk DATASET
Dataset 'YOUR_PROJECT:DATASET' successfully created.
$ bq mk --table DATASET.NAMES names.json
Table 'YOUR_PROJECT:DATASET.NAMES' successfully created.
```

#実装とデプロイ
##Fucntionsプログラムの実装
Functionsで動作させるプログラムを作成していきます。
GETリクエストが送られてきた際に"name"パラメータを取得してBigQueryへ出力しています。
GCPが提供している`"cloud.google.com/go/bigquery"`ライブラリを用いてBigQueryへ出力しています。リクエストパラメータに加え現在時刻を付与しています。

```FunctionsToBigQuery.go
package functions

import (
        "context"
        "fmt"
        "log"
        "net/http"
        "os"
        "time"

        //BigQueryの操作に必要なライブリ
        "cloud.google.com/go/bigquery"
)

//BigQueryにデータを追加するための構造体、タグで変数とキーを紐づける
type Data struct {
        Name     string    `bigquery:"NAME"`
        Datetime time.Time `bigquery:"DATETIME"`
}

//HTTPトリガーで実行される
func FunctionsToBigQuery(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case http.MethodGet: //GETの場合

                //GETパラメータの"name"から値を取り出す
                names, err := r.URL.Query()["name"]

                //取り出せない場合はエラーとして処理を終了する
                if !err || len(names[0]) < 1 {
                        fmt.Fprint(w, "パラメータに\"name\"がありません。\r\n")
                        return
                }

                //BigQueryeへ出力する関数を呼び出す
                WriteToBigQuery(names[0])

        default: //GET以外の場合はエラー
                http.Error(w, "405 - Method Not Allowed", http.StatusMethodNotAllowed)
        }
}

func WriteToBigQuery(name string) {

        //流し込むデータを構造へ格納する
        data := Data{}
        data.Name = name
        data.Datetime = time.Now()

        //コンテキストを取得する
        ctx := context.Background()

        //プロジェクトIDを取得する
        projectID := os.Getenv("GCP_PROJECT")

        //BigQueryを操作するクライアントを作成する、エラーの場合はLoggingへ出力する
        client, err := bigquery.NewClient(ctx, projectID)
        if err != nil {
                log.Printf("BigQuery接続エラー　Error:%T message: %v", err, err)
                return
        }

        //deferでクライアントを閉じる
        defer client.Close()

        //クライアントからテーブルを操作するためのアップローダーを取得する
        u := client.Dataset("DATASET").Table("NAMES").Uploader()

        items := []Data{data}

        //テーブルへデータの追加を行う、エラーの場合はLoggingへ出力する
        err = u.Put(ctx, items)
        if err != nil {
                log.Printf("データ書き込みエラー　Error:%T message: %v", err, err)
                return
        }
}
```


##モジュールの用意
次のgo.modを準備します。`"cloud.google.com/go/bigquery"`を使用しているので、必要なライブラリに追加します。バージョンは0.36.0[^1]を指定します。

[^1]: 2019/3/25現在0.37.1が最新ですが。Go1.12が必要になるためGoogle Cloud Functionsでは動きません。


```go.mod
module functions

go 1.11

require cloud.google.com/go v0.36.0
```

##デプロイ
プログラムの準備が出来たらデプロイをしていきます。
`gcloud functions deploy`コマンドを実行すると3分くらいでデプロイが完了します。

```shell-session
$ gcloud functions deploy FunctionsToBigQuery --runtime go111 --trigger-http 
（略）
status: ACTIVE
timeout: 60s
（略）
```

#テスト
`curl`で実際にGETリクエストを送信してみます。リクエストを送るエンドポイントはデプロイした時のログに出ていますので探します。

```shell-session
$ curl https://us-central1-YOUR_PROJECT.cloudfunctions.net/FunctionsToBigQuery?name=aaaaa
```
次に`bq`コマンドでクエリを発行しデータが追加されいていることを確認します。

```shell-session
$ bq query "SELECT * FROM DATASET.NAMES LIMIT 10"
Waiting on bqjob_XXXXXXXXXXXXXXXX_XXXXXXXXXXX_1 ... (0s) Current status: DONE   
+-------+---------------------+
| NAME  |      DATETIME       |
+-------+---------------------+
| aaaaa | 2019-03-24 04:38:06 |
+-------+---------------------+
```

#最後に
簡単にBigQueryへ書き込むREST APIが作成できました。あまりアクセス数の多くないアンケートフォームやキャンペーンサイトにも使えそうです。
2019/4/14の技術書典６に`"Goで学ぶGoogleCloudFunctions"`という本を頒布します。興味ある人は是非是非おこしください。[サークルサイトはこちらです。](https://techbookfest.org/event/tbf06/circle/34050001)