trying to rework database migrations
This commit is contained in:
@@ -138,6 +138,7 @@ pub async fn create_pack(
|
||||
tags: request.tags,
|
||||
runtime_deps: request.runtime_deps,
|
||||
is_standard: request.is_standard,
|
||||
installers: serde_json::json!({}),
|
||||
};
|
||||
|
||||
let pack = PackRepository::create(&state.db, pack_input).await?;
|
||||
@@ -220,6 +221,7 @@ pub async fn update_pack(
|
||||
tags: request.tags,
|
||||
runtime_deps: request.runtime_deps,
|
||||
is_standard: request.is_standard,
|
||||
installers: None,
|
||||
};
|
||||
|
||||
let pack = PackRepository::update(&state.db, existing_pack.id, update_input).await?;
|
||||
@@ -527,6 +529,7 @@ async fn register_pack_internal(
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
is_standard: false,
|
||||
installers: serde_json::json!({}),
|
||||
};
|
||||
|
||||
let pack = PackRepository::create(&state.db, pack_input).await?;
|
||||
@@ -624,12 +627,10 @@ pub async fn install_pack(
|
||||
StatusCode,
|
||||
Json<crate::dto::ApiResponse<PackInstallResponse>>,
|
||||
)> {
|
||||
use attune_common::models::CreatePackInstallation;
|
||||
use attune_common::pack_registry::{
|
||||
calculate_directory_checksum, DependencyValidator, PackInstaller, PackStorage,
|
||||
};
|
||||
use attune_common::repositories::List;
|
||||
use attune_common::repositories::PackInstallationRepository;
|
||||
|
||||
tracing::info!("Installing pack from source: {}", request.source);
|
||||
|
||||
@@ -782,34 +783,26 @@ pub async fn install_pack(
|
||||
.ok();
|
||||
|
||||
// Store installation metadata
|
||||
let installation_repo = PackInstallationRepository::new(state.db.clone());
|
||||
let (source_url, source_ref) =
|
||||
get_source_metadata(&source, &request.source, request.ref_spec.as_deref());
|
||||
|
||||
let installation_metadata = CreatePackInstallation {
|
||||
PackRepository::update_installation_metadata(
|
||||
&state.db,
|
||||
pack_id,
|
||||
source_type: source_type.to_string(),
|
||||
source_type.to_string(),
|
||||
source_url,
|
||||
source_ref,
|
||||
checksum: checksum.clone(),
|
||||
checksum_verified: installed.checksum.is_some() && checksum.is_some(),
|
||||
installed_by: user_id,
|
||||
installation_method: "api".to_string(),
|
||||
storage_path: final_path.to_string_lossy().to_string(),
|
||||
meta: Some(serde_json::json!({
|
||||
"original_source": request.source,
|
||||
"force": request.force,
|
||||
"skip_tests": request.skip_tests,
|
||||
})),
|
||||
};
|
||||
|
||||
installation_repo
|
||||
.create(installation_metadata)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::warn!("Failed to store installation metadata: {}", e);
|
||||
ApiError::DatabaseError(format!("Failed to store installation metadata: {}", e))
|
||||
})?;
|
||||
checksum.clone(),
|
||||
installed.checksum.is_some() && checksum.is_some(),
|
||||
user_id,
|
||||
"api".to_string(),
|
||||
final_path.to_string_lossy().to_string(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::warn!("Failed to store installation metadata: {}", e);
|
||||
ApiError::DatabaseError(format!("Failed to store installation metadata: {}", e))
|
||||
})?;
|
||||
|
||||
// Clean up temp directory
|
||||
let _ = installer.cleanup(&installed.path).await;
|
||||
|
||||
@@ -434,6 +434,7 @@ pub async fn create_test_pack(pool: &PgPool, ref_name: &str) -> Result<Pack> {
|
||||
tags: vec!["test".to_string()],
|
||||
runtime_deps: vec![],
|
||||
is_standard: false,
|
||||
installers: json!({}),
|
||||
};
|
||||
|
||||
Ok(PackRepository::create(pool, input).await?)
|
||||
|
||||
@@ -12,7 +12,7 @@ mod helpers;
|
||||
use attune_common::{
|
||||
models::Pack,
|
||||
pack_registry::calculate_directory_checksum,
|
||||
repositories::{pack::PackRepository, pack_installation::PackInstallationRepository, List},
|
||||
repositories::{pack::PackRepository, List},
|
||||
};
|
||||
use helpers::{Result, TestContext};
|
||||
use serde_json::json;
|
||||
@@ -351,19 +351,18 @@ async fn test_install_pack_metadata_tracking() -> Result<()> {
|
||||
let pack_id = body["data"]["pack"]["id"].as_i64().unwrap();
|
||||
|
||||
// Verify installation metadata was created
|
||||
let installation_repo = PackInstallationRepository::new(ctx.pool.clone());
|
||||
let installation = installation_repo
|
||||
.get_by_pack_id(pack_id)
|
||||
let pack = PackRepository::find_by_id(&ctx.pool, pack_id)
|
||||
.await?
|
||||
.expect("Should have installation record");
|
||||
.expect("Should have pack record");
|
||||
|
||||
assert_eq!(installation.pack_id, pack_id);
|
||||
assert_eq!(installation.source_type, "local_directory");
|
||||
assert!(installation.source_url.is_some());
|
||||
assert!(installation.checksum.is_some());
|
||||
assert_eq!(pack.id, pack_id);
|
||||
assert_eq!(pack.source_type.as_deref(), Some("local_directory"));
|
||||
assert!(pack.source_url.is_some());
|
||||
assert!(pack.checksum.is_some());
|
||||
assert!(pack.installed_at.is_some());
|
||||
|
||||
// Verify checksum matches
|
||||
let stored_checksum = installation.checksum.as_ref().unwrap();
|
||||
let stored_checksum = pack.checksum.as_ref().unwrap();
|
||||
assert_eq!(
|
||||
stored_checksum, &original_checksum,
|
||||
"Stored checksum should match calculated checksum"
|
||||
@@ -451,13 +450,14 @@ async fn test_install_pack_storage_path_created() -> Result<()> {
|
||||
let pack_id = body["data"]["pack"]["id"].as_i64().unwrap();
|
||||
|
||||
// Verify installation metadata has storage path
|
||||
let installation_repo = PackInstallationRepository::new(ctx.pool.clone());
|
||||
let installation = installation_repo
|
||||
.get_by_pack_id(pack_id)
|
||||
let pack = PackRepository::find_by_id(&ctx.pool, pack_id)
|
||||
.await?
|
||||
.expect("Should have installation record");
|
||||
.expect("Should have pack record");
|
||||
|
||||
let storage_path = &installation.storage_path;
|
||||
let storage_path = pack
|
||||
.storage_path
|
||||
.as_ref()
|
||||
.expect("Should have storage path");
|
||||
assert!(
|
||||
storage_path.contains("storage-test"),
|
||||
"Storage path should contain pack ref"
|
||||
|
||||
@@ -42,6 +42,7 @@ async fn setup_test_pack_and_action(pool: &PgPool) -> Result<(Pack, Action)> {
|
||||
tags: vec!["test".to_string()],
|
||||
runtime_deps: vec![],
|
||||
is_standard: false,
|
||||
installers: json!({}),
|
||||
};
|
||||
let pack = PackRepository::create(pool, pack_input).await?;
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ async fn create_test_pack(state: &AppState, name: &str) -> i64 {
|
||||
tags: vec![],
|
||||
runtime_deps: vec![],
|
||||
is_standard: false,
|
||||
installers: json!({}),
|
||||
};
|
||||
|
||||
let pack = PackRepository::create(&state.db, input)
|
||||
|
||||
@@ -48,6 +48,7 @@ async fn create_test_pack(state: &AppState, name: &str) -> i64 {
|
||||
tags: vec![],
|
||||
runtime_deps: vec![],
|
||||
is_standard: false,
|
||||
installers: json!({}),
|
||||
};
|
||||
|
||||
let pack = PackRepository::create(&state.db, input)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -360,6 +360,7 @@ impl PackFixture {
|
||||
tags: self.tags,
|
||||
runtime_deps: self.runtime_deps,
|
||||
is_standard: self.is_standard,
|
||||
installers: serde_json::json!({}),
|
||||
};
|
||||
|
||||
PackRepository::create(pool, input).await
|
||||
|
||||
@@ -393,6 +393,7 @@ async fn test_pack_transaction_commit() {
|
||||
tags: vec![],
|
||||
runtime_deps: vec![],
|
||||
is_standard: false,
|
||||
installers: json!({}),
|
||||
};
|
||||
|
||||
let pack = PackRepository::create(&mut *tx, input).await.unwrap();
|
||||
@@ -428,6 +429,7 @@ async fn test_pack_transaction_rollback() {
|
||||
tags: vec![],
|
||||
runtime_deps: vec![],
|
||||
is_standard: false,
|
||||
installers: json!({}),
|
||||
};
|
||||
|
||||
let pack = PackRepository::create(&mut *tx, input).await.unwrap();
|
||||
@@ -456,6 +458,7 @@ async fn test_pack_invalid_ref_format() {
|
||||
tags: vec![],
|
||||
runtime_deps: vec![],
|
||||
is_standard: false,
|
||||
installers: json!({}),
|
||||
};
|
||||
|
||||
let result = PackRepository::create(&pool, input).await;
|
||||
@@ -489,6 +492,7 @@ async fn test_pack_valid_ref_formats() {
|
||||
tags: vec![],
|
||||
runtime_deps: vec![],
|
||||
is_standard: false,
|
||||
installers: json!({}),
|
||||
};
|
||||
|
||||
let result = PackRepository::create(&pool, input).await;
|
||||
|
||||
@@ -80,6 +80,7 @@ impl PermissionSetFixture {
|
||||
meta: json!({}),
|
||||
runtime_deps: vec![],
|
||||
is_standard: false,
|
||||
installers: json!({}),
|
||||
};
|
||||
PackRepository::create(&self.pool, input)
|
||||
.await
|
||||
|
||||
@@ -378,6 +378,7 @@ async fn test_find_by_pack() {
|
||||
tags: vec!["test".to_string()],
|
||||
runtime_deps: vec![],
|
||||
is_standard: false,
|
||||
installers: json!({}),
|
||||
};
|
||||
|
||||
let pack = PackRepository::create(&pool, pack_input)
|
||||
|
||||
@@ -53,6 +53,7 @@ async fn create_test_pack(pool: &PgPool, suffix: &str) -> i64 {
|
||||
tags: vec![],
|
||||
runtime_deps: vec![],
|
||||
is_standard: false,
|
||||
installers: json!({}),
|
||||
};
|
||||
|
||||
PackRepository::create(pool, pack_input)
|
||||
|
||||
@@ -46,6 +46,7 @@ async fn create_test_pack(pool: &PgPool, suffix: &str) -> i64 {
|
||||
tags: vec![],
|
||||
runtime_deps: vec![],
|
||||
is_standard: false,
|
||||
installers: json!({}),
|
||||
};
|
||||
|
||||
let pack = PackRepository::create(pool, pack_input)
|
||||
|
||||
Reference in New Issue
Block a user