diff --git a/src/DynamoCore/Graph/Nodes/DummyNode.cs b/src/DynamoCore/Graph/Nodes/DummyNode.cs index 07ef295bdbc..dd39bd2a542 100644 --- a/src/DynamoCore/Graph/Nodes/DummyNode.cs +++ b/src/DynamoCore/Graph/Nodes/DummyNode.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Xml; -using Autodesk.DesignScript.Runtime; +using Autodesk.DesignScript.Runtime; using Dynamo.Core; using Dynamo.Engine; using Dynamo.Graph.Nodes.NodeLoaders; @@ -11,6 +7,10 @@ using Dynamo.Utilities; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Xml; namespace Dynamo.Graph.Nodes { @@ -45,6 +45,7 @@ public DummyNode() LegacyNodeName = "Dynamo.Graph.Nodes.DummyNode"; LegacyFullName = LegacyNodeName; LegacyAssembly = string.Empty; + FunctionName = string.Empty; NodeNature = Nature.Unresolved; Description = GetDescription(); ShouldDisplayPreviewCore = false; @@ -142,6 +143,101 @@ public DummyNode( UpdatePorts(); } + /// + /// This function creates DummyNode with specified number of ports. + /// + /// Id of the original node + /// Number of input ports + /// Number of output ports + /// Assembly of the node + /// Function name of the node + /// Original JSON description of the node + public DummyNode( + string id, + int inputCount, + int outputCount, + string legacyAssembly, + string functionName, + JObject originalElement) + { + GUID = new Guid(id); + + InputCount = inputCount; + OutputCount = outputCount; + + string legacyName = "Unresolved"; + LegacyNodeName = legacyName; + LegacyFullName = legacyName; + Name = legacyName; + FunctionName = functionName; + + OriginalNodeContent = originalElement; + + LegacyAssembly = legacyAssembly; + NodeNature = DummyNode.Nature.Unresolved; + + Description = GetDescription(); + ShouldDisplayPreviewCore = false; + + if (originalElement != null) + { + var legacyFullName = originalElement["FunctionSignature"]; + if (legacyFullName != null) + LegacyFullName = legacyFullName.ToString(); + } + + UpdatePorts(); + } + + /// + /// This function creates DummyNode with specified number of ports. + /// + /// Id of the original node + /// Number of input ports + /// Number of output ports + /// Assembly of the node + /// Function name of the node + /// Type of the node + /// Original JSON description of the node + public DummyNode( + string id, + int inputCount, + int outputCount, + string legacyAssembly, + string functionName, + string typeName, + JObject originalElement) + { + GUID = new Guid(id); + + InputCount = inputCount; + OutputCount = outputCount; + + string legacyName = "Unresolved"; + LegacyNodeName = legacyName; + LegacyFullName = legacyName; + Name = legacyName; + FunctionName = functionName; + + OriginalNodeContent = originalElement; + + LegacyAssembly = legacyAssembly; + TypeName = typeName; + NodeNature = DummyNode.Nature.Unresolved; + + Description = GetDescription(); + ShouldDisplayPreviewCore = false; + + if (originalElement != null) + { + var legacyFullName = originalElement["FunctionSignature"]; + if (legacyFullName != null) + LegacyFullName = legacyFullName.ToString(); + } + + UpdatePorts(); + } + private void LoadNode(XmlNode nodeElement) { XmlElement originalElement = OriginalXmlNodeContent; @@ -320,29 +416,29 @@ internal string GetDescription() { if (NodeNature == Nature.Deprecated) { - if (string.IsNullOrEmpty(LegacyAssembly)) + if (string.IsNullOrEmpty(FunctionName)) { - const string format = "Node of type '{0}' is now deprecated"; - return string.Format(format, LegacyNodeName); + const string format = "Node of type '{0}',from assembly '{1}', is now deprecated."; + return string.Format(format, TypeName, LegacyAssembly); } else { - const string format = "Node of type '{0}' ({1}) is now deprecated"; - return string.Format(format, LegacyNodeName, LegacyAssembly); + const string format = "Node '{0}' is now deprecated"; + return string.Format(format, FunctionName); } } if (NodeNature == Nature.Unresolved) { - if (string.IsNullOrEmpty(LegacyAssembly)) + if (string.IsNullOrEmpty(FunctionName)) { - const string format = "Node of type '{0}' cannot be resolved"; - return string.Format(format, LegacyNodeName); + const string format = "Node of type '{0}', from assembly '{1}', cannot be resolved."; + return string.Format(format, TypeName, LegacyAssembly); } else { - const string format = "Node of type '{0}' ({1}) cannot be resolved"; - return string.Format(format, LegacyNodeName, LegacyAssembly); + const string format = "Node '{0}' cannot be resolved."; + return string.Format(format, FunctionName); } } @@ -370,6 +466,16 @@ internal string GetDescription() /// public string LegacyAssembly { get; private set; } + /// + /// Type name of the node. This is property is only valid for NodeModel dummy nodes + /// + public string TypeName { get; private set; } + + /// + /// Returns the node's function name. This property is only valid for ZeroTouch dummy nodes + /// + public string FunctionName { get; private set; } + /// /// Returns the original node DSFunction description or UI node type /// diff --git a/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs b/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs index 773e8701eff..c84ba4e0709 100644 --- a/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs +++ b/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs @@ -96,11 +96,28 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist { NodeModel node = null; + String typeName = String.Empty; + String functionName = String.Empty; + String assemblyName = String.Empty; + var obj = JObject.Load(reader); Type type = null; + try { type = Type.GetType(obj["$type"].Value()); + typeName = obj["$type"].Value().Split(',').FirstOrDefault(); + + if (typeName.Equals("Dynamo.Graph.Nodes.ZeroTouch.DSFunction")) + { + // If it is a zero touch node, then get the whole function name including the namespace. + functionName = obj["FunctionSignature"].Value().Split('@').FirstOrDefault().Trim(); + } + // we get the assembly name from the type string for the node model nodes. + else + { + assemblyName = obj["$type"].Value().Split(',').Skip(1).FirstOrDefault().Trim(); + } } catch(Exception e) { @@ -115,9 +132,8 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist { List resultList; - var typeName = obj["$type"].Value().Split(',').FirstOrDefault(); // This assemblyName does not usually contain version information... - var assemblyName = obj["$type"].Value().Split(',').Skip(1).FirstOrDefault().Trim(); + assemblyName = obj["$type"].Value().Split(',').Skip(1).FirstOrDefault().Trim(); if (assemblyName != null) { if(this.loadedAssemblies.TryGetValue(assemblyName, out resultList)) @@ -159,7 +175,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist // If type is still null at this point return a dummy node if (type == null) { - node = CreateDummyNode(obj, assemblyLocation, inPorts, outPorts); + node = CreateDummyNode(obj, typeName, assemblyName, functionName, inPorts, outPorts); } // Attempt to create a valid node using the type else if (type == typeof(Function)) @@ -223,7 +239,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist // Use the functionDescriptor to try and restore the proper node if possible if (functionDescriptor == null) { - node = CreateDummyNode(obj, assemblyLocation, inPorts, outPorts); + node = CreateDummyNode(obj, assemblyName, functionName, inPorts, outPorts); } else { @@ -286,7 +302,21 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist return node; } - private DummyNode CreateDummyNode(JObject obj, string assemblyLocation, PortModel[] inPorts, PortModel[] outPorts) + private DummyNode CreateDummyNode(JObject obj, string legacyAssembly, string functionName, PortModel[] inPorts, PortModel[] outPorts) + { + var inputcount = inPorts.Count(); + var outputcount = outPorts.Count(); + + return new DummyNode( + obj["Id"].ToString(), + inputcount, + outputcount, + legacyAssembly, + functionName, + obj); + } + + private DummyNode CreateDummyNode(JObject obj, string typeName, string legacyAssembly, string functionName, PortModel[] inPorts, PortModel[] outPorts) { var inputcount = inPorts.Count(); var outputcount = outPorts.Count(); @@ -295,7 +325,9 @@ private DummyNode CreateDummyNode(JObject obj, string assemblyLocation, PortMode obj["Id"].ToString(), inputcount, outputcount, - assemblyLocation, + legacyAssembly, + functionName, + typeName, obj); } diff --git a/test/DynamoCoreTests/DummyNodeTests.cs b/test/DynamoCoreTests/DummyNodeTests.cs index 383b61ff2ba..af4a36555d2 100644 --- a/test/DynamoCoreTests/DummyNodeTests.cs +++ b/test/DynamoCoreTests/DummyNodeTests.cs @@ -160,5 +160,26 @@ public void ResolveDummyNodesInsideCustomNodeWorkspace() Assert.AreEqual(0, dummyNodes.Count()); Assert.AreEqual(CurrentDynamoModel.CurrentWorkspace.HasUnsavedChanges, false); } + + [Test] + public void DummyNodesWarningMessageTest() + { + String path = Path.Combine(TestDirectory, @"core\dummy_node\DummyNodesWarningMessageTest.dyn"); + OpenModel(path); + + var dummyNodes = CurrentDynamoModel.CurrentWorkspace.Nodes.OfType(); + Assert.AreEqual(2, dummyNodes.Count()); + + // Asserting the warning message that is displayed for zerotouch and nodemodel dummy nodes. + var zeroTouchDummyNode = dummyNodes.First(); + Assert.AreEqual(zeroTouchDummyNode.NodeNature, DummyNode.Nature.Unresolved); + Assert.AreEqual(zeroTouchDummyNode.FunctionName, "HowickMaker.hMember.ByLineVector"); + Assert.AreEqual(zeroTouchDummyNode.GetDescription(), "Node 'HowickMaker.hMember.ByLineVector' cannot be resolved."); + + var nodeModelDummyNode = dummyNodes.Last(); + Assert.AreEqual(nodeModelDummyNode.NodeNature, DummyNode.Nature.Unresolved); + Assert.AreEqual(nodeModelDummyNode.LegacyAssembly, "SampleLibraryUI"); + Assert.AreEqual(nodeModelDummyNode.GetDescription(), "Node of type 'SampleLibraryUI.Examples.DropDownExample', from assembly 'SampleLibraryUI', cannot be resolved."); + } } } \ No newline at end of file diff --git a/test/core/dummy_node/DummyNodesWarningMessageTest.dyn b/test/core/dummy_node/DummyNodesWarningMessageTest.dyn new file mode 100644 index 00000000000..a225397bfc9 --- /dev/null +++ b/test/core/dummy_node/DummyNodesWarningMessageTest.dyn @@ -0,0 +1,156 @@ +{ + "Uuid": "8ac993b7-6516-45c6-8bac-9ff985fbb0ad", + "IsCustomNode": false, + "Description": null, + "Name": "Dummy node message", + "ElementResolver": { + "ResolutionMap": { + "Point": { + "Key": "Autodesk.DesignScript.Geometry.Point", + "Value": "ProtoGeometry.dll" + } + } + }, + "Inputs": [], + "Outputs": [], + "Nodes": [ + { + "ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore", + "NodeType": "FunctionNode", + "FunctionSignature": "HowickMaker.hMember.ByLineVector@Autodesk.DesignScript.Geometry.Line,Autodesk.DesignScript.Geometry.Vector,string", + "Id": "aeb3e5df26cc4f16af55bf0baef46ae1", + "Inputs": [ + { + "Id": "2cda30bf385741c3ad5d4b290d8452c1", + "Name": "webAxis", + "Description": "Line", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "76a24bab17214e1697ab78692268bc77", + "Name": "webNormal", + "Description": "Vector", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "5cdd38339cef4f09a61f60abd6095076", + "Name": "name", + "Description": "string\nDefault value : \"0\"", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "e45e1d20e89b422698798660461602e6", + "Name": "hMember", + "Description": "hMember", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "Creates an hMember with its web lying along the webAxis, facing webNormal\n\nhMember.ByLineVector (webAxis: Line, webNormal: Vector, name: string = \"0\"): hMember" + }, + { + "ConcreteType": "SampleLibraryUI.Examples.DropDownExample, SampleLibraryUI", + "SelectedIndex": 0, + "SelectedString": "Tywin", + "NodeType": "ExtensionNode", + "Id": "5de408d41309424ea9ab0a9a57011168", + "Inputs": [], + "Outputs": [ + { + "Id": "6c8499495f5d4beeb7e3ca58b861a297", + "Name": "item", + "Description": "The selected item", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "An example drop down node." + } + ], + "Connectors": [], + "Dependencies": [], + "NodeLibraryDependencies": [ + { + "Name": "HowickMaker", + "Version": "0.4.1", + "ReferenceType": "Package", + "Nodes": [ + "aeb3e5df26cc4f16af55bf0baef46ae1" + ] + }, + { + "Name": "Dynamo Samples", + "Version": "2.0.0", + "ReferenceType": "Package", + "Nodes": [ + "5de408d41309424ea9ab0a9a57011168" + ] + } + ], + "Bindings": [], + "View": { + "Dynamo": { + "ScaleFactor": 1.0, + "HasRunWithoutCrash": true, + "IsVisibleInDynamoLibrary": true, + "Version": "2.5.0.6521", + "RunType": "Automatic", + "RunPeriod": "1000" + }, + "Camera": { + "Name": "Background Preview", + "EyeX": -17.0, + "EyeY": 24.0, + "EyeZ": 50.0, + "LookX": 12.0, + "LookY": -13.0, + "LookZ": -58.0, + "UpX": 0.0, + "UpY": 1.0, + "UpZ": 0.0 + }, + "NodeViews": [ + { + "ShowGeometry": true, + "Name": "hMember.ByLineVector", + "Id": "aeb3e5df26cc4f16af55bf0baef46ae1", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 39.256060510822806, + "Y": 268.69482120487669 + }, + { + "ShowGeometry": true, + "Name": "Drop Down Example", + "Id": "5de408d41309424ea9ab0a9a57011168", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 493.04874262470372, + "Y": 283.18790496760249 + } + ], + "Annotations": [], + "X": 82.818344906128914, + "Y": 51.13140818532122, + "Zoom": 0.82346504096947037 + } +} \ No newline at end of file