Incremental work on API + client

This commit is contained in:
Sipke Schoorstra 2020-11-12 23:45:18 +01:00
parent def637b4ca
commit cb99b26151
44 changed files with 673 additions and 60 deletions

View file

@ -10,6 +10,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Configurer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=localizer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Materializer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Noda/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Persister/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=persisters/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View file

@ -1,8 +0,0 @@
using System;
namespace Elsa.Client
{
public class Class1
{
}
}

View file

@ -0,0 +1,13 @@
using System.Collections.Generic;
using Elsa.Client.Models;
namespace Elsa.Client.Comparers
{
public class BlockingActivityEqualityComparer : IEqualityComparer<BlockingActivity>
{
public static BlockingActivityEqualityComparer Instance { get; } = new BlockingActivityEqualityComparer();
public bool Equals(BlockingActivity x, BlockingActivity y) => x.ActivityId.Equals(y.ActivityId);
public int GetHashCode(BlockingActivity obj) => obj.ActivityId.GetHashCode();
}
}

View file

@ -2,6 +2,26 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<PackageVersion>1.0.0</PackageVersion>
<Authors>Elsa Contributors</Authors>
<Description>
Elsa is a set of workflow libraries and tools that enable lean and mean workflowing capabilities in any .NET Core application.
This package provides a client using Refit for Elsa's REST API endpoints.
</Description>
<Copyright>2020</Copyright>
<PackageProjectUrl>https://github.com/elsa-workflows/elsa-core</PackageProjectUrl>
<RepositoryUrl>https://github.com/elsa-workflows/elsa-core</RepositoryUrl>
<RepositoryType>GitHub</RepositoryType>
<PackageTags>elsa, workflows, rest, api, client</PackageTags>
<PackageIcon>icon.png</PackageIcon>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NodaTime" Version="3.0.3" />
<PackageReference Include="Refit" Version="5.2.1" />
<PackageReference Include="Refit.HttpClientFactory" Version="5.2.1" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,14 @@
using Elsa.Client.Services;
namespace Elsa.Client
{
internal class ElsaClient : IElsaClient
{
public ElsaClient(IWorkflowDefinitionsApi workflowDefinitionsApi)
{
WorkflowDefinitions = workflowDefinitionsApi;
}
public IWorkflowDefinitionsApi WorkflowDefinitions { get; }
}
}

View file

@ -0,0 +1,9 @@
using Elsa.Client.Services;
namespace Elsa.Client
{
public interface IElsaClient
{
IWorkflowDefinitionsApi WorkflowDefinitions { get; }
}
}

View file

@ -0,0 +1,17 @@
namespace Elsa.Client.Models
{
public class ActivityDefinition
{
public string ActivityId { get; set; } = default!;
public string Type { get; set; } = default!;
public string? Name { get; set; }
public string? DisplayName { get; set; }
public string? Description { get; set; }
public int? Left { get; set; }
public int? Top { get; set; }
public bool PersistWorkflow { get; set; }
public bool LoadWorkflowContext { get; set; }
public bool SaveWorkflowContext { get; set; }
public ActivityDefinitionProperties Properties { get; set; } = new ActivityDefinitionProperties();
}
}

View file

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Elsa.Client.Models
{
public class ActivityDefinitionProperties : Dictionary<string, ActivityDefinitionPropertyValue>
{
}
}

View file

@ -0,0 +1,26 @@
using System;
namespace Elsa.Client.Models
{
public class ActivityDefinitionPropertyValue
{
public static ActivityDefinitionPropertyValue Literal<T>(T value) => new ActivityDefinitionPropertyValue(value!.ToString(), "Literal", typeof(T));
public static ActivityDefinitionPropertyValue Liquid<T>(string expression) => new ActivityDefinitionPropertyValue(expression, "Liquid", typeof(T));
public static ActivityDefinitionPropertyValue JavaScript<T>(string expression) => new ActivityDefinitionPropertyValue(expression, "JavaScript", typeof(T));
public ActivityDefinitionPropertyValue()
{
}
public ActivityDefinitionPropertyValue(string expression, string syntax, Type type)
{
Expression = expression;
Syntax = syntax;
Type = type;
}
public Type Type { get; set; } = default!;
public string Syntax { get; set; } = default!;
public string Expression { get; set; } = default!;
}
}

View file

