Merge remote-tracking branch 'upstream/main' into enh/Connections

This commit is contained in:
Jérémie DEVILLARD 2025-02-10 12:53:51 +01:00
commit 4565dfcfbc
429 changed files with 7344 additions and 3386 deletions

View file

@ -1,5 +1,5 @@
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
# Remove the line below if you want to inherit .editorconfig settings from higher directories
# C# files
[*.cs]
@ -76,9 +76,9 @@ dotnet_style_allow_statement_immediately_after_block_experimental = true
#### C# Coding Conventions ####
# var preferences
csharp_style_var_elsewhere = false
csharp_style_var_for_built_in_types = false
csharp_style_var_when_type_is_apparent = false
csharp_style_var_elsewhere = true
csharp_style_var_for_built_in_types = true
csharp_style_var_when_type_is_apparent = true
# Expression-bodied members
csharp_style_expression_bodied_accessors = true

View file

@ -1,8 +1,8 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
about: Create a bug report to help us improve
title: ""
type: Bug
assignees: ''
---

View file

@ -1,8 +1,8 @@
---
name: Documentation Improvement
name: Documentation
about: Suggest improvements to the documentation for this project
title: "[DOC] "
labels: documentation
title: ""
Type: Documentation
assignees: ''
---

View file

@ -1,8 +1,8 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FEAT]"
labels: enhancement
title: ""
type: Feature
assignees: ''
---

View file

@ -1,8 +1,8 @@
---
name: Enhancement
name: Improvement
about: Suggest an enhancement to an existing feature for this project
title: "[ENH] "
labels: enhancement
title: ""
type: Improvement
assignees: ''
---

View file

@ -1,8 +1,8 @@
---
name: Performance Improvement
name: Performance
about: Suggest a performance enhancement for this project
title: "[PERF] "
labels: performance
title: ""
type: Performance
assignees: ''
---

View file

@ -1,13 +1,13 @@
---
name: Maintenance Task
about: Suggest a maintenance task for this project
title: "[CHORE] "
labels: maintenance
name: Task
about: Suggest a task for this project
title: ""
type: Task
assignees: ''
---
## Maintenance Task Request
## Task Request
### Task Overview
**What maintenance task do you propose? Please describe.**

View file

@ -1,30 +0,0 @@
---
name: Test Improvement
about: Suggest improvements or additions to tests for this project
title: "[TEST] "
labels: test
assignees: ''
---
## Test Improvement Request
### Test Issue Overview
**Is your test improvement related to a specific problem? Please describe.**
Provide a detailed explanation of the testing issue, such as missing coverage, flaky tests, or inefficient testing processes. For example, "We currently have no tests covering our user authentication flow..."
### Proposed Test Improvements
**Describe the improvements you'd like**
Explain in detail what testing improvements or new tests you would like to see implemented. Describe how these tests should work and why they are necessary. Include any particular aspects like integration tests, unit tests, or end-to-end tests.
### Existing Tests
**Describe any existing tests and their limitations**
Detail the current testing setup and its limitations. This could include gaps in coverage, outdated tests, or tests that frequently fail under certain conditions.
### Impact of Improvements
**Explain the potential impact**
How would these test improvements benefit the development process, product reliability, or deployment cycles? Detail the anticipated benefits to help stakeholders understand the value of improving testing.
### Additional Context
**Add any other context**
Include any other information that might help clarify your request. This could be technical considerations, links to failed builds, or any other details that could inform the improvement process.

11
.github/dependabot.yml vendored Normal file
View file

@ -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"

View file

@ -5,10 +5,12 @@ on:
branches:
- 'main'
- 'bug/*'
- 'perf/*'
- 'patch/*'
release:
types: [ prereleased, published ]
env:
base_version: '3.3.0'
base_version: '3.4.0'
feedz_feed_source: 'https://f.feedz.io/elsa-workflows/elsa-3/nuget/index.json'
nuget_feed_source: 'https://api.nuget.org/v3/index.json'
@ -99,27 +101,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/download-artifact@v4.1.7
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/download-artifact@v4.1.7

View file

@ -5,7 +5,7 @@
#
# - To turn off auto-generation set:
#
# [GitHubActions (AutoGenerate = false)]
# [CustomGitHubActions (AutoGenerate = false)]
#
# - To trigger manual generation invoke:
#
@ -32,9 +32,10 @@ jobs:
name: ubuntu-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.x
dotnet-version: |
9.x
- uses: actions/checkout@v4
- name: 'Run: Compile, Test, Pack'
run: ./build.cmd Compile Test Pack

2
.gitignore vendored
View file

@ -76,3 +76,5 @@ unlist.sh
# build artifacts
/artifacts
/docker/data/

View file

@ -1,28 +1,5 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"AnalyseCode": {
"type": "boolean"
},
"Configuration": {
"type": "string",
"enum": [
"Debug",
"Release"
]
},
"IgnoreFailedSources": {
"type": "boolean",
"description": "Ignore unreachable sources during Restore"
},
"Solution": {
"type": "string",
"description": "Path to a solution file that is automatically loaded"
},
"Version": {
"type": "string"
}
},
"definitions": {
"Host": {
"type": "string",
@ -122,5 +99,34 @@
}
}
},
"$ref": "#/definitions/NukeBuild"
"allOf": [
{
"properties": {
"AnalyseCode": {
"type": "boolean"
},
"Configuration": {
"type": "string",
"enum": [
"Debug",
"Release"
]
},
"IgnoreFailedSources": {
"type": "boolean",
"description": "Ignore unreachable sources during Restore"
},
"Solution": {
"type": "string",
"description": "Path to a solution file that is automatically loaded"
},
"Version": {
"type": "string"
}
}
},
{
"$ref": "#/definitions/NukeBuild"
}
]
}

View file

@ -37,6 +37,6 @@
<NoWarn>$(NoWarn);IL2026;IL2046;IL2057;IL2067;IL2070;IL2072;IL2075;IL2087;IL2091</NoWarn>
</PropertyGroup>
<PropertyGroup>
<ElsaStudioVersion>3.3.0-preview.666</ElsaStudioVersion>
<ElsaStudioVersion>3.3.0-rc4</ElsaStudioVersion>
</PropertyGroup>
</Project>

View file

@ -1,234 +1,197 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Antlr4.Runtime.Standard" Version="4.13.1" />
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations" Version="0.5.1" />
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations.MySql" Version="0.5.1" />
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL" Version="0.5.1" />
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite" Version="0.5.1" />
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer" Version="0.5.1" />
<PackageVersion Include="AutoMapper" Version="13.0.1" PrivateAssets="All" />
<PackageVersion Include="Azure.Identity" Version="1.13.1" />
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.18.2" />
<PackageVersion Include="Azure.ResourceManager" Version="1.13.0" />
<PackageVersion Include="Azure.ResourceManager.AppContainers" Version="1.3.0" />
<PackageVersion Include="Azure.ResourceManager.Resources" Version="1.9.0" />
<PackageVersion Include="Azure.ResourceManager.ServiceBus" Version="1.0.1" />
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
<PackageVersion Include="Bogus" Version="35.6.1" />
<PackageVersion Include="ConfigureAwait.Fody" Version="3.3.2" PrivateAssets="All" />
<PackageVersion Include="Confluent.Kafka" Version="2.6.1" />
<PackageVersion Include="Confluent.SchemaRegistry.Serdes.Avro" Version="2.4.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" PrivateAssets="All" />
<PackageVersion Include="Cronos" Version="0.8.4" />
<PackageVersion Include="Dapper" Version="2.1.35" />
<PackageVersion Include="DistributedLock.Core" Version="1.0.7" />
<PackageVersion Include="DistributedLock.FileSystem" Version="1.0.2" />
<PackageVersion Include="DistributedLock.Postgres" Version="1.2.0" />
<PackageVersion Include="DistributedLock.Redis" Version="1.0.3" />
<PackageVersion Include="Elastic.Clients.Elasticsearch" Version="8.15.10" />
<PackageVersion Include="Elsa.Studio" Version="3.3.0-preview.630" />
<PackageVersion Include="Elsa.Studio.Agents" Version="3.3.0-preview.630" />
<PackageVersion Include="Elsa.Studio.Core.BlazorWasm" Version="3.3.0-preview.630" />
<PackageVersion Include="Elsa.Studio.Login.BlazorWasm" Version="3.3.0-preview.630" />
<PackageVersion Include="FastEndpoints" Version="5.31.0" />
<PackageVersion Include="FastEndpoints.Security" Version="5.31.0" />
<PackageVersion Include="FastEndpoints.Swagger" Version="5.31.0" />
<PackageVersion Include="FluentMigrator" Version="6.2.0" />
<PackageVersion Include="FluentMigrator.Runner" Version="6.2.0" />
<PackageVersion Include="FluentStorage" Version="5.6.0" />
<PackageVersion Include="FluentStorage.Azure.Blobs" Version="5.3.0" />
<PackageVersion Include="Fluid.Core" Version="2.14.0" />
<PackageVersion Include="Fody" Version="6.9.1" />
<PackageVersion Include="GitHubActionsTestLogger" Version="2.4.1" />
<PackageVersion Include="Google.Protobuf" Version="3.29.1" />
<PackageVersion Include="Grpc.Net.Client" Version="2.67.0" />
<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.Storage.SQLite" Version="0.4.2" />
<PackageVersion Include="Humanizer.Core" Version="2.14.1" />
<PackageVersion Include="IronCompress" Version="1.6.3" />
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageVersion Include="Jint" Version="4.1.0" />
<PackageVersion Include="LinqKit.Core" Version="1.2.7" />
<PackageVersion Include="MailKit" Version="4.8.0" />
<PackageVersion Include="MassTransit" Version="8.3.0" />
<PackageVersion Include="MassTransit.Azure.ServiceBus.Core" Version="8.3.0" />
<PackageVersion Include="MassTransit.RabbitMQ" Version="8.3.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.12.0" />
<PackageVersion Include="Microsoft.Identity.Client" Version="4.66.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.25.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="MongoDB.Driver" Version="3.1.0" />
<PackageVersion Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="1.5.0" />
<PackageVersion Include="MongoDB.Driver.Extensions" Version="2.0.2" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="NuGet.Packaging" Version="6.12.1" />
<PackageVersion Include="NuGet.Protocol" Version="6.12.1" />
<PackageVersion Include="Nuke.Components" Version="9.0.3" />
<PackageVersion Include="Open.Linq.AsyncExtensions" Version="1.2.0" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageVersion Include="PolySharp" Version="1.15.0" />
<PackageVersion Include="Proto.Actor" Version="1.7.0" />
<PackageVersion Include="Proto.Cluster" Version="1.7.0" />
<PackageVersion Include="Proto.Cluster.AzureContainerApps" Version="1.7.0" />
<PackageVersion Include="Proto.Cluster.CodeGen" Version="1.7.0" />
<PackageVersion Include="Proto.Cluster.Kubernetes" Version="1.7.0" />
<PackageVersion Include="Proto.Cluster.TestProvider" Version="1.7.0" />
<PackageVersion Include="Proto.OpenTelemetry" Version="1.7.0" />
<PackageVersion Include="Proto.Persistence" Version="1.7.0" />
<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.4" />
<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" />
<PackageVersion Include="ShortGuid" Version="2.0.1" />
<PackageVersion Include="StackExchange.Redis" Version="2.8.16" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.Data.SqlClient" Version="4.8.6" />
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.4.9" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageVersion Include="Testcontainers" Version="3.10.0" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="3.10.0" />
<PackageVersion Include="Testcontainers.RabbitMq" Version="3.10.0" />
<PackageVersion Include="Testcontainers.Redis" Version="3.10.0" />
<PackageVersion Include="ThrottleDebounce" Version="2.0.0" />
<PackageVersion Include="WebhooksCore" Version="0.0.1" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.abstractions" Version="2.0.3" />
<PackageVersion Include="xunit.extensibility.core" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="Yarp.ReverseProxy" Version="2.2.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="7.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="7.0.20" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.20" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.20" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.20" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="7.0.20" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="7.0.20" />
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="7.0.20" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="7.0.20" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.20" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.20" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.20" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.20" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="7.0.20" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
<PackageVersion Include="Npgsql" Version="7.0.8" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.18" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="7.21.13" />
<PackageVersion Include="Polly" Version="7.2.4" />
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
<PackageVersion Include="Refit" Version="7.2.22" />
<PackageVersion Include="Refit.HttpClientFactory" Version="7.0.0" />
<PackageVersion Include="System.Formats.Asn1" Version="8.0.1" />
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="8.0.1" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="8.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="8.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.10" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.10" />
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="8.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.10" />
<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.10" />
<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.5" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
<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="AspNetCore.Authentication.ApiKey" Version="8.0.1" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.0" />
<PackageVersion Include="Npgsql" Version="9.0.1" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.1" />
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.60" />
<PackageVersion Include="Polly" Version="8.5.0" />
<!-- <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="9.0.0" />
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
</ItemGroup>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Antlr4.Runtime.Standard" Version="4.13.1"/>
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations" Version="0.5.1"/>
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations.MySql" Version="0.5.1"/>
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations.PostgreSQL" Version="0.5.1"/>
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations.SQLite" Version="0.5.1"/>
<PackageVersion Include="AppAny.Quartz.EntityFrameworkCore.Migrations.SqlServer" Version="0.5.1"/>
<PackageVersion Include="AutoMapper" Version="13.0.1" PrivateAssets="All"/>
<PackageVersion Include="Azure.Identity" Version="1.13.2"/>
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.18.3"/>
<PackageVersion Include="Azure.ResourceManager" Version="1.13.0"/>
<PackageVersion Include="Azure.ResourceManager.AppContainers" Version="1.3.0"/>
<PackageVersion Include="Azure.ResourceManager.Resources" Version="1.9.0"/>
<PackageVersion Include="Azure.ResourceManager.ServiceBus" Version="1.1.0"/>
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0"/>
<PackageVersion Include="Bogus" Version="35.6.1"/>
<PackageVersion Include="ConfigureAwait.Fody" Version="3.3.2" PrivateAssets="All"/>
<PackageVersion Include="Confluent.Kafka" Version="2.8.0"/>
<PackageVersion Include="Confluent.SchemaRegistry.Serdes.Avro" Version="2.8.0"/>
<PackageVersion Include="coverlet.collector" Version="6.0.4" PrivateAssets="All"/>
<PackageVersion Include="Cronos" Version="0.9.0"/>
<PackageVersion Include="Dapper" Version="2.1.35"/>
<PackageVersion Include="DistributedLock.Core" Version="1.0.8"/>
<PackageVersion Include="DistributedLock.FileSystem" Version="1.0.3"/>
<PackageVersion Include="DistributedLock.Postgres" Version="1.2.1"/>
<PackageVersion Include="DistributedLock.Redis" Version="1.0.3"/>
<PackageVersion Include="Elastic.Clients.Elasticsearch" Version="8.17.1"/>
<PackageVersion Include="Elsa.Studio" Version="3.3.1"/>
<PackageVersion Include="Elsa.Studio.Agents" Version="3.3.1"/>
<PackageVersion Include="Elsa.Studio.Core.BlazorWasm" Version="3.3.1"/>
<PackageVersion Include="Elsa.Studio.Login.BlazorWasm" Version="3.3.1"/>
<PackageVersion Include="FastEndpoints" Version="5.33.0"/>
<PackageVersion Include="FastEndpoints.Security" Version="5.33.0"/>
<PackageVersion Include="FastEndpoints.Swagger" Version="5.33.0"/>
<PackageVersion Include="FluentMigrator" Version="6.2.0"/>
<PackageVersion Include="FluentMigrator.Runner" Version="6.2.0"/>
<PackageVersion Include="FluentStorage" Version="5.6.0"/>
<PackageVersion Include="FluentStorage.Azure.Blobs" Version="5.3.0"/>
<PackageVersion Include="Fluid.Core" Version="2.19.0"/>
<PackageVersion Include="Fody" Version="6.9.1"/>
<PackageVersion Include="GitHubActionsTestLogger" Version="2.4.1"/>
<PackageVersion Include="Google.Protobuf" Version="3.29.3"/>
<PackageVersion Include="Grpc.Net.Client" Version="2.67.0"/>
<PackageVersion Include="Grpc.Tools" Version="2.69.0"/>
<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"/>
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0"/>
<PackageVersion Include="Jint" Version="4.1.0"/>
<PackageVersion Include="LinqKit.Core" Version="1.2.8"/>
<PackageVersion Include="MailKit" Version="4.9.0"/>
<PackageVersion Include="MassTransit" Version="8.3.4"/>
<PackageVersion Include="MassTransit.Azure.ServiceBus.Core" Version="8.3.4"/>
<PackageVersion Include="MassTransit.RabbitMQ" Version="8.3.4"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.12.0"/>
<PackageVersion Include="Microsoft.Data.SqlClient" Version="5.2.2"/>
<PackageVersion Include="Microsoft.Identity.Client" Version="4.67.2"/>
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.34.0"/>
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0"/>
<PackageVersion Include="MongoDB.Driver" Version="3.1.0"/>
<PackageVersion Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="2.0.0"/>
<PackageVersion Include="MongoDB.Driver.Extensions" Version="2.0.2"/>
<PackageVersion Include="MySql.Data" Version="9.1.0"/>
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageVersion Include="NSubstitute" Version="5.3.0"/>
<PackageVersion Include="NuGet.Packaging" Version="6.12.1"/>
<PackageVersion Include="NuGet.Protocol" Version="6.12.1"/>
<PackageVersion Include="Nuke.Components" Version="9.0.4"/>
<PackageVersion Include="Open.Linq.AsyncExtensions" Version="1.2.0"/>
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0"/>
<PackageVersion Include="PolySharp" Version="1.15.0"/>
<PackageVersion Include="Proto.Actor" Version="1.7.0"/>
<PackageVersion Include="Proto.Cluster" Version="1.7.0"/>
<PackageVersion Include="Proto.Cluster.AzureContainerApps" Version="1.7.0"/>
<PackageVersion Include="Proto.Cluster.CodeGen" Version="1.7.0"/>
<PackageVersion Include="Proto.Cluster.Kubernetes" Version="1.7.0"/>
<PackageVersion Include="Proto.Cluster.TestProvider" Version="1.7.0"/>
<PackageVersion Include="Proto.OpenTelemetry" Version="1.7.0"/>
<PackageVersion Include="Proto.Persistence" Version="1.7.0"/>
<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.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="6.0.1"/>
<PackageVersion Include="ShortGuid" Version="2.0.1"/>
<PackageVersion Include="StackExchange.Redis" Version="2.8.24"/>
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1"/>
<PackageVersion Include="System.Data.SqlClient" Version="4.9.0"/>
<PackageVersion Include="System.Linq.Async" Version="6.0.1"/>
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.6.0-preview-02"/>
<PackageVersion Include="System.Net.Http" Version="4.3.4"/>
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1"/>
<PackageVersion Include="Testcontainers" Version="4.1.0"/>
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.1.0"/>
<PackageVersion Include="Testcontainers.RabbitMq" Version="4.1.0"/>
<PackageVersion Include="Testcontainers.Redis" Version="4.1.0"/>
<PackageVersion Include="ThrottleDebounce" Version="2.0.0"/>
<PackageVersion Include="WebhooksCore" Version="0.0.1"/>
<PackageVersion Include="xunit" Version="2.9.3"/>
<PackageVersion Include="xunit.abstractions" Version="2.0.3"/>
<PackageVersion Include="xunit.extensibility.core" Version="2.9.3"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.1"/>
<PackageVersion Include="Yarp.ReverseProxy" Version="2.2.0"/>
<PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="8.0.1"/>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="8.0.12"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.12"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.12"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.12"/>
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="8.0.12"/>
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.12"/>
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.12"/>
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="8.0.12"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.12"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.12"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.12"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.12"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.12"/>
<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.12"/>
<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.70"/>
<PackageVersion Include="Polly" Version="8.5.1"/>
<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.1"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.1"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.1"/>
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="9.0.1"/>
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="9.0.1"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.1"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.1"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.1"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.1"/>
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.1"/>
<PackageVersion Include="Npgsql" Version="9.0.2"/>
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3"/>
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.60"/>
<PackageVersion Include="Polly" Version="8.5.1"/>
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-preview.2.efcore.9.0.0"/>
<PackageVersion Include="Refit" Version="8.0.0"/>
<PackageVersion Include="Refit.HttpClientFactory" Version="8.0.0"/>
<PackageVersion Include="System.Formats.Asn1" Version="9.0.1"/>
<PackageVersion Include="System.Text.Json" Version="9.0.1"/>
</ItemGroup>
</Project>

