Removed public access to database config

Database connection is now acquired directly via Configuration
instead of an object within it.
This commit is contained in:
Noikoio 2018-02-16 23:41:12 -08:00
parent 26da617cf1
commit 683b852de7
3 changed files with 64 additions and 35 deletions

View file

@ -1,56 +1,43 @@
using Newtonsoft.Json.Linq;
using Npgsql;
using System;
using System.Threading.Tasks;
namespace Noikoio.RegexBot.ConfigItem
{
class DatabaseConfig
{
private readonly bool _enabled;
private readonly string _host;
private readonly string _user;
private readonly string _pass;
private readonly string _dbname;
private readonly string _parsemsg;
/// <summary>
/// Gets whether database storage is available.
/// </summary>
public bool Available => _enabled;
/// <summary>
/// Constructor error message (only if not enabled)
/// </summary>
public string ParseMsg => _parsemsg;
public DatabaseConfig(JToken ctok)
{
if (ctok == null || ctok.Type != JTokenType.Object)
{
_enabled = false;
_parsemsg = "Database configuration not defined.";
return;
throw new DatabaseConfigLoadException("");
}
var conf = (JObject)ctok;
_host = conf["hostname"]?.Value<string>() ?? "localhost"; // default to localhost
_user = conf["username"]?.Value<string>();
if (string.IsNullOrWhiteSpace(_user))
throw new DatabaseConfigLoadException("Value for username is not defined.");
_pass = conf["password"]?.Value<string>();
if (string.IsNullOrWhiteSpace(_pass))
throw new DatabaseConfigLoadException(
$"Value for password is not defined. {nameof(RegexBot)} only supports password authentication.");
_dbname = conf["database"]?.Value<string>();
if (string.IsNullOrWhiteSpace(_user) || string.IsNullOrWhiteSpace(_pass) || string.IsNullOrWhiteSpace(_dbname))
{
_parsemsg = "One or more required values are invalid or not defined.";
_enabled = false;
if (string.IsNullOrWhiteSpace(_dbname))
throw new DatabaseConfigLoadException("Value for database name is not defined.");
}
_parsemsg = null;
_enabled = true;
}
public async Task<NpgsqlConnection> GetOpenConnectionAsync()
internal async Task<NpgsqlConnection> GetOpenConnectionAsync()
{
if (!Available) return null;
var cs = new NpgsqlConnectionStringBuilder()
{
Host = _host,
@ -63,5 +50,10 @@ namespace Noikoio.RegexBot.ConfigItem
await db.OpenAsync();
return db;
}
internal class DatabaseConfigLoadException : Exception
{
public DatabaseConfigLoadException(string message) : base(message) { }
}
}
}

View file

@ -29,7 +29,6 @@ namespace Noikoio.RegexBot
public string BotUserToken => _botToken;
public string CurrentGame => _currentGame;
public DatabaseConfig Database => _dbConfig;
public ServerConfig[] Servers => _servers;
@ -72,22 +71,44 @@ namespace Noikoio.RegexBot
/// <summary>
/// Loads essential, unchanging values needed for bot startup. Returns false on failure.
/// </summary>
public bool LoadInitialConfig()
internal bool LoadInitialConfig()
{
var lt = LoadFile();
lt.Wait();
JObject conf = lt.Result;
if (conf == null) return false;
var log = Logger.GetLogger(LogPrefix);
_botToken = conf["bot-token"]?.Value<string>();
if (String.IsNullOrWhiteSpace(_botToken))
{
Logger.GetLogger(LogPrefix)("Error: Bot token not defined. Cannot continue.").Wait();
log("Error: Bot token not defined. Cannot continue.").Wait();
return false;
}
_currentGame = conf["playing"]?.Value<string>();
// Database configuration:
// Either it exists or it doesn't. Read config, but also attempt to make a database connection
// right here, or else make it known that database support is disabled for this instance.
try
{
_dbConfig = new DatabaseConfig(conf["database"]);
var conn = _dbConfig.GetOpenConnectionAsync().GetAwaiter().GetResult();
conn.Dispose();
}
catch (DatabaseConfig.DatabaseConfigLoadException ex)
{
if (ex.Message == "") log("Database configuration not found.").Wait();
else log("Error within database config: " + ex.Message).Wait();
_dbConfig = null;
}
catch (Npgsql.NpgsqlException ex)
{
log("An error occurred while establishing initial database connection: " + ex.Message).Wait();
_dbConfig = null;
}
// Modules that will not enable due to lack of database access should say so in their constructors.
return true;
}
@ -179,7 +200,23 @@ namespace Noikoio.RegexBot
_servers = newservers.ToArray();
return true;
}
}
/// <summary>
/// Gets a value stating if database access is available.
/// Specifically, indicates if <see cref="GetOpenDatabaseConnectionAsync"/> will return a non-null value.
/// </summary>
/// <remarks>
/// Ideally, this value remains constant on runtime. It does not take into account
/// the possibility of the database connection failing during the program's run time.
/// </remarks>
public bool DatabaseAvailable => _dbConfig != null;
/// <summary>
/// Gets an opened connection to the SQL database, if available.
/// </summary>
/// <returns>
/// An <see cref="Npgsql.NpgsqlConnection"/> in the opened state,
/// or null if an SQL database is not available.
/// </returns>
public Task<Npgsql.NpgsqlConnection> GetOpenDatabaseConnectionAsync() => _dbConfig?.GetOpenConnectionAsync();
}
}

View file

@ -100,7 +100,7 @@ namespace Noikoio.RegexBot.EntityCache
if (lresult != null) return lresult;
// Database cache search
var db = await RegexBot.Config.Database.GetOpenConnectionAsync();
var db = await RegexBot.Config.GetOpenDatabaseConnectionAsync();
if (db == null) return null; // Database not available for query.
using (db) return await DbQueryAsync(db, guild, user);
}
@ -171,7 +171,7 @@ namespace Noikoio.RegexBot.EntityCache
if (lresult.Count() != 0) return lresult;
// Database cache search
var db = await RegexBot.Config.Database.GetOpenConnectionAsync();
var db = await RegexBot.Config.GetOpenDatabaseConnectionAsync();
if (db == null) return null; // Database not available for query.
using (db) return await DbQueryAsync(db, guild, name, disc);
}
@ -207,7 +207,7 @@ namespace Noikoio.RegexBot.EntityCache
{
var result = new List<CacheUser>();
using (db = await RegexBot.Config.Database.GetOpenConnectionAsync())
using (db = await RegexBot.Config.GetOpenDatabaseConnectionAsync())
{
using (var c = db.CreateCommand())
{