From c62cac9ecb4743cc654bcfd5f22b8613ed7d97ac Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 18 Mar 2021 12:18:22 +0100 Subject: [PATCH] Dashboard hosting model improvements --- .../Models/ActivityPropertyDescriptor.cs | 3 ++- .../ActivityPropertyDescriptorSurrogate.cs | 4 ++-- .../Surrogates/RuntimeTypeModelExtensions.cs | 2 +- .../ElsaDashboard.WebAssembly.csproj | 4 +--- ...GrpcServicesServiceCollectionExtensions.cs | 6 ++++- .../Extensions/ServiceCollectionExtensions.cs | 11 ++++++++-- .../ElsaDashboardWebAssemblyOptions.cs | 14 ++++++++++++ .../BlazorRuntimeModel.cs | 2 +- .../ElsaDashboard.Samples.Monolith.csproj | 2 +- .../Pages/_Host.cshtml | 22 +++++++------------ .../Properties/launchSettings.json | 6 ++--- .../ElsaDashboard.Samples.Monolith/Startup.cs | 4 ++-- .../appsettings.json | 4 ++-- .../BlazorRuntimeModel.cs | 2 +- .../ElsaDashboard.Samples.Server.csproj | 2 +- .../Pages/_Host.cshtml | 21 +++++++----------- .../ElsaDashboard.Samples.Server/Program.cs | 2 +- .../Properties/launchSettings.json | 2 +- .../ElsaDashboard.Samples.Server/Startup.cs | 15 +++++++++---- .../Program.cs | 13 ++++++++--- .../Endpoints/WorkflowDefinitions/Save.cs | 2 +- 21 files changed, 85 insertions(+), 58 deletions(-) create mode 100644 src/dashboards/blazor/ElsaDashboard.WebAssembly/Options/ElsaDashboardWebAssemblyOptions.cs diff --git a/src/clients/Elsa.Client/Models/ActivityPropertyDescriptor.cs b/src/clients/Elsa.Client/Models/ActivityPropertyDescriptor.cs index 1521383c6..a79aff616 100644 --- a/src/clients/Elsa.Client/Models/ActivityPropertyDescriptor.cs +++ b/src/clients/Elsa.Client/Models/ActivityPropertyDescriptor.cs @@ -1,4 +1,5 @@ using System.Runtime.Serialization; +using Newtonsoft.Json.Linq; namespace Elsa.Client.Models { @@ -9,6 +10,6 @@ namespace Elsa.Client.Models [DataMember(Order = 2)] public string UIHint { get; set; } = default!; [DataMember(Order = 3)] public string? Label { get; set; } [DataMember(Order = 4)] public string? Hint { get; set; } - [DataMember(Order = 5)] public object? Options { get; set; } + [DataMember(Order = 5)] public JToken? Options { get; set; } } } \ No newline at end of file diff --git a/src/dashboards/blazor/ElsaDashboard.Shared/Surrogates/ActivityPropertyDescriptorSurrogate.cs b/src/dashboards/blazor/ElsaDashboard.Shared/Surrogates/ActivityPropertyDescriptorSurrogate.cs index 227a3051e..a590f7ba2 100644 --- a/src/dashboards/blazor/ElsaDashboard.Shared/Surrogates/ActivityPropertyDescriptorSurrogate.cs +++ b/src/dashboards/blazor/ElsaDashboard.Shared/Surrogates/ActivityPropertyDescriptorSurrogate.cs @@ -27,7 +27,7 @@ namespace ElsaDashboard.Shared.Surrogates Type = value.UIHint; Label = value.Label; Hint = value.Hint; - Options = value.Options != null ? JsonConvert.SerializeObject(value.Options, SerializerSettings) : default; + Options = value.Options?.ToString(Formatting.None); } [ProtoMember(1)] public string? Name { get; } @@ -44,7 +44,7 @@ namespace ElsaDashboard.Shared.Surrogates UIHint = surrogate.Type!, Hint = surrogate.Hint, Label = surrogate.Label, - Options = !string.IsNullOrEmpty(surrogate.Options) ? JsonConvert.DeserializeObject(surrogate.Options) : new JObject() + Options = surrogate.Options is null or "" ? default : JToken.Parse(surrogate.Options) } : default; diff --git a/src/dashboards/blazor/ElsaDashboard.Shared/Surrogates/RuntimeTypeModelExtensions.cs b/src/dashboards/blazor/ElsaDashboard.Shared/Surrogates/RuntimeTypeModelExtensions.cs index 9abffa5c5..3a635b489 100644 --- a/src/dashboards/blazor/ElsaDashboard.Shared/Surrogates/RuntimeTypeModelExtensions.cs +++ b/src/dashboards/blazor/ElsaDashboard.Shared/Surrogates/RuntimeTypeModelExtensions.cs @@ -20,7 +20,7 @@ namespace ElsaDashboard.Shared.Surrogates }; /// - /// Register all NodaTime surrogate types with the protobuf runtime model. + /// Register all surrogate types with the protobuf runtime model. /// public static RuntimeTypeModel AddElsaGrpcSurrogates(this RuntimeTypeModel runtimeTypeModel) { diff --git a/src/dashboards/blazor/ElsaDashboard.WebAssembly/ElsaDashboard.WebAssembly.csproj b/src/dashboards/blazor/ElsaDashboard.WebAssembly/ElsaDashboard.WebAssembly.csproj index f0dde1d5d..2f2a7d8f8 100644 --- a/src/dashboards/blazor/ElsaDashboard.WebAssembly/ElsaDashboard.WebAssembly.csproj +++ b/src/dashboards/blazor/ElsaDashboard.WebAssembly/ElsaDashboard.WebAssembly.csproj @@ -1,4 +1,4 @@ - + @@ -15,8 +15,6 @@ - - diff --git a/src/dashboards/blazor/ElsaDashboard.WebAssembly/Extensions/GrpcServicesServiceCollectionExtensions.cs b/src/dashboards/blazor/ElsaDashboard.WebAssembly/Extensions/GrpcServicesServiceCollectionExtensions.cs index c4723b3f6..af0d67f70 100644 --- a/src/dashboards/blazor/ElsaDashboard.WebAssembly/Extensions/GrpcServicesServiceCollectionExtensions.cs +++ b/src/dashboards/blazor/ElsaDashboard.WebAssembly/Extensions/GrpcServicesServiceCollectionExtensions.cs @@ -1,10 +1,12 @@ using System; using System.Net.Http; +using ElsaDashboard.WebAssembly.Options; using Grpc.Core; using Grpc.Net.Client; using Grpc.Net.Client.Web; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using ProtoBuf.Grpc.Client; namespace ElsaDashboard.WebAssembly.Extensions @@ -17,8 +19,10 @@ namespace ElsaDashboard.WebAssembly.Extensions private static GrpcChannel CreateGrpcChannel(IServiceProvider sp) { var navigationManager = sp.GetRequiredService(); - var backendUrl = navigationManager.BaseUri; + var options = sp.GetRequiredService>(); + var backendUrl = options.Value.BackendUrl?.ToString() ?? navigationManager.BaseUri; var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler())); + //var tokenManager = sp.GetRequiredService(); var credentials = CallCredentials.FromInterceptor( diff --git a/src/dashboards/blazor/ElsaDashboard.WebAssembly/Extensions/ServiceCollectionExtensions.cs b/src/dashboards/blazor/ElsaDashboard.WebAssembly/Extensions/ServiceCollectionExtensions.cs index fe141a6de..7fd643cf4 100644 --- a/src/dashboards/blazor/ElsaDashboard.WebAssembly/Extensions/ServiceCollectionExtensions.cs +++ b/src/dashboards/blazor/ElsaDashboard.WebAssembly/Extensions/ServiceCollectionExtensions.cs @@ -1,5 +1,7 @@ -using ElsaDashboard.Shared.Rpc; +using System; +using ElsaDashboard.Shared.Rpc; using ElsaDashboard.Shared.Surrogates; +using ElsaDashboard.WebAssembly.Options; using Microsoft.Extensions.DependencyInjection; using ProtoBuf.Meta; @@ -7,8 +9,13 @@ namespace ElsaDashboard.WebAssembly.Extensions { public static class ServiceCollectionExtensions { - public static IServiceCollection AddElsaDashboardBackend(this IServiceCollection services) + public static IServiceCollection AddElsaDashboardBackend(this IServiceCollection services, Action? configure = default) { + if (configure != null) + services.Configure(configure); + else + services.AddOptions(); + RuntimeTypeModel.Default.AddElsaGrpcSurrogates(); RuntimeTypeModel.Default.AddNodaTime(); return services diff --git a/src/dashboards/blazor/ElsaDashboard.WebAssembly/Options/ElsaDashboardWebAssemblyOptions.cs b/src/dashboards/blazor/ElsaDashboard.WebAssembly/Options/ElsaDashboardWebAssemblyOptions.cs new file mode 100644 index 000000000..aa5b73bd1 --- /dev/null +++ b/src/dashboards/blazor/ElsaDashboard.WebAssembly/Options/ElsaDashboardWebAssemblyOptions.cs @@ -0,0 +1,14 @@ +using System; + +namespace ElsaDashboard.WebAssembly.Options +{ + public class ElsaDashboardWebAssemblyOptions + { + /// + /// The URL of the dashboard's backend. + /// When the web assembly project is hosted from an ASP.NET Core host, that host will be used by default. + /// When the web assembly project on the other hand is hosted directly from e.g. blob storage, the URL to the gRPC backend needs to be specified explicitly. + /// + public Uri? BackendUrl { get; set; } + } +} \ No newline at end of file diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/BlazorRuntimeModel.cs b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/BlazorRuntimeModel.cs index 473030a3f..226ca5471 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/BlazorRuntimeModel.cs +++ b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/BlazorRuntimeModel.cs @@ -3,6 +3,6 @@ public enum BlazorRuntimeModel { Server, - Browser + WebAssembly } } \ No newline at end of file diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/ElsaDashboard.Samples.Monolith.csproj b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/ElsaDashboard.Samples.Monolith.csproj index 8fc84e22d..1833b4648 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/ElsaDashboard.Samples.Monolith.csproj +++ b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/ElsaDashboard.Samples.Monolith.csproj @@ -19,9 +19,9 @@ - + diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Pages/_Host.cshtml b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Pages/_Host.cshtml index d954f3de8..5dce15cf6 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Pages/_Host.cshtml +++ b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Pages/_Host.cshtml @@ -24,23 +24,17 @@ - - - +@if (Program.RuntimeModel == BlazorRuntimeModel.Server) +{ + +} +else +{ +
Loading...
+}
diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Properties/launchSettings.json b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Properties/launchSettings.json index a240ad590..fc5eb487f 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Properties/launchSettings.json +++ b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Properties/launchSettings.json @@ -3,8 +3,8 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:54938/", - "sslPort": 44399 + "applicationUrl": "https://localhost:11000/", + "sslPort": 11000 } }, "profiles": { @@ -21,7 +21,7 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" + "applicationUrl": "https://localhost:11000" } } } \ No newline at end of file diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Startup.cs b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Startup.cs index 89e1d6970..8ee158610 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Startup.cs +++ b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Startup.cs @@ -71,7 +71,7 @@ namespace ElsaDashboard.Samples.Monolith app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Elsa")); - if (Program.RuntimeModel == BlazorRuntimeModel.Browser) + if (Program.RuntimeModel == BlazorRuntimeModel.WebAssembly) app.UseWebAssemblyDebugging(); } else @@ -80,7 +80,7 @@ namespace ElsaDashboard.Samples.Monolith app.UseHsts(); } - if (Program.RuntimeModel == BlazorRuntimeModel.Browser) + if (Program.RuntimeModel == BlazorRuntimeModel.WebAssembly) app.UseBlazorFrameworkFiles(); app.UseStaticFiles(); diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/appsettings.json b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/appsettings.json index a8eb2d10d..31078274c 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/appsettings.json +++ b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/appsettings.json @@ -9,7 +9,7 @@ "AllowedHosts": "*", "Elsa": { "Http": { - "BaseUrl": "https://localhost:3956" + "BaseUrl": "https://localhost:11000" }, "Smtp": { "Host": "localhost", @@ -18,7 +18,7 @@ } }, "Dashboard": { - "ElsaServerUrl": "https://localhost:3956" + "ElsaServerUrl": "https://localhost:11000" }, "Telnyx": { "ApiUrl": " https://api.telnyx.com", diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Server/BlazorRuntimeModel.cs b/src/samples/dashboard/ElsaDashboard.Samples.Server/BlazorRuntimeModel.cs index a9db59b1e..b84d392a6 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Server/BlazorRuntimeModel.cs +++ b/src/samples/dashboard/ElsaDashboard.Samples.Server/BlazorRuntimeModel.cs @@ -3,6 +3,6 @@ public enum BlazorRuntimeModel { Server, - Browser + WebAssembly } } \ No newline at end of file diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Server/ElsaDashboard.Samples.Server.csproj b/src/samples/dashboard/ElsaDashboard.Samples.Server/ElsaDashboard.Samples.Server.csproj index d15494d79..6207dc866 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Server/ElsaDashboard.Samples.Server.csproj +++ b/src/samples/dashboard/ElsaDashboard.Samples.Server/ElsaDashboard.Samples.Server.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Server/Pages/_Host.cshtml b/src/samples/dashboard/ElsaDashboard.Samples.Server/Pages/_Host.cshtml index 5bbcdd807..059e8ec27 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Server/Pages/_Host.cshtml +++ b/src/samples/dashboard/ElsaDashboard.Samples.Server/Pages/_Host.cshtml @@ -25,22 +25,17 @@ - - +@if (Program.RuntimeModel == BlazorRuntimeModel.Server) +{ + +} +else +{ +
Loading...
+}
diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Server/Program.cs b/src/samples/dashboard/ElsaDashboard.Samples.Server/Program.cs index f4f64a116..8f4edc261 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Server/Program.cs +++ b/src/samples/dashboard/ElsaDashboard.Samples.Server/Program.cs @@ -6,7 +6,7 @@ namespace ElsaDashboard.Samples.Server { public class Program { - public static BlazorRuntimeModel RuntimeModel => BlazorRuntimeModel.Browser; + public static BlazorRuntimeModel RuntimeModel => BlazorRuntimeModel.WebAssembly; public static RenderMode RenderMode => RuntimeModel == BlazorRuntimeModel.Server ? RenderMode.ServerPrerendered : RenderMode.WebAssemblyPrerendered; public static void Main(string[] args) diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Server/Properties/launchSettings.json b/src/samples/dashboard/ElsaDashboard.Samples.Server/Properties/launchSettings.json index d1926f8b9..7af442401 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Server/Properties/launchSettings.json +++ b/src/samples/dashboard/ElsaDashboard.Samples.Server/Properties/launchSettings.json @@ -5,7 +5,7 @@ "dotnetRunMessages": "true", "launchBrowser": false, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:53451", + "applicationUrl": "https://localhost:54938", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Server/Startup.cs b/src/samples/dashboard/ElsaDashboard.Samples.Server/Startup.cs index 1deaacb29..7dd448275 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Server/Startup.cs +++ b/src/samples/dashboard/ElsaDashboard.Samples.Server/Startup.cs @@ -1,5 +1,6 @@ using System; using ElsaDashboard.Backend.Extensions; +using MediatR; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; @@ -22,9 +23,13 @@ namespace ElsaDashboard.Samples.Server public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); - services.AddElsaDashboardUI(); - services.AddElsaDashboardBackend(options => options.ServerUrl = Configuration.GetValue("Elsa:HostUrl")); + var elsaServerUrl = Configuration.GetValue("Elsa:HostUrl"); + + services + .AddElsaDashboardUI(options => options.ElsaServerUrl = elsaServerUrl) + .AddElsaDashboardBackend(options => options.ServerUrl = elsaServerUrl); + if (Program.RuntimeModel == BlazorRuntimeModel.Server) services.AddServerSideBlazor(options => { @@ -39,7 +44,7 @@ namespace ElsaDashboard.Samples.Server { app.UseDeveloperExceptionPage(); - if (Program.RuntimeModel == BlazorRuntimeModel.Browser) + if (Program.RuntimeModel == BlazorRuntimeModel.WebAssembly) app.UseWebAssemblyDebugging(); } else @@ -48,17 +53,19 @@ namespace ElsaDashboard.Samples.Server app.UseHsts(); } - if (Program.RuntimeModel == BlazorRuntimeModel.Browser) + if (Program.RuntimeModel == BlazorRuntimeModel.WebAssembly) app.UseBlazorFrameworkFiles(); app.UseStaticFiles(); app.UseRouting(); app.UseElsaGrpcServices(); + app.UseEndpoints(endpoints => { if (Program.RuntimeModel == BlazorRuntimeModel.Server) endpoints.MapBlazorHub(); + endpoints.MapRazorPages(); endpoints.MapFallbackToPage("/_Host"); }); } diff --git a/src/samples/dashboard/ElsaDashboard.Samples.WebAssembly/Program.cs b/src/samples/dashboard/ElsaDashboard.Samples.WebAssembly/Program.cs index c744da845..8134fc5a7 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.WebAssembly/Program.cs +++ b/src/samples/dashboard/ElsaDashboard.Samples.WebAssembly/Program.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using System; using System.Net.Http; using System.Threading.Tasks; +using ElsaDashboard.Application; using ElsaDashboard.WebAssembly.Extensions; namespace ElsaDashboard.Samples.WebAssembly @@ -14,9 +15,15 @@ namespace ElsaDashboard.Samples.WebAssembly var builder = WebAssemblyHostBuilder.CreateDefault(args); var services = builder.Services; - //builder.RootComponents.Add("#app"); - services.AddScoped(sp => new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)}); - services.AddElsaDashboardUI(); + builder.RootComponents.Add("#app"); + + // Change the Elsa Server URL to point to wherever you have hosted Elsa. In this example, Elsa is either hosted by "ElsaDashboard.Samples.Monolith" or "Elsa.Samples.Server.Host" + services.AddElsaDashboardUI(options => options.ElsaServerUrl = new Uri("https://localhost:11000")); + + // When the web assembly project is hosted directly from e.g. blob storage, the URL to the gRPC backend needs to be specified explicitly. + // When hosted from ElsaDashboard.Samples.Monolith or ElsaDashboard.Samples.Server, we don't need to specify a backend URL. + //services.AddElsaDashboardBackend(options => options.BackendUrl = new Uri("https://localhost:11000")); + services.AddElsaDashboardBackend(); await builder.Build().RunAsync(); diff --git a/src/server/Elsa.Server.Api/Endpoints/WorkflowDefinitions/Save.cs b/src/server/Elsa.Server.Api/Endpoints/WorkflowDefinitions/Save.cs index 2a23c5112..96541580c 100644 --- a/src/server/Elsa.Server.Api/Endpoints/WorkflowDefinitions/Save.cs +++ b/src/server/Elsa.Server.Api/Endpoints/WorkflowDefinitions/Save.cs @@ -35,7 +35,7 @@ namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions OperationId = "WorkflowDefinitions.Post", Tags = new[] { "WorkflowDefinitions" }) ] - public async Task> Handle(SaveRequest request, ApiVersion apiVersion, CancellationToken cancellationToken) + public async Task> Handle([FromBody]SaveRequest request, [FromRoute]ApiVersion apiVersion, CancellationToken cancellationToken) { var workflowDefinitionId = request.WorkflowDefinitionId; var workflowDefinition = !string.IsNullOrWhiteSpace(workflowDefinitionId) ? await _workflowPublisher.GetDraftAsync(workflowDefinitionId, cancellationToken) : default;