Skip to content

feat: Google Health接続基盤を追加#174

Merged
endo-ly merged 4 commits into
mainfrom
feat/google-health-connection-foundation
Jun 12, 2026
Merged

feat: Google Health接続基盤を追加#174
endo-ly merged 4 commits into
mainfrom
feat/google-health-connection-foundation

Conversation

@endo-ly

@endo-ly endo-ly commented Jun 10, 2026

Copy link
Copy Markdown
Owner

概要

Google Fitbit Air の健康データ連携に向け、Phase 1 の Google Health API 接続基盤を追加します。

変更内容

  • Google OAuth 2.0 Web Server flow の start / callback API
  • OAuth state の期限付き・一回限り検証
  • access token / refresh token の Fernet 暗号化 SQLite 保存
  • access token の期限切れ・401応答時の自動 refresh
  • connection 状態取得・削除 API
  • active / expired / revoked / error の状態管理
  • Google Health API v4 client と 429 / 5xx / network error retry
  • steps / sleep の smoke test API
  • Fitbit Air 対象 data type registry
  • OAuth callback query の access log redaction
  • Google Cloud、環境変数、接続手順のドキュメント

設計上のポイント

  • callback URI は GOOGLE_HEALTH_REDIRECT_URI で指定し、localhost、Tailscale Serve、Cloudflare Tunnel、独自ドメインを選択可能です。
  • token、authorization code、API response body はログに出しません。
  • connection は singleton とし、再認証時は同じ connection を UPSERT します。
  • Raw JSON / Parquet 保存、workflow、scheduler は Phase 2 以降の対象です。

検証

  • uv run pytest egograph/pipelines/tests/unit -q: 396 passed
  • uv run pytest egograph/pipelines/tests/integration -q: 35 passed
  • uv run pytest egograph/pipelines/tests/e2e -q: 1 passed
  • Google Health 対象の Ruff check: passed
  • CodeRabbit uncommitted review: major/minor 指摘を確認・修正済み

残作業

Google Cloud の OAuth client 設定後、実 Google Account で認可し、暗号化 token 保存、自動 refresh、steps / sleep の実 API 疎通を確認してから Ready for review に変更します。

Summary by CodeRabbit

リリースノート

  • 新機能

    • Google Health(Fitbit)からの健康・活動・睡眠データ取り込みに対応しました。
    • OAuth認証によるGoogle Health接続・管理機能を追加しました。
    • 接続状態確認・疎通確認・接続削除のAPIエンドポイントを提供開始しました。
  • インフラストラクチャ

    • Google Health連携に必要なデータベーススキーマ・トークン暗号化機能を整備しました。
    • OAuth認証フローおよびトークン更新・リトライ機構を実装しました。

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@endo-ly, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 57 minutes and 16 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a1f08ca5-6f7a-42b4-aa71-6ab3dd79d516

📥 Commits

Reviewing files that changed from the base of the PR and between 7713e9c and 7bb949b.

📒 Files selected for processing (13)
  • docs/data-sources/google-health.md
  • docs/plan/plan-fitbit.md
  • egograph/pipelines/api/google_health.py
  • egograph/pipelines/config.py
  • egograph/pipelines/service.py
  • egograph/pipelines/sources/google_health/__init__.py
  • egograph/pipelines/sources/google_health/auth.py
  • egograph/pipelines/sources/google_health/client.py
  • egograph/pipelines/sources/google_health/models.py
  • egograph/pipelines/sources/google_health/repository.py
  • egograph/pipelines/sources/google_health/token_cipher.py
  • egograph/pipelines/tests/unit/google_health/test_api.py
  • egograph/pipelines/tests/unit/google_health/test_client.py

Walkthrough

Google Fitbit Air のヘルスデータをEgoGraphに取り込むためのGoogle Health API OAuth連携基盤(Phase 1)を実装。OAuthフロー、トークン暗号化、API呼び出しのリトライ・トークン更新機構、REST APIエンドポイント、包括的テストを導入。

Changes

Google Health OAuth接続基盤

