From 3a369789978d27e7eee2ce0a51708b8422a602b5 Mon Sep 17 00:00:00 2001 From: Daniel Gerst Date: Fri, 3 Jul 2026 14:43:35 +0200 Subject: [PATCH] Update to latest code and nugets packages --- Directory.Packages.props | 58 ++++---- .../Configurations/WebApiConfiguration.cs | 7 +- .../Common/StartupTasks/ExampleStartupTask.cs | 4 +- .../GerstITS.Examples.Api.csproj | 11 +- .../Module.Configurations.cs | 1 + GerstITS.Examples.Api/Module.cs | 1 + GerstITS.Examples.Api/Program.cs | 1 + GerstITS.Examples.Api/appsettings.json | 18 ++- .../Jobs/SayHelloWorldJob.cs | 2 +- .../SayHelloWorldWithDefaultNameUsageJob.cs | 2 +- .../Module.cs | 3 +- .../Example/Mappings/IntegerMapping.cs | 2 +- .../GerstITS.Examples.Logic.csproj | 10 ++ GerstITS.Examples.Logic/Module.cs | 3 +- .../IReadOnlyRepositoryExtensions.cs | 2 +- ...GerstITS.Examples.WebClient.Console.csproj | 3 +- GerstITS.Examples.WebClient.Console/Module.cs | 1 + .../Tests/TestRunner.cs | 58 +++++--- .../appsettings.json | 2 +- GerstITS.Examples.sln | 72 +++++++++ .../Contracts/ICountry.cs | 26 ---- .../Contracts/Serialization/Country.cs | 11 -- .../Contracts/Serialization/SearchCriteria.cs | 17 --- .../ExampleApiClientCreatingRule.cs | 2 +- README.md | 138 ++++++++++++++++++ 25 files changed, 339 insertions(+), 116 deletions(-) delete mode 100644 GerstITS.WebClients.Example.Api/Contracts/ICountry.cs delete mode 100644 GerstITS.WebClients.Example.Api/Contracts/Serialization/Country.cs delete mode 100644 GerstITS.WebClients.Example.Api/Contracts/Serialization/SearchCriteria.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 1438ca8..569a221 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,34 +6,42 @@ - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + - + + + \ No newline at end of file diff --git a/GerstITS.Examples.Api/Common/Configurations/WebApiConfiguration.cs b/GerstITS.Examples.Api/Common/Configurations/WebApiConfiguration.cs index fc4b105..bbc0517 100644 --- a/GerstITS.Examples.Api/Common/Configurations/WebApiConfiguration.cs +++ b/GerstITS.Examples.Api/Common/Configurations/WebApiConfiguration.cs @@ -1,4 +1,5 @@ -using GerstITS.Authentication.OpenId; +using GerstITS.Authentication.ApiKey; +using GerstITS.Authentication.OpenId; using GerstITS.Data.EntityFramework; using GerstITS.System.Configurations; using GerstITS.Web.Api.Builder; @@ -11,6 +12,7 @@ internal sealed class WebApiConfiguration : GerstITS.System.Configurations.IConf #region Properties public IServerConfiguration Server { get; } + public IApiKeyConfiguration ApiKey { get; } public IOpenIdConfiguration OpenId { get; } public ISwaggerConfiguration Swagger { get; set; } public IEntityFrameworkMigrationConfiguration EntityFrameworkMigration { get; set; } @@ -24,7 +26,8 @@ internal sealed class WebApiConfiguration : GerstITS.System.Configurations.IConf var prefix = this.ToConfigurationPrefix(); EntityFrameworkMigration = new EntityFrameworkMigrationConfiguration(prefix, configuration); - //OpenId = new OpenIdConfiguration(prefix, configuration); + ApiKey = new ApiKeyConfiguration(prefix, configuration); + OpenId = new OpenIdConfiguration(prefix, configuration); Server = new ServerConfiguration(prefix, configuration); Swagger = new SwaggerConfiguration(prefix, configuration); } diff --git a/GerstITS.Examples.Api/Common/StartupTasks/ExampleStartupTask.cs b/GerstITS.Examples.Api/Common/StartupTasks/ExampleStartupTask.cs index eb8de52..692d8b4 100644 --- a/GerstITS.Examples.Api/Common/StartupTasks/ExampleStartupTask.cs +++ b/GerstITS.Examples.Api/Common/StartupTasks/ExampleStartupTask.cs @@ -7,14 +7,14 @@ namespace GerstITS.Examples.Api.StartupTasks; internal class ExampleStartupTask : IStartupTask { - #region Fields# + #region Fields private readonly IExampleProvider _provider; private readonly IJsonConvert _jsonConvert; #endregion - #region Construtcors + #region Constructors public ExampleStartupTask(IExampleProvider provider, IJsonConvert jsonConvert) diff --git a/GerstITS.Examples.Api/GerstITS.Examples.Api.csproj b/GerstITS.Examples.Api/GerstITS.Examples.Api.csproj index 4239de7..d7f0335 100644 --- a/GerstITS.Examples.Api/GerstITS.Examples.Api.csproj +++ b/GerstITS.Examples.Api/GerstITS.Examples.Api.csproj @@ -6,7 +6,7 @@ © 2025 Gerst ITS Gerst ITS Gerst ITS Examples Web Api - Exapmle Web Api + Example Web Api 0.0.0.0 0.0.0.0 0.0.0.0 @@ -18,7 +18,7 @@ true 1591 - bin\Release\net8.0\GerstITS.Example.Api.xml + bin\Debug\net10.0\GerstITS.Examples.Api.xml @@ -27,18 +27,23 @@ true 1591 - bin\Release\net8.0\GerstITS.Example.Api.xml + bin\Release\net10.0\GerstITS.Examples.Api.xml + + + + + diff --git a/GerstITS.Examples.Api/Module.Configurations.cs b/GerstITS.Examples.Api/Module.Configurations.cs index a9f8771..b60ee12 100644 --- a/GerstITS.Examples.Api/Module.Configurations.cs +++ b/GerstITS.Examples.Api/Module.Configurations.cs @@ -11,6 +11,7 @@ public sealed partial class Module container.AddSingleton(); container.AddSingleton(c => c.GetService().EntityFrameworkMigration); + container.AddSingleton(c => c.GetService().ApiKey); container.AddSingleton(c => c.GetService().OpenId); container.AddSingleton(c => c.GetService().Server); container.AddSingleton(c => c.GetService().Swagger); diff --git a/GerstITS.Examples.Api/Module.cs b/GerstITS.Examples.Api/Module.cs index aba8ba2..cad4a73 100644 --- a/GerstITS.Examples.Api/Module.cs +++ b/GerstITS.Examples.Api/Module.cs @@ -10,6 +10,7 @@ public sealed partial class Module : IIoCModule { RegisterConfigurations(container); RegisterExceptionHandling(container); + RegisterSession(container); RegisterStartupTasks(container); } diff --git a/GerstITS.Examples.Api/Program.cs b/GerstITS.Examples.Api/Program.cs index 6a2cbc7..2816d83 100644 --- a/GerstITS.Examples.Api/Program.cs +++ b/GerstITS.Examples.Api/Program.cs @@ -31,6 +31,7 @@ public class Program .UseAuthentication() .UseAuthorization() .UseRouting() + .UseSession() .UseEndpoints(endpoints => endpoints.MapControllers()) .UseRewriteUnknownPathsToIndexSite(ApplicationEnvironment.WebApi.BaseUrl) .UseSystemIndependentStaticFiles() diff --git a/GerstITS.Examples.Api/appsettings.json b/GerstITS.Examples.Api/appsettings.json index 39c7877..e0c61db 100644 --- a/GerstITS.Examples.Api/appsettings.json +++ b/GerstITS.Examples.Api/appsettings.json @@ -7,7 +7,23 @@ } }, "AllowedHosts": "*", + "ConnectionStrings": { + "CustomerDbContext": "Host=localhost;Port=5432;Database=gerstits_examples;Username=postgres;Password=postgres" + }, + "Mail": { + "SmtpServer": "smtp.example.com", + "SmtpPort": 587, + "SenderDisplayName": "Gerst ITS Examples", + "SenderAddress": "noreply@example.com", + "Username": "noreply@example.com", + "Password": "change-me", + "UseSsl": true + }, "WebApi": { + "ApiKey": { + "Header": "X-Api-Key", + "Key": "example-api-key" + }, "Swagger": { "SupportEMail": "support@example.com", "TermsOfService": "https://www.example.com/impressum/", @@ -16,7 +32,7 @@ "Url": "https://opensource.org/licenses/MIT" } }, - "EntityFrameworkmigration": { + "EntityFrameworkMigration": { "AutoMigrate": true }, "Server": { diff --git a/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldJob.cs b/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldJob.cs index 57abec4..e33b480 100644 --- a/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldJob.cs +++ b/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldJob.cs @@ -16,7 +16,7 @@ public class SayHelloWorldJob : JobBase #endregion - #region Cosntructors + #region Constructors public SayHelloWorldJob(SayHelloWorldJobConfiguration configuration, ISystemClock systemClock) diff --git a/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldWithDefaultNameUsageJob.cs b/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldWithDefaultNameUsageJob.cs index de269d1..c78d0ed 100644 --- a/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldWithDefaultNameUsageJob.cs +++ b/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldWithDefaultNameUsageJob.cs @@ -15,7 +15,7 @@ public class SayHelloWorldWithDefaultNameUsageJob : JobBase #endregion - #region Cosntructors + #region Constructors public SayHelloWorldWithDefaultNameUsageJob(SayHelloWorldWithDefaultNameUsageJobConfiguration configuration) { diff --git a/GerstITS.Examples.Jobs.SayHelloWorld/Module.cs b/GerstITS.Examples.Jobs.SayHelloWorld/Module.cs index f1a6a02..662f5d8 100644 --- a/GerstITS.Examples.Jobs.SayHelloWorld/Module.cs +++ b/GerstITS.Examples.Jobs.SayHelloWorld/Module.cs @@ -13,7 +13,8 @@ public sealed class Module : IIoCModule public void RegisterComponents(IServiceCollection container) { container.RegisterJob(); - } + container.RegisterJob(IgnoreConditions.EntityFramework, IgnoreConditions.Swagger); + } #endregion } \ No newline at end of file diff --git a/GerstITS.Examples.Logic/Example/Mappings/IntegerMapping.cs b/GerstITS.Examples.Logic/Example/Mappings/IntegerMapping.cs index 32badda..37ae917 100644 --- a/GerstITS.Examples.Logic/Example/Mappings/IntegerMapping.cs +++ b/GerstITS.Examples.Logic/Example/Mappings/IntegerMapping.cs @@ -4,7 +4,7 @@ namespace GerstITS.Examples.Logic.Example; internal sealed class IntegerMapping : Profile { - #region Construtcors + #region Constructors public IntegerMapping() { diff --git a/GerstITS.Examples.Logic/GerstITS.Examples.Logic.csproj b/GerstITS.Examples.Logic/GerstITS.Examples.Logic.csproj index eee5ea9..456ceda 100644 --- a/GerstITS.Examples.Logic/GerstITS.Examples.Logic.csproj +++ b/GerstITS.Examples.Logic/GerstITS.Examples.Logic.csproj @@ -31,9 +31,19 @@ + + + + + + + + + + diff --git a/GerstITS.Examples.Logic/Module.cs b/GerstITS.Examples.Logic/Module.cs index 01ca2e8..7cfb9eb 100644 --- a/GerstITS.Examples.Logic/Module.cs +++ b/GerstITS.Examples.Logic/Module.cs @@ -9,9 +9,10 @@ public sealed partial class Module : IIoCModule public void RegisterComponents(IServiceCollection container) { + RegisterCustomers(container); RegisterExample(container); RegisterShared(container); - } + } #endregion } \ No newline at end of file diff --git a/GerstITS.Examples.Logic/Shared/Extensions/IReadOnlyRepositoryExtensions.cs b/GerstITS.Examples.Logic/Shared/Extensions/IReadOnlyRepositoryExtensions.cs index 1120736..8ca3774 100644 --- a/GerstITS.Examples.Logic/Shared/Extensions/IReadOnlyRepositoryExtensions.cs +++ b/GerstITS.Examples.Logic/Shared/Extensions/IReadOnlyRepositoryExtensions.cs @@ -4,7 +4,7 @@ using GerstITS.Data; namespace GerstITS.Examples.Logic.Shared; -internal static class ISearchEngineExtensions +internal static class IReadOnlyRepositoryExtensions { #region Methods diff --git a/GerstITS.Examples.WebClient.Console/GerstITS.Examples.WebClient.Console.csproj b/GerstITS.Examples.WebClient.Console/GerstITS.Examples.WebClient.Console.csproj index 931d618..5ddf7db 100644 --- a/GerstITS.Examples.WebClient.Console/GerstITS.Examples.WebClient.Console.csproj +++ b/GerstITS.Examples.WebClient.Console/GerstITS.Examples.WebClient.Console.csproj @@ -41,9 +41,10 @@ + + - diff --git a/GerstITS.Examples.WebClient.Console/Module.cs b/GerstITS.Examples.WebClient.Console/Module.cs index d5a2d2e..ed5115f 100644 --- a/GerstITS.Examples.WebClient.Console/Module.cs +++ b/GerstITS.Examples.WebClient.Console/Module.cs @@ -12,6 +12,7 @@ public class Module : IIoCModule public void RegisterComponents(IServiceCollection container) { + container.AddTransient(); container.AddTransient(); container.AddTransient(c => new ConfigurationBuilder().SetBasePath(AppDomain.CurrentDomain.BaseDirectory) diff --git a/GerstITS.Examples.WebClient.Console/Tests/TestRunner.cs b/GerstITS.Examples.WebClient.Console/Tests/TestRunner.cs index 71c31ba..373fc74 100644 --- a/GerstITS.Examples.WebClient.Console/Tests/TestRunner.cs +++ b/GerstITS.Examples.WebClient.Console/Tests/TestRunner.cs @@ -1,4 +1,4 @@ -using GerstITS.Examples.WebClients.Examples.Api; +using GerstITS.Examples.WebClients.Examples.Api; using GerstITS.IoC.DotNetCore; using GerstITS.Web.WebClients; @@ -12,37 +12,55 @@ internal sealed class TestRunner : IApplicationStart #endregion - #region Construtors + #region Constructors public TestRunner(IWebClient webClient) { _webClient = webClient; - } - + } + #endregion - #region ITestRunner + #region IApplicationStart public void Run() { - var result1 = _webClient.Request() - .Execute(c => c.Search(new SearchCriteria - { SortBy = nameof(Country.Name), SortDirection = SortingDirections.Descending, Skip = 5, Take = 5})); + var created = _webClient.Request() + .Execute(c => c.Create(new Customer + { FirstName = "Erika", LastName = $"Mustermann-{Guid.NewGuid():N}", EMail = "erika.mustermann@example.com" })); - var result2 = _webClient.Request() - .Execute(c => c.Get(result1.Value.Result.First().Id)); + var loaded = _webClient.Request() + .Execute(c => c.Get(created.Value.Id)); - var test3 = _webClient.Request() - .Execute(c => c.Create(new Country {Name = $"Country-{Guid.NewGuid()}"})); + var searched = _webClient.Request() + .Execute(c => c.Search(new CustomerSearchCriteria + { Name = "Mustermann", SortBy = nameof(Customer.LastName), SortDirection = SortingDirections.Descending, Skip = 0, Take = 5 })); - test3.Value.Name += "_Renamed"; + loaded.Value.FirstName = "Max"; - var test4 = _webClient.Request() - .Execute(c => c.Update(test3.Value)); + var updated = _webClient.Request() + .Execute(c => c.Update(loaded.Value)); + + var note = _webClient.Request() + .Execute(c => c.AddNote(updated.Value.Id, new CustomerNote { Text = "Created by the example web client." })); + + var notes = _webClient.Request() + .Execute(c => c.GetNotes(updated.Value.Id)); + + var deleted = _webClient.Request() + .Execute(c => c.Delete(updated.Value.Id)); + + Print($"---> {nameof(TestRunner)}: Found {searched.Value.TotalCount} customer(s), added {notes.Value.Count()} note(s), deleted: {deleted.Value}"); + } - var test5 = _webClient.Request() - .Execute(c => c.Delete(test3.Value.Id)); - } - #endregion -} \ No newline at end of file + + #region Methods + + private static void Print(string message) + { + global::System.Console.WriteLine(message); + } + + #endregion +} diff --git a/GerstITS.Examples.WebClient.Console/appsettings.json b/GerstITS.Examples.WebClient.Console/appsettings.json index df91a93..9f49388 100644 --- a/GerstITS.Examples.WebClient.Console/appsettings.json +++ b/GerstITS.Examples.WebClient.Console/appsettings.json @@ -8,6 +8,6 @@ }, "AllowedHosts": "*", "ExampleApiClient": { - "BaseUrl": "https://localhost:44350/api/v1.1/" + "BaseUrl": "https://localhost:44350/api/" } } diff --git a/GerstITS.Examples.sln b/GerstITS.Examples.sln index 4e8c5f7..3514e18 100644 --- a/GerstITS.Examples.sln +++ b/GerstITS.Examples.sln @@ -19,32 +19,104 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Directory.Packages.props = Directory.Packages.props EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GerstITS.Examples.Data", "GerstITS.Examples.Data\GerstITS.Examples.Data.csproj", "{BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GerstITS.Examples.Tests", "GerstITS.Examples.Tests\GerstITS.Examples.Tests.csproj", "{1F71C429-A9B3-46C2-BDEE-8F8413765EB5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Debug|x64.ActiveCfg = Debug|Any CPU + {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Debug|x64.Build.0 = Debug|Any CPU + {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Debug|x86.ActiveCfg = Debug|Any CPU + {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Debug|x86.Build.0 = Debug|Any CPU {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Release|Any CPU.Build.0 = Release|Any CPU + {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Release|x64.ActiveCfg = Release|Any CPU + {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Release|x64.Build.0 = Release|Any CPU + {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Release|x86.ActiveCfg = Release|Any CPU + {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Release|x86.Build.0 = Release|Any CPU {A2E37A84-9331-48C7-8CCA-991528063087}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A2E37A84-9331-48C7-8CCA-991528063087}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2E37A84-9331-48C7-8CCA-991528063087}.Debug|x64.ActiveCfg = Debug|Any CPU + {A2E37A84-9331-48C7-8CCA-991528063087}.Debug|x64.Build.0 = Debug|Any CPU + {A2E37A84-9331-48C7-8CCA-991528063087}.Debug|x86.ActiveCfg = Debug|Any CPU + {A2E37A84-9331-48C7-8CCA-991528063087}.Debug|x86.Build.0 = Debug|Any CPU {A2E37A84-9331-48C7-8CCA-991528063087}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2E37A84-9331-48C7-8CCA-991528063087}.Release|Any CPU.Build.0 = Release|Any CPU + {A2E37A84-9331-48C7-8CCA-991528063087}.Release|x64.ActiveCfg = Release|Any CPU + {A2E37A84-9331-48C7-8CCA-991528063087}.Release|x64.Build.0 = Release|Any CPU + {A2E37A84-9331-48C7-8CCA-991528063087}.Release|x86.ActiveCfg = Release|Any CPU + {A2E37A84-9331-48C7-8CCA-991528063087}.Release|x86.Build.0 = Release|Any CPU {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Debug|x64.ActiveCfg = Debug|Any CPU + {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Debug|x64.Build.0 = Debug|Any CPU + {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Debug|x86.ActiveCfg = Debug|Any CPU + {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Debug|x86.Build.0 = Debug|Any CPU {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Release|Any CPU.ActiveCfg = Release|Any CPU {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Release|Any CPU.Build.0 = Release|Any CPU + {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Release|x64.ActiveCfg = Release|Any CPU + {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Release|x64.Build.0 = Release|Any CPU + {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Release|x86.ActiveCfg = Release|Any CPU + {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Release|x86.Build.0 = Release|Any CPU {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Debug|x64.ActiveCfg = Debug|Any CPU + {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Debug|x64.Build.0 = Debug|Any CPU + {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Debug|x86.ActiveCfg = Debug|Any CPU + {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Debug|x86.Build.0 = Debug|Any CPU {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Release|Any CPU.Build.0 = Release|Any CPU + {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Release|x64.ActiveCfg = Release|Any CPU + {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Release|x64.Build.0 = Release|Any CPU + {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Release|x86.ActiveCfg = Release|Any CPU + {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Release|x86.Build.0 = Release|Any CPU {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Debug|x64.ActiveCfg = Debug|Any CPU + {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Debug|x64.Build.0 = Debug|Any CPU + {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Debug|x86.ActiveCfg = Debug|Any CPU + {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Debug|x86.Build.0 = Debug|Any CPU {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Release|Any CPU.ActiveCfg = Release|Any CPU {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Release|Any CPU.Build.0 = Release|Any CPU + {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Release|x64.ActiveCfg = Release|Any CPU + {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Release|x64.Build.0 = Release|Any CPU + {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Release|x86.ActiveCfg = Release|Any CPU + {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Release|x86.Build.0 = Release|Any CPU + {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Debug|x64.ActiveCfg = Debug|Any CPU + {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Debug|x64.Build.0 = Debug|Any CPU + {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Debug|x86.ActiveCfg = Debug|Any CPU + {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Debug|x86.Build.0 = Debug|Any CPU + {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Release|Any CPU.Build.0 = Release|Any CPU + {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Release|x64.ActiveCfg = Release|Any CPU + {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Release|x64.Build.0 = Release|Any CPU + {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Release|x86.ActiveCfg = Release|Any CPU + {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Release|x86.Build.0 = Release|Any CPU + {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Debug|x64.ActiveCfg = Debug|Any CPU + {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Debug|x64.Build.0 = Debug|Any CPU + {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Debug|x86.ActiveCfg = Debug|Any CPU + {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Debug|x86.Build.0 = Debug|Any CPU + {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Release|Any CPU.Build.0 = Release|Any CPU + {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Release|x64.ActiveCfg = Release|Any CPU + {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Release|x64.Build.0 = Release|Any CPU + {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Release|x86.ActiveCfg = Release|Any CPU + {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GerstITS.WebClients.Example.Api/Contracts/ICountry.cs b/GerstITS.WebClients.Example.Api/Contracts/ICountry.cs deleted file mode 100644 index b10ce07..0000000 --- a/GerstITS.WebClients.Example.Api/Contracts/ICountry.cs +++ /dev/null @@ -1,26 +0,0 @@ -using GerstITS.Web.WebClients; - -namespace GerstITS.Examples.WebClients.Examples.Api; - -public interface ICountry : IWebService -{ - [WebMethod(WebMethods.Get), - ResourceUrl("Country/{id}")] - Country Get(int id); - - [WebMethod(WebMethods.Get), - ResourceUrl("Country/Search")] - SearchResult Search(SearchCriteria criteria); - - [WebMethod(WebMethods.Post), - ResourceUrl("Country")] - Country Create(Country model); - - [WebMethod(WebMethods.Put), - ResourceUrl("Country")] - void Update(Country model); - - [WebMethod(WebMethods.Delete), - ResourceUrl("Country/{id}")] - void Delete(int id); -} \ No newline at end of file diff --git a/GerstITS.WebClients.Example.Api/Contracts/Serialization/Country.cs b/GerstITS.WebClients.Example.Api/Contracts/Serialization/Country.cs deleted file mode 100644 index dca3174..0000000 --- a/GerstITS.WebClients.Example.Api/Contracts/Serialization/Country.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace GerstITS.Examples.WebClients.Examples.Api; - -public class Country -{ - #region Properties - - public int Id { get; set; } - public string Name { get; set; } - - #endregion -} \ No newline at end of file diff --git a/GerstITS.WebClients.Example.Api/Contracts/Serialization/SearchCriteria.cs b/GerstITS.WebClients.Example.Api/Contracts/Serialization/SearchCriteria.cs deleted file mode 100644 index 912fb77..0000000 --- a/GerstITS.WebClients.Example.Api/Contracts/Serialization/SearchCriteria.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace GerstITS.Examples.WebClients.Examples.Api; - -public class SearchCriteria -{ - #region Properties - - internal int Id { get; set; } - public string Name { get; set; } - - public int? Skip { get; set; } - public int? Take { get; set; } - - public string SortBy { get; set; } - public SortingDirections SortDirection { get; set; } - - #endregion -} \ No newline at end of file diff --git a/GerstITS.WebClients.Example.Api/CreationRules/ExampleApiClientCreatingRule.cs b/GerstITS.WebClients.Example.Api/CreationRules/ExampleApiClientCreatingRule.cs index c4040ef..85c29ad 100644 --- a/GerstITS.WebClients.Example.Api/CreationRules/ExampleApiClientCreatingRule.cs +++ b/GerstITS.WebClients.Example.Api/CreationRules/ExampleApiClientCreatingRule.cs @@ -7,7 +7,7 @@ internal sealed class ExampleApiClientCreatingRule : RestApiClientCreationRuleBa { #region Properties - protected override Type[] SupportedTypes => new[] { typeof(ICountry) }; + protected override Type[] SupportedTypes => new[] { typeof(ICustomer) }; #endregion diff --git a/README.md b/README.md index e69de29..5340c88 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,138 @@ +# GerstITS Framework Examples + +This solution demonstrates all features of the GerstITS framework packages in a small, coherent customer management domain. + +## Solution layout + +| Project | Purpose | +|---------|---------| +| `GerstITS.Examples.Api` | ASP.NET Core Web API host: fluent controllers, versioning, exception handling, Swagger, authentication, session cache, jobs and auto migration. | +| `GerstITS.Examples.Logic` | Business logic: providers, search engine integration, validation rules, customizations and AutoMapper profiles. | +| `GerstITS.Examples.Data` | Data access: entities with framework aspects, EF Core DbContext, entity mappings, database configurator and migrations. | +| `GerstITS.Examples.Jobs.SayHelloWorld` | Scheduled Quartz jobs with cron configuration. | +| `GerstITS.WebClients.Example.Api` | Typed REST client contract for the example API (`ICustomer`). | +| `GerstITS.Examples.WebClient.Console` | Console client consuming the API via `IWebClient` plus demos of the common/system utilities. | +| `GerstITS.Examples.Tests` | xUnit tests using the GerstITS test framework categories. | + +## Feature matrix + +### GerstITS.Common +| Feature | Example | +|---------|---------| +| String/object/enum/enumerable extensions | `WebClient.Console/Tests/CommonFeaturesRunner.cs` | +| Fluent exception handling (`Is(...).ThenThrows<...>().WithMessage(...)`) | `CommonFeaturesRunner.cs`, `Logic/Customers/CustomerProvider.cs` (`Import`) | +| `Range` + `Intersects` | `CommonFeaturesRunner.cs` | +| AES cryptography extensions (`Encrypt`/`Decrypt`) | `CommonFeaturesRunner.cs` | +| `ApplicationEnvironment` detection | `Api/Program.cs` (`IsEfCoreActionStarted`) | + +### GerstITS.System +| Feature | Example | +|---------|---------| +| `ISystemClock` | `MaintenanceController.Ping`, `SayHelloWorldJob` | +| `IJsonConvert` | `Api/Common/StartupTasks/ExampleStartupTask.cs`, `CommonFeaturesRunner.cs` | +| `IActivator` | `CommonFeaturesRunner.cs` | +| `ICache` (encrypted, session backed) | `Api/Controllers/1.1/CacheController.cs` | +| Customizations (`CustomizationBase`, `ApplyCustomizations()`) | `Logic/Customers/Customizations/CustomerDisplayNameCustomization.cs` | +| Configuration prefix pattern (`ToConfigurationPrefix`) | `Api/Common/Configurations/WebApiConfiguration.*.cs` | + +### GerstITS.IoC / GerstITS.IoC.DotNetCore +| Feature | Example | +|---------|---------| +| `IIoCModule` modules (partial pattern) | `Module.cs` in every project | +| `DotNetCoreApplicationBootstrapper` + `IApplicationStart` | `WebClient.Console/Program.cs`, `Tests/TestRunner.cs`, `Tests/CommonFeaturesRunner.cs` | +| `IStartupTask` with priorities | `Api/Common/StartupTasks/ExampleStartupTask.cs` | +| Namespace based assembly scanning (`Register...Of()`) | `Logic/Module.Customers.cs` | + +### GerstITS.Data / GerstITS.Data.EntityFramework / GerstITS.Data.EntityFramework.PostgreSql +| Feature | Example | +|---------|---------| +| `EntityBase` + aspects (`IActivatable`, `ICreatable`, `IModifiable`, `IVersionable`) | `Data/Entities/CustomerEntity.cs` | +| Historization (`IHistoricizable`) | `Data/Entities/CustomerNoteEntity.cs` | +| `EntityMappingBase` | `Data/Mappings/*.cs` | +| `EntityFrameworkDbContextBase` + `IDbContext` | `Data/CustomerDbContext.cs` | +| `PostgreSqlDatabaseConfiguratorBase` | `Data/Migrations/CustomerDatabaseConfigurator.cs` | +| Design time factory + migrations | `Data/Migrations/DesignTime/CustomerDbContextDesignTimeFactory.cs`, `Data/Migrations/20260703*_InitialCreate.cs` | +| Registration (`AddMigration`, `AddEntityFrameworkSession`) | `Data/Module.cs` | +| Repository queries (active only by default, `includeInactive` opt-in) | `Logic/Customers/CustomerProvider.cs` (`GetById`) | +| Soft delete by default, `forceDelete` opt-in | `CustomerProvider.Delete` | +| Bulk operations (`BulkSave`) | `CustomerProvider.Import` | +| Query projection (`Project`) | `Logic/Customers/Search/CustomerSearchQueryBuilderRule.cs` | +| `ThrowsAnExceptionIfNotExisting` / `EntityNotFoundException` | `CustomerProvider.cs`, `Api/.../EntityNotFoundExceptionTransformation.cs` | +| Auto migration on startup (`WebApi:EntityFrameworkMigration:AutoMigrate`) | `Api/appsettings.json` | + +> `GerstITS.Data.EntityFramework.SqlServer` works exactly like the PostgreSql package: derive from +> `SqlServerDatabaseConfiguratorBase` instead. Note that only one `IDatabaseConfigurator` +> implementation is allowed per assembly. + +### GerstITS.Search +| Feature | Example | +|---------|---------| +| `ISearchEngine` + `SearchResult` | `Logic/Customers/CustomerProvider.cs` (`Search`) | +| `SearchFilterBase` (paging + sorting) | `Logic/Customers/Contracts/CustomerSearchFilter.cs` | +| `SearchQueryBuilderRuleBase` | `Logic/Customers/Search/CustomerSearchQueryBuilderRule.cs` | +| `InitializationRuleBase` (filter defaults) | `Logic/Customers/Search/CustomerSearchFilterInitializationRule.cs` | +| `PagingValidationRuleBase` | `Logic/Customers/Validation/CustomerSearchFilterValidationRule.cs` | +| Custom `ISortingProvider` | `Logic/Shared/Search/Sorting/SortingProvider.cs` | + +### GerstITS.Validation +| Feature | Example | +|---------|---------| +| `ValidationRuleBase` (FluentValidation) | `Logic/Customers/Validation/CustomerValidationRule.cs`, `Logic/Shared/Validation/IdValidationRule.cs` | +| `IsEMail()` rule builder extension | `CustomerValidationRule.cs` | +| Automatic validation in fluent controllers (`IValidator`) | All controllers via `FluentApiControllerBase` | + +### GerstITS.Mapping.AutoMapper +| Feature | Example | +|---------|---------| +| Profiles + `RegisterMappingsOf()` | `Logic/Customers/Mappings/CustomerMapping.cs`, `Logic/Example/Mappings/IntegerMapping.cs` | + +### GerstITS.Web / GerstITS.Web.Api / GerstITS.Web.Api.Swagger / GerstITS.Web.Rest +| Feature | Example | +|---------|---------| +| `HostingStartup` fluent pipeline | `Api/Program.cs` | +| `FluentApiControllerBase` (`Api().Use(...).Get/Create/Update/Delete`) | `Api/Controllers/1.1/*.cs` | +| API versioning (side by side controllers) | `Api/Controllers/1.0` vs `Api/Controllers/1.1` | +| Global exception transformations | `Api/Common/ExceptionHandling/Transformations/*.cs` | +| Server configuration (CORS, HSTS, forwarded headers) | `Api/Common/Configurations/WebApiConfiguration.Server.*.cs` | +| Swagger configuration incl. security schemes | `Api/Common/Configurations/WebApiConfiguration.Swagger*.cs`, `WebApiConfiguration.OpenApiSecurityScheme.cs` | +| Session based cache storage (`AddHttpSession`) | `Api/Module.Session.cs`, `CacheController.cs` | +| Typed REST clients (`IWebService`, `WebMethod`, `ResourceUrl`, `HttpHeader`) | `GerstITS.WebClients.Example.Api/Contracts/ICustomer.cs` | +| `RestWebServiceConfigurationBase` + creation rules | `GerstITS.WebClients.Example.Api/Configurations`, `CreationRules` | +| `IWebClient.Request().Execute(...)` | `WebClient.Console/Tests/TestRunner.cs` | + +### GerstITS.Authentication.ApiKey / GerstITS.Authentication.OpenId +| Feature | Example | +|---------|---------| +| `[ApiKey]` attribute + `IApiKeyConfiguration` | `Api/Controllers/1.1/MaintenanceController.cs`, `WebApiConfiguration.ApiKey.cs` | +| JWT bearer via `IOpenIdConfiguration` + `[Authorize]` | `Api/Controllers/1.1/SecuredController.cs`, `WebApiConfiguration.OpenId*.cs` | + +### GerstITS.Mail +| Feature | Example | +|---------|---------| +| `IMailProvider`, `MailNotification`, `ContactRequest` | `Api/Controllers/1.1/ContactController.cs` | +| SMTP configuration (`Mail` section) | `Api/appsettings.json` | + +### GerstITS.Job / GerstITS.Job.Scheduling +| Feature | Example | +|---------|---------| +| `JobBase` + cron scheduling | `Jobs.SayHelloWorld/Jobs/*.cs` | +| `JobSchedulingConfigurationBase` (named + convention based) | `Jobs.SayHelloWorld/Configurations/*.cs`, `Api/appsettings.json` (`Jobs` section) | +| `RegisterJob()` + `IgnoreConditions` | `Jobs.SayHelloWorld/Module.cs` | + +### GerstITS.TestFramework +| Feature | Example | +|---------|---------| +| `[TestCategory(TestCategories.Unit/Integration)]` traits | `GerstITS.Examples.Tests/**/*Test.cs` | +| Filtering: `dotnet test --filter "Category=Unit"` | see below | + +## Running + +1. **Database**: the API auto-migrates on startup (`AutoMigrate: true`). Adjust the `CustomerDbContext` connection string in `GerstITS.Examples.Api/appsettings.json` (PostgreSQL). +2. **API**: start `GerstITS.Examples.Api`. Swagger UI is available in development at `/api/swagger`. +3. **Console client**: start `GerstITS.Examples.WebClient.Console` while the API is running. +4. **Tests**: `dotnet test` (optionally `--filter "Category=Unit"`). +5. **Migrations**: `dotnet ef migrations add ` inside `GerstITS.Examples.Data` (uses the design time factory and `appsettings.json` of that project). + +### Api key protected endpoints + +`GET /api/Maintenance/Ping` requires the header configured under `WebApi:ApiKey` (default: `X-Api-Key: example-api-key`).