@ -0,0 +1,24 @@
using Newtonsoft.Json.Linq;
namespace Elsa.Client.Models
{
public class ActivityInstance
{
public ActivityInstance()
{
}
public ActivityInstance(string id, string type, object? output, JObject data)
{
Id = id;
Type = type;
Data = data;
Output = output;
}
public string? Id { get; set; }
public string? Type { get; set; }
public JObject Data { get; set; } = new JObject();
public object? Output { get; set; }
}
}

View file

@ -0,0 +1,19 @@
namespace Elsa.Client.Models
{
public class BlockingActivity
{
public BlockingActivity()
{
}
public BlockingActivity(string activityId, string activityType)
{
ActivityId = activityId;
ActivityType = activityType;
}
public string ActivityId { get; set; } = default!;
public string ActivityType { get; set; }= default!;
public string? Tag { get; set; }
}
}

View file

@ -0,0 +1,17 @@
using System.Collections.Generic;
namespace Elsa.Client.Models
{
public class CompositeActivityDefinition : ActivityDefinition
{
public CompositeActivityDefinition()
{
Activities = new List<ActivityDefinition>();
Connections = new List<ConnectionDefinition>();
Type = "CompositeActivity";
}
public ICollection<ActivityDefinition> Activities { get; set; }
public ICollection<ConnectionDefinition> Connections { get; set; }
}
}

View file

@ -0,0 +1,20 @@
namespace Elsa.Client.Models
{
public class ConnectionDefinition
{
public ConnectionDefinition()
{
}
public ConnectionDefinition(string sourceActivityId, string targetActivityId, string outcome)
{
SourceActivityId = sourceActivityId;
TargetActivityId = targetActivityId;
Outcome = outcome;
}
public string? SourceActivityId { get; set; }
public string? TargetActivityId { get; set; }
public string? Outcome { get; set; }
}
}

View file

@ -0,0 +1,20 @@
using NodaTime;
namespace Elsa.Client.Models
{
public class ExecutionLogEntry
{
public ExecutionLogEntry()
{
}
public ExecutionLogEntry(string activityId, Instant timestamp)
{
ActivityId = activityId;
Timestamp = timestamp;
}
public string ActivityId { get; set; } = default!;
public Instant Timestamp { get; set; }
}
}

View file

@ -0,0 +1,18 @@
namespace Elsa.Client.Models
{
public class ScheduledActivity
{
public ScheduledActivity()
{
}
public ScheduledActivity(string activityId, object? input = default)
{
ActivityId = activityId;
Input = input;
}
public string ActivityId { get; set; } = default!;
public object? Input { get; set; }
}
}

View file

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Linq;
namespace Elsa.Client.Models
{
public class Scheduler
{
private readonly Stack<ScheduledActivity> _stack = new Stack<ScheduledActivity>();
public bool HasScheduledActivities => _stack.Any();
public ScheduledActivity Schedule(string activityId, object? input = null)
{
var scheduledActivity = new ScheduledActivity(activityId, input);
_stack.Push(scheduledActivity);
return scheduledActivity;
}
public ScheduledActivity Next() => _stack.Pop();
}
}

View file

@ -0,0 +1,46 @@
using System.Collections.Generic;
namespace Elsa.Client.Models
{
public class Variables
{
public Variables()
{
Data = new Dictionary<string, object?>();
}
public Variables(Variables other) : this(other.Data)
{
}
public Variables(IDictionary<string, object?> data)
{
Data = data;
}
public IDictionary<string, object?> Data { get; set; }
public object? Get(string name) => Has(name) ? Data[name] : default;
public T Get<T>(string name)
{
if (!Has(name))
return default!;
var value = Get(name);
if (value == null)
return default!;
return (T)value!;
}
public Variables Set(string name, object? value)
{
Data[name] = value;
return this;
}
public bool Has(string name) => Data.ContainsKey(name);
}
}

View file

