Move system updates to the same patch system as members

This commit is contained in:
Ske 2020-06-29 14:39:19 +02:00
parent c5697b33e2
commit 9c1efc7886
12 changed files with 172 additions and 111 deletions

View File

@ -26,18 +26,20 @@ namespace PluralKit.API
return o; return o;
} }
public static void ApplyJson(this PKSystem system, JObject o) public static SystemPatch ToSystemPatch(JObject o)
{ {
if (o.ContainsKey("name")) system.Name = o.Value<string>("name").NullIfEmpty().BoundsCheckField(Limits.MaxSystemNameLength, "System name"); var patch = new SystemPatch();
if (o.ContainsKey("description")) system.Description = o.Value<string>("description").NullIfEmpty().BoundsCheckField(Limits.MaxDescriptionLength, "System description"); if (o.ContainsKey("name")) patch.Name = o.Value<string>("name").NullIfEmpty().BoundsCheckField(Limits.MaxSystemNameLength, "System name");
if (o.ContainsKey("tag")) system.Tag = o.Value<string>("tag").NullIfEmpty().BoundsCheckField(Limits.MaxSystemTagLength, "System tag"); if (o.ContainsKey("description")) patch.Description = o.Value<string>("description").NullIfEmpty().BoundsCheckField(Limits.MaxDescriptionLength, "System description");
if (o.ContainsKey("avatar_url")) system.AvatarUrl = o.Value<string>("avatar_url").NullIfEmpty().BoundsCheckField(Limits.MaxUriLength, "System avatar URL"); if (o.ContainsKey("tag")) patch.Tag = o.Value<string>("tag").NullIfEmpty().BoundsCheckField(Limits.MaxSystemTagLength, "System tag");
if (o.ContainsKey("tz")) system.UiTz = o.Value<string>("tz") ?? "UTC"; if (o.ContainsKey("avatar_url")) patch.AvatarUrl = o.Value<string>("avatar_url").NullIfEmpty().BoundsCheckField(Limits.MaxUriLength, "System avatar URL");
if (o.ContainsKey("tz")) patch.UiTz = o.Value<string>("tz") ?? "UTC";
if (o.ContainsKey("description_privacy")) system.DescriptionPrivacy = o.Value<string>("description_privacy").ParsePrivacy("description"); if (o.ContainsKey("description_privacy")) patch.DescriptionPrivacy = o.Value<string>("description_privacy").ParsePrivacy("description");
if (o.ContainsKey("member_list_privacy")) system.MemberListPrivacy = o.Value<string>("member_list_privacy").ParsePrivacy("member list"); if (o.ContainsKey("member_list_privacy")) patch.MemberListPrivacy = o.Value<string>("member_list_privacy").ParsePrivacy("member list");
if (o.ContainsKey("front_privacy")) system.FrontPrivacy = o.Value<string>("front_privacy").ParsePrivacy("front"); if (o.ContainsKey("front_privacy")) patch.FrontPrivacy = o.Value<string>("front_privacy").ParsePrivacy("front");
if (o.ContainsKey("front_history_privacy")) system.FrontHistoryPrivacy = o.Value<string>("front_history_privacy").ParsePrivacy("front history"); if (o.ContainsKey("front_history_privacy")) patch.FrontHistoryPrivacy = o.Value<string>("front_history_privacy").ParsePrivacy("front history");
return patch;
} }
public static JObject ToJson(this PKMember member, LookupContext ctx) public static JObject ToJson(this PKMember member, LookupContext ctx)

View File

