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