Skip to content

基本APIの実装#7

Merged
turtton merged 78 commits intomainfrom
feat/rest-basic
Mar 15, 2026
Merged

基本APIの実装#7
turtton merged 78 commits intomainfrom
feat/rest-basic

Conversation

@turtton
Copy link
Member

@turtton turtton commented Jan 25, 2025

  • methods
    • /account
    • /user
    • /metadata
  • キャッシュをget_by_idsのように全部をlist受け取りにする段階で拡張する
  • Accountにadapor層を生やす
  • 他のモデルの修正(eventテーブルの分離)
  • Transaction->Executor
  • AuthAccount作成フローの構築
  • ketos導入
  • Deleteなんてドメイン上なくて、suspendとかbanとか色んな状態が考えられるよねってのに思いを馳せる
  • ユーザーroleシステム実装
  • Accountのsuspend/ban
  • yutoipa入れる
  • 外部にIDを露出させないようにする(nanoidメインにする)
  • UUIDv7をsnowflakeに置き換え
  • テスト内で利用するランダム文字列の改善
  • cargo nextestでテストできるように
  • clippy警告修正
  • sql migrationsの統合

@codecov
Copy link

codecov bot commented Jan 25, 2025

Codecov Report

❌ Patch coverage is 55.20665% with 2211 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.83%. Comparing base (fcf9689) to head (149875a).

Files with missing lines Patch % Lines
application/src/service/profile.rs 0.00% 232 Missing ⚠️
application/src/service/metadata.rs 0.00% 218 Missing ⚠️
server/src/route/account.rs 0.00% 165 Missing ⚠️
application/src/service/account.rs 0.00% 158 Missing ⚠️
server/src/route/metadata.rs 11.76% 135 Missing ⚠️
server/src/route/profile.rs 23.07% 130 Missing ⚠️
server/src/handler.rs 0.00% 120 Missing ⚠️
adapter/src/processor/account.rs 0.00% 96 Missing ⚠️
adapter/src/processor/profile.rs 0.00% 96 Missing ⚠️
adapter/src/processor/metadata.rs 0.00% 79 Missing ⚠️
... and 36 more
Additional details and impacted files
@@             Coverage Diff             @@
##             main       #7       +/-   ##
===========================================
- Coverage   95.08%   66.83%   -28.26%     
===========================================
  Files          58       71       +13     
  Lines        3888     7095     +3207     
===========================================
+ Hits         3697     4742     +1045     
- Misses        191     2353     +2162     

☔ View full report in Codecov by Sentry.
📢 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 added 23 commits March 12, 2026 10:37
Phase 1: Keycloak → Ory 移行

- Keycloak 依存を全て削除 (axum-keycloak-auth, KeycloakAuthLayer, KeycloakToken, expect_role!)
- JWT 検証ミドルウェア実装 (OIDC Discovery, JWKS キャッシュ, RS256)
- OAuth2 Login/Consent Provider エンドポイント (GET/POST /oauth2/login, /oauth2/consent)
- Hydra Admin API クライアント + Kratos セッションクライアント
- ルートハンドラ書き換え (AuthClaims ベース)
- Handler/AppModule に Hydra/Kratos クライアント統合
- compose.yml で開発環境一括起動 (PostgreSQL, Redis, Kratos, Hydra)
- E2E 検証完了 (Kratos login → OAuth2 flow → JWT → API access)
wiremockでHydra/Kratosをモックし、OAuth2エンドポイントの統合テスト10件を追加。
- GET /oauth2/login: skip/session有効/cookie無し/session無効の4ケース
- GET /oauth2/consent: skip/client skip/通常表示の3ケース
- POST /oauth2/consent: accept/scope不正/rejectの3ケース

その他の変更:
- tower 0.4→0.5にアップグレード(axum 0.7互換)
- Handler::init_with_urls()を抽出しコンストラクタの重複を解消
dotenvy::varでDATABASE_URLの存在を確認し、未設定時はテストをスキップするように変更。
CIなどDB未起動環境でテストが失敗しないようにする。
test_withのコンパイル時envチェックにより、DATABASE_URL未設定時は
テストがignoredとして正しく表示されるようにした。
kernel層にPermission/PermissionReq/Relation/Resource型とPermissionChecker/PermissionWriter traitを定義。
driver層にKetoClient (HTTP API実装)を追加。
application層にポリシー関数(account_view/edit/delete/sign, instance_moderate)とcheck_permissionヘルパーを追加。