@ -41,13 +41,13 @@ namespace PluralKit.API
public class SystemController : ControllerBase public class SystemController : ControllerBase
{ {
private IDataStore _data; private IDataStore _data;
private IDatabase _conn; private IDatabase _db;
private IAuthorizationService _auth; private IAuthorizationService _auth;
public SystemController(IDataStore data, IDatabase conn, IAuthorizationService auth) public SystemController(IDataStore data, IDatabase db, IAuthorizationService auth)
{ {
_data = data; _data = data;
_conn = conn; _db = db;
_auth = auth; _auth = auth;
} }
@ -55,7 +55,7 @@ namespace PluralKit.API
[Authorize] [Authorize]
public async Task<ActionResult<JObject>> GetOwnSystem() public async Task<ActionResult<JObject>> GetOwnSystem()
{ {
var system = await _conn.Execute(c => c.QuerySystem(User.CurrentSystem())); var system = await _db.Execute(c => c.QuerySystem(User.CurrentSystem()));
return system.ToJson(User.ContextFor(system)); return system.ToJson(User.ContextFor(system));
} }
@ -94,7 +94,7 @@ namespace PluralKit.API
var auth = await _auth.AuthorizeAsync(User, system, "ViewFrontHistory"); var auth = await _auth.AuthorizeAsync(User, system, "ViewFrontHistory");
if (!auth.Succeeded) return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view front history."); if (!auth.Succeeded) return StatusCode(StatusCodes.Status403Forbidden, "Unauthorized to view front history.");
using (var conn = await _conn.Obtain()) using (var conn = await _db.Obtain())
{ {
var res = await conn.QueryAsync<SwitchesReturn>( var res = await conn.QueryAsync<SwitchesReturn>(
@"select *, array( @"select *, array(
@ -132,17 +132,19 @@ namespace PluralKit.API
[Authorize] [Authorize]
public async Task<ActionResult<JObject>> EditSystem([FromBody] JObject changes) public async Task<ActionResult<JObject>> EditSystem([FromBody] JObject changes)
{ {
var system = await _conn.Execute(c => c.QuerySystem(User.CurrentSystem())); var system = await _db.Execute(c => c.QuerySystem(User.CurrentSystem()));
SystemPatch patch;
try try
{ {
system.ApplyJson(changes); patch = JsonModelExt.ToSystemPatch(changes);
} }
catch (JsonModelParseError e) catch (JsonModelParseError e)
{ {
return BadRequest(e.Message); return BadRequest(e.Message);
} }
await _data.SaveSystem(system); await _db.Execute(conn => conn.UpdateSystem(system.Id, patch));
return Ok(system.ToJson(User.ContextFor(system))); return Ok(system.ToJson(User.ContextFor(system)));
} }
@ -166,7 +168,7 @@ namespace PluralKit.API
// Resolve member objects for all given IDs // Resolve member objects for all given IDs
IEnumerable<PKMember> membersList; IEnumerable<PKMember> membersList;
using (var conn = await _conn.Obtain()) using (var conn = await _db.Obtain())
membersList = (await conn.QueryAsync<PKMember>("select * from members where hid = any(@Hids)", new {Hids = param.Members})).ToList(); membersList = (await conn.QueryAsync<PKMember>("select * from members where hid = any(@Hids)", new {Hids = param.Members})).ToList();
foreach (var member in membersList) foreach (var member in membersList)

View File

@ -34,8 +34,9 @@ namespace PluralKit.Bot
if (ctx.MatchFlag("c", "clear") || ctx.Match("clear")) if (ctx.MatchFlag("c", "clear") || ctx.Match("clear"))
{ {
ctx.System.Name = null; var clearPatch = new SystemPatch {Name = null};
await _data.SaveSystem(ctx.System); await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, clearPatch));
await ctx.Reply($"{Emojis.Success} System name cleared."); await ctx.Reply($"{Emojis.Success} System name cleared.");
return; return;
} }
@ -50,9 +51,12 @@ namespace PluralKit.Bot
return; return;
} }
if (newSystemName != null && newSystemName.Length > Limits.MaxSystemNameLength) throw Errors.SystemNameTooLongError(newSystemName.Length); if (newSystemName != null && newSystemName.Length > Limits.MaxSystemNameLength)
ctx.System.Name = newSystemName; throw Errors.SystemNameTooLongError(newSystemName.Length);
await _data.SaveSystem(ctx.System);
var patch = new SystemPatch {Name = newSystemName};
await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, patch));
await ctx.Reply($"{Emojis.Success} System name changed."); await ctx.Reply($"{Emojis.Success} System name changed.");
} }
@ -61,8 +65,9 @@ namespace PluralKit.Bot
if (ctx.MatchFlag("c", "clear") || ctx.Match("clear")) if (ctx.MatchFlag("c", "clear") || ctx.Match("clear"))
{ {
ctx.System.Description = null; var patch = new SystemPatch {Description = null};
await _data.SaveSystem(ctx.System); await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, patch));
await ctx.Reply($"{Emojis.Success} System description cleared."); await ctx.Reply($"{Emojis.Success} System description cleared.");
return; return;
} }
@ -84,8 +89,10 @@ namespace PluralKit.Bot
else else
{ {
if (newDescription.Length > Limits.MaxDescriptionLength) throw Errors.DescriptionTooLongError(newDescription.Length); if (newDescription.Length > Limits.MaxDescriptionLength) throw Errors.DescriptionTooLongError(newDescription.Length);
ctx.System.Description = newDescription;
await _data.SaveSystem(ctx.System); var patch = new SystemPatch {Description = newDescription};
await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, patch));
await ctx.Reply($"{Emojis.Success} System description changed."); await ctx.Reply($"{Emojis.Success} System description changed.");
} }
} }
@ -96,8 +103,9 @@ namespace PluralKit.Bot
if (ctx.MatchFlag("c", "clear") || ctx.Match("clear")) if (ctx.MatchFlag("c", "clear") || ctx.Match("clear"))
{ {
ctx.System.Tag = null; var patch = new SystemPatch {Tag = null};
await _data.SaveSystem(ctx.System); await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, patch));
await ctx.Reply($"{Emojis.Success} System tag cleared."); await ctx.Reply($"{Emojis.Success} System tag cleared.");
} else if (!ctx.HasNext(skipFlags: false)) } else if (!ctx.HasNext(skipFlags: false))
{ {
@ -112,8 +120,10 @@ namespace PluralKit.Bot
if (newTag != null) if (newTag != null)
if (newTag.Length > Limits.MaxSystemTagLength) if (newTag.Length > Limits.MaxSystemTagLength)
throw Errors.SystemNameTooLongError(newTag.Length); throw Errors.SystemNameTooLongError(newTag.Length);
ctx.System.Tag = newTag;
await _data.SaveSystem(ctx.System); var patch = new SystemPatch {Tag = newTag};
await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, patch));
await ctx.Reply($"{Emojis.Success} System tag changed. Member names will now end with `{newTag}` when proxied."); await ctx.Reply($"{Emojis.Success} System tag changed. Member names will now end with `{newTag}` when proxied.");
} }
} }
@ -124,8 +134,9 @@ namespace PluralKit.Bot
if (ctx.Match("clear") || ctx.MatchFlag("c", "clear")) if (ctx.Match("clear") || ctx.MatchFlag("c", "clear"))
{ {
ctx.System.AvatarUrl = null; var patch = new SystemPatch {AvatarUrl = null};
await _data.SaveSystem(ctx.System); await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, patch));
await ctx.Reply($"{Emojis.Success} System avatar cleared."); await ctx.Reply($"{Emojis.Success} System avatar cleared.");
return; return;
} }
@ -149,10 +160,12 @@ namespace PluralKit.Bot
if (member != null) if (member != null)
{ {
if (member.AvatarHash == null) throw Errors.UserHasNoAvatar; if (member.AvatarHash == null) throw Errors.UserHasNoAvatar;
ctx.System.AvatarUrl = member.GetAvatarUrl(ImageFormat.Png, size: 256);
await _data.SaveSystem(ctx.System);
var embed = new DiscordEmbedBuilder().WithImageUrl(ctx.System.AvatarUrl).Build(); var newUrl = member.GetAvatarUrl(ImageFormat.Png, size: 256);
var patch = new SystemPatch {AvatarUrl = newUrl};
await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, patch));
var embed = new DiscordEmbedBuilder().WithImageUrl(newUrl).Build();
await ctx.Reply( await ctx.Reply(
$"{Emojis.Success} System avatar changed to {member.Username}'s avatar! {Emojis.Warn} Please note that if {member.Username} changes their avatar, the system's avatar will need to be re-set.", embed: embed); $"{Emojis.Success} System avatar changed to {member.Username}'s avatar! {Emojis.Warn} Please note that if {member.Username} changes their avatar, the system's avatar will need to be re-set.", embed: embed);
} }
@ -163,8 +176,8 @@ namespace PluralKit.Bot
if (url?.Length > Limits.MaxUriLength) throw Errors.InvalidUrl(url); if (url?.Length > Limits.MaxUriLength) throw Errors.InvalidUrl(url);
await ctx.BusyIndicator(() => AvatarUtils.VerifyAvatarOrThrow(url)); await ctx.BusyIndicator(() => AvatarUtils.VerifyAvatarOrThrow(url));
ctx.System.AvatarUrl = url; var patch = new SystemPatch {AvatarUrl = url};
await _data.SaveSystem(ctx.System); await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, patch));
var embed = url != null ? new DiscordEmbedBuilder().WithImageUrl(url).Build() : null; var embed = url != null ? new DiscordEmbedBuilder().WithImageUrl(url).Build() : null;
await ctx.Reply($"{Emojis.Success} System avatar changed.", embed: embed); await ctx.Reply($"{Emojis.Success} System avatar changed.", embed: embed);
@ -178,7 +191,8 @@ namespace PluralKit.Bot
if (!await ctx.ConfirmWithReply(ctx.System.Hid)) if (!await ctx.ConfirmWithReply(ctx.System.Hid))
throw new PKError($"System deletion cancelled. Note that you must reply with your system ID (`{ctx.System.Hid}`) *verbatim*."); throw new PKError($"System deletion cancelled. Note that you must reply with your system ID (`{ctx.System.Hid}`) *verbatim*.");
await _data.DeleteSystem(ctx.System); await _db.Execute(conn => conn.DeleteSystem(ctx.System.Id));
await ctx.Reply($"{Emojis.Success} System deleted."); await ctx.Reply($"{Emojis.Success} System deleted.");
} }
@ -216,8 +230,9 @@ namespace PluralKit.Bot
if (ctx.MatchFlag("c", "clear") || ctx.Match("clear")) if (ctx.MatchFlag("c", "clear") || ctx.Match("clear"))
{ {
ctx.System.UiTz = "UTC"; var clearPatch = new SystemPatch {UiTz = "UTC"};
await _data.SaveSystem(ctx.System); await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, clearPatch));
await ctx.Reply($"{Emojis.Success} System time zone cleared (set to UTC)."); await ctx.Reply($"{Emojis.Success} System time zone cleared (set to UTC).");
return; return;
} }
@ -237,8 +252,9 @@ namespace PluralKit.Bot
var msg = await ctx.Reply( var msg = await ctx.Reply(
$"This will change the system time zone to **{zone.Id}**. The current time is **{currentTime.FormatZoned()}**. Is this correct?"); $"This will change the system time zone to **{zone.Id}**. The current time is **{currentTime.FormatZoned()}**. Is this correct?");
if (!await ctx.PromptYesNo(msg)) throw Errors.TimezoneChangeCancelled; if (!await ctx.PromptYesNo(msg)) throw Errors.TimezoneChangeCancelled;
ctx.System.UiTz = zone.Id;
await _data.SaveSystem(ctx.System); var patch = new SystemPatch {UiTz = zone.Id};
await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, patch));
await ctx.Reply($"System time zone changed to **{zone.Id}**."); await ctx.Reply($"System time zone changed to **{zone.Id}**.");
} }
@ -290,39 +306,41 @@ namespace PluralKit.Bot
string levelStr, levelExplanation, subjectStr; string levelStr, levelExplanation, subjectStr;
var subjectList = "`description`, `members`, `front`, `fronthistory`, or `all`"; var subjectList = "`description`, `members`, `front`, `fronthistory`, or `all`";
SystemPatch patch = new SystemPatch();
if (ctx.Match("description", "desc", "text", "info")) if (ctx.Match("description", "desc", "text", "info"))
{ {
subjectStr = "description"; subjectStr = "description";
ctx.System.DescriptionPrivacy = PopPrivacyLevel("description", out levelStr, out levelExplanation); patch.DescriptionPrivacy = PopPrivacyLevel("description", out levelStr, out levelExplanation);
} }
else if (ctx.Match("members", "memberlist", "list", "mlist")) else if (ctx.Match("members", "memberlist", "list", "mlist"))
{ {
subjectStr = "member list"; subjectStr = "member list";
ctx.System.MemberListPrivacy = PopPrivacyLevel("members", out levelStr, out levelExplanation); patch.MemberListPrivacy = PopPrivacyLevel("members", out levelStr, out levelExplanation);
} }
else if (ctx.Match("front", "fronter")) else if (ctx.Match("front", "fronter"))
{ {
subjectStr = "fronter(s)"; subjectStr = "fronter(s)";
ctx.System.FrontPrivacy = PopPrivacyLevel("front", out levelStr, out levelExplanation); patch.FrontPrivacy = PopPrivacyLevel("front", out levelStr, out levelExplanation);
} }
else if (ctx.Match("switch", "switches", "fronthistory", "fh")) else if (ctx.Match("switch", "switches", "fronthistory", "fh"))
{ {
subjectStr = "front history"; subjectStr = "front history";
ctx.System.FrontHistoryPrivacy = PopPrivacyLevel("fronthistory", out levelStr, out levelExplanation); patch.FrontHistoryPrivacy = PopPrivacyLevel("fronthistory", out levelStr, out levelExplanation);
} }
else if (ctx.Match("all")){ else if (ctx.Match("all")){
subjectStr = "all"; subjectStr = "all";
PrivacyLevel level = PopPrivacyLevel("all", out levelStr, out levelExplanation); PrivacyLevel level = PopPrivacyLevel("all", out levelStr, out levelExplanation);
ctx.System.DescriptionPrivacy = level; patch.DescriptionPrivacy = level;
ctx.System.MemberListPrivacy = level; patch.MemberListPrivacy = level;
ctx.System.FrontPrivacy = level; patch.FrontPrivacy = level;
ctx.System.FrontHistoryPrivacy = level; patch.FrontHistoryPrivacy = level;
} }
else else
throw new PKSyntaxError($"Invalid privacy subject `{ctx.PopArgument()}` (must be {subjectList})."); throw new PKSyntaxError($"Invalid privacy subject `{ctx.PopArgument()}` (must be {subjectList}).");
await _data.SaveSystem(ctx.System); await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, patch));
if(subjectStr == "all"){ if(subjectStr == "all"){
if(levelStr == "private") if(levelStr == "private")
await ctx.Reply($"All of your systems privacy settings have been set to **{levelStr}**. Other accounts will now see nothing on the member card."); await ctx.Reply($"All of your systems privacy settings have been set to **{levelStr}**. Other accounts will now see nothing on the member card.");
@ -345,13 +363,15 @@ namespace PluralKit.Bot
} }
else { else {
if (ctx.Match("on", "enable")) { if (ctx.Match("on", "enable")) {
ctx.System.PingsEnabled = true; var patch = new SystemPatch {PingsEnabled = true};
await _data.SaveSystem(ctx.System); await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, patch));
await ctx.Reply("Reaction pings have now been enabled."); await ctx.Reply("Reaction pings have now been enabled.");
} }
if (ctx.Match("off", "disable")) { if (ctx.Match("off", "disable")) {
ctx.System.PingsEnabled = false; var patch = new SystemPatch {PingsEnabled = false};
await _data.SaveSystem(ctx.System); await _db.Execute(conn => conn.UpdateSystem(ctx.System.Id, patch));
await ctx.Reply("Reaction pings have now been disabled."); await ctx.Reply("Reaction pings have now been disabled.");
} }
} }

