From 048835d558bbb1d073ce39e993209b7b2602cc89 Mon Sep 17 00:00:00 2001 From: GenjiruSUchiwa Date: Sat, 23 Nov 2024 19:07:27 +0100 Subject: [PATCH] feat: create web entry point for the app #58 --- .dockerignore | 25 ---- Directory.Packages.props | 1 + Place.sln | 10 ++ compose.yaml | 6 + src/Account/AccountModule.cs | 7 +- .../Configurations/DatabaseInitializer.cs | 119 ------------------ src/Account/Data/Seed/AccountDataSeeder.cs | 2 +- src/Account/IAccountRoot.cs | 3 + src/Account/IAssemblyMarker.cs | 3 - src/Account/Program.cs | 2 +- src/Account/appsettings.Development.json | 7 -- src/Account/appsettings.json | 25 +--- src/Common/Core.EF/IDataSeeder.cs | 4 +- .../Core.EF/ServiceCollectionExtensions.cs | 105 +++++++++++++--- .../ServiceCollectionExtensions.cs | 4 + .../Core.Identity/InitializeDatabase.cs | 44 ------- src/Identity/AuthenticateExtensions.cs | 3 +- src/Identity/IAssemblyMarker.cs | 3 - src/Identity/IIdentityRoot.cs | 3 + src/Identity/IdentityMdoule.cs | 7 +- src/Identity/Program.cs | 3 +- src/Identity/appsettings.json | 67 +++++----- src/Place.API/Place.API.csproj | 21 ++++ src/Place.API/Program.cs | 36 ++++++ src/Place.API/Properties/launchSettings.json | 23 ++++ src/Place.API/appsettings.Development.json | 8 ++ src/Place.API/appsettings.json | 62 +++++++++ .../Common/ProfileWebAppFactory.cs | 2 +- .../IdentityWebAppFactory.cs | 2 +- 29 files changed, 317 insertions(+), 290 deletions(-) delete mode 100644 .dockerignore create mode 100644 compose.yaml delete mode 100755 src/Account/Data/Configurations/DatabaseInitializer.cs create mode 100755 src/Account/IAccountRoot.cs delete mode 100755 src/Account/IAssemblyMarker.cs delete mode 100644 src/Common/Core.Identity/InitializeDatabase.cs delete mode 100644 src/Identity/IAssemblyMarker.cs create mode 100644 src/Identity/IIdentityRoot.cs create mode 100644 src/Place.API/Place.API.csproj create mode 100644 src/Place.API/Program.cs create mode 100644 src/Place.API/Properties/launchSettings.json create mode 100644 src/Place.API/appsettings.Development.json create mode 100644 src/Place.API/appsettings.json diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index cd967fc..0000000 --- a/.dockerignore +++ /dev/null @@ -1,25 +0,0 @@ -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/.idea -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/azds.yaml -**/bin -**/charts -**/docker-compose* -**/Dockerfile* -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -LICENSE -README.md \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index 49e0817..1d6b270 100755 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -40,6 +40,7 @@ + diff --git a/Place.sln b/Place.sln index 2d04d79..23d77e4 100755 --- a/Place.sln +++ b/Place.sln @@ -21,6 +21,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DA61C0F8-3DDC-47E1-93A9-3ACB68AF4AEE}" ProjectSection(SolutionItems) = preProject Directory.Packages.props = Directory.Packages.props + compose.yaml = compose.yaml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Account", "Account", "{5B48E2F5-D7CC-48EA-94FE-A2F132650C8F}" @@ -55,6 +56,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.MediatR", "src\Common\ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{4B354D87-FCA4-46F3-A64E-76376D3C68C2}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Api", "Api", "{CC68CA38-62BE-4535-9BB4-AB1597AFC90A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Place.API", "src\Place.API\Place.API.csproj", "{C4DD7B27-138C-4DFC-BC57-69BFC70853CA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -79,6 +84,7 @@ Global {5453898F-4A78-4AD6-89B5-BAB204FF67FF} = {25654B5F-D4E9-4019-B307-8759C38E94A8} {5B48E2F5-D7CC-48EA-94FE-A2F132650C8F} = {4B354D87-FCA4-46F3-A64E-76376D3C68C2} {C3F80B30-C2DE-41DC-933A-A24E04827504} = {4B354D87-FCA4-46F3-A64E-76376D3C68C2} + {C4DD7B27-138C-4DFC-BC57-69BFC70853CA} = {CC68CA38-62BE-4535-9BB4-AB1597AFC90A} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1DFC8537-6B29-4BB5-8449-1910496DB479}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -143,5 +149,9 @@ Global {5453898F-4A78-4AD6-89B5-BAB204FF67FF}.Debug|Any CPU.Build.0 = Debug|Any CPU {5453898F-4A78-4AD6-89B5-BAB204FF67FF}.Release|Any CPU.ActiveCfg = Release|Any CPU {5453898F-4A78-4AD6-89B5-BAB204FF67FF}.Release|Any CPU.Build.0 = Release|Any CPU + {C4DD7B27-138C-4DFC-BC57-69BFC70853CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C4DD7B27-138C-4DFC-BC57-69BFC70853CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4DD7B27-138C-4DFC-BC57-69BFC70853CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C4DD7B27-138C-4DFC-BC57-69BFC70853CA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..1191a95 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,6 @@ +services: + place.api: + image: place.api + build: + context: . + dockerfile: src/Place.API/Dockerfile diff --git a/src/Account/AccountModule.cs b/src/Account/AccountModule.cs index 8ece555..ef7b7be 100755 --- a/src/Account/AccountModule.cs +++ b/src/Account/AccountModule.cs @@ -1,3 +1,4 @@ +using System.Threading.Tasks; using Account.Data.Configurations; using Account.Data.Seed; using Core.EF; @@ -22,17 +23,17 @@ IConfiguration configuration nameof(Core.Identity), configuration ); - services.AddScoped(); + services.AddScoped, AccountDataSeeder>(); return services; } - public static WebApplication UseAccountModule( + public static async Task UseAccountModule( this WebApplication app, IWebHostEnvironment environment ) { app.UseCoreFramework(); - app.UseMigration(environment); + await app.UseMigrationAsync(environment); return app; } } diff --git a/src/Account/Data/Configurations/DatabaseInitializer.cs b/src/Account/Data/Configurations/DatabaseInitializer.cs deleted file mode 100755 index 6be71e0..0000000 --- a/src/Account/Data/Configurations/DatabaseInitializer.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Account.Data.Models; -using Account.Profile.Models; -using Microsoft.AspNetCore.Builder; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace Account.Data.Configurations; - -/// -/// Extension methods for database initialization and seeding. -/// -public static class DatabaseInitializer -{ - /// - /// Ensures the database is created and migrations are applied. - /// Also seeds initial data in development environment. - /// - public static async Task InitializeDatabaseAsync(this WebApplication app) - { - using IServiceScope scope = app.Services.CreateScope(); - IServiceProvider services = scope.ServiceProvider; - ILogger logger = services.GetRequiredService>(); - - try - { - AccountDbContext context = services.GetRequiredService(); - - logger.LogInformation("Starting database migration..."); - - await context.Database.MigrateAsync(); - - logger.LogInformation("Database migration completed successfully"); - - // Seed data only in development - if (app.Environment.IsDevelopment()) - { - logger.LogInformation("Development environment detected. Starting data seeding..."); - await SeedDataAsync(context); - logger.LogInformation("Data seeding completed successfully"); - } - } - catch (Exception ex) - { - logger.LogError(ex, "An error occurred while initializing the database"); - throw; - } - } - - private static async Task SeedDataAsync(AccountDbContext context) - { - // Check if there's any data already - if (await context.Profiles.AnyAsync()) - { - return; - } - - // Create sample profiles - List profiles = - [ - new() - { - Id = Guid.NewGuid(), - UserId = Guid.NewGuid(), - Email = "john.doe@example.com", - FirstName = "John", - LastName = "Doe", - DateOfBirth = new DateTime(1990, 1, 1), - Gender = Gender.Male, - PhoneNumber = "+33612345678", - Street = "123 Main St", - ZipCode = "75001", - City = "Paris", - Country = "France", - CreatedAt = DateTime.UtcNow, - CreatedBy = Guid.NewGuid(), - IsDeleted = false, - }, - new() - { - Id = Guid.NewGuid(), - UserId = Guid.NewGuid(), - Email = "jane.smith@example.com", - FirstName = "Jane", - LastName = "Smith", - DateOfBirth = new DateTime(1985, 5, 15), - Gender = Gender.Female, - PhoneNumber = "+237655555555", - City = "Yaoundé", - Country = "Cameroon", - CreatedAt = DateTime.UtcNow, - CreatedBy = Guid.NewGuid(), - IsDeleted = false, - }, - // Add a soft-deleted profile for testing - - new() - { - Id = Guid.NewGuid(), - UserId = Guid.NewGuid(), - Email = "deleted@example.com", - FirstName = "Deleted", - LastName = "User", - CreatedAt = DateTime.UtcNow.AddDays(-30), - CreatedBy = Guid.NewGuid(), - IsDeleted = true, - DeletedAt = DateTime.UtcNow.AddDays(-1), - DeletedBy = Guid.NewGuid(), - }, - ]; - - await context.Profiles.AddRangeAsync(profiles); - await context.SaveChangesAsync(); - } -} diff --git a/src/Account/Data/Seed/AccountDataSeeder.cs b/src/Account/Data/Seed/AccountDataSeeder.cs index e6c026d..d2705da 100644 --- a/src/Account/Data/Seed/AccountDataSeeder.cs +++ b/src/Account/Data/Seed/AccountDataSeeder.cs @@ -9,7 +9,7 @@ namespace Account.Data.Seed; -public class AccountDataSeeder(AccountDbContext context) : IDataSeeder +public class AccountDataSeeder(AccountDbContext context) : IDataSeeder { public async Task SeedAllAsync() { diff --git a/src/Account/IAccountRoot.cs b/src/Account/IAccountRoot.cs new file mode 100755 index 0000000..9b5af21 --- /dev/null +++ b/src/Account/IAccountRoot.cs @@ -0,0 +1,3 @@ +namespace Account; + +public interface IAccountRoot { } diff --git a/src/Account/IAssemblyMarker.cs b/src/Account/IAssemblyMarker.cs deleted file mode 100755 index 0c4bd66..0000000 --- a/src/Account/IAssemblyMarker.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Account; - -public interface IAssemblyMarker { } diff --git a/src/Account/Program.cs b/src/Account/Program.cs index ee727e1..657143b 100755 --- a/src/Account/Program.cs +++ b/src/Account/Program.cs @@ -17,6 +17,6 @@ WebApplication app = builder.Build(); -app.UseAccountModule(environment); +await app.UseAccountModule(environment); await app.RunAsync(); diff --git a/src/Account/appsettings.Development.json b/src/Account/appsettings.Development.json index ec38444..0c208ae 100755 --- a/src/Account/appsettings.Development.json +++ b/src/Account/appsettings.Development.json @@ -4,12 +4,5 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } - }, - "Postgres": { - "Host": "localhost", - "Port": 5489, - "Username": "postgres", - "Password": "postgres", - "Database": "PlaceApiProfile" } } diff --git a/src/Account/appsettings.json b/src/Account/appsettings.json index a3526c6..a337d54 100755 --- a/src/Account/appsettings.json +++ b/src/Account/appsettings.json @@ -8,24 +8,10 @@ "app": { "name": "Place Profile Api" }, - "serilog": { + "Serilog": { + "applicationName": "identity-service", + "excludePaths": ["/ping", "/metrics"], "level": "information", - "overrides": { - "Microsoft.AspNetCore": "Warning", - "Microsoft.EntityFrameworkCore.Database.Command": "Warning", - "Microsoft.EntityFrameworkCore.Infrastructure": "Warning" - }, - "excludePaths": [ - "/", - "/metrics", - "/ping" - ], - "excludeProperties": [ - "api_key", - "access_key", - "password", - "email" - ], "console": { "enabled": true }, @@ -37,9 +23,8 @@ "seq": { "enabled": true, "url": "http://localhost:5341", - "apiKey": "secret" - }, - "tags": {} + "token": "secret" + } }, "Swagger": { "Title": "Place Profile Api", diff --git a/src/Common/Core.EF/IDataSeeder.cs b/src/Common/Core.EF/IDataSeeder.cs index 83c494e..9e3fc6e 100644 --- a/src/Common/Core.EF/IDataSeeder.cs +++ b/src/Common/Core.EF/IDataSeeder.cs @@ -1,8 +1,10 @@ using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; namespace Core.EF; -public interface IDataSeeder +public interface IDataSeeder + where TContext : DbContext { Task SeedAllAsync(); } diff --git a/src/Common/Core.EF/ServiceCollectionExtensions.cs b/src/Common/Core.EF/ServiceCollectionExtensions.cs index 16723e7..b09db9b 100644 --- a/src/Common/Core.EF/ServiceCollectionExtensions.cs +++ b/src/Common/Core.EF/ServiceCollectionExtensions.cs @@ -13,6 +13,8 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Npgsql; namespace Core.EF; @@ -27,18 +29,27 @@ IConfiguration configuration { AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); - string? b = configuration.GetConnectionString(connectionName); + string? connectionString = configuration.GetConnectionString(connectionName); + if (string.IsNullOrEmpty(connectionString)) + { + throw new InvalidOperationException($"Connection string '{connectionName}' not found."); + } services.AddDbContext( (sp, options) => { - options.UseNpgsql( - configuration.GetConnectionString(connectionName), - dbOptions => - { - dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name); - } - ); + options + .UseNpgsql( + connectionString, + dbOptions => + { + dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name); + dbOptions.EnableRetryOnFailure(3); + } + ) + .EnableSensitiveDataLogging( + sp.GetService()?.IsDevelopment() ?? false + ); } ); @@ -47,22 +58,58 @@ IConfiguration configuration return services; } - public static IApplicationBuilder UseMigration( + public static async Task UseMigrationAsync( this IApplicationBuilder app, IWebHostEnvironment env ) where TContext : DbContext, IDbContext { - MigrateDatabaseAsync(app.ApplicationServices).GetAwaiter().GetResult(); + ILogger logger = app.ApplicationServices.GetRequiredService>(); + + await EnsureDatabaseExistsAsync(app.ApplicationServices, logger); + await MigrateDatabaseAsync(app.ApplicationServices, logger); if (!env.IsEnvironment("test")) { - SeedDataAsync(app.ApplicationServices).GetAwaiter().GetResult(); + await SeedDataAsync(app.ApplicationServices, logger); } return app; } + private static async Task EnsureDatabaseExistsAsync( + IServiceProvider serviceProvider, + ILogger logger + ) + where TContext : DbContext, IDbContext + { + using IServiceScope scope = serviceProvider.CreateScope(); + TContext context = scope.ServiceProvider.GetRequiredService(); + string? connectionString = context.Database.GetConnectionString(); + + try + { + await context.Database.CanConnectAsync(); + } + catch (PostgresException ex) when (ex.SqlState == "3D000") // Database doesn't exist + { + logger.LogInformation("Database does not exist. Creating database..."); + + NpgsqlConnectionStringBuilder builder = new(connectionString); + string? databaseName = builder.Database; + builder.Database = "postgres"; + + await using NpgsqlConnection masterConnection = new(builder.ToString()); + await masterConnection.OpenAsync(); + + using NpgsqlCommand command = masterConnection.CreateCommand(); + command.CommandText = $"CREATE DATABASE \"{databaseName}\" ENCODING 'UTF8'"; + + await command.ExecuteNonQueryAsync(); + logger.LogInformation("Database created successfully"); + } + } + // ref: https://github.com/pdevito3/MessageBusTestingInMemHarness/blob/main/RecipeManagement/src/RecipeManagement/Databases/RecipesDbContext.cs public static void FilterSoftDeletedProperties(this ModelBuilder modelBuilder) { @@ -118,7 +165,10 @@ public static void ToSnakeCaseTables(this ModelBuilder modelBuilder) } } - private static async Task MigrateDatabaseAsync(IServiceProvider serviceProvider) + private static async Task MigrateDatabaseAsync( + IServiceProvider serviceProvider, + ILogger logger + ) where TContext : DbContext, IDbContext { using IServiceScope scope = serviceProvider.CreateScope(); @@ -126,22 +176,43 @@ private static async Task MigrateDatabaseAsync(IServiceProvider servic TContext context = scope.ServiceProvider.GetRequiredService(); try { + logger.LogInformation("Applying migrations..."); await context.Database.MigrateAsync(); } catch (System.Exception e) { - Console.WriteLine(e); + logger.LogError(e, "An error occurred while applying migrations"); throw; } } - private static async Task SeedDataAsync(IServiceProvider serviceProvider) + private static async Task SeedDataAsync( + IServiceProvider serviceProvider, + ILogger logger + ) + where TContext : DbContext { using IServiceScope scope = serviceProvider.CreateScope(); - IEnumerable seeders = scope.ServiceProvider.GetServices(); - foreach (IDataSeeder seeder in seeders) + IEnumerable> seeders = scope.ServiceProvider.GetServices< + IDataSeeder + >(); + + foreach (IDataSeeder seeder in seeders) { - await seeder.SeedAllAsync(); + try + { + logger.LogInformation("Running seeder {SeederType}...", seeder.GetType().Name); + await seeder.SeedAllAsync(); + } + catch (Exception e) + { + logger.LogError( + e, + "An error occurred while running seeder {SeederType}", + seeder.GetType().Name + ); + throw; + } } } } diff --git a/src/Common/Core.Framework/ServiceCollectionExtensions.cs b/src/Common/Core.Framework/ServiceCollectionExtensions.cs index bdc178e..e1bfff2 100644 --- a/src/Common/Core.Framework/ServiceCollectionExtensions.cs +++ b/src/Common/Core.Framework/ServiceCollectionExtensions.cs @@ -1,8 +1,10 @@ using System; +using Core.Logging; using Core.Swagger; using Core.Versioning; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; +using Serilog; namespace Core.Framework; @@ -21,6 +23,8 @@ public static WebApplicationBuilder AddCoreFramework(this WebApplicationBuilder AppOptions appOptions = builder.Configuration.GetSection("app").BindOptions(); AppInfo appInfo = new(appOptions.Name, appOptions.Version); builder.Services.AddSingleton(appInfo); + builder.AddLogging(); + builder.Services.AddLogger(builder.Configuration); RenderLogo(appOptions); builder.Services.AddHttpContextAccessor(); diff --git a/src/Common/Core.Identity/InitializeDatabase.cs b/src/Common/Core.Identity/InitializeDatabase.cs deleted file mode 100644 index 090f8d8..0000000 --- a/src/Common/Core.Identity/InitializeDatabase.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Core.Identity; - -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -/// -/// Extension methods for database initialization and seeding. -/// -public static class DatabaseInitializer -{ - /// - /// Ensures the database is created and migrations are applied. - /// Also seeds initial data in development environment. - /// - public static async Task InitializeDatabaseAsync(this WebApplication app) - { - using IServiceScope scope = app.Services.CreateScope(); - IServiceProvider services = scope.ServiceProvider; - ILogger logger = services.GetRequiredService< - ILogger - >(); - - try - { - IdentityApplicationDbContext context = - services.GetRequiredService(); - - logger.LogInformation("Starting database migration..."); - - await context.Database.MigrateAsync(); - - logger.LogInformation("Database migration completed successfully"); - } - catch (Exception ex) - { - logger.LogError(ex, "An error occurred while initializing the database"); - throw; - } - } -} diff --git a/src/Identity/AuthenticateExtensions.cs b/src/Identity/AuthenticateExtensions.cs index 38ab1a6..1110b77 100644 --- a/src/Identity/AuthenticateExtensions.cs +++ b/src/Identity/AuthenticateExtensions.cs @@ -2,6 +2,7 @@ using Core.Identity; using Identity.Authenticate.Endpoints; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Configuration; using ApiVersion = Asp.Versioning.ApiVersion; @@ -22,7 +23,7 @@ internal static WebApplication WithAuthenticationEndpoints(this WebApplication a RouteGroupBuilder groupBuilder = app.MapGroup("api/v{apiVersion:apiVersion}") .WithApiVersionSet(apiVersionSet); - groupBuilder.MapAuthenticationEndpoints(); + groupBuilder.MapAuthenticationEndpoints().WithTags("Identity"); return app; } diff --git a/src/Identity/IAssemblyMarker.cs b/src/Identity/IAssemblyMarker.cs deleted file mode 100644 index 6549f54..0000000 --- a/src/Identity/IAssemblyMarker.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Identity; - -public interface IAssemblyMarker { } diff --git a/src/Identity/IIdentityRoot.cs b/src/Identity/IIdentityRoot.cs new file mode 100644 index 0000000..6ea7837 --- /dev/null +++ b/src/Identity/IIdentityRoot.cs @@ -0,0 +1,3 @@ +namespace Identity; + +public interface IIdentityRoot { } diff --git a/src/Identity/IdentityMdoule.cs b/src/Identity/IdentityMdoule.cs index 79c3903..fdc8667 100644 --- a/src/Identity/IdentityMdoule.cs +++ b/src/Identity/IdentityMdoule.cs @@ -1,3 +1,4 @@ +using System.Threading.Tasks; using Core.EF; using Core.Framework; using Core.Identity; @@ -28,13 +29,15 @@ WebApplicationBuilder builder return services; } - public static WebApplication UseIdentityModule( + public static async Task UseIdentityModule( this WebApplication app, IWebHostEnvironment environment ) { app.UseCoreFramework(); - app.UseMigration(environment); + await app.UseMigrationAsync(environment); + app.WithAuthenticationEndpoints(); + return app; } } diff --git a/src/Identity/Program.cs b/src/Identity/Program.cs index b6d5bbd..3748aad 100644 --- a/src/Identity/Program.cs +++ b/src/Identity/Program.cs @@ -13,8 +13,7 @@ { - app.UseIdentityModule(environment); - app.WithAuthenticationEndpoints(); + await app.UseIdentityModule(environment); await app.RunAsync(); } diff --git a/src/Identity/appsettings.json b/src/Identity/appsettings.json index e97a617..1e2ab5c 100644 --- a/src/Identity/appsettings.json +++ b/src/Identity/appsettings.json @@ -16,50 +16,39 @@ "queryStringParam": "api-version", "mediaTypeParam": "v" }, - "Identity": { - "Database": { - "Host": "localhost", - "Port": 5489, - "Username": "postgres", - "Password": "postgres", - "Database": "PlaceApiIdentity" - }, - "Authentication": { - "TokenExpiration": "01:00:00", - "RequireConfirmedEmail": true, - "RequireConfirmedAccount": false - }, - "Password": { - "RequireDigit": true, - "RequireLowercase": true, - "RequireUppercase": true, - "RequireNonAlphanumeric": true, - "RequiredLength": 10, - "RequiredUniqueChars": 3 - } - }, - "swagger": { - "enabled": true, - "title": "My API", - "versions": [ - "1.0", - "2.0" - ], - "useAuthentication": true, - "enableDownload": true, - "useSwaggerUI": true, - "useReDoc": true, - "swaggerUIRoute": "swagger", - "reDocRoute": "api-docs", - "uiOptions": { - "docExpansion": "List", - "defaultModelsExpandDepth": 1 - } + "Swagger": { + "Title": "Place - Identity Api", + "Description": "Place Profile Api documentation", + "Version": "v1", + "EnableBearerAuth": true, + "SecuritySchemaName": "Bearer", + "SecurityScheme": "JWT", + "SecurityDescription": "Utiliser le format: Bearer {votre_token}", + "EnableVersioning": true, + "RoutePrefix": "swagger" }, "app": { "name": "Identity Service" }, "ConnectionStrings": { "Identity": "Server=localhost;Port=5490;Database=identity_db;User Id=postgres;Password=postgres;Include Error Detail=true" + }, + "Serilog": { + "applicationName": "identity-service", + "excludePaths": ["/ping", "/metrics"], + "level": "information", + "console": { + "enabled": true + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "seq": { + "enabled": true, + "url": "http://localhost:5341", + "token": "secret" + } } } diff --git a/src/Place.API/Place.API.csproj b/src/Place.API/Place.API.csproj new file mode 100644 index 0000000..8f1e3bc --- /dev/null +++ b/src/Place.API/Place.API.csproj @@ -0,0 +1,21 @@ + + + + net9.0 + enable + enable + Linux + + + + + + + + + + + + + + diff --git a/src/Place.API/Program.cs b/src/Place.API/Program.cs new file mode 100644 index 0000000..1cfc007 --- /dev/null +++ b/src/Place.API/Program.cs @@ -0,0 +1,36 @@ +using Account; +using Core.Framework; +using Core.MediatR; +using Identity; +using Scalar.AspNetCore; + +WebApplicationBuilder builder = WebApplication.CreateBuilder(args); +IConfiguration configuration = builder.Configuration; +IWebHostEnvironment environment = builder.Environment; + +builder.AddCoreFramework(); + +builder.Services.AddIdentityModule(builder); + +builder.Services.AddAccountModule(configuration); + +builder.Services.AddCoreMediatR(typeof(IIdentityRoot).Assembly); + +builder.Services.AddCoreMediatR(typeof(IAccountRoot).Assembly); + +builder.Services.AddOpenApi(); + +WebApplication app = builder.Build(); + +app.UseCoreFramework(); + +await app.UseIdentityModule(environment); + +await app.UseAccountModule(environment); + +app.MapOpenApi(); +app.MapScalarApiReference(); + +app.MapGet("/", x => x.Response.WriteAsync("It Works!")); + +await app.RunAsync(); diff --git a/src/Place.API/Properties/launchSettings.json b/src/Place.API/Properties/launchSettings.json new file mode 100644 index 0000000..009f40e --- /dev/null +++ b/src/Place.API/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5059", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7076;http://localhost:5059", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/Place.API/appsettings.Development.json b/src/Place.API/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/src/Place.API/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Place.API/appsettings.json b/src/Place.API/appsettings.json new file mode 100644 index 0000000..e5c60dd --- /dev/null +++ b/src/Place.API/appsettings.json @@ -0,0 +1,62 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "apiVersioning": { + "enabled": true, + "defaultVersion": "1.0", + "assumeDefaultVersionWhenUnspecified": true, + "reportApiVersions": true, + "addVersionParamToNeutralEndpoints": false, + "versionReaderType": "UrlSegment", + "headerName": "X-Api-Version", + "queryStringParam": "api-version", + "mediaTypeParam": "v" + }, + "swagger": { + "enabled": true, + "title": "My API", + "versions": [ + "1.0", + "2.0" + ], + "useAuthentication": true, + "enableDownload": true, + "useSwaggerUI": true, + "useReDoc": true, + "swaggerUIRoute": "swagger", + "reDocRoute": "api-docs", + "uiOptions": { + "docExpansion": "List", + "defaultModelsExpandDepth": 1 + } + }, + "app": { + "name": "Place API Root" + }, + "ConnectionStrings": { + "Account": "Server=localhost;Port=5490;Database=profile_db;User Id=postgres;Password=postgres;Include Error Detail=true", + "Identity": "Server=localhost;Port=5490;Database=identity_db;User Id=postgres;Password=postgres;Include Error Detail=true" + }, + "Serilog": { + "applicationName": "identity-service", + "excludePaths": ["/ping", "/metrics"], + "level": "information", + "console": { + "enabled": true + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "seq": { + "enabled": true, + "url": "http://localhost:5341", + "token": "secret" + } + } +} diff --git a/tests/Account.IntegrationTests/Common/ProfileWebAppFactory.cs b/tests/Account.IntegrationTests/Common/ProfileWebAppFactory.cs index 230f31f..7760195 100755 --- a/tests/Account.IntegrationTests/Common/ProfileWebAppFactory.cs +++ b/tests/Account.IntegrationTests/Common/ProfileWebAppFactory.cs @@ -19,7 +19,7 @@ namespace Account.IntegrationTests.Common; [CollectionDefinition(nameof(ProfileApiCollection))] public class ProfileApiCollection : ICollectionFixture { } -public class ProfileWebAppFactory : WebApplicationFactory, IAsyncLifetime +public class ProfileWebAppFactory : WebApplicationFactory, IAsyncLifetime { private readonly PostgreSqlContainer _dbContainer = default!; private Respawner? _respawner = default!; diff --git a/tests/Identity.IntegrationTests/IdentityWebAppFactory.cs b/tests/Identity.IntegrationTests/IdentityWebAppFactory.cs index 94b5ff7..fe32e64 100755 --- a/tests/Identity.IntegrationTests/IdentityWebAppFactory.cs +++ b/tests/Identity.IntegrationTests/IdentityWebAppFactory.cs @@ -17,7 +17,7 @@ namespace Identity.IntegrationTests; -public class IdentityWebAppFactory : WebApplicationFactory, IAsyncLifetime +public class IdentityWebAppFactory : WebApplicationFactory, IAsyncLifetime { private readonly PostgreSqlContainer _dbContainer;