Skip to content

Commit

Permalink
Incremental work on FastEndpoints implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
sfmskywalker committed Aug 27, 2022
1 parent 078f21c commit d8d0834
Show file tree
Hide file tree
Showing 21 changed files with 275 additions and 281 deletions.
5 changes: 1 addition & 4 deletions src/bundles/Elsa.AllInOne.Web/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Elsa.Workflows.Api.Extensions;
using Elsa.Extensions;
using Elsa.Jobs.Extensions;
using Elsa.Http;
Expand All @@ -16,7 +15,6 @@
using Elsa.Workflows.Management.Extensions;
using Elsa.Workflows.Management.Serialization;
using Elsa.Workflows.Runtime.Extensions;
using FastEndpoints;

var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
Expand Down Expand Up @@ -69,9 +67,8 @@
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapManagementApiEndpoints();
app.UseElsaFastEndpoints();
app.UseHttpActivities();
app.UseFastEndpoints();
app.MapRazorPages();

app.Run();
41 changes: 1 addition & 40 deletions src/bundles/Elsa.WorkflowServer.Web/Program.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Elsa.ActivityDefinitions.EntityFrameworkCore.Extensions;
using Elsa.ActivityDefinitions.EntityFrameworkCore.Sqlite;
using Elsa.Extensions;
using Elsa.Features.Extensions;
using Elsa.Http;
using Elsa.Http.Extensions;
using Elsa.JavaScript.Activities;
Expand All @@ -21,7 +18,6 @@
using Elsa.Workflows.Core.Activities;
using Elsa.Workflows.Core.Activities.Flowchart.Activities;
using Elsa.Workflows.Core.Pipelines.WorkflowExecution.Components;
using Elsa.Workflows.Core.Serialization;
using Elsa.Workflows.Management.Extensions;
using Elsa.Workflows.Management.Services;
using Elsa.Workflows.Persistence.EntityFrameworkCore.Extensions;
Expand All @@ -30,7 +26,6 @@
using Elsa.Workflows.Runtime.Extensions;
using Elsa.WorkflowServer.Web.Jobs;
using FastEndpoints;
using FastEndpoints.Security;

var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
Expand Down Expand Up @@ -106,43 +101,9 @@
app.UseAuthentication();
app.UseAuthorization();

// Map Elsa API endpoint controllers.

// Deprecated.
app.MapManagementApiEndpoints();
app.MapLabelApiEndpoints();

// Use FastEndpoints middleware.

ValueTask<object?> DeserializeRequestAsync(HttpRequest httpRequest, Type modelType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken)
{
var serializerOptionsProvider = httpRequest.HttpContext.RequestServices.GetRequiredService<SerializerOptionsProvider>();
var options = serializerOptionsProvider.CreateApiOptions();

return serializerContext == null
? JsonSerializer.DeserializeAsync(httpRequest.Body, modelType, options, cancellationToken)
: JsonSerializer.DeserializeAsync(httpRequest.Body, modelType, serializerContext, cancellationToken);
}

Task SerializeRequestAsync(HttpResponse httpResponse, object dto, string contentType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken)
{
var serializerOptionsProvider = httpResponse.HttpContext.RequestServices.GetRequiredService<SerializerOptionsProvider>();
var options = serializerOptionsProvider.CreateApiOptions();

httpResponse.ContentType = contentType;
return serializerContext == null
? JsonSerializer.SerializeAsync(httpResponse.Body, dto, dto.GetType(), options, cancellationToken)
: JsonSerializer.SerializeAsync(httpResponse.Body, dto, dto.GetType(), serializerContext, cancellationToken);
}

app.UseFastEndpoints(config =>
{
config.Endpoints.RoutePrefix = "elsa/api";
config.Serializer.RequestDeserializer = DeserializeRequestAsync;
config.Serializer.ResponseSerializer = SerializeRequestAsync;
});

// Register Elsa middleware.
app.UseElsaFastEndpoints();
app.UseJsonSerializationErrorHandler();
app.UseHttpActivities();

Expand Down
1 change: 1 addition & 0 deletions src/common/Elsa.Api.Common/Elsa.Api.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\modules\Elsa.Workflows.Core\Elsa.Workflows.Core.csproj" />
<ProjectReference Include="..\Elsa.Features\Elsa.Features.csproj" />
<ProjectReference Include="..\Elsa.Models.Common\Elsa.Models.Common.csproj" />
</ItemGroup>
Expand Down
53 changes: 53 additions & 0 deletions src/common/Elsa.Api.Common/Extensions/WebApplicationExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Elsa.Workflows.Core.Serialization;
using FastEndpoints;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace Elsa.Extensions;