2015
Elsa.sln

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,7 @@
[![Stack Overflow questions](https://img.shields.io/badge/stackoverflow-elsa_workflows-orange.svg)]( http://stackoverflow.com/questions/tagged/elsa-workflows )
[![Gurubase](https://img.shields.io/badge/Gurubase-Ask%20Elsa%20Guru-006BFF)](https://gurubase.io/g/elsa)
### [For Elsa 2 Click Here](https://github.com/elsa-workflows/elsa-core/tree/2.x)
### [For Elsa 2, Click Here](https://github.com/elsa-workflows/elsa-core/tree/2.x)
## Introduction
Elsa is a powerful workflow library that enables workflow execution within any .NET application. Elsa allows you to define workflows in various ways, including:
@ -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.
@ -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
@ -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:
@ -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:

View file

@ -1,7 +1,11 @@
using System.Collections.Generic;
using Nuke.Common.CI.GitHubActions;
using Nuke.Common.CI.GitHubActions.Configuration;
using Nuke.Common.Execution;
using Nuke.Common.Utilities;
using Nuke.Components;
[GitHubActions(
[CustomGitHubActions(
"pr",
GitHubActionsImage.UbuntuLatest,
OnPullRequestBranches = ["main"],
@ -12,4 +16,55 @@ using Nuke.Components;
ConcurrencyCancelInProgress = true
)
]
public partial class Build;
public partial class Build;
class CustomGitHubActionsAttribute : GitHubActionsAttribute
{
public CustomGitHubActionsAttribute(string name, GitHubActionsImage image, params GitHubActionsImage[] images) : base(name, image, images)
{
}
protected override GitHubActionsJob GetJobs(GitHubActionsImage image, IReadOnlyCollection<ExecutableTarget> relevantTargets)
{
var job = base.GetJobs(image, relevantTargets);
var newSteps = new List<GitHubActionsStep>(job.Steps);
// only need to list the ones that are missing from default image
newSteps.Insert(0, new GitHubActionsSetupDotNetStep(["9.x"]));
job.Steps = newSteps.ToArray();
return job;
}
}
class GitHubActionsSetupDotNetStep : GitHubActionsStep
{
public GitHubActionsSetupDotNetStep(string[] versions)
{
Versions = versions;
}
string[] Versions { get; }
public override void Write(CustomFileWriter writer)
{
writer.WriteLine("- uses: actions/setup-dotnet@v4");
using (writer.Indent())
{
writer.WriteLine("with:");
using (writer.Indent())
{
writer.WriteLine("dotnet-version: |");
using (writer.Indent())
{
foreach (var version in Versions)
{
writer.WriteLine(version);
}
}
}
}
}
}

View file

@ -19,7 +19,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nuke.Components" Version="9.0.3" />
<PackageReference Include="Nuke.Components" Version="9.0.4" />
</ItemGroup>
<!--Overridden for vulnerability reasons with dependencies referencing older versions.-->

View file

@ -13,7 +13,7 @@
- "5432:5432"
mysql:
image: mysql:9.1.0
image: mysql:8.0
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: password
@ -23,7 +23,19 @@
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- mysql_data2:/var/lib/mysql
oracle:
image: container-registry.oracle.com/database/free:latest
container_name: oracle-db
environment:
ORACLE_PWD: elsa
ports:
- "1521:1521"
- "5500:5500"
volumes:
- ./data/oracle-data:/opt/oracle/oradata
- ./setup/oracle-setup:/opt/oracle/scripts/setup
mongodb:
image: mongo:latest
@ -72,6 +84,17 @@
environment:
- PlantUml__RemoteUrl=
- ConnectionStrings__DefaultConnection=USER ID=tracelens;PASSWORD=tracelenspass;HOST=postgres;PORT=5432;DATABASE=tracelens;POOLING=true;
smtp4dev: # Mock SMTP server
image: rnwood/smtp4dev
container_name: smtp4dev
restart: always
ports:
- "3000:80" # Web interface
- "2525:25" # SMTP port
environment:
- ASPNETCORE_URLS=http://+:80
- Logging__LogLevel__Default=Information
elsa-server:
build:
@ -104,6 +127,7 @@
volumes:
postgres-data:
mysql_data:
oracle-data-free1:
mysql_data2:
cockroachdb-data:
mongodb_data:

View file

@ -0,0 +1,3 @@
alter session set "_ORACLE_SCRIPT"=true;
CREATE USER ELSA IDENTIFIED BY elsa;
GRANT ALL PRIVILEGES TO ELSA;

View file

@ -16,6 +16,6 @@ for module in "${mods[@]}"; do
echo "Updating migrations for $provider..."
echo "Provider path: ${providerPath:?}/${migrationsPath}"
echo "Migrations path: $migrationsPath"
ef-migration-runtime-schema --interface Elsa.EntityFrameworkCore.Common.Contracts.IElsaDbContextSchema --efOptions "migrations add V3_1 -c ""$module""ElsaDbContext -p ""$providerPath"" -o ""$migrationsPath"""
ef-migration-runtime-schema --interface Elsa.EntityFrameworkCore.IElsaDbContextSchema --efOptions "migrations add V3_1 -c ""$module""ElsaDbContext -p ""$providerPath"" -o ""$migrationsPath"""
done
done

View file

@ -16,6 +16,6 @@ for module in "${mods[@]}"; do
echo "Updating migrations for $provider..."
echo "Provider path: ${providerPath:?}/${migrationsPath}"
echo "Migrations path: $migrationsPath"
ef-migration-runtime-schema --interface Elsa.EntityFrameworkCore.Common.Contracts.IElsaDbContextSchema --efOptions "migrations add V3_2 -c ""$module""ElsaDbContext -p ""$providerPath"" -o ""$migrationsPath"""
ef-migration-runtime-schema --interface Elsa.EntityFrameworkCore.IElsaDbContextSchema --efOptions "migrations add V3_2 -c ""$module""ElsaDbContext -p ""$providerPath"" -o ""$migrationsPath"""
done
done

View file

@ -3,7 +3,7 @@
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<PropertyGroup Label="TargetFrameworks">
<TargetFrameworks>net6.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup Label="Files">

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<ItemGroup>
<ProjectReference Include="..\..\modules\Elsa.Agents.Activities\Elsa.Agents.Activities.csproj" />
@ -9,58 +9,76 @@
<ProjectReference Include="..\..\modules\Elsa.Caching.Distributed.MassTransit\Elsa.Caching.Distributed.MassTransit.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Caching.Distributed.ProtoActor\Elsa.Caching.Distributed.ProtoActor.csproj" />
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.MySql\Elsa.EntityFrameworkCore.MySql.csproj" Condition=" '$(TargetFramework)' != 'net9.0' " />
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.PostgreSql\Elsa.EntityFrameworkCore.PostgreSql.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.Oracle\Elsa.EntityFrameworkCore.Oracle.csproj" />
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.PostgreSql\Elsa.EntityFrameworkCore.PostgreSql.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Kafka\Elsa.Kafka.csproj" />
<ProjectReference Include="..\..\modules\Elsa.MassTransit.AzureServiceBus\Elsa.MassTransit.AzureServiceBus.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.MassTransit.AzureServiceBus\Elsa.MassTransit.AzureServiceBus.csproj" />
<ProjectReference Include="..\..\modules\Elsa.OpenTelemetry\Elsa.OpenTelemetry.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Quartz.EntityFrameworkCore.MySql\Elsa.Quartz.EntityFrameworkCore.MySql.csproj" Condition=" '$(TargetFramework)' != 'net9.0' " />
<ProjectReference Include="..\..\modules\Elsa.Quartz.EntityFrameworkCore.PostgreSql\Elsa.Quartz.EntityFrameworkCore.PostgreSql.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Retention\Elsa.Retention.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Secrets.Api\Elsa.Secrets.Api.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Secrets.Persistence.EntityFrameworkCore.PostgreSql\Elsa.Secrets.Persistence.EntityFrameworkCore.PostgreSql.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Secrets.Persistence.EntityFrameworkCore.Sqlite\Elsa.Secrets.Persistence.EntityFrameworkCore.Sqlite.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Secrets.Persistence.EntityFrameworkCore.SqlServer\Elsa.Secrets.Persistence.EntityFrameworkCore.SqlServer.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Secrets.Scripting\Elsa.Secrets.Scripting.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Sql.MySql\Elsa.Sql.MySql.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Sql.PostgreSql\Elsa.Sql.PostgreSql.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Sql.Sqlite\Elsa.Sql.Sqlite.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Sql.SqlServer\Elsa.Sql.SqlServer.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Sql\Elsa.Sql.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Tenants.AspNetCore\Elsa.Tenants.AspNetCore.csproj" />
<ProjectReference Include="..\..\modules\Elsa\Elsa.csproj"/>
<ProjectReference Include="..\..\common\Elsa.DropIns\Elsa.DropIns.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Alterations.MassTransit\Elsa.Alterations.MassTransit.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Alterations\Elsa.Alterations.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.CSharp\Elsa.CSharp.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.Sqlite\Elsa.EntityFrameworkCore.Sqlite.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.SqlServer\Elsa.EntityFrameworkCore.SqlServer.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.MassTransit.RabbitMq\Elsa.MassTransit.RabbitMq.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Python\Elsa.Python.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Quartz.EntityFrameworkCore.Sqlite\Elsa.Quartz.EntityFrameworkCore.Sqlite.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.FileStorage\Elsa.FileStorage.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Dapper.Migrations\Elsa.Dapper.Migrations.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Dapper\Elsa.Dapper.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Elasticsearch\Elsa.Elasticsearch.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Email\Elsa.Email.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore\Elsa.EntityFrameworkCore.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Environments\Elsa.Environments.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.MassTransit\Elsa.MassTransit.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.MongoDb\Elsa.MongoDb.csproj"/>
<ProjectReference Include="..\..\modules\Elsa\Elsa.csproj" />
<ProjectReference Include="..\..\common\Elsa.DropIns\Elsa.DropIns.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Alterations.MassTransit\Elsa.Alterations.MassTransit.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Alterations\Elsa.Alterations.csproj" />
<ProjectReference Include="..\..\modules\Elsa.CSharp\Elsa.CSharp.csproj" />
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.Sqlite\Elsa.EntityFrameworkCore.Sqlite.csproj" />
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.SqlServer\Elsa.EntityFrameworkCore.SqlServer.csproj" />
<ProjectReference Include="..\..\modules\Elsa.MassTransit.RabbitMq\Elsa.MassTransit.RabbitMq.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Python\Elsa.Python.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Quartz.EntityFrameworkCore.Sqlite\Elsa.Quartz.EntityFrameworkCore.Sqlite.csproj" />
<ProjectReference Include="..\..\modules\Elsa.FileStorage\Elsa.FileStorage.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Dapper.Migrations\Elsa.Dapper.Migrations.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Dapper\Elsa.Dapper.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Elasticsearch\Elsa.Elasticsearch.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Email\Elsa.Email.csproj" />
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore\Elsa.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Environments\Elsa.Environments.csproj" />
<ProjectReference Include="..\..\modules\Elsa.MassTransit\Elsa.MassTransit.csproj" />
<ProjectReference Include="..\..\modules\Elsa.MongoDb\Elsa.MongoDb.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Workflows.Runtime.ProtoActor\Elsa.Workflows.Runtime.ProtoActor.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Identity\Elsa.Identity.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.WorkflowProviders.BlobStorage\Elsa.WorkflowProviders.BlobStorage.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Hangfire\Elsa.Hangfire.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Http\Elsa.Http.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Labels\Elsa.Labels.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Liquid\Elsa.Liquid.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Quartz\Elsa.Quartz.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.WorkflowContexts\Elsa.WorkflowContexts.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Workflows.Api\Elsa.Workflows.Api.csproj"/>
<ProjectReference Include="..\..\modules\Elsa.Identity\Elsa.Identity.csproj" />
<ProjectReference Include="..\..\modules\Elsa.WorkflowProviders.BlobStorage\Elsa.WorkflowProviders.BlobStorage.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Hangfire\Elsa.Hangfire.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Http\Elsa.Http.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Labels\Elsa.Labels.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Liquid\Elsa.Liquid.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Quartz\Elsa.Quartz.csproj" />
<ProjectReference Include="..\..\modules\Elsa.WorkflowContexts\Elsa.WorkflowContexts.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Workflows.Api\Elsa.Workflows.Api.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity"/>
<PackageReference Include="Bogus"/>
<PackageReference Include="DistributedLock.Postgres"/>
<PackageReference Include="DistributedLock.Redis"/>
<PackageReference Include="FluentStorage.Azure.Blobs"/>
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Bogus" />
<PackageReference Include="DistributedLock.Postgres" />
<PackageReference Include="DistributedLock.Redis" />
<PackageReference Include="FluentStorage.Azure.Blobs" />
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Bogus" />
<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"/>
<PackageReference Include="Proto.Persistence.SqlServer"/>
<PackageReference Include="Proto.Persistence.Sqlite" />
<PackageReference Include="Proto.Persistence.SqlServer" />
</ItemGroup>
<ItemGroup>
<Folder Include="App_Data\" />
</ItemGroup>
</Project>

View file

@ -6,5 +6,6 @@ public enum SqlDatabaseProvider
Sqlite,
MySql,
PostgreSql,
Oracle,
CockroachDb
}

View file

@ -2,12 +2,14 @@ using System.Text.Encodings.Web;
using Elsa.Agents;
using Elsa.Alterations.Extensions;
using Elsa.Alterations.MassTransit.Extensions;
using Elsa.Caching.Options;
using Elsa.Common.DistributedHosting.DistributedLocks;
using Elsa.Common.RecurringTasks;
using Elsa.Common.Serialization;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Services;
using Elsa.DropIns.Extensions;
using Elsa.EntityFrameworkCore;
using Elsa.EntityFrameworkCore.Extensions;
using Elsa.EntityFrameworkCore.Modules.Alterations;
using Elsa.EntityFrameworkCore.Modules.Identity;
@ -27,6 +29,8 @@ using Elsa.MongoDb.Modules.Management;
using Elsa.MongoDb.Modules.Runtime;
using Elsa.MongoDb.Modules.Tenants;
using Elsa.OpenTelemetry.Middleware;
using Elsa.Retention.Extensions;
using Elsa.Retention.Models;
using Elsa.Secrets.Extensions;
using Elsa.Secrets.Management.Tasks;
using Elsa.Secrets.Persistence;
@ -34,9 +38,16 @@ using Elsa.Server.Web;
using Elsa.Server.Web.Extensions;
using Elsa.Server.Web.Filters;
using Elsa.Server.Web.Messages;
using Elsa.Sql.Extensions;
using Elsa.Sql.MySql;
using Elsa.Sql.PostgreSql;
using Elsa.Sql.Sqlite;
using Elsa.Sql.SqlServer;
using Elsa.Tenants.AspNetCore;
using Elsa.Tenants.Extensions;
using Elsa.Workflows;
using Elsa.Workflows.Api;
using Elsa.Workflows.CommitStates.Strategies;
using Elsa.Workflows.LogPersistence;
using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Compression;
@ -45,6 +56,12 @@ using Elsa.Workflows.Runtime.Distributed.Extensions;
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;
@ -75,10 +92,11 @@ const WorkflowRuntime workflowRuntime = WorkflowRuntime.Distributed;
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 = true;
const bool useSecrets = false;
const bool disableVariableWrappers = false;
const bool disableVariableCopying = false;
var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
@ -88,6 +106,7 @@ var identityTokenSection = identitySection.GetSection("Tokens");
var sqliteConnectionString = configuration.GetConnectionString("Sqlite")!;
var sqlServerConnectionString = configuration.GetConnectionString("SqlServer")!;
var postgresConnectionString = configuration.GetConnectionString("PostgreSql")!;
var oracleConnectionString = configuration.GetConnectionString("Oracle")!;
var mySqlConnectionString = configuration.GetConnectionString("MySql")!;
var cockroachDbConnectionString = configuration.GetConnectionString("CockroachDb")!;
var mongoDbConnectionString = configuration.GetConnectionString("MongoDb")!;
@ -129,7 +148,36 @@ services
});
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>()
@ -150,11 +198,13 @@ services
else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql)
ef.UsePostgreSql(postgresConnectionString!);
#if !NET9_0
else if (sqlDatabaseProvider == SqlDatabaseProvider.MySql)
else if (sqlDatabaseProvider == SqlDatabaseProvider.MySql)
ef.UseMySql(mySqlConnectionString);
#endif
else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb)
ef.UsePostgreSql(cockroachDbConnectionString!);
else if (sqlDatabaseProvider == SqlDatabaseProvider.Oracle)
ef.UseOracle(oracleConnectionString, new ElsaDbContextOptions{ SchemaName = "ELSA"});
else
ef.UseSqlite(sp => sp.GetSqliteConnectionString());
@ -171,6 +221,11 @@ services
{
workflows.WithDefaultWorkflowExecutionPipeline(pipeline => pipeline.UseWorkflowExecutionTracing());
workflows.WithDefaultActivityExecutionPipeline(pipeline => pipeline.UseActivityExecutionTracing());
workflows.UseCommitStrategies(strategies =>
{
strategies.AddStandardStrategies();
strategies.Add("Every 10 seconds", new PeriodicWorkflowStrategy(TimeSpan.FromSeconds(10)));
});
})
.UseWorkflowManagement(management =>
{
@ -191,6 +246,8 @@ services
#endif
else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb)
ef.UsePostgreSql(cockroachDbConnectionString!);
else if (sqlDatabaseProvider == SqlDatabaseProvider.Oracle)
ef.UseOracle(oracleConnectionString, new ElsaDbContextOptions{ SchemaName = "ELSA"});
else
ef.UseSqlite(sp => sp.GetSqliteConnectionString());
@ -236,7 +293,9 @@ services
ef.UseMySql(mySqlConnectionString);
#endif
else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb)
ef.UsePostgreSql(cockroachDbConnectionString!);
ef.UsePostgreSql(cockroachDbConnectionString);
else if (sqlDatabaseProvider == SqlDatabaseProvider.Oracle)
ef.UseOracle(oracleConnectionString, new ElsaDbContextOptions{ SchemaName = "ELSA"});
else
ef.UseSqlite(sp => sp.GetSqliteConnectionString());
@ -316,6 +375,7 @@ services
{
options.AllowClrAccess = true;
options.DisableWrappers = disableVariableWrappers;
options.DisableVariableCopying = disableVariableCopying;
options.RegisterType<OrderReceived>();
options.ConfigureEngine(engine =>
{
@ -330,6 +390,12 @@ services
// Make sure to configure the path to the python DLL. E.g. /opt/homebrew/Cellar/python@3.11/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)
@ -340,6 +406,16 @@ services
if (useCaching)
http.UseCache();
})
.UseSql(options =>
{
options.Clients = client =>
{
client.Register<MySqlClient>("MySql");
client.Register<PostgreSqlClient>("PostgreSql");
client.Register<SqliteClient>("Sqlite");
client.Register<SqlServerClient>("Sql Server");
};
})
.UseEmail(email => email.ConfigureOptions = options => configuration.GetSection("Smtp").Bind(options))
.UseAlterations(alterations =>
{
@ -364,7 +440,9 @@ services
ef.UseMySql(mySqlConnectionString);
#endif
else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb)
ef.UsePostgreSql(cockroachDbConnectionString!);
ef.UsePostgreSql(cockroachDbConnectionString);
else if (sqlDatabaseProvider == SqlDatabaseProvider.Oracle)
ef.UseOracle(oracleConnectionString, new ElsaDbContextOptions{ SchemaName = "ELSA"});
else
ef.UseSqlite(sp => sp.GetSqliteConnectionString());
@ -381,7 +459,13 @@ services
if (useQuartz)
{
elsa.UseQuartz(quartz => { quartz.UseSqlite(sqliteConnectionString); });
elsa.UseQuartz(quartz =>
{
if (sqlDatabaseProvider == SqlDatabaseProvider.Sqlite)
quartz.UseSqlite(sqliteConnectionString);
else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql)
quartz.UsePostgreSql(postgresConnectionString);
});
}
if (useSignalR)
@ -514,6 +598,19 @@ services
;
}
elsa.UseRetention(r =>
{
r.SweepInterval = TimeSpan.FromHours(5);
r.AddDeletePolicy("Delete all finished workflows", sp =>
{
var filter = new RetentionWorkflowInstanceFilter
{
WorkflowStatus = WorkflowStatus.Finished
};
return filter;
});
});
if (useMultitenancy)
{
elsa.UseTenants(tenants =>
@ -548,8 +645,10 @@ services
if (sqlDatabaseProvider == SqlDatabaseProvider.Sqlite) ef.UseSqlite(sqliteConnectionString);
if (sqlDatabaseProvider == SqlDatabaseProvider.SqlServer) ef.UseSqlServer(sqlServerConnectionString);
if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql) ef.UsePostgreSql(postgresConnectionString);
if (sqlDatabaseProvider == SqlDatabaseProvider.Oracle) ef.UseOracle(oracleConnectionString, new ElsaDbContextOptions{ SchemaName = "ELSA"});
#if !NET9_0
if (sqlDatabaseProvider == SqlDatabaseProvider.MySql) ef.UseMySql(mySqlConnectionString);
if (sqlDatabaseProvider == SqlDatabaseProvider.MySql)
ef.UseMySql(mySqlConnectionString);
#endif
if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb) ef.UsePostgreSql(cockroachDbConnectionString);
});
@ -560,7 +659,11 @@ services
}
});
elsa.UseTenantHttpRouting();
elsa.UseTenantHttpRouting(tenantHttpRouting =>
{
// Override the tenant header name with a custom one.
tenantHttpRouting.WithTenantHeader("X-Company-Id");
});
}
elsa.InstallDropIns(options => options.DropInRootDirectory = Path.Combine(Directory.GetCurrentDirectory(), "App_Data", "DropIns"));
@ -582,7 +685,7 @@ services.Configure<RecurringTaskOptions>(options =>
services.Configure<BookmarkQueuePurgeOptions>(options => options.Ttl = TimeSpan.FromSeconds(10));
//services.Configure<CachingOptions>(options => options.CacheDuration = TimeSpan.FromDays(1));
services.Configure<CachingOptions>(options => options.CacheDuration = TimeSpan.FromDays(1));
services.AddHealthChecks();
services.AddControllers();
services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().WithExposedHeaders("*")));