全UseCase traitのインライン所有者チェック(find_by_auth_id)をKeto経由のpermissionチェックに置換。
Account作成時にowner tupleを自動作成、削除時に自動削除。

インフラ: compose.ymlにketo-migrate/ketoサービス追加、ory/keto/keto.yml設定ファイル追加。
- Account: Deleted → Deactivated (論理削除、entity は Some のまま deleted_at をセット)
  - serde alias "deleted" で旧イベント後方互換性を維持
  - 二重退会防止ガード追加
- Profile/AuthAccount: 独立した Deleted イベントを除去(ドメイン的に不要)
- AccountApplier: 退会時のカスケード処理を追加
  - Profile 削除、全 Metadata 削除、全 Follow 削除、auth link 解除
- AccountReadModel: delete() → deactivate() + unlink_all_auth_accounts() 追加
- update() SQL に deleted_at カラムを追加
- DeleteProfileUseCase を完全削除、Profile の DELETE エンドポイントを除去
- DeleteAccountUseCase → DeactivateAccountUseCase にリネーム
個別ID取得エンドポイントをバッチ取得APIに統一し、N+1問題を解消。

- GET /accounts?ids=a,b,c でアカウントのバッチ取得(既存のページネーション一覧と共存)
- GET /profiles?account_ids=a,b,c でプロフィールのバッチ取得
- GET /metadata?account_ids=a,b,c でメタデータのバッチ取得
- GET /accounts/:id, GET /accounts/:account_id/profile(GET), GET /accounts/:account_id/metadata(GET) を削除
- CUD routes (POST/PUT/DELETE) は変更なし

各層の変更:
- kernel: ReadModelにfind_by_nanoids/find_by_account_idsトレイトメソッド追加
- driver: WHERE ... = ANY($1) によるPostgreSQL実装
- adapter: QueryProcessorにバッチメソッド追加(blanket impl)
- application: DTOにaccount_nanoidフィールド追加、バッチUseCaseメソッド追加
- server: バッチハンドラ追加、バッチサイズ上限(100)、空文字列フィルタ、ids+pagination排他チェック

権限チェックはアカウント単位でイテレートし、権限のないアカウントは結果から除外。
Relation enumにリソース関連(Owner, Editor, Signer)とインスタンスRole(Admin, Moderator)が
混在していたため、型安全に分離。

- Relation → AccountRelation + InstanceRole に分離
- Resource 削除、PermissionReq を struct → enum 化
- PermissionWriter用に RelationTarget enum を導入
- namespace/object_id の定数を共通化(ACCOUNT_NAMESPACE, INSTANCE_NAMESPACE, INSTANCE_OBJECT_ID)
- Keto 5xx レスポンス時にサイレント拒否ではなくエラーを返すように修正
モデレーション機能として、アカウントの Suspend(一時停止)/ Ban(永久追放)操作を実装。

- AccountStatus enum (Active/Suspended/Banned) を追加し、Account エンティティに status フィールドを導入
- AccountEvent に Suspended/Unsuspended/Banned バリアントを追加(タイムスタンプはイベントペイロードに含めてリプレイ決定性を保証)
- KernelError::Rejected を追加(状態遷移エラー用、422 にマッピング)
- AccountReadModel に unfiltered クエリ(suspended/banned 含む)と suspend/unsuspend/ban 操作を追加
- 通常クエリ(find_by_id/name/nanoid/nanoids)は suspended/banned を除外、find_by_auth_id は除外しない(自分の停止状態確認用)
- 期限付き suspend は SQL クエリ時 + From<AccountRow> 変換時の両方で期限切れ判定
- SuspendAccountUseCase / UnsuspendAccountUseCase / BanAccountUseCase を追加(instance_moderate 権限必須)
- POST /accounts/:id/suspend, /unsuspend, /ban エンドポイントを追加
- reason の長さバリデーション(1-1000文字)、expires_at の未来日バリデーションを実装
- マイグレーションに CHECK 制約を追加(suspend/ban フィールドの整合性保証)
- Applier を find_by_id_unfiltered に切り替え、suspended/banned 状態の projection 更新に対応
routeモジュールからRequest/Response構造体と変換関数をschemaモジュールに分離
- utoipa 5.4.0を導入し、全ルートハンドラに#[utoipa::path]アトリビュートを追加
- スキーマ型にToSchema deriveを追加(account, profile, metadata, oauth2)
- openapi.rsにOpenApi定義を集約、SecurityScheme(Bearer JWT)を設定
- create_accountのレスポンスを200→201に修正(他のcreateエンドポイントと統一)
- CIでopenapi.jsonの差分チェックジョブを追加(openapi-check)
- openapi.json生成テストは#[ignore]で通常テストから除外
Profileレスポンスのicon/bannerフィールドでImageId(UUIDv7)が外部APIに露出していた問題を修正。
- icon_id/banner_id (UUID) → icon_url/banner_url (String) に変更
- リクエスト側もURL文字列で画像を指定するように変更
- ImageRepositoryを使ってURL↔ImageIdの相互変換を実装
- UpdateProfileRequestでOption<Option<String>>による3状態サポート
  (absent=変更なし, null=クリア, "url"=設定)
- EventApplierでicon/bannerのクリア(Some(None))に対応
- 空URL入力のバリデーション追加
- 参照先Image消失時のwarningログ追加
- create_profileのレスポンスをDB解決URLで返すよう修正
ImageRepositoryにfind_by_idsバッチメソッドを追加し、get_profiles_batchでの
プロファイルごとの個別画像クエリ(2N回)を1回のバッチクエリに置換。
重複ImageIdはHashSetで除去してからクエリに渡すよう最適化。
Profile更新時のnullable fieldの3状態(変更なし/クリア/設定)を
Option<Option<T>>から明示的なFieldAction<T> enumに置き換え。

- kernel: FieldAction<T> {Unchanged, Clear, Set(T)} を定義
- kernel: ProfileEvent::Updated, Profile::update()で使用
- adapter: ProfileCommandProcessor::updateシグネチャ変更
- application: EditProfileUseCase, resolve_field_action_image_id
- server: schema層にinto_field_action()変換関数を配置
- serde互換: skip_serializing_if + defaultで既存イベントと後方互換
全エンティティIDをUUIDv7(128bit, PostgreSQL UUID)からferroid Snowflake ID
(64bit, PostgreSQL BIGINT)に移行。

- kernel: Snowflake ID基盤モジュール(id.rs)追加、カスタムエポック2026-01-01
  - define_snowflake_id! マクロで1+41+10+12ビットレイアウト定義
  - OnceLockベースのグローバルジェネレータ(スレッドセーフ)
  - WORKER_IDバリデーション(0-1023)
  - ensure_generator_initialized()をtest-utils feature gateに制限
- kernel: 全エンティティID型、EventId、EventVersionをUuid→i64に変更
- kernel: CreatedAtのタイムスタンプ抽出をfrom_timestamp_ms()に変更
- driver: 全Row構造体、EventStore、ReadModel、RepositoryをUuid→i64に変更
- adapter/application: uuid依存削除、generate_id()使用に移行
- server: main.rsにinit_generator(worker_id)追加
- migrations: 全テーブルのUUID列をBIGINTに変更、event_streams削除
- dead feature flags(uuid, time)をkernel/Cargo.tomlから削除
kernel/src/test_utils/ にBuilderパターンのテストデータファクトリを導入し、
全クレートのテストコードを統一的なAPIで書き換え。

- AccountBuilder, ProfileBuilder, MetadataBuilder 等の各エンティティ用Builder
- account_create_command() 等のCommandEnvelopeファクトリ
- unique_account_name(), unique_image_url() 等のユニーク値ヘルパー
- 意味のあるデフォルト値定数 (DEFAULT_ACCOUNT_NAME="alice" 等)
- 重複テストヘルパー関数の削除 (make_account, make_profile, url, acct_url 等)
- test-utils featureゲートで本番ビルドへの混入を防止

約600行の削減、テストの可読性と一貫性を向上。
nextest はテストごとに別プロセスで実行するため、プロセスローカルな
AtomicUsize カウンターが毎回0からスタートし、UNIQUE制約違反が発生していた。