@ -0,0 +1,42 @@
namespace Elsa.Client.Models
{
public struct VersionOptions
{
/// <summary>
/// Gets the latest version.
/// </summary>
public static readonly VersionOptions Latest = new VersionOptions { IsLatest = true };
/// <summary>
/// Gets the published version.
/// </summary>
public static readonly VersionOptions Published = new VersionOptions { IsPublished = true };
/// <summary>
/// Gets the latest or published version.
/// </summary>
public static readonly VersionOptions LatestOrPublished = new VersionOptions { IsLatestOrPublished = true };
/// <summary>
/// Gets the draft version.
/// </summary>
public static readonly VersionOptions Draft = new VersionOptions { IsDraft = true };
/// <summary>
/// Gets all versions.
/// </summary>
public static readonly VersionOptions All = new VersionOptions { AllVersions = true };
/// <summary>
/// Gets a specific version.
/// </summary>
public static VersionOptions SpecificVersion(int version) => new VersionOptions { Version = version };
public bool IsLatest { get; private set; }
public bool IsLatestOrPublished { get; private set; }
public bool IsPublished { get; private set; }
public bool IsDraft { get; private set; }
public bool AllVersions { get; private set; }
public int Version { get; private set; }
}
}

View file

@ -1,4 +1,4 @@
namespace Elsa.Services.Models
namespace Elsa.Client.Models
{
public enum WorkflowContextFidelity
{

View file

@ -1,6 +1,6 @@
using System;
namespace Elsa.Services.Models
namespace Elsa.Client.Models
{
public class WorkflowContextOptions
{

View file

@ -0,0 +1,24 @@
namespace Elsa.Client.Models
{
public class WorkflowDefinition : CompositeActivityDefinition
{
public WorkflowDefinition()
{
Variables = new Variables();
Type = "Workflow";
}
public int Id { get; set; }
public string WorkflowDefinitionId { get; set; } = default!;
public string WorkflowDefinitionVersionId { get; set; } = default!;
public int Version { get; set; }
public Variables? Variables { get; set; }
public WorkflowContextOptions? ContextOptions { get; set; }
public bool IsSingleton { get; set; }
public WorkflowPersistenceBehavior PersistenceBehavior { get; set; }
public bool DeleteCompletedInstances { get; set; }
public bool IsEnabled { get; set; }
public bool IsPublished { get; set; }
public bool IsLatest { get; set; }
}
}

View file

@ -0,0 +1,17 @@
namespace Elsa.Client.Models
{
public class WorkflowExecutionScope
{
public WorkflowExecutionScope()
{
Variables = new Variables();
}
public WorkflowExecutionScope(Variables variables)
{
Variables = variables;
}
public Variables Variables { get; }
}
}

View file

@ -0,0 +1,8 @@
namespace Elsa.Client.Models
{
public class WorkflowFault
{
public string? FaultedActivityId { get; set; }
public string? Message { get; set; }
}
}

View file

@ -0,0 +1,43 @@
using System.Collections.Generic;
using Elsa.Client.Comparers;
using NodaTime;
namespace Elsa.Client.Models
{
public class WorkflowInstance
{
private HashSet<BlockingActivity> _blockingActivities = new HashSet<BlockingActivity>(BlockingActivityEqualityComparer.Instance);
public WorkflowInstance()
{
Variables = new Variables();
Activities = new List<ActivityInstance>();
ExecutionLog = new List<ExecutionLogEntry>();
ScheduledActivities = new Stack<ScheduledActivity>();
PostScheduledActivities = new Stack<ScheduledActivity>();
}
public int Id { get; set; }
public string WorkflowInstanceId { get; set; } = default!;
public string WorkflowDefinitionId { get; set; } = default!;
public int Version { get; set; }
public WorkflowStatus Status { get; set; }
public string? CorrelationId { get; set; }
public string? ContextId { get; set; }
public Instant CreatedAt { get; set; }
public Variables Variables { get; set; }
public object? Output { get; set; }
public ICollection<ActivityInstance> Activities { get; set; }
public HashSet<BlockingActivity> BlockingActivities
{
get => _blockingActivities;
set => _blockingActivities = new HashSet<BlockingActivity>(value, BlockingActivityEqualityComparer.Instance);
}
public ICollection<ExecutionLogEntry> ExecutionLog { get; set; }
public WorkflowFault? Fault { get; set; }
public Stack<ScheduledActivity> ScheduledActivities { get; set; }
public Stack<ScheduledActivity> PostScheduledActivities { get; set; }
}
}

View file

@ -0,0 +1,20 @@
namespace Elsa.Client.Models
{
public enum WorkflowPersistenceBehavior
{
/// <summary>
/// Workflow instances are persisted only when being suspended.
/// </summary>
Suspended,
/// <summary>
/// Workflow instances are persisted after the workflow executed scheduled activities.
/// </summary>
WorkflowPassCompleted,
/// <summary>
/// Workflow instances are persisted after each activity that executed.
/// </summary>
ActivityExecuted
}
}

View file

@ -0,0 +1,12 @@
namespace Elsa.Client.Models
{
public enum WorkflowStatus
{
Idle,
Running,
Finished,
Suspended,
Faulted,
Cancelled
}
}

View file

@ -0,0 +1,16 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Client.Models;
using Refit;
namespace Elsa.Client.Services
{
public interface IWorkflowDefinitionsApi
{
[Get("/v1/workflow-definitions/{id}/latest")]
Task<WorkflowDefinition> GetLatestAsync([AliasAs("id")] string workflowDefinitionId, CancellationToken cancellationToken = default);
[Get("/v1/workflow-definitions/{id}/published")]
Task<WorkflowDefinition> GetPublishedAsync([AliasAs("id")] string workflowDefinitionId, CancellationToken cancellationToken = default);
}
}

View file

@ -42,19 +42,16 @@ namespace Elsa
}
public static T GetActivityState<TActivity, T>(
this IWorkflowBlueprint workflowBlueprint,
string activityId,
this IWorkflowBlueprint workflowBlueprint,
Expression<Func<TActivity, T>> propertyExpression,
ActivityExecutionContext activityExecutionContext) where TActivity : IActivity
{
var expression = (MemberExpression)propertyExpression.Body;
string propertyName = expression.Member.Name;
return GetActivityState<T>(workflowBlueprint, activityId, propertyName, activityExecutionContext);
return GetActivityState<T>(propertyName, activityExecutionContext);
}
public static T GetActivityState<T>(
this IWorkflowBlueprint workflowBlueprint,
string activityId,
string propertyName,
ActivityExecutionContext activityExecutionContext)
{

View file

@ -1,4 +1,6 @@
using System;
using Elsa.Converters;
using Newtonsoft.Json;
namespace Elsa.Models
{
@ -19,7 +21,7 @@ namespace Elsa.Models
Type = type;
}
public Type Type { get; set; } = default!;
[JsonConverter(typeof(TypeConverter))] public Type Type { get; set; } = default!;
public string Syntax { get; set; } = default!;
public string Expression { get; set; } = default!;
}

View file

@ -0,0 +1,15 @@
namespace Elsa.Models
{
public enum WorkflowContextFidelity
{
/// <summary>
/// The workflow context is loaded only once per burst of execution.
/// </summary>
Burst,
/// <summary>
/// The workflow context is loaded before executing an activity.
/// </summary>
Activity
}
}

View file

@ -0,0 +1,11 @@
using System;
using Elsa.Services.Models;
namespace Elsa.Models
{
public class WorkflowContextOptions
{
public Type ContextType { get; set; } = default!;
public WorkflowContextFidelity ContextFidelity { get; set; }
}
}

View file

@ -11,9 +11,10 @@ namespace Elsa.Services
{
public interface IWorkflowDefinitionManager
{
ValueTask SaveAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default);
ValueTask DeleteAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default);
ValueTask<IEnumerable<WorkflowDefinition>> ListAsync(CancellationToken cancellationToken = default);
Task SaveAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default);
Task DeleteAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default);
Task<int> CountAsync(VersionOptions? version = default, CancellationToken cancellationToken = default);
Task<IEnumerable<WorkflowDefinition>> ListAsync(int? skip = default, int? take = default, VersionOptions? version = default, CancellationToken cancellationToken = default);
IQuery<WorkflowDefinition> Query();
IQuery<WorkflowDefinition, TIndex> Query<TIndex>() where TIndex : class, IIndex;

View file

@ -26,8 +26,7 @@ namespace Elsa.Triggers
public T GetState<T>(Expression<Func<TActivity, T>> propertyExpression)
{
var workflowBlueprint = _activityExecutionContext.WorkflowExecutionContext.WorkflowBlueprint;
var activityId = _activityExecutionContext.ActivityBlueprint.Id;
return workflowBlueprint.GetActivityState(activityId, propertyExpression, _activityExecutionContext);
return workflowBlueprint.GetActivityState(propertyExpression, _activityExecutionContext);
}
}
}

