141 lines
3.8 KiB
C#
141 lines
3.8 KiB
C#
using Npgsql;
|
|
|
|
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.");
|
|
}
|
|
}
|
|
} |