/// <summary>
/// Provides extension methods to add the FastEndpoints middleware configured for use with Elsa API endpoints.
/// </summary>
public static class WebApplicationExtensions
{
/// <summary>
/// Register the FastEndpoints middleware configured for use with with Elsa API endpoints.
/// </summary>
/// <param name="app"></param>
/// <param name="routePrefix">The route prefix to apply to Elsa API endpoints.</param>
/// <example>E.g. "elsa/api" will expose endpoints like this: "/elsa/api/workflow-definitions"</example>
public static WebApplication UseElsaFastEndpoints(this WebApplication app, string routePrefix = "elsa/api")
{
return app.UseFastEndpoints(config =>
{
config.Endpoints.RoutePrefix = routePrefix;
config.Serializer.RequestDeserializer = DeserializeRequestAsync;
config.Serializer.ResponseSerializer = SerializeRequestAsync;
});
}

static ValueTask<object?> DeserializeRequestAsync(HttpRequest httpRequest, Type modelType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken)
{
var serializerOptionsProvider = httpRequest.HttpContext.RequestServices.GetRequiredService<SerializerOptionsProvider>();
var options = serializerOptionsProvider.CreateApiOptions();

return serializerContext == null
? JsonSerializer.DeserializeAsync(httpRequest.Body, modelType, options, cancellationToken)
: JsonSerializer.DeserializeAsync(httpRequest.Body, modelType, serializerContext, cancellationToken);
}

static Task SerializeRequestAsync(HttpResponse httpResponse, object dto, string contentType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken)
{
var serializerOptionsProvider = httpResponse.HttpContext.RequestServices.GetRequiredService<SerializerOptionsProvider>();
var options = serializerOptionsProvider.CreateApiOptions();

httpResponse.ContentType = contentType;
return serializerContext == null
? JsonSerializer.SerializeAsync(httpResponse.Body, dto, dto.GetType(), options, cancellationToken)
: JsonSerializer.SerializeAsync(httpResponse.Body, dto, dto.GetType(), serializerContext, cancellationToken);
}

}
Original file line number Diff line number Diff line change
@@ -1,53 +1,29 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Workflows.Core.Serialization;
using Elsa.Workflows.Persistence.Services;
using FastEndpoints;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.BulkDelete;

public class BulkDelete : Endpoint<Request, Response>
{
private readonly IWorkflowInstanceStore _store;
private readonly SerializerOptionsProvider _serializerOptionsProvider;

public BulkDelete(IWorkflowInstanceStore store, SerializerOptionsProvider serializerOptionsProvider)
public BulkDelete(IWorkflowInstanceStore store)
{
_store = store;
_serializerOptionsProvider = serializerOptionsProvider;
}

public override void Configure()
{
Post("");
Post("/bulk-actions/delete/workflow-instances/by-id");
}

[Microsoft.AspNetCore.Mvc.HttpPost]
public async Task<IActionResult> HandleAsync(CancellationToken cancellationToken)
public override async Task<Response> ExecuteAsync(Request request, CancellationToken cancellationToken)
{
var serializerOptions = _serializerOptionsProvider.CreateApiOptions();
var model = await Request.ReadFromJsonAsync<Request>(serializerOptions, cancellationToken);
var count = await _store.DeleteManyAsync(model!.Ids, cancellationToken);
var count = await _store.DeleteManyAsync(request.Ids, cancellationToken);

return Json(new Response(count), serializerOptions);
return new Response(count);
}
}

public class Request
{
public ICollection<string> Ids { get; set; } = default!;
}

public class Response
{
public Response(int deletedCount)
{
DeletedCount = deletedCount;
}

[JsonPropertyName("deleted")] public int DeletedCount { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.BulkDelete;

public class Request
{
public ICollection<string> Ids { get; set; } = default!;
}

public class Response
{
public Response(int deletedCount)
{
DeletedCount = deletedCount;
}

[JsonPropertyName("deleted")] public int DeletedCount { get; }
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Workflows.Persistence.Services;
using FastEndpoints;

namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.Delete;

public class Delete : Endpoint<Request>
{
private readonly IWorkflowInstanceStore _store;
public Delete(IWorkflowInstanceStore store) => _store = store;

public override void Configure()
{
Delete("/workflow-instances/{id}");
}

public override async Task HandleAsync(Request request, CancellationToken cancellationToken)
{
var deleted = await _store.DeleteAsync(request.Id, cancellationToken);

if (deleted)
await SendNoContentAsync(cancellationToken);
else
await SendNotFoundAsync(cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.Delete;

public class Request
{
public string Id { get; set; } = default!;
}
30 changes: 0 additions & 30 deletions src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Get.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Workflows.Persistence.Services;
using FastEndpoints;

namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.Get;

public class Get : Endpoint<Request, Response, WorkflowInstanceMapper>
{
private readonly IWorkflowInstanceStore _store;

public Get(IWorkflowInstanceStore store)
{
_store = store;
}

public override void Configure()
{
Get("/workflow-instances/{id}");
}

public override async Task HandleAsync(Request request, CancellationToken cancellationToken)
{
var workflowInstance = await _store.FindByIdAsync(request.Id, cancellationToken);

if (workflowInstance == null)
await SendNotFoundAsync(cancellationToken);
else
await SendOkAsync(await Map.FromEntityAsync(workflowInstance), cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Elsa.Workflows.Persistence.Entities;
using FastEndpoints;

namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.Get;

public class WorkflowInstanceMapper : ResponseMapper<Response, WorkflowInstance>
{
public override Response FromEntity(WorkflowInstance e) => new()
{
DefinitionId = e.DefinitionId,
DefinitionVersionId = e.DefinitionVersionId,
Version = e.Version,
WorkflowState = e.WorkflowState,
Status = e.Status,
SubStatus = e.SubStatus,
CorrelationId = e.CorrelationId,
Name = e.Name,
Fault = e.Fault,
CancelledAt = e.CancelledAt,
CreatedAt = e.CreatedAt,
FaultedAt = e.FaultedAt,
FinishedAt = e.FinishedAt,
LastExecutedAt = e.LastExecutedAt
};
}
Loading

0 comments on commit d8d0834

Please sign in to comment.