From 27ae1fae64e751d8bbe935d4c23c163d163ff6dc Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 17 May 2026 14:58:10 +0200 Subject: [PATCH] [codex] Resolve build warnings (#7458) * Resolve build warnings * Address Greptile review feedback * Remove dead metadata null guard * Preserve tenant-agnostic workflow activities --- .../ShellFeatures/DefaultFormattersFeature.cs | 6 ++-- .../ShellFeatures/MultitenancyFeature.cs | 6 ++-- .../ShellFeatures/StringCompressionFeature.cs | 6 ++-- .../Compiler/ElsaScriptCompiler.cs | 6 ++-- .../Parser/ElsaScriptParser.cs | 9 ++---- ...tivityOutputFunctionsDefinitionProvider.cs | 4 +-- .../ShellFeatures/ResilienceFeature.cs | 4 +-- .../ExpressionExecutionContextExtensions.cs | 4 +-- .../ShellFeatures/CommitStrategiesFeature.cs | 6 ++-- .../ShellFeatures/WorkflowsFeature.cs | 6 ++-- .../WorkflowDefinitionActivityProvider.cs | 30 ++++++++++++++++--- .../Multitenancy/MultitenancyTests.cs | 2 +- .../ReloadWorkflowTests.cs | 4 +-- .../ExceptionExtensionsTests.cs | 2 ++ .../CachingWorkflowDefinitionServiceTests.cs | 4 +-- 15 files changed, 66 insertions(+), 33 deletions(-) diff --git a/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs b/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs index be1213da8..adb8af027 100644 --- a/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs +++ b/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs @@ -6,7 +6,9 @@ using Microsoft.Extensions.DependencyInjection; namespace Elsa.Common.ShellFeatures; -[ShellFeature("DefaultFormatters")] +[ShellFeature( + "DefaultFormatters", + Description = "Registers default serializers and type converters")] public class DefaultFormattersFeature : IShellFeature { public void ConfigureServices(IServiceCollection services) @@ -14,4 +16,4 @@ public class DefaultFormattersFeature : IShellFeature TypeDescriptor.AddAttributes(typeof(Type), new TypeConverterAttribute(typeof(TypeTypeConverter))); services.AddSingleton(); } -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs b/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs index a3723d694..e014bbc59 100644 --- a/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs +++ b/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs @@ -9,7 +9,9 @@ using Microsoft.Extensions.DependencyInjection; namespace Elsa.Common.ShellFeatures; -[ShellFeature("Multitenancy")] +[ShellFeature( + "Multitenancy", + Description = "Provides tenant resolution, tenant scopes, and tenant lifecycle services")] public class MultitenancyFeature : IShellFeature { private readonly Func _tenantsProviderFactory = sp => sp.GetRequiredService(); @@ -42,4 +44,4 @@ public class MultitenancyFeature : IShellFeature .AddTransient() ; } -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs b/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs index 9a0b4ed59..8b23777c2 100644 --- a/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs +++ b/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs @@ -7,7 +7,9 @@ using Microsoft.Extensions.DependencyInjection; namespace Elsa.Common.ShellFeatures; [UsedImplicitly] -[ShellFeature("StringCompression")] +[ShellFeature( + "StringCompression", + Description = "Registers string compression codecs")] public class StringCompressionFeature : IShellFeature { public void ConfigureServices(IServiceCollection services) @@ -18,4 +20,4 @@ public class StringCompressionFeature : IShellFeature .AddSingleton() .AddSingleton(); } -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Dsl.ElsaScript/Compiler/ElsaScriptCompiler.cs b/src/modules/Elsa.Dsl.ElsaScript/Compiler/ElsaScriptCompiler.cs index 65a53fb89..84c3fff30 100644 --- a/src/modules/Elsa.Dsl.ElsaScript/Compiler/ElsaScriptCompiler.cs +++ b/src/modules/Elsa.Dsl.ElsaScript/Compiler/ElsaScriptCompiler.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Elsa.Dsl.ElsaScript.Ast; using Elsa.Dsl.ElsaScript.Contracts; using Elsa.Dsl.ElsaScript.Helpers; @@ -114,7 +115,7 @@ public class ElsaScriptCompiler(IActivityRegistryLookupService activityRegistryL metadata.TryGetValue(key, out var value) ? ConvertValue(value, default) : default; private T GetMetadataValueOrDefault(Dictionary metadata, string key, T defaultValue) => - metadata.TryGetValue(key, out var value) ? ConvertValue(value, defaultValue) : defaultValue; + metadata.TryGetValue(key, out var value) ? ConvertValue(value, defaultValue) ?? defaultValue : defaultValue; private static T? ConvertValue(object value, T? defaultValue) { @@ -477,6 +478,7 @@ public class ElsaScriptCompiler(IActivityRegistryLookupService activityRegistryL throw new NotSupportedException($"Expression type {exprNode.GetType().Name} is not supported as Expression"); } + [UnconditionalSuppressMessage("Trimming", "IL2060:Call to MakeGenericMethod can not be statically analyzed", Justification = "ElsaScript activity input types are discovered from runtime activity descriptors and must be bound dynamically.")] private object CompileExpression(ExpressionNode exprNode, Type targetType) { // Check if targetType is already Input @@ -598,4 +600,4 @@ public class ElsaScriptCompiler(IActivityRegistryLookupService activityRegistryL private static string MapLanguageName(string dslLanguage) => LanguageMappings.TryGetValue(dslLanguage, out var mapped) ? mapped : dslLanguage; -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Dsl.ElsaScript/Parser/ElsaScriptParser.cs b/src/modules/Elsa.Dsl.ElsaScript/Parser/ElsaScriptParser.cs index 76bf394af..2f8eb3b03 100644 --- a/src/modules/Elsa.Dsl.ElsaScript/Parser/ElsaScriptParser.cs +++ b/src/modules/Elsa.Dsl.ElsaScript/Parser/ElsaScriptParser.cs @@ -454,7 +454,7 @@ public class ElsaScriptParser : IElsaScriptParser var workflowWithoutMetadata = workflowKeyword .And(identifier) - .Then(x => (WorkflowId: x.Item2.ToString(), Metadata: (IReadOnlyList<(string Name, object Value)>?)null)); + .Then(x => (WorkflowId: x.Item2.ToString(), Metadata: (IReadOnlyList<(string Name, object Value)>)[])); var workflowHeader = workflowWithMetadata.Or(workflowWithoutMetadata); @@ -466,12 +466,9 @@ public class ElsaScriptParser : IElsaScriptParser var bodyElements = x.Item2; var metadataDict = new Dictionary(); - if (header.Metadata != null) + foreach (var entry in header.Metadata) { - foreach (var entry in header.Metadata) - { - metadataDict[entry.Name] = entry.Value; - } + metadataDict[entry.Name] = entry.Value; } // Separate use statements from regular statements in body diff --git a/src/modules/Elsa.Expressions.JavaScript/Providers/ActivityOutputFunctionsDefinitionProvider.cs b/src/modules/Elsa.Expressions.JavaScript/Providers/ActivityOutputFunctionsDefinitionProvider.cs index cd3f66fca..1e50e7c7f 100644 --- a/src/modules/Elsa.Expressions.JavaScript/Providers/ActivityOutputFunctionsDefinitionProvider.cs +++ b/src/modules/Elsa.Expressions.JavaScript/Providers/ActivityOutputFunctionsDefinitionProvider.cs @@ -30,10 +30,10 @@ internal class ActivityOutputFunctionsDefinitionProvider(IActivityRegistryLookup definitions.AddRange(from output in activityDescriptor.Outputs.Where(x => x.Name.IsValidVariableName()) select output.Name.Pascalize() into outputPascalName - let activityNamePascalName = activity.Name.Pascalize() + let activityNamePascalName = activity.Name!.Pascalize() select CreateFunctionDefinition(builder => builder.Name($"get{outputPascalName}From{activityNamePascalName}").ReturnType("any"))); } return definitions; } -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Resilience/ShellFeatures/ResilienceFeature.cs b/src/modules/Elsa.Resilience/ShellFeatures/ResilienceFeature.cs index e77e4668b..61fa1041d 100644 --- a/src/modules/Elsa.Resilience/ShellFeatures/ResilienceFeature.cs +++ b/src/modules/Elsa.Resilience/ShellFeatures/ResilienceFeature.cs @@ -13,7 +13,7 @@ using Microsoft.Extensions.DependencyInjection; namespace Elsa.Resilience.ShellFeatures; -[ShellFeature] +[ShellFeature(Description = "Provides workflow resilience strategies and retry attempt tracking")] public class ResilienceFeature : IFastEndpointsShellFeature { public void ConfigureServices(IServiceCollection services) @@ -44,4 +44,4 @@ public class ResilienceFeature : IFastEndpointsShellFeature .AddSingleton() .AddSingleton(); } -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs index 700f2d40b..529b2504a 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs @@ -472,7 +472,7 @@ public static class ExpressionExecutionContextExtensions { var activity = activityWithOutput.Activity; var activityDescriptor = activityWithOutput.ActivityDescriptor; - var activityIdentifier = useActivityName ? activity.Name : activity.Id; + var activityIdentifier = useActivityName ? activity.Name! : activity.Id; var activityIdPascalName = activityIdentifier.Pascalize(); foreach (var output in activityDescriptor.Outputs) @@ -568,4 +568,4 @@ public static class ExpressionExecutionContextExtensions enumerable ])!; } -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs index 5c8803789..643a20c13 100644 --- a/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs @@ -6,7 +6,9 @@ using Microsoft.Extensions.DependencyInjection; namespace Elsa.Workflows.ShellFeatures; -[ShellFeature("CommitStrategies")] +[ShellFeature( + "CommitStrategies", + Description = "Registers workflow commit strategies")] public class CommitStrategiesFeature : IShellFeature { public void ConfigureServices(IServiceCollection services) @@ -14,4 +16,4 @@ public class CommitStrategiesFeature : IShellFeature services.AddSingleton(); services.AddStartupTask(); } -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs index 7181efa16..b91894baa 100644 --- a/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs @@ -30,7 +30,9 @@ using Microsoft.Extensions.DependencyInjection; namespace Elsa.Workflows.ShellFeatures; -[ShellFeature(DependsOn = +[ShellFeature( + Description = "Provides core workflow execution, activity execution, and workflow serialization services", + DependsOn = [ "SystemClock", "Expressions", @@ -177,4 +179,4 @@ public class WorkflowsFeature : IShellFeature // Overridable services services.AddScoped(); } -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Workflows.Management/Activities/WorkflowDefinitionActivity/WorkflowDefinitionActivityProvider.cs b/src/modules/Elsa.Workflows.Management/Activities/WorkflowDefinitionActivity/WorkflowDefinitionActivityProvider.cs index 460202469..105d5921c 100644 --- a/src/modules/Elsa.Workflows.Management/Activities/WorkflowDefinitionActivity/WorkflowDefinitionActivityProvider.cs +++ b/src/modules/Elsa.Workflows.Management/Activities/WorkflowDefinitionActivity/WorkflowDefinitionActivityProvider.cs @@ -9,8 +9,23 @@ namespace Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity; /// /// Provides activity descriptors based on s stored in the database. /// -public class WorkflowDefinitionActivityProvider(IWorkflowDefinitionStore store, WorkflowDefinitionActivityDescriptorFactory workflowDefinitionActivityDescriptorFactory, ITenantAccessor tenantAccessor) : IActivityProvider +public class WorkflowDefinitionActivityProvider : IActivityProvider { + private readonly IWorkflowDefinitionStore _store; + private readonly WorkflowDefinitionActivityDescriptorFactory _workflowDefinitionActivityDescriptorFactory; + private readonly ITenantAccessor? _tenantAccessor; + + public WorkflowDefinitionActivityProvider(IWorkflowDefinitionStore store, WorkflowDefinitionActivityDescriptorFactory workflowDefinitionActivityDescriptorFactory) + { + _store = store; + _workflowDefinitionActivityDescriptorFactory = workflowDefinitionActivityDescriptorFactory; + } + + public WorkflowDefinitionActivityProvider(IWorkflowDefinitionStore store, WorkflowDefinitionActivityDescriptorFactory workflowDefinitionActivityDescriptorFactory, ITenantAccessor tenantAccessor) : this(store, workflowDefinitionActivityDescriptorFactory) + { + _tenantAccessor = tenantAccessor; + } + /// public async ValueTask> GetDescriptorsAsync(CancellationToken cancellationToken = default) { @@ -20,7 +35,14 @@ public class WorkflowDefinitionActivityProvider(IWorkflowDefinitionStore store, VersionOptions = VersionOptions.All }; - var definitions = (await store.FindManyAsync(filter, cancellationToken)).ToList(); + var definitions = (await _store.FindManyAsync(filter, cancellationToken)).ToList(); + + if (_tenantAccessor != null) + { + var currentTenantId = _tenantAccessor.TenantId; + definitions = definitions.Where(x => x.TenantId == Tenant.AgnosticTenantId || x.TenantId.NormalizeTenantId() == currentTenantId).ToList(); + } + return CreateDescriptors(definitions).ToList(); } @@ -34,6 +56,6 @@ public class WorkflowDefinitionActivityProvider(IWorkflowDefinitionStore store, var latestPublishedVersion = allDefinitions .Where(x => x.DefinitionId == definition.DefinitionId && x.IsPublished) .MaxBy(x => x.Version); - return workflowDefinitionActivityDescriptorFactory.CreateDescriptor(definition, latestPublishedVersion); + return _workflowDefinitionActivityDescriptorFactory.CreateDescriptor(definition, latestPublishedVersion); } -} \ No newline at end of file +} diff --git a/test/component/Elsa.Workflows.ComponentTests/Scenarios/Multitenancy/MultitenancyTests.cs b/test/component/Elsa.Workflows.ComponentTests/Scenarios/Multitenancy/MultitenancyTests.cs index 838ed8bba..b885e1f41 100644 --- a/test/component/Elsa.Workflows.ComponentTests/Scenarios/Multitenancy/MultitenancyTests.cs +++ b/test/component/Elsa.Workflows.ComponentTests/Scenarios/Multitenancy/MultitenancyTests.cs @@ -17,7 +17,7 @@ public class MultitenancyTests(App app) : AppComponentTest(app) public void DefaultTenant_ShouldUseEmptyStringAsId() { // Assert - Assert.Equal(string.Empty, Tenant.DefaultTenantId); + Assert.Empty(Tenant.DefaultTenantId); Assert.Equal(Tenant.DefaultTenantId, Tenant.Default.Id); } diff --git a/test/component/Elsa.Workflows.ComponentTests/Scenarios/WorkflowDefinitionReload/ReloadWorkflowTests.cs b/test/component/Elsa.Workflows.ComponentTests/Scenarios/WorkflowDefinitionReload/ReloadWorkflowTests.cs index d8399a075..e4ed6f4bd 100644 --- a/test/component/Elsa.Workflows.ComponentTests/Scenarios/WorkflowDefinitionReload/ReloadWorkflowTests.cs +++ b/test/component/Elsa.Workflows.ComponentTests/Scenarios/WorkflowDefinitionReload/ReloadWorkflowTests.cs @@ -88,7 +88,7 @@ public class ReloadWorkflowTests : AppComponentTest // Set up the initial workflow version. _testWorkflowProvider.MaterializedWorkflows = [workflowV1]; await _workflowDefinitionsReloader.ReloadWorkflowDefinitionsAsync(); - var activityTypeName = workflowV1.Workflow.Name.Pascalize(); + var activityTypeName = workflowV1.Workflow.Name!.Pascalize(); var activityV1 = _activityRegistry.Find(activityTypeName); Assert.Equal(1, activityV1!.Version); @@ -151,4 +151,4 @@ public class ReloadWorkflowTests : AppComponentTest workflow.Name = definitionId; return new(workflow, _testWorkflowProvider.Name, TestWorkflowMaterializer.MaterializerName); } -} \ No newline at end of file +} diff --git a/test/unit/Elsa.Common.UnitTests/ExceptionExtensionsTests.cs b/test/unit/Elsa.Common.UnitTests/ExceptionExtensionsTests.cs index 485cc519f..0947566c6 100644 --- a/test/unit/Elsa.Common.UnitTests/ExceptionExtensionsTests.cs +++ b/test/unit/Elsa.Common.UnitTests/ExceptionExtensionsTests.cs @@ -17,7 +17,9 @@ public class ExceptionExtensionsTests [InlineData(typeof(ThreadAbortException))] public void FatalExceptions(Type exceptionType) { +#pragma warning disable SYSLIB0050 // FormatterServices is needed to instantiate fatal exception types without throwing them. var ex = (Exception)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(exceptionType); +#pragma warning restore SYSLIB0050 Assert.True(ex.IsFatal()); } diff --git a/test/unit/Elsa.Workflows.Management.UnitTests/Services/CachingWorkflowDefinitionServiceTests.cs b/test/unit/Elsa.Workflows.Management.UnitTests/Services/CachingWorkflowDefinitionServiceTests.cs index 49e7363a1..16c88fbef 100644 --- a/test/unit/Elsa.Workflows.Management.UnitTests/Services/CachingWorkflowDefinitionServiceTests.cs +++ b/test/unit/Elsa.Workflows.Management.UnitTests/Services/CachingWorkflowDefinitionServiceTests.cs @@ -514,10 +514,10 @@ public class CachingWorkflowDefinitionServiceTests .Returns(async callInfo => await callInfo.Arg>>()(Substitute.For())); _cache.FindOrCreateAsync(Arg.Any(), Arg.Any>>()) - .Returns(async callInfo => await callInfo.Arg>>()(Substitute.For())); + .Returns(async callInfo => (WorkflowDefinition?)await callInfo.Arg>>()(Substitute.For())); _cache.FindOrCreateAsync(Arg.Any(), Arg.Any>>()) - .Returns(async callInfo => await callInfo.Arg>>()(Substitute.For())); + .Returns(async callInfo => (WorkflowGraph?)await callInfo.Arg>>()(Substitute.For())); } ///