SoftTraderBackend/DatabaseHandler/PostgresUserInterestStore.cs
2026-03-20 10:42:48 -04:00

142 lines
3.8 KiB
C#

using Npgsql;
using Backend.Interface;
namespace Backend.DatabaseHandler;
public sealed class PostgresUserInterestStore : IUserInterestStore
{
private readonly string _connectionString;
public PostgresUserInterestStore(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("MarketDataDb")
?? throw new InvalidOperationException("Missing connection string: MarketDataDb");
}
public async Task<List<string>> FetchAllAsync(string user, string type)
{
ValidateUser(user);
string normalizedType = NormalizeType(type);
string sql = normalizedType == "symbol"
? """
SELECT symbol
FROM user_interest_symbols
WHERE username = @user
ORDER BY symbol ASC;
"""
: """
SELECT topic
FROM user_interest_topics
WHERE username = @user
ORDER BY topic ASC;
""";
var results = new List<string>();
await using var conn = new NpgsqlConnection(_connectionString);
await conn.OpenAsync();
await using var cmd = new NpgsqlCommand(sql, conn);
cmd.Parameters.AddWithValue("user", user.Trim());
await using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
results.Add(reader.GetString(0));
}
return results;
}
public async Task<bool> AddAsync(string user, string type, string value)
{
ValidateUser(user);
ValidateValue(value);
string normalizedType = NormalizeType(type);
string normalizedValue = normalizedType == "symbol"
? value.Trim().ToUpperInvariant()
: value.Trim();
string sql = normalizedType == "symbol"
? """
INSERT INTO user_interest_symbols (username, symbol)
VALUES (@user, @value)
ON CONFLICT (username, symbol) DO NOTHING;
"""
: """
INSERT INTO user_interest_topics (username, topic)
VALUES (@user, @value)
ON CONFLICT (username, topic) DO NOTHING;
""";
await using var conn = new NpgsqlConnection(_connectionString);
await conn.OpenAsync();
await using var cmd = new NpgsqlCommand(sql, conn);
cmd.Parameters.AddWithValue("user", user.Trim());
cmd.Parameters.AddWithValue("value", normalizedValue);
int rows = await cmd.ExecuteNonQueryAsync();
return rows > 0;
}
public async Task<bool> RemoveAsync(string user, string type, string value)
{
ValidateUser(user);
ValidateValue(value);
string normalizedType = NormalizeType(type);
string normalizedValue = normalizedType == "symbol"
? value.Trim().ToUpperInvariant()
: value.Trim();
string sql = normalizedType == "symbol"
? """
DELETE FROM user_interest_symbols
WHERE username = @user AND symbol = @value;
"""
: """
DELETE FROM user_interest_topics
WHERE username = @user AND topic = @value;
""";
await using var conn = new NpgsqlConnection(_connectionString);
await conn.OpenAsync();
await using var cmd = new NpgsqlCommand(sql, conn);
cmd.Parameters.AddWithValue("user", user.Trim());
cmd.Parameters.AddWithValue("value", normalizedValue);
int rows = await cmd.ExecuteNonQueryAsync();
return rows > 0;
}
private static string NormalizeType(string type)
{
string normalized = type.Trim().ToLowerInvariant();
return normalized switch
{
"symbol" => "symbol",
"topic" => "topic",
_ => throw new ArgumentException("Type must be either 'symbol' or 'topic'.")
};
}
private static void ValidateUser(string user)
{
if (string.IsNullOrWhiteSpace(user))
{
throw new ArgumentException("User cannot be empty.");
}
}
private static void ValidateValue(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Value cannot be empty.");
}
}
}