Skip to content

Commit

Permalink
Merge pull request #4268 from systeminit/victor/eng-2615-component-le…
Browse files Browse the repository at this point in the history
…vel-conflict-data

feat: conflicts_for_component api endpoint
  • Loading branch information
vbustamante authored Aug 1, 2024
2 parents 3893fa9 + d02a2fd commit 577fad3
Show file tree
Hide file tree
Showing 9 changed files with 327 additions and 39 deletions.
85 changes: 81 additions & 4 deletions lib/dal/src/workspace_snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,15 @@ use thiserror::Error;
use tokio::sync::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
use tokio::task::JoinError;

use self::graph::ConflictsAndUpdates;
use self::node_weight::{NodeWeightDiscriminants, OrderingNodeWeight};
use crate::action::{Action, ActionError};
use crate::attribute::prototype::argument::{
AttributePrototypeArgument, AttributePrototypeArgumentError, AttributePrototypeArgumentId,
};
use crate::change_set::{ChangeSetError, ChangeSetId};
use crate::slow_rt::{self, SlowRuntimeError};
use crate::workspace_snapshot::content_address::ContentAddressDiscriminants;
use crate::workspace_snapshot::edge_weight::{
EdgeWeight, EdgeWeightError, EdgeWeightKind, EdgeWeightKindDiscriminants,
};
Expand All @@ -63,15 +66,15 @@ use crate::workspace_snapshot::node_weight::category_node_weight::CategoryNodeKi
use crate::workspace_snapshot::node_weight::NodeWeight;
use crate::workspace_snapshot::update::Update;
use crate::workspace_snapshot::vector_clock::VectorClockId;
use crate::{pk, ChangeSet, Component, ComponentError, ComponentId, Workspace, WorkspaceError};
use crate::{
pk, AttributeValueId, ChangeSet, Component, ComponentError, ComponentId, Workspace,
WorkspaceError,
};
use crate::{
workspace_snapshot::{graph::WorkspaceSnapshotGraphError, node_weight::NodeWeightError},
DalContext, TransactionsError, WorkspaceSnapshotGraphV1,
};

use self::graph::ConflictsAndUpdates;
use self::node_weight::{NodeWeightDiscriminants, OrderingNodeWeight};

pk!(NodeId);
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct NodeInformation {
Expand Down Expand Up @@ -2121,4 +2124,78 @@ impl WorkspaceSnapshot {

Ok(())
}

/// If this node is associated to a single av, return it
pub async fn associated_attribute_value_id(
&self,
node_weight: NodeWeight,
) -> WorkspaceSnapshotResult<Option<AttributeValueId>> {
let mut this_node_weight = node_weight;
while let Some(edge_kind) = match &this_node_weight {
NodeWeight::AttributeValue(av) => return Ok(Some(av.id().into())),
NodeWeight::AttributePrototypeArgument(_) => {
Some(EdgeWeightKindDiscriminants::PrototypeArgument)
}
NodeWeight::Ordering(_) => Some(EdgeWeightKindDiscriminants::Ordering),

NodeWeight::Content(content) => match content.content_address_discriminants() {
ContentAddressDiscriminants::AttributePrototype => {
Some(EdgeWeightKindDiscriminants::Prototype)
}
ContentAddressDiscriminants::StaticArgumentValue => {
Some(EdgeWeightKindDiscriminants::PrototypeArgumentValue)
}
ContentAddressDiscriminants::ValidationOutput => {
Some(EdgeWeightKindDiscriminants::ValidationOutput)
}

ContentAddressDiscriminants::ActionPrototype
| ContentAddressDiscriminants::Component
| ContentAddressDiscriminants::DeprecatedAction
| ContentAddressDiscriminants::DeprecatedActionBatch
| ContentAddressDiscriminants::DeprecatedActionRunner
| ContentAddressDiscriminants::Func
| ContentAddressDiscriminants::FuncArg
| ContentAddressDiscriminants::InputSocket
| ContentAddressDiscriminants::JsonValue
| ContentAddressDiscriminants::Module
| ContentAddressDiscriminants::OutputSocket
| ContentAddressDiscriminants::Prop
| ContentAddressDiscriminants::Root
| ContentAddressDiscriminants::Schema
| ContentAddressDiscriminants::SchemaVariant
| ContentAddressDiscriminants::Secret
| ContentAddressDiscriminants::ValidationPrototype => None,
},

NodeWeight::Action(_)
| NodeWeight::ActionPrototype(_)
| NodeWeight::Category(_)
| NodeWeight::Component(_)
| NodeWeight::DependentValueRoot(_)
| NodeWeight::Func(_)
| NodeWeight::FuncArgument(_)
| NodeWeight::Prop(_)
| NodeWeight::Secret(_) => None,
} {
let next_node_idxs = self
.incoming_sources_for_edge_weight_kind(this_node_weight.id(), edge_kind)
.await?;

this_node_weight = match next_node_idxs.first() {
Some(&next_node_idx) if next_node_idxs.len() == 1 => {
self.get_node_weight(next_node_idx).await?
}
_ => {
return Err(WorkspaceSnapshotError::UnexpectedNumberOfIncomingEdges(
edge_kind,
NodeWeightDiscriminants::from(&this_node_weight),
this_node_weight.id(),
))
}
};
}

Ok(None)
}
}
5 changes: 4 additions & 1 deletion lib/dal/src/workspace_snapshot/conflict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ pub enum Conflict {
destination: NodeInformation,
edge_kind: EdgeWeightKindDiscriminants,
},
ModifyRemovedItem(NodeInformation),
ModifyRemovedItem {
container: NodeInformation,
modified_item: NodeInformation,
},
NodeContent {
onto: NodeInformation,
to_rebase: NodeInformation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,19 @@ impl<'a, 'b> DetectConflictsAndUpdates<'a, 'b> {
id: to_rebase_item_weight.id().into(),
node_weight_kind: to_rebase_item_weight.into(),
};
conflicts.push(Conflict::ModifyRemovedItem(node_information))
let container_node_weight = self
.to_rebase_graph
.get_node_weight(to_rebase_container_index)?;
let container_node_information = NodeInformation {
index: to_rebase_container_index,
id: container_node_weight.id().into(),
node_weight_kind: container_node_weight.into(),
};

conflicts.push(Conflict::ModifyRemovedItem {
container: container_node_information,
modified_item: node_information,
});
} else {
let source_node_weight = self
.to_rebase_graph
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ mod test {
}
}

fn get_root_node_info(graph: &WorkspaceSnapshotGraphV1) -> NodeInformation {
let root_id = graph
.get_node_weight(graph.root_index)
.expect("Unable to get root node")
.id();

NodeInformation {
index: graph.root_index,
node_weight_kind: NodeWeightDiscriminants::Content,
id: root_id.into(),
}
}

#[test]
fn detect_conflicts_and_updates_simple_no_conflicts_no_updates_in_base() {
let actor_id = Ulid::new();
Expand Down Expand Up @@ -995,14 +1008,19 @@ mod test {
.detect_conflicts_and_updates(new_vector_clock_id, &base_graph, initial_vector_clock_id)
.expect("Unable to detect conflicts and updates");

let container = get_root_node_info(&new_graph);

assert_eq!(
vec![Conflict::ModifyRemovedItem(NodeInformation {
id: component_id.into(),
index: new_graph
.get_node_index_by_id(component_id)
.expect("Unable to get NodeIndex"),
node_weight_kind: NodeWeightDiscriminants::Content,
})],
vec![Conflict::ModifyRemovedItem {
container,
modified_item: NodeInformation {
id: component_id.into(),
index: new_graph
.get_node_index_by_id(component_id)
.expect("Unable to get NodeIndex"),
node_weight_kind: NodeWeightDiscriminants::Content,
}
}],
conflicts_and_updates.conflicts
);
assert!(conflicts_and_updates.updates.is_empty());
Expand Down Expand Up @@ -1135,17 +1153,22 @@ mod test {
.detect_conflicts_and_updates(vector_clock_id, &base_graph, vector_clock_id)
.expect("Unable to detect conflicts and updates");

let container = get_root_node_info(&new_graph);

// Even though we have identical vector clocks, this still produces a
// conflict, since this item has been modified in to_rebase after onto
// removed it.
assert_eq!(
vec![Conflict::ModifyRemovedItem(NodeInformation {
id: component_id.into(),
index: new_graph
.get_node_index_by_id(component_id)
.expect("Unable to get NodeIndex"),
node_weight_kind: NodeWeightDiscriminants::Content,
})],
vec![Conflict::ModifyRemovedItem {
container,
modified_item: NodeInformation {
id: component_id.into(),
index: new_graph
.get_node_index_by_id(component_id)
.expect("Unable to get NodeIndex"),
node_weight_kind: NodeWeightDiscriminants::Content,
}
}],
conflicts_and_updates.conflicts
);
assert!(conflicts_and_updates.updates.is_empty());
Expand Down Expand Up @@ -1672,15 +1695,19 @@ mod test {

// base_graph.dot();
// new_graph.dot();
let container = get_root_node_info(&new_graph);

let expected_conflicts = vec![
Conflict::ModifyRemovedItem(NodeInformation {
index: new_graph
.get_node_index_by_id(nginx_butane_component_id)
.expect("Unable to get component NodeIndex"),
id: nginx_butane_component_id.into(),
node_weight_kind: NodeWeightDiscriminants::Content,
}),
Conflict::ModifyRemovedItem {
container,
modified_item: NodeInformation {
index: new_graph
.get_node_index_by_id(nginx_butane_component_id)
.expect("Unable to get component NodeIndex"),
id: nginx_butane_component_id.into(),
node_weight_kind: NodeWeightDiscriminants::Content,
},
},
Conflict::NodeContent {
onto: NodeInformation {
index: base_graph
Expand Down Expand Up @@ -4063,15 +4090,8 @@ mod test {
assert!(updates.is_empty());
assert_eq!(1, conflicts.len());

let container = NodeInformation {
index: to_rebase_graph.root_index,
id: to_rebase_graph
.get_node_weight(to_rebase_graph.root())
.expect("Unable to get root node")
.id()
.into(),
node_weight_kind: NodeWeightDiscriminants::Content,
};
let container = get_root_node_info(&to_rebase_graph);

let removed_index = onto_graph
.get_node_index_by_id(prototype_node_id)
.expect("get_node_index_by_id");
Expand All @@ -4097,7 +4117,14 @@ mod test {
.detect_conflicts_and_updates(vector_clock_id, &to_rebase_graph, vector_clock_id)
.expect("detect_conflicts_and_updates");
assert!(updates.is_empty());
assert_eq!(Conflict::ModifyRemovedItem(removed_item), conflicts[0]);
let container = get_root_node_info(&onto_graph);
assert_eq!(
Conflict::ModifyRemovedItem {
container,
modified_item: removed_item
},
conflicts[0]
);
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use si_events::merkle_tree_hash::MerkleTreeHash;
use si_events::VectorClockId;
use si_events::{ulid::Ulid, ContentHash};

use super::deprecated::DeprecatedContentNodeWeight;
use crate::workspace_snapshot::content_address::ContentAddressDiscriminants;
use crate::workspace_snapshot::{
content_address::ContentAddress,
graph::LineageId,
Expand All @@ -11,8 +13,6 @@ use crate::workspace_snapshot::{
};
use crate::EdgeWeightKindDiscriminants;

use super::deprecated::DeprecatedContentNodeWeight;

#[derive(Clone, Serialize, Deserialize)]
pub struct ContentNodeWeight {
/// The stable local ID of the object in question. Mainly used by external things like
Expand Down Expand Up @@ -63,6 +63,10 @@ impl ContentNodeWeight {
self.content_address
}

pub fn content_address_discriminants(&self) -> ContentAddressDiscriminants {
self.content_address.into()
}

pub fn content_hash(&self) -> ContentHash {
self.content_address.content_hash()
}
Expand Down
6 changes: 6 additions & 0 deletions lib/sdf-server/src/server/service/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use dal::validation::ValidationError;
use dal::{
action::prototype::ActionPrototypeError, action::ActionError,
ComponentError as DalComponentError, FuncError, StandardModelError, WorkspaceError,
WorkspaceSnapshotError,
};
use dal::{
attribute::value::debug::AttributeDebugViewError, component::ComponentId, PropId,
Expand All @@ -20,6 +21,7 @@ use dal::{ChangeSetError, TransactionsError};
use thiserror::Error;

use crate::server::state::AppState;
use crate::service::component::conflicts_for_component::conflicts_for_component;

pub mod delete_property_editor_value;
pub mod get_actions;
Expand All @@ -34,6 +36,7 @@ pub mod update_property_editor_value;
// pub mod list_resources;
pub mod refresh;
// pub mod resource_domain_diff;
pub mod conflicts_for_component;
pub mod debug;
pub mod get_code;
pub mod restore_default_function;
Expand Down Expand Up @@ -95,6 +98,8 @@ pub enum ComponentError {
ValidationResolver(#[from] ValidationError),
#[error("workspace error: {0}")]
Workspace(#[from] WorkspaceError),
#[error("workspace snapshot error: {0}")]
WorkspaceSnapshot(#[from] WorkspaceSnapshotError),
#[error("ws event error: {0}")]
WsEvent(#[from] WsEventError),
}
Expand Down Expand Up @@ -164,4 +169,5 @@ pub fn routes() -> Router<AppState> {
.route("/debug", get(debug::debug_component))
.route("/json", get(json::json))
.route("/upgrade_component", post(upgrade::upgrade))
.route("/conflicts", get(conflicts_for_component))
}
Loading

0 comments on commit 577fad3

Please sign in to comment.