feat(apiv2): better model validation error UX
This commit is contained in:
parent
5add31c77e
commit
098d804344
@ -58,21 +58,21 @@ namespace PluralKit.API
|
||||
await using var tx = await conn.BeginTransactionAsync();
|
||||
var member = await _repo.CreateMember(systemId, properties.Value<string>("name"), conn);
|
||||
|
||||
MemberPatch patch;
|
||||
try
|
||||
{
|
||||
patch = MemberPatch.FromJSON(properties);
|
||||
patch.AssertIsValid();
|
||||
}
|
||||
catch (FieldTooLongError e)
|
||||
var patch = MemberPatch.FromJSON(properties);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
await tx.RollbackAsync();
|
||||
return BadRequest(e.Message);
|
||||
}
|
||||
catch (ValidationError e)
|
||||
{
|
||||
await tx.RollbackAsync();
|
||||
return BadRequest($"Request field '{e.Message}' is invalid.");
|
||||
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
return BadRequest($"Field {err.Key} is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
else if (err.Text != null)
|
||||
return BadRequest(err.Text);
|
||||
else
|
||||
return BadRequest($"Field {err.Key} is invalid.");
|
||||
}
|
||||
|
||||
member = await _repo.UpdateMember(member.Id, patch, conn);
|
||||
@ -90,19 +90,19 @@ namespace PluralKit.API
|
||||
var res = await _auth.AuthorizeAsync(User, member, "EditMember");
|
||||
if (!res.Succeeded) return Unauthorized($"Member '{hid}' is not part of your system.");
|
||||
|
||||
MemberPatch patch;
|
||||
try
|
||||
var patch = MemberPatch.FromJSON(changes);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
patch = MemberPatch.FromJSON(changes);
|
||||
patch.AssertIsValid();
|
||||
}
|
||||
catch (FieldTooLongError e)
|
||||
{
|
||||
return BadRequest(e.Message);
|
||||
}
|
||||
catch (ValidationError e)
|
||||
{
|
||||
return BadRequest($"Request field '{e.Message}' is invalid.");
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
return BadRequest($"Field {err.Key} is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
else if (err.Text != null)
|
||||
return BadRequest(err.Text);
|
||||
else
|
||||
return BadRequest($"Field {err.Key} is invalid.");
|
||||
}
|
||||
|
||||
var newMember = await _repo.UpdateMember(member.Id, patch);
|
||||
|
@ -133,19 +133,17 @@ namespace PluralKit.API
|
||||
{
|
||||
var system = await _repo.GetSystem(User.CurrentSystem());
|
||||
|
||||
SystemPatch patch;
|
||||
try
|
||||
var patch = SystemPatch.FromJSON(changes);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
patch = SystemPatch.FromJSON(changes);
|
||||
patch.AssertIsValid();
|
||||
}
|
||||
catch (FieldTooLongError e)
|
||||
{
|
||||
return BadRequest(e.Message);
|
||||
}
|
||||
catch (ValidationError e)
|
||||
{
|
||||
return BadRequest($"Request field '{e.Message}' is invalid.");
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
return BadRequest($"Field {err.Key} is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
|
||||
return BadRequest($"Field {err.Key} is invalid.");
|
||||
}
|
||||
|
||||
system = await _repo.UpdateSystem(system!.Id, patch);
|
||||
|
@ -55,17 +55,11 @@ namespace PluralKit.API
|
||||
else
|
||||
memberId = settings.AutoproxyMember;
|
||||
|
||||
SystemGuildPatch patch = null;
|
||||
try
|
||||
{
|
||||
patch = SystemGuildPatch.FromJson(data, memberId);
|
||||
patch.AssertIsValid();
|
||||
}
|
||||
catch (ValidationError e)
|
||||
{
|
||||
// todo
|
||||
return BadRequest(e.Message);
|
||||
}
|
||||
var patch = SystemGuildPatch.FromJson(data, memberId);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
|
||||
// this is less than great, but at least it's legible
|
||||
if (patch.AutoproxyMember.Value == null)
|
||||
@ -116,17 +110,11 @@ namespace PluralKit.API
|
||||
if (settings == null)
|
||||
throw APIErrors.MemberGuildNotFound;
|
||||
|
||||
MemberGuildPatch patch = null;
|
||||
try
|
||||
{
|
||||
patch = MemberGuildPatch.FromJson(data);
|
||||
patch.AssertIsValid();
|
||||
}
|
||||
catch (ValidationError e)
|
||||
{
|
||||
// todo
|
||||
return BadRequest(e.Message);
|
||||
}
|
||||
var patch = MemberGuildPatch.FromJson(data);
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
throw new ModelParseError(patch.Errors);
|
||||
|
||||
var newSettings = await _repo.UpdateMemberGuild(member.Id, guild_id, patch);
|
||||
return Ok(newSettings.ToJson());
|
||||
|
@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.API
|
||||
{
|
||||
public class PKError: Exception
|
||||
@ -25,15 +28,49 @@ namespace PluralKit.API
|
||||
|
||||
public class ModelParseError: PKError
|
||||
{
|
||||
public ModelParseError() : base(400, 40001, "Error parsing JSON model")
|
||||
private IEnumerable<ValidationError> _errors { get; init; }
|
||||
public ModelParseError(IEnumerable<ValidationError> errors) : base(400, 40001, "Error parsing JSON model")
|
||||
{
|
||||
// todo
|
||||
_errors = errors;
|
||||
}
|
||||
|
||||
public new JObject ToJson()
|
||||
{
|
||||
var j = base.ToJson();
|
||||
var e = new JObject();
|
||||
|
||||
foreach (var err in _errors)
|
||||
{
|
||||
var o = new JObject();
|
||||
|
||||
if (err is FieldTooLongError fe)
|
||||
{
|
||||
o.Add("message", $"Field {err.Key} is too long.");
|
||||
o.Add("actual_length", fe.ActualLength);
|
||||
o.Add("max_length", fe.MaxLength);
|
||||
}
|
||||
else if (err.Text != null)
|
||||
o.Add("message", err.Text);
|
||||
else
|
||||
o.Add("message", $"Field {err.Key} is invalid.");
|
||||
|
||||
if (e[err.Key] != null)
|
||||
{
|
||||
if (e[err.Key].Type == JTokenType.Object)
|
||||
{
|
||||
var current = e[err.Key];
|
||||
e.Remove(err.Key);
|
||||
e.Add(err.Key, new JArray());
|
||||
(e[err.Key] as JArray).Add(current);
|
||||
}
|
||||
|
||||
(e[err.Key] as JArray).Add(o);
|
||||
}
|
||||
else
|
||||
e.Add(err.Key, o);
|
||||
}
|
||||
|
||||
j.Add("errors", e);
|
||||
return j;
|
||||
}
|
||||
}
|
||||
|
@ -154,6 +154,15 @@ namespace PluralKit.API
|
||||
return;
|
||||
}
|
||||
|
||||
// for some reason, if we don't specifically cast to ModelParseError, it uses the base's ToJson method
|
||||
if (exc.Error is ModelParseError fe)
|
||||
{
|
||||
ctx.Response.StatusCode = fe.ResponseCode;
|
||||
await ctx.Response.WriteAsync(JsonConvert.SerializeObject(fe.ToJson()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var err = (PKError)exc.Error;
|
||||
ctx.Response.StatusCode = err.ResponseCode;
|
||||
|
||||
|
29
PluralKit.Core/Models/ModelTypes/Validation.cs
Normal file
29
PluralKit.Core/Models/ModelTypes/Validation.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public class ValidationError
|
||||
{
|
||||
public string Key;
|
||||
public string? Text;
|
||||
public ValidationError(string key, string? text = null)
|
||||
{
|
||||
Key = key;
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
|
||||
public class FieldTooLongError: ValidationError
|
||||
{
|
||||
public int MaxLength;
|
||||
public int ActualLength;
|
||||
|
||||
public FieldTooLongError(string key, int maxLength, int actualLength) : base(key)
|
||||
{
|
||||
MaxLength = maxLength;
|
||||
ActualLength = actualLength;
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ namespace PluralKit.Core
|
||||
|
||||
public new void AssertIsValid()
|
||||
{
|
||||
if (Name.IsPresent)
|
||||
if (Name.Value != null)
|
||||
AssertValid(Name.Value, "name", Limits.MaxGroupNameLength);
|
||||
if (DisplayName.Value != null)
|
||||
AssertValid(DisplayName.Value, "display_name", Limits.MaxGroupNameLength);
|
||||
@ -59,10 +59,13 @@ namespace PluralKit.Core
|
||||
{
|
||||
var patch = new GroupPatch();
|
||||
|
||||
if (o.ContainsKey("name") && o["name"].Type == JTokenType.Null)
|
||||
throw new ValidationError("Group name can not be set to null.");
|
||||
if (o.ContainsKey("name"))
|
||||
{
|
||||
patch.Name = o.Value<string>("name").NullIfEmpty();
|
||||
if (patch.Name.Value == null)
|
||||
patch.Errors.Add(new ValidationError("name", "Group name can not be set to null."));
|
||||
}
|
||||
|
||||
if (o.ContainsKey("name")) patch.Name = o.Value<string>("name");
|
||||
if (o.ContainsKey("display_name")) patch.DisplayName = o.Value<string>("display_name").NullIfEmpty();
|
||||
if (o.ContainsKey("description")) patch.Description = o.Value<string>("description").NullIfEmpty();
|
||||
if (o.ContainsKey("icon")) patch.Icon = o.Value<string>("icon").NullIfEmpty();
|
||||
@ -74,16 +77,16 @@ namespace PluralKit.Core
|
||||
var privacy = o.Value<JObject>("privacy");
|
||||
|
||||
if (privacy.ContainsKey("description_privacy"))
|
||||
patch.DescriptionPrivacy = privacy.ParsePrivacy("description_privacy");
|
||||
patch.DescriptionPrivacy = patch.ParsePrivacy(privacy, "description_privacy");
|
||||
|
||||
if (privacy.ContainsKey("icon_privacy"))
|
||||
patch.IconPrivacy = privacy.ParsePrivacy("icon_privacy");
|
||||
patch.IconPrivacy = patch.ParsePrivacy(privacy, "icon_privacy");
|
||||
|
||||
if (privacy.ContainsKey("list_privacy"))
|
||||
patch.ListPrivacy = privacy.ParsePrivacy("list_privacy");
|
||||
patch.ListPrivacy = patch.ParsePrivacy(privacy, "list_privacy");
|
||||
|
||||
if (privacy.ContainsKey("visibility"))
|
||||
patch.Visibility = privacy.ParsePrivacy("visibility");
|
||||
patch.Visibility = patch.ParsePrivacy(privacy, "visibility");
|
||||
}
|
||||
|
||||
return patch;
|
||||
|
@ -58,7 +58,7 @@ namespace PluralKit.Core
|
||||
|
||||
public new void AssertIsValid()
|
||||
{
|
||||
if (Name.IsPresent)
|
||||
if (Name.Value != null)
|
||||
AssertValid(Name.Value, "name", Limits.MaxMemberNameLength);
|
||||
if (DisplayName.Value != null)
|
||||
AssertValid(DisplayName.Value, "display_name", Limits.MaxMemberNameLength);
|
||||
@ -77,7 +77,7 @@ namespace PluralKit.Core
|
||||
if (ProxyTags.IsPresent && (ProxyTags.Value.Length > 100 ||
|
||||
ProxyTags.Value.Any(tag => tag.ProxyString.IsLongerThan(100))))
|
||||
// todo: have a better error for this
|
||||
throw new ValidationError("proxy_tags");
|
||||
Errors.Add(new ValidationError("proxy_tags"));
|
||||
}
|
||||
|
||||
#nullable disable
|
||||
@ -86,8 +86,12 @@ namespace PluralKit.Core
|
||||
{
|
||||
var patch = new MemberPatch();
|
||||
|
||||
if (o.ContainsKey("name") && o["name"].Type == JTokenType.Null)
|
||||
throw new ValidationError("Member name can not be set to null.");
|
||||
if (o.ContainsKey("name"))
|
||||
{
|
||||
patch.Name = o.Value<string>("name").NullIfEmpty();
|
||||
if (patch.Name.Value == null)
|
||||
patch.Errors.Add(new ValidationError("name", "Member name can not be set to null."));
|
||||
}
|
||||
|
||||
if (o.ContainsKey("name")) patch.Name = o.Value<string>("name");
|
||||
if (o.ContainsKey("color")) patch.Color = o.Value<string>("color").NullIfEmpty()?.ToLower();
|
||||
@ -101,7 +105,7 @@ namespace PluralKit.Core
|
||||
var res = DateTimeFormats.DateExportFormat.Parse(str);
|
||||
if (res.Success) patch.Birthday = res.Value;
|
||||
else if (str == null) patch.Birthday = null;
|
||||
else throw new ValidationError("birthday");
|
||||
else patch.Errors.Add(new ValidationError("birthday"));
|
||||
}
|
||||
|
||||
if (o.ContainsKey("pronouns")) patch.Pronouns = o.Value<string>("pronouns").NullIfEmpty();
|
||||
@ -123,7 +127,7 @@ namespace PluralKit.Core
|
||||
|
||||
if (o.ContainsKey("privacy"))
|
||||
{
|
||||
var plevel = o.ParsePrivacy("privacy");
|
||||
var plevel = patch.ParsePrivacy(o, "privacy");
|
||||
|
||||
patch.Visibility = plevel;
|
||||
patch.NamePrivacy = plevel;
|
||||
@ -136,14 +140,14 @@ namespace PluralKit.Core
|
||||
}
|
||||
else
|
||||
{
|
||||
if (o.ContainsKey("visibility")) patch.Visibility = o.ParsePrivacy("visibility");
|
||||
if (o.ContainsKey("name_privacy")) patch.NamePrivacy = o.ParsePrivacy("name_privacy");
|
||||
if (o.ContainsKey("description_privacy")) patch.DescriptionPrivacy = o.ParsePrivacy("description_privacy");
|
||||
if (o.ContainsKey("avatar_privacy")) patch.AvatarPrivacy = o.ParsePrivacy("avatar_privacy");
|
||||
if (o.ContainsKey("birthday_privacy")) patch.BirthdayPrivacy = o.ParsePrivacy("birthday_privacy");
|
||||
if (o.ContainsKey("pronoun_privacy")) patch.PronounPrivacy = o.ParsePrivacy("pronoun_privacy");
|
||||
if (o.ContainsKey("visibility")) patch.Visibility = patch.ParsePrivacy(o, "visibility");
|
||||
if (o.ContainsKey("name_privacy")) patch.NamePrivacy = patch.ParsePrivacy(o, "name_privacy");
|
||||
if (o.ContainsKey("description_privacy")) patch.DescriptionPrivacy = patch.ParsePrivacy(o, "description_privacy");
|
||||
if (o.ContainsKey("avatar_privacy")) patch.AvatarPrivacy = patch.ParsePrivacy(o, "avatar_privacy");
|
||||
if (o.ContainsKey("birthday_privacy")) patch.BirthdayPrivacy = patch.ParsePrivacy(o, "birthday_privacy");
|
||||
if (o.ContainsKey("pronoun_privacy")) patch.PronounPrivacy = patch.ParsePrivacy(o, "pronoun_privacy");
|
||||
// if (o.ContainsKey("color_privacy")) member.ColorPrivacy = o.ParsePrivacy("member");
|
||||
if (o.ContainsKey("metadata_privacy")) patch.MetadataPrivacy = o.ParsePrivacy("metadata_privacy");
|
||||
if (o.ContainsKey("metadata_privacy")) patch.MetadataPrivacy = patch.ParsePrivacy(o, "metadata_privacy");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -161,25 +165,25 @@ namespace PluralKit.Core
|
||||
var privacy = o.Value<JObject>("privacy");
|
||||
|
||||
if (privacy.ContainsKey("visibility"))
|
||||
patch.Visibility = privacy.ParsePrivacy("visibility");
|
||||
patch.Visibility = patch.ParsePrivacy(privacy, "visibility");
|
||||
|
||||
if (privacy.ContainsKey("name_privacy"))
|
||||
patch.NamePrivacy = privacy.ParsePrivacy("name_privacy");
|
||||
patch.NamePrivacy = patch.ParsePrivacy(privacy, "name_privacy");
|
||||
|
||||
if (privacy.ContainsKey("description_privacy"))
|
||||
patch.DescriptionPrivacy = privacy.ParsePrivacy("description_privacy");
|
||||
patch.DescriptionPrivacy = patch.ParsePrivacy(privacy, "description_privacy");
|
||||
|
||||
if (privacy.ContainsKey("avatar_privacy"))
|
||||
patch.AvatarPrivacy = privacy.ParsePrivacy("avatar_privacy");
|
||||
patch.AvatarPrivacy = patch.ParsePrivacy(privacy, "avatar_privacy");
|
||||
|
||||
if (privacy.ContainsKey("birthday_privacy"))
|
||||
patch.BirthdayPrivacy = privacy.ParsePrivacy("birthday_privacy");
|
||||
patch.BirthdayPrivacy = patch.ParsePrivacy(privacy, "birthday_privacy");
|
||||
|
||||
if (privacy.ContainsKey("pronoun_privacy"))
|
||||
patch.PronounPrivacy = privacy.ParsePrivacy("pronoun_privacy");
|
||||
patch.PronounPrivacy = patch.ParsePrivacy(privacy, "pronoun_privacy");
|
||||
|
||||
if (privacy.ContainsKey("metadata_privacy"))
|
||||
patch.MetadataPrivacy = privacy.ParsePrivacy("metadata_privacy");
|
||||
patch.MetadataPrivacy = patch.ParsePrivacy(privacy, "metadata_privacy");
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -1,50 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using SqlKata;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public abstract class PatchObject
|
||||
{
|
||||
public List<ValidationError> Errors = new();
|
||||
public abstract Query Apply(Query q);
|
||||
|
||||
public void AssertIsValid() { }
|
||||
|
||||
protected bool AssertValid(string input, string name, int maxLength, Func<string, bool>? validate = null)
|
||||
protected void AssertValid(string input, string name, int maxLength, Func<string, bool>? validate = null)
|
||||
{
|
||||
if (input.Length > maxLength)
|
||||
throw new FieldTooLongError(name, maxLength, input.Length);
|
||||
Errors.Add(new FieldTooLongError(name, maxLength, input.Length));
|
||||
if (validate != null && !validate(input))
|
||||
throw new ValidationError(name);
|
||||
return true;
|
||||
Errors.Add(new ValidationError(name));
|
||||
}
|
||||
|
||||
protected bool AssertValid(string input, string name, string pattern)
|
||||
protected void AssertValid(string input, string name, string pattern)
|
||||
{
|
||||
if (!Regex.IsMatch(input, pattern))
|
||||
throw new ValidationError(name);
|
||||
return true;
|
||||
Errors.Add(new ValidationError(name));
|
||||
}
|
||||
}
|
||||
|
||||
public class ValidationError: Exception
|
||||
{
|
||||
public ValidationError(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class FieldTooLongError: ValidationError
|
||||
{
|
||||
public string Name;
|
||||
public int MaxLength;
|
||||
public int ActualLength;
|
||||
|
||||
public FieldTooLongError(string name, int maxLength, int actualLength) :
|
||||
base($"{name} too long ({actualLength} > {maxLength})")
|
||||
public PrivacyLevel ParsePrivacy(JObject o, string propertyName)
|
||||
{
|
||||
Name = name;
|
||||
MaxLength = maxLength;
|
||||
ActualLength = actualLength;
|
||||
var input = o.Value<string>(propertyName);
|
||||
|
||||
if (input == null) return PrivacyLevel.Public;
|
||||
if (input == "") return PrivacyLevel.Private;
|
||||
if (input == "private") return PrivacyLevel.Private;
|
||||
if (input == "public") return PrivacyLevel.Public;
|
||||
|
||||
Errors.Add(new ValidationError(propertyName));
|
||||
// unused, but the compiler will complain if this isn't here
|
||||
return PrivacyLevel.Private;
|
||||
}
|
||||
}
|
||||
}
|
@ -36,8 +36,14 @@ namespace PluralKit.Core
|
||||
if (o.ContainsKey("proxying_enabled") && o["proxying_enabled"].Type != JTokenType.Null)
|
||||
patch.ProxyEnabled = o.Value<bool>("proxying_enabled");
|
||||
|
||||
if (o.ContainsKey("autoproxy_mode") && o["autoproxy_mode"].ParseAutoproxyMode() is { } autoproxyMode)
|
||||
patch.AutoproxyMode = autoproxyMode;
|
||||
if (o.ContainsKey("autoproxy_mode"))
|
||||
{
|
||||
var (val, err) = o["autoproxy_mode"].ParseAutoproxyMode();
|
||||
if (err != null)
|
||||
patch.Errors.Add(err);
|
||||
else
|
||||
patch.AutoproxyMode = val.Value;
|
||||
}
|
||||
|
||||
patch.AutoproxyMember = memberId;
|
||||
|
||||
|
@ -69,7 +69,7 @@ namespace PluralKit.Core
|
||||
if (Color.Value != null)
|
||||
AssertValid(Color.Value, "color", "^[0-9a-fA-F]{6}$");
|
||||
if (UiTz.IsPresent && DateTimeZoneProviders.Tzdb.GetZoneOrNull(UiTz.Value) == null)
|
||||
throw new ValidationError("avatar_url");
|
||||
Errors.Add(new ValidationError("timezone"));
|
||||
}
|
||||
|
||||
#nullable disable
|
||||
@ -91,10 +91,10 @@ namespace PluralKit.Core
|
||||
{
|
||||
if (o.ContainsKey("tz")) patch.UiTz = o.Value<string>("tz") ?? "UTC";
|
||||
|
||||
if (o.ContainsKey("description_privacy")) patch.DescriptionPrivacy = o.ParsePrivacy("description_privacy");
|
||||
if (o.ContainsKey("member_list_privacy")) patch.MemberListPrivacy = o.ParsePrivacy("member_list_privacy");
|
||||
if (o.ContainsKey("front_privacy")) patch.FrontPrivacy = o.ParsePrivacy("front_privacy");
|
||||
if (o.ContainsKey("front_history_privacy")) patch.FrontHistoryPrivacy = o.ParsePrivacy("front_history_privacy");
|
||||
if (o.ContainsKey("description_privacy")) patch.DescriptionPrivacy = patch.ParsePrivacy(o, "description_privacy");
|
||||
if (o.ContainsKey("member_list_privacy")) patch.MemberListPrivacy = patch.ParsePrivacy(o, "member_list_privacy");
|
||||
if (o.ContainsKey("front_privacy")) patch.FrontPrivacy = patch.ParsePrivacy(o, "front_privacy");
|
||||
if (o.ContainsKey("front_history_privacy")) patch.FrontHistoryPrivacy = patch.ParsePrivacy(o, "front_history_privacy");
|
||||
|
||||
break;
|
||||
}
|
||||
@ -105,16 +105,16 @@ namespace PluralKit.Core
|
||||
var privacy = o.Value<JObject>("privacy");
|
||||
|
||||
if (privacy.ContainsKey("description_privacy"))
|
||||
patch.DescriptionPrivacy = privacy.ParsePrivacy("description_privacy");
|
||||
patch.DescriptionPrivacy = patch.ParsePrivacy(privacy, "description_privacy");
|
||||
|
||||
if (privacy.ContainsKey("member_list_privacy"))
|
||||
patch.DescriptionPrivacy = privacy.ParsePrivacy("member_list_privacy");
|
||||
patch.DescriptionPrivacy = patch.ParsePrivacy(privacy, "member_list_privacy");
|
||||
|
||||
if (privacy.ContainsKey("front_privacy"))
|
||||
patch.DescriptionPrivacy = privacy.ParsePrivacy("front_privacy");
|
||||
patch.DescriptionPrivacy = patch.ParsePrivacy(privacy, "front_privacy");
|
||||
|
||||
if (privacy.ContainsKey("front_history_privacy"))
|
||||
patch.DescriptionPrivacy = privacy.ParsePrivacy("front_history_privacy");
|
||||
patch.DescriptionPrivacy = patch.ParsePrivacy(privacy, "front_history_privacy");
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -1,7 +1,5 @@
|
||||
using System;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public enum PrivacyLevel
|
||||
@ -42,18 +40,5 @@ namespace PluralKit.Core
|
||||
}
|
||||
|
||||
public static string ToJsonString(this PrivacyLevel level) => level.LevelName();
|
||||
|
||||
public static PrivacyLevel ParsePrivacy(this JObject o, string propertyName)
|
||||
{
|
||||
var input = o.Value<string>(propertyName);
|
||||
|
||||
if (input == null) return PrivacyLevel.Public;
|
||||
if (input == "") return PrivacyLevel.Private;
|
||||
if (input == "private") return PrivacyLevel.Private;
|
||||
if (input == "public") return PrivacyLevel.Public;
|
||||
|
||||
throw new ValidationError(propertyName);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -41,27 +41,27 @@ namespace PluralKit.Core
|
||||
return o;
|
||||
}
|
||||
|
||||
public static AutoproxyMode? ParseAutoproxyMode(this JToken o)
|
||||
public static (AutoproxyMode?, ValidationError?) ParseAutoproxyMode(this JToken o)
|
||||
{
|
||||
if (o.Type == JTokenType.Null)
|
||||
return AutoproxyMode.Off;
|
||||
return (AutoproxyMode.Off, null);
|
||||
else if (o.Type != JTokenType.String)
|
||||
return null;
|
||||
return (null, new ValidationError("autoproxy_mode"));
|
||||
|
||||
var value = o.Value<string>();
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case "off":
|
||||
return AutoproxyMode.Off;
|
||||
return (AutoproxyMode.Off, null);
|
||||
case "front":
|
||||
return AutoproxyMode.Front;
|
||||
return (AutoproxyMode.Front, null);
|
||||
case "latch":
|
||||
return AutoproxyMode.Latch;
|
||||
return (AutoproxyMode.Latch, null);
|
||||
case "member":
|
||||
return AutoproxyMode.Member;
|
||||
return (AutoproxyMode.Member, null);
|
||||
default:
|
||||
throw new ValidationError($"Value '{value}' is not a valid autoproxy mode.");
|
||||
return (null, new ValidationError("autoproxy_mode", $"Value '{value}' is not a valid autoproxy mode."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,13 +20,17 @@ namespace PluralKit.Core
|
||||
{
|
||||
var patch = SystemPatch.FromJSON(importFile);
|
||||
|
||||
try
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
patch.AssertIsValid();
|
||||
}
|
||||
catch (ValidationError e)
|
||||
{
|
||||
throw new ImportException($"Field {e.Message} in export file is invalid.");
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
throw new ImportException($"Field {err.Key} in export file is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
else if (err.Text != null)
|
||||
throw new ImportException(err.Text);
|
||||
else
|
||||
throw new ImportException($"Field {err.Key} in export file is invalid.");
|
||||
}
|
||||
|
||||
await _repo.UpdateSystem(_system.Id, patch, _conn);
|
||||
@ -87,17 +91,18 @@ namespace PluralKit.Core
|
||||
);
|
||||
|
||||
var patch = MemberPatch.FromJSON(member);
|
||||
try
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
patch.AssertIsValid();
|
||||
}
|
||||
catch (FieldTooLongError e)
|
||||
{
|
||||
throw new ImportException($"Field {e.Name} in member {referenceName} is too long ({e.ActualLength} > {e.MaxLength}).");
|
||||
}
|
||||
catch (ValidationError e)
|
||||
{
|
||||
throw new ImportException($"Field {e.Message} in member {referenceName} is invalid.");
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
throw new ImportException($"Field {err.Key} in member {name} is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
else if (err.Text != null)
|
||||
throw new ImportException($"member {name}: {err.Text}");
|
||||
else
|
||||
throw new ImportException($"Field {err.Key} in member {name} is invalid.");
|
||||
}
|
||||
|
||||
MemberId? memberId = found;
|
||||
@ -128,17 +133,18 @@ namespace PluralKit.Core
|
||||
);
|
||||
|
||||
var patch = GroupPatch.FromJson(group);
|
||||
try
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
patch.AssertIsValid();
|
||||
}
|
||||
catch (FieldTooLongError e)
|
||||
{
|
||||
throw new ImportException($"Field {e.Name} in group {referenceName} is too long ({e.ActualLength} > {e.MaxLength}).");
|
||||
}
|
||||
catch (ValidationError e)
|
||||
{
|
||||
throw new ImportException($"Field {e.Message} in group {referenceName} is invalid.");
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
throw new ImportException($"Field {err.Key} in group {name} is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
else if (err.Text != null)
|
||||
throw new ImportException($"group {name}: {err.Text}");
|
||||
else
|
||||
throw new ImportException($"Field {err.Key} in group {name} is invalid.");
|
||||
}
|
||||
|
||||
GroupId? groupId = found;
|
||||
|
@ -87,6 +87,19 @@ namespace PluralKit.Core
|
||||
patch.DisplayName = $"{name} {tag}";
|
||||
}
|
||||
|
||||
patch.AssertIsValid();
|
||||
if (patch.Errors.Count > 0)
|
||||
{
|
||||
var err = patch.Errors[0];
|
||||
if (err is FieldTooLongError)
|
||||
throw new ImportException($"Field {err.Key} in tupper {name} is too long "
|
||||
+ $"({(err as FieldTooLongError).ActualLength} > {(err as FieldTooLongError).MaxLength}).");
|
||||
else if (err.Text != null)
|
||||
throw new ImportException($"tupper {name}: {err.Text}");
|
||||
else
|
||||
throw new ImportException($"Field {err.Key} in tupper {name} is invalid.");
|
||||
}
|
||||
|
||||
var isNewMember = false;
|
||||
if (!_existingMemberNames.TryGetValue(name, out var memberId))
|
||||
{
|
||||
@ -101,19 +114,6 @@ namespace PluralKit.Core
|
||||
_logger.Debug("Importing member with identifier {FileId} to system {System} (is creating new member? {IsCreatingNewMember})",
|
||||
name, _system.Id, isNewMember);
|
||||
|
||||
try
|
||||
{
|
||||
patch.AssertIsValid();
|
||||
}
|
||||
catch (FieldTooLongError e)
|
||||
{
|
||||
throw new ImportException($"Field {e.Name} in tupper {name} is too long ({e.ActualLength} > {e.MaxLength}).");
|
||||
}
|
||||
catch (ValidationError e)
|
||||
{
|
||||
throw new ImportException($"Field {e.Message} in tupper {name} is invalid.");
|
||||
}
|
||||
|
||||
await _repo.UpdateMember(memberId, patch, _conn);
|
||||
|
||||
return (lastSetTag, multipleTags, hasGroup);
|
||||
|
Loading…
Reference in New Issue
Block a user