using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using SupportChild.Interviews; namespace SupportChild.Interviews; // This class is identical to the normal interview question and just exists as a hack to get JSON validation when // new entries are entered but not when read from database in order to be more lenient with old interviews. // I might do this in a more proper way at some point. public class ValidatedInterviewQuestion { [JsonProperty("title", Required = Required.Default)] public string title; [JsonProperty("message", Required = Required.Always)] public string message; [JsonConverter(typeof(StringEnumConverter))] [JsonProperty("type", Required = Required.Always)] public QuestionType type; [JsonProperty("color", Required = Required.Always)] public string color; [JsonProperty("summary-field", Required = Required.Default)] public string summaryField; [JsonConverter(typeof(StringEnumConverter))] [JsonProperty("button-style", Required = Required.Default)] public ButtonType? buttonStyle; [JsonProperty("selector-placeholder", Required = Required.Default)] public string selectorPlaceholder; [JsonProperty("selector-description", Required = Required.Default)] public string selectorDescription; [JsonProperty("max-length", Required = Required.Default)] public int? maxLength; [JsonProperty("min-length", Required = Required.Default)] public int? minLength; [JsonProperty("paths", Required = Required.Always)] public Dictionary paths; public void Validate(ref List errors, out int summaryCount, out int summaryMaxLength) { if (string.IsNullOrWhiteSpace(message)) { errors.Add("Message cannot be empty."); } if (type is QuestionType.ERROR or QuestionType.END_WITH_SUMMARY or QuestionType.END_WITHOUT_SUMMARY) { if (paths.Count > 0) { errors.Add("'" + type + "' questions cannot have child paths."); } } else if (paths.Count == 0) { errors.Add("'" + type + "' questions must have at least one child path."); } List summaryCounts = []; Dictionary childMaxLengths = new Dictionary(); foreach (KeyValuePair path in paths) { path.Value.Validate(ref errors, out int summaries, out int maxLen); summaryCounts.Add(summaries); childMaxLengths.Add(path.Key, maxLen); } summaryCount = summaryCounts.Count == 0 ? 0 : summaryCounts.Max(); string childPathString = ""; int childMaxLength = 0; if (childMaxLengths.Count != 0) { (childPathString, childMaxLength) = childMaxLengths.ToArray().MaxBy(x => x.Key.Length + x.Value); } summaryMaxLength = childMaxLength; if (string.IsNullOrWhiteSpace(summaryField)) { ++summaryCount; } // Only count summaries that end in a summary question. if (type == QuestionType.END_WITH_SUMMARY) { summaryMaxLength = message?.Length ?? 0; summaryMaxLength += title?.Length ?? 0; } // Only add to the total max length if the summary field is not empty. That way we know this branch ends in a summary. else if (summaryMaxLength > 0 && !string.IsNullOrEmpty(summaryField)) { summaryMaxLength += summaryField.Length; switch (type) { case QuestionType.BUTTONS: case QuestionType.TEXT_SELECTOR: summaryMaxLength += childPathString.Length; break; case QuestionType.USER_SELECTOR: case QuestionType.ROLE_SELECTOR: case QuestionType.MENTIONABLE_SELECTOR: case QuestionType.CHANNEL_SELECTOR: // Approximate length of a mention summaryMaxLength += 23; break; case QuestionType.TEXT_INPUT: summaryMaxLength += Math.Min(maxLength ?? 1024, 1024); break; case QuestionType.END_WITH_SUMMARY: case QuestionType.END_WITHOUT_SUMMARY: case QuestionType.ERROR: default: break; } } } } public class ValidatedTemplate(ulong categoryID, ValidatedInterviewQuestion interview) { [JsonProperty("category-id", Required = Required.Always)] public ulong categoryID = categoryID; [JsonProperty("interview", Required = Required.Always)] public ValidatedInterviewQuestion interview = interview; }