View file

@ -4,6 +4,7 @@ using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Data;
using Elsa.Extensions;
using Elsa.Models;
using YesSql;
using YesSql.Indexes;
@ -19,19 +20,36 @@ namespace Elsa.Services
_session = session;
}
public ValueTask SaveAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default)
public Task SaveAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default)
{
_session.Save(workflowDefinition, CollectionNames.WorkflowDefinitions);
return new ValueTask();
return Task.CompletedTask;
}
public ValueTask DeleteAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default)
public Task DeleteAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default)
{
_session.Delete(workflowDefinition, CollectionNames.WorkflowDefinitions);
return new ValueTask();
return Task.CompletedTask;
}
public async Task<int> CountAsync(VersionOptions? version = default, CancellationToken cancellationToken = default) =>
await Query()
.WithVersion(version ?? VersionOptions.Published)
.CountAsync();
public async Task<IEnumerable<WorkflowDefinition>> ListAsync(int? skip = default, int? take = default, VersionOptions? version = default, CancellationToken cancellationToken = default)
{
var query = Query().WithVersion(version ?? VersionOptions.Published);
if (skip != null)
query = query.Skip(skip.Value);
if (take != null)
query = query.Take(take.Value);
return await query.ListAsync();
}
public async ValueTask<IEnumerable<WorkflowDefinition>> ListAsync(CancellationToken cancellationToken = default) => await Query().ListAsync();
public IQuery<WorkflowDefinition> Query() => _session.Query<WorkflowDefinition>(CollectionNames.WorkflowDefinitions);
public IQuery<WorkflowDefinition, TIndex> Query<TIndex>() where TIndex : class, IIndex => _session.Query<WorkflowDefinition, TIndex>(CollectionNames.WorkflowDefinitions);
public IQuery<WorkflowDefinition, TIndex> Query<TIndex>(Expression<Func<TIndex, bool>> predicate) where TIndex : class, IIndex => _session.Query<WorkflowDefinition, TIndex>(predicate, CollectionNames.WorkflowDefinitions);

