Skip to content

Commit

Permalink
Implemented IConvertible to improve error reporting (#624)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Petrochuk (from Dev Box) <[email protected]>
  • Loading branch information
petrochuk and anpetroc authored Apr 8, 2024
1 parent baeea03 commit ecd502f
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/Persistence.Tests/MsApp/MsappArchiveTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void Msapp_ShouldHave_Screens(string testDirectory, int allEntriesCount,
msappArchive.CanonicalEntries.Count.Should().Be(allEntriesCount);
msappArchive.App.Should().NotBeNull();
msappArchive.App!.Screens.Count.Should().Be(controlsCount);
msappArchive.Version.Should().Be(Version.Parse("2.0"));
msappArchive.Version.Should().Be(Version.Parse("2.2"));

var screen = msappArchive.App.Screens.Single(c => c.Name == topLevelControlName);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"DocVersion":"1.336","MinVersionToLoad":"1.331","MSAppStructureVersion":"2.0","LastSavedDateTimeUTC":"01/20/2024 16:03:28","AnalysisOptions":{"DataflowAnalysisEnabled":false,"DataflowAnalysisFlagStateToggledByUser":false}}
{"DocVersion":"1.336","MinVersionToLoad":"1.331","MSAppStructureVersion":"2.2","LastSavedDateTimeUTC":"01/20/2024 16:03:28","AnalysisOptions":{"DataflowAnalysisEnabled":false,"DataflowAnalysisFlagStateToggledByUser":false}}
2 changes: 1 addition & 1 deletion src/Persistence/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static void AddPowerAppsPersistence(this IServiceCollection services, boo
private static void AddMinimalTemplates(ControlTemplateStore store)
{
store.Add(new() { Name = "hostControl", DisplayName = "host", Id = "http://microsoft.com/appmagic/hostcontrol" });
store.Add(new() { Name = "appInfo", DisplayName = "app", Id = "http://microsoft.com/appmagic/appinfo" });
store.Add(new() { Name = "appinfo", DisplayName = "app", Id = "http://microsoft.com/appmagic/appinfo" });
store.Add(new() { Name = "screen", Id = "http://microsoft.com/appmagic/screen" });
store.Add(new() { Name = "component", Id = "http://microsoft.com/appmagic/Component" });
store.Add(new() { Name = "group", Id = "http://microsoft.com/appmagic/group" });
Expand Down
91 changes: 90 additions & 1 deletion src/Persistence/Models/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Microsoft.PowerPlatform.PowerApps.Persistence.Models;
/// </summary>
[FirstClass(templateName: BuiltInTemplates.App)]
[YamlSerializable]
public record App : Control
public record App : Control, IConvertible
{
public App()
{
Expand Down Expand Up @@ -49,4 +49,93 @@ internal override void AfterCreate(Dictionary<string, object?> controlDefinition
Screens = new List<Screen>();
}
}

#region IConvertible

TypeCode IConvertible.GetTypeCode()
{
return TypeCode.Object;
}

bool IConvertible.ToBoolean(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(bool)}");
}

byte IConvertible.ToByte(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(byte)}");
}

char IConvertible.ToChar(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(char)}");
}

DateTime IConvertible.ToDateTime(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(DateTime)}");
}

decimal IConvertible.ToDecimal(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(decimal)}");
}

double IConvertible.ToDouble(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(double)}");
}

short IConvertible.ToInt16(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(short)}");
}

int IConvertible.ToInt32(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(int)}");
}

long IConvertible.ToInt64(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(long)}");
}

sbyte IConvertible.ToSByte(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(sbyte)}");
}

float IConvertible.ToSingle(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(float)}");
}

string IConvertible.ToString(IFormatProvider? provider)
{
return Name;
}

object IConvertible.ToType(Type conversionType, IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {conversionType.Name}");
}

ushort IConvertible.ToUInt16(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(ushort)}");
}

uint IConvertible.ToUInt32(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(uint)}");
}

ulong IConvertible.ToUInt64(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(App)} to {typeof(ulong)}");
}

