Updated HttpRequestTrigger activity to support reading request content body

This commit is contained in:
Sipke Schoorstra 2019-01-02 00:09:13 +01:00
parent cb8a66e116
commit 6731968803
22 changed files with 116 additions and 32 deletions

View file

@ -21,11 +21,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Abstractions", "src\co
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Persistence.Abstractions", "src\core\Elsa.Persistence.Abstractions\Elsa.Persistence.Abstractions.csproj", "{A5829896-89C5-4C4E-ADC7-C806C8077316}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esla.Persistence.InMemory", "src\core\Esla.Persistence.InMemory\Esla.Persistence.InMemory.csproj", "{E93A1A6B-708F-4DCC-A399-1B8EC7682F92}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Runtime", "src\core\Elsa.Runtime\Elsa.Runtime.csproj", "{3C1DCAA2-D77E-4F96-9899-DA8E0E0C757F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esla.Runtime", "src\core\Esla.Runtime\Esla.Runtime.csproj", "{3C1DCAA2-D77E-4F96-9899-DA8E0E0C757F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esla.Runtime.Abstractions", "src\core\Esla.Runtime.Abstractions\Esla.Runtime.Abstractions.csproj", "{792362BF-D26D-46E9-8970-E5668941AA75}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Runtime.Abstractions", "src\core\Elsa.Runtime.Abstractions\Elsa.Runtime.Abstractions.csproj", "{792362BF-D26D-46E9-8970-E5668941AA75}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{35F44BE9-13D0-417C-A01A-F0787BEE6DC3}"
EndProject
@ -81,10 +79,6 @@ Global
{A5829896-89C5-4C4E-ADC7-C806C8077316}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5829896-89C5-4C4E-ADC7-C806C8077316}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5829896-89C5-4C4E-ADC7-C806C8077316}.Release|Any CPU.Build.0 = Release|Any CPU
{E93A1A6B-708F-4DCC-A399-1B8EC7682F92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E93A1A6B-708F-4DCC-A399-1B8EC7682F92}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E93A1A6B-708F-4DCC-A399-1B8EC7682F92}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E93A1A6B-708F-4DCC-A399-1B8EC7682F92}.Release|Any CPU.Build.0 = Release|Any CPU
{3C1DCAA2-D77E-4F96-9899-DA8E0E0C757F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C1DCAA2-D77E-4F96-9899-DA8E0E0C757F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C1DCAA2-D77E-4F96-9899-DA8E0E0C757F}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -157,7 +151,6 @@ Global
{300EE2D5-54C5-46F2-AD03-BB43589EA074} = {35F44BE9-13D0-417C-A01A-F0787BEE6DC3}
{3130C574-557C-4DA2-933E-AED7A9EBE48A} = {35F44BE9-13D0-417C-A01A-F0787BEE6DC3}
{A5829896-89C5-4C4E-ADC7-C806C8077316} = {35F44BE9-13D0-417C-A01A-F0787BEE6DC3}
{E93A1A6B-708F-4DCC-A399-1B8EC7682F92} = {35F44BE9-13D0-417C-A01A-F0787BEE6DC3}
{3C1DCAA2-D77E-4F96-9899-DA8E0E0C757F} = {35F44BE9-13D0-417C-A01A-F0787BEE6DC3}
{792362BF-D26D-46E9-8970-E5668941AA75} = {35F44BE9-13D0-417C-A01A-F0787BEE6DC3}
{35F44BE9-13D0-417C-A01A-F0787BEE6DC3} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}

View file

@ -14,5 +14,11 @@ namespace Elsa.Activities.Http.Activities
/// The HTTP method that triggers this activity.
/// </summary>
public string Method { get; set; }
/// <summary>
/// A value indicating whether the HTTP request content body should be read and stored as part of the HTTP request model.
/// The stored format depends on the content-type header.
/// </summary>
public bool ReadContent { get; set; }
}
}

View file

