Skip to content

Commit a3ff418

Browse files
authored
Allow specifying the connection string for Postgres (#811)
This can be done with either of the following methods: - Using the `POSTGRES_CONNECTION_STRING` environment variable - Writing it to `db.json` This refactors a bit of the database initialization code to make this possible. On Realm builds, this creates the new config file but has no effect
2 parents 240b45e + d17f2f7 commit a3ff418

9 files changed

Lines changed: 114 additions & 37 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Bunkum.Core.Configuration;
2+
using Refresh.Database;
3+
using Refresh.Database.Configuration;
4+
5+
namespace Refresh.Core.Configuration;
6+
7+
public class DatabaseConfig : Config, IDatabaseConfig
8+
{
9+
public override int CurrentConfigVersion => 1;
10+
public override int Version { get; set; }
11+
12+
protected override void Migrate(int oldVer, dynamic oldConfig)
13+
{
14+
15+
}
16+
17+
public string ConnectionString { get; set; } = "Database=refresh;Username=refresh;Password=refresh;Host=localhost;Port=5432";
18+
public bool PreferConnectionStringEnvironmentVariable { get; set; } = true;
19+
}

Refresh.Core/RefreshContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ public enum RefreshContext
1111
Publishing,
1212
Aipi,
1313
Presence,
14+
Database,
1415
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace Refresh.Database.Configuration;
2+
3+
public class EmptyDatabaseConfig : IDatabaseConfig
4+
{
5+
#if POSTGRES
6+
public string ConnectionString => new Npgsql.NpgsqlConnectionStringBuilder
7+
{
8+
Database = "refresh",
9+
Username = "refresh",
10+
Password = "refresh",
11+
Host = "localhost",
12+
Port = 5432,
13+
}.ToString();
14+
#else
15+
public string ConnectionString => string.Empty;
16+
#endif
17+
18+
public bool PreferConnectionStringEnvironmentVariable => false;
19+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Refresh.Database.Configuration;
2+
3+
public interface IDatabaseConfig
4+
{
5+
public string ConnectionString { get; }
6+
public bool PreferConnectionStringEnvironmentVariable { get; }
7+
}

Refresh.Database/GameDatabaseContext.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Diagnostics.CodeAnalysis;
22
using System.Runtime.CompilerServices;
33
using Refresh.Common.Time;
4+
using Refresh.Database.Configuration;
45
using Refresh.Database.Models.Authentication;
56
using Refresh.Database.Models.Activity;
67
using Refresh.Database.Models.Assets;
@@ -35,6 +36,7 @@ public partial class GameDatabaseContext :
3536
private static readonly object IdLock = new();
3637

3738
private readonly IDateTimeProvider _time;
39+
private readonly IDatabaseConfig _dbConfig;
3840

3941
#if !POSTGRES
4042
private RealmDbSet<GameUser> GameUsers => new(this._realm);
@@ -119,28 +121,29 @@ public partial class GameDatabaseContext :
119121
internal DbSet<GameSkillReward> GameSkillRewards { get; set; }
120122
#endif
121123

122-
internal GameDatabaseContext(IDateTimeProvider time)
124+
internal GameDatabaseContext(IDateTimeProvider time, IDatabaseConfig dbConfig)
123125
{
124126
this._time = time;
127+
this._dbConfig = dbConfig;
125128
}
126129

127130
#if POSTGRES
128131
[Obsolete("For use by the `dotnet ef` tool only.", true)]
129-
public GameDatabaseContext() : this(new SystemDateTimeProvider())
132+
public GameDatabaseContext() : this(new SystemDateTimeProvider(), new EmptyDatabaseConfig())
130133
{}
131134

132135
protected override void OnConfiguring(DbContextOptionsBuilder options)
133136
{
134137
base.OnConfiguring(options);
135-
NpgsqlConnectionStringBuilder builder = new()
138+
string connectionString = this._dbConfig.ConnectionString;
139+
if (this._dbConfig.PreferConnectionStringEnvironmentVariable)
136140
{
137-
Database = "refresh",
138-
Username = "refresh",
139-
Password = "refresh",
140-
Host = "localhost",
141-
Port = 5432,
142-
};
143-
options.UseNpgsql(builder.ToString());
141+
string? envVarString = Environment.GetEnvironmentVariable("POSTGRES_CONNECTION_STRING");
142+
if (envVarString != null)
143+
connectionString = envVarString;
144+
}
145+
146+
options.UseNpgsql(connectionString);
144147
}
145148

146149
protected override void ConfigureConventions(ModelConfigurationBuilder config)

Refresh.Database/GameDatabaseProvider.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Refresh.Common.Time;
2+
using Refresh.Database.Configuration;
23
#if !POSTGRES
34
using Refresh.Database.Models.Authentication;
45
using Refresh.Database.Models.Comments;
@@ -27,15 +28,18 @@ public class GameDatabaseProvider :
2728
#endif
2829
{
2930
private readonly IDateTimeProvider _time;
30-
31-
public GameDatabaseProvider()
31+
private readonly IDatabaseConfig _dbConfig;
32+
33+
public GameDatabaseProvider(IDatabaseConfig dbConfig)
3234
{
3335
this._time = new SystemDateTimeProvider();
36+
this._dbConfig = dbConfig;
3437
}
3538

3639
protected GameDatabaseProvider(IDateTimeProvider time)
3740
{
3841
this._time = time;
42+
this._dbConfig = new EmptyDatabaseConfig();
3943
}
4044

4145
#if POSTGRES
@@ -137,7 +141,7 @@ public virtual GameDatabaseContext GetContext()
137141
protected override GameDatabaseContext CreateContext()
138142
#endif
139143
{
140-
return new GameDatabaseContext(this._time);
144+
return new GameDatabaseContext(this._time, this._dbConfig);
141145
}
142146

143147
#if !POSTGRES

Refresh.GameServer/RefreshGameServer.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,8 @@ public RefreshGameServer(
5959
IDataStore? dataStore = null
6060
) : base(listener)
6161
{
62-
databaseProvider ??= () => new GameDatabaseProvider();
6362
dataStore ??= new FileSystemDataStore();
64-
65-
this._databaseProvider = databaseProvider.Invoke();
66-
this._databaseProvider.Initialize();
63+
6764
this._dataStore = dataStore;
6865

6966
try
@@ -77,6 +74,26 @@ public RefreshGameServer(
7774
this.Logger.LogWarning(BunkumCategory.Configuration, "Failed to read dry.json: " + ex);
7875
}
7976

77+
if (databaseProvider == null)
78+
{
79+
DatabaseConfig? dbConfig = null;
80+
try
81+
{
82+
dbConfig = Config.LoadFromJsonFile<DatabaseConfig>("db.json", this.Logger);
83+
}
84+
catch (Exception ex)
85+
{
86+
this.Logger.LogCritical(RefreshContext.Database, "Failed to read the database configuration file: " + ex);
87+
this.Logger.Dispose();
88+
Environment.Exit(1);
89+
}
90+
91+
databaseProvider = () => new GameDatabaseProvider(dbConfig);
92+
}
93+
94+
this._databaseProvider = databaseProvider.Invoke();
95+
this._databaseProvider.Initialize();
96+
8097
// Uncomment if you want to use production refresh as a source for assets
8198
#if DEBUG
8299
// this._dataStore = new AggregateDataStore(dataStore, new RemoteRefreshDataStore());
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using Refresh.Database.Configuration;
2+
3+
namespace RefreshTests.GameServer.GameServer;
4+
5+
public class TestDatabaseConfig : IDatabaseConfig
6+
{
7+
#if POSTGRES
8+
public TestDatabaseConfig(Testcontainers.PostgreSql.PostgreSqlContainer container)
9+
{
10+
this.ConnectionString = container.GetConnectionString() + ";Include Error Detail=true";
11+
this.PreferConnectionStringEnvironmentVariable = false;
12+
}
13+
#else
14+
// ReSharper disable once ConvertConstructorToMemberInitializers
15+
public TestDatabaseConfig()
16+
{
17+
this.ConnectionString = "";
18+
this.PreferConnectionStringEnvironmentVariable = false;
19+
}
20+
#endif
21+
22+
public string ConnectionString { get; }
23+
public bool PreferConnectionStringEnvironmentVariable { get; }
24+
}
Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#if POSTGRES
22

3-
using Microsoft.EntityFrameworkCore;
4-
using Microsoft.Extensions.Logging;
53
using Refresh.Common.Time;
64
using Refresh.Database;
75
using Testcontainers.PostgreSql;
@@ -10,24 +8,9 @@ namespace RefreshTests.GameServer.GameServer;
108

119
public class TestGameDatabaseContext : GameDatabaseContext
1210
{
13-
private readonly PostgreSqlContainer _container;
14-
15-
[Obsolete("Do not use.", true)]
16-
public TestGameDatabaseContext()
17-
{
18-
throw new InvalidOperationException("Do not use this constructor.");
19-
}
20-
21-
public TestGameDatabaseContext(IDateTimeProvider time, PostgreSqlContainer container) : base(time)
22-
{
23-
this._container = container;
24-
}
25-
26-
protected override void OnConfiguring(DbContextOptionsBuilder options)
27-
{
28-
options.UseNpgsql(this._container.GetConnectionString() + ";Include Error Detail=true");
29-
// options.LogTo(Console.WriteLine, LogLevel.Information);
30-
}
11+
public TestGameDatabaseContext(IDateTimeProvider time, PostgreSqlContainer container) :
12+
base(time, new TestDatabaseConfig(container))
13+
{}
3114
}
3215

3316
#endif

0 commit comments

Comments
 (0)