- next_unique() を AtomicUsize から Snowflake ID 生成に変更
- テスト用 ensure_generator_initialized() の worker_id を PID ベースに変更し、
  プロセス間での Snowflake ID 衝突を回避
- workspace resolverを"2"に設定
- clippy警告を修正(too_many_arguments, useless_conversion, derivable_impls, needless_lifetimes)
- テスト専用コードに#[cfg(test)]を付与
- デシリアライズ用構造体にallow(dead_code)を理由付きで付与
- OAuth2 loginフローでKratosセッション検証失敗時にHydraへreject_loginを通知するよう修正(401を返すのではなくリダイレクトURLを返す)
各層に専用のデータ構造を導入し、関数引数を構造体にまとめた。
- adapter層: *Param (CreateProfileParam, UpdateProfileParam 等)
- application層: 入力用 *Dto (CreateProfileDto, UpdateProfileDto 等)
- server層: Request に into_dto() メソッドを追加

併せて以下も実施:
- Edit*UseCase を Update*UseCase にリネーム (adapter/kernel との命名統一)
- CLAUDE.md の UseCase トレイト名を実態に合わせて更新
- clippy::too_many_arguments の #[allow] を削除
- 6つのマイグレーションファイルを1つに統合
- accountsテーブルにモデレーション列とCHECK制約を初期定義に含める
- profiles.account_idのUNIQUE制約を初期定義に含める
- 4つのイベントテーブル(account/auth_account/profile/metadata)を統合
- auth_emumet_accounts.auth_idにON DELETE CASCADEを追加(既存バグ修正)
- 不要なdbml関連ファイル(init.dbml, gensql.sh, dbml-error.log)を削除
@turtton turtton marked this pull request as ready for review March 15, 2026 08:28
turtton added 3 commits March 15, 2026 18:24
- begin_transaction → get_executor にリネーム(実態に合った命名)
- CORS設定を環境変数(CORS_ALLOWED_ORIGINS)から読み込むよう変更
- Keto設定からハードコードDSNを削除(compose.ymlの環境変数を使用)
- Suspend/Ban済みアカウントへのmutation操作を拒否するステータスチェック追加
- ProfileのUpdatedイベントでdisplay_name/summaryをFieldAction<T>に変更(クリア操作対応)
- CIにadapterクレートを追加
- 未使用依存関係を削除(async-trait, rand, zeroize, tower→dev-deps)
- Axumルートパスを非推奨の:param構文から{param}構文に移行
- GET /accountsの空結果を404から200+空リストに変更
- 重複テスト属性の削除、Snowflake IDキャストにコメント追加
- EventStoreの楽観的並行制御をCTE化しレースコンディションを緩和
- auth_accountsに(host_id, client_id) UNIQUE制約、followsに排他CHECK制約追加
- AccountPrivateKey/EncryptedPrivateKeyのDebug出力をマスク
- KeyEncryptor::decryptの戻り値をZeroizing<Vec<u8>>に変更
- Deactivate時に全リレーション(Owner/Editor/Signer)を削除
- AuthAccount ReadModel失敗時にSignal発行でリカバリ対応
- ReadModel/Repositoryのupdate/deleteに影響行数チェック追加
- Keto 4xxレスポンスをエラーとして処理
- 入力バリデーション追加(最大長チェック)
- 値オブジェクトにvalidate()メソッド追加(空文字/URL形式検証)
- AuthAccountReadModelからupdate/delete削除(YAGNI)
- Account Applierにパーミッション同期追加
- Signal失敗ログをerrorレベルに統一
- metadatas.account_idインデックス追加
- PREVページネーションで昇順統一
- password.rsのTOCTOU修正、JwksCache fetch失敗時rate limit適用
- ErrorStatusにログ出力追加、clippy警告修正
@turtton turtton merged commit f6c0ff2 into main Mar 15, 2026
9 of 10 checks passed
@turtton turtton deleted the feat/rest-basic branch March 15, 2026 15:54
@turtton turtton linked an issue Mar 15, 2026 that may be closed by this pull request
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.

RestAPIまで実装する(AP関連を除く)

1 participant