@ -13,16 +13,16 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\core\Elsa.Core\Elsa.Core.csproj"/>
<ProjectReference Include="..\..\core\Elsa.Persistence.Abstractions\Elsa.Persistence.Abstractions.csproj"/>
<ProjectReference Include="..\..\core\Esla.Runtime.Abstractions\Esla.Runtime.Abstractions.csproj"/>
<ProjectReference Include="..\..\core\Elsa.Core\Elsa.Core.csproj" />
<ProjectReference Include="..\..\core\Elsa.Persistence.Abstractions\Elsa.Persistence.Abstractions.csproj" />
<ProjectReference Include="..\..\core\Elsa.Runtime.Abstractions\Elsa.Runtime.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.AsyncInitialization" Version="1.1.1"/>
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.0"/>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0"/>
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.2.0"/>
<PackageReference Include="AspNetCore.AsyncInitialization" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.2.0" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,26 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Elsa.Activities.Http.Extensions
{
public static class HttpRequestExtensions
{
public static async Task<string> ReadBodyAsync(this HttpRequest request)
{
if (request.Body == null || request.Body == Stream.Null)
{
return null;
}
request.EnableBuffering();
using (var reader = new StreamReader(request.Body))
{
var content = await reader.ReadToEndAsync();
request.Body.Seek(0, SeekOrigin.Begin);
return content;
}
}
}
}

View file

@ -1,4 +1,10 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Activities.Http.Activities;
using Elsa.Activities.Http.Extensions;
using Elsa.Activities.Http.Models;
using Elsa.Handlers;
using Elsa.Models;
using Elsa.Results;
@ -29,14 +35,30 @@ namespace Elsa.Activities.Http.Handlers
public override LocalizedString Description => T["Triggers when an incoming HTTP request is received."];
protected override LocalizedString GetEndpoint() => T["Done"];
protected override ActivityExecutionResult OnExecute(HttpRequestTrigger activity, WorkflowExecutionContext workflowContext)
protected override async Task<ActivityExecutionResult> OnExecuteAsync(HttpRequestTrigger activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
{
var request = httpContextAccessor.HttpContext.Request;
var requestData = new
var model = new HttpRequestModel
{
Path = request.Path
Path = new Uri(request.Path.ToString(), UriKind.Relative),
QueryString = request.Query.ToDictionary(x => x.Key, x => x.Value),
Headers = request.Headers.ToDictionary(x => x.Key, x => x.Value),
Method = request.Method
};
workflowContext.CurrentScope.LastResult = requestData;
if (activity.ReadContent)
{
if (request.HasFormContentType)
{
model.Form = (await request.ReadFormAsync(cancellationToken)).ToDictionary(x => x.Key, x => x.Value);
}
else
{
model.Content = await request.ReadBodyAsync();
}
}
workflowContext.CurrentScope.LastResult = model;
return TriggerEndpoint("Done");
}
}

View file

@ -23,7 +23,6 @@ namespace Elsa.Activities.Http.Middleware
public async Task InvokeAsync(HttpContext context, IWorkflowHost workflowHost, IHttpWorkflowCache httpWorkflowCache)
{
await workflowHost.TriggerWorkflowAsync("a", Variables.Empty, CancellationToken.None);
var requestPath = new Uri(context.Request.Path.ToString(), UriKind.Relative);
var cancellationToken = context.RequestAborted;
var workflows = await httpWorkflowCache.GetWorkflowsByPathAsync(requestPath, cancellationToken).ToListAsync();

View file

@ -0,0 +1,10 @@
using System.Collections.Generic;
using Microsoft.Extensions.Primitives;
namespace Elsa.Activities.Http.Models
{
public class HttpRequestHeaders : Dictionary<string, StringValues>
{
public string ContentType => this["content-type"];
}
}

View file

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Primitives;
namespace Elsa.Activities.Http.Models
{
public class HttpRequestModel
{
public Uri Path { get; set; }
public string Method { get; set; }
public IDictionary<string, StringValues> QueryString { get; set; }
public IDictionary<string, StringValues> Headers { get; set; }
public string Content { get; set; }
public IDictionary<string, StringValues> Form { get; set; }
}
}

View file

@ -11,7 +11,7 @@ namespace Elsa.Persistence.FileSystem.Extensions
public static IServiceCollection AddWorkflowsFileSystemPersistence(this IServiceCollection services, IConfiguration configuration)
{
services.TryAddSingleton<IFileSystem, System.IO.Abstractions.FileSystem>();
services.AddSingleton<IWorkflowStore, FileSystemWorkflowStore>();
services.AddScoped<IWorkflowStore, FileSystemWorkflowStore>();
services.Configure<FileSystemStoreOptions>(configuration);
return services;

View file

@ -15,7 +15,7 @@
<ItemGroup>
<ProjectReference Include="..\Elsa.Core\Elsa.Core.csproj" />
<ProjectReference Include="..\Elsa.Persistence.Abstractions\Elsa.Persistence.Abstractions.csproj" />
<ProjectReference Include="..\Esla.Runtime.Abstractions\Esla.Runtime.Abstractions.csproj" />
<ProjectReference Include="..\Elsa.Runtime.Abstractions\Elsa.Runtime.Abstractions.csproj" />
</ItemGroup>
</Project>

View file

@ -1,13 +1,14 @@
using Elsa.Runtime;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Runtime.Extensions
namespace Esla.Runtime.Extensions
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddWorkflowsHost(this IServiceCollection services)
{
return services
.AddSingleton<IWorkflowHost, WorkflowHost>();
.AddScoped<IWorkflowHost, WorkflowHost>();
}
}
}

View file

@ -24,7 +24,7 @@
<ProjectReference Include="..\..\activities\Elsa.Activities.Primitives\Elsa.Activities.Primitives.csproj" />
<ProjectReference Include="..\..\core\Elsa.Core\Elsa.Core.csproj" />
<ProjectReference Include="..\..\core\Elsa.Persistence.FileSystem\Elsa.Persistence.FileSystem.csproj" />
<ProjectReference Include="..\..\core\Esla.Runtime\Esla.Runtime.csproj" />
<ProjectReference Include="..\..\core\Elsa.Runtime\Elsa.Runtime.csproj" />
</ItemGroup>
</Project>

View file

@ -3,7 +3,7 @@ using Elsa.Activities.Http.Extensions;
using Elsa.Activities.Primitives.Extensions;
using Elsa.Extensions;
using Elsa.Persistence.FileSystem.Extensions;
using Elsa.Runtime.Extensions;
using Esla.Runtime.Extensions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;

View file

@ -6,7 +6,7 @@
},
"AllowedHosts": "*",
"FileStore": {
"RootDirectory": "C:\\Activities\\Projects\\Elsa.Web.BootstrapTheme\\src\\web\\dashboard\\App_Data\\Sites\\Default\\workflows\\workflows",
"RootDirectory": "C:\\Activities\\Projects\\Elsa\\src\\web\\dashboard\\Elsa.Web.Dashboard\\App_Data\\Sites\\Default\\elsa\\workflows",
"Format": "YAML"
}
}

View file

@ -10,12 +10,14 @@ namespace Elsa.Web.Activities.Http.Drivers
{
model.Path = activity.Path;
model.Method = activity.Method;
model.ReadContent = activity.ReadContent;
}
protected override void UpdateActivity(HttpRequestTriggerViewModel model, HttpRequestTrigger activity)
{
activity.Path = model.Path;
activity.Method = model.Method;
activity.ReadContent = model.ReadContent;
}
}
}

View file

@ -14,6 +14,8 @@ namespace Elsa.Web.Activities.Http.ViewModels
[Required]
public string Method { get; set; }
public bool ReadContent { get; set; }
public ICollection<SelectListItem> GetAvailableHttpMethods()
{
var availableHttpMethods = new[] { "GET", "POST", "PUT", "DELETE", "OPTIONS" };

View file

@ -11,6 +11,12 @@
<span asp-validation-for="Path" class="text-danger"></span>
<small class="form-text text-muted">@T["The relative path that will trigger this activity. Example: '/webhooks/my'"]</small>
</div>
<div class="form-group">
<label asp-for="Method">@T["Method"]</label>
<select class="form-control" asp-for="Method" asp-items="Model.GetAvailableHttpMethods()"></select>
<span asp-validation-for="Method" class="text-danger"></span>
<small class="form-text text-muted">@T["The HTTP method to match.'"]</small>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input"/>
@ -19,8 +25,9 @@
<small class="form-text text-muted">@T["Check to generate a nonce that needs to be included when invoking the URL.'"]</small>
</div>
<div class="form-group">
<label asp-for="Method">@T["Method"]</label>
<select class="form-control" asp-for="Method" asp-items="Model.GetAvailableHttpMethods()"></select>
<span asp-validation-for="Method" class="text-danger"></span>
<small class="form-text text-muted">@T["The HTTP method to match.'"]</small>
<div class="custom-control custom-checkbox">
<input asp-for="ReadContent" type="checkbox" class="custom-control-input"/>
<label asp-for="ReadContent" class="custom-control-label">@T["Read content"]</label>
</div>
<small class="form-text text-muted">@T["Check to read the request body. If the content type represents a form, the body is parsed as a collection of key/value pairs. Otherwise the body is stored as a raw string'"]</small>
</div>