#endregion
}
91 changes: 90 additions & 1 deletion src/Persistence/Models/Component.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Microsoft.PowerPlatform.PowerApps.Persistence.Models;
/// </summary>
[FirstClass(templateName: BuiltInTemplates.Component)]
[YamlSerializable]
public record Component : Control
public record Component : Control, IConvertible
{
protected Component() { }

Expand Down Expand Up @@ -58,4 +58,93 @@ internal override void AfterCreate(Dictionary<string, object?> controlDefinition
CustomProperties = customProperties;
}
}

#region IConvertible

public TypeCode GetTypeCode()
{
return TypeCode.Object;
}

public bool ToBoolean(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(bool)}");
}

public byte ToByte(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(byte)}");
}

public char ToChar(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(char)}");
}

public DateTime ToDateTime(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(DateTime)}");
}

public decimal ToDecimal(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(decimal)}");
}

public double ToDouble(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(double)}");
}

public short ToInt16(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(short)}");
}

public int ToInt32(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(int)}");
}

public long ToInt64(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(long)}");
}

public sbyte ToSByte(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(sbyte)}");
}

public float ToSingle(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(float)}");
}

public string ToString(IFormatProvider? provider)
{
return Name;
}

public object ToType(Type conversionType, IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {conversionType.Name}");
}

public ushort ToUInt16(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(ushort)}");
}

public uint ToUInt32(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(uint)}");
}

public ulong ToUInt64(IFormatProvider? provider)
{
throw new NotSupportedException($"Cannot covert {typeof(Component)} to {typeof(ulong)}");
}

#endregion
}
22 changes: 15 additions & 7 deletions src/Persistence/MsApp/MsappArchive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -431,11 +431,19 @@ public static string NormalizePath(string path)

private App? LoadApp()
{
// For app entry name is always "App.pa.yaml" now
var appEntry = GetEntry(Path.Combine(Directories.Src, AppFileName));
if (appEntry == null)
return null;
var app = Deserialize<App>(appEntry.FullName);
App app;
try
{
// For app entry name is always "App.pa.yaml" now
var appEntry = GetEntry(Path.Combine(Directories.Src, AppFileName));
if (appEntry == null)
return null;
app = Deserialize<App>(appEntry.FullName, ensureRoundTrip: false);
}
catch (Exception ex)
{
throw new PersistenceException("Failed to deserialize app.", ex) { FileName = AppFileName };
}

app.Screens = LoadScreens();

Expand All @@ -447,13 +455,13 @@ private List<Screen> LoadScreens()
_logger?.LogInformation("Loading top level screens from Yaml.");

var screens = new Dictionary<string, Screen>();
foreach (var yamlEntry in GetDirectoryEntries(Directories.Src, YamlFileExtension))
foreach (var yamlEntry in GetDirectoryEntries(Directories.Src, YamlFileExtension, recursive: false))
{
// Skip the app file
if (yamlEntry.FullName.EndsWith(AppFileName, StringComparison.OrdinalIgnoreCase))
continue;

var screen = Deserialize<Screen>(yamlEntry.FullName);
var screen = Deserialize<Screen>(yamlEntry.FullName, ensureRoundTrip: false);
screens.Add(screen.Name, screen);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Persistence/Templates/BuiltInTemplates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Microsoft.PowerPlatform.PowerApps.Persistence.Templates;

public static class BuiltInTemplates
{
public const string App = "AppInfo";
public const string App = "Appinfo";
public const string Host = "HostControl";
public const string Screen = "Screen";
public const string Component = "Component";
Expand Down
14 changes: 0 additions & 14 deletions src/Persistence/Yaml/ControlConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,20 +158,6 @@ public void ReadControlDefinitonHeader(IParser parser, out string controlName, o
return ValueDeserializer!.DeserializeValue(parser, typeof(object), serializerState, ValueDeserializer);
}

/*
else if (parser.Current is Scalar)
{
value = parser.Consume<Scalar>().Value;
if (!Options.IsControlIdentifiers)
{
if (key.Value == nameof(Control.Name))
controlName = (string)value;
}
}
else
throw new YamlException(parser.Current!.Start, parser.Current.End, $"Expected scalar value for control property '{key.Value}' in control '{controlName}'");
*/

return null;
}

Expand Down

0 comments on commit ecd502f

Please sign in to comment.