Layer / File(s) Summary
ドメインモデルと暗号化基盤
egograph/pipelines/sources/google_health/models.py, egograph/pipelines/sources/google_health/token_cipher.py, egograph/pipelines/sources/google_health/data_types.py
ConnectionStatusGoogleHealthConnectionOAuthTokenEncryptedOAuthTokenドメインモデル定義。FernetベースのTokenCipherによる平文トークンの暗号化・復号機能。DataCategoryDataTypeレジストリ。
永続化レイヤーとリポジトリ
egograph/pipelines/infrastructure/db/schema.py, egograph/pipelines/sources/google_health/repository.py
SQLiteスキーマにgoogle_health_connectionsgoogle_health_oauth_tokensgoogle_health_oauth_statesgoogle_health_sync_cursorsテーブル追加。GoogleHealthRepositoryによる接続・トークン・state管理、CRUD操作、カスケード削除。
OAuth認可コードフロー実装
egograph/pipelines/sources/google_health/auth.py, egograph/pipelines/infrastructure/logging_filters.py, egograph/pipelines/config.py, egograph/pipelines/pyproject.toml
GoogleHealthAuthによるOAuth開始・完了・トークン交換処理。state生成・検証・消費。トークン応答パース・暗号化・永続化。PipelinesConfigにOAuth設定フィールド追加。OAuthCallbackAccessLogFilterでcode/stateをログから伏せ。Fernet依存追加。
Google Health APIクライアント実装
egograph/pipelines/sources/google_health/client.py
GoogleHealthAPIClientによる例外階層(認証/レート制限/サーバ/ネットワーク)。トークン有効期限チェック・更新。API呼び出しのリトライ・指数バックオフ。401時の一度きりリトライ。429/5xxの自動リトライ。接続状態更新。
REST APIエンドポイントとアプリ統合
egograph/pipelines/api/google_health.py, egograph/pipelines/app.py
/v1/sources/google-health 配下に6エンドポイント(/auth/start/auth/callback/connection GET/DELETE、/connection/smoke-test)。APIキー検証、503/502/409エラー処理。app.pyにフィルタ初期化とルーター統合。
PipelineService依存注入
egograph/pipelines/service.py
google_health_repositorygoogle_health_auth(任意)、google_health_client(任意)フィールド追加。設定有効時のみTokenCipherと各コンポーネント組み立て。
アーキテクチャドキュメントと実装計画
docs/architecture/pipelines.md, docs/data-sources/google-health.md, docs/plan/plan-fitbit.md, egograph/pipelines/.env.example
アーキテクチャ図にGoogle Health APIノード・接続追加。5エンドポイントをAPI表に記載。Google Healthデータソース設計(スキーマ・パーティション・クエリ例・セットアップ手順)を549行で記載。Phase 1/2/3実装計画を詳細化。環境変数テンプレート更新。
包括的なユニットテスト一式
egograph/pipelines/tests/unit/google_health/test_repository.py, egograph/pipelines/tests/unit/google_health/test_auth.py, egograph/pipelines/tests/unit/google_health/test_client.py, egograph/pipelines/tests/unit/google_health/test_api.py
リポジトリの永続化・state消費・接続更新・カスケード削除。OAuth state一度消費。トークン暗号化・DB非保存。code/state再利用拒否。APIクライアントのトークン更新・リトライ・429分類・接続状態更新。REST APIエンドポイント・ログ伏せ・未設定時503・未接続時409を検証。合計630行超。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes


Poem

🐰 Google Health, ウサギはやっぱり健康大事!
フィットビットの夢を OAuth で暗号化して、
トークンひとつ、リトライもバックオフ、
API呼び出し、状態もすっきり管理🏃
ドキュメント計画、テストも完璧、
接続の基盤、いま完成した! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed プルリクエストのタイトルは「feat: Google Health接続基盤を追加」で、このPRの主要な変更(Google Health OAuth接続基盤の実装)を明確に反映している。短潔で、実装内容の本質を的確に表現している。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/google-health-connection-foundation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@endo-ly endo-ly marked this pull request as ready for review June 10, 2026 14:29
@endo-ly endo-ly force-pushed the feat/google-health-connection-foundation branch 2 times, most recently from a0e1bca to 7d8557f Compare June 10, 2026 14:58
@endo-ly endo-ly force-pushed the feat/google-health-connection-foundation branch from 14b557d to 7713e9c Compare June 10, 2026 15:13
@endo-ly

