feat(backend): migration mekanizması, indeksler ve ortak model yardımcıları
- db/migrations.rs: schema_migrations ile sürümlü migration (v1: koleksiyonlar) - db/indexes.rs: DATA_MODEL indeksleri + senkronizasyon indeksi, açılışta otomatik - models/common.rs: UUID v7, ortak alanlar, hane kapsamlı filtre - Birim testleri (4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
//! Basit şema migration mekanizması (DATA_MODEL.md §3.15).
|
||||
//!
|
||||
//! Her migration bir kez çalışır; `schema_migrations` koleksiyonuna
|
||||
//! `{ _id: sürüm, name, applied_at }` olarak kaydedilir.
|
||||
//! Yeni migration eklemek: `MIGRATIONS` listesine bir sonraki sürüm numarasıyla ekle.
|
||||
|
||||
use std::{future::Future, pin::Pin};
|
||||
|
||||
use mongodb::{
|
||||
Database,
|
||||
bson::{DateTime, Document, doc},
|
||||
error::Result,
|
||||
};
|
||||
|
||||
use super::col;
|
||||
|
||||
type MigrationFn =
|
||||
for<'a> fn(&'a Database) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>;
|
||||
|
||||
struct Migration {
|
||||
version: i32,
|
||||
name: &'static str,
|
||||
run: MigrationFn,
|
||||
}
|
||||
|
||||
/// Sıralı migration listesi. Sürümler 1'den başlar ve atlamadan artar.
|
||||
const MIGRATIONS: &[Migration] = &[Migration {
|
||||
version: 1,
|
||||
name: "initial",
|
||||
run: |db| Box::pin(m001_initial(db)),
|
||||
}];
|
||||
|
||||
/// Uygulanmamış migration'ları sırayla çalıştırır.
|
||||
pub async fn run(db: &Database) -> Result<()> {
|
||||
let coll = db.collection::<Document>(col::SCHEMA_MIGRATIONS);
|
||||
|
||||
for m in MIGRATIONS {
|
||||
if coll.find_one(doc! { "_id": m.version }).await?.is_some() {
|
||||
continue;
|
||||
}
|
||||
tracing::info!("Migration çalışıyor: v{} ({})", m.version, m.name);
|
||||
(m.run)(db).await?;
|
||||
coll.insert_one(doc! {
|
||||
"_id": m.version,
|
||||
"name": m.name,
|
||||
"applied_at": DateTime::now(),
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// v1: Tüm koleksiyonları açıkça oluşturur.
|
||||
/// (Transaction içinde ilk kez yazılan koleksiyonlar sorun çıkarmasın diye önceden var olsunlar.)
|
||||
async fn m001_initial(db: &Database) -> Result<()> {
|
||||
let existing = db.list_collection_names().await?;
|
||||
let all = col::HOUSEHOLD_SCOPED
|
||||
.iter()
|
||||
.chain(&[col::EXCHANGE_RATES, col::SCHEMA_MIGRATIONS]);
|
||||
|
||||
for name in all {
|
||||
if !existing.iter().any(|e| e == name) {
|
||||
db.create_collection(*name).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn versions_start_at_1_and_are_sequential() {
|
||||
for (i, m) in MIGRATIONS.iter().enumerate() {
|
||||
assert_eq!(
|
||||
m.version,
|
||||
i as i32 + 1,
|
||||
"migration sürümleri ardışık olmalı"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user