View file

@ -23,7 +23,7 @@ namespace Elsa.WorkflowProviders
protected override async ValueTask<IEnumerable<IWorkflowBlueprint>> OnGetWorkflowsAsync(CancellationToken cancellationToken)
{
var workflowDefinitions = await _workflowDefinitionManager.ListAsync(cancellationToken);
var workflowDefinitions = await _workflowDefinitionManager.ListAsync(cancellationToken: cancellationToken);
return workflowDefinitions.Select(_workflowBlueprintMaterializer.CreateWorkflowBlueprint);
}
}

View file

@ -28,6 +28,7 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="4.2.0" />
<PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="6.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.6.3" />
</ItemGroup>

View file

@ -1,32 +1,41 @@
using System.Threading;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Extensions;
using Elsa.Models;
using Elsa.Serialization;
using Elsa.Server.Api.Swagger;
using Elsa.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Serialization;
using Swashbuckle.AspNetCore.Filters;
namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
{
[ApiController]
[ApiVersion("1")]
[Route("v{version:apiVersion}/workflow-definitions/{id}")]
[Route("v{version:apiVersion}/workflow-definitions/{id:int}")]
[Produces("application/json")]
public class Get : Controller
{
private readonly IWorkflowDefinitionManager _workflowDefinitionManager;
private readonly IContentSerializer _serializer;
public Get(IWorkflowDefinitionManager workflowDefinitionManager, IContentSerializer serializer)
protected Get(IWorkflowDefinitionManager workflowDefinitionManager, IContentSerializer serializer)
{
_serializer = serializer;
_workflowDefinitionManager = workflowDefinitionManager;
_serializer = serializer;
}
[HttpGet]
public async Task<IActionResult> Handle([FromRoute(Name = "id")] string workflowDefinitionId, CancellationToken cancellationToken)
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WorkflowDefinition))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[SwaggerResponseExample(StatusCodes.Status200OK, typeof(WorkflowDefinitionExample))]
public async Task<IActionResult> Handle(string workflowDefinitionId, VersionOptions? version = default, CancellationToken cancellationToken = default)
{
var workflowDefinition = await _workflowDefinitionManager.GetAsync(workflowDefinitionId, VersionOptions.Latest, cancellationToken);
return workflowDefinition == null ? (IActionResult)NotFound() : Json(workflowDefinition, _serializer.GetSettings());
var workflowDefinition = await _workflowDefinitionManager.GetAsync(workflowDefinitionId, version ?? VersionOptions.Latest, cancellationToken);
return workflowDefinition == null ? (IActionResult) NotFound() : Json(workflowDefinition, _serializer.GetSettings());
}
}
}

View file

