Incremental work on saving and loading workflow definitions
This commit is contained in:
parent
acf884c075
commit
c193b28ba1
|
|
@ -9,7 +9,9 @@ indent_size=4
|
|||
# ReSharper properties
|
||||
resharper_csharp_max_line_length=240
|
||||
resharper_keep_user_linebreaks=true
|
||||
resharper_place_field_attribute_on_same_line=if_owner_is_single_line
|
||||
resharper_space_within_single_line_array_initializer_braces=true
|
||||
resharper_wrap_before_linq_expression=true
|
||||
|
||||
# Microsoft .NET properties
|
||||
csharp_new_line_before_members_in_object_initializers=false
|
||||
|
|
|
|||
|
|
@ -5,10 +5,6 @@ namespace Elsa.Client.Models
|
|||
[DataContract]
|
||||
public class ConnectionDefinition
|
||||
{
|
||||
public ConnectionDefinition()
|
||||
{
|
||||
}
|
||||
|
||||
public ConnectionDefinition(string sourceActivityId, string targetActivityId, string outcome)
|
||||
{
|
||||
SourceActivityId = sourceActivityId;
|
||||
|
|
@ -16,8 +12,8 @@ namespace Elsa.Client.Models
|
|||
Outcome = outcome;
|
||||
}
|
||||
|
||||
[DataMember(Order = 1)] public string? SourceActivityId { get; set; }
|
||||
[DataMember(Order = 2)] public string? TargetActivityId { get; set; }
|
||||
[DataMember(Order = 3)] public string? Outcome { get; set; }
|
||||
[DataMember(Order = 1)] public string SourceActivityId { get; set; }
|
||||
[DataMember(Order = 2)] public string TargetActivityId { get; set; }
|
||||
[DataMember(Order = 3)] public string Outcome { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Elsa.Client.Models
|
||||
{
|
||||
[DataContract]
|
||||
public class List<T>
|
||||
{
|
||||
public ICollection<T> Items { get; set; } = new System.Collections.Generic.List<T>();
|
||||
[DataMember(Order = 1)] public ICollection<T> Items { get; set; } = new System.Collections.Generic.List<T>();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
namespace Elsa.Client.Models
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Elsa.Client.Models
|
||||
{
|
||||
[DataContract]
|
||||
public class PagedList<T> : List<T>
|
||||
{
|
||||
public int? Page { get; set; }
|
||||
public int? PageSize { get; set; }
|
||||
public int TotalCount { get; set; }
|
||||
[DataMember(Order = 1)] public int? Page { get; set; }
|
||||
[DataMember(Order = 2)] public int? PageSize { get; set; }
|
||||
[DataMember(Order = 3)] public int TotalCount { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -7,16 +7,19 @@ namespace Elsa.Client.Services
|
|||
{
|
||||
public interface IWorkflowDefinitionsApi
|
||||
{
|
||||
[Get("/v1/workflow-definitions/{workflowDefinitionId}")]
|
||||
Task<WorkflowDefinition> GetAsync(string workflowDefinitionId, VersionOptions? versionOptions = default, CancellationToken cancellationToken = default);
|
||||
[Get("/v1/workflow-definitions/{workflowDefinitionId}/{versionOptions}")]
|
||||
Task<WorkflowDefinition> GetByDefinitionAndVersionAsync(string workflowDefinitionId, VersionOptions versionOptions, CancellationToken cancellationToken = default);
|
||||
|
||||
[Get("/v1/workflow-definitions/{workflowDefinitionVersionId}")]
|
||||
Task<WorkflowDefinition> GetByVersionIdAsync(string workflowDefinitionVersionId, CancellationToken cancellationToken = default);
|
||||
|
||||
[Get("/v1/workflow-definitions")]
|
||||
Task<WorkflowDefinition> ListAsync(string workflowDefinitionId, VersionOptions? versionOptions = default, CancellationToken cancellationToken = default);
|
||||
Task<PagedList<WorkflowDefinition>> ListAsync(int? page = default, int? pageSize = default, VersionOptions? versionOptions = default, CancellationToken cancellationToken = default);
|
||||
|
||||
[Post("/v1/workflow-definitions")]
|
||||
Task<WorkflowDefinition> PostAsync([Body(BodySerializationMethod.Serialized)]PostWorkflowDefinitionRequest request, CancellationToken cancellationToken = default);
|
||||
Task<WorkflowDefinition> PostAsync([Body(BodySerializationMethod.Serialized)] PostWorkflowDefinitionRequest request, CancellationToken cancellationToken = default);
|
||||
|
||||
[Post("/v1/workflow-definitions/{workflowDefinitionId}")]
|
||||
Task<WorkflowDefinition> PostAsync(string workflowDefinitionId, [Body(BodySerializationMethod.Serialized)]PostWorkflowDefinitionRequest request, CancellationToken cancellationToken = default);
|
||||
Task<WorkflowDefinition> PostAsync(string workflowDefinitionId, [Body(BodySerializationMethod.Serialized)] PostWorkflowDefinitionRequest request, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
namespace ElsaDashboard.Application.Server
|
||||
{
|
||||
public enum BlazorRuntimeModel
|
||||
{
|
||||
Server,
|
||||
Browser
|
||||
}
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
|
||||
@if (Program.UseBlazorServer)
|
||||
@if (Program.RuntimeModel == BlazorRuntimeModel.Server)
|
||||
{
|
||||
<script src="_framework/blazor.server.js"></script>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@ namespace ElsaDashboard.Application.Server
|
|||
{
|
||||
public class Program
|
||||
{
|
||||
public static bool UseBlazorServer = true;
|
||||
public static bool UseBlazorWebAssembly => !UseBlazorServer;
|
||||
public static RenderMode RenderMode => UseBlazorServer ? RenderMode.ServerPrerendered: RenderMode.WebAssemblyPrerendered;
|
||||
public static BlazorRuntimeModel RuntimeModel => BlazorRuntimeModel.Browser;
|
||||
public static RenderMode RenderMode => RuntimeModel == BlazorRuntimeModel.Server ? RenderMode.ServerPrerendered : RenderMode.WebAssemblyPrerendered;
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
|
|
@ -17,9 +16,6 @@ namespace ElsaDashboard.Application.Server
|
|||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ namespace ElsaDashboard.Application.Server
|
|||
services.AddElsaDashboardUI();
|
||||
services.AddElsaDashboardBackend(options => options.ServerUrl = new Uri("https://localhost:11000"));
|
||||
|
||||
if (Program.UseBlazorServer)
|
||||
if (Program.RuntimeModel == BlazorRuntimeModel.Server)
|
||||
services.AddServerSideBlazor(options =>
|
||||
{
|
||||
options.DetailedErrors = !Environment.IsProduction();
|
||||
|
|
@ -40,7 +40,7 @@ namespace ElsaDashboard.Application.Server
|
|||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
if(Program.UseBlazorWebAssembly)
|
||||
if (Program.RuntimeModel == BlazorRuntimeModel.Browser)
|
||||
app.UseWebAssemblyDebugging();
|
||||
}
|
||||
else
|
||||
|
|
@ -50,7 +50,7 @@ namespace ElsaDashboard.Application.Server
|
|||
app.UseHsts();
|
||||
}
|
||||
|
||||
if(Program.UseBlazorWebAssembly)
|
||||
if (Program.RuntimeModel == BlazorRuntimeModel.Browser)
|
||||
app.UseBlazorFrameworkFiles();
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
|
@ -58,7 +58,7 @@ namespace ElsaDashboard.Application.Server
|
|||
app.UseElsaGrpcServices();
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
if(Program.UseBlazorServer)
|
||||
if (Program.RuntimeModel == BlazorRuntimeModel.Server)
|
||||
endpoints.MapBlazorHub();
|
||||
|
||||
endpoints.MapFallbackToPage("/_Host");
|
||||
|
|
|
|||
|
|
@ -266,7 +266,14 @@
|
|||
</div>
|
||||
<main class="flex-1 relative z-0 overflow-y-auto focus:outline-none" tabindex="0" x-data="" x-init="$el.focus()">
|
||||
|
||||
|
||||
<!-- Page title & actions -->
|
||||
<div class="border-b border-gray-200 px-4 py-4 sm:flex sm:items-center sm:justify-between sm:px-6 lg:px-8">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h1 class="text-lg font-medium leading-6 text-gray-900 sm:truncate">
|
||||
Home
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
<module href="./src/partials/layout.html">
|
||||
|
||||
<!-- Page title & actions -->
|
||||
<div class="border-b border-gray-200 px-4 py-4 sm:flex sm:items-center sm:justify-between sm:px-6 lg:px-8">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h1 class="text-lg font-medium leading-6 text-gray-900 sm:truncate">
|
||||
Home
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</module>
|
||||
|
|
@ -1,16 +1,19 @@
|
|||
namespace ElsaDashboard.Application.Models
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
|
||||
namespace ElsaDashboard.Application.Models
|
||||
{
|
||||
public class MenuItem
|
||||
{
|
||||
public MenuItem(string text, string url, string icon)
|
||||
public MenuItem(string text, string url, string icon, NavLinkMatch match = NavLinkMatch.Prefix)
|
||||
{
|
||||
Text = text;
|
||||
Url = url;
|
||||
Icon = icon;
|
||||
}
|
||||
|
||||
public string Text { get; set; }
|
||||
public string Icon { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string Text { get; init; }
|
||||
public string Icon { get; init; }
|
||||
public string Url { get; init; }
|
||||
public NavLinkMatch Match { get; init; } = NavLinkMatch.Prefix;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
@page "/designer"
|
||||
@page "/workflows/designer"
|
||||
@page "/workflows/{workflowDefinitionVersionId}/designer"
|
||||
<div class="flex-0 border-b border-gray-200 px-4 py-4 sm:flex sm:items-center sm:justify-between sm:px-6 lg:px-8">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h1 class="text-lg font-medium leading-6 text-gray-900 sm:truncate">
|
||||
|
|
@ -13,4 +14,4 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<WorkflowDesigner />
|
||||
<WorkflowDesigner Model="WorkflowModel" />
|
||||
|
|
@ -1,6 +1,56 @@
|
|||
namespace ElsaDashboard.Application.Pages
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Client.Models;
|
||||
using ElsaDashboard.Application.Models;
|
||||
using ElsaDashboard.Shared.Rpc;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace ElsaDashboard.Application.Pages
|
||||
{
|
||||
partial class Designer
|
||||
{
|
||||
[Parameter]public string? WorkflowDefinitionVersionId { get; set; }
|
||||
[Inject] private IWorkflowDefinitionService WorkflowDefinitionService { get; set; } = default!;
|
||||
[Inject] private IActivityService ActivityService { get; set; } = default!;
|
||||
private IDictionary<string, ActivityDescriptor> ActivityDescriptors { get; set; } = default!;
|
||||
private WorkflowModel WorkflowModel { get; set; } = WorkflowModel.Blank();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
ActivityDescriptors = (await ActivityService.GetActivitiesAsync()).ToDictionary(x => x.Type);
|
||||
|
||||
if (WorkflowDefinitionVersionId != null)
|
||||
{
|
||||
var workflowDefinition = await WorkflowDefinitionService.GetByVersionIdAsync(WorkflowDefinitionVersionId);
|
||||
WorkflowModel = CreateWorkflowModel(workflowDefinition);
|
||||
}
|
||||
else
|
||||
{
|
||||
WorkflowModel = WorkflowModel.Blank();
|
||||
}
|
||||
}
|
||||
|
||||
private WorkflowModel CreateWorkflowModel(WorkflowDefinition workflowDefinition)
|
||||
{
|
||||
return new WorkflowModel
|
||||
{
|
||||
Name = workflowDefinition.Name,
|
||||
Activities = workflowDefinition.Activities.Select(CreateActivityModel).ToImmutableList(),
|
||||
Connections = workflowDefinition.Connections.Select(CreateConnectionModel).ToImmutableList()
|
||||
};
|
||||
}
|
||||
|
||||
private ConnectionModel CreateConnectionModel(ConnectionDefinition connectionDefinition)
|
||||
{
|
||||
return new(connectionDefinition.SourceActivityId, connectionDefinition.TargetActivityId, connectionDefinition.Outcome);
|
||||
}
|
||||
|
||||
private ActivityModel CreateActivityModel(ActivityDefinition activityDefinition)
|
||||
{
|
||||
var descriptor = ActivityDescriptors[activityDefinition.Type];
|
||||
return new ActivityModel(activityDefinition.ActivityId, activityDefinition.Type, descriptor.Outcomes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,10 @@
|
|||
@page "/"
|
||||
|
||||
<h1>Hello, world!</h1>
|
||||
|
||||
Welcome to your new app.
|
||||
<!-- Page title & actions -->
|
||||
<div class="border-b border-gray-200 px-4 py-4 sm:flex sm:items-center sm:justify-between sm:px-6 lg:px-8">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h1 class="text-lg font-medium leading-6 text-gray-900 sm:truncate">
|
||||
Home
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
</div>
|
||||
<div class="mt-4 flex sm:mt-0 sm:ml-4">
|
||||
<span class="order-0 sm:order-1 sm:ml-3 shadow-sm rounded-md">
|
||||
<a href="/designer" type="button" class="inline-flex items-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-blue-500 hover:bg-blue-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-700 active:bg-blue-700 transition duration-150 ease-in-out">
|
||||
<a href="workflows/designer" type="button" class="inline-flex items-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-blue-500 hover:bg-blue-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-700 active:bg-blue-700 transition duration-150 ease-in-out">
|
||||
Create
|
||||
</a>
|
||||
</span>
|
||||
|
|
@ -108,12 +108,15 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-100">
|
||||
@foreach (var workflowDefinition in WorkflowDefinitions.Items)
|
||||
{
|
||||
var displayName = workflowDefinition.DisplayName ?? workflowDefinition.Name ?? "Unnamed";
|
||||
<tr>
|
||||
<td class="px-6 py-3 whitespace-no-wrap text-sm leading-5 font-medium text-gray-900">
|
||||
<div class="flex items-center space-x-3 lg:pl-2">
|
||||
<div class="flex-shrink-0 w-2.5 h-2.5 rounded-full bg-pink-600"></div>
|
||||
<a href="#" class="truncate hover:text-gray-600">
|
||||
<span>Document Approval</span>
|
||||
<a href="@($"workflows/{workflowDefinition.WorkflowDefinitionVersionId}/designer")" class="truncate hover:text-gray-600">
|
||||
<span>@displayName</span>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
|
|
@ -151,7 +154,7 @@
|
|||
class="z-10 mx-3 origin-top-right absolute right-7 top-0 w-48 mt-1 rounded-md shadow-lg">
|
||||
<div class="rounded-md bg-white shadow-xs" role="menu" aria-orientation="vertical" aria-labelledby="project-options-menu-0">
|
||||
<div class="py-1">
|
||||
<a href="#" class="group flex items-center px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900" role="menuitem">
|
||||
<a href="@($"workflows/{workflowDefinition.WorkflowDefinitionVersionId}/designer")" class="group flex items-center px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900" role="menuitem">
|
||||
<!-- Heroicon name: pencil-alt -->
|
||||
<svg class="mr-3 h-5 w-5 text-gray-400 group-hover:text-gray-500 group-focus:text-gray-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z"/>
|
||||
|
|
@ -190,8 +193,7 @@
|
|||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- More project rows... -->
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
using System.Threading.Tasks;
|
||||
using Elsa.Client.Models;
|
||||
using ElsaDashboard.Shared.Rpc;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace ElsaDashboard.Application.Pages
|
||||
{
|
||||
partial class Workflows
|
||||
{
|
||||
[Inject] private IWorkflowDefinitionService WorkflowDefinitionService { get; set; } = default!;
|
||||
private PagedList<WorkflowDefinition> WorkflowDefinitions { get; set; } = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
WorkflowDefinitions = await WorkflowDefinitionService.ListAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ElsaDashboard.Application.Services
|
||||
{
|
||||
public class BackgroundWorker
|
||||
{
|
||||
private Channel<Func<ValueTask>> _channel;
|
||||
|
||||
public BackgroundWorker()
|
||||
{
|
||||
_channel = Channel.CreateBounded<Func<ValueTask>>(10);
|
||||
}
|
||||
|
||||
public async Task ScheduleTask(Func<ValueTask> task, CancellationToken cancellationToken = default) => await _channel.Writer.WriteAsync(task, cancellationToken);
|
||||
|
||||
public async Task ScheduleTask(Action task, CancellationToken cancellationToken = default) =>
|
||||
await _channel.Writer.WriteAsync(() =>
|
||||
{
|
||||
task();
|
||||
return ValueTask.CompletedTask;
|
||||
}, cancellationToken);
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
while (await _channel.Reader.WaitToReadAsync(cancellationToken))
|
||||
{
|
||||
while (_channel.Reader.TryRead(out var task))
|
||||
{
|
||||
await task();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,13 +29,13 @@
|
|||
|
||||
<div class="p-6 space-y-6 overflow-y-auto">
|
||||
|
||||
@foreach (var grouping in ActivityGroupings)
|
||||
@foreach (var grouping in ActivityGroupings.OrderBy(x => x.Key))
|
||||
{
|
||||
<div>
|
||||
<h2>@grouping.Key</h2>
|
||||
|
||||
<ul class="grid grid-cols-1 gap-4 mt-3" x-max="1">
|
||||
@foreach (var activity in grouping)
|
||||
@foreach (var activity in grouping.OrderBy(x => x.DisplayName))
|
||||
{
|
||||
<li @onclick="(() => OnActivityClick(activity))" class="relative col-span-1 flex shadow-sm rounded-md select-none cursor-pointer">
|
||||
<div class="flex-shrink-0 flex items-center justify-center w-16 bg-blue-600 text-white text-sm leading-5 font-medium rounded-l-md">
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace ElsaDashboard.Application.Shared
|
|||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var response = (await ActivityService.GetActivitiesAsync());
|
||||
Activities = response.Activities.ToList();
|
||||
Activities = response.ToList();
|
||||
}
|
||||
|
||||
private void OnActivityTypeFilterClick(ActivityTraitFilter activityTraitFilter)
|
||||
|
|
|
|||
|
|
@ -27,34 +27,6 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<!-- Profile dropdown -->
|
||||
<div x-on:click.away="open = false" class="ml-3 relative" x-data="{ open: false }">
|
||||
<div>
|
||||
<button x-on:click="open = !open" class="max-w-xs flex items-center text-sm rounded-full focus:outline-none focus:shadow-outline" id="user-menu" aria-label="User menu" aria-haspopup="true" x-bind:aria-expanded="open">
|
||||
<img class="h-8 w-8 rounded-full" src="https://images.unsplash.com/photo-1502685104226-ee32379fefbe?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
|
||||
</button>
|
||||
</div>
|
||||
<div x-show="open" x-description="Profile dropdown panel, show/hide based on dropdown state." x-transition:enter="transition ease-out duration-100" x-transition:enter-start="transform opacity-0 scale-95" x-transition:enter-end="transform opacity-100 scale-100" x-transition:leave="transition ease-in duration-75" x-transition:leave-start="transform opacity-100 scale-100" x-transition:leave-end="transform opacity-0 scale-95" class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg" style="display: none;">
|
||||
<div class="rounded-md bg-white shadow-xs" role="menu" aria-orientation="vertical" aria-labelledby="user-menu">
|
||||
<div class="py-1">
|
||||
<a href="#" class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900" role="menuitem">View profile</a>
|
||||
<a href="#" class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900" role="menuitem">Settings</a>
|
||||
<a href="#" class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900" role="menuitem">Notifications</a>
|
||||
</div>
|
||||
<div class="border-t border-gray-100"></div>
|
||||
<div class="py-1">
|
||||
<a href="#" class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900" role="menuitem">Get desktop app</a>
|
||||
<a href="#" class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900" role="menuitem">Support</a>
|
||||
</div>
|
||||
<div class="border-t border-gray-100"></div>
|
||||
<div class="py-1">
|
||||
<a href="#" class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900" role="menuitem">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main class="flex flex-col flex-1 relative z-0 overflow-y-auto focus:outline-none" tabindex="0" x-data="" x-init="$el.focus()">
|
||||
|
|
|
|||
|
|
@ -17,28 +17,15 @@
|
|||
<div class="mt-5 flex-1 h-0 overflow-y-auto">
|
||||
<nav class="px-2">
|
||||
<div class="space-y-1">
|
||||
|
||||
<a href="#" class="group flex items-center px-2 py-2 text-base leading-5 font-medium rounded-md text-gray-900 bg-gray-100 hover:text-gray-900 hover:bg-gray-100 focus:bg-gray-200 focus:outline-none transition ease-in-out duration-150">
|
||||
@foreach (var menuItem in MenuItems)
|
||||
{
|
||||
const string activeClass = "text-gray-900 bg-gray-100 hover:text-gray-900 hover:bg-gray-100 focus:bg-gray-200";
|
||||
<NavLink Match="@menuItem.Match" href="@menuItem.Url" class="group flex items-center px-2 py-2 text-base leading-5 font-medium rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-50 focus:bg-gray-50 focus:outline-none transition ease-in-out duration-150" ActiveClass="@activeClass">
|
||||
<svg class="mr-3 h-6 w-6 text-gray-500 group-hover:text-gray-500 group-focus:text-gray-600 transition ease-in-out duration-150" x-description="Heroicon name: home" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="@menuItem.Icon"></path>
|
||||
</svg>
|
||||
Home
|
||||
</a>
|
||||
|
||||
<a href="#" class="group flex items-center px-2 py-2 text-base leading-5 font-medium rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-50 focus:bg-gray-50 focus:outline-none transition ease-in-out duration-150">
|
||||
<svg class="mr-3 h-6 w-6 text-gray-400 group-hover:text-gray-500 group-focus:text-gray-600 transition ease-in-out duration-150" x-description="Heroicon name: view-list" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16"></path>
|
||||
</svg>
|
||||
My tasks
|
||||
</a>
|
||||
|
||||
<a href="#" class="group flex items-center px-2 py-2 text-base leading-5 font-medium rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-50 focus:bg-gray-50 focus:outline-none transition ease-in-out duration-150">
|
||||
<svg class="mr-3 h-6 w-6 text-gray-400 group-hover:text-gray-500 group-focus:text-gray-600 transition ease-in-out duration-150" x-description="Heroicon name: clock" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
Recent
|
||||
</a>
|
||||
|
||||
@menuItem.Text
|
||||
</NavLink> }
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
@ -72,27 +59,16 @@
|
|||
<nav class="px-3 mt-6">
|
||||
<div class="space-y-1">
|
||||
|
||||
<a href="#" class="group flex items-center px-2 py-2 text-sm leading-5 font-medium rounded-md text-gray-900 bg-gray-200 focus:outline-none focus:bg-gray-50 transition ease-in-out duration-150">
|
||||
<svg class="mr-3 h-6 w-6 text-gray-500 group-hover:text-gray-500 group-focus:text-gray-600 transition ease-in-out duration-150" x-description="Heroicon name: home" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
@foreach (var menuItem in MenuItems)
|
||||
{
|
||||
const string activeClass = "text-gray-900 bg-gray-200 focus:bg-gray-50";
|
||||
<NavLink Match="@menuItem.Match" href="@menuItem.Url" class="group flex items-center px-2 py-2 text-sm leading-5 font-medium rounded-md text-gray-700 hover:text-gray-900 hover:bg-gray-50 focus:outline-none focus:bg-gray-50 transition ease-in-out duration-150" ActiveClass="@activeClass">
|
||||
<svg class="mr-3 h-6 w-6 text-gray-400 group-hover:text-gray-500 group-focus:text-gray-600 transition ease-in-out duration-150" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="@menuItem.Icon"></path>
|
||||
</svg>
|
||||
Home
|
||||
</a>
|
||||
|
||||
<a href="#" class="group flex items-center px-2 py-2 text-sm leading-5 font-medium rounded-md text-gray-700 hover:text-gray-900 hover:bg-gray-50 focus:outline-none focus:bg-gray-50 transition ease-in-out duration-150">
|
||||
<svg class="mr-3 h-6 w-6 text-gray-400 group-hover:text-gray-500 group-focus:text-gray-600 transition ease-in-out duration-150" x-description="Heroicon name: view-list" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16"></path>
|
||||
</svg>
|
||||
My tasks
|
||||
</a>
|
||||
|
||||
<a href="#" class="group flex items-center px-2 py-2 text-sm leading-5 font-medium rounded-md text-gray-700 hover:text-gray-900 hover:bg-gray-50 focus:outline-none focus:bg-gray-50 transition ease-in-out duration-150">
|
||||
<svg class="mr-3 h-6 w-6 text-gray-400 group-hover:text-gray-500 group-focus:text-gray-600 transition ease-in-out duration-150" x-description="Heroicon name: clock" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
Recent
|
||||
</a>
|
||||
|
||||
@menuItem.Text
|
||||
</NavLink>
|
||||
}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using ElsaDashboard.Application.Models;
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
|
||||
namespace ElsaDashboard.Application.Shared
|
||||
{
|
||||
|
|
@ -9,8 +10,9 @@ namespace ElsaDashboard.Application.Shared
|
|||
{
|
||||
MenuItems = new[]
|
||||
{
|
||||
new MenuItem("Dashboard", "/", "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"),
|
||||
new MenuItem("Workflows", "/workflows", "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"),
|
||||
new MenuItem("Home", "", "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6", NavLinkMatch.All),
|
||||
new MenuItem("Workflows", "workflows", "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z", NavLinkMatch.Prefix),
|
||||
new MenuItem("Composite Activities", "composite-activities", "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z", NavLinkMatch.Prefix),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@ using System.Collections.Generic;
|
|||
using System.Collections.Immutable;
|
||||
using System.Formats.Asn1;
|
||||
using System.Linq;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Client.Models;
|
||||
using ElsaDashboard.Application.Extensions;
|
||||
using ElsaDashboard.Application.Models;
|
||||
using ElsaDashboard.Application.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
|
|
@ -18,17 +20,36 @@ namespace ElsaDashboard.Application.Shared
|
|||
{
|
||||
private static Action<ConnectionModel> _connectionCreatedAction = default!;
|
||||
|
||||
[Parameter] public WorkflowModel Model { private get; set; } = WorkflowModel.Demo();
|
||||
[Parameter] public WorkflowModel Model { private get; set; } = WorkflowModel.Blank();
|
||||
[Inject] private IJSRuntime JS { get; set; } = default!;
|
||||
[Inject] private IFlyoutPanelService FlyoutPanelService { get; set; } = default!;
|
||||
private IJSObjectReference _designerModule = default!;
|
||||
private bool _connectionsChanged = true;
|
||||
private EventCallbackFactory EventCallbackFactory { get; } = new();
|
||||
private BackgroundWorker BackgroundWorker { get; } = new();
|
||||
|
||||
[JSInvokableAttribute("InvokeConnectionCreated")]
|
||||
public static void InvokeConnectionCreated(ConnectionModel connection) => _connectionCreatedAction(connection);
|
||||
|
||||
protected override void OnInitialized() => _connectionCreatedAction = OnConnectionCreated;
|
||||
|
||||
int _currentCount = 0;
|
||||
|
||||
void IncrementCount()
|
||||
{
|
||||
_currentCount++;
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_connectionCreatedAction = OnConnectionCreated;
|
||||
|
||||
InvokeAsync(() => BackgroundWorker.StartAsync());
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
ConnectionsHasChanged();
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
|
|
@ -38,14 +59,24 @@ namespace ElsaDashboard.Application.Shared
|
|||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
if (_designerModule == null!)
|
||||
_designerModule = await JS.InvokeAsync<IJSObjectReference>("import", "./_content/ElsaDashboard.Application/workflowDesigner.js");
|
||||
}
|
||||
|
||||
await RepaintConnections();
|
||||
}
|
||||
|
||||
private async Task UpdateModelAsync(WorkflowModel model)
|
||||
{
|
||||
Model = model;
|
||||
ConnectionsHasChanged();
|
||||
await BackgroundWorker.ScheduleTask(SaveWorkflowAsync);
|
||||
}
|
||||
|
||||
private async ValueTask SaveWorkflowAsync()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private IEnumerable<ActivityModel> GetRootActivities() => Model.GetChildActivities(null);
|
||||
|
||||
private async ValueTask RepaintConnections()
|
||||
|
|
@ -197,6 +228,7 @@ namespace ElsaDashboard.Application.Shared
|
|||
Model = model;
|
||||
await FlyoutPanelService.HideAsync();
|
||||
ConnectionsHasChanged();
|
||||
await BackgroundWorker.ScheduleTask(() => Console.WriteLine("TEST"));
|
||||
}
|
||||
|
||||
private void ConnectionsHasChanged()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace ElsaDashboard.Backend.Extensions
|
|||
services.AddElsaClient(configure);
|
||||
services.AddCodeFirstGrpc(options => options.ResponseCompressionLevel = CompressionLevel.Optimal);
|
||||
services.AddScoped<IActivityService, ActivityService>();
|
||||
services.AddScoped<IWorkflowDefinitionService, WorkflowDefinitionService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Client;
|
||||
using Elsa.Client.Models;
|
||||
using ElsaDashboard.Shared.Rpc;
|
||||
using ProtoBuf.Grpc;
|
||||
|
||||
|
|
@ -14,10 +16,10 @@ namespace ElsaDashboard.Backend.Rpc
|
|||
_elsaClient = elsaClient;
|
||||
}
|
||||
|
||||
public async Task<GetActivitiesResponse> GetActivitiesAsync(CallContext callContext)
|
||||
public async Task<IEnumerable<ActivityDescriptor>> GetActivitiesAsync(CallContext callContext)
|
||||
{
|
||||
var result = await _elsaClient.Activities.ListAsync(callContext.CancellationToken);
|
||||
return new GetActivitiesResponse(result.Items);
|
||||
return result.Items;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
namespace ElsaDashboard.Backend.Rpc
|
||||
{
|
||||
public class WorkflowDefinitionManager
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
using System.Threading.Tasks;
|
||||
using Elsa.Client;
|
||||
using Elsa.Client.Models;
|
||||
using ElsaDashboard.Shared.Rpc;
|
||||
using ProtoBuf.Grpc;
|
||||
|
||||
namespace ElsaDashboard.Backend.Rpc
|
||||
{
|
||||
public class WorkflowDefinitionService : IWorkflowDefinitionService
|
||||
{
|
||||
private readonly IElsaClient _elsaClient;
|
||||
|
||||
public WorkflowDefinitionService(IElsaClient elsaClient)
|
||||
{
|
||||
_elsaClient = elsaClient;
|
||||
}
|
||||
|
||||
public async Task<PagedList<WorkflowDefinition>> ListAsync(ListWorkflowDefinitionsRequest request, CallContext context) =>
|
||||
await _elsaClient.WorkflowDefinitions.ListAsync(request.Page, request.PageSize, request.VersionOptions, context.CancellationToken);
|
||||
|
||||
public Task<WorkflowDefinition> GetByVersionIdAsync(string workflowDefinitionVersionId, CallContext context = default) => _elsaClient.WorkflowDefinitions.GetByVersionIdAsync(workflowDefinitionVersionId, context.CancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,21 +12,6 @@ namespace ElsaDashboard.Shared.Rpc
|
|||
public interface IActivityService
|
||||
{
|
||||
[ProtoBehavior]
|
||||
Task<GetActivitiesResponse> GetActivitiesAsync(CallContext context = default);
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public sealed class GetActivitiesResponse
|
||||
{
|
||||
public GetActivitiesResponse()
|
||||
{
|
||||
}
|
||||
|
||||
public GetActivitiesResponse(IEnumerable<ActivityDescriptor> activities)
|
||||
{
|
||||
Activities = activities;
|
||||
}
|
||||
|
||||
[ProtoMember(1)] public IEnumerable<ActivityDescriptor> Activities { get; set; } = new System.Collections.Generic.List<ActivityDescriptor>();
|
||||
Task<IEnumerable<ActivityDescriptor>> GetActivitiesAsync(CallContext context = default);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,38 @@
|
|||
using System.ServiceModel;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Client.Models;
|
||||
using ProtoBuf;
|
||||
using ProtoBuf.Grpc;
|
||||
|
||||
namespace ElsaDashboard.Shared.Rpc
|
||||
{
|
||||
[ProtoContract]
|
||||
[ServiceContract]
|
||||
public interface IWorkflowDefinitionService
|
||||
{
|
||||
//ValueTask<ICollection<Test>> GetAsync(string workflowDefinitionId, VersionOptions version);
|
||||
Task<PagedList<WorkflowDefinition>> ListAsync(ListWorkflowDefinitionsRequest request, CallContext context = default);
|
||||
Task<WorkflowDefinition> GetByVersionIdAsync(string workflowDefinitionVersionId, CallContext context = default);
|
||||
}
|
||||
|
||||
public record Test(int a, bool b);
|
||||
[ProtoContract]
|
||||
public record ListWorkflowDefinitionsRequest
|
||||
{
|
||||
public ListWorkflowDefinitionsRequest(int? page = default, int? pageSize = default, VersionOptions? versionOptions = default)
|
||||
{
|
||||
Page = page;
|
||||
PageSize = pageSize;
|
||||
VersionOptions = versionOptions;
|
||||
}
|
||||
|
||||
[ProtoMember(1)] public int? Page { get; init; }
|
||||
[ProtoMember(2)] public int? PageSize { get; init; }
|
||||
[ProtoMember(3)] public VersionOptions? VersionOptions { get; init; }
|
||||
}
|
||||
|
||||
public static class WorkflowDefinitionServiceExtensions
|
||||
{
|
||||
public static Task<PagedList<WorkflowDefinition>> ListAsync(this IWorkflowDefinitionService service, int? page = default, int? pageSize = default, VersionOptions? versionOptions = default)
|
||||
{
|
||||
return service.ListAsync(new ListWorkflowDefinitionsRequest(page, pageSize, versionOptions));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,10 +17,6 @@ namespace ElsaDashboard.Shared.Surrogates
|
|||
SerializerSettings = new JsonSerializerSettings().ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
|
||||
}
|
||||
|
||||
public ActivityPropertyDescriptorSurrogate()
|
||||
{
|
||||
}
|
||||
|
||||
public ActivityPropertyDescriptorSurrogate(ActivityPropertyDescriptor value)
|
||||
{
|
||||
Name = value.Name;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ namespace ElsaDashboard.Shared.Surrogates
|
|||
{
|
||||
private static readonly IDictionary<Type, Type> SurrogateMapping = new Dictionary<Type, Type>
|
||||
{
|
||||
[typeof(ActivityPropertyDescriptor)] = typeof(ActivityPropertyDescriptorSurrogate)
|
||||
[typeof(ActivityPropertyDescriptor)] = typeof(ActivityPropertyDescriptorSurrogate),
|
||||
[typeof(VersionOptions)] = typeof(VersionOptionsSurrogate)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
using Elsa.Client.Models;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NodaTime;
|
||||
using NodaTime.Serialization.JsonNet;
|
||||
using ProtoBuf;
|
||||
|
||||
namespace ElsaDashboard.Shared.Surrogates
|
||||
{
|
||||
[ProtoContract(IgnoreListHandling = true)]
|
||||
public class VersionOptionsSurrogate
|
||||
{
|
||||
public VersionOptionsSurrogate()
|
||||
{
|
||||
}
|
||||
|
||||
public VersionOptionsSurrogate(VersionOptions value)
|
||||
{
|
||||
Value = value.ToString();
|
||||
}
|
||||
|
||||
[ProtoMember(1)] public string Value { get; } = default!;
|
||||
|
||||
public static implicit operator VersionOptions(VersionOptionsSurrogate surrogate) => VersionOptions.FromString(surrogate.Value);
|
||||
public static implicit operator VersionOptionsSurrogate(VersionOptions source) => new(source);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
|
@ -13,6 +15,7 @@ namespace Elsa.Samples.HelloWorldConsole
|
|||
.AddElsa()
|
||||
.AddConsoleActivities()
|
||||
.AddWorkflow<HelloWorld>()
|
||||
.AddAutoMapperProfiles<Program>()
|
||||
.BuildServiceProvider();
|
||||
|
||||
// Run startup actions (not needed when registering Elsa with a Host).
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
|
|||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1")]
|
||||
[Route("v{apiVersion:apiVersion}/workflow-definitions/{workflowDefinitionId}")]
|
||||
[Route("v{apiVersion:apiVersion}/workflow-definitions/{workflowDefinitionId}/{versionOptions}")]
|
||||
[Produces("application/json")]
|
||||
public class Get : Controller
|
||||
public class GetByDefinitionAndVersion : Controller
|
||||
{
|
||||
private readonly IWorkflowDefinitionManager _workflowDefinitionManager;
|
||||
private readonly IContentSerializer _serializer;
|
||||
|
||||
public Get(IWorkflowDefinitionManager workflowDefinitionManager, IContentSerializer serializer)
|
||||
public GetByDefinitionAndVersion(IWorkflowDefinitionManager workflowDefinitionManager, IContentSerializer serializer)
|
||||
{
|
||||
_workflowDefinitionManager = workflowDefinitionManager;
|
||||
_serializer = serializer;
|
||||
|
|
@ -33,13 +33,13 @@ namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
|
|||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[SwaggerOperation(
|
||||
Summary = "Returns a single workflow definition.",
|
||||
Description = "Returns a single workflow definition using the specified workflow definition ID and optional version options. When no version options are specified, the latest version is returned.",
|
||||
Description = "Returns a single workflow definition using the specified workflow definition ID and version options.",
|
||||
OperationId = "WorkflowDefinitions.Get",
|
||||
Tags = new[] { "WorkflowDefinitions" })
|
||||
]
|
||||
public async Task<IActionResult> Handle(string workflowDefinitionId, [FromQuery]VersionOptions? versionOptions = default, CancellationToken cancellationToken = default)
|
||||
public async Task<IActionResult> Handle(string workflowDefinitionId, VersionOptions versionOptions, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var workflowDefinition = await _workflowDefinitionManager.GetAsync(workflowDefinitionId, versionOptions ?? VersionOptions.Latest, cancellationToken);
|
||||
var workflowDefinition = await _workflowDefinitionManager.GetAsync(workflowDefinitionId, versionOptions, cancellationToken);
|
||||
return workflowDefinition == null ? (IActionResult) NotFound() : Json(workflowDefinition, _serializer.GetSettings());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
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 Swashbuckle.AspNetCore.Annotations;
|
||||
using Swashbuckle.AspNetCore.Filters;
|
||||
|
||||
namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1")]
|
||||
[Route("v{apiVersion:apiVersion}/workflow-definitions/{workflowDefinitionVersionId}")]
|
||||
[Produces("application/json")]
|
||||
public class GetByVersionId : Controller
|
||||
{
|
||||
private readonly IWorkflowDefinitionManager _workflowDefinitionManager;
|
||||
private readonly IContentSerializer _serializer;
|
||||
|
||||
public GetByVersionId(IWorkflowDefinitionManager workflowDefinitionManager, IContentSerializer serializer)
|
||||
{
|
||||
_workflowDefinitionManager = workflowDefinitionManager;
|
||||
_serializer = serializer;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(WorkflowDefinition))]
|
||||
[SwaggerResponseExample(StatusCodes.Status200OK, typeof(WorkflowDefinitionExample))]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[SwaggerOperation(
|
||||
Summary = "Returns a single workflow definition.",
|
||||
Description = "Returns a single workflow definition using the specified workflow definition version ID.",
|
||||
OperationId = "WorkflowDefinitions.Get",
|
||||
Tags = new[] { "WorkflowDefinitions" })
|
||||
]
|
||||
public async Task<IActionResult> Handle(string workflowDefinitionVersionId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var workflowDefinition = await _workflowDefinitionManager.GetByVersionIdAsync(workflowDefinitionVersionId, cancellationToken);
|
||||
return workflowDefinition == null ? (IActionResult) NotFound() : Json(workflowDefinition, _serializer.GetSettings());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
|
|||
else
|
||||
await _workflowPublisher.SaveDraftAsync(workflowDefinition, cancellationToken);
|
||||
|
||||
return CreatedAtAction("Handle", "Get", new {workflowDefinitionId = workflowDefinition.WorkflowDefinitionId, apiVersion = apiVersion.ToString()}, workflowDefinition);
|
||||
return CreatedAtAction("Handle", "GetByVersionId", new {workflowDefinitionVersionId = workflowDefinition.WorkflowDefinitionVersionId, apiVersion = apiVersion.ToString()}, workflowDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue