Skip to content

Commit

Permalink
Merge branch 'vnext' into raspberry
Browse files Browse the repository at this point in the history
  • Loading branch information
sfmskywalker committed Jan 20, 2025
2 parents e61c5c0 + 7edbdb9 commit dc09980
Show file tree
Hide file tree
Showing 96 changed files with 1,182 additions and 830 deletions.
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
- package-ecosystem: "NuGet" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
17 changes: 1 addition & 16 deletions .github/workflows/packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,27 +98,12 @@ jobs:
- name: Publish to feedz.io
run: dotnet nuget push *.nupkg -k ${{ secrets.FEEDZ_API_KEY }} -s ${{ env.feedz_feed_source }} --skip-duplicate

publish_preview_nuget:
name: Publish preview to nuget.org
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
if: ${{ github.event_name == 'prereleased' && github.event.action == 'published' }}
steps:
- name: Download Packages
uses: actions/[email protected]
with:
name: elsa-nuget-packages

- name: Publish to nuget.org
run: dotnet nuget push *.nupkg -k ${{ secrets.NUGET_API_KEY }} -s ${{ env.nuget_feed_source }} --skip-duplicate

publish_nuget:
name: Publish release to nuget.org
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
if: ${{ github.event_name == 'release' && github.event.action == 'published' }}
if: ${{ github.event.action == 'published' }}
steps:
- name: Download Packages
uses: actions/[email protected]
Expand Down
79 changes: 40 additions & 39 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<PackageVersion Include="Grpc.Tools" Version="2.68.1"/>
<PackageVersion Include="Hangfire" Version="1.8.17"/>
<PackageVersion Include="Hangfire.MemoryStorage" Version="1.8.1.1"/>
<PackageVersion Include="Hangfire.PostgreSql" Version="1.20.10"/>
<PackageVersion Include="Hangfire.Storage.SQLite" Version="0.4.2"/>
<PackageVersion Include="Humanizer.Core" Version="2.14.1"/>
<PackageVersion Include="IronCompress" Version="1.6.3"/>
Expand Down Expand Up @@ -86,7 +87,7 @@
<PackageVersion Include="Proto.Persistence.Sqlite" Version="1.7.0"/>
<PackageVersion Include="Proto.Persistence.SqlServer" Version="1.7.0"/>
<PackageVersion Include="Proto.Remote" Version="1.7.0"/>
<PackageVersion Include="pythonnet" Version="3.0.5"/>
<PackageVersion Include="pythonnet" Version="3.1.0-preview2024-09-06"/>
<PackageVersion Include="Quartz.Extensions.Hosting" Version="3.13.1"/>
<PackageVersion Include="Quartz.Serialization.Json" Version="3.13.1"/>
<PackageVersion Include="Scrutor" Version="5.0.2"/>
Expand All @@ -112,44 +113,44 @@
<PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="8.0.1"/>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="8.0.11" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.11" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.11" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.11" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="8.0.11" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.11" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.11" />
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="8.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.11" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="8.0.11" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
<PackageVersion Include="Npgsql" Version="8.0.6" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.11" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="8.23.60" />
<PackageVersion Include="Polly" Version="8.4.2" />
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
<PackageVersion Include="Refit" Version="8.0.0" />
<PackageVersion Include="Refit.HttpClientFactory" Version="8.0.0" />
<PackageVersion Include="System.Formats.Asn1" Version="8.0.1" />
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="8.0.11"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.11"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.11"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.11"/>
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="8.0.11"/>
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.11"/>
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.11"/>
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="8.0.11"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.11"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.11"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.11"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0"/>
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0"/>
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0"/>
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1"/>
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1"/>
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2"/>
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="8.0.2"/>
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="8.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2"/>
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2"/>
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0"/>
<PackageVersion Include="Npgsql" Version="8.0.6"/>
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.11"/>
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="8.23.60"/>
<PackageVersion Include="Polly" Version="8.4.2"/>
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2"/>
<PackageVersion Include="Refit" Version="8.0.0"/>
<PackageVersion Include="Refit.HttpClientFactory" Version="8.0.0"/>
<PackageVersion Include="System.Formats.Asn1" Version="8.0.1"/>
<PackageVersion Include="System.Text.Json" Version="8.0.5"/>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.0"/>
Expand Down
20 changes: 4 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,13 @@ By default, you can access http://localhost:13000 and log in with:

## Documentation

