diff --git a/ConfigItem/DatabaseConfig.cs b/ConfigItem/DatabaseConfig.cs
index 3cee6e3..5afe137 100644
--- a/ConfigItem/DatabaseConfig.cs
+++ b/ConfigItem/DatabaseConfig.cs
@@ -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;
-
- ///
- /// Gets whether database storage is available.
- ///
- public bool Available => _enabled;
- ///
- /// Constructor error message (only if not enabled)
- ///
- 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() ?? "localhost"; // default to localhost
+
_user = conf["username"]?.Value();
+ if (string.IsNullOrWhiteSpace(_user))
+ throw new DatabaseConfigLoadException("Value for username is not defined.");
+
_pass = conf["password"]?.Value();
+ if (string.IsNullOrWhiteSpace(_pass))
+ throw new DatabaseConfigLoadException(
+ $"Value for password is not defined. {nameof(RegexBot)} only supports password authentication.");
+
_dbname = conf["database"]?.Value();
-
- if (string.IsNullOrWhiteSpace(_user) || string.IsNullOrWhiteSpace(_pass) || string.IsNullOrWhiteSpace(_dbname))
- {
- _parsemsg = "One or more required values are invalid or not defined.";
- _enabled = false;
- }
-
- _parsemsg = null;
- _enabled = true;
+ if (string.IsNullOrWhiteSpace(_dbname))
+ throw new DatabaseConfigLoadException("Value for database name is not defined.");
}
- public async Task GetOpenConnectionAsync()
+ internal async Task 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) { }
+ }
}
}
diff --git a/Configuration.cs b/Configuration.cs
index e49a760..6592ee5 100644
--- a/Configuration.cs
+++ b/Configuration.cs
@@ -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
///
/// Loads essential, unchanging values needed for bot startup. Returns false on failure.
///
- 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();
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();
- _dbConfig = new DatabaseConfig(conf["database"]);
+ // 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;
}
- }
-
+ ///
+ /// Gets a value stating if database access is available.
+ /// Specifically, indicates if will return a non-null value.
+ ///
+ ///
+ /// 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.
+ ///
+ public bool DatabaseAvailable => _dbConfig != null;
+ ///
+ /// Gets an opened connection to the SQL database, if available.
+ ///
+ ///
+ /// An in the opened state,
+ /// or null if an SQL database is not available.
+ ///
+ public Task GetOpenDatabaseConnectionAsync() => _dbConfig?.GetOpenConnectionAsync();
+ }
}
diff --git a/EntityCache/CacheUser.cs b/EntityCache/CacheUser.cs
index 0d1f024..740ed9c 100644
--- a/EntityCache/CacheUser.cs
+++ b/EntityCache/CacheUser.cs
@@ -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();
- using (db = await RegexBot.Config.Database.GetOpenConnectionAsync())
+ using (db = await RegexBot.Config.GetOpenDatabaseConnectionAsync())
{
using (var c = db.CreateCommand())
{