View file

@ -0,0 +1,25 @@
using Elsa.Workflows;
using Elsa.Workflows.Activities;
using Elsa.Extensions;
using Elsa.Workflows.CommitStates.Strategies;
namespace Elsa.Server.Web;
public class SampleWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder builder)
{
builder.WorkflowOptions.CommitStrategyName = "Every 10 seconds";
builder.Root = new Sequence
{
Activities =
{
new WriteLine("Commit before executing").SetCommitStrategy(nameof(ExecutingActivityStrategy)),
new WriteLine("Commit after executing").SetCommitStrategy(nameof(ExecutedActivityStrategy)),
new WriteLine("Commit before & after executing").SetCommitStrategy(nameof(CommitAlwaysActivityStrategy)),
new WriteLine("Commit only based on the workflow commit options").SetCommitStrategy(nameof(DefaultActivityStrategy)),
new WriteLine("Never commit the workflow when this activity is about to execute or has executed").SetCommitStrategy(nameof(CommitNeverActivityStrategy)),
}
};
}
}

View file

@ -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": {
@ -18,7 +11,9 @@
"AllowedHosts": "*",
"ConnectionStrings": {
"Sqlite": "Data Source=App_Data/elsa.sqlite.db;Cache=Shared;",
"MySql": "Server=localhost;Database=elsa;Uid=admin;Pwd=password;",
"PostgreSql": "Server=localhost;Username=elsa;Database=elsa;Port=5432;Password=elsa;SSLMode=Prefer;MaxPoolSize=2000;Timeout=60",
"Oracle": "Data Source=(DESCRIPTION = (ADDRESS_LIST = (FAILOVER =ON) (LOAD_BALANCE = OFF) (ADDRESS = (PROTOCOL =TCP)(HOST=localhost)(PORT=1521))) (CONNECT_DATA = (SID= FREE) ));User Id=ELSA;Password=elsa;",
"CockroachDb": "Host=localhost;Port=26257;Database=elsa;SslMode=Disable;Username=root;IncludeErrorDetail=true",
"MongoDb": "mongodb://localhost:27017/elsa-workflows",
"AzureServiceBus": "",

View file

@ -2,9 +2,8 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@inject IConfiguration Configuration;
@{
var baseUrl = Configuration["Hosting:BaseUrl"]!;
var apiUrl = baseUrl + Url.Content("~/elsa/api");
var basePath = Configuration["Hosting:BasePath"]!;
var baseUrl = Configuration["Hosting:BaseUrl"] ?? Configuration["Http:BaseUrl"]!; // HttpBaseUrl is for backward compatibility.
var apiUrl = $"{baseUrl.TrimEnd('/')}/{(Configuration["Hosting:ApiPrefix"] ?? "elsa/api").TrimStart('/')}";
}
<!DOCTYPE html>
@ -15,20 +14,20 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>Elsa Studio 3.0</title>
<base href="/"/>
<link rel="apple-touch-icon" sizes="180x180" href="@basePath/_content/Elsa.Studio.Shell/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="@basePath/_content/Elsa.Studio.Shell/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="@basePath/_content/Elsa.Studio.Shell/favicon-16x16.png">
<link rel="manifest" href="@basePath/_content/Elsa.Studio.Shell/site.webmanifest">
<link rel="apple-touch-icon" sizes="180x180" href="@baseUrl/_content/Elsa.Studio.Shell/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="@baseUrl/_content/Elsa.Studio.Shell/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="@baseUrl/_content/Elsa.Studio.Shell/favicon-16x16.png">
<link rel="manifest" href="@baseUrl/_content/Elsa.Studio.Shell/site.webmanifest">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Grandstander:wght@100&display=swap" rel="stylesheet">
<link href="@basePath/_content/MudBlazor/MudBlazor.min.css" rel="stylesheet"/>
<link href="@basePath/_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.css" rel="stylesheet"/>
<link href="@basePath/_content/Radzen.Blazor/css/material-base.css" rel="stylesheet">
<link href="@basePath/_content/Elsa.Studio.Shell/css/shell.css" rel="stylesheet">
<link href="@baseUrl/_content/MudBlazor/MudBlazor.min.css" rel="stylesheet"/>
<link href="@baseUrl/_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.css" rel="stylesheet"/>
<link href="@baseUrl/_content/Radzen.Blazor/css/material-base.css" rel="stylesheet">
<link href="@baseUrl/_content/Elsa.Studio.Shell/css/shell.css" rel="stylesheet">
<link href="ElsaStudioWebAssembly.styles.css" rel="stylesheet">
</head>
@ -44,19 +43,18 @@
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="@basePath/_content/BlazorMonaco/jsInterop.js"></script>
<script src="@basePath/_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
<script src="@basePath/_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script>
<script src="@basePath/_content/MudBlazor/MudBlazor.min.js"></script>
<script src="@basePath/_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.js"></script>
<script src="@basePath/_content/Radzen.Blazor/Radzen.Blazor.js"></script>
<script src="@baseUrl/_content/BlazorMonaco/jsInterop.js"></script>
<script src="@baseUrl/_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
<script src="@baseUrl/_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script>
<script src="@baseUrl/_content/MudBlazor/MudBlazor.min.js"></script>
<script src="@baseUrl/_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.js"></script>
<script src="@baseUrl/_content/Radzen.Blazor/Radzen.Blazor.js"></script>
<script>
window.getClientConfig = function() { return {
"apiUrl": "@apiUrl",
"basePath": "@basePath"
"apiUrl": "@apiUrl"
} };
</script>
<script src="@basePath/_framework/blazor.webassembly.js"></script>
<script src="@baseUrl/_framework/blazor.webassembly.js"></script>
</body>
</html>

View file

@ -131,7 +131,11 @@ services
if (useCaching)
http.UseCache();
http.ConfigureHttpOptions = options => configuration.GetSection("Http").Bind(options);
http.ConfigureHttpOptions = options =>
{
options.BaseUrl = new Uri(configuration["Hosting:BaseUrl"] ?? configuration["Http:BaseUrl"]!); // HttpBaseUrl is for backward compatibility.
options.BasePath = configuration["Http:BasePath"];
};
})
.UseEmail(email => email.ConfigureOptions = options => configuration.GetSection("Smtp").Bind(options))
.UseWebhooks(webhooks => webhooks.ConfigureSinks = options => builder.Configuration.GetSection("Webhooks:Sinks").Bind(options))

View file

@ -15,10 +15,10 @@
"SqlServer": "Server=localhost;Database=elsa;User Id=sa;Password=Password12!",
"PostgreSql": "Server=localhost;Username=elsa;Database=elsa;Port=5432;Password=elsa;SSLMode=Prefer;MaxPoolSize=2000;Timeout=60"
},
"DatabaseProvider": "PostgreSql",
"DatabaseProvider": "Sqlite",
"Hosting": {
"BaseUrl": "https://localhost:8080",
"BasePath": ""
"ApiPrefix": "/elsa/api"
},
"Identity": {
"Tokens": {
@ -81,7 +81,6 @@
"DefaultSender": "noreply@crmservices.com"
},
"Http": {
"BaseUrl": "https://localhost:5001",
"BasePath": "/api/workflows"
},
"Webhooks": {

View file

@ -1,4 +1,5 @@
using System.Text.Json.Nodes;
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
using Elsa.Api.Client.Shared.Models;
namespace Elsa.Api.Client.Extensions;
@ -182,4 +183,14 @@ public static class ActivityExtensions
/// Sets a value indicating whether the specified activity can trigger the workflow.
/// </summary>
public static void SetRunAsynchronously(this JsonObject activity, bool value) => activity.SetProperty(JsonValue.Create(value), "customProperties", "runAsynchronously");
/// <summary>
/// Gets the commit state behavior for the specified activity.
/// </summary>
public static string? GetCommitStrategy(this JsonObject activity) => activity.TryGetProperty<string?>("customProperties", "commitStrategyName");
/// <summary>
/// Sets the commit state behavior for the specified activity.
/// </summary>
public static void SetCommitStrategy(this JsonObject activity, string? name) => activity.SetProperty(JsonValue.Create(name), "customProperties", "commitStrategyName");
}

View file

@ -2,6 +2,7 @@ using Elsa.Api.Client.Options;
using Elsa.Api.Client.Resources.ActivityDescriptorOptions.Contracts;
using Elsa.Api.Client.Resources.ActivityDescriptors.Contracts;
using Elsa.Api.Client.Resources.ActivityExecutions.Contracts;
using Elsa.Api.Client.Resources.CommitStrategies.Contracts;
using Elsa.Api.Client.Resources.Features.Contracts;
using Elsa.Api.Client.Resources.Identity.Contracts;
using Elsa.Api.Client.Resources.IncidentStrategies.Contracts;
@ -73,6 +74,7 @@ public static class DependencyInjectionExtensions
services.AddApi<IWorkflowActivationStrategiesApi>(builderOptions);
services.AddApi<IIncidentStrategiesApi>(builderOptions);
services.AddApi<ILogPersistenceStrategiesApi>(builderOptions);
services.AddApi<ICommitStrategiesApi>(builderOptions);
services.AddApi<ILoginApi>(builderOptions);
services.AddApi<IFeaturesApi>(builderOptions);
services.AddApi<IJavaScriptApi>(builderOptions);

View file

@ -15,52 +15,43 @@ public static class JsonObjectExtensions
{
return obj.ContainsKey("type") && obj.ContainsKey("id") && obj.ContainsKey("version");
}
/// <summary>
/// Serializes the specified value to a <see cref="JsonObject"/>.
/// </summary>
/// <param name="value">The value to serialize.</param>
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
/// <returns>A <see cref="JsonObject"/> representing the specified value.</returns>
public static JsonNode SerializeToNode(this object value, JsonSerializerOptions? options = default)
public static JsonNode SerializeToNode(this object value, JsonSerializerOptions? options = null)
{
options ??= new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
options ??= new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
return JsonSerializer.SerializeToNode(value, options)!;
}
/// <summary>
/// Serializes the specified value to a <see cref="JsonArray"/>.
/// </summary>
/// <param name="value">The value to serialize.</param>
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
/// <returns>A <see cref="JsonObject"/> representing the specified value.</returns>
public static JsonArray SerializeToArray(this object value, JsonSerializerOptions? options = default)
public static JsonArray SerializeToArray(this object value, JsonSerializerOptions? options = null)
{
options ??= new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
options ??= new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
return JsonSerializer.SerializeToNode(value, options)!.AsArray();
}
/// <summary>
/// Serializes the specified value to a <see cref="JsonArray"/>.
/// </summary>
/// <param name="value">The value to serialize.</param>
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
/// <returns>A <see cref="JsonObject"/> representing the specified value.</returns>
public static JsonArray SerializeToArray<T>(this IEnumerable<T> value, JsonSerializerOptions? options = default)
public static JsonArray SerializeToArray<T>(this IEnumerable<T> value, JsonSerializerOptions? options = null)
{
options ??= new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
options ??= new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
return JsonSerializer.SerializeToNode(value, options)!.AsArray();
}
@ -71,19 +62,23 @@ public static class JsonObjectExtensions
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
/// <typeparam name="T">The type to deserialize to.</typeparam>
/// <returns>The deserialized value.</returns>
public static T Deserialize<T>(this JsonNode value, JsonSerializerOptions? options = default)
public static T Deserialize<T>(this JsonNode value, JsonSerializerOptions? options = null)
{
options ??= new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
options ??= new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
if (value is JsonObject jsonObject)
return JsonSerializer.Deserialize<T>(jsonObject, options)!;
if (value is JsonArray jsonArray)
return JsonSerializer.Deserialize<T>(jsonArray, options)!;
if (typeof(T).IsEnum || (Nullable.GetUnderlyingType(typeof(T))?.IsEnum ?? false))
{
if (value.GetValueKind() == JsonValueKind.Null)
return default!;
return (T)Enum.Parse(Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T), value.ToString());
}
if (value is JsonValue jsonValue)
return jsonValue.GetValue<T>();
@ -101,7 +96,7 @@ public static class JsonObjectExtensions
model = GetPropertyContainer(model, path);
model[path.Last()] = value?.SerializeToNode();
}
/// <summary>
/// Sets the property value of the specified model.
/// </summary>
@ -113,7 +108,7 @@ public static class JsonObjectExtensions
model = GetPropertyContainer(model, path);
model[path.Last()] = value?.SerializeToNode();
}
/// <summary>
/// Sets the property value of the specified model.
/// </summary>
@ -125,7 +120,7 @@ public static class JsonObjectExtensions
model = GetPropertyContainer(model, path);
model[path.Last()] = new JsonArray(value.Select(x => x.SerializeToNode()).ToArray());
}
/// <summary>
/// Gets the property value of the specified model.
/// </summary>
@ -139,7 +134,7 @@ public static class JsonObjectExtensions
foreach (var prop in path.SkipLast(1))
{
if (currentModel[prop] is not JsonObject value)
return default;
return null;
currentModel = value;
}
@ -147,6 +142,25 @@ public static class JsonObjectExtensions
return currentModel[path.Last()];
}
/// <summary>
/// Gets the property value of the specified model.
/// </summary>
/// <param name="model">The model to get the property value from.</param>
/// <param name="path">The path to the property.</param>
/// <typeparam name="T">The type to deserialize to.</typeparam>
/// <returns>The property value.</returns>
public static T? TryGetProperty<T>(this JsonObject model, params string[] path)
{
try
{
return model.GetProperty<T>(path);
}
catch (Exception e)
{
return default;
}
}
/// <summary>
/// Gets the property value of the specified model.
/// </summary>
@ -173,7 +187,7 @@ public static class JsonObjectExtensions
var property = GetProperty(model, path);
return property != null ? property.Deserialize<T>(options) : default;
}
/// <summary>
/// Returns the property container of the specified model.
/// </summary>
@ -190,5 +204,4 @@ public static class JsonObjectExtensions
return model;
}
}

View file

@ -0,0 +1,6 @@
namespace Elsa.Api.Client.Resources.Alterations.Contracts;
/// <summary>
/// Marker interface for all alteration classes
/// </summary>
public interface IAlteration;

View file

@ -0,0 +1,52 @@
using Elsa.Api.Client.Resources.Alterations.Models;
using Elsa.Api.Client.Resources.Alterations.Requests;
using Elsa.Api.Client.Resources.Alterations.Responses;
using Refit;
namespace Elsa.Api.Client.Resources.Alterations.Contracts;
/// <summary>
/// Represents a client for the alterations API. Requires the Elsa.Alterations feature.
/// </summary>
public interface IAlterationsApi
{
/// <summary>
/// Returns an alteration plan and its associated jobs.
/// </summary>
/// <param name="id">The ID of the alteration plan to return.</param>
/// <param name="cancellationToken">The cancellation token.</param>
[Get("/alterations/{id}")]
Task<GetAlterationPlanResponse> GetAsync(string id, CancellationToken cancellationToken = default);
/// <summary>
/// Determines which workflow instances a "Submit" request would target without actually running an alteration
/// </summary>
/// <param name="request">The requested workflow filter to dry run</param>
/// <param name="cancellationToken">The cancellation token.</param>
[Post("/alterations/dry-run")]
Task<DryRunResponse> DryRun(AlterationWorkflowInstanceFilter request, CancellationToken cancellationToken = default);
/// <summary>
/// Submits an alteration plan and a filter for workflows instances to be executed against
/// </summary>
/// <param name="request">The alterations and filter to submit</param>
/// <param name="cancellationToken">The cancellation token.</param>
[Post("/alterations/submit")]
Task<SubmitResponse> Submit(AlterationPlanParams request, CancellationToken cancellationToken = default);
/// <summary>
/// Runs an alteration plan and a list of workflow Instance Ids to be executed against
/// </summary>
/// <param name="request">The alterations and workflowInstanceIds to execute</param>
/// <param name="cancellationToken">The cancellation token.</param>
[Post("/alterations/run")]
Task<RunResponse> Run(RunRequest request, CancellationToken cancellationToken = default);
/// <summary>
/// Retries the specified workflow instances.
/// </summary>
/// <param name="request">The request containing the selection of workflow instances to retry.</param>
/// <param name="cancellationToken">The cancellation token.</param>
[Post("/alterations/workflows/retry")]
Task<BulkRetryResponse> BulkRetryAsync(BulkRetryRequest request, CancellationToken cancellationToken);
}

View file

@ -0,0 +1,32 @@
namespace Elsa.Api.Client.Resources.Alterations.Enums;
/// <summary>
/// Represents the status of an activity.
/// </summary>
public enum ActivityStatus
{
/// <summary>
/// The activity is in the Pending state.
/// </summary>
Pending,
/// <summary>
/// The activity is in the Running state. Note that event if an activity is running, it may not be executing.
/// </summary>
Running,
/// <summary>
/// The activity is in the Completed state.
/// </summary>
Completed,
/// <summary>
/// The activity is in the Canceled state.
/// </summary>
Canceled,
/// <summary>
/// The activity is in the Faulted state.
/// </summary>
Faulted
}

View file

@ -0,0 +1,27 @@
namespace Elsa.Api.Client.Resources.Alterations.Enums;
/// <summary>
/// The status of an alteration plan for a workflow instance.
/// </summary>
public enum AlterationJobStatus
{
/// <summary>
/// The plan is pending execution.
/// </summary>
Pending,
/// <summary>
/// The plan is currently being executed.
/// </summary>
Running,
/// <summary>
/// The plan has been completed.
/// </summary>
Completed,
/// <summary>
/// The job has failed.
/// </summary>
Failed
}

View file

@ -0,0 +1,37 @@
namespace Elsa.Api.Client.Resources.Alterations.Enums;
/// <summary>
/// The status of an alteration plan.
/// </summary>
public enum AlterationPlanStatus
{
/// <summary>
/// The plan is pending execution.
/// </summary>
Pending,
/// <summary>
/// The plan is currently generating jobs.
/// </summary>
Generating,
/// <summary>
/// The plan is currently dispatching jobs.
/// </summary>
Dispatching,
/// <summary>
/// The plan is currently being executed.
/// </summary>
Running,
/// <summary>
/// The plan has been completed.
/// </summary>
Completed,
/// <summary>
/// The plan has failed.
/// </summary>
Failed
}

View file

@ -0,0 +1,34 @@
using Elsa.Api.Client.Resources.Alterations.Enums;
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// A filter for activities within a workflow instance
/// </summary>
public class ActivityFilter
{
/// <summary>
/// The ID of the activity.
/// </summary>
public string? ActivityId { get; set; }
/// <summary>
/// The ID of the activity instance.
/// </summary>
public string? ActivityInstanceId { get; set; }
/// <summary>
/// The node ID of the activity.
/// </summary>
public string? NodeId { get; set; }
/// <summary>
/// The name of the activity.
/// </summary>
public string? Name { get; set; }
/// <summary>
/// The status of the activity.
/// </summary>
public ActivityStatus? Status { get; set; }
}

View file

@ -0,0 +1,8 @@
using Elsa.Api.Client.Resources.Alterations.Contracts;
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// A base class for all IAlterations.
/// </summary>
public abstract class AlterationBase : IAlteration;

View file

@ -0,0 +1,45 @@
using Elsa.Api.Client.Resources.Alterations.Enums;
using Elsa.Api.Client.Shared.Models;
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// Represents the execution of the plan for an individual workflow instance.
/// </summary>
public class AlterationJob : Entity
{
/// <summary>
/// The ID of the plan that this job belongs to.
/// </summary>
public string PlanId { get; set; } = default!;
/// <summary>
/// The ID of the workflow instance that this job applies to.
/// </summary>
public string WorkflowInstanceId { get; set; } = default!;
/// <summary>
/// The status of the job.
/// </summary>
public AlterationJobStatus Status { get; set; }
/// <summary>
/// The serialized log of the job.
/// </summary>
public ICollection<AlterationLogEntry>? Log { get; set; } = new List<AlterationLogEntry>();
/// <summary>
/// The date and time at which the job was created.
/// </summary>
public DateTimeOffset CreatedAt { get; set; }
/// <summary>
/// The date and time at which the job was started.
/// </summary>
public DateTimeOffset? StartedAt { get; set; }
/// <summary>
/// The date and time at which the job was completed.
/// </summary>
public DateTimeOffset? CompletedAt { get; set; }
}

View file

@ -0,0 +1,13 @@
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// Represents a log of alterations.
/// </summary>
public class AlterationLog
{
/// <summary>
/// The log entries.
/// </summary>
public ICollection<AlterationLogEntry> LogEntries { get; set; } = new List<AlterationLogEntry>();
}

View file

@ -0,0 +1,12 @@
using Microsoft.Extensions.Logging;
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// An individual log entry about an alteration
/// </summary>
/// <param name="Message"></param>
/// <param name="LogLevel"></param>
/// <param name="Timestamp"></param>
/// <param name="EventName"></param>
public record AlterationLogEntry(string Message, LogLevel LogLevel, DateTimeOffset Timestamp, string? EventName = null);

View file

@ -0,0 +1,41 @@
using Elsa.Api.Client.Resources.Alterations.Contracts;
using Elsa.Api.Client.Resources.Alterations.Enums;
using Elsa.Api.Client.Shared.Models;
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// A plan that contains a list of alterations to be applied to a set of workflow instances.
/// </summary>
public class AlterationPlan : Entity
{
/// <summary>
/// The alterations to be applied.
/// </summary>
public ICollection<IAlteration> Alterations { get; set; } = new List<IAlteration>();
/// <summary>
/// The IDs of the workflow instances that this plan applies to.
/// </summary>
public AlterationWorkflowInstanceFilter WorkflowInstanceFilter { get; set; } = new();
/// <summary>
/// The status of the plan.
/// </summary>
public AlterationPlanStatus Status { get; set; }
/// <summary>
/// The date and time at which the plan was created.
/// </summary>
public DateTimeOffset CreatedAt { get; set; }
/// <summary>
/// The date and time at which the plan was started.
/// </summary>
public DateTimeOffset? StartedAt { get; set; }
/// <summary>
/// The date and time at which the plan was completed.
/// </summary>
public DateTimeOffset? CompletedAt { get; set; }
}

View file

@ -0,0 +1,24 @@
using Elsa.Api.Client.Resources.Alterations.Contracts;
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// Represents the execution of an alteration plan against a set of workflow instances defined by the given filter
/// </summary>
public class AlterationPlanParams
{
/// <summary>
/// The unique identifier for the alteration plan. If not specified, a new ID will be generated.
/// </summary>
public string? Id { get; set; }
/// <summary>
/// The alterations to be applied.
/// </summary>
public ICollection<IAlteration> Alterations { get; set; } = new List<IAlteration>();
/// <summary>
/// The IDs of the workflow instances that this plan applies to.
/// </summary>
public AlterationWorkflowInstanceFilter Filter { get; set; } = new();
}

View file

@ -0,0 +1,45 @@
using Elsa.Api.Client.Shared.Models;
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// Represents a filter for workflow instances.
/// </summary>
public class AlterationWorkflowInstanceFilter
{
/// <summary>
/// The IDs of the workflow instances that this plan applies to.
/// </summary>
public IEnumerable<string>? WorkflowInstanceIds { get; set; }
/// <summary>
/// The correlation IDs of the workflow instances that this plan applies to.
/// </summary>
public IEnumerable<string>? CorrelationIds { get; set; }
/// <summary>
/// A collection of timestamp filters used for filtering data based on specified timestamp columns and operators.
/// </summary>
public IEnumerable<TimestampFilter>? TimestampFilters { get; set; }
/// <summary>
/// The IDs of the workflow definitions that this plan applies to.
/// </summary>
public IEnumerable<string>? DefinitionVersionIds { get; set; }
/// <summary>
/// Whether the workflow instances to match have incidents.
/// </summary>
public bool? HasIncidents { get; set; }
/// <summary>
/// Whether the workflow instances to match are system workflows. Defaults to <c>false</c>.
/// </summary>
public bool? IsSystem { get; set; } = false;
/// <summary>
/// Represents a collection of filters for activities.
/// </summary>
public IEnumerable<ActivityFilter>? ActivityFilters { get; set; }
}

View file

@ -0,0 +1,17 @@
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// Cancels a workflow instance activity during an alteration
/// </summary>
public class CancelActivity : AlterationBase
{
/// <summary>
/// The ID of the activity to be cancelled. If not specified, the activity instance ID will be used.
/// </summary>
public string? ActivityId { get; set; }
/// <summary>
/// The ID of the activity instance to be cancelled. If specified, overrides <see cref="ActivityId"/>.
/// </summary>
public string? ActivityInstanceId { get; set; }
}

View file

@ -0,0 +1,12 @@
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// Migrates a workflow instance to a newer version in an alteration.
/// </summary>
public class Migrate : AlterationBase
{
/// <summary>
/// The target version to upgrade to.
/// </summary>
public int TargetVersion { get; set; }
}

View file

@ -0,0 +1,18 @@
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// Modifies a variable in a workflow instance alteration
/// </summary>
public class ModifyVariable : AlterationBase
{
/// <summary>
/// The ID of the variable to modify.
/// </summary>
public string VariableId { get; set; } = default!;
/// <summary>
/// The new value of the variable.
/// </summary>
public object? Value { get; set; }
}

View file

