From 421ace1f5b6f4e5042aa48e345562cae15192a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20DEVILLARD?= Date: Wed, 12 Feb 2025 23:56:10 +0100 Subject: [PATCH] ConfigurePermisssions Add Get Endpoints Add entity framework Core feature SqlLite implementation --- src/modules/Elsa.Connections.Api/Constants.cs | 11 ++++ .../{List/Get.cs => Get/Endpoint.cs} | 8 +-- .../List/Endpoint.cs | 2 +- .../Endpoints/Add/Endpoint.cs | 52 +--------------- .../Endpoints/Delete/Endpoint.cs | 2 +- .../Endpoints/Get/Endpoint.cs | 33 ++++++++++ .../Endpoints/Get/Request.cs | 12 ++++ .../Endpoints/List/Endpoint.cs | 2 +- .../Endpoints/Update/Endpoint.cs | 58 +++++++++++++++++ .../ConnectionConfigurationModel.cs | 22 ------- ...tadataModel.cs => ConnectionInputModel.cs} | 0 .../Elsa.Connections.Models.csproj | 14 ++--- .../DbContextFactories.cs | 15 +++++ ...sistence.EntityFrameworkCore.Sqlite.csproj | 19 ++++++ .../FodyWeavers.xml | 3 + .../20250212220543_V3_3.Designer.cs | 60 ++++++++++++++++++ .../Migrations/20250212220543_V3_3.cs | 62 +++++++++++++++++++ .../ConnectionDbContextModelSnapshot.cs | 57 +++++++++++++++++ .../SqliteProvidersExtensions.cs | 28 +++++++++ .../efcore-3.3.sh | 1 + .../Configurations.cs | 3 + .../EFCoreConnectionStore.cs | 54 ++++++++++++++++ .../Extensions.cs | 22 +++++++ .../Feature.cs | 34 ++++++++++ 24 files changed, 485 insertions(+), 89 deletions(-) create mode 100644 src/modules/Elsa.Connections.Api/Constants.cs rename src/modules/Elsa.Connections.Api/Endpoints/ActivityConnectionDescriptor/{List/Get.cs => Get/Endpoint.cs} (65%) create mode 100644 src/modules/Elsa.Connections.Api/Endpoints/Get/Endpoint.cs create mode 100644 src/modules/Elsa.Connections.Api/Endpoints/Get/Request.cs create mode 100644 src/modules/Elsa.Connections.Api/Endpoints/Update/Endpoint.cs delete mode 100644 src/modules/Elsa.Connections.Models/ConnectionConfigurationModel.cs rename src/modules/Elsa.Connections.Models/{ConnectionConfigurationMetadataModel.cs => ConnectionInputModel.cs} (100%) create mode 100644 src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/DbContextFactories.cs create mode 100644 src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite.csproj create mode 100644 src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/FodyWeavers.xml create mode 100644 src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Migrations/20250212220543_V3_3.Designer.cs create mode 100644 src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Migrations/20250212220543_V3_3.cs create mode 100644 src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Migrations/ConnectionDbContextModelSnapshot.cs create mode 100644 src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/SqliteProvidersExtensions.cs create mode 100644 src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/efcore-3.3.sh create mode 100644 src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/EFCoreConnectionStore.cs create mode 100644 src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/Extensions.cs create mode 100644 src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/Feature.cs diff --git a/src/modules/Elsa.Connections.Api/Constants.cs b/src/modules/Elsa.Connections.Api/Constants.cs new file mode 100644 index 000000000..86cfd703b --- /dev/null +++ b/src/modules/Elsa.Connections.Api/Constants.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Elsa.Connections.Api; +public class Constants +{ + public const string PermissionsNamespace = "connections"; +} diff --git a/src/modules/Elsa.Connections.Api/Endpoints/ActivityConnectionDescriptor/List/Get.cs b/src/modules/Elsa.Connections.Api/Endpoints/ActivityConnectionDescriptor/Get/Endpoint.cs similarity index 65% rename from src/modules/Elsa.Connections.Api/Endpoints/ActivityConnectionDescriptor/List/Get.cs rename to src/modules/Elsa.Connections.Api/Endpoints/ActivityConnectionDescriptor/Get/Endpoint.cs index 9a60511c5..5718d454b 100644 --- a/src/modules/Elsa.Connections.Api/Endpoints/ActivityConnectionDescriptor/List/Get.cs +++ b/src/modules/Elsa.Connections.Api/Endpoints/ActivityConnectionDescriptor/Get/Endpoint.cs @@ -2,19 +2,19 @@ using Elsa.Connections.Contracts; using Elsa.Workflows.Models; -namespace Elsa.Connections.Api.Endpoints.ActivityConnectionDescriptor.List; +namespace Elsa.Connections.Api.Endpoints.ActivityConnectionDescriptor.Get; -public class Get(IConnectionDescriptorRegistry store) : ElsaEndpointWithoutRequest> +public class Endpoint(IConnectionDescriptorRegistry store) : ElsaEndpointWithoutRequest> { public override void Configure() { Get("/connection-configuration/input-descriptor/{ActivityType}"); - AllowAnonymous(); + ConfigurePermissions($"{Constants.PermissionsNamespace}/descriptor:read"); } public override async Task HandleAsync(CancellationToken ct) { - string type = Route("ActivityType"); + var type = Route("ActivityType"); var config = await store.GetConnectionDescriptor(type); if (config == null) diff --git a/src/modules/Elsa.Connections.Api/Endpoints/ActivityConnectionDescriptor/List/Endpoint.cs b/src/modules/Elsa.Connections.Api/Endpoints/ActivityConnectionDescriptor/List/Endpoint.cs index 0d5f77649..1fbc961ac 100644 --- a/src/modules/Elsa.Connections.Api/Endpoints/ActivityConnectionDescriptor/List/Endpoint.cs +++ b/src/modules/Elsa.Connections.Api/Endpoints/ActivityConnectionDescriptor/List/Endpoint.cs @@ -22,7 +22,7 @@ public class List(IConnectionDescriptorRegistry registry) : ElsaEndpointWithoutR public override void Configure() { Get("/connection-configuration/descriptors"); - AllowAnonymous(); + ConfigurePermissions($"{Constants.PermissionsNamespace}/descriptor:read"); } public override Task> ExecuteAsync(CancellationToken ct) diff --git a/src/modules/Elsa.Connections.Api/Endpoints/Add/Endpoint.cs b/src/modules/Elsa.Connections.Api/Endpoints/Add/Endpoint.cs index 40a98b176..ed24cd6f3 100644 --- a/src/modules/Elsa.Connections.Api/Endpoints/Add/Endpoint.cs +++ b/src/modules/Elsa.Connections.Api/Endpoints/Add/Endpoint.cs @@ -14,7 +14,7 @@ public class Endpoint(IConnectionStore store, IIdentityGenerator identityGenerat public override void Configure() { Post("/connection-configuration"); - AllowAnonymous(); + ConfigurePermissions($"{Constants.PermissionsNamespace}:write"); } public override async Task ExecuteAsync(ConnectionInputModel model, CancellationToken ct) @@ -52,53 +52,3 @@ public class Endpoint(IConnectionStore store, IIdentityGenerator identityGenerat } } -public class EndpointUpdate(IConnectionStore store) : ElsaEndpoint -{ - public override void Configure() - { - Put("/connection-configuration/{id}"); - AllowAnonymous(); - } - - public override async Task ExecuteAsync(ConnectionInputModel model, CancellationToken ct) - { - var id = Route("id")!; - var entity = await store.GetAsync(id, ct); - - if(entity == null) - { - await SendNotFoundAsync(ct); - return null!; - } - - var isNameDuplicate = await IsNameDuplicateAsync(model.Name, id, ct); - - if (isNameDuplicate) - { - AddError("Another connection already exist with the specified name"); - await SendErrorsAsync(cancellation: ct); - return entity.ToModel(); - } - - entity.Name = model.Name; - entity.Description = model.Description; - entity.ConnectionConfiguration = model.ConnectionConfiguration; - - - await store.UpdateAsync(entity, ct); - - return entity.ToModel(); - } - - private async Task IsNameDuplicateAsync(string name, string id, CancellationToken cancellationToken) - { - var entities = await store.FindAsync(new ConnectionDefinitionFilter - { - NotId = id, - Name = name - }); - - return !(entities == null); - } -} - diff --git a/src/modules/Elsa.Connections.Api/Endpoints/Delete/Endpoint.cs b/src/modules/Elsa.Connections.Api/Endpoints/Delete/Endpoint.cs index 94ddc4c5d..c4c358494 100644 --- a/src/modules/Elsa.Connections.Api/Endpoints/Delete/Endpoint.cs +++ b/src/modules/Elsa.Connections.Api/Endpoints/Delete/Endpoint.cs @@ -9,7 +9,7 @@ public class Endpoint(IConnectionStore store) : ElsaEndpoint public override void Configure() { Delete("/connection-configuration/{id}"); - AllowAnonymous(); + ConfigurePermissions($"{Constants.PermissionsNamespace}:delete"); } public override async Task HandleAsync(Request req, CancellationToken ct) diff --git a/src/modules/Elsa.Connections.Api/Endpoints/Get/Endpoint.cs b/src/modules/Elsa.Connections.Api/Endpoints/Get/Endpoint.cs new file mode 100644 index 000000000..4a5324a09 --- /dev/null +++ b/src/modules/Elsa.Connections.Api/Endpoints/Get/Endpoint.cs @@ -0,0 +1,33 @@ +using Elsa.Abstractions; +using Elsa.Connections.Models; +using Elsa.Connections.Persistence.Contracts; +using Elsa.Connections.Api.Extensions; + +namespace Elsa.Connections.Api.Endpoints.Get; + +public class Endpoint(IConnectionStore store) : ElsaEndpoint +{ + public override void Configure() + { + Get("/connection-configuration/{id}"); + ConfigurePermissions($"{Constants.PermissionsNamespace}:read"); + } + + public override async Task ExecuteAsync(Request req, CancellationToken ct) + { + var entity = await store.FindAsync( + new Persistence.Filters.ConnectionDefinitionFilter + { + Id = req.Id + } + ); + + if (entity == null) + { + await SendNotFoundAsync(ct); + return null!; + } + + return entity.ToModel(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Connections.Api/Endpoints/Get/Request.cs b/src/modules/Elsa.Connections.Api/Endpoints/Get/Request.cs new file mode 100644 index 000000000..024c0c055 --- /dev/null +++ b/src/modules/Elsa.Connections.Api/Endpoints/Get/Request.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Elsa.Connections.Api.Endpoints.Get; +public class Request +{ + [Required] public string Id { get; set; } = default!; +} diff --git a/src/modules/Elsa.Connections.Api/Endpoints/List/Endpoint.cs b/src/modules/Elsa.Connections.Api/Endpoints/List/Endpoint.cs index 9a81a3eae..8c82bc3c6 100644 --- a/src/modules/Elsa.Connections.Api/Endpoints/List/Endpoint.cs +++ b/src/modules/Elsa.Connections.Api/Endpoints/List/Endpoint.cs @@ -15,7 +15,7 @@ public class Endpoint(IConnectionStore store) : ElsaEndpointWithoutRequest> ExecuteAsync(CancellationToken ct) diff --git a/src/modules/Elsa.Connections.Api/Endpoints/Update/Endpoint.cs b/src/modules/Elsa.Connections.Api/Endpoints/Update/Endpoint.cs new file mode 100644 index 000000000..350e37999 --- /dev/null +++ b/src/modules/Elsa.Connections.Api/Endpoints/Update/Endpoint.cs @@ -0,0 +1,58 @@ +using Elsa.Abstractions; +using Elsa.Connections.Models; +using Elsa.Connections.Persistence.Contracts; +using Elsa.Connections.Persistence.Filters; +using Elsa.Connections.Api.Extensions; + +namespace Elsa.Connections.Api.Endpoints.Update; + +public class Endpoint(IConnectionStore store) : ElsaEndpoint +{ + public override void Configure() + { + Put("/connection-configuration/{id}"); + ConfigurePermissions($"{Constants.PermissionsNamespace}:write"); + } + + public override async Task ExecuteAsync(ConnectionInputModel model, CancellationToken ct) + { + var id = Route("id")!; + var entity = await store.GetAsync(id, ct); + + if (entity == null) + { + await SendNotFoundAsync(ct); + return null!; + } + + var isNameDuplicate = await IsNameDuplicateAsync(model.Name, id, ct); + + if (isNameDuplicate) + { + AddError("Another connection already exist with the specified name"); + await SendErrorsAsync(cancellation: ct); + return entity.ToModel(); + } + + entity.Name = model.Name; + entity.Description = model.Description; + entity.ConnectionConfiguration = model.ConnectionConfiguration; + + + await store.UpdateAsync(entity, ct); + + return entity.ToModel(); + } + + private async Task IsNameDuplicateAsync(string name, string id, CancellationToken cancellationToken) + { + var entities = await store.FindAsync(new ConnectionDefinitionFilter + { + NotId = id, + Name = name + }); + + return !(entities == null); + } +} + diff --git a/src/modules/Elsa.Connections.Models/ConnectionConfigurationModel.cs b/src/modules/Elsa.Connections.Models/ConnectionConfigurationModel.cs deleted file mode 100644 index 4d6b2f356..000000000 --- a/src/modules/Elsa.Connections.Models/ConnectionConfigurationModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text; -using System.Text.Json.Nodes; -using System.Threading.Tasks; - -namespace Elsa.Connections.Models; - -public class ConnectionConfigurationModel -{ - public string Id { get; set; } - [Required] - public string? Name { get; set; } - public string Description { get; set; } - - public JsonObject ConnectionConfiguration { get; set; } - - [Required] - public string ConnectionType { get; set; } -} diff --git a/src/modules/Elsa.Connections.Models/ConnectionConfigurationMetadataModel.cs b/src/modules/Elsa.Connections.Models/ConnectionInputModel.cs similarity index 100% rename from src/modules/Elsa.Connections.Models/ConnectionConfigurationMetadataModel.cs rename to src/modules/Elsa.Connections.Models/ConnectionInputModel.cs diff --git a/src/modules/Elsa.Connections.Models/Elsa.Connections.Models.csproj b/src/modules/Elsa.Connections.Models/Elsa.Connections.Models.csproj index 52ed24920..1bd63d620 100644 --- a/src/modules/Elsa.Connections.Models/Elsa.Connections.Models.csproj +++ b/src/modules/Elsa.Connections.Models/Elsa.Connections.Models.csproj @@ -1,13 +1,9 @@  - - net8.0 - enable - enable - - - - - + + Provides models that can be shared between the API and API clients + elsa module connections api + Elsa.Connections + diff --git a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/DbContextFactories.cs b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/DbContextFactories.cs new file mode 100644 index 000000000..cd4fca536 --- /dev/null +++ b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/DbContextFactories.cs @@ -0,0 +1,15 @@ +using Elsa.EntityFrameworkCore.Abstractions; +using Elsa.EntityFrameworkCore.Extensions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite; + +[UsedImplicitly] +public class SqliteConnectionDbContextFactory : DesignTimeDbContextFactoryBase +{ + protected override void ConfigureBuilder(DbContextOptionsBuilder builder, string connectionString) + { + builder.UseElsaSqlite(GetType().Assembly, connectionString); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite.csproj b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite.csproj new file mode 100644 index 000000000..0f350f1e2 --- /dev/null +++ b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite.csproj @@ -0,0 +1,19 @@ + + + + Provides an EF Core migrations for SQLite for the Connection Persistence module. + elsa module persistence efcore entity framework core sqlite + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/FodyWeavers.xml b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/FodyWeavers.xml new file mode 100644 index 000000000..ac6b5b292 --- /dev/null +++ b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Migrations/20250212220543_V3_3.Designer.cs b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Migrations/20250212220543_V3_3.Designer.cs new file mode 100644 index 000000000..8f3eb3658 --- /dev/null +++ b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Migrations/20250212220543_V3_3.Designer.cs @@ -0,0 +1,60 @@ +// +using Elsa.Connections.Persistence.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite.Migrations +{ + [DbContext(typeof(ConnectionDbContext))] + [Migration("20250212220543_V3_3")] + partial class V3_3 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "8.0.12"); + + modelBuilder.Entity("Elsa.Connections.Persistence.Entities.ConnectionDefinition", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConnectionConfiguration") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ConnectionType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TenantId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_ConnectionDefinition_Name"); + + b.HasIndex("TenantId") + .HasDatabaseName("IX_ConnectionDefinition_TenantId"); + + b.ToTable("ConnectionDefinitions", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Migrations/20250212220543_V3_3.cs b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Migrations/20250212220543_V3_3.cs new file mode 100644 index 000000000..dd64e3a5f --- /dev/null +++ b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Migrations/20250212220543_V3_3.cs @@ -0,0 +1,62 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite.Migrations +{ + /// + public partial class V3_3 : Migration + { + private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + + /// + public V3_3(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + { + _schema = schema; + } + + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: _schema.Schema); + + migrationBuilder.CreateTable( + name: "ConnectionDefinitions", + schema: _schema.Schema, + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + Description = table.Column(type: "TEXT", nullable: true), + ConnectionConfiguration = table.Column(type: "TEXT", nullable: false), + ConnectionType = table.Column(type: "TEXT", nullable: false), + TenantId = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ConnectionDefinitions", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_ConnectionDefinition_Name", + schema: _schema.Schema, + table: "ConnectionDefinitions", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ConnectionDefinition_TenantId", + schema: _schema.Schema, + table: "ConnectionDefinitions", + column: "TenantId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ConnectionDefinitions", + schema: _schema.Schema); + } + } +} diff --git a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Migrations/ConnectionDbContextModelSnapshot.cs b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Migrations/ConnectionDbContextModelSnapshot.cs new file mode 100644 index 000000000..50c535a68 --- /dev/null +++ b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/Migrations/ConnectionDbContextModelSnapshot.cs @@ -0,0 +1,57 @@ +// +using Elsa.Connections.Persistence.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite.Migrations +{ + [DbContext(typeof(ConnectionDbContext))] + partial class ConnectionDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "8.0.12"); + + modelBuilder.Entity("Elsa.Connections.Persistence.Entities.ConnectionDefinition", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConnectionConfiguration") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ConnectionType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TenantId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_ConnectionDefinition_Name"); + + b.HasIndex("TenantId") + .HasDatabaseName("IX_ConnectionDefinition_TenantId"); + + b.ToTable("ConnectionDefinitions", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/SqliteProvidersExtensions.cs b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/SqliteProvidersExtensions.cs new file mode 100644 index 000000000..9a38e3722 --- /dev/null +++ b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/SqliteProvidersExtensions.cs @@ -0,0 +1,28 @@ +using System.Reflection; +using Elsa.Connections.Persistence.EntityFrameworkCore; + +// ReSharper disable once CheckNamespace +namespace Elsa.EntityFrameworkCore.Extensions; + +/// +/// Provides extensions to configure EF Core to use Sqlite. +/// +public static class ConnectionsSqliteProvidersExtensions +{ + private static Assembly Assembly => typeof(ConnectionsSqliteProvidersExtensions).Assembly; + + /// + /// Configures the feature to use Sqlite. + /// + public static EFCoreConnectionPersistenceFeature UseSqlite(this EFCoreConnectionPersistenceFeature feature, string? connectionString = null, ElsaDbContextOptions? options = null) + { + feature.UseSqlite(Assembly, connectionString, options); + return feature; + } + + public static EFCoreConnectionPersistenceFeature UseSqlite(this EFCoreConnectionPersistenceFeature feature, Func connectionStringFunc, ElsaDbContextOptions? options = null) + { + feature.UseSqlite(Assembly, connectionStringFunc, options); + return feature; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/efcore-3.3.sh b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/efcore-3.3.sh new file mode 100644 index 000000000..e5eb4289e --- /dev/null +++ b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore.Sqlite/efcore-3.3.sh @@ -0,0 +1 @@ +ef-migration-runtime-schema --interface Elsa.EntityFrameworkCore.IElsaDbContextSchema --efOptions "migrations add V3_3 -c ConnectionDbContext -o Migrations" \ No newline at end of file diff --git a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/Configurations.cs b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/Configurations.cs index 14c375ca5..cc2a3167d 100644 --- a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/Configurations.cs +++ b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/Configurations.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using Elsa.Connections.Models; using Elsa.Connections.Persistence.Entities; +using Elsa.EntityFrameworkCore.Extensions; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -13,7 +14,9 @@ public class Configurations : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { + builder.Property(x => x.ConnectionConfiguration).HasJsonValueConversion(); builder.HasIndex(x => x.Name).HasDatabaseName($"IX_{nameof(ConnectionDefinition)}_{nameof(ConnectionDefinition.Name)}"); builder.HasIndex(x => x.TenantId).HasDatabaseName($"IX_{nameof(ConnectionDefinition)}_{nameof(ConnectionDefinition.TenantId)}"); + } } diff --git a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/EFCoreConnectionStore.cs b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/EFCoreConnectionStore.cs new file mode 100644 index 000000000..b7eea5679 --- /dev/null +++ b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/EFCoreConnectionStore.cs @@ -0,0 +1,54 @@ +using Elsa.Connections.Persistence.Contracts; +using Elsa.Connections.Persistence.Entities; +using Elsa.Connections.Persistence.Filters; +using Elsa.EntityFrameworkCore; +using JetBrains.Annotations; + +namespace Elsa.Connections.Persistence.EntityFrameworkCore; + +/// +/// An EF Core implementation of . +/// +[UsedImplicitly] +public class EFCoreConnectionStore(EntityStore store) : IConnectionStore +{ + public Task AddAsync(ConnectionDefinition entity, CancellationToken cancellationToken = default) + { + return store.AddAsync(entity, cancellationToken); + } + + public Task UpdateAsync(ConnectionDefinition entity, CancellationToken cancellationToken = default) + { + return store.UpdateAsync(entity, cancellationToken); + } + + public Task GetAsync(string id, CancellationToken cancellationToken = default) + { + var filter = new ConnectionDefinitionFilter + { + Id = id + }; + + return FindAsync(filter, cancellationToken); + } + + public Task FindAsync(ConnectionDefinitionFilter filter, CancellationToken cancellationToken = default) + { + return store.FindAsync(filter.Apply, cancellationToken); + } + + public Task> ListAsync(CancellationToken cancellationToken = default) + { + return store.ListAsync(cancellationToken); + } + + public Task DeleteAsync(ConnectionDefinition entity, CancellationToken cancellationToken = default) + { + return store.DeleteAsync(entity, cancellationToken); + } + + public Task> FindManyAsync(ConnectionDefinitionFilter filter, CancellationToken cancellationToken = default) + { + return store.QueryAsync(filter.Apply, cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/Extensions.cs b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/Extensions.cs new file mode 100644 index 000000000..718e6446c --- /dev/null +++ b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/Extensions.cs @@ -0,0 +1,22 @@ +using Elsa.Connections.Persistence.Features; +using Elsa.Connections.Persistence.EntityFrameworkCore; +using JetBrains.Annotations; + +// ReSharper disable once CheckNamespace +namespace Elsa.Agents; + +/// +/// Provides extensions to the feature. +/// +[PublicAPI] +public static class Extensions +{ + /// + /// Configures the to use EF Core persistence providers. + /// + public static ConnectionPersistenceFeature UseEntityFrameworkCore(this ConnectionPersistenceFeature feature, Action? configure = default) + { + feature.Module.Configure(configure); + return feature; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/Feature.cs b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/Feature.cs new file mode 100644 index 000000000..5d8d9524b --- /dev/null +++ b/src/modules/Elsa.Connections.Persistence.EntityFrameworkCore/Feature.cs @@ -0,0 +1,34 @@ +using Elsa.Connections.Persistence.Entities; +using Elsa.Connections.Persistence.Features; +using Elsa.EntityFrameworkCore; +using Elsa.EntityFrameworkCore.EntityHandlers; +using Elsa.Features.Attributes; +using Elsa.Features.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Connections.Persistence.EntityFrameworkCore; + +/// +/// Configures the default workflow runtime to use EF Core persistence providers. +/// +[DependsOn(typeof(ConnectionPersistenceFeature))] +public class EFCoreConnectionPersistenceFeature(IModule module) : PersistenceFeatureBase(module) +{ + /// + public override void Configure() + { + Module.Configure(feature => + { + feature + .UseConnectionStore(sp=> sp.GetRequiredService()) + ; + }); + } + + /// + public override void Apply() + { + base.Apply(); + AddEntityStore(); + } +} \ No newline at end of file