View File

@ -8,10 +8,10 @@ namespace PluralKit.Bot
{ {
public class Token public class Token
{ {
private IDataStore _data; private readonly IDatabase _db;
public Token(IDataStore data) public Token(IDatabase db)
{ {
_data = data; _db = db;
} }
public async Task GetToken(Context ctx) public async Task GetToken(Context ctx)
@ -35,8 +35,9 @@ namespace PluralKit.Bot
private async Task<string> MakeAndSetNewToken(PKSystem system) private async Task<string> MakeAndSetNewToken(PKSystem system)
{ {
system.Token = Core.StringUtils.GenerateToken(); var patch = new SystemPatch {Token = StringUtils.GenerateToken()};
await _data.SaveSystem(system); await _db.Execute(conn => conn.UpdateSystem(system.Id, patch));
return system.Token; return system.Token;
} }

View File

@ -28,16 +28,5 @@ namespace PluralKit.Core
conn.QueryFirstAsync<MemberGuildSettings>( conn.QueryFirstAsync<MemberGuildSettings>(
"insert into member_guild (guild, member) values (@guild, @member) on conflict (guild, member) do update set guild = @guild, member = @member returning *", "insert into member_guild (guild, member) values (@guild, @member) on conflict (guild, member) do update set guild = @guild, member = @member returning *",
new {guild, member}); new {guild, member});
public static Task<PKMember> UpdateMember(this IPKConnection conn, MemberId id, MemberPatch patch)
{
var (query, pms) = patch.Apply(new UpdateQueryBuilder("members", "id = @id"))
.WithConstant("id", id)
.Build("returning *");
return conn.QueryFirstAsync<PKMember>(query, pms);
}
public static Task DeleteMember(this IPKConnection conn, MemberId id) =>
conn.ExecuteAsync("delete from members where id = @Id", new {Id = id});
} }
} }

