diff --git a/src/core/Elsa.Abstractions/Attributes/ContextTypeNameAttribute.cs b/src/core/Elsa.Abstractions/Attributes/ContextTypeNameAttribute.cs new file mode 100644 index 000000000..016ecefe1 --- /dev/null +++ b/src/core/Elsa.Abstractions/Attributes/ContextTypeNameAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Elsa.Attributes +{ + [AttributeUsage(AttributeTargets.Class)] + public class ContextTypeNameAttribute : Attribute + { + public string Name { get; } + + public ContextTypeNameAttribute(string name) + { + Name = name; + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Extensions/TypeExtensions.cs b/src/core/Elsa.Abstractions/Extensions/TypeExtensions.cs new file mode 100644 index 000000000..10d049819 --- /dev/null +++ b/src/core/Elsa.Abstractions/Extensions/TypeExtensions.cs @@ -0,0 +1,16 @@ +using Elsa.Attributes; +using System; +using System.Reflection; + + +namespace Elsa.Extensions +{ + public static class TypeExtensions + { + public static string GetContextTypeName(this Type type, bool inherit = false) + { + var attribute = type.GetCustomAttribute(inherit); + return attribute?.Name ?? type.FullName!; + } + } +} diff --git a/src/core/Elsa.Abstractions/Models/WorkflowDefinition.cs b/src/core/Elsa.Abstractions/Models/WorkflowDefinition.cs index 2e5c29318..43f447a0d 100644 --- a/src/core/Elsa.Abstractions/Models/WorkflowDefinition.cs +++ b/src/core/Elsa.Abstractions/Models/WorkflowDefinition.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; +using System.Collections.Generic; namespace Elsa.Models { diff --git a/src/core/Elsa.Abstractions/Models/WorkflowInstance.cs b/src/core/Elsa.Abstractions/Models/WorkflowInstance.cs index 5d2733d6b..18b013581 100644 --- a/src/core/Elsa.Abstractions/Models/WorkflowInstance.cs +++ b/src/core/Elsa.Abstractions/Models/WorkflowInstance.cs @@ -1,5 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text.Json.Serialization; using Elsa.Comparers; +using Elsa.Converters; using NodaTime; namespace Elsa.Models @@ -22,6 +26,7 @@ namespace Elsa.Models public int Version { get; set; } public WorkflowStatus WorkflowStatus { get; set; } public string? CorrelationId { get; set; } + public string? ContextType { get; set; } public string? ContextId { get; set; } public string? Name { get; set; } public Instant CreatedAt { get; set; } diff --git a/src/core/Elsa.Core/Persistence/Specifications/SpecificationExtensions.cs b/src/core/Elsa.Core/Persistence/Specifications/SpecificationExtensions.cs index 8c0135908..f57e44235 100644 --- a/src/core/Elsa.Core/Persistence/Specifications/SpecificationExtensions.cs +++ b/src/core/Elsa.Core/Persistence/Specifications/SpecificationExtensions.cs @@ -1,13 +1,9 @@ -using Elsa.Models; +using Elsa.Models; namespace Elsa.Persistence.Specifications { public static class SpecificationExtensions { public static ISpecification WithTenant(this ISpecification specification, string? tenantId) where T : ITenantScope => specification.And(new TenantSpecification(tenantId)); - public static ISpecification WithWorkflowDefinition(this ISpecification specification, string workflowDefinitionId) => specification.And(new WorkflowInstanceDefinitionIdSpecification(workflowDefinitionId)); - public static ISpecification WithWorkflowName(this ISpecification specification, string name) => specification.And(new WorkflowInstanceNameMatchSpecification(name)); - public static ISpecification WithStatus(this ISpecification specification, WorkflowStatus status) => specification.And(new WorkflowStatusSpecification(status)); - public static ISpecification WithVersionOptions(this ISpecification specification, VersionOptions versionOptions) => specification.And(new VersionOptionsSpecification(versionOptions)); } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Persistence/Specifications/WorkflowDefinitionSpecificationExtensions.cs b/src/core/Elsa.Core/Persistence/Specifications/WorkflowDefinitionSpecificationExtensions.cs new file mode 100644 index 000000000..8944f92d4 --- /dev/null +++ b/src/core/Elsa.Core/Persistence/Specifications/WorkflowDefinitionSpecificationExtensions.cs @@ -0,0 +1,9 @@ +using Elsa.Models; + +namespace Elsa.Persistence.Specifications +{ + public static class WorkflowDefinitionSpecificationExtensions + { + public static ISpecification WithVersionOptions(this ISpecification specification, VersionOptions versionOptions) => specification.And(new VersionOptionsSpecification(versionOptions)); + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Persistence/Specifications/WorkflowInstancSpecificationExtensions.cs b/src/core/Elsa.Core/Persistence/Specifications/WorkflowInstancSpecificationExtensions.cs new file mode 100644 index 000000000..be3a7498b --- /dev/null +++ b/src/core/Elsa.Core/Persistence/Specifications/WorkflowInstancSpecificationExtensions.cs @@ -0,0 +1,19 @@ +using Elsa.Models; +using System; + +namespace Elsa.Persistence.Specifications +{ + public static class WorkflowInstancSpecificationExtensions + { + public static ISpecification WithWorkflowDefinition(this ISpecification specification, string workflowDefinitionId) => specification.And(new WorkflowInstanceDefinitionIdSpecification(workflowDefinitionId)); + public static ISpecification WithWorkflowName(this ISpecification specification, string name) => specification.And(new WorkflowInstanceNameMatchSpecification(name)); + public static ISpecification WithContextId(this ISpecification specification, string contextType, string contextId) => specification.And(new WorkflowInstanceContextIdMatchSpecification(contextType, contextId)); + public static ISpecification WithContextId(this ISpecification specification, Type contextType, string contextId) => specification.And(new WorkflowInstanceContextIdMatchSpecification(contextType, contextId)); + public static ISpecification WithContextId(this ISpecification specification, string contextId) => specification.And(new WorkflowInstanceContextIdMatchSpecification(typeof(TContextType), contextId)); + public static ISpecification WithContext(this ISpecification specification, string contextType) => specification.And(new WorkflowInstanceContextMatchSpecification(contextType)); + public static ISpecification WithContext(this ISpecification specification, Type contextType) => specification.And(new WorkflowInstanceContextMatchSpecification(contextType)); + public static ISpecification WithContextd(this ISpecification specification) => specification.And(new WorkflowInstanceContextMatchSpecification(typeof(TContextType))); + public static ISpecification WithStatus(this ISpecification specification, WorkflowStatus status) => specification.And(new WorkflowStatusSpecification(status)); + + } +} diff --git a/src/core/Elsa.Core/Persistence/Specifications/WorkflowInstanceContextIdMatchSpecification.cs b/src/core/Elsa.Core/Persistence/Specifications/WorkflowInstanceContextIdMatchSpecification.cs new file mode 100644 index 000000000..00f0a89c6 --- /dev/null +++ b/src/core/Elsa.Core/Persistence/Specifications/WorkflowInstanceContextIdMatchSpecification.cs @@ -0,0 +1,30 @@ +using System; +using System.Linq.Expressions; +using Elsa.Extensions; +using Elsa.Models; + +namespace Elsa.Persistence.Specifications +{ + public class WorkflowInstanceContextIdMatchSpecification : Specification + { + public string ContextType { get; set; } + public string ContextId { get; set; } + + public WorkflowInstanceContextIdMatchSpecification(string contextType, string contextId) + { + ContextType = contextType; + ContextId = contextId; + } + + public WorkflowInstanceContextIdMatchSpecification(Type contextType, string contextId) : this(contextType.GetContextTypeName(), contextId) + { + } + + public override Expression> ToExpression() => x => x.ContextType!.Contains(ContextType) && x.ContextId!.Contains(ContextId); + + public override bool IsSatisfiedBy(WorkflowInstance entity) => entity.ContextType != null + && entity.ContextType.IndexOf(ContextType, StringComparison.OrdinalIgnoreCase) >= 0 + && entity.ContextId != null + && entity.ContextId.IndexOf(ContextId, StringComparison.OrdinalIgnoreCase) >= 0; + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Persistence/Specifications/WorkflowInstanceContextMatchSpecification.cs b/src/core/Elsa.Core/Persistence/Specifications/WorkflowInstanceContextMatchSpecification.cs new file mode 100644 index 000000000..a206e0194 --- /dev/null +++ b/src/core/Elsa.Core/Persistence/Specifications/WorkflowInstanceContextMatchSpecification.cs @@ -0,0 +1,26 @@ +using System; +using System.Linq.Expressions; +using Elsa.Extensions; +using Elsa.Models; + +namespace Elsa.Persistence.Specifications +{ + public class WorkflowInstanceContextMatchSpecification : Specification + { + public string ContextType { get; set; } + + public WorkflowInstanceContextMatchSpecification(string contextType) + { + ContextType = contextType; + } + + public WorkflowInstanceContextMatchSpecification(Type contextType) : this(contextType.GetContextTypeName()) + { + } + + public override Expression> ToExpression() => x => x.ContextType!.Contains(ContextType); + + public override bool IsSatisfiedBy(WorkflowInstance entity) => entity.ContextType != null + && entity.ContextType.IndexOf(ContextType, StringComparison.OrdinalIgnoreCase) >= 0; + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Services/WorkflowFactory.cs b/src/core/Elsa.Core/Services/WorkflowFactory.cs index a6e5a03b0..ff8c5b9ba 100644 --- a/src/core/Elsa.Core/Services/WorkflowFactory.cs +++ b/src/core/Elsa.Core/Services/WorkflowFactory.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Elsa.Extensions; using Elsa.Models; using Elsa.Services.Models; using NodaTime; @@ -37,7 +38,8 @@ namespace Elsa.Services ContextId = contextId, CreatedAt = _clock.GetCurrentInstant(), Activities = workflowBlueprint.Activities.Select(CreateInstance).ToList(), - Variables = new Variables(workflowBlueprint.Variables) + Variables = new Variables(workflowBlueprint.Variables), + ContextType = workflowBlueprint.ContextOptions?.ContextType ?.GetContextTypeName() }; return Task.FromResult(workflowInstance); diff --git a/src/persistence/Elsa.Persistence.YesSql/Documents/WorkflowInstanceDocument.cs b/src/persistence/Elsa.Persistence.YesSql/Documents/WorkflowInstanceDocument.cs index dd79ac011..634c9ef6c 100644 --- a/src/persistence/Elsa.Persistence.YesSql/Documents/WorkflowInstanceDocument.cs +++ b/src/persistence/Elsa.Persistence.YesSql/Documents/WorkflowInstanceDocument.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using AutoMapper.Configuration.Conventions; using Elsa.Comparers; using Elsa.Models; @@ -16,6 +16,7 @@ namespace Elsa.Persistence.YesSql.Documents public int Version { get; set; } public WorkflowStatus WorkflowStatus { get; set; } public string? CorrelationId { get; set; } + public string? ContextType { get; set; } public string? ContextId { get; set; } public string? Name { get; set; } public Instant CreatedAt { get; set; } diff --git a/src/persistence/Elsa.Persistence.YesSql/Indexes/WorkflowInstanceIndex.cs b/src/persistence/Elsa.Persistence.YesSql/Indexes/WorkflowInstanceIndex.cs index 2b233ce17..3430778d3 100644 --- a/src/persistence/Elsa.Persistence.YesSql/Indexes/WorkflowInstanceIndex.cs +++ b/src/persistence/Elsa.Persistence.YesSql/Indexes/WorkflowInstanceIndex.cs @@ -14,6 +14,7 @@ namespace Elsa.Persistence.YesSql.Indexes public string DefinitionId { get; set; } = default!; public int Version { get; set; } public string? CorrelationId { get; set; } + public string? ContextType { get; set; } public string? ContextId { get; set; } public string? Name { get; set; } public WorkflowStatus WorkflowStatus { get; set; } @@ -50,6 +51,7 @@ namespace Elsa.Persistence.YesSql.Indexes Version = workflowInstance.Version, WorkflowStatus = workflowInstance.WorkflowStatus, CorrelationId = workflowInstance.CorrelationId, + ContextType = workflowInstance.ContextType, ContextId = workflowInstance.ContextId, Name = workflowInstance.Name, CreatedAt = workflowInstance.CreatedAt.ToDateTimeOffset(),