Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dal, sdf): Add ability to create a note on the diagram #3800

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions lib/dal/src/diagram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::attribute::value::AttributeValueError;
use crate::change_status::ChangeStatus;
use crate::component::{ComponentError, IncomingConnection, InferredIncomingConnection};
use crate::history_event::HistoryEventMetadata;
use crate::note::{Note, NoteError, NoteId};
use crate::schema::variant::SchemaVariantError;
use crate::socket::connection_annotation::ConnectionAnnotation;
use crate::socket::input::InputSocketError;
Expand Down Expand Up @@ -55,6 +56,8 @@ pub enum DiagramError {
InputSocket(#[from] InputSocketError),
#[error("node not found")]
NodeNotFound,
#[error("note error: {0}")]
Note(#[from] NoteError),
#[error("output socket error: {0}")]
OutputSocket(#[from] OutputSocketError),
#[error(transparent)]
Expand Down Expand Up @@ -128,6 +131,29 @@ pub struct SummaryDiagramComponent {
pub can_be_upgraded: bool,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all(serialize = "camelCase"))]
pub struct SummaryDiagramNote {
pub id: NoteId,
pub position: GridPoint,
pub note: String,
}

impl SummaryDiagramNote {
pub async fn assemble(note: &Note) -> DiagramResult<Self> {
let position = GridPoint {
x: note.x().parse::<f64>()?.round() as isize,
y: note.y().parse::<f64>()?.round() as isize,
};

Ok(SummaryDiagramNote {
id: note.id(),
position,
note: note.note(),
})
}
}

impl SummaryDiagramComponent {
pub async fn assemble(ctx: &DalContext, component: &Component) -> DiagramResult<Self> {
let mut diagram_sockets: HashMap<SchemaVariantId, serde_json::Value> = HashMap::new();
Expand Down Expand Up @@ -359,6 +385,7 @@ pub struct Diagram {
pub components: Vec<SummaryDiagramComponent>,
pub edges: Vec<SummaryDiagramEdge>,
pub inferred_edges: Vec<SummaryDiagramInferredEdge>,
pub notes: Vec<SummaryDiagramNote>,
}

impl Diagram {
Expand Down Expand Up @@ -392,10 +419,17 @@ impl Diagram {
component_views.push(SummaryDiagramComponent::assemble(ctx, component).await?);
}

let mut diagram_notes: Vec<SummaryDiagramNote> = vec![];
let notes = Note::list(ctx).await?;
for note in notes {
diagram_notes.push(SummaryDiagramNote::assemble(&note).await?)
}

Ok(Self {
edges: diagram_edges,
components: component_views,
inferred_edges: diagram_inferred_edges,
notes: diagram_notes,
})
}
}
16 changes: 16 additions & 0 deletions lib/dal/src/layer_db_types/content_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub enum ContentTypes {
FuncArgument(FuncArgumentContent),
InputSocket(InputSocketContent),
Module(ModuleContent),
Note(NoteContent),
Prop(PropContent),
Schema(SchemaContent),
SchemaVariant(SchemaVariantContent),
Expand Down Expand Up @@ -89,6 +90,7 @@ impl_into_content_types!(FuncArgument);
impl_into_content_types!(InputSocket);
impl_into_content_types!(OutputSocket);
impl_into_content_types!(Module);
impl_into_content_types!(Note);
impl_into_content_types!(Prop);
impl_into_content_types!(Schema);
impl_into_content_types!(SchemaVariant);
Expand Down Expand Up @@ -279,6 +281,20 @@ pub struct ModuleContentV1 {
pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, EnumDiscriminants, Serialize, Deserialize, PartialEq)]
pub enum NoteContent {
V1(NoteContentV1),
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct NoteContentV1 {
pub timestamp: Timestamp,
pub note: String,
pub x: String,
pub y: String,
pub created_by_email: String,
}

#[derive(Debug, Clone, EnumDiscriminants, Serialize, Deserialize, PartialEq)]
pub enum OutputSocketContent {
V1(OutputSocketContentV1),
Expand Down
1 change: 1 addition & 0 deletions lib/dal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub mod key_pair;
pub mod label_list;
pub mod layer_db_types;
pub mod module;
pub mod note;
pub mod pkg;
pub mod prop;
pub mod property_editor;
Expand Down
207 changes: 207 additions & 0 deletions lib/dal/src/note.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
use std::{collections::HashMap, sync::Arc};

use serde::{Deserialize, Serialize};
use si_events::ContentHash;
use thiserror::Error;

use crate::{
layer_db_types::{NoteContent, NoteContentV1},
pk,
workspace_snapshot::{
content_address::{ContentAddress, ContentAddressDiscriminants},
node_weight::{category_node_weight::CategoryNodeKind, NodeWeight, NodeWeightError},
},
ChangeSetError, DalContext, EdgeWeight, EdgeWeightError, EdgeWeightKind,
EdgeWeightKindDiscriminants, Timestamp, TransactionsError, WorkspaceSnapshotError,
};

#[remain::sorted]
#[derive(Error, Debug)]
pub enum NoteError {
#[error("change set error: {0}")]
ChangeSet(#[from] ChangeSetError),
#[error("edge weight error: {0}")]
EdgeWeight(#[from] EdgeWeightError),
#[error("layer db error: {0}")]
LayerDb(#[from] si_layer_cache::LayerDbError),
#[error("node weight error: {0}")]
NodeWeight(#[from] NodeWeightError),
#[error("serde json error: {0}")]
Serde(#[from] serde_json::Error),
#[error("transactions error: {0}")]
Transactions(#[from] TransactionsError),
#[error("could not acquire lock: {0}")]
TryLock(#[from] tokio::sync::TryLockError),
#[error("workspace snapshot error: {0}")]
WorkspaceSnapshot(#[from] WorkspaceSnapshotError),
}

pub type NoteResult<T> = Result<T, NoteError>;

pk!(NoteId);

#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct Note {
id: NoteId,
#[serde(flatten)]
timestamp: Timestamp,
x: String,
y: String,
created_by_email: String,
note: String,
}

impl Note {
pub fn assemble(id: NoteId, inner: NoteContentV1) -> Self {
Self {
id,
timestamp: inner.timestamp,
x: inner.x,
y: inner.y,
created_by_email: inner.created_by_email,
note: inner.note,
}
}

pub fn id(&self) -> NoteId {
self.id
}

pub fn note(&self) -> String {
self.note.clone()
}

pub fn x(&self) -> String {
self.x.clone()
}

pub fn y(&self) -> String {
self.y.clone()
}

pub async fn new(
ctx: &DalContext,
x: String,
y: String,
note: String,
created_by_email: String,
) -> NoteResult<Self> {
let timestamp = Timestamp::now();
let content = NoteContentV1 {
timestamp,
note,
x,
y,
created_by_email,
};

let (hash, _) = ctx
.layer_db()
.cas()
.write(
Arc::new(NoteContent::V1(content.clone()).into()),
None,
ctx.events_tenancy(),
ctx.events_actor(),
)
.await?;

let change_set = ctx.change_set()?;
let id = change_set.generate_ulid()?;
let node_weight = NodeWeight::new_content(change_set, id, ContentAddress::Note(hash))?;

let workspace_snapshot = ctx.workspace_snapshot()?;
workspace_snapshot.add_node(node_weight).await?;

let note_index_id = workspace_snapshot
.get_category_node(None, CategoryNodeKind::Note)
.await?;
workspace_snapshot
.add_edge(
note_index_id,
EdgeWeight::new(change_set, EdgeWeightKind::new_use())?,
id,
)
.await?;

Ok(Note::assemble(id.into(), content))
}

pub async fn get_by_id(ctx: &DalContext, id: NoteId) -> NoteResult<Self> {
let workspace_snapshot = ctx.workspace_snapshot()?;

let ulid: si_events::ulid::Ulid = id.into();
let node_index = workspace_snapshot.get_node_index_by_id(ulid).await?;
let node_weight = workspace_snapshot.get_node_weight(node_index).await?;
let hash = node_weight.content_hash();

let content: NoteContent = ctx
.layer_db()
.cas()
.try_read_as(&hash)
.await?
.ok_or(WorkspaceSnapshotError::MissingContentFromStore(ulid))?;

// If we had a v2, then there would be migration logic here.
let NoteContent::V1(inner) = content;

Ok(Note::assemble(id, inner))
}

pub async fn delete_by_id(ctx: &DalContext, id: NoteId) -> NoteResult<()> {
let workspace_snapshot = ctx.workspace_snapshot()?;
let change_set = ctx.change_set()?;
workspace_snapshot.remove_node_by_id(change_set, id).await?;

Ok(())
}

pub async fn list(ctx: &DalContext) -> NoteResult<Vec<Self>> {
let workspace_snapshot = ctx.workspace_snapshot()?;

let mut notes = vec![];
let note_category_index_id = workspace_snapshot
.get_category_node(None, CategoryNodeKind::Note)
.await?;

let note_node_indicies = workspace_snapshot
.outgoing_targets_for_edge_weight_kind(
note_category_index_id,
EdgeWeightKindDiscriminants::Use,
)
.await?;

let mut node_weights = vec![];
let mut content_hashes = vec![];

for note_index in note_node_indicies {
let node_weight = workspace_snapshot
.get_node_weight(note_index)
.await?
.get_content_node_weight_of_kind(ContentAddressDiscriminants::Note)?;
content_hashes.push(node_weight.content_hash());
node_weights.push(node_weight);
}

let content_map: HashMap<ContentHash, NoteContent> = ctx
.layer_db()
.cas()
.try_read_many_as(content_hashes.as_slice())
.await?;

for node_weight in node_weights {
match content_map.get(&node_weight.content_hash()) {
Some(note_content) => {
let NoteContent::V1(inner) = note_content;

notes.push(Self::assemble(node_weight.id().into(), inner.to_owned()))
}
None => Err(WorkspaceSnapshotError::MissingContentFromStore(
node_weight.id(),
))?,
}
}

Ok(notes)
}
}
6 changes: 6 additions & 0 deletions lib/dal/src/workspace_snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ impl WorkspaceSnapshot {
let secret_node_index = graph.add_category_node(change_set, CategoryNodeKind::Secret)?;
let module_node_index = graph.add_category_node(change_set, CategoryNodeKind::Module)?;
let action_node_index = graph.add_category_node(change_set, CategoryNodeKind::Action)?;
let notes_node_index = graph.add_category_node(change_set, CategoryNodeKind::Note)?;

// Connect them to root.
graph.add_edge(
Expand Down Expand Up @@ -282,6 +283,11 @@ impl WorkspaceSnapshot {
EdgeWeight::new(change_set, EdgeWeightKind::new_use())?,
action_node_index,
)?;
graph.add_edge(
graph.root(),
EdgeWeight::new(change_set, EdgeWeightKind::new_use())?,
notes_node_index,
)?;

// We do not care about any field other than "working_copy" because "write" will populate
// them using the assigned working copy.
Expand Down
2 changes: 2 additions & 0 deletions lib/dal/src/workspace_snapshot/content_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub enum ContentAddress {
InputSocket(ContentHash),
JsonValue(ContentHash),
Module(ContentHash),
Note(ContentHash),
OutputSocket(ContentHash),
Prop(ContentHash),
Root,
Expand Down Expand Up @@ -48,6 +49,7 @@ impl ContentAddress {
| ContentAddress::InputSocket(id)
| ContentAddress::JsonValue(id)
| ContentAddress::Module(id)
| ContentAddress::Note(id)
| ContentAddress::Prop(id)
| ContentAddress::Schema(id)
| ContentAddress::SchemaVariant(id)
Expand Down
2 changes: 2 additions & 0 deletions lib/dal/src/workspace_snapshot/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,7 @@ impl WorkspaceSnapshotGraph {
ContentAddressDiscriminants::InputSocket => "red",
ContentAddressDiscriminants::JsonValue => "fuchsia",
ContentAddressDiscriminants::Module => "yellow",
ContentAddressDiscriminants::Note => "black",
ContentAddressDiscriminants::Prop => "orange",
ContentAddressDiscriminants::Root => "black",
ContentAddressDiscriminants::Schema => "black",
Expand Down Expand Up @@ -857,6 +858,7 @@ impl WorkspaceSnapshotGraph {
CategoryNodeKind::Schema => ("Schemas (Category)".to_string(), "black"),
CategoryNodeKind::Secret => ("Secrets (Category)".to_string(), "black"),
CategoryNodeKind::Module => ("Modules (Category)".to_string(), "black"),
CategoryNodeKind::Note => ("Notes (Category)".to_string(), "black"),
},
NodeWeight::Component(component) => (
"Component".to_string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub enum CategoryNodeKind {
DeprecatedActionBatch,
Func,
Module,
Note,
Schema,
Secret,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ impl ContentNodeWeight {
ContentAddress::InputSocket(_) => ContentAddress::InputSocket(content_hash),
ContentAddress::JsonValue(_) => ContentAddress::JsonValue(content_hash),
ContentAddress::Module(_) => ContentAddress::Module(content_hash),
ContentAddress::Note(_) => ContentAddress::Note(content_hash),
ContentAddress::Prop(_) => {
return Err(NodeWeightError::InvalidContentAddressForWeightKind(
"Prop".to_string(),
Expand Down
Loading
Loading