View File

@ -10,18 +10,18 @@ namespace PluralKit.Core {
// Additions here should be mirrored in SystemStore::Save // Additions here should be mirrored in SystemStore::Save
[Key] public SystemId Id { get; } [Key] public SystemId Id { get; }
public string Hid { get; } public string Hid { get; }
public string Name { get; set; } public string Name { get; }
public string Description { get; set; } public string Description { get; }
public string Tag { get; set; } public string Tag { get; }
public string AvatarUrl { get; set; } public string AvatarUrl { get; }
public string Token { get; set; } public string Token { get; }
public Instant Created { get; } public Instant Created { get; }
public string UiTz { get; set; } public string UiTz { get; set; }
public bool PingsEnabled { get; set; } public bool PingsEnabled { get; }
public PrivacyLevel DescriptionPrivacy { get; set; } public PrivacyLevel DescriptionPrivacy { get; }
public PrivacyLevel MemberListPrivacy { get; set; } public PrivacyLevel MemberListPrivacy { get;}
public PrivacyLevel FrontPrivacy { get; set; } public PrivacyLevel FrontPrivacy { get; }
public PrivacyLevel FrontHistoryPrivacy { get; set; } public PrivacyLevel FrontHistoryPrivacy { get; }
[JsonIgnore] public DateTimeZone Zone => DateTimeZoneProviders.Tzdb.GetZoneOrNull(UiTz); [JsonIgnore] public DateTimeZone Zone => DateTimeZoneProviders.Tzdb.GetZoneOrNull(UiTz);
} }

