Skip to content

Commit

Permalink
imp - Enhanced validation code
Browse files Browse the repository at this point in the history
---

We've enhanced the contact card validation logic so that it's more dynamic than before.

---

Type: imp
Breaking: False
Doc Required: False
Backport Required: False
Part: 1/1
  • Loading branch information
AptiviCEO committed Sep 10, 2024
1 parent 23bb38a commit fe54710
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 30 deletions.
2 changes: 1 addition & 1 deletion VisualCard.Calendar/Parsers/VCalendarParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ internal void ValidateCalendar(Parts.Calendar calendar)
foreach (var eventInfo in calendar.events)
{
if (!ValidateComponent(ref expectedEventFields, out string[] actualEventFields, eventInfo))
throw new InvalidDataException($"The following keys [{string.Join(", ", expectedEventFields)}] are required in the root calendar. Got [{string.Join(", ", actualEventFields)}].");
throw new InvalidDataException($"The following keys [{string.Join(", ", expectedEventFields)}] are required in the event representation. Got [{string.Join(", ", actualEventFields)}].");
}
}

Expand Down
2 changes: 1 addition & 1 deletion VisualCard.Calendar/Parts/Calendar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public BaseCalendarPartInfo[] GetPartsArray(Type partType)
public BaseCalendarPartInfo[] GetPartsArray(Type partType, CalendarPartsArrayEnum key)
{
// Check the base type
if (partType.BaseType != typeof(BaseCalendarPartInfo))
if (partType.BaseType != typeof(BaseCalendarPartInfo) && partType != typeof(BaseCalendarPartInfo))
throw new InvalidOperationException($"Base type is not BaseCalendarPartInfo [{partType.BaseType.Name}] and the part type is [{partType.Name}] that doesn't represent calendar part.");

return GetPartsArray(partType, key, version, partsArray);
Expand Down
62 changes: 42 additions & 20 deletions VisualCard/Parsers/VcardParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,34 +253,56 @@ public Card Parse()
internal void ValidateCard(Card card)
{
// Track the required fields
List<string> expectedFields = [];
List<string> actualFields = [];
List<string> expectedFieldList = [];
var nameCardinality = VcardParserTools.GetPartsArrayEnumFromType(typeof(NameInfo), CardVersion).Item2;
var fullNameCardinality = VcardParserTools.GetPartsArrayEnumFromType(typeof(FullNameInfo), CardVersion).Item2;
if (nameCardinality == PartCardinality.ShouldBeOne)
expectedFields.Add(VcardConstants._nameSpecifier);
expectedFieldList.Add(VcardConstants._nameSpecifier);
if (fullNameCardinality == PartCardinality.AtLeastOne)
expectedFields.Add(VcardConstants._fullNameSpecifier);
expectedFieldList.Add(VcardConstants._fullNameSpecifier);

// Now, check for requirements
string[] expectedFields = [.. expectedFieldList];
if (!ValidateComponent(ref expectedFields, out string[] actualFields, card))
throw new InvalidDataException($"The following keys [{string.Join(", ", expectedFields)}] are required. Got [{string.Join(", ", actualFields)}].");
}

private bool ValidateComponent<TComponent>(ref string[] expectedFields, out string[] actualFields, TComponent component)
where TComponent : Card
{
// Track the required fields
List<string> actualFieldList = [];

// Requirement checks
if (expectedFields.Contains(VcardConstants._nameSpecifier))
{
var names = card.GetPartsArray<NameInfo>(PartsArrayEnum.Names);
bool exists = names is not null && names.Length > 0;
if (exists)
actualFields.Add(VcardConstants._nameSpecifier);
}
if (expectedFields.Contains(VcardConstants._fullNameSpecifier))
foreach (string expectedFieldName in expectedFields)
{
var fullNames = card.GetPartsArray<FullNameInfo>(PartsArrayEnum.FullName);
bool exists = fullNames is not null && fullNames.Length > 0;
if (exists)
actualFields.Add(VcardConstants._fullNameSpecifier);
var (type, enumeration, enumType, _, _, _, _) = VcardParserTools.GetPartType(expectedFieldName);
switch (type)
{
case PartType.Strings:
{
string value = component.GetString((StringsEnum)enumeration);
bool exists = !string.IsNullOrEmpty(value);
if (exists)
actualFieldList.Add(expectedFieldName);
}
break;
case PartType.PartsArray:
{
if (enumType is null)
continue;
var values = component.GetPartsArray(enumType, (PartsArrayEnum)enumeration);
bool exists = values.Length > 0;
if (exists)
actualFieldList.Add(expectedFieldName);
}
break;
}
}
expectedFields.Sort();
actualFields.Sort();
if (!actualFields.SequenceEqual(expectedFields))
throw new InvalidDataException($"The following keys [{string.Join(", ", expectedFields)}] are required. Got [{string.Join(", ", actualFields)}].");
Array.Sort(expectedFields);
actualFieldList.Sort();
actualFields = [.. actualFieldList];
return actualFields.SequenceEqual(expectedFields);
}

internal VcardParser(string[] cardContent, Version cardVersion)
Expand Down
48 changes: 40 additions & 8 deletions VisualCard/Parts/Card.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ public class Card : IEquatable<Card>
/// Gets a part array from a specified key
/// </summary>
/// <returns>An array of values or an empty part array []</returns>
public TPart[] GetPartsArray<TPart>() where TPart : BaseCardPartInfo
public TPart[] GetPartsArray<TPart>()
where TPart : BaseCardPartInfo
{
// Get the parts enumeration according to the type
var key = VcardParserTools.GetPartsArrayEnumFromType(typeof(TPart), CardVersion);
Expand All @@ -78,24 +79,55 @@ public TPart[] GetPartsArray<TPart>() where TPart : BaseCardPartInfo
/// </summary>
/// <param name="key">A key to use</param>
/// <returns>An array of values or an empty part array []</returns>
public TPart[] GetPartsArray<TPart>(PartsArrayEnum key) where TPart : BaseCardPartInfo
public TPart[] GetPartsArray<TPart>(PartsArrayEnum key)
where TPart : BaseCardPartInfo =>
GetPartsArray(typeof(TPart), key).Cast<TPart>().ToArray();

/// <summary>
/// Gets a part array from a specified key
/// </summary>
/// <param name="partType">Target part type that derives from <see cref="BaseCardPartInfo"/></param>
/// <returns>An array of values or an empty part array []</returns>
public BaseCardPartInfo[] GetPartsArray(Type partType)
{
// Check the base type
if (partType.BaseType != typeof(BaseCardPartInfo))
throw new InvalidOperationException($"Base type is not BaseCardPartInfo [{partType.BaseType.Name}] and the part type is [{partType.Name}] that doesn't represent card part.");

// Get the parts enumeration according to the type
var key = VcardParserTools.GetPartsArrayEnumFromType(partType, CardVersion);

// Now, return the value
return GetPartsArray(partType, key.Item1);
}

/// <summary>
/// Gets a part array from a specified key
/// </summary>
/// <param name="partType">Target part type that derives from <see cref="BaseCardPartInfo"/></param>
/// <param name="key">A key to use</param>
/// <returns>An array of values or an empty part array []</returns>
public BaseCardPartInfo[] GetPartsArray(Type partType, PartsArrayEnum key)
{
// Check the base type
if (partType.BaseType != typeof(BaseCardPartInfo) && partType != typeof(BaseCardPartInfo))
throw new InvalidOperationException($"Base type is not BaseCardPartInfo [{partType.BaseType.Name}] and the part type is [{partType.Name}] that doesn't represent card part.");

// Check for version support
if (!VcardParserTools.EnumArrayTypeSupported(key, CardVersion))
return [];

// Get the parts enumeration according to the type
var type = typeof(TPart);
if (type != typeof(BaseCardPartInfo))
if (partType != typeof(BaseCardPartInfo))
{
// We don't need the base, but a derivative of it. Check it.
var partsArrayEnum = VcardParserTools.GetPartsArrayEnumFromType(typeof(TPart), CardVersion).Item1;
var partsArrayEnum = VcardParserTools.GetPartsArrayEnumFromType(partType, CardVersion).Item1;
if (key != partsArrayEnum)
throw new InvalidOperationException($"Parts array enumeration [{key}] is different from the expected one [{partsArrayEnum}] according to type {typeof(TPart).Name}.");
throw new InvalidOperationException($"Parts array enumeration [{key}] is different from the expected one [{partsArrayEnum}] according to type {typeof(BaseCardPartInfo).Name}.");
}

// Get the fallback value
TPart[] fallback = [];
BaseCardPartInfo[] fallback = [];

// Check to see if the partarray has a value or not
bool hasValue = partsArray.ContainsKey(key);
Expand All @@ -104,7 +136,7 @@ public TPart[] GetPartsArray<TPart>(PartsArrayEnum key) where TPart : BaseCardPa

// Cast the values
var value = partsArray[key];
TPart[] parts = value.Cast<TPart>().ToArray();
BaseCardPartInfo[] parts = value.ToArray();

// Now, return the value
return parts;
Expand Down

0 comments on commit fe54710

Please sign in to comment.