diff --git a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs index aa22988eb..64826f508 100644 --- a/src/WorkflowCore.DSL/Services/DefinitionLoader.cs +++ b/src/WorkflowCore.DSL/Services/DefinitionLoader.cs @@ -222,7 +222,7 @@ private void AttachInputs(StepSourceV1 source, Type dataType, Type stepType, Wor var dataParameter = Expression.Parameter(dataType, "data"); var contextParameter = Expression.Parameter(typeof(IStepExecutionContext), "context"); var environmentVarsParameter = Expression.Parameter(typeof(IDictionary), "environment"); - var stepProperty = stepType.GetProperty(input.Key); + var stepProperty = stepType.GetProperty(input.Key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); if (stepProperty == null) { @@ -243,6 +243,21 @@ private void AttachInputs(StepSourceV1 source, Type dataType, Type stepType, Wor continue; } + // Handle primitive values (bool, int, etc.) directly + if (input.Value != null && (input.Value.GetType().IsPrimitive || input.Value is bool || input.Value is int || input.Value is long || input.Value is double || input.Value is decimal)) + { + var primitiveValue = input.Value; + void primitiveAction(IStepBody pStep, object pData) + { + if (stepProperty.PropertyType.IsAssignableFrom(primitiveValue.GetType())) + stepProperty.SetValue(pStep, primitiveValue); + else + stepProperty.SetValue(pStep, System.Convert.ChangeType(primitiveValue, stepProperty.PropertyType)); + } + step.Inputs.Add(new ActionParameter(primitiveAction)); + continue; + } + throw new ArgumentException($"Unknown type for input {input.Key} on {source.Id}"); } } diff --git a/test/WorkflowCore.TestAssets/DataTypes/CounterBoard.cs b/test/WorkflowCore.TestAssets/DataTypes/CounterBoard.cs index a93b43585..d7c03c2d7 100644 --- a/test/WorkflowCore.TestAssets/DataTypes/CounterBoard.cs +++ b/test/WorkflowCore.TestAssets/DataTypes/CounterBoard.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace WorkflowCore.TestAssets.DataTypes { @@ -17,5 +18,6 @@ public class CounterBoard public bool Flag1 { get; set; } public bool Flag2 { get; set; } public bool Flag3 { get; set; } + public List DataList { get; set; } = new List { "item1", "item2", "item3" }; } } diff --git a/test/WorkflowCore.TestAssets/Steps/IterateListStep.cs b/test/WorkflowCore.TestAssets/Steps/IterateListStep.cs new file mode 100644 index 000000000..77e9069f0 --- /dev/null +++ b/test/WorkflowCore.TestAssets/Steps/IterateListStep.cs @@ -0,0 +1,12 @@ +using WorkflowCore.Interface; +using WorkflowCore.Models; +using WorkflowCore.Primitives; + +namespace WorkflowCore.TestAssets.Steps +{ + public class IterateListStep : Foreach + { + // This class inherits from Foreach and should be able to use + // the RunParallel property from the base class in YAML definitions + } +} \ No newline at end of file diff --git a/test/WorkflowCore.UnitTests/Services/DefinitionStorage/YamlInheritedPropertyIntegrationTest.cs b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/YamlInheritedPropertyIntegrationTest.cs new file mode 100644 index 000000000..f036851f2 --- /dev/null +++ b/test/WorkflowCore.UnitTests/Services/DefinitionStorage/YamlInheritedPropertyIntegrationTest.cs @@ -0,0 +1,75 @@ +using System; +using FakeItEasy; +using WorkflowCore.Interface; +using WorkflowCore.Services.DefinitionStorage; +using Xunit; + +namespace WorkflowCore.UnitTests.Services.DefinitionStorage +{ + /// + /// Integration test to verify the fix for inherited property binding in YAML definitions. + /// This test specifically reproduces the issue mentioned in GitHub issue #1375. + /// + public class YamlInheritedPropertyIntegrationTest + { + [Fact(DisplayName = "Should bind inherited properties like RunParallel from base Foreach class")] + public void ShouldBindInheritedPropertiesInYamlDefinition() + { + // Arrange + var registry = A.Fake(); + var loader = new DefinitionLoader(registry, new TypeResolver()); + + // This YAML definition uses a custom step (IterateListStep) that inherits from Foreach + // and tries to set the RunParallel property which is defined in the base Foreach class + var yamlWithInheritedProperty = @" +Id: TestInheritedPropertyWorkflow +Version: 1 +Description: Test workflow for inherited property binding +DataType: WorkflowCore.TestAssets.DataTypes.CounterBoard, WorkflowCore.TestAssets +Steps: + - Id: IterateList + StepType: WorkflowCore.TestAssets.Steps.IterateListStep, WorkflowCore.TestAssets + Inputs: + Collection: ""data.DataList"" + RunParallel: false +"; + + // Act & Assert + // Before the fix, this would throw: "Unknown property for input RunParallel on IterateList" + // After the fix, this should succeed + var exception = Record.Exception(() => + loader.LoadDefinition(yamlWithInheritedProperty, Deserializers.Yaml)); + + // Verify no exception was thrown + Assert.Null(exception); + } + + [Fact(DisplayName = "Should still throw exception for truly unknown properties")] + public void ShouldStillThrowForUnknownProperties() + { + // Arrange + var registry = A.Fake(); + var loader = new DefinitionLoader(registry, new TypeResolver()); + + var yamlWithUnknownProperty = @" +Id: TestInheritedPropertyWorkflow +Version: 1 +Description: Test workflow for inherited property binding +DataType: WorkflowCore.TestAssets.DataTypes.CounterBoard, WorkflowCore.TestAssets +Steps: + - Id: IterateList + StepType: WorkflowCore.TestAssets.Steps.IterateListStep, WorkflowCore.TestAssets + Inputs: + Collection: ""data.DataList"" + NonExistentProperty: false +"; + + // Act & Assert + // This should still throw an exception for truly unknown properties + var exception = Assert.Throws(() => + loader.LoadDefinition(yamlWithUnknownProperty, Deserializers.Yaml)); + + Assert.Contains("Unknown property for input NonExistentProperty", exception.Message); + } + } +} \ No newline at end of file