Skip to content

profile-transfer: Profile 移管 (個人→組織) — 2段階フロー + 所有移転 + 参照ファイルコピー要求 - #62

Merged
turtton merged 4 commits into
mainfrom
profile-transfer
Sep 3, 2026
Merged

turtton merged 4 commits into
mainfrom
profile-transfer

Conversation

@turtton

@turtton turtton commented Sep 3, 2026

Copy link
Copy Markdown
Member

Goal

Closes #61

個人の Profile を組織へ移管する 2段階フロー (申請 → 承認) を実装。承認時に Profile の所有 (account_id) が組織へ移転し、参照ファイル (icon/banner) のコピー要求が port に発行される。あわせて profiles.account_id を部分ユニーク化し、組織が複数 Profile を持てるようにする。

What changed

  • ProfileTransferRequest aggregate (kernel): Requested/Accepted/Rejected/Cancelled イベント + pending → accepted|rejected|cancelled 状態遷移。ES 永続化は AccountReport と同型 (profile_transfer_request_events + AggregateRepository + read model)。tailing projector は持たない (外部イベント producer がなく、use case が同一 tx で read model を同期更新するため — close_report と同パターン)。
  • ProfileEvent::AccountTransferred (kernel): Profile::transfer_account (last-write-wins) + apply で account_id を付け替え。acct/Actor URI・nanoid・署名鍵・フォロー関係は Profile スコープで不変 (grill Q6)。serde 後方互換 (既存 profile_events 行はそのまま読める)。
  • Use cases (application): request / accept / reject / cancel。申請=プロフィール所有者かつ移管先 org の active メンバー、承認/却下=移管先 org の Owner/Admin (個人 identity のメンバーロール判定)、取消=申請者本人 (pending のみ)。受理後の取消・却下・再承認は 422。
  • REST API (server): POST /api/v1/profiles/{profile_nanoid}/transfer-requests (201)、POST /api/v1/profile-transfer-requests/{nanoid}/accept|reject|cancel (204)。OpenAPI 登録 + contract test + 仕様再生成済み。
  • Migration 20260904000001_profile_transfer.sql: profiles.owner_kind 非正規化カラム (accounts.kind から backfill、read model SQL が subselect で維持) + profiles.account_id の全件 UNIQUE を WHERE owner_kind='personal' の部分ユニーク index に置換。個人 1:1 は維持、組織は複数 Profile を持てる。移管申請の二重 pending は DB の部分ユニーク index でも backstop。
  • ファイルコピー要求 port: kernel::interfaces::storage::ProfileMediaCopyGateway (request_copy(ProfileMediaCopyRequest { from_account_id, to_account_id, image_ids }))。実装は NoopProfileMediaCopyGateway stub (driver)。承認 tx commit 後に icon/banner の ImageId を集めて発行 (空なら skip、失敗は warn ログのみ)。Booskiff 実連携は別 packet。

影響ノート

  • CreateOrganizationProfileUseCase の org 重複プロフィール拒否ガードを削除 (複数所持が仕様化)。既存テスト create_second_profile_for_same_organization_* を新仕様 (201 成功) に書き換え済み。
  • org が複数 Profile を持つ場合、read model find_by_account_id は従来通り単一返し (任意の1件)。複数列挙・選択 UI は後続 packet の論点。
  • 重複申請ガードは read model 参照 (422) + DB 部分ユニーク index の二本立て。極めて稀な同時実行レースでは 422 でなく 500 が返り得るが、整合性は index が保証。

Verification

  • cargo fmt --all --check / cargo check --workspace / cargo clippy --workspace -- -D warnings 全緑
  • cargo test --workspace (実 Postgres 16, migration を空 DB から適用): kernel 166 / driver 157 / application 104 / server 67 passed, 0 failed
  • write_openapi_spec_to_file 再生成 ↔ コミット済み openapi.json の no-diff 確認済み (CI openapi-check 対応)
  • driver 実 DB テストで部分ユニークを直接検証 (org 複数 Profile 可 / personal 2件目は unique 違反)
  • ユースケース単体テスト 28 件 (申請→承認・却下・取消・重複拒否・権限拒否・コピー port 呼び出し検証・複数個人アカウント時の反復解決) + kernel entity テスト 6 件

Closeout writeback 想定 (host 側)

  • ADR 0007: ProfileEvent::AccountTransferred + profiles.account_id 部分ユニーク化 + コピー port/stub、及び非正規化 owner_kind カラム (Postgres partial index predicate は他テーブル参照不可のため)
  • packets.md unit 3 へ issue/PR リンク追記

@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.73913% with 765 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.47%. Comparing base (c696210) to head (3c9c4b9).

Files with missing lines Patch % Lines
...c/service/profile_transfer/test_support/account.rs 12.90% 216 Missing ⚠️
...rc/service/profile_transfer/test_support/module.rs 48.53% 123 Missing ⚠️
...c/service/profile_transfer/test_support/profile.rs 55.79% 61 Missing ⚠️
server/src/route/profile_transfer/client.rs 0.00% 58 Missing ⚠️
server/src/api/profile_transfer.rs 0.00% 52 Missing ⚠️
application/src/service/profile_transfer/mod.rs 68.80% 39 Missing ⚠️
...e/postgres/profile_transfer_request_event_store.rs 85.49% 28 Missing ⚠️
...se/postgres/profile_transfer_request_repository.rs 0.00% 28 Missing ⚠️
kernel/src/entity/profile_transfer_request.rs 89.25% 26 Missing ⚠️
...c/service/profile_transfer/test_support/request.rs 83.73% 20 Missing ⚠️
... and 13 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #62      +/-   ##
==========================================
- Coverage   76.28%   75.47%   -0.81%     
==========================================
  Files         211      232      +21     
  Lines       24930    27216    +2286     
==========================================
+ Hits        19018    20542    +1524     
- Misses       5912     6674     +762     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@turtton turtton added intent-target intent-cli workflow label intent-pr-approved intent-cli workflow label labels Sep 3, 2026
@turtton
turtton merged commit a34f524 into main Sep 3, 2026
12 checks passed
@turtton
turtton deleted the profile-transfer branch September 3, 2026 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

intent-pr-approved intent-cli workflow label intent-target intent-cli workflow label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

profile-transfer: Profile 移管 (個人→組織) — 2段階フロー + 所有移転 + 参照ファイルコピー要求

1 participant