Skip to content

Commit 78c87c9

Browse files
committed
fix assistant bootstrap with soft-deleted definitions
1 parent 8e6f32f commit 78c87c9

3 files changed

Lines changed: 80 additions & 2 deletions

File tree

crates/aionui-assistant/src/service.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,7 +1656,7 @@ impl AssistantService {
16561656
if let Some(source_ref) = source_ref
16571657
&& let Some(existing) = self
16581658
.definition_repo
1659-
.get_by_source_ref(source, source_ref)
1659+
.get_by_source_ref_including_deleted(source, source_ref)
16601660
.await
16611661
.map_err(|e| AssistantError::Internal(format!("get assistant definition by source_ref: {e}")))?
16621662
{
@@ -1665,7 +1665,7 @@ impl AssistantService {
16651665

16661666
if let Some(existing) = self
16671667
.definition_repo
1668-
.get_by_assistant_id(assistant_id)
1668+
.get_by_assistant_id_including_deleted(assistant_id)
16691669
.await
16701670
.map_err(|e| AssistantError::Internal(format!("get assistant definition by key: {e}")))?
16711671
{
@@ -3019,6 +3019,44 @@ mod tests {
30193019
assert_eq!(builtin_state.last_used_at, Some(1234));
30203020
}
30213021

3022+
#[tokio::test]
3023+
async fn bootstrap_reactivates_soft_deleted_builtin_definition_by_source_ref() {
3024+
let mut builtin = mk_builtin("aionui-assistant", "AionUi Butler");
3025+
builtin.rule_file = Some("rules/aionui-assistant.{locale}.md".into());
3026+
let fx = fixture_with_builtins(vec![builtin]).await;
3027+
3028+
let original = fx
3029+
.definition_repo
3030+
.get_by_assistant_id("aionui-assistant")
3031+
.await
3032+
.unwrap()
3033+
.expect("builtin definition should be materialized");
3034+
fx.definition_repo
3035+
.soft_delete(&original.id, now_ms())
3036+
.await
3037+
.expect("soft-delete builtin definition");
3038+
assert!(
3039+
fx.definition_repo
3040+
.get_by_assistant_id("aionui-assistant")
3041+
.await
3042+
.unwrap()
3043+
.is_none(),
3044+
"active lookup should hide the soft-deleted row"
3045+
);
3046+
3047+
fx.service.bootstrap_assistant_storage().await.unwrap();
3048+
3049+
let restored = fx
3050+
.definition_repo
3051+
.get_by_assistant_id("aionui-assistant")
3052+
.await
3053+
.unwrap()
3054+
.expect("bootstrap should reactivate the soft-deleted builtin");
3055+
assert_eq!(restored.id, original.id);
3056+
assert_eq!(restored.rule_resource_ref.as_deref(), Some("aionui-assistant"));
3057+
assert!(restored.deleted_at.is_none());
3058+
}
3059+
30223060
#[tokio::test]
30233061
async fn bootstrap_soft_deletes_builtin_removed_from_manifest() {
30243062
let mut fx = fixture_with_builtins(vec![mk_builtin("builtin-office", "Office")]).await;

crates/aionui-db/src/repository/assistant.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,25 @@ pub trait IAssistantOverrideRepository: Send + Sync {
6161
pub trait IAssistantDefinitionRepository: Send + Sync {
6262
async fn list(&self) -> Result<Vec<AssistantDefinitionRow>, DbError>;
6363
async fn get_by_assistant_id(&self, assistant_id: &str) -> Result<Option<AssistantDefinitionRow>, DbError>;
64+
async fn get_by_assistant_id_including_deleted(
65+
&self,
66+
assistant_id: &str,
67+
) -> Result<Option<AssistantDefinitionRow>, DbError> {
68+
self.get_by_assistant_id(assistant_id).await
69+
}
6470
async fn get_by_id(&self, id: &str) -> Result<Option<AssistantDefinitionRow>, DbError>;
6571
async fn get_by_source_ref(
6672
&self,
6773
source: &str,
6874
source_ref: &str,
6975
) -> Result<Option<AssistantDefinitionRow>, DbError>;
76+
async fn get_by_source_ref_including_deleted(
77+
&self,
78+
source: &str,
79+
source_ref: &str,
80+
) -> Result<Option<AssistantDefinitionRow>, DbError> {
81+
self.get_by_source_ref(source, source_ref).await
82+
}
7083
async fn upsert(&self, params: &UpsertAssistantDefinitionParams<'_>) -> Result<AssistantDefinitionRow, DbError>;
7184
async fn soft_delete(&self, id: &str, deleted_at: i64) -> Result<bool, DbError>;
7285
}

crates/aionui-db/src/repository/sqlite_assistant.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,18 @@ impl IAssistantDefinitionRepository for SqliteAssistantDefinitionRepository {
362362
Ok(row)
363363
}
364364

365+
async fn get_by_assistant_id_including_deleted(
366+
&self,
367+
assistant_id: &str,
368+
) -> Result<Option<AssistantDefinitionRow>, DbError> {
369+
let row =
370+
sqlx::query_as::<_, AssistantDefinitionRow>("SELECT * FROM assistant_definitions WHERE assistant_id = ?")
371+
.bind(assistant_id)
372+
.fetch_optional(&self.pool)
373+
.await?;
374+
Ok(row)
375+
}
376+
365377
async fn get_by_id(&self, id: &str) -> Result<Option<AssistantDefinitionRow>, DbError> {
366378
let row = sqlx::query_as::<_, AssistantDefinitionRow>(
367379
"SELECT * FROM assistant_definitions WHERE id = ? AND deleted_at IS NULL",
@@ -387,6 +399,21 @@ impl IAssistantDefinitionRepository for SqliteAssistantDefinitionRepository {
387399
Ok(row)
388400
}
389401

402+
async fn get_by_source_ref_including_deleted(
403+
&self,
404+
source: &str,
405+
source_ref: &str,
406+
) -> Result<Option<AssistantDefinitionRow>, DbError> {
407+
let row = sqlx::query_as::<_, AssistantDefinitionRow>(
408+
"SELECT * FROM assistant_definitions WHERE source = ? AND source_ref = ?",
409+
)
410+
.bind(source)
411+
.bind(source_ref)
412+
.fetch_optional(&self.pool)
413+
.await?;
414+
Ok(row)
415+
}
416+
390417
async fn upsert(&self, params: &UpsertAssistantDefinitionParams<'_>) -> Result<AssistantDefinitionRow, DbError> {
391418
let now = now_ms();
392419

0 commit comments

Comments
 (0)