endo-ly commented Jun 11, 2026

Copy link
Copy Markdown
Owner Author

@CodeRabbit review it

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

了解、レビューするよ!ちょっと待ってね 🐰

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
egograph/pipelines/service.py (1)

81-105: 冗長な None チェックを整理する提案

egograph/pipelines/service.py の Lines 81-105 で google_health_is_configured が True のあとに、client_id/client_secret/token_encryption_key/redirect_uri をさらに is not None で再チェックしてる。PipelinesConfig.google_health_is_configuredall((self.google_health_client_id, ...)) で各フィールドの「None じゃないこと」しか見てないので、二重の None チェックは冗長。

(うさぎ視点だと)必要なのが None 以外(例: 空文字の弾き)なら get_secret_value() 側の検証に寄せて、現在の二重チェックは削る選択肢がある。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@egograph/pipelines/service.py` around lines 81 - 105, The inner redundant
None checks after google_health_is_configured should be removed: rely on
PipelinesConfig.google_health_is_configured to guarantee presence of
google_health_client_id, google_health_client_secret,
google_health_token_encryption_key and google_health_redirect_uri, so eliminate
the if block that re-checks client_id/client_secret/encryption_key/redirect_uri
for is not None and proceed to construct TokenCipher, GoogleHealthAuth and
GoogleHealthAPIClient directly; if you need to validate empty-string secrets
instead, perform that validation via get_secret_value() (or in
TokenCipher/GoogleHealthAuth constructors) rather than duplicating None checks
here (refer to TokenCipher, GoogleHealthAuth, GoogleHealthAPIClient and
google_health_is_configured).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/plan/plan-fitbit.md`:
- Line 99: ドキュメントのテーブルで示している `api/routes/google_health.py`
のパスが実際の実装と一致していないので、ドキュメントの該当行(テーブルの該当セル)を実装で追加された実ファイル名に更新してください:リポジトリ内で今回のPRで追加されたGoogle
Health関連のルート実装ファイル名(例:実際にコミットされたモジュール名)を確認し、docs/plan/plan-fitbit.md
の該当テーブル行のパス文字列をその実ファイル名に置き換え、表の整合性を保ってください。

In `@egograph/pipelines/api/google_health.py`:
- Line 55: 現状の raise HTTPException(..., detail=str(exc)) を直接透過する代わりに、発生した例外 (変数名
exc を使用している箇所) を明示的にマップして "invalid_<field>: <reason>" 形式の detail
に変換してから例外を再送出してください;具体的には raise 文を含む箇所(現在 detail=str(exc) を使っている raise
HTTPException(... from exc) のすべて)を修正し、もし exc が pydantic ValidationError なら
e.errors() をループして各エントリから field と msg を取り "invalid_<field>: <reason>"
に組み立ててセミコロンで連結、その他の型の例外は既知の属性(例えば .field,
.message)から同様に抽出してフォーマットし、いずれにも当てはまらない場合は "invalid_request: <短いメッセージ>" を detail
として設定するようにしてください(該当 raise を含む箇所はファイル内の raise HTTPException(..., detail=str(exc))
を置き換えてください)。

In `@egograph/pipelines/config.py`:
- Around line 57-63: google_health_is_configured currently relies on truthiness
of SecretStr objects so values like SecretStr("   ") pass; change it to
explicitly check each secret's string value after unwrapping and stripping. For
the four fields (google_health_client_id, google_health_client_secret,
google_health_redirect_uri, google_health_token_encryption_key) call
get_secret_value() and verify .strip() != "" (e.g., ensure each
get_secret_value().strip() is non-empty) so the config boolean only returns True
for non-blank values; update any usages that assume the previous truthiness
accordingly (e.g., service.py flow that calls get_secret_value()).

