今回の記事までにいくつかCloud Workflowsの記事を書いた
GCPのCloud Workflowsを試す - notebook
Cloud Workflowsでランタイム引数のデフォルト値を設定する - notebook
これらで行ったことをもとに今回はテーマを決めてワークフローを作ってみる
今回の題材は「Togglの特定プロジェクトの前日分の作業時間をPixelaにPUTする」
細かなサンプルを作る
基本的な動かし方は前回までの記事でわかったので一連の流れの中から切り出せるものを切り出してサンプルとして実行できるようにする
- 実行時に引数を渡せるようにする
- デフォルト値も指定可能にする(前回の記事があるので今回は割愛)
- 昨日の日付文字列を取得する関数を呼び出す
- あらかじめ適当な関数を作ってデプロイしておく
- SecretManagerからAPIのTOKENを取得する
- Toggl,Pixelaともに必要なのでサブワークフローにする
- TogglのAPIをたたいて集計結果を取得する
- PixelaのAPIをたたいてグラフにプロットする
昨日の日付文字列を取得する関数を呼び出す
Toggl,Pixelaともにリクエストを送るために日付情報が必要なのでランタイム引数で指定する以外にデフォルトで昨日の日付を設定する
というのをやりたかった
色々試行錯誤してみたがどうにもworkflowsのYAML内のみで行うのが厳しそうだったので簡単なCloudFunctionsのコードを書いた(関数の内容に関しては本記事からは割愛する)
Ruby 2.7(ベータ版)でこんな感じのレスポンスが返ってくる関数を書いた
$ curl -X GET -H "Authorization: Bearer $(gcloud auth print-identity-token)" 'https://us-central1-sample-project-111111.cloudfunctions.net/datetime/yesterday' {"date":{"from":"2021-03-24","to":"2021-03-24"},"ymd":{"from":"20210324","to":"20210324"},"time":{"from":"2021-03-24T00:00:00","to":"2021-03-24T23:59:59"}}
で、この関数をたたく箇所が下記
- toggl-to-pixela.workflow.yml(一部抜粋)
- get_yesterday: call: http.get args: url: ${params.url + "/yesterday"} query: zone: Asia/Tokyo auth: type: OIDC audience: ${params.url} result: yesterday_res
params.url
には上記関数のURLが入っている
SecretManagerへアクセスする
ほとんどコネクタのサンプルの内容をもらってサブワークフローにした
- toggl-to-pixela.workflow.yml(一部抜粋)
get_token_from_secret_manager: params: [project, secret] steps: - get_secret: try: call: googleapis.secretmanager.v1.projects.secrets.versions.access args: name: ${"projects/" + project + "/secrets/" + secret + "/versions/latest"} result: secretResult except: as: e steps: - handle_secret_manager_error: switch: - condition: ${e.code == 404} raise: "Secret not found" - condition: ${e.code == 403} raise: "Error authenticating to Secret Manager" - unhandled_exception: raise: ${e} - return_secret: return: ${text.decode(base64.decode(secretResult.payload.data))}
TogglのAPIをたたいて集計結果を取得する
TogglのAPIのたたき方に合わせて設定する
レポートのAPIドキュメントは下記
toggl_api_docs/reports.md at master · toggl/toggl_api_docs
今回はSummaryReportを使うので次のドキュメントを見ながら設定する
toggl_api_docs/summary.md at master · toggl/toggl_api_docs
さきほど説明したSecretManagerからTOKENを取得するサブワークフローを呼んでAPI TOKENを引っ張ってきている
こちらも一連の流れをサブワークフローにまとめた
call_toggl_api: params: [project, params] steps: - get_toggl_token: call: get_token_from_secret_manager args: project: ${project} secret: TOGGL_API_TOKEN result: toggl_api_token - set_toggl_auth_value: assign: - basic_auth_value: ${base64.encode(text.encode(toggl_api_token + ":api_token"))} - get_toggl_value: call: http.get args: url: https://api.track.toggl.com/reports/api/v2/summary headers: Authorization: ${"Basic " + basic_auth_value} query: page: 1 workspace_id: ${params.toggl.workspace_id} since: ${params.toggl.since} until: ${params.toggl.until} user_agent: api_test project_ids: ${params.toggl.project_id} result: toggl_res - log_toggl: call: sys.log args: text: ${json.encode_to_string(toggl_res)} severity: INFO - return_value: return: ${toggl_res}
PixelaのAPIをたたいてグラフにプロットする
PixelaのAPIのドキュメントは下記
PUT - /v1/users/
これに合わせてステップを設定する
TogglのAPIをたたくサブワークフローと同様にサブワークフローにまとめた
PixelaのAPIをたたくときに数値でも文字列にキャストしないとエラーになるのでそのへんだけ気を付ける
call_pixela_api: params: [project, params, toggl_value] steps: - get_pixela_token: call: get_token_from_secret_manager args: project: ${project} secret: PIXELA_API_TOKEN result: pixela_api_token - to_pixela: call: http.put args: url: ${"https://pixe.la/v1/users/" + params.pixela.user + "/graphs/" + params.pixela.graph_id + "/" + params.pixela.target_date} headers: X-USER-TOKEN: ${pixela_api_token} body: quantity: ${string(toggl_value)} result: pixela_res - log_pixela: call: sys.log args: text: ${json.encode_to_string(pixela_res)} severity: INFO - return_value: return: ${pixela_res}
各サブワークフローをつなげてワークフローを完成させる
値のハンドリング
Togglのレスポンス内容で前日に該当作業をしていなかった場合に分岐を発生させる
前日作業なしの場合はPixelaのAPIをたたかないようにする
具体的には次のようなレスポンスが返ってくるのでそれに合わせて条件分岐を設定する
- 該当期間中にレコードがない場合
{ "total_grand": null, "total_billable": null, "total_currencies": [], "data": [] }
- 該当期間中にレコードがある場合
{ "total_grand": 36600000, "total_billable": null, "total_currencies": [ { "currency": null, "amount": null } ], "data": [ { "id": 156548296, "title": { "project": "読書", "client": null, "color": "0", "hex_color": "#0b83d9" }, "time": 36600000, "total_currencies": [ { "currency": null, "amount": null } ], "items": [ { "title": { "time_entry": "なぜ人と組織は変われないのか" }, "time": 16727000, "cur": null, "sum": null, "rate": null }, { "title": { "time_entry": "理科系の作文技術" }, "time": 19873000, "cur": null, "sum": null, "rate": null } ] } ] }
レコードがある場合とない場合でbody.data
の件数に違いがあるのでレスポンスによって書き分けるようにした(conditional_switch
のステップ)
それらをまとめたmainのワークフローは下記のようになる
- toggl-to-pixela.workflow.yml(一部抜粋)
main: params: [args] steps: - init_variables: assign: - project: ${sys.get_env("GOOGLE_CLOUD_PROJECT_NUMBER")} - build_parameters: call: build_params args: params: ${args} result: merged_params - get_toggl_value: call: call_toggl_api args: project: ${project} params: ${merged_params} result: toggl_res - conditional_switch: switch: - condition: ${len(toggl_res.body.data) > 0} steps: - to_min_value: assign: - toggl_value: ${toggl_res.body.total_grand / 1000 / 60} - log_value: call: sys.log args: text: ${toggl_value} severity: INFO - to_pixela: call: call_pixela_api args: project: ${project} params: ${merged_params} toggl_value: ${toggl_value} result: pixela_res next: return_value next: no_project_record - no_project_record: steps: - return: return: "not found target project time." - return_value: return: ${pixela_res}
スッキリまとまった(きがする)
mainのフロー図
IaC
動くものが作れるようになったのであとはIaCで管理できるようにする
TerraformでSchedulerとWorkflows、サービスアカウントを管理する
SecretManagerに関しては手動で登録した
functionsもTerraformとは別で管理するようにしてdata
で関数を指定して必要な項目を指定できるようにした
少しつまずいたのがSchedulerからWorkflowsをたたくときの作法
Cloud Scheduler の使用によるワークフローのスケジュール設定 | Google Cloud
をみて「なるほどね」って感じで設定したが実行時にパラメータを渡すときどうするのかは書いていなかった
そのため単純に渡したいJSONをhttp_target.body
に突っ込んだだけではINVALID_ARGUMENTS
で怒られた
REST Resource: projects.locations.workflows.executions | ワークフロー
上記のドキュメントからAPIでWorkflowsをたたくためのJSONの中身を調べた
argument
というキーにencodeしたパラメータのJSON文字列をいれてたたくと意図通りパラメータが渡されるようになった
あとはtfファイルを書くだけ
リポジトリ
今回使ったソースコードがあるリポジトリはこちらです
swfz/toggl-to-pixela-cloud-workflows
テンプレートリポジトリにしてあるのでREADME読めばある程度使えるかと思います
ぜひ使ってみてください
余談
今回色々触ってたらいくつか不具合っぽいものにあたってしまったのでissue投稿したところどちらも短期間で対応してくれた
- Workflows
Assigned variables cannot be reference [184491211] - Visible to Public - Issue Tracker
- terraform-provider-google
次回は自分でPR作れるようにGo言語書く機会持ちたいなーと思った