Always load database config at start
Ensures that the database settings used by ef tools are those in config.
This commit is contained in:
parent
c3ecf2a877
commit
584a55cd60
3 changed files with 62 additions and 35 deletions
|
@ -1,22 +1,22 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Npgsql;
|
||||||
|
|
||||||
namespace RegexBot.Data;
|
namespace RegexBot.Data;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a database connection using the settings defined in the bot's global configuration.
|
/// Represents a database connection using the settings defined in the bot's global configuration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BotDatabaseContext : DbContext {
|
public class BotDatabaseContext : DbContext {
|
||||||
private static string? _npgsqlConnectionString;
|
private static readonly string _connectionString;
|
||||||
internal static string PostgresConnectionString {
|
|
||||||
#if DEBUG
|
static BotDatabaseContext() {
|
||||||
get {
|
// Get our own config loaded just for the SQL stuff
|
||||||
if (_npgsqlConnectionString != null) return _npgsqlConnectionString;
|
var conf = new InstanceConfig();
|
||||||
Console.WriteLine($"{nameof(RegexBot)} - {nameof(BotDatabaseContext)} note: Using hardcoded connection string!");
|
_connectionString = new NpgsqlConnectionStringBuilder() {
|
||||||
return _npgsqlConnectionString ?? "Host=localhost;Username=regexbot;Password=rb";
|
Host = conf.SqlHost ?? "localhost", // default to localhost
|
||||||
}
|
Database = conf.SqlDatabase,
|
||||||
#else
|
Username = conf.SqlUsername,
|
||||||
get => _npgsqlConnectionString!;
|
Password = conf.SqlPassword
|
||||||
#endif
|
}.ToString();
|
||||||
set => _npgsqlConnectionString ??= value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -37,7 +37,7 @@ public class BotDatabaseContext : DbContext {
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected sealed override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected sealed override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
=> optionsBuilder
|
=> optionsBuilder
|
||||||
.UseNpgsql(PostgresConnectionString)
|
.UseNpgsql(_connectionString)
|
||||||
.UseSnakeCaseNamingConvention();
|
.UseSnakeCaseNamingConvention();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Newtonsoft.Json;
|
using CommandLine;
|
||||||
using RegexBot.Data;
|
using Newtonsoft.Json;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace RegexBot;
|
namespace RegexBot;
|
||||||
|
@ -18,20 +19,20 @@ class InstanceConfig {
|
||||||
/// List of assemblies to load, by file. Paths are always relative to the bot directory.
|
/// List of assemblies to load, by file. Paths are always relative to the bot directory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal IReadOnlyList<string> Assemblies { get; }
|
internal IReadOnlyList<string> Assemblies { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Webhook URL for bot log reporting.
|
|
||||||
/// </summary>
|
|
||||||
internal string InstanceLogTarget { get; }
|
internal string InstanceLogTarget { get; }
|
||||||
|
|
||||||
// TODO add fields for services to be configurable: DMRelay
|
public string? SqlHost { get; }
|
||||||
|
public string? SqlDatabase { get; }
|
||||||
|
public string SqlUsername { get; }
|
||||||
|
public string SqlPassword { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets up instance configuration object from file and command line parameters.
|
/// Sets up instance configuration object from file and command line parameters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal InstanceConfig() {
|
internal InstanceConfig() {
|
||||||
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)
|
var args = CommandLineParameters.Parse(Environment.GetCommandLineArgs());
|
||||||
+ "." + Path.DirectorySeparatorChar + "instance.json";
|
var path = args?.ConfigFile ?? Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)
|
||||||
|
+ Path.DirectorySeparatorChar + "." + Path.DirectorySeparatorChar + "instance.json";
|
||||||
|
|
||||||
JObject conf;
|
JObject conf;
|
||||||
try {
|
try {
|
||||||
|
@ -45,19 +46,8 @@ class InstanceConfig {
|
||||||
throw new Exception(pfx + ex.Message, ex);
|
throw new Exception(pfx + ex.Message, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input validation - throw exception on errors. Exception messages printed as-is.
|
BotToken = ReadConfKey<string>(conf, nameof(BotToken), true);
|
||||||
BotToken = conf[nameof(BotToken)]?.Value<string>()!;
|
InstanceLogTarget = ReadConfKey<string>(conf, nameof(InstanceLogTarget), true);
|
||||||
if (string.IsNullOrEmpty(BotToken))
|
|
||||||
throw new Exception($"'{nameof(BotToken)}' is not properly specified in configuration.");
|
|
||||||
|
|
||||||
var pginput = conf[nameof(BotDatabaseContext.PostgresConnectionString)]?.Value<string>()!;
|
|
||||||
if (string.IsNullOrEmpty(pginput))
|
|
||||||
throw new Exception($"'{nameof(BotDatabaseContext.PostgresConnectionString)}' is not properly specified in configuration.");
|
|
||||||
BotDatabaseContext.PostgresConnectionString = pginput;
|
|
||||||
|
|
||||||
InstanceLogTarget = conf[nameof(InstanceLogTarget)]?.Value<string>()!;
|
|
||||||
if (string.IsNullOrEmpty(InstanceLogTarget))
|
|
||||||
throw new Exception($"'{nameof(InstanceLogTarget)}' is not properly specified in configuration.");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Assemblies = Common.Utilities.LoadStringOrStringArray(conf[nameof(Assemblies)]).AsReadOnly();
|
Assemblies = Common.Utilities.LoadStringOrStringArray(conf[nameof(Assemblies)]).AsReadOnly();
|
||||||
|
@ -66,5 +56,41 @@ class InstanceConfig {
|
||||||
} catch (ArgumentException) {
|
} catch (ArgumentException) {
|
||||||
throw new Exception($"'{nameof(Assemblies)}' is not properly specified in configuration.");
|
throw new Exception($"'{nameof(Assemblies)}' is not properly specified in configuration.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SqlHost = ReadConfKey<string>(conf, nameof(SqlHost), false);
|
||||||
|
SqlDatabase = ReadConfKey<string?>(conf, nameof(SqlDatabase), false);
|
||||||
|
SqlUsername = ReadConfKey<string>(conf, nameof(SqlUsername), true);
|
||||||
|
SqlPassword = ReadConfKey<string>(conf, nameof(SqlPassword), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T? ReadConfKey<T>(JObject jc, string key, [DoesNotReturnIf(true)] bool failOnEmpty) {
|
||||||
|
if (jc.ContainsKey(key)) return jc[key]!.Value<T>();
|
||||||
|
if (failOnEmpty) throw new Exception($"'{key}' must be specified in the instance configuration.");
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Command line options
|
||||||
|
/// </summary>
|
||||||
|
class CommandLineParameters {
|
||||||
|
[Option('c', "config", Default = null,
|
||||||
|
HelpText = "Custom path to instance configuration. Defaults to instance.json in bot directory.")]
|
||||||
|
public string ConfigFile { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Command line arguments parsed here. Depending on inputs, the program can exit here.
|
||||||
|
/// </summary>
|
||||||
|
public static CommandLineParameters? Parse(string[] args) {
|
||||||
|
CommandLineParameters? result = null;
|
||||||
|
|
||||||
|
new Parser(settings => {
|
||||||
|
settings.IgnoreUnknownArguments = true;
|
||||||
|
settings.AutoHelp = false;
|
||||||
|
settings.AutoVersion = false;
|
||||||
|
}).ParseArguments<CommandLineParameters>(args)
|
||||||
|
.WithParsed(p => result = p)
|
||||||
|
.WithNotParsed(e => { /* ignore */ });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageReference Include="Discord.Net" Version="3.7.2" />
|
<PackageReference Include="Discord.Net" Version="3.7.2" />
|
||||||
<PackageReference Include="EFCore.NamingConventions" Version="6.0.0" />
|
<PackageReference Include="EFCore.NamingConventions" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.7" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.7" />
|
||||||
|
|
Loading…
Reference in a new issue