Add Fault Code, Category and Type (#5362)
* Add error handling with fault categories and codes The code now includes error handling through the introduction of fault categories and codes. New files containing constants for fault categories and codes have been added for different modules. FaultException has also been updated to include these properties. Changes are evident in various files where FaultException is thrown for error handling. * Add DotSettings file for Elsa.Alterations module A new DotSettings file is added for the Elsa.Alterations module. This includes configuration for namespace folders to be skipped during CodeInspection. * Renamed "DefaultFaultKinds" to "DefaultFaultTypes" and updated usages This commit renames the class "DefaultFaultKinds" to "DefaultFaultTypes" and updates all its references across the project files. The change is made keeping the more accurate naming context i.e., 'Types' suits better in the thrown exception scenarios.
This commit is contained in:
parent
016fa22145
commit
a2908960a4
|
|
@ -41,7 +41,7 @@ public class CompleteAlterationPlan : CodeActivity
|
|||
var plan = await manager.GetPlanAsync(planId, cancellationToken);
|
||||
|
||||
if (plan == null)
|
||||
throw new FaultException($"Alteration Plan with ID {planId} not found.");
|
||||
throw new FaultException(AlterationFaultCodes.PlanNotFound, AlterationFaultCategories.Alteration, DefaultFaultTypes.System, $"Alteration Plan with ID {planId} not found.");
|
||||
|
||||
await manager.CompletePlanAsync(plan, cancellationToken);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,12 +23,12 @@ public class DispatchAlterationJobs : CodeActivity
|
|||
{
|
||||
PlanId = new Input<string>(planId);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public DispatchAlterationJobs([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the alteration plan.
|
||||
/// </summary>
|
||||
|
|
@ -47,7 +47,7 @@ public class DispatchAlterationJobs : CodeActivity
|
|||
var plan = await alterationPlanStore.FindAsync(planFilter, cancellationToken);
|
||||
|
||||
if (plan == null)
|
||||
throw new FaultException($"Alteration Plan with ID {planId} not found.");
|
||||
throw new FaultException(AlterationFaultCodes.PlanNotFound, AlterationFaultCategories.Alteration, DefaultFaultTypes.System, $"Alteration Plan with ID {planId} not found.");
|
||||
|
||||
// Update status.
|
||||
plan.Status = AlterationPlanStatus.Dispatching;
|
||||
|
|
@ -65,7 +65,7 @@ public class DispatchAlterationJobs : CodeActivity
|
|||
var alterationJobDispatcher = context.GetRequiredService<IAlterationJobDispatcher>();
|
||||
foreach (var jobId in alterationJobIds)
|
||||
await alterationJobDispatcher.DispatchAsync(jobId, cancellationToken);
|
||||
|
||||
|
||||
// Update status.
|
||||
plan.Status = AlterationPlanStatus.Running;
|
||||
await alterationPlanStore.SaveAsync(plan, cancellationToken);
|
||||
|
|
|
|||
|
|
@ -11,12 +11,8 @@ using Elsa.Workflows;
|
|||
using Elsa.Workflows.Attributes;
|
||||
using Elsa.Workflows.Contracts;
|
||||
using Elsa.Workflows.Exceptions;
|
||||
using Elsa.Workflows.Management.Contracts;
|
||||
using Elsa.Workflows.Management.Filters;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Elsa.Workflows.Models;
|
||||
using Elsa.Workflows.Runtime.Contracts;
|
||||
using Elsa.Workflows.Runtime.Filters;
|
||||
|
||||
namespace Elsa.Alterations.Activities;
|
||||
|
||||
|
|
@ -68,7 +64,7 @@ public class GenerateAlterationJobs : CodeActivity<int>
|
|||
var plan = await alterationPlanStore.FindAsync(planFilter, cancellationToken);
|
||||
|
||||
if (plan == null)
|
||||
throw new FaultException($"Alteration Plan with ID {planId} not found.");
|
||||
throw new FaultException(AlterationFaultCodes.PlanNotFound, AlterationFaultCategories.Alteration, DefaultFaultTypes.System, $"Alteration Plan with ID {planId} not found.");
|
||||
|
||||
return plan;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
namespace Elsa.Alterations;
|
||||
|
||||
public static class AlterationFaultCategories
|
||||
{
|
||||
public const string Alteration = "Alteration";
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
namespace Elsa.Alterations;
|
||||
|
||||
public static class AlterationFaultCodes
|
||||
{
|
||||
public const string PlanNotFound = "PlanNotFound";
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=constants/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
|
|
@ -303,7 +303,7 @@ public class WriteFileHttpResponse : Activity
|
|||
var httpContext = httpContextAccessor.HttpContext;
|
||||
|
||||
if (httpContext == null)
|
||||
throw new FaultException("Cannot execute in a non-HTTP context");
|
||||
throw new FaultException(HttpFaultCodes.NoHttpContext, HttpFaultCategories.Http, DefaultFaultTypes.System, "Cannot execute in a non-HTTP context");
|
||||
|
||||
await WriteResponseAsync(context, httpContext);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ public class WriteHttpResponse : Activity
|
|||
if (httpContext == null)
|
||||
{
|
||||
// We're not in an HTTP context, so let's fail.
|
||||
throw new FaultException("Cannot execute in a non-HTTP context");
|
||||
throw new FaultException(HttpFaultCodes.NoHttpContext, HttpFaultCategories.Http, DefaultFaultTypes.System, "Cannot execute in a non-HTTP context");
|
||||
}
|
||||
|
||||
await WriteResponseAsync(context, httpContext.Response);
|
||||
|
|
|
|||
6
src/modules/Elsa.Http/Constants/HttpFaultCategories.cs
Normal file
6
src/modules/Elsa.Http/Constants/HttpFaultCategories.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
namespace Elsa.Http;
|
||||
|
||||
public static class HttpFaultCategories
|
||||
{
|
||||
public const string Http = "HTTP";
|
||||
}
|
||||
6
src/modules/Elsa.Http/Constants/HttpFaultCodes.cs
Normal file
6
src/modules/Elsa.Http/Constants/HttpFaultCodes.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
namespace Elsa.Http;
|
||||
|
||||
public static class HttpFaultCodes
|
||||
{
|
||||
public const string NoHttpContext = "NoHttpContext";
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Chttpendpoint/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Chttptrigger/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Chttptrigger/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=constants/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
|
|
@ -67,7 +67,8 @@ internal class Endpoint(IWorkflowDefinitionStore store, IWorkflowDispatcher work
|
|||
|
||||
if(!result.Succeeded)
|
||||
{
|
||||
AddError(result.ErrorMessage!);
|
||||
var fault = result.Fault!;
|
||||
AddError(fault.Message, fault.Code);
|
||||
await SendErrorsAsync(cancellation: cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Elsa.Workflows.Attributes;
|
|||
using Elsa.Workflows.Exceptions;
|
||||
using Elsa.Workflows.Models;
|
||||
using JetBrains.Annotations;
|
||||
// ReSharper disable ExplicitCallerInfoArgument
|
||||
|
||||
namespace Elsa.Workflows.Activities;
|
||||
|
||||
|
|
@ -15,20 +16,58 @@ namespace Elsa.Workflows.Activities;
|
|||
public class Fault : Activity
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Fault([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line)
|
||||
public Fault([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fault activity.
|
||||
/// </summary>
|
||||
public static Fault Create(string code, string category, string type, string? message = null, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null)
|
||||
{
|
||||
return new Fault(source, line)
|
||||
{
|
||||
Code = new(code),
|
||||
Message = new(message),
|
||||
Category = new(category),
|
||||
FaultType = new(type)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Code to categorize the fault.
|
||||
/// </summary>
|
||||
[Input(Description = "Code to categorize the fault.")]
|
||||
public Input<string> Code { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Category to categorize the fault. Examples: HTTP, Alteration, Azure, etc.
|
||||
/// </summary>
|
||||
[Input(Description = "Category to categorize the fault. Examples: HTTP, Alteration, Azure, etc.")]
|
||||
public Input<string> Category { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The type of fault. Examples: System, Business, Integration, etc.
|
||||
/// </summary>
|
||||
[Input(
|
||||
DisplayName = "Type",
|
||||
Description = "The type of fault. Examples: System, Business, Integration, etc."
|
||||
)]
|
||||
public Input<string> FaultType { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The message to include with the fault.
|
||||
/// </summary>
|
||||
[Input(Description = "The message to include with the fault.")]
|
||||
public Input<string> Message { get; set; } = default!;
|
||||
public Input<string?> Message { get; set; } = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Execute(ActivityExecutionContext context)
|
||||
{
|
||||
var code = Code.GetOrDefault(context) ?? "0";
|
||||
var category = Category.GetOrDefault(context) ?? "General";
|
||||
var type = FaultType.GetOrDefault(context) ?? "System";
|
||||
var message = Message.GetOrDefault(context);
|
||||
throw new FaultException(message);
|
||||
throw new FaultException(code, category, type, message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
namespace Elsa.Workflows;
|
||||
|
||||
public static class DefaultFaultTypes
|
||||
{
|
||||
public const string System = "System";
|
||||
public const string Business = "Business";
|
||||
public const string Integration = "Integration";
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Cprimitives_005Csetname/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Csignaling_005Cactivities_005Csignalreceived/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Cworkflows_005Cfreeflowchart/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=constants/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=contexts/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=enums/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
|
|
@ -6,7 +6,26 @@ namespace Elsa.Workflows.Exceptions;
|
|||
public class FaultException : Exception
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public FaultException(string? message) : base(message)
|
||||
public FaultException(string code, string category, string type, string? message) : base(message)
|
||||
{
|
||||
Code = code;
|
||||
Category = category;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Code that identifies the fault type.
|
||||
/// </summary>
|
||||
public string Code { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Category to categorize the fault. E.g. "HTTP", "Alteration", "Azure", etc.
|
||||
/// This is used to distinguish error codes between modules.
|
||||
/// </summary>
|
||||
public string Category { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Kind of fault. E.g. "System", "Business", "Integration", etc.
|
||||
/// </summary>
|
||||
public string Type { get; }
|
||||
}
|
||||
|
|
@ -221,9 +221,7 @@ public class BulkDispatchWorkflows : Activity
|
|||
};
|
||||
|
||||
var dispatchResponse = await workflowDispatcher.DispatchAsync(request, options, context.CancellationToken);
|
||||
|
||||
if (!dispatchResponse.Succeeded)
|
||||
throw new FaultException(dispatchResponse.ErrorMessage);
|
||||
dispatchResponse.ThrowIfFailed();
|
||||
|
||||
return instanceId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,9 +123,7 @@ public class DispatchWorkflow : Activity<object>
|
|||
|
||||
// Dispatch the child workflow.
|
||||
var dispatchResponse = await workflowDispatcher.DispatchAsync(request, options, context.CancellationToken);
|
||||
|
||||
if (!dispatchResponse.Succeeded)
|
||||
throw new FaultException(dispatchResponse.ErrorMessage);
|
||||
dispatchResponse.ThrowIfFailed();
|
||||
|
||||
return instanceId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
namespace Elsa.Workflows.Runtime;
|
||||
|
||||
public static class RuntimeFaultCategories
|
||||
{
|
||||
public const string Dispatch = "Dispatch";
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
namespace Elsa.Workflows.Runtime;
|
||||
|
||||
public static class RuntimeFaultCodes
|
||||
{
|
||||
public const string UnknownChannel = "UnknownChannel";
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=constants/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
|
|
@ -1,18 +1,37 @@
|
|||
using Elsa.Workflows.Exceptions;
|
||||
|
||||
namespace Elsa.Workflows.Runtime.Responses;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the response of a dispatch action for a workflow definition.
|
||||
/// </summary>
|
||||
public record DispatchWorkflowResponse(bool Succeeded, string? ErrorMessage)
|
||||
public record DispatchWorkflowResponse(FaultException? Fault)
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a response indicating that the dispatch action was successful.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static DispatchWorkflowResponse Success() => new DispatchWorkflowResponse(true, default);
|
||||
|
||||
public static DispatchWorkflowResponse Success() => new(default(FaultException?));
|
||||
|
||||
/// <summary>
|
||||
/// Creates a response indicating that the specified channel does not exist.
|
||||
/// </summary>
|
||||
public static DispatchWorkflowResponse UnknownChannel() => new DispatchWorkflowResponse(false, "The specified channel does not exist.");
|
||||
public static DispatchWorkflowResponse UnknownChannel() => new(new FaultException(RuntimeFaultCodes.UnknownChannel, RuntimeFaultCategories.Dispatch, DefaultFaultTypes.System, "The specified channel does not exist."));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the dispatch of a workflow definition succeeded.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the dispatch succeeded; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool Succeeded => Fault == null;
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if the dispatch failed.
|
||||
/// </summary>
|
||||
public void ThrowIfFailed()
|
||||
{
|
||||
if (Fault != null)
|
||||
throw Fault;
|
||||
}
|
||||
}
|
||||
|
|
@ -9,8 +9,6 @@ namespace Elsa.Workflows.IntegrationTests.Scenarios.Incidents.Workflows;
|
|||
|
||||
public class FaultyWorkflow : WorkflowBase
|
||||
{
|
||||
|
||||
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
var start = new WriteLine("Start");
|
||||
|
|
@ -20,7 +18,7 @@ public class FaultyWorkflow : WorkflowBase
|
|||
var step2A = new WriteLine("Step 2a");
|
||||
var event2 = new Event("Event 2");
|
||||
var step2B = new WriteLine("Step 2b");
|
||||
var fault = new Fault("Whoops!");
|
||||
var fault = Fault.Create("Whoops!", "Test", "Test");
|
||||
|
||||
builder.WorkflowOptions.IncidentStrategyType = TestSettings.IncidentStrategyType;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue