headless_lms_models/library/agent_changesets/
conflicts.rs1use std::collections::HashMap;
4
5use utoipa::ToSchema;
6
7use crate::{
8 agent_changeset_entities::AgentChangesetEntity,
9 entity_history,
10 library::entity_snapshots::{self, EntityKey, EntityRef, EntitySnapshot},
11 prelude::*,
12};
13
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
16pub struct PublishConflict {
17 #[serde(flatten)]
18 pub entity: EntityRef,
19 pub last_changed_at: Option<DateTime<Utc>>,
21 pub last_changed_by_user_id: Option<Uuid>,
22 pub last_changed_via_client: Option<String>,
24}
25
26pub(super) async fn conflicts_of(
29 conn: &mut PgConnection,
30 entities: &[AgentChangesetEntity],
31 bases: &HashMap<EntityKey, EntitySnapshot>,
32) -> ModelResult<Vec<PublishConflict>> {
33 let keys: Vec<_> = entities.iter().map(AgentChangesetEntity::key).collect();
34 let mut live = entity_snapshots::load_live_many(conn, &keys).await?;
35 let mut conflicts = Vec::new();
36 for key in keys {
37 let live = live.remove(&key).flatten();
38 conflicts.extend(conflict_with_live(conn, key, bases.get(&key), live.as_ref()).await?);
39 }
40 Ok(conflicts)
41}
42
43async fn conflict_with_live(
46 conn: &mut PgConnection,
47 key: EntityKey,
48 base: Option<&EntitySnapshot>,
49 live: Option<&EntitySnapshot>,
50) -> ModelResult<Option<PublishConflict>> {
51 if base == live {
52 return Ok(None);
53 }
54 let version = entity_history::latest_version(conn, key.entity_id, None).await?;
55 Ok(Some(PublishConflict {
56 entity: EntityRef::of(key, live, base),
57 last_changed_at: version.as_ref().map(|v| v.created_at),
58 last_changed_by_user_id: version.as_ref().map(|v| v.author_user_id),
59 last_changed_via_client: version.and_then(|v| v.agent_client_name),
60 }))
61}