diff --git a/src/DynamoCore/Core/CustomNodeManager.cs b/src/DynamoCore/Core/CustomNodeManager.cs index dca2ad07a1b..6b12e64606b 100644 --- a/src/DynamoCore/Core/CustomNodeManager.cs +++ b/src/DynamoCore/Core/CustomNodeManager.cs @@ -6,6 +6,7 @@ using System.Xml; using Dynamo.Engine; using Dynamo.Engine.NodeToCode; +using Dynamo.Exceptions; using Dynamo.Graph; using Dynamo.Graph.Annotations; using Dynamo.Graph.Connectors; @@ -119,7 +120,7 @@ protected virtual void OnInfoUpdated(CustomNodeInfo obj) var handler = InfoUpdated; if (handler != null) handler(obj); } - + /// /// An event that is fired when a custom node is removed from Dynamo. /// @@ -341,7 +342,7 @@ public bool AddUninitializedCustomNode(string file, bool isTestMode, out CustomN { if (TryGetInfoFromPath(file, isTestMode, out info)) { - SetNodeInfo(info); + SetNodeInfo(info, isTestMode); return true; } return false; @@ -397,7 +398,7 @@ public IEnumerable AddUninitializedCustomNodesInPath(string path { info.IsPackageMember = isPackageMember; - SetNodeInfo(info); + SetNodeInfo(info, isTestMode); result.Add(info); } return result; @@ -442,7 +443,7 @@ private IEnumerable ScanNodeHeadersInDirectory(string dir, bool /// Stores the path and function definition without initializing a node. Overwrites /// the existing NodeInfo if necessary /// - private void SetNodeInfo(CustomNodeInfo newInfo) + private void SetNodeInfo(CustomNodeInfo newInfo, bool isTestMode) { var guids = NodeInfos.Where(x => { @@ -455,6 +456,23 @@ private void SetNodeInfo(CustomNodeInfo newInfo) NodeInfos.Remove(guid); } + CustomNodeInfo info; + if (!isTestMode && NodeInfos.TryGetValue(newInfo.FunctionId, out info)) + { + var newInfoPath = Path.GetDirectoryName(newInfo.Path); + var infoPath = Path.GetDirectoryName(info.Path); + var message = string.Format(Resources.MessageCustomNodePackageFailedToLoad, + infoPath, newInfoPath); + + var ex = new CustomNodePackageLoadException(newInfoPath, infoPath, message); + Log(ex.Message, WarningLevel.Moderate); + + // Log to notification view extension + Log(ex); + + throw ex; + } + NodeInfos[newInfo.FunctionId] = newInfo; OnInfoUpdated(newInfo); } @@ -645,7 +663,7 @@ public bool OpenCustomNodeWorkspace( if (InitializeCustomNode( workspaceInfo, xmlDoc, - out customNodeWorkspace)) + out customNodeWorkspace, isTestMode)) { workspace = customNodeWorkspace; return true; @@ -657,7 +675,7 @@ public bool OpenCustomNodeWorkspace( private bool InitializeCustomNode( WorkspaceInfo workspaceInfo, XmlDocument xmlDoc, - out CustomNodeWorkspaceModel workspace) + out CustomNodeWorkspaceModel workspace, bool isTestMode) { // Add custom node definition firstly so that a recursive // custom node won't recursively load itself. @@ -691,21 +709,21 @@ private bool InitializeCustomNode( } } - RegisterCustomNodeWorkspace(newWorkspace); + RegisterCustomNodeWorkspace(newWorkspace, isTestMode); workspace = newWorkspace; return true; } - private void RegisterCustomNodeWorkspace(CustomNodeWorkspaceModel newWorkspace) + private void RegisterCustomNodeWorkspace(CustomNodeWorkspaceModel newWorkspace, bool isTestMode = false) { RegisterCustomNodeWorkspace( newWorkspace, newWorkspace.CustomNodeInfo, - newWorkspace.CustomNodeDefinition); + newWorkspace.CustomNodeDefinition, isTestMode); } private void RegisterCustomNodeWorkspace( - CustomNodeWorkspaceModel newWorkspace, CustomNodeInfo info, CustomNodeDefinition definition) + CustomNodeWorkspaceModel newWorkspace, CustomNodeInfo info, CustomNodeDefinition definition, bool isTestMode) { loadedWorkspaceModels[newWorkspace.CustomNodeId] = newWorkspace; SetFunctionDefinition(definition); @@ -717,12 +735,12 @@ private void RegisterCustomNodeWorkspace( OnDefinitionUpdated(newDef); }; - SetNodeInfo(info); + SetNodeInfo(info, isTestMode); newWorkspace.InfoChanged += () => { var newInfo = newWorkspace.CustomNodeInfo; - SetNodeInfo(newInfo); + SetNodeInfo(newInfo, isTestMode); OnInfoUpdated(newInfo); }; @@ -764,7 +782,7 @@ private bool InitializeCustomNode( info.ID = functionId.ToString(); if (migrationManager.ProcessWorkspace(info, xmlDoc, isTestMode, nodeFactory)) { - return InitializeCustomNode(info, xmlDoc, out workspace); + return InitializeCustomNode(info, xmlDoc, out workspace, isTestMode); } } } @@ -773,7 +791,7 @@ private bool InitializeCustomNode( // TODO: Skip Json migration for now WorkspaceInfo.FromJsonDocument(strInput, path, isTestMode, false, AsLogger(), out info); info.ID = functionId.ToString(); - return InitializeCustomNode(info, null, out workspace); + return InitializeCustomNode(info, null, out workspace, isTestMode); } else throw ex; Log(string.Format(Properties.Resources.CustomNodeCouldNotBeInitialized, customNodeInfo.Name)); @@ -803,7 +821,7 @@ private bool InitializeCustomNode( /// Optional identifier to be used for the custom node. By default, will make a new unique one. /// /// Newly created Custom Node Workspace. - internal WorkspaceModel CreateCustomNode(string name, string category, string description, Guid? functionId = null) + internal WorkspaceModel CreateCustomNode(string name, string category, string description, Guid? functionId = null, bool isTestMode = false) { var newId = functionId ?? Guid.NewGuid(); @@ -820,7 +838,7 @@ internal WorkspaceModel CreateCustomNode(string name, string category, string de }; var workspace = new CustomNodeWorkspaceModel(info, nodeFactory); - RegisterCustomNodeWorkspace(workspace); + RegisterCustomNodeWorkspace(workspace, isTestMode); return workspace; } @@ -1246,7 +1264,7 @@ from output in outputs newWorkspace.HasUnsavedChanges = true; - RegisterCustomNodeWorkspace(newWorkspace); + RegisterCustomNodeWorkspace(newWorkspace, isTestMode); Debug.WriteLine("Collapsed workspace has {0} nodes and {1} connectors", newWorkspace.Nodes.Count(), newWorkspace.Connectors.Count()); diff --git a/src/DynamoCore/DynamoCore.csproj b/src/DynamoCore/DynamoCore.csproj index ffd42b76dc7..aefff4b48db 100644 --- a/src/DynamoCore/DynamoCore.csproj +++ b/src/DynamoCore/DynamoCore.csproj @@ -121,6 +121,7 @@ limitations under the License. + diff --git a/src/DynamoCore/Exceptions/CustomNodePackageLoadException.cs b/src/DynamoCore/Exceptions/CustomNodePackageLoadException.cs new file mode 100644 index 00000000000..f72cba8c736 --- /dev/null +++ b/src/DynamoCore/Exceptions/CustomNodePackageLoadException.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dynamo.Exceptions +{ + public class CustomNodePackageLoadException : LibraryLoadFailedException + { + /// + /// File path of existing custom node package. + /// + public string InstalledPath { get; } + + /// + /// Constructor. + /// + /// File path of failing custom node package. + /// File path of existing package. + /// Failure reason. + public CustomNodePackageLoadException(string path, string installedPath, string reason) + : base(path, reason) + { + InstalledPath = installedPath; + } + } +} diff --git a/src/DynamoCore/Properties/Resources.Designer.cs b/src/DynamoCore/Properties/Resources.Designer.cs index 29a19fc343a..2d5588d8fcf 100644 --- a/src/DynamoCore/Properties/Resources.Designer.cs +++ b/src/DynamoCore/Properties/Resources.Designer.cs @@ -871,6 +871,18 @@ public static string MalformedHeaderPackage { } } + /// + /// Looks up a localized string similar to {1} cannot be loaded. + ///Installing it will conflict with one or more node definitions that already exist in {0}, which is currently loaded. + ///To install {1}, Dynamo needs to first uninstall {0}. + ///Restart Dynamo to complete the uninstall, then try and download {1} again.. + /// + public static string MessageCustomNodePackageFailedToLoad { + get { + return ResourceManager.GetString("MessageCustomNodePackageFailedToLoad", resourceCulture); + } + } + /// /// Looks up a localized string similar to SHOW MORE ({0}). /// diff --git a/src/DynamoCore/Properties/Resources.en-US.resx b/src/DynamoCore/Properties/Resources.en-US.resx index 951352426c7..c9aa5dcab21 100644 --- a/src/DynamoCore/Properties/Resources.en-US.resx +++ b/src/DynamoCore/Properties/Resources.en-US.resx @@ -700,4 +700,10 @@ The input name should be a valid variable name, without spaces. An input type an Python template loaded from DynamoSettings.xml path + + {1} cannot be loaded. +Installing it will conflict with one or more node definitions that already exist in {0}, which is currently loaded. +To install {1}, Dynamo needs to first uninstall {0}. +Restart Dynamo to complete the uninstall, then try and download {1} again. + \ No newline at end of file diff --git a/src/DynamoCore/Properties/Resources.resx b/src/DynamoCore/Properties/Resources.resx index d5f9f63c10f..7aaac80ac40 100644 --- a/src/DynamoCore/Properties/Resources.resx +++ b/src/DynamoCore/Properties/Resources.resx @@ -700,4 +700,10 @@ The input name should be a valid variable name, without spaces. An input type an Python template set by host integrator + + {1} cannot be loaded. +Installing it will conflict with one or more node definitions that already exist in {0}, which is currently loaded. +To install {1}, Dynamo needs to first uninstall {0}. +Restart Dynamo to complete the uninstall, then try and download {1} again. + \ No newline at end of file diff --git a/src/DynamoCoreWpf/Properties/Resources.Designer.cs b/src/DynamoCoreWpf/Properties/Resources.Designer.cs index 764cc98a92d..b1f69426d18 100644 --- a/src/DynamoCoreWpf/Properties/Resources.Designer.cs +++ b/src/DynamoCoreWpf/Properties/Resources.Designer.cs @@ -3304,6 +3304,20 @@ public static string MessageToUndeprecatePackage { } } + /// + /// Looks up a localized string similar to {1} cannot be loaded. + ///Installing it will conflict with one or more node definitions that already exist in {0}, which is currently loaded. + ///To install {1}, Dynamo needs to first uninstall {0}. + ///Restart Dynamo to complete the uninstall, then try and download {1} again. + /// + ///Uninstall the following packages: {0}?. + /// + public static string MessageUninstallCustomNodeToContinue { + get { + return ResourceManager.GetString("MessageUninstallCustomNodeToContinue", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} needs to uninstall {1} to continue, but cannot as one of its types appears to be in use. Try restarting {0}.. /// diff --git a/src/DynamoCoreWpf/Properties/Resources.en-US.resx b/src/DynamoCoreWpf/Properties/Resources.en-US.resx index 34ab8e65153..64ef9702045 100644 --- a/src/DynamoCoreWpf/Properties/Resources.en-US.resx +++ b/src/DynamoCoreWpf/Properties/Resources.en-US.resx @@ -2199,6 +2199,14 @@ Do you want to install the latest Dynamo update? No new tab is added, as the extension is already present in the extensions side bar. + + {1} cannot be loaded. +Installing it will conflict with one or more node definitions that already exist in {0}, which is currently loaded. +To install {1}, Dynamo needs to first uninstall {0}. +Restart Dynamo to complete the uninstall, then try and download {1} again. + +Uninstall the following packages: {0}? + Provide Feedback diff --git a/src/DynamoCoreWpf/Properties/Resources.resx b/src/DynamoCoreWpf/Properties/Resources.resx index 815fa989c96..9a0969f4a54 100644 --- a/src/DynamoCoreWpf/Properties/Resources.resx +++ b/src/DynamoCoreWpf/Properties/Resources.resx @@ -2202,6 +2202,14 @@ Want to publish a different package? No new tab is added, as the extension is already present in the extensions side bar. + + {1} cannot be loaded. +Installing it will conflict with one or more node definitions that already exist in {0}, which is currently loaded. +To install {1}, Dynamo needs to first uninstall {0}. +Restart Dynamo to complete the uninstall, then try and download {1} again. + +Uninstall the following packages: {0}? + Provide Feedback diff --git a/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerClientViewModel.cs b/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerClientViewModel.cs index 0bc286a6ee0..ca5a7a218d2 100644 --- a/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerClientViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerClientViewModel.cs @@ -10,6 +10,7 @@ using System.Windows; using System.Windows.Input; using Dynamo.Core; +using Dynamo.Exceptions; using Dynamo.Graph.Nodes.CustomNodes; using Dynamo.Graph.Workspaces; using Dynamo.Models; @@ -183,6 +184,13 @@ public ObservableCollection Downloads set { _downloads = value; } } + private PackageManagerExtension pmExtension; + + internal PackageManagerExtension PackageManagerExtension + { + get { return pmExtension ?? (pmExtension = DynamoViewModel.Model.GetPackageManagerExtension()); } + } + public List CachedPackageList { get; private set; } public readonly DynamoViewModel DynamoViewModel; @@ -393,7 +401,6 @@ private void ShowNodePublishInfo(ICollection {p}); packageDownloadHandle.DownloadState = PackageDownloadHandle.State.Installed; diff --git a/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerSearchViewModel.cs b/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerSearchViewModel.cs index 99ac1b20ec4..fee6bbae50c 100644 --- a/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerSearchViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerSearchViewModel.cs @@ -263,8 +263,10 @@ public PackageManagerSearchViewModel(PackageManagerClientViewModel client) : thi { this.PackageManagerClientViewModel = client; PackageManagerClientViewModel.Downloads.CollectionChanged += DownloadsOnCollectionChanged; + PackageManagerClientViewModel.PackageManagerExtension.PackageLoader.ConflictingCustomNodePackageLoaded += + ConflictingCustomNodePackageLoaded; } - + /// /// Sort the search results /// @@ -639,6 +641,23 @@ public static string FormatPackageVersionList(IEnumerable x.Item1.name + " " + x.Item2.version)); } + private void ConflictingCustomNodePackageLoaded(Package installed, Package conflicting) + { + var message = string.Format(Resources.MessageUninstallCustomNodeToContinue, + installed.Name + " " + installed.VersionName, conflicting.Name + " " + conflicting.VersionName); + + var dialogResult = MessageBox.Show(message, + Resources.CannotDownloadPackageMessageBoxTitle, + MessageBoxButton.YesNo, MessageBoxImage.Error); + + if (dialogResult == MessageBoxResult.Yes) + { + // mark for uninstallation + var settings = PackageManagerClientViewModel.DynamoViewModel.Model.PreferenceSettings; + installed.MarkForUninstall(settings); + } + } + private void DownloadsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) { if (args.NewItems != null) @@ -724,8 +743,17 @@ public void SelectPrevious() SelectedIndex = SelectedIndex - 1; } + internal void UnregisterHandlers() + { + RequestShowFileDialog -= OnRequestShowFileDialog; + SearchResults.CollectionChanged -= SearchResultsOnCollectionChanged; + PackageManagerClientViewModel.Downloads.CollectionChanged -= DownloadsOnCollectionChanged; + PackageManagerClientViewModel.PackageManagerExtension.PackageLoader.ConflictingCustomNodePackageLoaded -= + ConflictingCustomNodePackageLoaded; + } + /// - /// Performs a search using the internal SearcText as the query and + /// Performs a search using the internal SearchText as the query and /// updates the observable SearchResults property. /// internal void SearchAndUpdateResults() diff --git a/src/DynamoCoreWpf/Views/PackageManager/PackageManagerSearchView.xaml.cs b/src/DynamoCoreWpf/Views/PackageManager/PackageManagerSearchView.xaml.cs index ee7fab803c1..ae261951f1d 100644 --- a/src/DynamoCoreWpf/Views/PackageManager/PackageManagerSearchView.xaml.cs +++ b/src/DynamoCoreWpf/Views/PackageManager/PackageManagerSearchView.xaml.cs @@ -25,8 +25,10 @@ public PackageManagerSearchView(PackageManagerSearchViewModel pm) protected override void OnClosing(System.ComponentModel.CancelEventArgs e) { - (this.DataContext as PackageManagerSearchViewModel).RequestShowFileDialog -= OnRequestShowFileDialog; - this.Owner.Focus(); + var viewModel = DataContext as PackageManagerSearchViewModel; + viewModel.UnregisterHandlers(); + + Owner.Focus(); base.OnClosing(e); } diff --git a/src/DynamoPackages/PackageLoader.cs b/src/DynamoPackages/PackageLoader.cs index 17971e79be6..04d0646ab23 100644 --- a/src/DynamoPackages/PackageLoader.cs +++ b/src/DynamoPackages/PackageLoader.cs @@ -207,6 +207,12 @@ internal void TryLoadPackageIntoLibrary(Package package) package.Loaded = true; this.PackgeLoaded?.Invoke(package); } + catch (CustomNodePackageLoadException e) + { + Package originalPackage = + localPackages.FirstOrDefault(x => x.CustomNodeDirectory == e.InstalledPath); + OnConflictingPackageLoaded(originalPackage, package); + } catch (Exception e) { Log("Exception when attempting to load package " + package.Name + " from " + package.RootDirectory); @@ -214,6 +220,17 @@ internal void TryLoadPackageIntoLibrary(Package package) } } + /// + /// Event raised when a custom node package containing conflicting node definition + /// with an existing package is tried to load. + /// + public event Action ConflictingCustomNodePackageLoaded; + private void OnConflictingPackageLoaded(Package installed, Package conflicting) + { + var handler = ConflictingCustomNodePackageLoaded; + handler?.Invoke(installed, conflicting); + } + /// /// Load the package into Dynamo (including all node libraries and custom nodes) /// and add to LocalPackages. diff --git a/test/DynamoCoreTests/CustomNodes.cs b/test/DynamoCoreTests/CustomNodes.cs index 53ac98b0306..931901fac75 100644 --- a/test/DynamoCoreTests/CustomNodes.cs +++ b/test/DynamoCoreTests/CustomNodes.cs @@ -1208,7 +1208,7 @@ public void InputNodesShouldMaintainCommentsWhenSavingBrandNewCustomNode() const string nodeName = "someCustomNode"; const string catName1 = "CatName1"; - var cnworkspace = CurrentDynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName1, "a node with an input with comments"); + var cnworkspace = CurrentDynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName1, "a node with an input with comments", null, true); var inputNode = new Symbol(); inputNode.InputSymbol = "/* a new and better comment*/" + Environment.NewLine + "inputName: string = \"a def string\""; cnworkspace.AddAndRegisterNode(inputNode); diff --git a/test/DynamoCoreWpfTests/SerializationTests.cs b/test/DynamoCoreWpfTests/SerializationTests.cs index 8ca6fb59310..b7fcf40f8b3 100644 --- a/test/DynamoCoreWpfTests/SerializationTests.cs +++ b/test/DynamoCoreWpfTests/SerializationTests.cs @@ -922,19 +922,19 @@ public void NewCustomNodeSaveAndLoadPt1() var funcguid = GuidUtility.Create(GuidUtility.UrlNamespace, "NewCustomNodeSaveAndLoad"); //first create a new custom node. - this.ViewModel.ExecuteCommand(new DynamoModel.CreateCustomNodeCommand(funcguid, "testnode", "testcategory", "atest", true)); + var ws = this.ViewModel.Model.CustomNodeManager.CreateCustomNode("testnode", "testcategory", "atest", funcguid, true); var outnode1 = new Output(); outnode1.Symbol = "out1"; var outnode2 = new Output(); - outnode1.Symbol = "out2"; + outnode2.Symbol = "out2"; var numberNode = new DoubleInput(); numberNode.Value = "5"; - this.ViewModel.CurrentSpace.AddAndRegisterNode(numberNode); - this.ViewModel.CurrentSpace.AddAndRegisterNode(outnode1); - this.ViewModel.CurrentSpace.AddAndRegisterNode(outnode2); + ws.AddAndRegisterNode(numberNode); + ws.AddAndRegisterNode(outnode1); + ws.AddAndRegisterNode(outnode2); new ConnectorModel(numberNode.OutPorts.FirstOrDefault(), outnode1.InPorts.FirstOrDefault(), Guid.NewGuid()); new ConnectorModel(numberNode.OutPorts.FirstOrDefault(), outnode2.InPorts.FirstOrDefault(), Guid.NewGuid()); @@ -942,7 +942,7 @@ public void NewCustomNodeSaveAndLoadPt1() var savePath = Path.Combine(this.ViewModel.Model.PathManager.DefinitionDirectories.FirstOrDefault(), "NewCustomNodeSaveAndLoad.dyf"); //save it to the definitions folder so it gets loaded at startup. - this.ViewModel.CurrentSpace.Save(savePath); + ws.Save(savePath); //assert the filesaved Assert.IsTrue(File.Exists(savePath)); @@ -955,7 +955,8 @@ public void NewCustomNodeSaveAndLoadPt2() // This unit test is a follow-up of NewCustomNodeSaveAndLoadPt1 test to make sure the newly created // custom node will be loaded once DynamoCore restarted var funcguid = GuidUtility.Create(GuidUtility.UrlNamespace, "NewCustomNodeSaveAndLoad"); - var functionnode = this.ViewModel.Model.CustomNodeManager.CreateCustomNodeInstance(funcguid,"testnode",true); + var functionnode = + this.ViewModel.Model.CustomNodeManager.CreateCustomNodeInstance(funcguid, "testnode", true); Assert.IsTrue(functionnode.IsCustomFunction); Assert.IsFalse(functionnode.IsInErrorState); Assert.AreEqual(functionnode.OutPorts.Count, 2); diff --git a/test/DynamoCoreWpfTests/WorkspaceSaving.cs b/test/DynamoCoreWpfTests/WorkspaceSaving.cs index 32d575a1e49..b1c12511a63 100644 --- a/test/DynamoCoreWpfTests/WorkspaceSaving.cs +++ b/test/DynamoCoreWpfTests/WorkspaceSaving.cs @@ -228,7 +228,7 @@ public void CustomNodeWorkspaceFilePathIsUpdatedOnSaveAs() var nodeName = "Cool node"; var catName = "Custom Nodes"; - var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, ""); + var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", null, true); var newPath = GetNewFileNameOnTempPath("dyf"); def.Save(newPath); @@ -274,7 +274,7 @@ public void CustomNodeWorkspaceCanSaveAsMultipleTimes() var nodeName = "Cool node"; var catName = "Custom Nodes"; - var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, ""); + var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", null, true); foreach (var i in Enumerable.Range(0, 10)) { @@ -333,7 +333,7 @@ public void CustomNodeWorkspaceCanSaveAsAndThenSaveExistingFile() var nodeName = "Cool node"; var catName = "Custom Nodes"; - var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, ""); + var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", null, true); var newPath = GetNewFileNameOnTempPath("dyf"); def.Save(newPath); @@ -402,7 +402,7 @@ public void CustomNodeWorkspaceCanSaveMultipleTimes() var nodeName = "Cool node"; var catName = "Custom Nodes"; - var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, ""); + var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", null, true); var newPath = GetNewFileNameOnTempPath("dyf"); def.Save(newPath); @@ -666,7 +666,7 @@ public void CustomNodeWorkspaceHasUnsavedChangesPropertyIsSetOnSaveAs() var nodeName = "Cool node"; var catName = "Custom Nodes"; - var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, ""); + var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", null, true); Assert.IsFalse(def.HasUnsavedChanges); var node = new DSFunction(dynamoModel.LibraryServices.GetFunctionDescriptor("+")); @@ -720,7 +720,7 @@ public void CustomNodeSaveAsDoesGiveNewFunctionIdToNewCustomNode() var catName = "Custom Nodes"; var initialId = Guid.NewGuid(); - var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", initialId); + var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", initialId, true); var workspace = (CustomNodeWorkspaceModel)def; var newPath = GetNewFileNameOnTempPath("dyf"); @@ -807,7 +807,7 @@ public void CustomNodeEditNodeDescriptionKeepingViewBlockInDyf() var catName = "Custom Nodes"; var initialId = Guid.NewGuid(); - var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", initialId); + var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", initialId, true); var workspace = (CustomNodeWorkspaceModel)def; // Set file path @@ -852,7 +852,7 @@ public void CustomNodeEditNodeDescriptionKeepingIsVisibleInDynamoLibraryInDyf() var customNodeWorkspace = dynamoModel.CurrentWorkspace; var initialId = new Guid("6aecda57-7679-4afb-aa02-05a75cc3433e"); - var newCustNodeInstance = dynamoModel.CustomNodeManager.CreateCustomNodeInstance(initialId); + var newCustNodeInstance = dynamoModel.CustomNodeManager.CreateCustomNodeInstance(initialId, null, true); // Switch HomeWorkspace and place custom node on it dynamoModel.CurrentWorkspace = dynamoModel.Workspaces.First(); dynamoModel.CurrentWorkspace.AddAndRegisterNode(newCustNodeInstance, false); @@ -1152,7 +1152,7 @@ public void MultipleCustomNodeSaveAsOperationsAddsMultipleValidFunctionIdsToCust var nodeName = "Cool node"; var catName = "Custom Nodes"; - var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, ""); + var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", null, true); var workspace = (CustomNodeWorkspaceModel)def; var listGuids = new List(); @@ -1203,7 +1203,7 @@ public void CusotmNodeSaveAsUpdateItsName() var nodeName = "Foo"; var catName = "Custom Nodes"; - var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, ""); + var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", null, true); var workspace = (CustomNodeWorkspaceModel)def; ViewModel.SearchViewModel.Visible = true; @@ -1246,7 +1246,7 @@ public void CustomNodeWorkspaceSavedToSamePlaceNotCausingDuplicatedLibraryItems( var nodeName = Guid.NewGuid().ToString(); var catName = "Custom Nodes"; - var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, ""); + var def = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", null, true); var tempPath1 = Path.Combine(TempFolder, nodeName + ".dyf"); diff --git a/test/Libraries/PackageManagerTests/PackageLoaderTests.cs b/test/Libraries/PackageManagerTests/PackageLoaderTests.cs index 9a9af8feb62..0a4d39239d8 100644 --- a/test/Libraries/PackageManagerTests/PackageLoaderTests.cs +++ b/test/Libraries/PackageManagerTests/PackageLoaderTests.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using Dynamo.Extensions; +using Dynamo.Search.SearchElements; using Moq; using NUnit.Framework; @@ -129,8 +130,8 @@ public void LoadPackagesReturnsAllValidPackagesInValidDirectory() PathManager = CurrentDynamoModel.PathManager }); - // There are 11 packages in "GitHub\Dynamo\test\pkgs" - Assert.AreEqual(11, loader.LocalPackages.Count()); + // There are 13 packages in "GitHub\Dynamo\test\pkgs" + Assert.AreEqual(13, loader.LocalPackages.Count()); // Verify that interdependent packages are resolved successfully var libs = CurrentDynamoModel.LibraryServices.ImportedLibraries.ToList(); @@ -160,8 +161,8 @@ public void LoadingPackageDoesNotAffectLoadedSearchEntries() PathManager = CurrentDynamoModel.PathManager }); - // There are 11 packages in "GitHub\Dynamo\test\pkgs" - Assert.AreEqual(11, loader.LocalPackages.Count()); + // There are 13 packages in "GitHub\Dynamo\test\pkgs" + Assert.AreEqual(13, loader.LocalPackages.Count()); // Simulate loading new package from PM string packageDirectory = Path.Combine(TestDirectory, @"core\packageDependencyTests\ZTTestPackage"); @@ -177,6 +178,37 @@ public void LoadingPackageDoesNotAffectLoadedSearchEntries() Assert.IsTrue(entries.Count(x => x.FullName == "AnotherPackage.AnotherPackage.AnotherPackage.HelloAnotherWorld") == 1); } + [Test] + public void LoadingConflictingCustomNodePackageDoesNotGetLoaded() + { + var loader = new PackageLoader(PackagesDirectory); + var libraryLoader = new ExtensionLibraryLoader(CurrentDynamoModel); + + loader.PackagesLoaded += libraryLoader.LoadPackages; + loader.RequestLoadNodeLibrary += libraryLoader.LoadNodeLibrary; + + // This test needs the "isTestMode" flag to be turned off as an exception to be able + // to test duplicate custom node def loading. + loader.RequestLoadCustomNodeDirectory += + (dir) => CurrentDynamoModel.CustomNodeManager.AddUninitializedCustomNodesInPath(dir, isTestMode: false); + + loader.LoadAll(new LoadPackageParams + { + Preferences = CurrentDynamoModel.PreferenceSettings, + PathManager = CurrentDynamoModel.PathManager + }); + + // There are 13 packages in "GitHub\Dynamo\test\pkgs" + Assert.AreEqual(13, loader.LocalPackages.Count()); + + var entries = CurrentDynamoModel.SearchModel.SearchEntries.OfType(); + + // Check that conflicting custom node package "EvenOdd2" is not installed + Assert.IsTrue(entries.Count(x => Path.GetDirectoryName(x.Path).EndsWith(@"EvenOdd2\dyf")) == 0); + Assert.IsTrue(entries.Count(x => Path.GetDirectoryName(x.Path).EndsWith(@"EvenOdd\dyf") && + x.FullName == "Test.EvenOdd") == 1); + } + [Test] public void LoadPackagesReturnsNoPackagesForInvalidDirectory() { diff --git a/test/pkgs/EvenOdd/dyf/EvenOdd.dyf b/test/pkgs/EvenOdd/dyf/EvenOdd.dyf new file mode 100644 index 00000000000..d2ea6b57082 --- /dev/null +++ b/test/pkgs/EvenOdd/dyf/EvenOdd.dyf @@ -0,0 +1,397 @@ +{ + "Uuid": "3f19c484-d3f3-49ba-88c2-6c386a41f6ac", + "IsCustomNode": true, + "Category": "Test", + "Description": "Tells if a number in a range is even or odd", + "Name": "EvenOdd", + "ElementResolver": { + "ResolutionMap": {} + }, + "Inputs": [], + "Outputs": [], + "Nodes": [ + { + "ConcreteType": "CoreNodeModels.Range, CoreNodeModels", + "NodeType": "ExtensionNode", + "Id": "41fdfda5044b44f0b364e1a31d10bcb3", + "Inputs": [ + { + "Id": "39c5ca40b4ff41a1bb4379032f7dff27", + "Name": "start", + "Description": "Number or letter to start the sequence at\r\nDefault value: 0", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "c70ffc1d585447acb96b26313ba350af", + "Name": "end", + "Description": "Number or letter to end the sequence at\r\nDefault value: 9", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "31390c529e1440dba846361af4a77905", + "Name": "step", + "Description": "Space between numbers or letters\r\nDefault value: 1", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "889f5e5e5a6a453aba1818f22d485fb5", + "Name": "seq", + "Description": "New sequence", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "Creates a sequence of numbers or letters in the specified range." + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "NodeType": "CodeBlockNode", + "Code": "2;", + "Id": "1713fa72580348369985b6d3ff43f6b6", + "Inputs": [], + "Outputs": [ + { + "Id": "19fe83cf33d84d10bbcc70a75b535d7f", + "Name": "", + "Description": "Value of expression at line 1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Allows for DesignScript code to be authored directly" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore", + "NodeType": "FunctionNode", + "FunctionSignature": "%@var[]..[],var[]..[]", + "Id": "1ddf4b4cc39f42acadd578db42bcb6d3", + "Inputs": [ + { + "Id": "52c34d69fe42408ca5c857f371168587", + "Name": "x", + "Description": "x value.\n\nvar[]..[]", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "02afc74a897f47b4a939f9adde53d613", + "Name": "y", + "Description": "y value.\n\nvar[]..[]", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "b92ceaf7eae649c280a7e8e3206631ee", + "Name": "var[]..[]", + "Description": "var[]..[]", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "Finds the remainder of x/y\n\n% (x: var[]..[], y: var[]..[]): var[]..[]" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "NodeType": "CodeBlockNode", + "Code": "x == 0 ? \"even\" : \"odd\";", + "Id": "7151fed9e4db49ada604b0d42eb7f7f2", + "Inputs": [ + { + "Id": "412b1849c1f142aaa72ad8f81d8166ba", + "Name": "x", + "Description": "x", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "87b4d22fb0aa4fe28c96a6217ecd874c", + "Name": "", + "Description": "Value of expression at line 1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Allows for DesignScript code to be authored directly" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CustomNodes.Symbol, DynamoCore", + "NodeType": "InputNode", + "Parameter": { + "Name": "start", + "TypeName": "var", + "TypeRank": -1, + "DefaultValue": null, + "Description": "" + }, + "Id": "d1285386263b495388a76953bfc82357", + "Inputs": [], + "Outputs": [ + { + "Id": "3bd721697c394adf9b6550f16674e34f", + "Name": "", + "Description": "Symbol", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "A function parameter, use with custom nodes.\r\n\r\nYou can specify the type and default value for parameter. E.g.,\r\n\r\ninput : var[]..[]\r\nvalue : bool = false" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CustomNodes.Symbol, DynamoCore", + "NodeType": "InputNode", + "Parameter": { + "Name": "end", + "TypeName": "var", + "TypeRank": -1, + "DefaultValue": null, + "Description": "" + }, + "Id": "6bbad8b9abae4e029957792104a3f50e", + "Inputs": [], + "Outputs": [ + { + "Id": "3a5786784fcb4732a91e16eb237da7d4", + "Name": "", + "Description": "Symbol", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "A function parameter, use with custom nodes.\r\n\r\nYou can specify the type and default value for parameter. E.g.,\r\n\r\ninput : var[]..[]\r\nvalue : bool = false" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CustomNodes.Symbol, DynamoCore", + "NodeType": "InputNode", + "Parameter": { + "Name": "step", + "TypeName": "var", + "TypeRank": -1, + "DefaultValue": null, + "Description": "" + }, + "Id": "a8b1297e19224a30ba48e2bfca132423", + "Inputs": [], + "Outputs": [ + { + "Id": "08dbbcb643984628a3b6779d3aa03f5f", + "Name": "", + "Description": "Symbol", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "A function parameter, use with custom nodes.\r\n\r\nYou can specify the type and default value for parameter. E.g.,\r\n\r\ninput : var[]..[]\r\nvalue : bool = false" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CustomNodes.Output, DynamoCore", + "NodeType": "OutputNode", + "ElementResolver": null, + "Symbol": "", + "Id": "2bcbfb454357477181db694346874e71", + "Inputs": [ + { + "Id": "40390ef2e99a4a23992a7df06b8be2bf", + "Name": "", + "Description": "", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [], + "Replication": "Disabled", + "Description": "A function output, use with custom nodes" + } + ], + "Connectors": [ + { + "Start": "889f5e5e5a6a453aba1818f22d485fb5", + "End": "52c34d69fe42408ca5c857f371168587", + "Id": "6576704eb36f4ed0b8f00fd6f2734dac" + }, + { + "Start": "19fe83cf33d84d10bbcc70a75b535d7f", + "End": "02afc74a897f47b4a939f9adde53d613", + "Id": "6b3cecd3c41249e6baa4c6ecb53c696b" + }, + { + "Start": "b92ceaf7eae649c280a7e8e3206631ee", + "End": "412b1849c1f142aaa72ad8f81d8166ba", + "Id": "f3de091f0a464646b785d132fbdb2cb4" + }, + { + "Start": "87b4d22fb0aa4fe28c96a6217ecd874c", + "End": "40390ef2e99a4a23992a7df06b8be2bf", + "Id": "46ac5c27131b426cb949b3c6c41e3a93" + }, + { + "Start": "3bd721697c394adf9b6550f16674e34f", + "End": "39c5ca40b4ff41a1bb4379032f7dff27", + "Id": "fe5b410acf474dfb8daf509305c0bd72" + }, + { + "Start": "3a5786784fcb4732a91e16eb237da7d4", + "End": "c70ffc1d585447acb96b26313ba350af", + "Id": "874e6d04ff5e4308a9bcccd33a3206ea" + }, + { + "Start": "08dbbcb643984628a3b6779d3aa03f5f", + "End": "31390c529e1440dba846361af4a77905", + "Id": "c74a545ec25d4aea95e9aabc2b51c79b" + } + ], + "Dependencies": [], + "PackageDependencies": [], + "Bindings": [], + "View": { + "Dynamo": { + "ScaleFactor": 1.0, + "HasRunWithoutCrash": false, + "IsVisibleInDynamoLibrary": true, + "Version": "2.3.0.5425", + "RunType": "Manual", + "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": "Range", + "Id": "41fdfda5044b44f0b364e1a31d10bcb3", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 250.0, + "Y": 0.0 + }, + { + "ShowGeometry": true, + "Name": "Code Block", + "Id": "1713fa72580348369985b6d3ff43f6b6", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 492.12166172106822, + "Y": 126.10385756676556 + }, + { + "ShowGeometry": true, + "Name": "%", + "Id": "1ddf4b4cc39f42acadd578db42bcb6d3", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 648.22551928783378, + "Y": 21.169139465875332 + }, + { + "ShowGeometry": true, + "Name": "Code Block", + "Id": "7151fed9e4db49ada604b0d42eb7f7f2", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 874.0, + "Y": 69.0 + }, + { + "ShowGeometry": true, + "Name": "Input", + "Id": "d1285386263b495388a76953bfc82357", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 0.0, + "Y": 0.0 + }, + { + "ShowGeometry": true, + "Name": "Input", + "Id": "6bbad8b9abae4e029957792104a3f50e", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 0.0, + "Y": 150.0 + }, + { + "ShowGeometry": true, + "Name": "Input", + "Id": "a8b1297e19224a30ba48e2bfca132423", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 0.0, + "Y": 300.0 + }, + { + "ShowGeometry": true, + "Name": "Output", + "Id": "2bcbfb454357477181db694346874e71", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 1205.4955489614242, + "Y": 84.272997032640973 + } + ], + "Annotations": [], + "X": 58.237500000000011, + "Y": 100.88749999999999, + "Zoom": 0.8425 + } +} \ No newline at end of file diff --git a/test/pkgs/EvenOdd/pkg.json b/test/pkgs/EvenOdd/pkg.json new file mode 100644 index 00000000000..392194be880 --- /dev/null +++ b/test/pkgs/EvenOdd/pkg.json @@ -0,0 +1 @@ +{"license":"","file_hash":null,"name":"EvenOdd","version":"1.0.0","description":"Tells if a number in a range is even or odd","group":"","keywords":null,"dependencies":[],"contents":"EvenOdd - Tells if a number in a range is even or odd","engine_version":"2.1.0.7840","engine":"dynamo","engine_metadata":"","site_url":"","repository_url":"","contains_binaries":false,"node_libraries":[]} \ No newline at end of file diff --git a/test/pkgs/EvenOdd2/dyf/EvenOdd.dyf b/test/pkgs/EvenOdd2/dyf/EvenOdd.dyf new file mode 100644 index 00000000000..d2ea6b57082 --- /dev/null +++ b/test/pkgs/EvenOdd2/dyf/EvenOdd.dyf @@ -0,0 +1,397 @@ +{ + "Uuid": "3f19c484-d3f3-49ba-88c2-6c386a41f6ac", + "IsCustomNode": true, + "Category": "Test", + "Description": "Tells if a number in a range is even or odd", + "Name": "EvenOdd", + "ElementResolver": { + "ResolutionMap": {} + }, + "Inputs": [], + "Outputs": [], + "Nodes": [ + { + "ConcreteType": "CoreNodeModels.Range, CoreNodeModels", + "NodeType": "ExtensionNode", + "Id": "41fdfda5044b44f0b364e1a31d10bcb3", + "Inputs": [ + { + "Id": "39c5ca40b4ff41a1bb4379032f7dff27", + "Name": "start", + "Description": "Number or letter to start the sequence at\r\nDefault value: 0", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "c70ffc1d585447acb96b26313ba350af", + "Name": "end", + "Description": "Number or letter to end the sequence at\r\nDefault value: 9", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "31390c529e1440dba846361af4a77905", + "Name": "step", + "Description": "Space between numbers or letters\r\nDefault value: 1", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "889f5e5e5a6a453aba1818f22d485fb5", + "Name": "seq", + "Description": "New sequence", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "Creates a sequence of numbers or letters in the specified range." + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "NodeType": "CodeBlockNode", + "Code": "2;", + "Id": "1713fa72580348369985b6d3ff43f6b6", + "Inputs": [], + "Outputs": [ + { + "Id": "19fe83cf33d84d10bbcc70a75b535d7f", + "Name": "", + "Description": "Value of expression at line 1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Allows for DesignScript code to be authored directly" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore", + "NodeType": "FunctionNode", + "FunctionSignature": "%@var[]..[],var[]..[]", + "Id": "1ddf4b4cc39f42acadd578db42bcb6d3", + "Inputs": [ + { + "Id": "52c34d69fe42408ca5c857f371168587", + "Name": "x", + "Description": "x value.\n\nvar[]..[]", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "02afc74a897f47b4a939f9adde53d613", + "Name": "y", + "Description": "y value.\n\nvar[]..[]", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "b92ceaf7eae649c280a7e8e3206631ee", + "Name": "var[]..[]", + "Description": "var[]..[]", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "Finds the remainder of x/y\n\n% (x: var[]..[], y: var[]..[]): var[]..[]" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "NodeType": "CodeBlockNode", + "Code": "x == 0 ? \"even\" : \"odd\";", + "Id": "7151fed9e4db49ada604b0d42eb7f7f2", + "Inputs": [ + { + "Id": "412b1849c1f142aaa72ad8f81d8166ba", + "Name": "x", + "Description": "x", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "87b4d22fb0aa4fe28c96a6217ecd874c", + "Name": "", + "Description": "Value of expression at line 1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Allows for DesignScript code to be authored directly" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CustomNodes.Symbol, DynamoCore", + "NodeType": "InputNode", + "Parameter": { + "Name": "start", + "TypeName": "var", + "TypeRank": -1, + "DefaultValue": null, + "Description": "" + }, + "Id": "d1285386263b495388a76953bfc82357", + "Inputs": [], + "Outputs": [ + { + "Id": "3bd721697c394adf9b6550f16674e34f", + "Name": "", + "Description": "Symbol", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "A function parameter, use with custom nodes.\r\n\r\nYou can specify the type and default value for parameter. E.g.,\r\n\r\ninput : var[]..[]\r\nvalue : bool = false" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CustomNodes.Symbol, DynamoCore", + "NodeType": "InputNode", + "Parameter": { + "Name": "end", + "TypeName": "var", + "TypeRank": -1, + "DefaultValue": null, + "Description": "" + }, + "Id": "6bbad8b9abae4e029957792104a3f50e", + "Inputs": [], + "Outputs": [ + { + "Id": "3a5786784fcb4732a91e16eb237da7d4", + "Name": "", + "Description": "Symbol", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "A function parameter, use with custom nodes.\r\n\r\nYou can specify the type and default value for parameter. E.g.,\r\n\r\ninput : var[]..[]\r\nvalue : bool = false" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CustomNodes.Symbol, DynamoCore", + "NodeType": "InputNode", + "Parameter": { + "Name": "step", + "TypeName": "var", + "TypeRank": -1, + "DefaultValue": null, + "Description": "" + }, + "Id": "a8b1297e19224a30ba48e2bfca132423", + "Inputs": [], + "Outputs": [ + { + "Id": "08dbbcb643984628a3b6779d3aa03f5f", + "Name": "", + "Description": "Symbol", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "A function parameter, use with custom nodes.\r\n\r\nYou can specify the type and default value for parameter. E.g.,\r\n\r\ninput : var[]..[]\r\nvalue : bool = false" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CustomNodes.Output, DynamoCore", + "NodeType": "OutputNode", + "ElementResolver": null, + "Symbol": "", + "Id": "2bcbfb454357477181db694346874e71", + "Inputs": [ + { + "Id": "40390ef2e99a4a23992a7df06b8be2bf", + "Name": "", + "Description": "", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [], + "Replication": "Disabled", + "Description": "A function output, use with custom nodes" + } + ], + "Connectors": [ + { + "Start": "889f5e5e5a6a453aba1818f22d485fb5", + "End": "52c34d69fe42408ca5c857f371168587", + "Id": "6576704eb36f4ed0b8f00fd6f2734dac" + }, + { + "Start": "19fe83cf33d84d10bbcc70a75b535d7f", + "End": "02afc74a897f47b4a939f9adde53d613", + "Id": "6b3cecd3c41249e6baa4c6ecb53c696b" + }, + { + "Start": "b92ceaf7eae649c280a7e8e3206631ee", + "End": "412b1849c1f142aaa72ad8f81d8166ba", + "Id": "f3de091f0a464646b785d132fbdb2cb4" + }, + { + "Start": "87b4d22fb0aa4fe28c96a6217ecd874c", + "End": "40390ef2e99a4a23992a7df06b8be2bf", + "Id": "46ac5c27131b426cb949b3c6c41e3a93" + }, + { + "Start": "3bd721697c394adf9b6550f16674e34f", + "End": "39c5ca40b4ff41a1bb4379032f7dff27", + "Id": "fe5b410acf474dfb8daf509305c0bd72" + }, + { + "Start": "3a5786784fcb4732a91e16eb237da7d4", + "End": "c70ffc1d585447acb96b26313ba350af", + "Id": "874e6d04ff5e4308a9bcccd33a3206ea" + }, + { + "Start": "08dbbcb643984628a3b6779d3aa03f5f", + "End": "31390c529e1440dba846361af4a77905", + "Id": "c74a545ec25d4aea95e9aabc2b51c79b" + } + ], + "Dependencies": [], + "PackageDependencies": [], + "Bindings": [], + "View": { + "Dynamo": { + "ScaleFactor": 1.0, + "HasRunWithoutCrash": false, + "IsVisibleInDynamoLibrary": true, + "Version": "2.3.0.5425", + "RunType": "Manual", + "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": "Range", + "Id": "41fdfda5044b44f0b364e1a31d10bcb3", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 250.0, + "Y": 0.0 + }, + { + "ShowGeometry": true, + "Name": "Code Block", + "Id": "1713fa72580348369985b6d3ff43f6b6", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 492.12166172106822, + "Y": 126.10385756676556 + }, + { + "ShowGeometry": true, + "Name": "%", + "Id": "1ddf4b4cc39f42acadd578db42bcb6d3", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 648.22551928783378, + "Y": 21.169139465875332 + }, + { + "ShowGeometry": true, + "Name": "Code Block", + "Id": "7151fed9e4db49ada604b0d42eb7f7f2", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 874.0, + "Y": 69.0 + }, + { + "ShowGeometry": true, + "Name": "Input", + "Id": "d1285386263b495388a76953bfc82357", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 0.0, + "Y": 0.0 + }, + { + "ShowGeometry": true, + "Name": "Input", + "Id": "6bbad8b9abae4e029957792104a3f50e", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 0.0, + "Y": 150.0 + }, + { + "ShowGeometry": true, + "Name": "Input", + "Id": "a8b1297e19224a30ba48e2bfca132423", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 0.0, + "Y": 300.0 + }, + { + "ShowGeometry": true, + "Name": "Output", + "Id": "2bcbfb454357477181db694346874e71", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 1205.4955489614242, + "Y": 84.272997032640973 + } + ], + "Annotations": [], + "X": 58.237500000000011, + "Y": 100.88749999999999, + "Zoom": 0.8425 + } +} \ No newline at end of file diff --git a/test/pkgs/EvenOdd2/pkg.json b/test/pkgs/EvenOdd2/pkg.json new file mode 100644 index 00000000000..3829f8ddad3 --- /dev/null +++ b/test/pkgs/EvenOdd2/pkg.json @@ -0,0 +1 @@ +{"license":"","file_hash":null,"name":"EvenOdd2","version":"1.0.0","description":"Tells if number in range is even or odd","group":"","keywords":null,"dependencies":[],"contents":"EvenOdd - Tells if a number in a range is even or odd","engine_version":"2.1.0.7840","engine":"dynamo","engine_metadata":"","site_url":"","repository_url":"","contains_binaries":false,"node_libraries":[]} \ No newline at end of file