@ -0,0 +1,27 @@
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// The result of running a series of alterations.
/// </summary>
public class RunAlterationsResult
{
/// <summary>
/// The ID of the workflow instance that was altered.
/// </summary>
public string WorkflowInstanceId { get; set; } = string.Empty;
/// <summary>
/// A log of the alterations that were run.
/// </summary>
public AlterationLog Log { get; set; } = new();
/// <summary>
/// A flag indicating whether the workflow has scheduled work.
/// </summary>
public bool WorkflowHasScheduledWork { get; set; }
/// <summary>
/// A flag indicating whether the alterations have succeeded.
/// </summary>
public bool IsSuccessful { get; set; }
}

View file

@ -0,0 +1,17 @@
namespace Elsa.Api.Client.Resources.Alterations.Models;
/// <summary>
/// Schedules an activity for execution in an alteration.
/// </summary>
public class ScheduleActivity : AlterationBase
{
/// <summary>
/// The ID of the next activity to be scheduled. If not specified, the ActivityInstanceId will be used.
/// </summary>
public string? ActivityId { get; set; }
/// <summary>
/// The ID of the activity instance to be scheduled. If not specified, the ActivityId will be used.
/// </summary>
public string? ActivityInstanceId { get; set; }
}

View file

@ -0,0 +1,17 @@
namespace Elsa.Api.Client.Resources.Alterations.Requests;
/// <summary>
/// Represents a request to bulk retry workflow instances.
/// </summary>
public class BulkRetryRequest
{
/// <summary>
/// The IDs of the workflow instances that have incidents to be retried.
/// </summary>
public ICollection<string> WorkflowInstanceIds { get; set; } = new List<string>();
/// <summary>
/// An optional list of explicitly specified activity IDs to retry. If omitted, all faulted activities will be retried.
/// </summary>
public ICollection<string>? ActivityIds { get; set; }
}

View file

@ -0,0 +1,14 @@
using Elsa.Api.Client.Resources.Alterations.Models;
namespace Elsa.Api.Client.Resources.Alterations.Responses;
/// <summary>
/// Represents a response to bulk retry workflow instances.
/// </summary>
public class BulkRetryResponse
{
/// <summary>
/// The alterations that resulted from the bulk retry request
/// </summary>
public ICollection<RunAlterationsResult> Results { get;set; } = new List<RunAlterationsResult>();
}

View file

@ -0,0 +1,12 @@
namespace Elsa.Api.Client.Resources.Alterations.Responses;
/// <summary>
/// The response to the DryRun request
/// </summary>
public class DryRunResponse
{
/// <summary>
/// The list of workflow instance IDs that would be affected by a "Submit" request
/// </summary>
public ICollection<string> WorkflowInstanceIds { get; set; } = new List<string>();
}

View file

@ -0,0 +1,19 @@
using Elsa.Api.Client.Resources.Alterations.Models;
namespace Elsa.Api.Client.Resources.Alterations.Responses;
/// <summary>
/// The response from the "Get" alteration plan endpoint
/// </summary>
public class GetAlterationPlanResponse
{
/// <summary>
/// The alteration plan mathching the provided ID
/// </summary>
public AlterationPlan Plan { get; set; } = new();
/// <summary>
/// The list of jobs that exist for that AlterationPlan
/// </summary>
public ICollection<AlterationJob> Jobs { get; set; } = new List<AlterationJob>();
}

View file

@ -0,0 +1,19 @@
using Elsa.Api.Client.Resources.Alterations.Contracts;
namespace Elsa.Api.Client.Resources.Alterations.Responses;
/// <summary>
/// A plan that contains a list of alterations to be applied to a set of workflow instances.
/// </summary>
public class RunRequest
{
/// <summary>
/// The alterations to be applied.
/// </summary>
public ICollection<IAlteration> Alterations { get; set; } = new List<IAlteration>();
/// <summary>
/// The IDs of the workflow instances that this plan applies to.
/// </summary>
public ICollection<string> WorkflowInstanceIds { get; set; } = new List<string>();
}

View file

@ -0,0 +1,14 @@
using Elsa.Api.Client.Resources.Alterations.Models;
namespace Elsa.Api.Client.Resources.Alterations.Responses;
/// <summary>
/// The response to the Run endpoint
/// </summary>
public class RunResponse
{
/// <summary>
/// The alteration results of a Run request
/// </summary>
private ICollection<RunAlterationsResult> Results { get; set; } = new List<RunAlterationsResult>();
}

View file

@ -0,0 +1,12 @@
namespace Elsa.Api.Client.Resources.Alterations.Responses;
/// <summary>
/// The response to the "Submit" endpoint
/// </summary>
public class SubmitResponse
{
/// <summary>
/// The ID of the alteration plan created as part of the Submit request
/// </summary>
public string PlanId { get; set; } = string.Empty;
}

View file

@ -0,0 +1,25 @@
using Elsa.Api.Client.Resources.CommitStrategies.Models;
using Elsa.Api.Client.Shared.Models;
using Refit;
namespace Elsa.Api.Client.Resources.CommitStrategies.Contracts;
/// <summary>
/// Represents a client for the commit strategies API.
/// </summary>
public interface ICommitStrategiesApi
{
/// <summary>
/// Lists workflow commit strategies.
/// </summary>
/// <returns>A list response containing activity commit strategy descriptors and their count.</returns>
[Get("/descriptors/commit-strategies/workflows")]
Task<ListResponse<CommitStrategyDescriptor>> ListWorkflowStrategiesAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Lists activity commit strategies.
/// </summary>
/// <returns>A list response containing activity commit strategy descriptors and their count.</returns>
[Get("/descriptors/commit-strategies/activities")]
Task<ListResponse<CommitStrategyDescriptor>> ListActivityStrategiesAsync(CancellationToken cancellationToken = default);
}

View file

@ -0,0 +1,7 @@
namespace Elsa.Api.Client.Resources.CommitStrategies.Models;
/// <summary>
/// Represents a descriptor for a commit strategy, containing information such as its technical name,
/// display name, and description.
/// </summary>
public record CommitStrategyDescriptor(string Name, string DisplayName, string Description);

View file

@ -0,0 +1,19 @@
namespace Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
public class WorkflowCommitStateOptions
{
/// <summary>
/// Commit workflow state before the workflow starts.
/// </summary>
public bool Starting { get; set; }
/// <summary>
/// Commit workflow state before an activity executes, unless the activity is configured to not commit state.
/// </summary>
public bool ActivityExecuting { get; set; }
/// <summary>
/// Commit workflow state after an activity executes, unless the activity is configured to not commit state.
/// </summary>
public bool ActivityExecuted { get; set; }
}

View file

@ -29,4 +29,9 @@ public class WorkflowOptions
/// The type of <c>IIncidentStrategy</c> to use when a fault occurs in the workflow.
/// </summary>
public string? IncidentStrategyType { get; set; }
/// <summary>
/// The options for committing workflow state.
/// </summary>
public string? CommitStrategyName { get; set; }
}

View file

@ -0,0 +1,51 @@
using System.Text.Json.Serialization;
namespace Elsa.Api.Client.Resources.WorkflowInstances.Models;
/// <summary>
/// Holds information about an activity incident.
/// </summary>
public class ActivityIncident
{
/// <summary>
/// Initializes a new instance of the <see cref="ActivityIncident"/> class.
/// </summary>
[JsonConstructor]
public ActivityIncident()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ActivityIncident"/> class.
/// </summary>
/// <param name="activityId">The ID of the activity that caused the incident.</param>
/// <param name="activityType">The type of the activity that caused the incident.</param>
/// <param name="message">The message of the incident.</param>
/// <param name="exception">The exception that caused the incident.</param>
/// <param name="timestamp">The timestamp of the incident.</param>
public ActivityIncident(string activityId, string activityType, string message, ExceptionState? exception, DateTimeOffset timestamp)
{
ActivityId = activityId;
ActivityType = activityType;
Message = message;
Exception = exception;
Timestamp = timestamp;
}
/// <summary>The ID of the activity that caused the incident.</summary>
public string ActivityId { get; init; } = default!;
/// <summary>The type of the activity that caused the incident.</summary>
public string ActivityType { get; init; } = default!;
/// <summary>The message of the incident.</summary>
public string Message { get; init; } = default!;
/// <summary>The exception that caused the incident.</summary>
public ExceptionState? Exception { get; init; }
/// <summary>
/// The timestamp of the incident.
/// </summary>
public DateTimeOffset Timestamp { get; init; }
}

View file

@ -38,6 +38,11 @@ public class WorkflowState : Entity
/// </summary>
public ICollection<Bookmark> Bookmarks { get; set; } = new List<Bookmark>();
/// <summary>
/// A collection of incidents that may have occurred during execution.
/// </summary>
public ICollection<ActivityIncident> Incidents { get; set; } = new List<ActivityIncident>();
/// <summary>
/// The serialized workflow state, if any.
/// </summary>

View file

@ -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);
}

View file

@ -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)
{
@ -33,22 +19,26 @@ public class RequestHandlerInvokerMiddleware : IRequestMiddleware
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");
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;
if (handlers.Length > 1)
throw new InvalidOperationException($"Multiple handlers were found to handle the {requestType.FullName} request");
// Get result of task.
var taskWithReturnType = typeof(Task<>).MakeGenericType(responseType);
var resultProperty = taskWithReturnType.GetProperty(nameof(Task<object>.Result))!;
context.Responses.Add(resultProperty.GetValue(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.Response = resultProperty.GetValue(task)!;
// Invoke next middleware.
await _next(context);
await next(context);
}
}

View file

@ -36,7 +36,7 @@ public class RequestContext
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; }
}

View file

@ -41,23 +41,20 @@ public class DefaultMediator : IMediator
}
/// <inheritdoc />
public async Task<IEnumerable<T>> SendAsync<T>(IRequest<T> request, CancellationToken cancellationToken = default)
public async Task<T?> SendAsync<T>(IRequest<T> request, CancellationToken cancellationToken = default)
{
var responseType = typeof(T);
var context = new RequestContext(request, responseType, cancellationToken);
await _requestPipeline.ExecuteAsync(context);
if (context.Responses.All(x => x is T))
return context.Responses.Cast<T>().AsEnumerable();
throw new InvalidCastException($"Unable to cast objects in Responses property to type {typeof(T)}");
return (T?)context.Response;
}
/// <inheritdoc />
public async Task SendAsync(ICommand command, CancellationToken cancellationToken = default) => await SendAsync(command, _defaultCommandStrategy, cancellationToken);
/// <inheritdoc />
public async Task SendAsync(ICommand command, ICommandStrategy? strategy = default, CancellationToken cancellationToken = default)
public async Task SendAsync(ICommand command, ICommandStrategy? strategy = null, CancellationToken cancellationToken = default)
{
var resultType = typeof(Unit);
strategy ??= _defaultCommandStrategy;
@ -69,7 +66,7 @@ public class DefaultMediator : IMediator
public async Task<T> SendAsync<T>(ICommand<T> command, CancellationToken cancellationToken = default) => await SendAsync(command, _defaultCommandStrategy, cancellationToken);
/// <inheritdoc />
public async Task<T> SendAsync<T>(ICommand<T> command, ICommandStrategy strategy, CancellationToken cancellationToken = default)
public async Task<T> SendAsync<T>(ICommand<T> command, ICommandStrategy? strategy, CancellationToken cancellationToken = default)
{
var resultType = typeof(T);
strategy ??= _defaultCommandStrategy;
@ -83,7 +80,7 @@ public class DefaultMediator : IMediator
public async Task SendAsync(INotification notification, CancellationToken cancellationToken = default) => await SendAsync(notification, _defaultPublishingStrategy, cancellationToken);
/// <inheritdoc />
public async Task SendAsync(INotification notification, IEventPublishingStrategy? strategy = default, CancellationToken cancellationToken = default)
public async Task SendAsync(INotification notification, IEventPublishingStrategy? strategy = null, CancellationToken cancellationToken = default)
{
strategy ??= _defaultPublishingStrategy;
var context = new NotificationContext(notification, strategy, cancellationToken);

View file

@ -25,7 +25,7 @@ public class SignalManager
await Task.WhenAny(taskCompletionSource.Task, Task.Delay(millisecondsTimeout, cancellationTokenSource.Token));
cancellationTokenSource.Token.ThrowIfCancellationRequested();
_signals.TryRemove(signal, out _);
return taskCompletionSource.Task.Result;
return await taskCompletionSource.Task;
}
catch (OperationCanceledException)
{

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<Description>Provides an EF Core migrations for MySQL for the Agents Persistence module.</Description>
<PackageTags>elsa module agents semantic kernel llm ai persistence efcore entity framework core mysql</PackageTags>
</PropertyGroup>

View file

@ -34,6 +34,5 @@ public class EFCoreAgentPersistenceFeature(IModule module) : PersistenceFeatureB
AddEntityStore<ApiKeyDefinition, EFCoreApiKeyStore>();
AddEntityStore<ServiceDefinition, EFCoreServiceStore>();
AddEntityStore<AgentDefinition, EFCoreAgentStore>();
Services.AddScoped<IEntityModelCreatingHandler, SetupForOracle>();
}
}

View file

@ -1,25 +0,0 @@
using Elsa.Agents.Persistence.Entities;
using Elsa.EntityFrameworkCore;
using Elsa.EntityFrameworkCore.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
namespace Elsa.Agents.Persistence.EntityFrameworkCore;
/// <summary>
/// Represents a class that handles entity model creation for SQLite databases.
/// </summary>
public class SetupForOracle : IEntityModelCreatingHandler
{
/// <inheritdoc />
public void Handle(ElsaDbContextBase dbContext, ModelBuilder modelBuilder, IMutableEntityType entityType)
{
if(!dbContext.Database.IsOracle())
return;
// In order to use data more than 2000 char we have to use NCLOB.
// In Oracle, we have to explicitly say the column is NCLOB otherwise it would be considered nvarchar(2000).
modelBuilder.Entity<ServiceDefinition>().Property("SerializedSettings").HasColumnType("NCLOB");
modelBuilder.Entity<AgentDefinition>().Property("SerializedAgentConfig").HasColumnType("NCLOB");
}
}

View file

@ -18,7 +18,7 @@ public class AlterationPlanParams
public ICollection<IAlteration> Alterations { get; set; } = new List<IAlteration>();
/// <summary>
/// The IDs of the workflow instances that this plan applies to.
/// The filter used to determine which workflow instances that this plan applies to.
/// </summary>
public AlterationWorkflowInstanceFilter Filter { get; set; } = new();
}

View file

@ -6,7 +6,7 @@ using JetBrains.Annotations;
namespace Elsa.Alterations.Endpoints.Alterations.DryRun;
/// <summary>
/// Executes an alteration plan.
/// Determines which workflow instances a "Submit" request would target without actually running an alteration.
/// </summary>
[PublicAPI]
public class DryRun(IWorkflowInstanceFinder workflowInstanceFinder) : ElsaEndpoint<AlterationWorkflowInstanceFilter, Response>

View file

@ -6,7 +6,7 @@ using JetBrains.Annotations;
namespace Elsa.Alterations.Endpoints.Alterations.Get;
/// <summary>
/// Executes an alteration plan.
/// Gets an alteration plan and its associated jobs.
/// </summary>
[PublicAPI]
public class Get : ElsaEndpointWithoutRequest<Response>

View file

@ -5,7 +5,7 @@ using JetBrains.Annotations;
namespace Elsa.Alterations.Endpoints.Alterations.Run;
/// <summary>
/// Executes an alteration plan.
/// Executes an alteration plan by targeting workflow instances by ID.
/// </summary>
[PublicAPI]
public class Run : ElsaEndpoint<Request, Response>

View file

@ -8,7 +8,7 @@ using JetBrains.Annotations;
namespace Elsa.Alterations.Endpoints.Alterations.Submit;
/// <summary>
/// Executes an alteration plan.
/// Submits an alteration plan to be executed targeting workflow instances by a filter.
/// </summary>
[PublicAPI]
public class Submit : ElsaEndpoint<AlterationPlanParams, Response>

View file

@ -49,7 +49,8 @@ public class GenerateWorkflowInputAccessors(IOptions<CSharpOptions> options) : I
}
sb.AppendLine("}");
sb.AppendLine("var Inputs = new WorkflowInputsProxy(ExecutionContext);");
sb.AppendLine("var Inputs = new WorkflowInputsProxy(ExecutionContext);"); // Obsolete; use Input instead.
sb.AppendLine("var Input = Inputs;");
notification.AppendScript(sb.ToString());
return Task.CompletedTask;
}

View file

@ -48,7 +48,8 @@ public class GenerateWorkflowVariableAccessors(IOptions<CSharpOptions> options)
}
sb.AppendLine("}");
sb.AppendLine("var Variables = new WorkflowVariablesProxy(ExecutionContext);");
sb.AppendLine("var Variables = new WorkflowVariablesProxy(ExecutionContext);"); // Obsolete; use Variable instead.
sb.AppendLine("var Variable = Variables;");
notification.AppendScript(sb.ToString());
return Task.CompletedTask;
}

View file

@ -16,7 +16,6 @@ public partial class Globals
ExpressionExecutionContext = expressionExecutionContext;
Arguments = arguments;
ExecutionContext = new ExecutionContextProxy(expressionExecutionContext);
Input = new InputProxy(expressionExecutionContext);
Output = new OutputProxy(expressionExecutionContext);
Outcome = new OutcomeProxy(expressionExecutionContext);
}
@ -31,11 +30,6 @@ public partial class Globals
/// </summary>
public OutputProxy Output { get; set; }
/// <summary>
/// Provides access to workflow inputs.
/// </summary>
public InputProxy Input { get; set; }
/// <summary>
/// Gets the current execution context.
/// </summary>

View file

@ -1,30 +0,0 @@
using Elsa.Expressions.Models;
using Elsa.Extensions;
namespace Elsa.CSharp.Models;
/// <summary>
/// Provides access to workflow inputs.
/// </summary>
public class InputProxy
{
private readonly ExpressionExecutionContext _expressionExecutionContext;
/// <summary>
/// Initializes a new instance of the <see cref="InputProxy"/> class.
/// </summary>
public InputProxy(ExpressionExecutionContext expressionExecutionContext)
{
_expressionExecutionContext = expressionExecutionContext;
}
/// <summary>
/// Gets the value of the specified input.
/// </summary>
public object? Get(string name) => _expressionExecutionContext.GetInput(name);
/// <summary>
/// Gets the value of the specified input.
/// </summary>
public T? Get<T>(string name) => _expressionExecutionContext.GetInput<T>(name);
}

View file

@ -25,7 +25,7 @@ public class OutputProxy
/// <param name="activityIdOrName">The ID or name of the activity that produced the output.</param>
/// <param name="outputName">The name of the output.</param>
/// <returns>The value of the output.</returns>
public object? From(string activityIdOrName, string? outputName = default) => _expressionExecutionContext.GetOutput(activityIdOrName, outputName);
public object? From(string activityIdOrName, string? outputName = null) => _expressionExecutionContext.GetOutput(activityIdOrName, outputName);
/// <summary>
/// Gets the value of the specified output from the specified activity.
@ -33,7 +33,7 @@ public class OutputProxy
/// <param name="activityIdOrName">The ID or name of the activity that produced the output.</param>
/// <param name="outputName">The name of the output.</param>
/// <returns>The value of the output.</returns>
public T? From<T>(string activityIdOrName, string? outputName = default) => Get(activityIdOrName, outputName).ConvertTo<T>();
public T? From<T>(string activityIdOrName, string? outputName = null) => From(activityIdOrName, outputName).ConvertTo<T>();
/// <summary>
/// Gets the value of the specified output.
@ -42,7 +42,7 @@ public class OutputProxy
/// <param name="outputName">The name of the output.</param>
/// <returns>The value of the output.</returns>
[Obsolete("Use From instead.")]
public object? Get(string activityIdOrName, string? outputName = default) => From(activityIdOrName, outputName);
public object? Get(string activityIdOrName, string? outputName = null) => From(activityIdOrName, outputName);
/// <summary>
/// Gets the value of the specified output.
@ -51,7 +51,7 @@ public class OutputProxy
/// <param name="outputName">The name of the output.</param>
/// <returns>The value of the output.</returns>
[Obsolete("Use From instead.")]
public T? Get<T>(string activityIdOrName, string? outputName = default) => From<T>(activityIdOrName, outputName);
public T? Get<T>(string activityIdOrName, string? outputName = null) => From<T>(activityIdOrName, outputName);
/// <summary>
/// Gets the result of the last activity that executed.

View file

@ -21,8 +21,4 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="System.Text.Json" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,37 @@
namespace Elsa.Extensions;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
public static class ServiceCollectionExtensions
{
/// <summary>
/// Adds the service with a specific implementation type only if the combination
/// of service and implementation does not already exist in the service collection.
/// </summary>
public static IServiceCollection TryAddScopedImplementation<TService, TImplementation>(
this IServiceCollection services)
where TService : class
where TImplementation : class, TService
{
if (!services.Any(sd => sd.ServiceType == typeof(TService) && sd.ImplementationType == typeof(TImplementation)))
services.AddScoped<TService, TImplementation>();
return services;
}
/// <summary>
/// Adds the service with a specific implementation factory only if the combination
/// of service and implementation already doesn't exist.
/// </summary>
public static IServiceCollection TryAddScopedImplementation<TService>(
this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory)
where TService : class
{
if (services.All(sd => sd.ServiceType != typeof(TService)))
services.AddScoped(implementationFactory);
return services;
}
}

View file

@ -2,7 +2,6 @@ using Elsa.Common.Multitenancy;
using Elsa.Common.Multitenancy.EventHandlers;
using Elsa.Common.Multitenancy.HostedServices;
using Elsa.Common.RecurringTasks;
using Elsa.Extensions;
using Elsa.Features.Abstractions;
using Elsa.Features.Services;
using Microsoft.Extensions.DependencyInjection;
@ -41,6 +40,9 @@ public class MultitenancyFeature(IModule module) : FeatureBase(module)
.AddSingleton<ITenantFinder, DefaultTenantFinder>()
.AddSingleton<ITenantService, DefaultTenantService>()
// Order is important: Startup task first, then background and recurring tasks.
.AddSingleton<ITenantActivatedEvent, RunStartupTasks>()
.AddSingleton<RunBackgroundTasks>()
.AddSingleton<ITenantActivatedEvent>(sp => sp.GetRequiredService<RunBackgroundTasks>())
.AddSingleton<ITenantDeactivatedEvent>(sp => sp.GetRequiredService<RunBackgroundTasks>())
@ -49,10 +51,8 @@ public class MultitenancyFeature(IModule module) : FeatureBase(module)
.AddSingleton<ITenantActivatedEvent>(sp => sp.GetRequiredService<StartRecurringTasks>())
.AddSingleton<ITenantDeactivatedEvent>(sp => sp.GetRequiredService<StartRecurringTasks>())
.AddSingleton<ITenantActivatedEvent, RunStartupTasks>()
.AddSingleton<RecurringTaskScheduleManager>()
.AddSingleton<TenantEventsManager>()
.AddStartupTask<ConfigureRecurringTasksScheduleStartupTask>()
.AddScoped<DefaultTenantsProvider>()
.AddScoped<DefaultTenantResolver>()
.AddScoped<ITaskExecutor, TaskExecutor>()

View file

@ -7,7 +7,7 @@ namespace Elsa.Common.Multitenancy.EventHandlers;
public class StartRecurringTasks(RecurringTaskScheduleManager scheduleManager, ILogger<StartRecurringTasks> logger) : ITenantActivatedEvent, ITenantDeactivatedEvent
{
private readonly ICollection<ScheduledTimer> _scheduledTimers = new List<ScheduledTimer>();
private CancellationTokenSource _cancellationTokenSource = default!;
private CancellationTokenSource _cancellationTokenSource = null!;
public async Task TenantActivatedAsync(TenantActivatedEventArgs args)
{
@ -16,7 +16,7 @@ public class StartRecurringTasks(RecurringTaskScheduleManager scheduleManager, I
var tenantScope = args.TenantScope;
var tasks = tenantScope.ServiceProvider.GetServices<IRecurringTask>().ToList();
var taskExecutor = tenantScope.ServiceProvider.GetRequiredService<ITaskExecutor>();
foreach (var task in tasks)
{
var schedule = scheduleManager.GetScheduleFor(task.GetType());

View file

@ -11,7 +11,7 @@ public class DefaultTenantAccessor : ITenantAccessor
public Tenant? Tenant
{
get => CurrentTenantField.Value;
internal set => CurrentTenantField.Value = value;
private set => CurrentTenantField.Value = value;
}
public IDisposable PushContext(Tenant? tenant)

View file

@ -1,26 +0,0 @@
using Cronos;
using JetBrains.Annotations;
using Microsoft.Extensions.Options;
namespace Elsa.Common.RecurringTasks;
[UsedImplicitly]
public class ConfigureRecurringTasksScheduleStartupTask(IOptions<RecurringTaskOptions> options, ISystemClock systemClock, RecurringTaskScheduleManager recurringTaskScheduleManager) : IStartupTask
{
public Task ExecuteAsync(CancellationToken cancellationToken)
{
foreach(var entry in options.Value.Schedule.ScheduledTasks)
{
var taskType = entry.Key;
var intervalExpression = entry.Value;
var schedule = intervalExpression.Type switch
{
IntervalExpressionType.Cron => (ISchedule)new CronSchedule(systemClock, CronExpression.Parse(intervalExpression.Expression)),
IntervalExpressionType.Interval => new IntervalSchedule(TimeSpan.Parse(intervalExpression.Expression)),
_ => throw new NotSupportedException($"Interval expression type '{intervalExpression.Type}' is not supported.")
};
recurringTaskScheduleManager.ConfigureScheduledTask(taskType, schedule);
}
return Task.CompletedTask;
}
}

View file

@ -1,21 +1,31 @@
using Cronos;
using Microsoft.Extensions.Options;
namespace Elsa.Common.RecurringTasks;
public class RecurringTaskScheduleManager
public class RecurringTaskScheduleManager(IOptions<RecurringTaskOptions> options, ISystemClock systemClock)
{
public IDictionary<Type, ISchedule> ScheduledTasks { get; set; } = new Dictionary<Type, ISchedule>();
public void ConfigureScheduledTask<T>(ISchedule schedule) where T : IRecurringTask
{
ConfigureScheduledTask(typeof(T), schedule);
}
public void ConfigureScheduledTask(Type recurringTaskType, ISchedule schedule)
{
ScheduledTasks[recurringTaskType] = schedule;
}
public ISchedule GetScheduleFor(Type taskType)
{
return ScheduledTasks.TryGetValue(taskType, out var schedule) ? schedule : new IntervalSchedule(TimeSpan.FromMinutes(1));
if (!ScheduledTasks.TryGetValue(taskType, out var schedule))
{
var intervalExpression = options.Value.Schedule.ScheduledTasks.TryGetValue(taskType, out var expr) ? expr : null;
schedule = intervalExpression != null ? CreateSchedule(intervalExpression) : new IntervalSchedule(TimeSpan.FromMinutes(1));
ScheduledTasks[taskType] = schedule;
}
return schedule;
}
private ISchedule CreateSchedule(IntervalExpression intervalExpression)
{
return intervalExpression.Type switch
{
IntervalExpressionType.Cron => new CronSchedule(systemClock, CronExpression.Parse(intervalExpression.Expression)),
IntervalExpressionType.Interval => new IntervalSchedule(TimeSpan.Parse(intervalExpression.Expression)),
_ => throw new NotSupportedException($"Interval expression type '{intervalExpression.Type}' is not supported.")
};
}
}

View file

@ -18,7 +18,7 @@ public class TypeTypeConverter : TypeConverter
{
if (TypeAliasRegistry.GetType(stringValue) is { } type)
return type;
return Type.GetType(stringValue);
return Type.GetType(stringValue) ?? throw new InvalidOperationException($"Type '{stringValue}' not found.");
}
return base.ConvertFrom(context, culture, value);
}

View file

@ -14,12 +14,12 @@ public class V3_3 : Migration
/// <inheritdoc />
public override void Up()
{
Delete.Table("WorkflowInboxMessages");
Alter.Table("Triggers").AddColumn("TenantId").AsString().Nullable();
Alter.Table("Bookmarks").AddColumn("TenantId").AsString().Nullable();
Alter.Table("WorkflowExecutionLogRecords").AddColumn("TenantId").AsString().Nullable();
Alter.Table("ActivityExecutionRecords").AddColumn("TenantId").AsString().Nullable();
Alter.Table("KeyValuePairs").AddColumn("TenantId").AsString().Nullable();
Rename.Column("Key").OnTable("KeyValuePairs").To("Id");
IfDatabase("SqlServer", "Oracle", "MySql", "Postgres")
.Create
@ -56,37 +56,7 @@ public class V3_3 : Migration
Delete.Column("TenantId").FromTable("WorkflowExecutionLogRecords");
Delete.Column("TenantId").FromTable("ActivityExecutionRecords");
Delete.Column("TenantId").FromTable("KeyValuePairs");
IfDatabase("SqlServer", "Oracle", "MySql", "Postgres")
.Create
.Table("WorkflowInboxMessages")
.WithColumn("Id").AsString().PrimaryKey()
.WithColumn("ActivityTypeName").AsString().NotNullable().Indexed()
.WithColumn("WorkflowInstanceId").AsString().Nullable().Indexed()
.WithColumn("ActivityInstanceId").AsString().Nullable().Indexed()
.WithColumn("CorrelationId").AsString().Nullable().Indexed()
.WithColumn("Hash").AsString().NotNullable().Indexed()
.WithColumn("SerializedBookmarkPayload").AsString(MaxValue)
.WithColumn("SerializedInput").AsString(MaxValue).Nullable()
.WithColumn("CreatedAt").AsDateTimeOffset().Indexed()
.WithColumn("ExpiresAt").AsDateTimeOffset().Indexed()
;
IfDatabase("Sqlite")
.Create
.Table("WorkflowInboxMessages")
.WithColumn("Id").AsString().PrimaryKey()
.WithColumn("ActivityTypeName").AsString().NotNullable().Indexed()
.WithColumn("WorkflowInstanceId").AsString().Nullable().Indexed()
.WithColumn("ActivityInstanceId").AsString().Nullable().Indexed()
.WithColumn("CorrelationId").AsString().Nullable().Indexed()
.WithColumn("Hash").AsString().NotNullable().Indexed()
.WithColumn("SerializedBookmarkPayload").AsString(MaxValue)
.WithColumn("SerializedInput").AsString(MaxValue).Nullable()
.WithColumn("CreatedAt").AsDateTime2().Indexed()
.WithColumn("ExpiresAt").AsDateTime2().Indexed()
;
Rename.Column("Id").OnTable("KeyValuePairs").To("Key");
Delete.Table("BookmarkQueueItems");
}
}

View file

@ -1,4 +1,5 @@
using System.Collections;
using System.Dynamic;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
@ -84,8 +85,7 @@ public class SendEmail : Activity
/// <summary>
/// The activity to execute when an error occurs while trying to send the email.
/// </summary>
[Port]
public IActivity? Error { get; set; }
[Port] public IActivity? Error { get; set; }
/// <inheritdoc />
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
@ -99,7 +99,10 @@ public class SendEmail : Activity
message.From.Add(MailboxAddress.Parse(from));
message.Subject = Subject.GetOrDefault(context) ?? "";
var bodyBuilder = new BodyBuilder { HtmlBody = Body.GetOrDefault(context) };
var bodyBuilder = new BodyBuilder
{
HtmlBody = Body.GetOrDefault(context)
};
await AddAttachmentsAsync(context, bodyBuilder, cancellationToken);
message.Body = bodyBuilder.ToMessageBody();
@ -119,7 +122,10 @@ public class SendEmail : Activity
catch (Exception e)
{
logger.LogWarning(e, "Error while sending email message");
context.AddExecutionLogEntry("Error", e.Message, payload: new { e.StackTrace });
context.AddExecutionLogEntry("Error", e.Message, payload: new
{
e.StackTrace
});
await context.ScheduleActivityAsync(Error, OnErrorCompletedAsync);
}
}
@ -155,38 +161,54 @@ public class SendEmail : Activity
await AttachLocalFileAsync(bodyBuilder, path, cancellationToken);
break;
case byte[] bytes:
{
var fileName = $"Attachment-{++index}";
bodyBuilder.Attachments.Add(fileName, bytes, ContentType.Parse("application/binary"));
break;
}
{
var fileName = $"Attachment-{++index}";
bodyBuilder.Attachments.Add(fileName, bytes, ContentType.Parse("application/binary"));
break;
}
case Stream stream:
{
var fileName = $"Attachment-{++index}";
await bodyBuilder.Attachments.AddAsync(fileName, stream, ContentType.Parse("application/binary"), cancellationToken);
break;
}
{
var fileName = $"Attachment-{++index}";
await bodyBuilder.Attachments.AddAsync(fileName, stream, ContentType.Parse("application/binary"), cancellationToken);
break;
}
case EmailAttachment emailAttachment:
{
var fileName = emailAttachment.FileName ?? $"Attachment-{++index}";
var contentType = emailAttachment.ContentType ?? "application/binary";
var parsedContentType = ContentType.Parse(contentType);
{
var fileName = emailAttachment.FileName ?? $"Attachment-{++index}";
var contentType = emailAttachment.ContentType ?? "application/binary";
var parsedContentType = ContentType.Parse(contentType);
if (emailAttachment.Content is byte[] bytes)
bodyBuilder.Attachments.Add(fileName, bytes, parsedContentType);
if (emailAttachment.Content is byte[] bytes)
bodyBuilder.Attachments.Add(fileName, bytes, parsedContentType);
else if (emailAttachment.Content is Stream stream)
await bodyBuilder.Attachments.AddAsync(fileName, stream, parsedContentType, cancellationToken);
else if (emailAttachment.Content is Stream stream)
await bodyBuilder.Attachments.AddAsync(fileName, stream, parsedContentType, cancellationToken);
break;
}
break;
}
case ExpandoObject expandoObject:
{
var dictionary = new Dictionary<string, object>(expandoObject, StringComparer.OrdinalIgnoreCase);
var fileName = dictionary.GetValue<string>("FileName") ?? $"Attachment-{++index}";
var contentType = dictionary.GetValue<string>("ContentType") ?? "application/binary";
var parsedContentType = ContentType.Parse(contentType);
var content = dictionary.GetValue<object>("Content");
if (content is byte[] bytes)
bodyBuilder.Attachments.Add(fileName, bytes, parsedContentType);
else if (content is Stream stream)
await bodyBuilder.Attachments.AddAsync(fileName, stream, parsedContentType, cancellationToken);
break;
}
default:
{
var json = JsonSerializer.Serialize(attachmentObject);
var fileName = $"Attachment-{++index}";
bodyBuilder.Attachments.Add(fileName, Encoding.UTF8.GetBytes(json), ContentType.Parse("application/json"));
break;
}
{
var json = JsonSerializer.Serialize(attachmentObject);
var fileName = $"Attachment-{++index}";
bodyBuilder.Attachments.Add(fileName, Encoding.UTF8.GetBytes(json), ContentType.Parse("application/json"));
break;
}
}
}
}
@ -203,7 +225,24 @@ public class SendEmail : Activity
await bodyBuilder.Attachments.AddAsync(fileName, contentStream, ContentType.Parse(contentType), cancellationToken);
}
private IEnumerable InterpretAttachmentsModel(object attachments) => attachments is string text ? new[] { text } : attachments is IEnumerable enumerable ? enumerable : new[] { attachments };
private IEnumerable InterpretAttachmentsModel(object attachments)
{
if (attachments is byte[] bytes)
return new[]
{
bytes
};
return attachments is string text
? new[]
{
text
}
: attachments as IEnumerable ?? new[]
{
attachments
};
}
private void SetRecipientsEmailAddresses(InternetAddressList list, IEnumerable<string>? addresses)
{

View file

@ -27,7 +27,7 @@ public abstract class DesignTimeDbContextFactoryBase<TDbContext> : IDesignTimeDb
ConfigureBuilder(builder, connectionString);
return (TDbContext)Activator.CreateInstance(typeof(TDbContext), builder.Options, serviceProvider)!;
return (TDbContext)ActivatorUtilities.CreateInstance(serviceProvider, typeof(TDbContext), builder.Options);
}
/// <summary>

View file

@ -13,6 +13,5 @@ public class CommonPersistenceFeature(IModule module) : FeatureBase(module)
{
Services.AddScoped<IEntitySavingHandler, ApplyTenantId>();
Services.AddScoped<IEntityModelCreatingHandler, SetTenantIdFilter>();
Services.AddScoped<IEntityModelCreatingHandler, SetupForSqlite>();
}
}

View file

@ -10,17 +10,14 @@ namespace Elsa.EntityFrameworkCore;
/// <summary>
/// Class That enable Schema change for Migration
/// </summary>
public class DbSchemaAwareMigrationAssembly : MigrationsAssembly
public class DbSchemaAwareMigrationAssembly(
ICurrentDbContext currentContext,
IDbContextOptions options,
IMigrationsIdGenerator idGenerator,
IDiagnosticsLogger<DbLoggerCategory.Migrations> logger)
: MigrationsAssembly(currentContext, options, idGenerator, logger)
{
private readonly DbContext _context;
public DbSchemaAwareMigrationAssembly(ICurrentDbContext currentContext,
IDbContextOptions options, IMigrationsIdGenerator idGenerator,
IDiagnosticsLogger<DbLoggerCategory.Migrations> logger)
: base(currentContext, options, idGenerator, logger)
{
_context = currentContext.Context;
}
private readonly DbContext _context = currentContext.Context;
public override Migration CreateMigration(TypeInfo migrationClass, string activeProvider)
{

View file

@ -12,7 +12,6 @@
<PackageReference Include="Microsoft.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" PrivateAssets="all" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="Open.Linq.AsyncExtensions" />
<PackageReference Include="System.CommandLine" />

View file

@ -2,6 +2,7 @@ using Elsa.Common.Entities;
using Elsa.Common.Multitenancy;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.EntityFrameworkCore;
@ -16,8 +17,9 @@ public abstract class ElsaDbContextBase : DbContext, IElsaDbContextSchema
EntityState.Added,
EntityState.Modified,
};
protected readonly IServiceProvider ServiceProvider;
protected IServiceProvider ServiceProvider { get; }
private readonly ElsaDbContextOptions? _elsaDbContextOptions;
public string? TenantId { get; set; }
/// <summary>
@ -39,18 +41,18 @@ public abstract class ElsaDbContextBase : DbContext, IElsaDbContextSchema
protected ElsaDbContextBase(DbContextOptions options, IServiceProvider serviceProvider) : base(options)
{
ServiceProvider = serviceProvider;
var elsaDbContextOptions = options.FindExtension<ElsaDbContextOptionsExtension>()?.Options;
// ReSharper disable once VirtualMemberCallInConstructor
Schema = !string.IsNullOrWhiteSpace(elsaDbContextOptions?.SchemaName) ? elsaDbContextOptions.SchemaName : ElsaSchema;
_elsaDbContextOptions = options.FindExtension<ElsaDbContextOptionsExtension>()?.Options;
// ReSharper disable once VirtualMemberCallInConstructor
Schema = !string.IsNullOrWhiteSpace(_elsaDbContextOptions?.SchemaName) ? _elsaDbContextOptions.SchemaName : ElsaSchema;
var tenantAccessor = serviceProvider.GetService<ITenantAccessor>();
var tenantId = tenantAccessor?.Tenant?.Id;
if(!string.IsNullOrWhiteSpace(tenantId))
if (!string.IsNullOrWhiteSpace(tenantId))
TenantId = tenantId;
}
/// <inheritdoc/>
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
@ -58,26 +60,33 @@ public abstract class ElsaDbContextBase : DbContext, IElsaDbContextSchema
return await base.SaveChangesAsync(cancellationToken);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.EnableSensitiveDataLogging();
#if NET9_0_OR_GREATER
optionsBuilder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
#endif
}
/// <inheritdoc />
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
if (!string.IsNullOrWhiteSpace(Schema))
{
if (!Database.IsSqlite())
modelBuilder.HasDefaultSchema(Schema);
}
if (!string.IsNullOrWhiteSpace(Schema))
modelBuilder.HasDefaultSchema(Schema);
var additionalConfigurations = _elsaDbContextOptions?.GetModelConfigurations(this);
additionalConfigurations?.Invoke(modelBuilder);
var entityTypeHandlers = ServiceProvider.GetServices<IEntityModelCreatingHandler>().ToList();
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
foreach (var entityType in modelBuilder.Model.GetEntityTypes().ToList())
{
foreach (var handler in entityTypeHandlers)
{
foreach (var handler in entityTypeHandlers)
handler.Handle(this, modelBuilder, entityType);
}
}
}
private async Task OnBeforeSavingAsync(CancellationToken cancellationToken)
{
var handlers = ServiceProvider.GetServices<IEntitySavingHandler>().ToList();

Some files were not shown because too many files have changed in this diff Show more