trying to rework database migrations

This commit is contained in:
2026-02-05 11:42:04 -06:00
parent 3b14c65998
commit 343488b3eb
83 changed files with 5793 additions and 876 deletions

View File

@@ -18,7 +18,6 @@ pub use inquiry::*;
pub use key::*;
pub use notification::*;
pub use pack::*;
pub use pack_installation::*;
pub use pack_test::*;
pub use rule::*;
pub use runtime::*;
@@ -195,49 +194,20 @@ pub mod pack {
pub tags: Vec<String>,
pub runtime_deps: Vec<String>,
pub is_standard: bool,
pub created: DateTime<Utc>,
pub updated: DateTime<Utc>,
}
}
/// Pack installation metadata model
pub mod pack_installation {
use super::*;
use utoipa::ToSchema;
#[derive(Debug, Clone, Serialize, Deserialize, FromRow, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct PackInstallation {
pub id: Id,
pub pack_id: Id,
pub source_type: String,
pub installers: JsonDict,
// Installation metadata (nullable for non-installed packs)
pub source_type: Option<String>,
pub source_url: Option<String>,
pub source_ref: Option<String>,
pub checksum: Option<String>,
pub checksum_verified: bool,
pub installed_at: DateTime<Utc>,
pub checksum_verified: Option<bool>,
pub installed_at: Option<DateTime<Utc>>,
pub installed_by: Option<Id>,
pub installation_method: String,
pub storage_path: String,
pub meta: JsonDict,
pub installation_method: Option<String>,
pub storage_path: Option<String>,
pub created: DateTime<Utc>,
pub updated: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct CreatePackInstallation {
pub pack_id: Id,
pub source_type: String,
pub source_url: Option<String>,
pub source_ref: Option<String>,
pub checksum: Option<String>,
pub checksum_verified: bool,
pub installed_by: Option<Id>,
pub installation_method: String,
pub storage_path: String,
pub meta: Option<JsonDict>,
}
}
/// Runtime model

View File

@@ -36,7 +36,6 @@ pub mod inquiry;
pub mod key;
pub mod notification;
pub mod pack;
pub mod pack_installation;
pub mod pack_test;
pub mod queue_stats;
pub mod rule;
@@ -54,7 +53,6 @@ pub use inquiry::InquiryRepository;
pub use key::KeyRepository;
pub use notification::NotificationRepository;
pub use pack::PackRepository;
pub use pack_installation::PackInstallationRepository;
pub use pack_test::PackTestRepository;
pub use queue_stats::QueueStatsRepository;
pub use rule::RuleRepository;

View File

