Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/DynamoCore/Graph/Nodes/IValueSchemaProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Dynamo.Graph.Nodes
{
/// <summary>
/// Implemented by NodeModels that can describe their value type
/// for external consumers (DynamoPlayer, MCP, etc.).
/// Returns a canonical Forge Data Schema type identifier and list context.
/// </summary>
public interface IValueSchemaProvider
{
/// <summary>
/// Canonical type identifier in Forge Data Schema format.
/// Examples: "autodesk.math:point3d-1.0.0", "String", "Float64".
/// Returns a non-null type identifier. If the type is not yet determined,
/// implementations should return their existing non-null fallback value.
/// </summary>
string ValueTypeId { get; }

/// <summary>
/// Whether the value is a list (array) of the declared type.
/// </summary>
bool IsListValue { get; }
}
}
3 changes: 3 additions & 0 deletions src/DynamoCore/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
Dynamo.Graph.Nodes.IValueSchemaProvider
Dynamo.Graph.Nodes.IValueSchemaProvider.IsListValue.get -> bool
Dynamo.Graph.Nodes.IValueSchemaProvider.ValueTypeId.get -> string
Dynamo.Models.DynamoModel.DefaultStartConfiguration.EnableUnTrustedLocationsNotifications.get -> bool
Dynamo.Models.DynamoModel.DefaultStartConfiguration.EnableUnTrustedLocationsNotifications.set -> void
Dynamo.Models.DynamoModel.IStartConfiguration.EnableUnTrustedLocationsNotifications.get -> bool
19 changes: 18 additions & 1 deletion src/Libraries/CoreNodeModels/DefineData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,25 @@ namespace CoreNodeModels
[OutPortDescriptions(typeof(Properties.Resources), nameof(Properties.Resources.DefineDataOutputTooltip))]
[IsDesignScriptCompatible]
[AlsoKnownAs("Data.DefineData")]
public class DefineData : DSDropDownBase
public class DefineData : DSDropDownBase, IValueSchemaProvider
{
/// <inheritdoc/>
[JsonIgnore]
public string ValueTypeId
Comment thread
johnpierson marked this conversation as resolved.
{
get
{
if (SelectedIndex < 0 || SelectedIndex >= Items.Count)
return SelectedString;

return (Items[SelectedIndex].Item as Data.DataNodeDynamoType)?.TypeId ?? SelectedString;
}
}

/// <inheritdoc/>
[JsonIgnore]
public bool IsListValue => IsList;
Comment thread
kalunkuo marked this conversation as resolved.

private bool isAutoMode = true; // default start with auto-detect 'on'
private bool isList;
private string displayValue = Properties.Resources.DefineDataDisplayValueMessage;
Expand Down
73 changes: 42 additions & 31 deletions src/Libraries/CoreNodes/Data.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@

//types supported by Geometry.FromJson
case "autodesk.math:point3d-1.0.0":
case "dynamo.geometry:sab-1.0.0":

Check warning on line 110 in src/Libraries/CoreNodes/Data.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of using this literal 'dynamo.geometry:sab-1.0.0' 6 times.

See more on https://sonarcloud.io/project/issues?id=DynamoDS_Dynamo&issues=AZ2Hra2fz0UVeFxneQn4&open=AZ2Hra2fz0UVeFxneQn4&pullRequest=17040
case "dynamo.geometry:tsm-1.0.0":
case "dynamo.geometry:rectangle-1.0.0":
case "dynamo.geometry:cuboid-1.0.0":
Expand Down Expand Up @@ -534,7 +534,7 @@
/// <summary>
/// A class representing a DataType supported by Dynamo
/// </summary>
internal class DataNodeDynamoType(Type type, string name = null)
internal class DataNodeDynamoType(Type type, string name = null, string typeId = null)
{
/// <summary>
/// The underlying Type
Expand All @@ -545,6 +545,17 @@
/// </summary>
public string Name { get; private set; } = name ?? type.Name;
/// <summary>
/// Wire-format $typeid as emitted by ProtoGeometry's ToJson() / DSCore.Data.StringifyJSON
/// (e.g. "autodesk.math:point3d-1.0.0", "autodesk.geometry.curve:bcurve-1.0.0").
/// Null for primitive types (bool, string, Number, etc.) that don't use $typeid serialization.
/// Must match the identifiers recognised by <see cref="DynamoJObjectToNative"/>
/// and the actual $typeid values produced by ProtoGeometry serialization.
/// Exposed to external consumers via <see cref="Dynamo.Graph.Nodes.IValueSchemaProvider.ValueTypeId"/>
/// (implemented by DefineData), which DynamoPlayer reads to populate
/// ValueSchema.TypeId and DynamoMCP uses to resolve JSON Schemas for LLM inputs.
/// </summary>
public string TypeId { get; private set; } = typeId;
/// <summary>
/// The hierarchical level to be displayed in the UI
/// </summary>
public int Level { get; private set; } = 0;
Expand All @@ -557,8 +568,8 @@
/// </summary>
public DataNodeDynamoType Parent { get; private set; }

public DataNodeDynamoType(Type type, int level, bool isLastChild = false, string name = null, DataNodeDynamoType parent = null)
: this(type, name)
public DataNodeDynamoType(Type type, int level, bool isLastChild = false, string name = null, DataNodeDynamoType parent = null, string typeId = null)
: this(type, name, typeId)
{
Level = level;
IsLastChild = isLastChild;
Expand All @@ -577,53 +588,53 @@
/// </summary>
static Data()
{
var curve = new DataNodeDynamoType(typeof(Curve), 0, false, null, null);
var polyCurve = new DataNodeDynamoType(typeof(PolyCurve), 1, false, null, curve);
var polygon = new DataNodeDynamoType(typeof(Polygon), 2, false, null, polyCurve); // polygon is subtype of polyCurve
var rectangle = new DataNodeDynamoType(typeof(Autodesk.DesignScript.Geometry.Rectangle), 3, true, null, polyCurve); // rectangle is subtype of polygon
var solid = new DataNodeDynamoType(typeof(Solid), 0, false, null, null);
var cone = new DataNodeDynamoType(typeof(Cone), 1, false, null, solid); // cone is subtype of solid
var cylinder = new DataNodeDynamoType(typeof(Cylinder), 2, false, null, cone); // cylinder is subtype of cone
var cuboid = new DataNodeDynamoType(typeof(Cuboid), 1, false, null, solid); // cuboid is subtype of solid
var sphere = new DataNodeDynamoType(typeof(Sphere), 1, true, null, solid); // sphere is subtype of solid

var surface = new DataNodeDynamoType(typeof(Surface), 0, false, null, null);
var curve = new DataNodeDynamoType(typeof(Curve), 0, false, null, null, "dynamo.geometry:sab-1.0.0");
var polyCurve = new DataNodeDynamoType(typeof(PolyCurve), 1, false, null, curve, "autodesk.geometry.curve:compositecurve-1.0.0");
var polygon = new DataNodeDynamoType(typeof(Polygon), 2, false, null, polyCurve, "autodesk.geometry.curve:polyline-1.0.0");
var rectangle = new DataNodeDynamoType(typeof(Autodesk.DesignScript.Geometry.Rectangle), 3, true, null, polyCurve, "dynamo.geometry:rectangle-1.0.0");
var solid = new DataNodeDynamoType(typeof(Solid), 0, false, null, null, "dynamo.geometry:sab-1.0.0");
var cone = new DataNodeDynamoType(typeof(Cone), 1, false, null, solid, "dynamo.geometry:cone-1.0.0");
Comment thread
kalunkuo marked this conversation as resolved.
var cylinder = new DataNodeDynamoType(typeof(Cylinder), 2, false, null, cone, "autodesk.geometry.surface:cylinder-2.0.0");
var cuboid = new DataNodeDynamoType(typeof(Cuboid), 1, false, null, solid, "dynamo.geometry:cuboid-1.0.0");
var sphere = new DataNodeDynamoType(typeof(Sphere), 1, true, null, solid, "autodesk.geometry.surface:sphere-1.0.0");

var surface = new DataNodeDynamoType(typeof(Surface), 0, false, null, null, "dynamo.geometry:sab-1.0.0");

var typeList = new List<DataNodeDynamoType>
{
new(typeof(bool)),
new(typeof(BoundingBox)),
new(typeof(CoordinateSystem)),
new(typeof(BoundingBox), typeId: "autodesk.geometry:boundingbox3d-1.0.0"),
new(typeof(CoordinateSystem), typeId: "autodesk.math:matrix44d-1.0.0"),
curve,
new(typeof(Arc), 1, false, null, curve),
new(typeof(Circle), 1, false, null, curve),
new(typeof(Ellipse), 1, false, null, curve),
new(typeof(EllipseArc), 1, false, null, curve),
new(typeof(Helix), 1, false, null, curve),
new(typeof(Line), 1, false, null, curve),
new(typeof(NurbsCurve), 1, false, null, curve),
new(typeof(Arc), 1, false, null, curve, "autodesk.geometry.curve:circle-1.0.0"),
new(typeof(Circle), 1, false, null, curve, "autodesk.geometry.curve:circle-1.0.0"),
new(typeof(Ellipse), 1, false, null, curve, "autodesk.geometry.curve:ellipse-1.0.0"),
new(typeof(EllipseArc), 1, false, null, curve, "autodesk.geometry.curve:ellipse-1.0.0"),
new(typeof(Helix), 1, false, null, curve, "dynamo.geometry:sab-1.0.0"),
new(typeof(Line), 1, false, null, curve, "autodesk.geometry.curve:line-1.0.0"),
new(typeof(NurbsCurve), 1, false, null, curve, "autodesk.geometry.curve:bcurve-1.0.0"),
polyCurve,
polygon,
rectangle,
new(typeof(System.DateTime)),
new(typeof(double), "Number"),
new(typeof(long), "Integer"),
new(typeof(Location)),
new(typeof(Mesh)),
new(typeof(Plane)),
new(typeof(Autodesk.DesignScript.Geometry.Point)),
new(typeof(Location), typeId: "dynamo.data:location-1.0.0"),
new(typeof(Mesh), typeId: "dynamo.geometry:mesh-1.0.0"),
new(typeof(Plane), typeId: "autodesk.geometry.surface:plane-1.0.0"),
new(typeof(Autodesk.DesignScript.Geometry.Point), typeId: "autodesk.math:point3d-1.0.0"),
solid,
cone,
cylinder,
cuboid,
sphere,
new(typeof(string)),
surface,
new(typeof(NurbsSurface), 1, false, null, surface),
new(typeof(PolySurface), 1, true, null, surface),
new(typeof(NurbsSurface), 1, false, null, surface, "autodesk.geometry.curve:bsurface-1.0.0"),
new(typeof(PolySurface), 1, true, null, surface, "dynamo.geometry:sab-1.0.0"),
new(typeof(System.TimeSpan)),
new(typeof(UV)),
new(typeof(Vector))
new(typeof(UV), typeId: "autodesk.math:uv-1.0.0"),
new(typeof(Vector), typeId: "autodesk.math:vector3d-1.0.0")
};

DataNodeDynamoTypeList = new ReadOnlyCollection<DataNodeDynamoType>(typeList);
Expand Down
59 changes: 59 additions & 0 deletions test/DynamoCoreTests/Nodes/DefineDataTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.Linq;
using CoreNodeModels;
using NUnit.Framework;

namespace Dynamo.Tests.Nodes
{
[TestFixture]
internal class DefineDataTests : DynamoModelTestBase
{
protected override void GetLibrariesToPreload(List<string> libraries)
{
libraries.Add("DSCoreNodes.dll");
base.GetLibrariesToPreload(libraries);
}

[Test]
[Category("UnitTests")]
public void ValueSchemaProviderReturnsTypeId()
{
var node = new DefineData();
CurrentDynamoModel.CurrentWorkspace.AddAndRegisterNode(node, false);

// Find a type with a non-null TypeId for testing
var pointType = DSCore.Data.DataNodeDynamoTypeList.First(t => t.Type == typeof(Autodesk.DesignScript.Geometry.Point));
Assert.IsNotNull(pointType.TypeId, "Point type should have a valid TypeId");

// Set the node to Point type
node.SelectedIndex = DSCore.Data.DataNodeDynamoTypeList.IndexOf(pointType);

Assert.AreEqual(pointType.TypeId, node.ValueTypeId);
Assert.IsFalse(node.IsListValue);
}

[Test]
[Category("UnitTests")]
public void ValueTypeIdSafeWhenNoSelection()
{
var node = new DefineData();
Assert.DoesNotThrow(() => { var _ = node.ValueTypeId; });
Assert.AreEqual(node.SelectedString, node.ValueTypeId);
}

[Test]
[Category("UnitTests")]
public void ValueTypeIdHandlesNullTypeId()
{
var node = new DefineData();
CurrentDynamoModel.CurrentWorkspace.AddAndRegisterNode(node, false);

// Default selection (bool) has null TypeId
var firstType = DSCore.Data.DataNodeDynamoTypeList.First();
Assert.IsNull(firstType.TypeId, "First type (bool) should have null TypeId");

// Should fall back to SelectedString when TypeId is null
Assert.AreEqual(node.SelectedString, node.ValueTypeId);
}
}
}
Loading