Skip to main content

headless_lms_models/library/agent_changesets/
conflicts.rs

1//! Staged entities whose live state changed after they were staged.
2
3use 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/// A staged entity whose live state changed after it was staged.
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
16pub struct PublishConflict {
17    #[serde(flatten)]
18    pub entity: EntityRef,
19    /// When the live entity was last changed, if its history knows.
20    pub last_changed_at: Option<DateTime<Utc>>,
21    pub last_changed_by_user_id: Option<Uuid>,
22    /// The agent client of the last change; `None` for the web UI or when unknown.
23    pub last_changed_via_client: Option<String>,
24}
25
26/// The staged entities whose live state no longer matches `bases`, the states they were staged
27/// on.
28pub(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
43/// The conflict of a staged entity whose live state, `live`, no longer matches `base`, the state
44/// it was staged on; `None` if it still matches.
45async 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}