@@ -32,6 +32,7 @@ pub struct CreatePackInput {
pub tags: Vec<String>,
pub runtime_deps: Vec<String>,
pub is_standard: bool,
pub installers: JsonDict,
}
/// Input for updating a pack
@@ -46,6 +47,7 @@ pub struct UpdatePackInput {
pub tags: Option<Vec<String>>,
pub runtime_deps: Option<Vec<String>>,
pub is_standard: Option<bool>,
pub installers: Option<JsonDict>,
}
#[async_trait::async_trait]
@@ -57,7 +59,10 @@ impl FindById for PackRepository {
let pack = sqlx::query_as::<_, Pack>(
r#"
SELECT id, ref, label, description, version, conf_schema, config, meta,
tags, runtime_deps, is_standard, created, updated
tags, runtime_deps, is_standard, installers,
source_type, source_url, source_ref, checksum, checksum_verified,
installed_at, installed_by, installation_method, storage_path,
created, updated
FROM pack
WHERE id = $1
"#,
@@ -79,7 +84,10 @@ impl FindByRef for PackRepository {
let pack = sqlx::query_as::<_, Pack>(
r#"
SELECT id, ref, label, description, version, conf_schema, config, meta,
tags, runtime_deps, is_standard, created, updated
tags, runtime_deps, is_standard, installers,
source_type, source_url, source_ref, checksum, checksum_verified,
installed_at, installed_by, installation_method, storage_path,
created, updated
FROM pack
WHERE ref = $1
"#,
@@ -101,7 +109,10 @@ impl List for PackRepository {
let packs = sqlx::query_as::<_, Pack>(
r#"
SELECT id, ref, label, description, version, conf_schema, config, meta,
tags, runtime_deps, is_standard, created, updated
tags, runtime_deps, is_standard, installers,
source_type, source_url, source_ref, checksum, checksum_verified,
installed_at, installed_by, installation_method, storage_path,
created, updated
FROM pack
ORDER BY ref ASC
"#,
@@ -136,10 +147,13 @@ impl Create for PackRepository {
let pack = sqlx::query_as::<_, Pack>(
r#"
INSERT INTO pack (ref, label, description, version, conf_schema, config, meta,
tags, runtime_deps, is_standard)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
tags, runtime_deps, is_standard, installers)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
RETURNING id, ref, label, description, version, conf_schema, config, meta,
tags, runtime_deps, is_standard, created, updated
tags, runtime_deps, is_standard, installers,
source_type, source_url, source_ref, checksum, checksum_verified,
installed_at, installed_by, installation_method, storage_path,
created, updated
"#,
)
.bind(&input.r#ref)
@@ -152,6 +166,7 @@ impl Create for PackRepository {
.bind(&input.tags)
.bind(&input.runtime_deps)
.bind(input.is_standard)
.bind(&input.installers)
.fetch_one(executor)
.await
.map_err(|e| {
@@ -261,6 +276,15 @@ impl Update for PackRepository {
has_updates = true;
}
if let Some(installers) = &input.installers {
if has_updates {
query.push(", ");
}
query.push("installers = ");
query.push_bind(installers);
has_updates = true;
}
if !has_updates {
// No updates requested, fetch and return existing pack
return Self::find_by_id(executor, id)
@@ -271,7 +295,7 @@ impl Update for PackRepository {
// Add updated timestamp
query.push(", updated = NOW() WHERE id = ");
query.push_bind(id);
query.push(" RETURNING id, ref, label, description, version, conf_schema, config, meta, tags, runtime_deps, is_standard, created, updated");
query.push(" RETURNING id, ref, label, description, version, conf_schema, config, meta, tags, runtime_deps, is_standard, installers, source_type, source_url, source_ref, checksum, checksum_verified, installed_at, installed_by, installation_method, storage_path, created, updated");
let pack = query
.build_query_as::<Pack>()
@@ -310,7 +334,10 @@ impl PackRepository {
let packs = sqlx::query_as::<_, Pack>(
r#"
SELECT id, ref, label, description, version, conf_schema, config, meta,
tags, runtime_deps, is_standard, created, updated
tags, runtime_deps, is_standard, installers,
source_type, source_url, source_ref, checksum, checksum_verified,
installed_at, installed_by, installation_method, storage_path,
created, updated
FROM pack
ORDER BY ref ASC
LIMIT $1 OFFSET $2
@@ -344,7 +371,10 @@ impl PackRepository {
let packs = sqlx::query_as::<_, Pack>(
r#"
SELECT id, ref, label, description, version, conf_schema, config, meta,
tags, runtime_deps, is_standard, created, updated
tags, runtime_deps, is_standard, installers,
source_type, source_url, source_ref, checksum, checksum_verified,
installed_at, installed_by, installation_method, storage_path,
created, updated
FROM pack
WHERE $1 = ANY(tags)
ORDER BY ref ASC
@@ -365,7 +395,10 @@ impl PackRepository {
let packs = sqlx::query_as::<_, Pack>(
r#"
SELECT id, ref, label, description, version, conf_schema, config, meta,
tags, runtime_deps, is_standard, created, updated
tags, runtime_deps, is_standard, installers,
source_type, source_url, source_ref, checksum, checksum_verified,
installed_at, installed_by, installation_method, storage_path,
created, updated
FROM pack
WHERE is_standard = true
ORDER BY ref ASC
@@ -386,7 +419,10 @@ impl PackRepository {
let packs = sqlx::query_as::<_, Pack>(
r#"
SELECT id, ref, label, description, version, conf_schema, config, meta,
tags, runtime_deps, is_standard, created, updated
tags, runtime_deps, is_standard, installers,
source_type, source_url, source_ref, checksum, checksum_verified,
installed_at, installed_by, installation_method, storage_path,
created, updated
FROM pack
WHERE LOWER(ref) LIKE $1 OR LOWER(label) LIKE $1 OR LOWER(description) LIKE $1
ORDER BY ref ASC
@@ -404,14 +440,131 @@ impl PackRepository {
where
E: Executor<'e, Database = Postgres> + 'e,
{
let exists: (bool,) =
sqlx::query_as("SELECT EXISTS(SELECT 1 FROM pack WHERE ref = $1)")
.bind(ref_str)
.fetch_one(executor)
.await?;
let exists: (bool,) = sqlx::query_as("SELECT EXISTS(SELECT 1 FROM pack WHERE ref = $1)")
.bind(ref_str)
.fetch_one(executor)
.await?;
Ok(exists.0)
}
/// Update installation metadata for a pack
pub async fn update_installation_metadata<'e, E>(
executor: E,
id: i64,
source_type: String,
source_url: Option<String>,
source_ref: Option<String>,
checksum: Option<String>,
checksum_verified: bool,
installed_by: Option<i64>,
installation_method: String,
storage_path: String,
) -> Result<Pack>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let pack = sqlx::query_as::<_, Pack>(
r#"
UPDATE pack
SET source_type = $2,
source_url = $3,
source_ref = $4,
checksum = $5,
checksum_verified = $6,
installed_at = NOW(),
installed_by = $7,
installation_method = $8,
storage_path = $9,
updated = NOW()
WHERE id = $1
RETURNING id, ref, label, description, version, conf_schema, config, meta,
tags, runtime_deps, is_standard, installers,
source_type, source_url, source_ref, checksum, checksum_verified,
installed_at, installed_by, installation_method, storage_path,
created, updated
"#,
)
.bind(id)
.bind(source_type)
.bind(source_url)
.bind(source_ref)
.bind(checksum)
.bind(checksum_verified)
.bind(installed_by)
.bind(installation_method)
.bind(storage_path)
.fetch_one(executor)
.await
.map_err(|e| match e {
sqlx::Error::RowNotFound => Error::not_found("pack", "id", id.to_string()),
_ => e.into(),
})?;
Ok(pack)
}
/// Check if a pack has installation metadata
pub async fn is_installed<'e, E>(executor: E, pack_id: i64) -> Result<bool>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let exists: (bool,) = sqlx::query_as(
"SELECT EXISTS(SELECT 1 FROM pack WHERE id = $1 AND installed_at IS NOT NULL)",
)
.bind(pack_id)
.fetch_one(executor)
.await?;
Ok(exists.0)
}
/// List all installed packs
pub async fn list_installed<'e, E>(executor: E) -> Result<Vec<Pack>>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let packs = sqlx::query_as::<_, Pack>(
r#"
SELECT id, ref, label, description, version, conf_schema, config, meta,
tags, runtime_deps, is_standard, installers,
source_type, source_url, source_ref, checksum, checksum_verified,
installed_at, installed_by, installation_method, storage_path,
created, updated
FROM pack
WHERE installed_at IS NOT NULL
ORDER BY installed_at DESC
"#,
)
.fetch_all(executor)
.await?;
Ok(packs)
}
/// List packs by source type
pub async fn list_by_source_type<'e, E>(executor: E, source_type: &str) -> Result<Vec<Pack>>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let packs = sqlx::query_as::<_, Pack>(
r#"
SELECT id, ref, label, description, version, conf_schema, config, meta,
tags, runtime_deps, is_standard, installers,
source_type, source_url, source_ref, checksum, checksum_verified,
installed_at, installed_by, installation_method, storage_path,
created, updated
FROM pack
WHERE source_type = $1
ORDER BY installed_at DESC
"#,
)
.bind(source_type)
.fetch_all(executor)
.await?;
Ok(packs)
}
}
#[cfg(test)]
@@ -431,6 +584,7 @@ mod tests {
tags: vec!["test".to_string()],
runtime_deps: vec![],
is_standard: false,
installers: serde_json::json!({}),
};
assert_eq!(input.r#ref, "test.pack");

View File

@@ -1,173 +0,0 @@
//! Pack Installation Repository
//!
//! This module provides database operations for pack installation metadata.
use crate::error::Result;
use crate::models::{CreatePackInstallation, Id, PackInstallation};
use sqlx::PgPool;
/// Repository for pack installation metadata operations
pub struct PackInstallationRepository {
pool: PgPool,
}
impl PackInstallationRepository {
/// Create a new PackInstallationRepository
pub fn new(pool: PgPool) -> Self {
Self { pool }
}
/// Create a new pack installation record
pub async fn create(&self, data: CreatePackInstallation) -> Result<PackInstallation> {
let installation = sqlx::query_as::<_, PackInstallation>(
r#"
INSERT INTO pack_installation (
pack_id, source_type, source_url, source_ref,
checksum, checksum_verified, installed_by,
installation_method, storage_path, meta
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING *
"#,
)
.bind(data.pack_id)
.bind(&data.source_type)
.bind(&data.source_url)
.bind(&data.source_ref)
.bind(&data.checksum)
.bind(data.checksum_verified)
.bind(data.installed_by)
.bind(&data.installation_method)
.bind(&data.storage_path)
.bind(data.meta.unwrap_or_else(|| serde_json::json!({})))
.fetch_one(&self.pool)
.await?;
Ok(installation)
}
/// Get pack installation by ID
pub async fn get_by_id(&self, id: Id) -> Result<Option<PackInstallation>> {
let installation =
sqlx::query_as::<_, PackInstallation>("SELECT * FROM pack_installation WHERE id = $1")
.bind(id)
.fetch_optional(&self.pool)
.await?;
Ok(installation)
}
/// Get pack installation by pack ID
pub async fn get_by_pack_id(&self, pack_id: Id) -> Result<Option<PackInstallation>> {
let installation = sqlx::query_as::<_, PackInstallation>(
"SELECT * FROM pack_installation WHERE pack_id = $1",
)
.bind(pack_id)
.fetch_optional(&self.pool)
.await?;
Ok(installation)
}
/// List all pack installations
pub async fn list(&self) -> Result<Vec<PackInstallation>> {
let installations = sqlx::query_as::<_, PackInstallation>(
"SELECT * FROM pack_installation ORDER BY installed_at DESC",
)
.fetch_all(&self.pool)
.await?;
Ok(installations)
}
/// List pack installations by source type
pub async fn list_by_source_type(&self, source_type: &str) -> Result<Vec<PackInstallation>> {
let installations = sqlx::query_as::<_, PackInstallation>(
"SELECT * FROM pack_installation WHERE source_type = $1 ORDER BY installed_at DESC",
)
.bind(source_type)
.fetch_all(&self.pool)
.await?;
Ok(installations)
}
/// Update pack installation checksum
pub async fn update_checksum(
&self,
id: Id,
checksum: &str,
verified: bool,
) -> Result<PackInstallation> {
let installation = sqlx::query_as::<_, PackInstallation>(
r#"
UPDATE pack_installation
SET checksum = $2, checksum_verified = $3
WHERE id = $1
RETURNING *
"#,
)
.bind(id)
.bind(checksum)
.bind(verified)
.fetch_one(&self.pool)
.await?;
Ok(installation)
}
/// Update pack installation metadata
pub async fn update_meta(&self, id: Id, meta: serde_json::Value) -> Result<PackInstallation> {
let installation = sqlx::query_as::<_, PackInstallation>(
r#"
UPDATE pack_installation
SET meta = $2
WHERE id = $1
RETURNING *
"#,
)
.bind(id)
.bind(meta)
.fetch_one(&self.pool)
.await?;
Ok(installation)
}
/// Delete pack installation by ID
pub async fn delete(&self, id: Id) -> Result<()> {
sqlx::query("DELETE FROM pack_installation WHERE id = $1")
.bind(id)
.execute(&self.pool)
.await?;
Ok(())
}
/// Delete pack installation by pack ID
pub async fn delete_by_pack_id(&self, pack_id: Id) -> Result<()> {
sqlx::query("DELETE FROM pack_installation WHERE pack_id = $1")
.bind(pack_id)
.execute(&self.pool)
.await?;
Ok(())
}
/// Check if a pack has installation metadata
pub async fn exists_for_pack(&self, pack_id: Id) -> Result<bool> {
let count: (i64,) =
sqlx::query_as("SELECT COUNT(*) FROM pack_installation WHERE pack_id = $1")
.bind(pack_id)
.fetch_one(&self.pool)
.await?;
Ok(count.0 > 0)
}
}
#[cfg(test)]
mod tests {
// Note: Integration tests should be added in tests/ directory
// These would require a test database setup
}