FitbitはAPIがしっかり整備されていて、OAuth2 endpoint経由でデータが取り放題。
せっかくなので、話題のExternal Network Access(2023年12月現在 PuPr)を試してみようと思う。
つまり、FitbitAPI→Snowflakeをやってみようと思う。
Fitbit APIを使用するにはOAuth2.0 Authorizationを通す必要がある。
Snowflakeの公式にOAuth2.0 Endpoint経由でGoogle翻訳APIと連携する段取りが書かれていて、
それをそのままFitbit APIのものに差し替えるだけで動いた。
外部ネットワークアクセスについては以下。
2016年6月に書いた記事。phpで検証をしていた。
この辺りからバッテリーがダメになる度に新しいFitbit Charge(1,2,3)を買って溜めてきた。
この間、FitbitがGoogleに買われてしまったり、スマホアプリが大幅に変わったり、色々あった。
基本的な機能はずっと動いているので、7年分のデータが溜まっているんじゃないかな、と期待。
Fitbit API側の準備
OAuth2連携に必要な情報を dev.fitbit.com から取得する必要がある。
Authorization Code Grant Flow with PKCE
こちらを参考にさせていただいた。
以下を準備すればOK。
- access-token
- refresh-token
- client-id
Snowflakeでリソース作り
Snowsightでポチポチとリソースを作っていく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
USE ROLE SYSADMIN; -- 外部ロケーションを表すネットワークルールの作成 -- CREATE OR REPLACE NETWORK RULE fitbit_apis_network_rule MODE = EGRESS TYPE = HOST_PORT VALUE_LIST = ('api.fitbit.com'); -- 外部ロケーションとの認証に必要なOAuth認証情報を保持するセキュリティ統合の作成 -- CREATE OR REPLACE SECURITY INTEGRATION fitbit_api_oauth TYPE = API_AUTHENTICATION AUTH_TYPE = OAUTH2 OAUTH_CLIENT_ID = '<client-id>' OAUTH_CLIENT_SECRET = '<access-token>' OAUTH_TOKEN_ENDPOINT = 'https://api.fitbit.com/oauth2/token' OAUTH_AUTHORIZATION_ENDPOINT = 'https://www.fitbit.com/oauth2/authorize' ENABLED = TRUE; -- セキュリティ統合に含まれる認証情報を表すシークレットの作成 -- CREATE OR REPLACE SECRET fitbit_api_oauth_token TYPE = oauth2 API_AUTHENTICATION = fitbit_api_oauth OAUTH_REFRESH_TOKEN = '<refresh-token>'; |
最後に外部アクセス統合を作成する。
ストレージ統合や、Notification統合など、統合の作成にはACCOUNTADMINが必要で、
同様に外部アクセス統合の作成にはACCOUNTADMINが必要とのこと。
1 2 3 4 5 |
USE ROLE ACCOUNTADMIN; CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION fitbit_apis_access_integration ALLOWED_NETWORK_RULES = (fitbit_apis_network_rule) ALLOWED_AUTHENTICATION_SECRETS = (fitbit_api_oauth_token) ENABLED = TRUE; |
外部ロケーション(ネットワーク)にアクセスするUDFsを書くロールを作成する。
UDFsを書く際に、シークレットを参照する必要がある。
UDFsを書けるロールにシークレットのREAD権限を付与しておく必要がある。
以下、そのままでは SECURITYADMINがDB・スキーマに触れないので環境により修正が必要。
1 2 3 4 5 6 7 8 9 |
USE ROLE USERADMIN; CREATE OR REPLACE ROLE ikuty_fitbitapi_developer; USE ROLE SECURITYADMIN; USE SCHEMA IKUTY_DB.PUBLIC; GRANT READ ON SECRET IKUTY_DB.PUBLIC.fitbit_api_oauth_token TO ROLE ikuty_fitbitapi_developer; GRANT USAGE ON INTEGRATION fitbit_apis_access_integration TO ROLE ikuty_fitbitapi_developer; GRANT ROLE ikuty_fitbitapi_developer TO role SYSADMIN; |
本体の実装
PythonでOAuth2 Endpoint経由でFitbit APIにGETリクエストを投げるFunctionを書く。
最初、トークンのexpire時のrefreshを自力で書いていたが、get_oauth_access_token(‘cred’)により、
自動的にrefreshしてくれていることに気づいた。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
use role sysadmin; use schema IKUTY_DB.PUBLIC; CREATE OR REPLACE FUNCTION fitbit_python() RETURNS STRING LANGUAGE PYTHON RUNTIME_VERSION = 3.8 HANDLER = 'hello_fitbit' EXTERNAL_ACCESS_INTEGRATIONS = (fitbit_apis_access_integration) PACKAGES = ('snowflake-snowpark-python','requests') SECRETS = ('cred' = fitbit_api_oauth_token ) AS $$ import _snowflake import requests import json def hello_fitbit(): with requests.Session() as s: access_token = _snowflake.get_oauth_access_token('cred') url = "https://api.fitbit.com/1/user/-/activities/steps/date/today/1m.json" res = s.get(url,headers={"Authorization": "Bearer " + access_token}) res_data = res.json() return res_data $$; |
実行結果は以下。1日毎の歩数を1ヶ月分取得できた(恥…)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
select parse_json(fitbit_python()); { "activities-steps": [ { "dateTime": "2023-11-23", "value": "15570" }, { "dateTime": "2023-11-24", "value": "5392" }, { "dateTime": "2023-11-25", "value": "8993" }, { "dateTime": "2023-11-26", "value": "10525" }, { "dateTime": "2023-11-27", "value": "6371" }, { "dateTime": "2023-11-28", "value": "2713" }, { "dateTime": "2023-11-29", "value": "9252" }, { "dateTime": "2023-11-30", "value": "0" }, { "dateTime": "2023-12-01", "value": "7947" }, { "dateTime": "2023-12-02", "value": "11265" }, { "dateTime": "2023-12-03", "value": "8557" }, { "dateTime": "2023-12-04", "value": "2366" }, { "dateTime": "2023-12-05", "value": "7985" }, { "dateTime": "2023-12-06", "value": "8109" }, { "dateTime": "2023-12-07", "value": "6852" }, { "dateTime": "2023-12-08", "value": "3707" }, { "dateTime": "2023-12-09", "value": "12640" }, { "dateTime": "2023-12-10", "value": "7122" }, { "dateTime": "2023-12-11", "value": "7190" }, { "dateTime": "2023-12-12", "value": "8034" }, { "dateTime": "2023-12-13", "value": "5228" }, { "dateTime": "2023-12-14", "value": "2861" }, { "dateTime": "2023-12-15", "value": "6785" }, { "dateTime": "2023-12-16", "value": "11720" }, { "dateTime": "2023-12-17", "value": "11021" }, { "dateTime": "2023-12-18", "value": "0" }, { "dateTime": "2023-12-19", "value": "11021" }, { "dateTime": "2023-12-20", "value": "0" }, { "dateTime": "2023-12-21", "value": "2703" }, { "dateTime": "2023-12-22", "value": "3336" }, { "dateTime": "2023-12-23", "value": "7497" } ] } |
結論
PuPrのExternal Network Accessを使用して、FitbitAPI→Snowflakeが出来ることを確認した。
(途中、自動的にトークンをrefreshしてくれている、と書いたが、何度かExpireさせないと良くわからない。)
相手がOAuth2.0ならとても簡単に繋ぐことができると思う。
次は、せっかくなのでSiS(Streamlit in Snowflake)で可視化してみたりしたい。