View File

@ -4,7 +4,7 @@ using NodaTime;
namespace PluralKit.Core namespace PluralKit.Core
{ {
public class MemberPatch: PatchObject<MemberId, PKMember> public class MemberPatch: PatchObject
{ {
public Partial<string> Name { get; set; } public Partial<string> Name { get; set; }
public Partial<string?> DisplayName { get; set; } public Partial<string?> DisplayName { get; set; }

View File

@ -0,0 +1,31 @@
using System.Threading.Tasks;
using Dapper;
namespace PluralKit.Core
{
public static class ModelPatchExt
{
public static Task<PKSystem> UpdateSystem(this IPKConnection conn, SystemId id, SystemPatch patch)
{
var (query, pms) = patch.Apply(new UpdateQueryBuilder("systems", "id = @id"))
.WithConstant("id", id)
.Build("returning *");
return conn.QueryFirstAsync<PKSystem>(query, pms);
}
public static Task DeleteSystem(this IPKConnection conn, SystemId id) =>
conn.ExecuteAsync("delete from systems where id = @Id", new {Id = id});
public static Task<PKMember> UpdateMember(this IPKConnection conn, MemberId id, MemberPatch patch)
{
var (query, pms) = patch.Apply(new UpdateQueryBuilder("members", "id = @id"))
.WithConstant("id", id)
.Build("returning *");
return conn.QueryFirstAsync<PKMember>(query, pms);
}
public static Task DeleteMember(this IPKConnection conn, MemberId id) =>
conn.ExecuteAsync("delete from members where id = @Id", new {Id = id});
}
}

View File

@ -1,8 +1,6 @@
using PluralKit.Core; namespace PluralKit.Core
namespace PluralKit.Core
{ {
public abstract class PatchObject<TKey, TObj> public abstract class PatchObject
{ {
public abstract UpdateQueryBuilder Apply(UpdateQueryBuilder b); public abstract UpdateQueryBuilder Apply(UpdateQueryBuilder b);
} }

View File

@ -0,0 +1,31 @@
#nullable enable
namespace PluralKit.Core
{
public class SystemPatch: PatchObject
{
public Partial<string?> Name { get; set; }
public Partial<string?> Description { get; set; }
public Partial<string?> Tag { get; set; }
public Partial<string?> AvatarUrl { get; set; }
public Partial<string?> Token { get; set; }
public Partial<string> UiTz { get; set; }
public Partial<PrivacyLevel> DescriptionPrivacy { get; set; }
public Partial<PrivacyLevel> MemberListPrivacy { get; set; }
public Partial<PrivacyLevel> FrontPrivacy { get; set; }
public Partial<PrivacyLevel> FrontHistoryPrivacy { get; set; }
public Partial<bool> PingsEnabled { get; set; }
public override UpdateQueryBuilder Apply(UpdateQueryBuilder b) => b
.With("name", Name)
.With("description", Description)
.With("tag", Tag)
.With("avatar_url", AvatarUrl)
.With("token", Token)
.With("ui_tz", UiTz)
.With("description_privacy", DescriptionPrivacy)
.With("member_list_privacy", MemberListPrivacy)
.With("front_privacy", FrontPrivacy)
.With("front_history_privacy", FrontHistoryPrivacy)
.With("pings_enabled", PingsEnabled);
}
}

View File

@ -116,16 +116,17 @@ namespace PluralKit.Core
await _data.AddAccount(system, accountId); await _data.AddAccount(system, accountId);
} }
await using var conn = await _db.Obtain();
// Apply system info // Apply system info
system.Name = data.Name; var patch = new SystemPatch {Name = data.Name};
if (data.Description != null) system.Description = data.Description; if (data.Description != null) patch.Description = data.Description;
if (data.Tag != null) system.Tag = data.Tag; if (data.Tag != null) patch.Tag = data.Tag;
if (data.AvatarUrl != null) system.AvatarUrl = data.AvatarUrl; if (data.AvatarUrl != null) patch.AvatarUrl = data.AvatarUrl;
if (data.TimeZone != null) system.UiTz = data.TimeZone ?? "UTC"; if (data.TimeZone != null) patch.UiTz = data.TimeZone ?? "UTC";
await _data.SaveSystem(system); await conn.UpdateSystem(system.Id, patch);
// -- Member/switch import -- // -- Member/switch import --
await using var conn = await _db.Obtain();
await using (var imp = await BulkImporter.Begin(system, conn)) await using (var imp = await BulkImporter.Begin(system, conn))
{ {
// Tally up the members that didn't exist before, and check member count on import // Tally up the members that didn't exist before, and check member count on import

View File

@ -103,20 +103,6 @@ namespace PluralKit.Core {
/// <exception>Throws an exception (TODO: which?) if the given account is not linked to the given system.</exception> /// <exception>Throws an exception (TODO: which?) if the given account is not linked to the given system.</exception>
Task RemoveAccount(PKSystem system, ulong accountToRemove); Task RemoveAccount(PKSystem system, ulong accountToRemove);
/// <summary>
/// Saves the information within the given <see cref="PKSystem"/> struct to the data store.
/// </summary>
Task SaveSystem(PKSystem system);
/// <summary>
/// Deletes the given system from the database.
/// </summary>
/// <para>
/// This will also delete all the system's members, all system switches, and every message that has been proxied
/// by members in the system.
/// </para>
Task DeleteSystem(PKSystem system);
/// <summary> /// <summary>
/// Gets a member by its user-facing human ID. /// Gets a member by its user-facing human ID.
/// </summary> /// </summary>