#nullable enable using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using Dapper; namespace PluralKit.Core { public partial class ModelRepository { public Task GetSystem(IPKConnection conn, SystemId id) => conn.QueryFirstOrDefaultAsync("select * from systems where id = @id", new {id}); public Task GetSystemByAccount(IPKConnection conn, ulong accountId) => conn.QuerySingleOrDefaultAsync( "select systems.* from systems, accounts where accounts.system = systems.id and accounts.uid = @Id", new {Id = accountId}); public Task GetSystemByHid(IPKConnection conn, string hid) => conn.QuerySingleOrDefaultAsync("select * from systems where systems.hid = @Hid", new {Hid = hid.ToLower()}); public Task> GetSystemAccounts(IPKConnection conn, SystemId system) => conn.QueryAsync("select uid from accounts where system = @Id", new {Id = system}); public IAsyncEnumerable GetSystemMembers(IPKConnection conn, SystemId system) => conn.QueryStreamAsync("select * from members where system = @SystemID", new {SystemID = system}); public Task GetSystemMemberCount(IPKConnection conn, SystemId id, PrivacyLevel? privacyFilter = null) { var query = new StringBuilder("select count(*) from members where system = @Id"); if (privacyFilter != null) query.Append($" and member_visibility = {(int) privacyFilter.Value}"); return conn.QuerySingleAsync(query.ToString(), new {Id = id}); } public async Task CreateSystem(IPKConnection conn, string? systemName = null) { var system = await conn.QuerySingleAsync( "insert into systems (hid, name) values (find_free_system_hid(), @Name) returning *", new {Name = systemName}); _logger.Information("Created {SystemId}", system.Id); return system; } public Task UpdateSystem(IPKConnection conn, SystemId id, SystemPatch patch) { _logger.Information("Updated {SystemId}: {@SystemPatch}", id, patch); var (query, pms) = patch.Apply(UpdateQueryBuilder.Update("systems", "id = @id")) .WithConstant("id", id) .Build("returning *"); return conn.QueryFirstAsync(query, pms); } public async Task AddAccount(IPKConnection conn, SystemId system, ulong accountId) { // We have "on conflict do nothing" since linking an account when it's already linked to the same system is idempotent // This is used in import/export, although the pk;link command checks for this case beforehand await conn.ExecuteAsync("insert into accounts (uid, system) values (@Id, @SystemId) on conflict do nothing", new {Id = accountId, SystemId = system}); _logger.Information("Linked account {UserId} to {SystemId}", accountId, system); } public async Task RemoveAccount(IPKConnection conn, SystemId system, ulong accountId) { await conn.ExecuteAsync("delete from accounts where uid = @Id and system = @SystemId", new {Id = accountId, SystemId = system}); _logger.Information("Unlinked account {UserId} from {SystemId}", accountId, system); } public Task DeleteSystem(IPKConnection conn, SystemId id) { _logger.Information("Deleted {SystemId}", id); return conn.ExecuteAsync("delete from systems where id = @Id", new {Id = id}); } } }