For comprehensive documentation and to get started with Elsa, please visit the [Elsa Documentation Website](https://v3.elsaworkflows.io/).
[Elsa Documentation Website](https://docs.elsaworkflows.io/).

## Known Issues and Limitations

Elsa is continually evolving, and while it offers powerful capabilities, there are some known limitations and ongoing work:

- Documentation is still a work in progress.
- The designer is not yet fully embeddable in other applications; this feature is planned for a future release.
- C# and Python expressions are not yet fully tested.
- Bulk Dispatch Workflows is a new activity and not yet fully tested.
- Input/Output is not yet implemented in the Workflow Instance Viewer.
- Starting workflows from the designer is currently supported only for workflows that do not require input and do not start with a trigger; this is planned for a future release.
- The designer currently only supports Flowchart activities. Support for Sequence and StateMachine activities is planned for a future release.
Expand All @@ -90,15 +87,7 @@ Elsa offers a wide range of features for building and executing workflows, inclu

## Roadmap

The following features are planned for future releases of Elsa:

- [ ] Multi-tenancy
- [ ] State Machine activity
- [ ] Designer support for Sequence activity & StateMachine activity
- [ ] BPMN 2.0 support
- [ ] DMN support
- [ ] Workflow migration to new versions via UI
- [ ] Capsules ("hot" deployable workflow packages containing activities and configuration)
See #3232

## Use Cases

Expand All @@ -109,7 +98,7 @@ Elsa can be used in a variety of scenarios, including:
- Scheduled workflows such as sending daily reports.
- Event-driven workflows such as sending welcome emails when a user signs up.

## Programmatic Workflows
## Coding Workflows

Elsa allows you to define workflows in code using C#. The following example shows how to receive HTTP requests and send an email in response:

Expand Down Expand Up @@ -141,13 +130,12 @@ public class SendEmailWorkflow : WorkflowBase
}
```

## Designed Workflows
## Designing Workflows

Elsa allows you to define workflows using a visual designer. The following example shows how to receive HTTP requests and send an email in response:

![Elsa ships with a powerful visual designer](./design/screenshots/http-send-email-workflow-designer.png)


## Contributing

We welcome contributions from the community and are pleased that you are interested in helping to improve the Elsa Workflow project! Here are the steps to contribute to our project:
Expand Down
1 change: 1 addition & 0 deletions src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
<PackageReference Include="DistributedLock.Postgres"/>
<PackageReference Include="DistributedLock.Redis"/>
<PackageReference Include="FluentStorage.Azure.Blobs"/>
<PackageReference Include="Hangfire.PostgreSql" />
<PackageReference Include="Proto.Cluster.Kubernetes" />
<PackageReference Include="Grpc.Net.Client" />
<PackageReference Include="Proto.Persistence.Sqlite"/>
Expand Down
45 changes: 43 additions & 2 deletions src/apps/Elsa.Server.Web/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@
using Elsa.Workflows.Runtime.Options;
using Elsa.Workflows.Runtime.Stores;
using Elsa.Workflows.Runtime.Tasks;
using Hangfire;
using Hangfire.MemoryStorage;
using Hangfire.PostgreSql;
using Hangfire.PostgreSql.Factories;
using Hangfire.SqlServer;
using Hangfire.Storage.SQLite;
using JetBrains.Annotations;
using Medallion.Threading.FileSystem;
using Medallion.Threading.Postgres;
Expand Down Expand Up @@ -79,7 +85,7 @@
const DistributedCachingTransport distributedCachingTransport = DistributedCachingTransport.MassTransit;
const MassTransitBroker massTransitBroker = MassTransitBroker.Memory;
const bool useMultitenancy = false;
const bool useTenantsFromConfiguration = false;
const bool useTenantsFromConfiguration = true;
const bool useAgents = false;
const bool useSecrets = false;
const bool disableVariableWrappers = false;
Expand Down Expand Up @@ -133,7 +139,36 @@
});

if (useHangfire)
elsa.UseHangfire();
{
JobStorage jobStorage;
if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql)
{
jobStorage = new PostgreSqlStorage(new NpgsqlConnectionFactory(postgresConnectionString, new()
{
QueuePollInterval = TimeSpan.FromSeconds(1)
}));
}
else if (sqlDatabaseProvider == SqlDatabaseProvider.Sqlite)
{
jobStorage = new SQLiteStorage(sqliteConnectionString, new()
{
QueuePollInterval = TimeSpan.FromSeconds(1)
});
}
else if (sqlDatabaseProvider == SqlDatabaseProvider.SqlServer)
{
jobStorage = new SqlServerStorage(sqlServerConnectionString, new()
{
QueuePollInterval = TimeSpan.FromSeconds(1)
});
}
else
{
jobStorage = new MemoryStorage();
}

elsa.UseHangfire(hangfire => hangfire.UseJobStorage(jobStorage));
}

elsa
.AddActivitiesFrom<Program>()
Expand Down Expand Up @@ -334,6 +369,12 @@
// Make sure to configure the path to the python DLL. E.g. /opt/homebrew/Cellar/[email protected]/3.11.6_1/Frameworks/Python.framework/Versions/3.11/bin/python3.11
// alternatively, you can set the PYTHONNET_PYDLL environment variable.
configuration.GetSection("Scripting:Python").Bind(options);

options.AddScript(sb =>
{
sb.AppendLine("def greet():");
sb.AppendLine(" return \"Hello, welcome to Python!\"");
});
};
})
.UseLiquid(liquid => liquid.FluidOptions = options => options.Encoder = HtmlEncoder.Default)
Expand Down
9 changes: 1 addition & 8 deletions src/apps/Elsa.Server.Web/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,7 @@
"Logging": {
"LogLevel": {
"Default": "Warning",
"Elsa": "Warning",
"MassTransit": "Warning",
"Microsoft.Extensions.Http": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore": "Warning",
"Microsoft.AspNetCore": "Warning",
"Quartz": "Warning",
"System.Net.Http": "Warning"
"Microsoft.Hosting.Lifetime": "Information"
}
},
"HostBuilder": {
Expand Down
2 changes: 1 addition & 1 deletion src/common/Elsa.Mediator/Contracts/IRequestSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ public interface IRequestSender
/// <param name="cancellationToken">The cancellation token.</param>
/// <typeparam name="T">The type of the response.</typeparam>
/// <returns>The response.</returns>
Task<IEnumerable<T>> SendAsync<T>(IRequest<T> request, CancellationToken cancellationToken = default);
Task<T?> SendAsync<T>(IRequest<T> request, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,10 @@ namespace Elsa.Mediator.Middleware.Request.Components;
/// <summary>
/// A middleware component that invokes request handlers.
/// </summary>
public class RequestHandlerInvokerMiddleware : IRequestMiddleware
public class RequestHandlerInvokerMiddleware(
RequestMiddlewareDelegate next,
IEnumerable<IRequestHandler> requestHandlers) : IRequestMiddleware
{
private readonly RequestMiddlewareDelegate _next;
private readonly IEnumerable<IRequestHandler> _requestHandlers;

/// <summary>
/// Initializes a new instance of the <see cref="RequestHandlerInvokerMiddleware"/> class.
/// </summary>
/// <param name="next">The next middleware in the pipeline.</param>
/// <param name="requestHandlers"></param>
public RequestHandlerInvokerMiddleware(
RequestMiddlewareDelegate next,
IEnumerable<IRequestHandler> requestHandlers)
{
_next = next;
_requestHandlers = requestHandlers;
}

/// <inheritdoc />
public async ValueTask InvokeAsync(RequestContext context)
{
Expand All @@ -33,22 +19,26 @@ public async ValueTask InvokeAsync(RequestContext context)
var requestType = request.GetType();
var responseType = context.ResponseType;
var handlerType = typeof(IRequestHandler<,>).MakeGenericType(requestType, responseType);
var handlers = _requestHandlers.Where(x => handlerType.IsInstanceOfType(x)).ToArray();
var handlers = requestHandlers.Where(x => handlerType.IsInstanceOfType(x)).ToArray();

if (handlers.Length == 0)
throw new InvalidOperationException($"There is no handler to handle the {requestType.FullName} request");

if (handlers.Length > 1)
throw new InvalidOperationException($"Multiple handlers were found to handle the {requestType.FullName} request");

foreach (var handler in handlers)
{
var handleMethod = handlerType.GetMethod("HandleAsync")!;
var cancellationToken = context.CancellationToken;
var task = (Task)handleMethod.Invoke(handler, new object?[] { request, cancellationToken })!;
await task;
var handler = handlers.First();
var handleMethod = handlerType.GetMethod("HandleAsync")!;
var cancellationToken = context.CancellationToken;
var task = (Task)handleMethod.Invoke(handler, [request, cancellationToken])!;
await task;

// Get result of task.
var taskWithReturnType = typeof(Task<>).MakeGenericType(responseType);
var resultProperty = taskWithReturnType.GetProperty(nameof(Task<object>.Result))!;
context.Responses.Add(resultProperty.GetValue(task)!);
}
// Get result of task.
var taskWithReturnType = typeof(Task<>).MakeGenericType(responseType);
var resultProperty = taskWithReturnType.GetProperty(nameof(Task<object>.Result))!;
context.Response = resultProperty.GetValue(task)!;

// Invoke next middleware.
await _next(context);
await next(context);
}
}
4 changes: 2 additions & 2 deletions src/common/Elsa.Mediator/Middleware/Request/RequestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public RequestContext(IRequest request, Type responseType, CancellationToken can
public CancellationToken CancellationToken { get; init; }

/// <summary>
/// Gets the responses from each request handler.
/// Gets the response the request handler.
/// </summary>
public ICollection<object> Responses { get; set; } = new List<object>();
public object? Response { get; set; }
}
Loading

0 comments on commit dc09980

Please sign in to comment.