@ -0,0 +1,37 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Models;
using Elsa.Serialization;
using Elsa.Server.Api.Models;
using Elsa.Services;
using Microsoft.AspNetCore.Mvc;
namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
{
[ApiController]
[ApiVersion("1")]
[Route("v{version:apiVersion}/workflow-definitions")]
[Produces("application/json")]
public class List : Controller
{
private readonly IWorkflowDefinitionManager _workflowDefinitionManager;
private readonly IContentSerializer _serializer;
public List(IWorkflowDefinitionManager workflowDefinitionManager, IContentSerializer serializer)
{
_workflowDefinitionManager = workflowDefinitionManager;
_serializer = serializer;
}
[HttpGet]
public async Task<ActionResult<PagedList<WorkflowDefinition>>> Handle(int page = 0, int pageSize = 50, VersionOptions? version = default, CancellationToken cancellationToken = default)
{
version ??= VersionOptions.Latest;
var totalCount = await _workflowDefinitionManager.CountAsync(version, cancellationToken);
var items = await _workflowDefinitionManager.ListAsync(page, pageSize, version, cancellationToken);
var pagedList = new PagedList<WorkflowDefinition>(items, page, pageSize, totalCount);
return Json(pagedList, _serializer.GetSettings());
}
}
}

View file

@ -2,18 +2,18 @@
using System.Threading.Tasks;
using Elsa.Services;
using Microsoft.AspNetCore.Mvc;
using YesSql;
namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
{
[ApiController]
[ApiVersion("1")]
[Route("v{version:apiVersion}/workflow-definitions")]
[Produces("application/json")]
public class Post : ControllerBase
{
private readonly IWorkflowPublisher _workflowPublisher;
public Post(IWorkflowDefinitionManager workflowDefinitionManager, IWorkflowPublisher workflowPublisher, ISession session)
public Post(IWorkflowPublisher workflowPublisher)
{
_workflowPublisher = workflowPublisher;
}

View file

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Linq;
namespace Elsa.Server.Api.Models
{
public class PagedList<T>
{
public PagedList(IEnumerable<T> items, int page, int pageSize, int totalCount)
{
Page = page;
PageSize = pageSize;
TotalCount = totalCount;
Items = items.ToList();
}
public ICollection<T> Items { get; }
public int Page { get; }
public int PageSize { get; }
public int TotalCount { get; }
}
}

View file

@ -0,0 +1,41 @@
using System.Collections.Generic;
using Elsa.Models;
using Swashbuckle.AspNetCore.Filters;
namespace Elsa.Server.Api.Swagger
{
public class WorkflowDefinitionExample : IExamplesProvider<WorkflowDefinition>
{
public WorkflowDefinition GetExamples()
{
return new WorkflowDefinition
{
Id = 1,
Name = "ProcessOrderWorkflow",
DisplayName = "Process Order Workflow",
Description = "Process new orders",
Version = 1,
IsPublished = true,
WorkflowDefinitionId = "26bd1dca39954c8f93364a18c2a35528",
WorkflowDefinitionVersionId = "c8c57402e68949598a45411c74e463e1",
Type = "Workflow",
ContextOptions = new WorkflowContextOptions
{
ContextFidelity = WorkflowContextFidelity.Burst,
ContextType = typeof(Order)
},
Activities = new[]
{
new ActivityDefinition
{
ActivityId = "activity-1",
}
}
};
}
}
public class Order
{
}
}

View file

@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>

View file

@ -1,26 +1,13 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:11000"
"profiles": {
"Elsa.Server.Host": {
"commandName": "Project",
"launchBrowser": false,
"applicationUrl": "http://localhost:11000",
"launchUrl": "http://localhost:11000/swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Elsa.Server.Host": {
"commandName": "Project",
"launchBrowser": false,
"applicationUrl": "http://localhost:11000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View file

@ -1,4 +1,5 @@
using Elsa.Runtime;
using Elsa.Server.Api.Swagger;
using Elsa.StartupTasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@ -6,6 +7,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Filters;
using YesSql.Provider.Sqlite;
namespace Elsa.Server.Host
@ -31,8 +33,12 @@ namespace Elsa.Server.Host
elsa => elsa
.UsePersistence(config => config.UseSqLite(connectionString)));
services.AddSingleton<WorkflowDefinitionExample>();
services
.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Elsa", Version = "v1"}); })
.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo {Title = "Elsa", Version = "v1"});
})
.AddElsaApiEndpoints()
.AddCors(
options => options.AddDefaultPolicy(