In `@egograph/pipelines/sources/google_health/__init__.py`:
- Line 1:
モジュールレベルの英語docstringが残っているため、egograph.pipelines.sources.google_healthパッケージ内の__init__モジュール(モジュールレベルdocstring)と同パッケージ内の他2ファイルにあるモジュール先頭の英語docstringを日本語に統一してください;具体的には各ファイルの先頭にある三重引用符で囲まれたモジュールdocstring(例:
__init__ のトップにある """Google Health API
integration.""")を日本語の説明に置き換え、文字エンコーディングやPEP257に準拠した形式(トレーリング空行や一行要約の有無)を保つよう修正してください。

In `@egograph/pipelines/sources/google_health/client.py`:
- Line 60: The retry loop in the request method that uses the max_attempts
parameter mishandles the 401+token-refresh path: after refreshing the token the
code just continues which still consumes the one allowed attempt and with
max_attempts=1 falls through to the generic error handler. Modify the retry
logic in the function that accepts max_attempts (the request method in
client.py) so that a successful token refresh does not count as a consumed
attempt—either reset or increment the attempt counter appropriately or restart
the loop without decrementing attempts when you hit the 401 refresh branch;
ensure the refreshed-token path retries the request and can succeed even when
max_attempts==1.

---

Nitpick comments:
In `@egograph/pipelines/service.py`:
- Around line 81-105: The inner redundant None checks after
google_health_is_configured should be removed: rely on
PipelinesConfig.google_health_is_configured to guarantee presence of
google_health_client_id, google_health_client_secret,
google_health_token_encryption_key and google_health_redirect_uri, so eliminate
the if block that re-checks client_id/client_secret/encryption_key/redirect_uri
for is not None and proceed to construct TokenCipher, GoogleHealthAuth and
GoogleHealthAPIClient directly; if you need to validate empty-string secrets
instead, perform that validation via get_secret_value() (or in
TokenCipher/GoogleHealthAuth constructors) rather than duplicating None checks
here (refer to TokenCipher, GoogleHealthAuth, GoogleHealthAPIClient and
google_health_is_configured).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: af25cf6b-cbe5-4f1e-97bf-914587bf25dd

📥 Commits

Reviewing files that changed from the base of the PR and between d9072f3 and 7713e9c.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (23)
  • docs/architecture/pipelines.md
  • docs/data-sources/google-health.md
  • docs/plan/plan-fitbit.md
  • egograph/pipelines/.env.example
  • egograph/pipelines/api/google_health.py
  • egograph/pipelines/app.py
  • egograph/pipelines/config.py
  • egograph/pipelines/infrastructure/db/schema.py
  • egograph/pipelines/infrastructure/logging_filters.py
  • egograph/pipelines/pyproject.toml
  • egograph/pipelines/service.py
  • egograph/pipelines/sources/google_health/__init__.py
  • egograph/pipelines/sources/google_health/auth.py
  • egograph/pipelines/sources/google_health/client.py
  • egograph/pipelines/sources/google_health/data_types.py
  • egograph/pipelines/sources/google_health/models.py
  • egograph/pipelines/sources/google_health/repository.py
  • egograph/pipelines/sources/google_health/token_cipher.py
  • egograph/pipelines/tests/unit/google_health/__init__.py
  • egograph/pipelines/tests/unit/google_health/test_api.py
  • egograph/pipelines/tests/unit/google_health/test_auth.py
  • egograph/pipelines/tests/unit/google_health/test_client.py
  • egograph/pipelines/tests/unit/google_health/test_repository.py

Comment thread docs/plan/plan-fitbit.md Outdated
Comment thread egograph/pipelines/api/google_health.py Outdated
Comment thread egograph/pipelines/config.py Outdated
Comment thread egograph/pipelines/sources/google_health/__init__.py Outdated
Comment thread egograph/pipelines/sources/google_health/client.py
@endo-ly endo-ly merged commit b86df11 into main Jun 12, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant