mvSapphire / PowerPipe

A library for .NET that uses a fluent interface to construct advanced workflows with ease.
MIT License
174 stars 9 forks source link

Visualizations Not Working #59

Open chrismccownQ opened 1 month ago

chrismccownQ commented 1 month ago

I have created a pipeline and followed the documentation for visualization, yet my pipeline isn't being rendered.

My pipeline class looks like this:

public class DataLookupService(IPipelineStepFactory stepFactory) : IDataLookupService
{
    public IPipeline<PropertyFindings> BuildPipeline(FeatureProcessorRequest message, List<Feature> features)
    {
        var input = new DataLoaderContext(message, features);

        return new PipelineBuilder<DataLoaderContext, PropertyFindings>(stepFactory, input)
            .Add<Step1>()
            .AddIf<Step2>(ctx => condition)
            .Add<Step3>()
            .If(x => condition, inner => inner
                .Add<Step2>()
                .Add<Step3>()
            )
            .Add<Step4>()
            .Add<Step5>()
            .Build();
    }
}

I've registered everything IOC:

services.AddPowerPipe(cfg => cfg.RegisterServicesFromAssembly(typeof(IFeatureServicesMarker).Assembly));
builder.Services.AddPowerPipeVisualization(o => o.ScanFromType(typeof(DataLookupService)));
// ...
app.UsePowerPipeVisualization();

However, when I go to /powerpipe, all I see END OF WORKFLOW

image

chrismccownQ commented 1 month ago

I figured out one of the issue is that the Visualizer doesn't work with the new dotnet 8 primary constructor syntax. Once I fixed that, I discovered that AddIf and the If step both results in errors.

If I comment out everything but the add steps, I get a diagram. With the AddIf and If steps, I get the following error:

Exception occured during retrieving of diagrams. System.NullReferenceException: Object reference not set to an instance of an object.
   at PowerPipe.Visualization.Antlr.Extensions.ParserExtensions.GetPredicateName(ITerminalNode node)
   at PowerPipe.Visualization.Antlr.PipelineParserVisitor.VisitAddIfStep(AddIfStepContext context)
   at PowerPipe.Visualization.Antlr.PipelineParser.AddIfStepContext.Accept[TResult](IParseTreeVisitor`1 visitor)
   at Antlr4.Runtime.Tree.AbstractParseTreeVisitor`1.VisitChildren(IRuleNode node)
   at PowerPipe.Visualization.Antlr.PipelineParserBaseVisitor`1.VisitStep(StepContext context)
   at PowerPipe.Visualization.Antlr.PipelineParser.StepContext.Accept[TResult](IParseTreeVisitor`1 visitor)
   at Antlr4.Runtime.Tree.AbstractParseTreeVisitor`1.Visit(IParseTree tree)
   at PowerPipe.Visualization.Antlr.PipelineParserVisitor.VisitStart(StartContext context)
   at PowerPipe.Visualization.Antlr.PipelineParser.StartContext.Accept[TResult](IParseTreeVisitor`1 visitor)
   at Antlr4.Runtime.Tree.AbstractParseTreeVisitor`1.Visit(IParseTree tree)
   at PowerPipe.Visualization.PipelineDiagramsService.ProcessDecompiledType(String decompiledType)+MoveNext()
   at PowerPipe.Visualization.PipelineDiagramsService.GetDiagrams()
mvSapphire commented 1 month ago

Thank you for reporting an issue. The current implementation is raw, but I will improve it.

I have a working workflow visualization that is quite the same as your example but I have a few differences: 1 - I don't use primary constructors (This requires more investigation; currently I don't know why this change affects rendering). 2 - I have predicates as methods (probably a bug with lambda parsing).

This is how I changed your example to work:

public class DataLookupService : IDataLookupService
{
    private readonly IPipelineStepFactory stepFactory;

    public DataLookupService(IPipelineStepFactory stepFactory)
    {
        this.stepFactory = stepFactory;
    }

    public bool Condition1(DataLoaderContext ctx) => true;
    public bool Condition2(DataLoaderContext ctx) => true;

    public IPipeline<PropertyFindings> BuildPipeline(FeatureProcessorRequest message, List<Feature> features)
    {
        var input = new DataLoaderContext(message, features);

        return new PipelineBuilder<DataLoaderContext, PropertyFindings>(stepFactory, input)
            .Add<Step1>()
            .AddIf<Step2>(Condition1)
            .Add<Step3>()
            .If(Condition2, inner => inner
                .Add<Step2>()
                .Add<Step3>()
            )
            .Add<Step4>()
            .Add<Step5>()
            .Build();
    }
}

Result -

image
chrismccownQ commented 1 month ago

If it helps, this is what the decompiled code looks like with Primary Constructors:

public class DataLookupService : IDataLookupService
{
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private IPipelineStepFactory <stepFactory>P;

    public DataLookupService(IPipelineStepFactory stepFactory)
    {
        <stepFactory>P = stepFactory;
        base..ctor();
    }

    public IPipeline<PropertyFindings> BuildPipeline(FeatureProcessorRequest message, List<LenderFeature> features)
    {
        DataLoaderContext context = new DataLoaderContext(message, features);
        return new PipelineBuilder<DataLoaderContext, PropertyFindings>(<stepFactory>P, context)
                        // ... add steps
            .Build();
    }
}

The Antlr parser doesn't seem to know what to do with <stepFactory>P