From 8dd01d291dfb3146cb0c0f97676bf9314826f65a Mon Sep 17 00:00:00 2001 From: Janus Knudsen Date: Fri, 21 Feb 2025 17:03:49 +0100 Subject: [PATCH] More work on this Console Host --- .../SetupConfiguration.cs | 122 ++++++------ .../ConnectionFactory/IDbConnectionFactory.cs | 8 + ...Class1.cs => PostgresConnectionFactory.cs} | 11 +- Database/Core/DCL/SetupApplicationUser.cs | 153 ++++++++------- Database/Core/DCL/SetupDbAdmin.cs | 62 +++--- Database/Core/DDL/SetupIdentitySystem.cs | 179 +++++++++--------- Database/Core/Sql/SqlOperations.cs | 8 +- Database/ModuleRegistry/DbPostgreSqlModule.cs | 8 +- SetupInfrastructure/Program.cs | 60 +++--- SetupInfrastructure/Startup.cs | 64 ++++--- Tests/PostgresTests.cs | 5 +- 11 files changed, 336 insertions(+), 344 deletions(-) create mode 100644 Database/Core/ConnectionFactory/IDbConnectionFactory.cs rename Database/Core/ConnectionFactory/{Class1.cs => PostgresConnectionFactory.cs} (89%) diff --git a/Database/ConfigurationManagementSystem/SetupConfiguration.cs b/Database/ConfigurationManagementSystem/SetupConfiguration.cs index d8b82c7..45ca0bf 100644 --- a/Database/ConfigurationManagementSystem/SetupConfiguration.cs +++ b/Database/ConfigurationManagementSystem/SetupConfiguration.cs @@ -1,54 +1,46 @@ using Insight.Database; using PlanTempus.Database.Core; +using PlanTempus.Database.Core.ConnectionFactory; using System.Data; namespace PlanTempus.Database.ConfigurationManagementSystem; public class SetupConfiguration : IDbConfigure { - public class Command - { + public class Command { } - } + private readonly IDbConnectionFactory _connectionFactory; - private readonly IDbConnection _db; + public SetupConfiguration(IDbConnectionFactory connectionFactory) + { + _connectionFactory = connectionFactory; + } + public void With(Command notInUse) + { + using var conn = _connectionFactory.Create(); + using var transaction = conn.OpenWithTransaction(); + try + { + CreateConfigurationTable(conn); + CreateHistoryTable(conn); + CreateConfigurationIndexes(conn); + CreateModifiedAtTrigger(conn); + CreateNotifyTrigger(conn); + CreateHistoryTrigger(conn); - public SetupConfiguration(IDbConnection connection) - { - _db = connection; - } - public void With(Command notInUse) - { - using (var transaction = _db.OpenWithTransaction()) - { - try - { - CreateConfigurationTable(); - CreateHistoryTable(); - CreateConfigurationIndexes(); - CreateModifiedAtTrigger(); - CreateNotifyTrigger(); - CreateHistoryTrigger(); + transaction.Commit(); + } + catch (Exception ex) + { + transaction.Rollback(); + throw new InvalidOperationException("Failed to SetupConfiguration in Database", ex); + } - transaction.Commit(); - } - catch (Exception ex) - { - transaction.Rollback(); - throw new InvalidOperationException("Failed to SetupConfiguration in Database", ex); - } - } + } - } - - private void ExecuteSql(string sql) - { - _db.ExecuteSql(sql); - } - - void CreateConfigurationTable() - { - const string sql = @" + void CreateConfigurationTable(IDbConnection db) + { + const string sql = @" CREATE TABLE IF NOT EXISTS app_configuration ( id bigserial NOT NULL, ""key"" varchar(255) NOT NULL, @@ -62,12 +54,12 @@ public class SetupConfiguration : IDbConfigure etag uuid DEFAULT gen_random_uuid() NULL, CONSTRAINT app_configuration_pkey PRIMARY KEY (id) );"; - ExecuteSql(sql); - } + db.ExecuteSql(sql); + } - void CreateHistoryTable() - { - const string sql = @" + void CreateHistoryTable(IDbConnection db) + { + const string sql = @" CREATE TABLE IF NOT EXISTS app_configuration_history ( history_id bigserial NOT NULL, action_type char(1) NOT NULL, @@ -85,20 +77,20 @@ public class SetupConfiguration : IDbConfigure etag uuid NULL, CONSTRAINT app_configuration_history_pkey PRIMARY KEY (history_id) );"; - ExecuteSql(sql); - } + db.ExecuteSql(sql); + } - void CreateConfigurationIndexes() - { - const string sql = @" + void CreateConfigurationIndexes(IDbConnection db) + { + const string sql = @" CREATE INDEX IF NOT EXISTS idx_app_configuration_key ON app_configuration(""key""); CREATE INDEX IF NOT EXISTS idx_app_configuration_validity ON app_configuration(valid_from, expires_at);"; - ExecuteSql(sql); - } + db.ExecuteSql(sql); + } - void CreateModifiedAtTrigger() - { - const string sql = @" + void CreateModifiedAtTrigger(IDbConnection db) + { + const string sql = @" CREATE OR REPLACE FUNCTION update_app_configuration_modified_at() RETURNS TRIGGER AS $$ BEGIN @@ -111,12 +103,12 @@ public class SetupConfiguration : IDbConfigure BEFORE UPDATE ON app_configuration FOR EACH ROW EXECUTE FUNCTION update_app_configuration_modified_at();"; - ExecuteSql(sql); - } + db.ExecuteSql(sql); + } - void CreateNotifyTrigger() - { - const string sql = @" + void CreateNotifyTrigger(IDbConnection db) + { + const string sql = @" CREATE OR REPLACE FUNCTION notify_app_configuration_change() RETURNS TRIGGER AS $$ BEGIN @@ -129,12 +121,12 @@ public class SetupConfiguration : IDbConfigure AFTER INSERT OR UPDATE ON app_configuration FOR EACH ROW EXECUTE FUNCTION notify_app_configuration_change();"; - ExecuteSql(sql); - } + db.ExecuteSql(sql); + } - void CreateHistoryTrigger() - { - const string sql = @" + void CreateHistoryTrigger(IDbConnection db) + { + const string sql = @" CREATE OR REPLACE FUNCTION log_app_configuration_changes() RETURNS TRIGGER AS $$ BEGIN @@ -173,8 +165,8 @@ public class SetupConfiguration : IDbConfigure CREATE OR REPLACE TRIGGER trg_app_configuration_history AFTER INSERT OR UPDATE OR DELETE ON app_configuration FOR EACH ROW EXECUTE FUNCTION log_app_configuration_changes();"; - ExecuteSql(sql); - } + db.ExecuteSql(sql); + } } diff --git a/Database/Core/ConnectionFactory/IDbConnectionFactory.cs b/Database/Core/ConnectionFactory/IDbConnectionFactory.cs new file mode 100644 index 0000000..f2ace54 --- /dev/null +++ b/Database/Core/ConnectionFactory/IDbConnectionFactory.cs @@ -0,0 +1,8 @@ +namespace PlanTempus.Database.Core.ConnectionFactory +{ + public interface IDbConnectionFactory + { + System.Data.IDbConnection Create(); + System.Data.IDbConnection Create(string username, string password); + } +} diff --git a/Database/Core/ConnectionFactory/Class1.cs b/Database/Core/ConnectionFactory/PostgresConnectionFactory.cs similarity index 89% rename from Database/Core/ConnectionFactory/Class1.cs rename to Database/Core/ConnectionFactory/PostgresConnectionFactory.cs index cc80b4e..3cf8d58 100644 --- a/Database/Core/ConnectionFactory/Class1.cs +++ b/Database/Core/ConnectionFactory/PostgresConnectionFactory.cs @@ -1,15 +1,10 @@ using Npgsql; -using PlanTempus.Database.ModuleRegistry; -using System; -using System.Collections.Generic; using System.Data; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace PlanTempus.Database.Core.ConnectionFactory { - public class PostgresConnectionFactory : IDbConnectionFactory, IAsyncDisposable + + + public class PostgresConnectionFactory : IDbConnectionFactory, IAsyncDisposable { private readonly NpgsqlDataSource _baseDataSource; private readonly Action _configureDataSource; diff --git a/Database/Core/DCL/SetupApplicationUser.cs b/Database/Core/DCL/SetupApplicationUser.cs index e1afbd6..b750733 100644 --- a/Database/Core/DCL/SetupApplicationUser.cs +++ b/Database/Core/DCL/SetupApplicationUser.cs @@ -1,114 +1,109 @@ using System.Data; using Insight.Database; using PlanTempus.Database.Common; -using PlanTempus.Database.Core; +using PlanTempus.Database.Core.ConnectionFactory; namespace PlanTempus.Database.Core.DCL { - /// - /// Only a superadmin or similar can create Application Users - /// - public class SetupApplicationUser : IDbConfigure - { - public class Command - { - public required string Schema { get; init; } - public required string User { get; init; } - public required string Password { get; init; } - } + /// + /// Only a superadmin or similar can create Application Users + /// + public class SetupApplicationUser : IDbConfigure + { + public class Command + { + public required string Schema { get; init; } + public required string User { get; init; } + public required string Password { get; init; } + } - IDbConnection _db; - Command _command; + Command _command; + private readonly IDbConnectionFactory _connectionFactory; - public SetupApplicationUser(IDbConnection db) - { - _db = db; - } + public SetupApplicationUser(IDbConnectionFactory connectionFactory) + { + _connectionFactory = connectionFactory; + } - public void With(Command command) - { - _command = command; + public void With(Command command) + { + _command = command; - if (!Validations.IsValidSchemaName(_command.Schema)) - throw new ArgumentException("Invalid schema name", _command.Schema); + if (!Validations.IsValidSchemaName(_command.Schema)) + throw new ArgumentException("Invalid schema name", _command.Schema); - using (var transaction = _db.OpenWithTransaction()) - { - try - { - CreateSchema(); - CreateRole(); - GrantSchemaRights(); + using var conn = _connectionFactory.Create(); + using var transaction = conn.OpenWithTransaction(); + try + { + CreateSchema(conn); + CreateRole(conn); + GrantSchemaRights(conn); - transaction.Commit(); - } - catch (Exception ex) - { - transaction.Rollback(); - throw new InvalidOperationException("Failed to SetupApplicationUser in Database", ex); - } - } + transaction.Commit(); + } + catch (Exception ex) + { + transaction.Rollback(); + throw new InvalidOperationException("Failed to SetupApplicationUser in Database", ex); + } - } - private void ExecuteSql(string sql) - { - _db.ExecuteSql(sql); - } + } - private void CreateSchema() - { - var sql = $"CREATE SCHEMA IF NOT EXISTS {_command.Schema}"; - ExecuteSql(sql); - } + private void CreateSchema(IDbConnection db) + { + var sql = $"CREATE SCHEMA IF NOT EXISTS {_command.Schema}"; + db.ExecuteSql(sql); + } - private void CreateRole() - { - var sql = $@" + private void CreateRole(IDbConnection db) + { + var sql = $@" DO $$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '{_command.User}') THEN CREATE ROLE {_command.User} WITH CREATEDB CREATEROLE LOGIN PASSWORD '{_command.Password}'; END IF; END $$;"; - ExecuteSql(sql); + db.ExecuteSql(sql); - var sql1 = $"ALTER ROLE {_command.User} SET search_path='{_command.Schema}';"; - ExecuteSql(sql1); - } + var sql1 = $"ALTER ROLE {_command.User} SET search_path='{_command.Schema}';"; + db.ExecuteSql(sql1); + } - private void GrantSchemaRights() - { - // Grant USAGE og alle CREATE rettigheder på schema niveau - var sql = $@" + private void GrantSchemaRights(IDbConnection db) + { + // Grant USAGE og alle CREATE rettigheder på schema niveau + var sql = $@" GRANT USAGE ON SCHEMA {_command.Schema} TO {_command.User}; GRANT ALL ON SCHEMA {_command.Schema} TO {_command.User};"; - ExecuteSql(sql); + db.ExecuteSql(sql); - // Grant rettigheder på eksisterende og fremtidige tabeller - var sql1 = $"GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA {_command.Schema} TO {_command.User};"; - ExecuteSql(sql1); + // Grant rettigheder på eksisterende og fremtidige tabeller + var sql1 = $"GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA {_command.Schema} TO {_command.User};"; + db.ExecuteSql(sql1); - var sql2 = $"ALTER DEFAULT PRIVILEGES IN SCHEMA {_command.Schema} GRANT ALL PRIVILEGES ON TABLES TO {_command.User};"; - ExecuteSql(sql2); + var sql2 = $"ALTER DEFAULT PRIVILEGES IN SCHEMA {_command.Schema} GRANT ALL PRIVILEGES ON TABLES TO {_command.User};"; + db.ExecuteSql(sql2); - // Grant sequence rettigheder - var sql3 = $"GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA {_command.Schema} TO {_command.User};"; - ExecuteSql(sql3); + // Grant sequence rettigheder + var sql3 = $"GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA {_command.Schema} TO {_command.User};"; + db.ExecuteSql(sql3); - // Grant execute på functions - var sql4 = $"GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA {_command.Schema} TO {_command.User};"; - ExecuteSql(sql4); + // Grant execute på functions + var sql4 = $"GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA {_command.Schema} TO {_command.User};"; + db.ExecuteSql(sql4); - // Grant for fremtidige functions - var sql5 = $"ALTER DEFAULT PRIVILEGES IN SCHEMA {_command.Schema} GRANT EXECUTE ON FUNCTIONS TO {_command.User};"; - ExecuteSql(sql5); + // Grant for fremtidige functions + var sql5 = $"ALTER DEFAULT PRIVILEGES IN SCHEMA {_command.Schema} GRANT EXECUTE ON FUNCTIONS TO {_command.User};"; + db.ExecuteSql(sql5); - // Grant for fremtidige sequences - var sql6 = $"ALTER DEFAULT PRIVILEGES IN SCHEMA {_command.Schema} GRANT USAGE ON SEQUENCES TO {_command.User};"; - ExecuteSql(sql6); - } + // Grant for fremtidige sequences + var sql6 = $"ALTER DEFAULT PRIVILEGES IN SCHEMA {_command.Schema} GRANT USAGE ON SEQUENCES TO {_command.User};"; + db.ExecuteSql(sql6); + } - } + } } diff --git a/Database/Core/DCL/SetupDbAdmin.cs b/Database/Core/DCL/SetupDbAdmin.cs index 3156e2c..37af87e 100644 --- a/Database/Core/DCL/SetupDbAdmin.cs +++ b/Database/Core/DCL/SetupDbAdmin.cs @@ -2,6 +2,7 @@ using Insight.Database; using PlanTempus.Database.Common; using PlanTempus.Database.Core; +using PlanTempus.Database.Core.ConnectionFactory; namespace PlanTempus.Database.Core.DCL { @@ -19,13 +20,13 @@ namespace PlanTempus.Database.Core.DCL } - IDbConnection _db; Command _command; + private readonly IDbConnectionFactory _connectionFactory; - public SetupDbAdmin(IDbConnection db) + public SetupDbAdmin(IDbConnectionFactory connectionFactory) { - _db = db; - } + _connectionFactory = connectionFactory; + } public void With(Command command) @@ -35,36 +36,31 @@ namespace PlanTempus.Database.Core.DCL if (!Validations.IsValidSchemaName(_command.Schema)) throw new ArgumentException("Invalid schema name", _command.Schema); - using (var transaction = _db.OpenWithTransaction()) - { - try - { - CreateSchema(); - CreateRole(); - GrantSchemaRights(); + using var conn = _connectionFactory.Create(); + using var transaction = conn.OpenWithTransaction(); + try + { + CreateSchema(conn); + CreateRole(conn); + GrantSchemaRights(conn); - transaction.Commit(); - } - catch (Exception ex) - { - transaction.Rollback(); - throw new InvalidOperationException("Failed to SetupApplicationUser in Database", ex); - } - } + transaction.Commit(); + } + catch (Exception ex) + { + transaction.Rollback(); + throw new InvalidOperationException("Failed to SetupApplicationUser in Database", ex); + } - } - private void ExecuteSql(string sql) - { - _db.ExecuteSql(sql); - } - - private void CreateSchema() + } + + private void CreateSchema(IDbConnection db) { var sql = $"CREATE SCHEMA IF NOT EXISTS {_command.Schema}"; - ExecuteSql(sql); + db.ExecuteSql(sql); } - private void CreateRole() + private void CreateRole(IDbConnection db) { var sql = $@" DO $$ @@ -73,24 +69,24 @@ namespace PlanTempus.Database.Core.DCL CREATE ROLE {_command.User} WITH CREATEDB CREATEROLE LOGIN PASSWORD '{_command.Password}'; END IF; END $$;"; - ExecuteSql(sql); + db.ExecuteSql(sql); var sql1 = $"ALTER ROLE {_command.User} SET search_path='{_command.Schema}';"; - ExecuteSql(sql1); + db.ExecuteSql(sql1); var sql2 = $"ALTER SCHEMA {_command.Schema} OWNER TO {_command.User};"; - ExecuteSql(sql2); + db.ExecuteSql(sql2); } - private void GrantSchemaRights() + private void GrantSchemaRights(IDbConnection db) { // Grant USAGE og alle CREATE rettigheder på schema niveau //GRANT USAGE ON SCHEMA {_command.Schema} TO {_command.User}; var sql = $@" GRANT CREATE ON SCHEMA {_command.Schema} TO {_command.User};"; - ExecuteSql(sql); + db.ExecuteSql(sql); // Grant rettigheder på eksisterende og fremtidige tabeller //var sql1 = $"GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA {_command.Schema} TO {_command.User};"; diff --git a/Database/Core/DDL/SetupIdentitySystem.cs b/Database/Core/DDL/SetupIdentitySystem.cs index 98a5fbc..3641e26 100644 --- a/Database/Core/DDL/SetupIdentitySystem.cs +++ b/Database/Core/DDL/SetupIdentitySystem.cs @@ -1,70 +1,61 @@ using Insight.Database; -using PlanTempus.Database.Core; +using PlanTempus.Database.Core.ConnectionFactory; using System.Data; namespace PlanTempus.Database.Core.DDL { - /// - /// This is by purpose not async await - /// It is intended that this is created with the correct Application User, which is why the schema name is omitted. - /// - public class SetupIdentitySystem : IDbConfigure - { - public class Command - { - public required string Schema { get; init; } - } + /// + /// This is by purpose not async await + /// It is intended that this is created with the correct Application User, which is why the schema name is omitted. + /// + public class SetupIdentitySystem : IDbConfigure + { + public class Command + { + public required string Schema { get; init; } + } - readonly IDbConnection _db; - IDbTransaction _transaction = null; - Command _command; + Command _command; + private readonly IDbConnectionFactory _connectionFactory; - public SetupIdentitySystem(IDbConnection db) - { - _db = db; - } + public SetupIdentitySystem(IDbConnectionFactory connectionFactory) + { + _connectionFactory = connectionFactory; + } - /// - /// Creates the system tables in the specified schema within a transaction. - /// - /// The schema name where the tables will be created. - public void With(Command command) - { - _command = command; + /// + /// Creates the system tables in the specified schema within a transaction. + /// + /// The schema name where the tables will be created. + public void With(Command command) + { + _command = command; - using (_transaction = _db.OpenWithTransaction()) - { - try - { - CreateUsersTable(); - CreateOrganizationsTable(); - CreateUserOrganizationsTable(); - SetupRLS(); + using var conn = _connectionFactory.Create(); + using var transaction = conn.OpenWithTransaction(); + try + { + CreateUsersTable(conn); + CreateOrganizationsTable(conn); + CreateUserOrganizationsTable(conn); + SetupRLS(conn); - _transaction.Commit(); - } - catch (Exception ex) - { - _transaction.Rollback(); - throw new InvalidOperationException("Failed to SetupIdentitySystem. Transaction is rolled back", ex); - } - } - } - private void ExecuteSql(string sql) - { - if (string.IsNullOrEmpty(sql)) - throw new ArgumentNullException(nameof(sql)); + transaction.Commit(); + } + catch (Exception ex) + { + transaction.Rollback(); + throw new InvalidOperationException("Failed to SetupIdentitySystem. Transaction is rolled back", ex); + } + } + - _db.ExecuteSql(sql); - } - - - /// - /// Creates the users table - /// - void CreateUsersTable() - { - var sql = @$" + /// + /// Creates the users table + /// + void CreateUsersTable(IDbConnection db) + { + var sql = @$" CREATE TABLE IF NOT EXISTS {_command.Schema}.users ( id SERIAL PRIMARY KEY, email VARCHAR(256) NOT NULL UNIQUE, @@ -79,16 +70,16 @@ namespace PlanTempus.Database.Core.DDL last_login_at TIMESTAMPTZ NULL );"; - ExecuteSql(sql); + db.ExecuteSql(sql); - } + } - /// - /// Creates the organizations table - /// - void CreateOrganizationsTable() - { - var sql = @$" + /// + /// Creates the organizations table + /// + void CreateOrganizationsTable(IDbConnection db) + { + var sql = @$" CREATE TABLE IF NOT EXISTS {_command.Schema}.organizations ( id SERIAL PRIMARY KEY, connection_string VARCHAR(500) NOT NULL, @@ -97,16 +88,16 @@ namespace PlanTempus.Database.Core.DDL created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP );"; - ExecuteSql(sql); + db.ExecuteSql(sql); - } + } - /// - /// Creates the user_organizations table - /// - void CreateUserOrganizationsTable() - { - var sql = @$" + /// + /// Creates the user_organizations table + /// + void CreateUserOrganizationsTable(IDbConnection db) + { + var sql = @$" CREATE TABLE IF NOT EXISTS {_command.Schema}.user_organizations ( user_id INTEGER NOT NULL REFERENCES {_command.Schema}.users(id), organization_id INTEGER NOT NULL REFERENCES {_command.Schema}.organizations(id), @@ -115,37 +106,37 @@ namespace PlanTempus.Database.Core.DDL PRIMARY KEY (user_id, organization_id) );"; - ExecuteSql(sql); + db.ExecuteSql(sql); - } + } - /// - /// Sets up Row Level Security (RLS) for the organizations and user_organizations tables. - /// - void SetupRLS() - { - var sql = new[] - { - $"ALTER TABLE {_command.Schema}.organizations ENABLE ROW LEVEL SECURITY;", - $"ALTER TABLE {_command.Schema}.user_organizations ENABLE ROW LEVEL SECURITY;", - $"DROP POLICY IF EXISTS organization_access ON {_command.Schema}.organizations;", - @$"CREATE POLICY organization_access ON {_command.Schema}.organizations + /// + /// Sets up Row Level Security (RLS) for the organizations and user_organizations tables. + /// + void SetupRLS(IDbConnection db) + { + var sql = new[] + { + $"ALTER TABLE {_command.Schema}.organizations ENABLE ROW LEVEL SECURITY;", + $"ALTER TABLE {_command.Schema}.user_organizations ENABLE ROW LEVEL SECURITY;", + $"DROP POLICY IF EXISTS organization_access ON {_command.Schema}.organizations;", + @$"CREATE POLICY organization_access ON {_command.Schema}.organizations USING (id IN ( SELECT organization_id FROM {_command.Schema}.user_organizations WHERE user_id = current_setting('app.user_id', TRUE)::INTEGER )) WITH CHECK (true);", - $"DROP POLICY IF EXISTS user_organization_access ON {_command.Schema}.user_organizations;", - @$"CREATE POLICY user_organization_access ON {_command.Schema}.user_organizations + $"DROP POLICY IF EXISTS user_organization_access ON {_command.Schema}.user_organizations;", + @$"CREATE POLICY user_organization_access ON {_command.Schema}.user_organizations USING (user_id = current_setting('app.user_id', TRUE)::INTEGER) WITH CHECK (true);" - }; + }; - foreach (var statement in sql) - { - ExecuteSql(statement); - } - } + foreach (var statement in sql) + { + db.ExecuteSql(statement); + } + } - } + } } \ No newline at end of file diff --git a/Database/Core/Sql/SqlOperations.cs b/Database/Core/Sql/SqlOperations.cs index e0c5ef4..e044fc4 100644 --- a/Database/Core/Sql/SqlOperations.cs +++ b/Database/Core/Sql/SqlOperations.cs @@ -6,7 +6,9 @@ using System.Data; namespace PlanTempus.Database.Core.Sql { - public class DatabaseScope : IDisposable + + + public class DatabaseScope : IDisposable { private readonly IDbConnection _connection; private readonly IOperationHolder _operation; @@ -46,10 +48,10 @@ namespace PlanTempus.Database.Core.Sql public class SqlOperations : IDatabaseOperations { - private readonly IDbConnectionFactory _connectionFactory; + private readonly ConnectionFactory.IDbConnectionFactory _connectionFactory; private readonly TelemetryClient _telemetryClient; - public SqlOperations(IDbConnectionFactory connectionFactory, TelemetryClient telemetryClient) + public SqlOperations(ConnectionFactory.IDbConnectionFactory connectionFactory, TelemetryClient telemetryClient) { _connectionFactory = connectionFactory; _telemetryClient = telemetryClient; diff --git a/Database/ModuleRegistry/DbPostgreSqlModule.cs b/Database/ModuleRegistry/DbPostgreSqlModule.cs index b914756..fba43cb 100644 --- a/Database/ModuleRegistry/DbPostgreSqlModule.cs +++ b/Database/ModuleRegistry/DbPostgreSqlModule.cs @@ -3,11 +3,7 @@ using Npgsql; using System.Data; namespace PlanTempus.Database.ModuleRegistry { - public interface IDbConnectionFactory - { - IDbConnection Create(); - IDbConnection Create(string username, string password); - } + public class DbPostgreSqlModule : Module { public required string ConnectionString { get; set; } @@ -16,7 +12,7 @@ namespace PlanTempus.Database.ModuleRegistry { Insight.Database.Providers.PostgreSQL.PostgreSQLInsightDbProvider.RegisterProvider(); - builder.Register(c => + builder.Register(c => new Core.ConnectionFactory.PostgresConnectionFactory(ConnectionString)) .SingleInstance(); diff --git a/SetupInfrastructure/Program.cs b/SetupInfrastructure/Program.cs index 58b83a1..2d18d7e 100644 --- a/SetupInfrastructure/Program.cs +++ b/SetupInfrastructure/Program.cs @@ -1,6 +1,8 @@ using Autofac; using Insight.Database; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Npgsql; using PlanTempus.Database.ConfigurationManagementSystem; using PlanTempus.Database.Core.DCL; using PlanTempus.Database.Core.DDL; @@ -23,28 +25,28 @@ namespace PlanTempus.SetupInfrastructure /// internal class Program { - static IContainer _container; - static ConsoleColor _backgroundColor = Console.BackgroundColor; - static ConsoleColor _foregroundColor = Console.ForegroundColor; - + static async Task Main(string[] args) { try { var host = Host.CreateDefaultBuilder(args) - .UseEnvironment("Development") + .UseEnvironment("Dev") .UseServiceProviderFactory(new Autofac.Extensions.DependencyInjection.AutofacServiceProviderFactory()) .ConfigureContainer((hostContext, builder) => { var startup = new Startup(); - startup.ConfigureContainer(); + startup.ConfigureContainer(builder); }) .Build(); await host.StartAsync(); Console.WriteLine("Host has started."); - Run(); + + + var console = host.Services.GetRequiredService(); + console.Run(); await host.WaitForShutdownAsync(); } @@ -52,10 +54,26 @@ namespace PlanTempus.SetupInfrastructure { Console.WriteLine($"Host failed to start: {ex}"); } - - } + } + public class ConsoleService + { + static ConsoleColor _backgroundColor = Console.BackgroundColor; + static ConsoleColor _foregroundColor = Console.ForegroundColor; + + private readonly SetupDbAdmin _setupDbAdmin; + private readonly SetupIdentitySystem _setupIdentitySystem; + private readonly SetupConfiguration _setupConfiguration; + private readonly SetupApplicationUser _setupApplicationUser; + + public ConsoleService(SetupDbAdmin setupDbAdmin, SetupIdentitySystem setupIdentitySystem, SetupConfiguration setupConfiguration, SetupApplicationUser setupApplicationUser) + { + _setupDbAdmin = setupDbAdmin; + _setupIdentitySystem = setupIdentitySystem; + _setupConfiguration = setupConfiguration; + _setupApplicationUser = setupApplicationUser; + } static bool IsSuperAdmin() { //test db access @@ -63,8 +81,9 @@ namespace PlanTempus.SetupInfrastructure string query = @"SELECT usename, usesuper FROM pg_user WHERE usename = CURRENT_USER;"; - var conn = _container.Resolve(); - var result = (dynamic)conn.QuerySql(query).Single(); + var conn = new NpgsqlConnection(); + + var result = (dynamic)conn.QuerySql(query).Single(); string username = result.usename; bool isSuperuser = (bool)result.usesuper; @@ -135,10 +154,8 @@ namespace PlanTempus.SetupInfrastructure } - static void Run() + public void Run() { - - Welcome(); string userPass; @@ -165,8 +182,8 @@ namespace PlanTempus.SetupInfrastructure Console.Write("Database.Core.DCL.SetupDbAdmin..."); sw.Start(); - var setupDbAdmin = _container.Resolve(); - setupDbAdmin.With(new SetupDbAdmin.Command { Password = "3911", Schema = "system", User = "heimdall" }); + + _setupDbAdmin.With(new SetupDbAdmin.Command { Password = "3911", Schema = "system", User = "heimdall" }); Console.WriteLine($"DONE, took: {sw.ElapsedMilliseconds} ms"); @@ -178,16 +195,14 @@ namespace PlanTempus.SetupInfrastructure //create new container with application user, we use that role from now. //_container = new Startup().ConfigureContainer(new Startup.ConnectionStringTemplateParameters("heimdall", "3911")); - var setupIdentitySystem = _container.Resolve(); - setupIdentitySystem.With(new SetupIdentitySystem.Command { Schema = "system" }); + _setupIdentitySystem.With(new SetupIdentitySystem.Command { Schema = "system" }); Console.WriteLine($"DONE, took: {sw.ElapsedMilliseconds} ms"); Console.WriteLine("::"); Console.WriteLine("::"); Console.Write("Database.ConfigurationManagementSystem.SetupConfiguration..."); sw.Restart(); - var setupConfigurationSystem = _container.Resolve(); - setupConfigurationSystem.With(new SetupConfiguration.Command()); + _setupConfiguration.With(new SetupConfiguration.Command()); Console.Write($"DONE, took: {sw.ElapsedMilliseconds} ms"); @@ -195,8 +210,8 @@ namespace PlanTempus.SetupInfrastructure Console.WriteLine("::"); Console.Write("Database.Core.DCL.SetupApplicationUser..."); sw.Start(); - var setupApplicationUser = _container.Resolve(); - setupApplicationUser.With(new SetupApplicationUser.Command { Password = "3911", Schema = "system", User = "sathumper" }); + + _setupApplicationUser.With(new SetupApplicationUser.Command { Password = "3911", Schema = "system", User = "sathumper" }); Console.WriteLine($"DONE, took: {sw.ElapsedMilliseconds} ms"); @@ -207,7 +222,6 @@ namespace PlanTempus.SetupInfrastructure Console.ForegroundColor = _foregroundColor; } - } catch (Exception e) diff --git a/SetupInfrastructure/Startup.cs b/SetupInfrastructure/Startup.cs index b6a6138..2d3eb82 100644 --- a/SetupInfrastructure/Startup.cs +++ b/SetupInfrastructure/Startup.cs @@ -6,48 +6,50 @@ using PlanTempus.Database.Core; namespace PlanTempus.SetupInfrastructure { - public class Startup - { + public class Startup + { - public virtual IConfigurationRoot Configuration() - { - var configuration = new ConfigurationBuilder() - .AddJsonFile("appconfiguration.json") - .Build(); + public virtual IConfigurationRoot Configuration() + { + var configuration = new ConfigurationBuilder() + .AddJsonFile("appconfiguration.json") + .Build(); - return configuration; - } + return configuration; + } - public IContainer ConfigureContainer() - { + public void ConfigureContainer(ContainerBuilder builder) + { - var builder = new ContainerBuilder(); - var configuration = Configuration(); + //var builder = new ContainerBuilder(); + var configuration = Configuration(); - builder.RegisterModule(new Database.ModuleRegistry.DbPostgreSqlModule - { - ConnectionString = configuration.GetConnectionString("DefaultConnection") - }); + builder.RegisterModule(new Database.ModuleRegistry.DbPostgreSqlModule + { + ConnectionString = configuration.GetConnectionString("DefaultConnection") + }); - builder.RegisterModule(new TelemetryModule - { - TelemetryConfig = configuration.GetSection("ApplicationInsights").ToObject() - }); + builder.RegisterModule(new TelemetryModule + { + TelemetryConfig = configuration.GetSection("ApplicationInsights").ToObject() + }); - builder.RegisterModule(new SeqLoggingModule - { - SeqConfiguration = configuration.GetSection("SeqConfiguration").ToObject() - }); + builder.RegisterModule(new SeqLoggingModule + { + SeqConfiguration = configuration.GetSection("SeqConfiguration").ToObject() + }); - builder.RegisterAssemblyTypes(typeof(IDbConfigure<>).Assembly) - .AsClosedTypesOf(typeof(IDbConfigure<>)) - .AsSelf(); + builder.RegisterAssemblyTypes(typeof(IDbConfigure<>).Assembly) + .AsClosedTypesOf(typeof(IDbConfigure<>)) + .AsSelf(); + + builder.RegisterType(); - return builder.Build(); - } + //return builder.Build(); + } - } + } } diff --git a/Tests/PostgresTests.cs b/Tests/PostgresTests.cs index a3732a2..853b455 100644 --- a/Tests/PostgresTests.cs +++ b/Tests/PostgresTests.cs @@ -3,19 +3,20 @@ using System.Data; using Insight.Database; using Microsoft.VisualStudio.TestTools.UnitTesting; using PlanTempus.Database.Core.Sql; +using PlanTempus.Database.Core.ConnectionFactory; namespace PlanTempus.Tests { [TestClass] public class PostgresTests : TestFixture { - Database.ModuleRegistry.IDbConnectionFactory _connFactory; + IDbConnectionFactory _connFactory; IDatabaseOperations _databaseOperations; [TestInitialize] public void MyTestMethod() { - _connFactory = Container.Resolve(); + _connFactory = Container.Resolve(); _databaseOperations = Container.Resolve(); } [TestMethod]