Skip to content

Commit

Permalink
Merge branch 'master' into users/joemay/PaYamlV3-updates-and-fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
joem-msft authored Jun 25, 2024
2 parents 3bc61fc + c57c3d4 commit 197999d
Show file tree
Hide file tree
Showing 31 changed files with 588 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .version/PipelineAssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
using System.Reflection;
[assembly: AssemblyVersion("0.0.0.0")]
[assembly: AssemblyFileVersion("0.0.0.0")]
[assembly: AssemblyInformationalVersion("0.0.0.0-dev-00000000")]
[assembly: AssemblyInformationalVersion("0.0.0.0-dev-00000000")]
2 changes: 1 addition & 1 deletion docs/pa.yaml-schema.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://raw.githubusercontent.com/microsoft/PowerApps-Tooling/master/docs/pa.yaml-schema.json",
"$id": "pa.yaml-schema.json",
"title": "Microsoft Power Apps",
"description": "Canvas YAML",
"oneOf": [
Expand Down
2 changes: 1 addition & 1 deletion docs/subschemas/control-property-schema.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://raw.githubusercontent.com/microsoft/PowerApps-Tooling/master/docs/control-type-schema.json",
"$id": "control-property-schema.json",
"title": "Microsoft Power Apps Properties",
"description": "The properties of the control",
"type": "object",
Expand Down
2 changes: 1 addition & 1 deletion docs/subschemas/control-type-schema.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://raw.githubusercontent.com/microsoft/PowerApps-Tooling/master/docs/control-type-schema.json",
"$id": "control-type-schema.json",
"title": "Microsoft Power Apps Control Type",
"description": "The type of the control",
"type": "string",
Expand Down
13 changes: 13 additions & 0 deletions src/PASopa.sln
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerPlatform.Pow
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Persistence.Tests", "Persistence.Tests\Persistence.Tests.csproj", "{8AB1C901-FE5E-44BF-AA21-B8F20A9D7CDD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YamlValidator", "YamlValidator\YamlValidator.csproj", "{F0AD11CE-E634-4945-A6B1-7866CDE0059C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YamlValidator.Tests", "YamlValidator.Tests\YamlValidator.Tests.csproj", "{8BA5DD4B-9423-4827-AF37-540E0300DB9A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7361DB16-D534-4E0E-8597-BE22317DEF47}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
Expand Down Expand Up @@ -52,13 +56,22 @@ Global
{8AB1C901-FE5E-44BF-AA21-B8F20A9D7CDD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AB1C901-FE5E-44BF-AA21-B8F20A9D7CDD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8AB1C901-FE5E-44BF-AA21-B8F20A9D7CDD}.Release|Any CPU.Build.0 = Release|Any CPU
{F0AD11CE-E634-4945-A6B1-7866CDE0059C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F0AD11CE-E634-4945-A6B1-7866CDE0059C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F0AD11CE-E634-4945-A6B1-7866CDE0059C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F0AD11CE-E634-4945-A6B1-7866CDE0059C}.Release|Any CPU.Build.0 = Release|Any CPU
{8BA5DD4B-9423-4827-AF37-540E0300DB9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8BA5DD4B-9423-4827-AF37-540E0300DB9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8BA5DD4B-9423-4827-AF37-540E0300DB9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8BA5DD4B-9423-4827-AF37-540E0300DB9A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8AD94CC0-7330-4880-A8E0-177B37CDB27B} = {4993E606-484B-46D9-892E-7AE9CE8D4423}
{8AB1C901-FE5E-44BF-AA21-B8F20A9D7CDD} = {4993E606-484B-46D9-892E-7AE9CE8D4423}
{8BA5DD4B-9423-4827-AF37-540E0300DB9A} = {4993E606-484B-46D9-892E-7AE9CE8D4423}
{7361DB16-D534-4E0E-8597-BE22317DEF47} = {794D8C68-BF6F-49C8-BCA5-AA52D8F45EF4}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
Expand Down
46 changes: 46 additions & 0 deletions src/YamlValidator.Tests/ValidatorTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Json.Schema;
using Microsoft.PowerPlatform.PowerApps.Persistence.YamlValidator;

namespace Persistence.Tests.YamlValidator;

[TestClass]
public class ValidatorTest
{
private const string _validPath = @".\_TestData\ValidYaml";
private const string _invalidPath = @".\_TestData\InvalidYaml";
private const string _schemaPath = @"..\YamlValidator\schema\pa.yaml-schema.json";

private readonly JsonSchema _schema;
private readonly Validator _yamlValidator;

public ValidatorTest()
{
var schemaFileLoader = new SchemaLoader();
_schema = schemaFileLoader.Load(_schemaPath);
var resultVerbosity = new VerbosityData(Constants.Verbose);
_yamlValidator = new Validator(resultVerbosity.EvalOptions, resultVerbosity.JsonOutputOptions);
}

[TestMethod]
[DataRow($@"{_invalidPath}\ScreenWithNameNoColon.yaml", false)]
[DataRow($@"{_invalidPath}\ScreenWithNameNoValue.yaml", false)]
[DataRow($@"{_invalidPath}\ScreenWithoutControlProperty.yaml", false)]
[DataRow($@"{_invalidPath}\WrongControlDefinition.yaml", false)]
[DataRow($@"{_invalidPath}\ControlWithInvalidProperty.yaml", false)]
[DataRow($@"{_invalidPath}\EmptyArray.yaml", false)]
[DataRow($@"{_invalidPath}\Empty.yaml", false)]
[DataRow($@"{_invalidPath}\NamelessObjectNoControl.yaml", false)]
[DataRow($@"{_validPath}\NamelessObjectWithControl.yaml", true)]
[DataRow($@"{_validPath}\ValidScreen1.yaml", true)]
[DataRow($@"{_validPath}\SimpleNoRecursiveDefinition.yaml", true)]

public void TestValidation(string filepath, bool expectedResult)
{
var rawYaml = Utility.ReadFileData($@"{filepath}");
var result = _yamlValidator.Validate(_schema, rawYaml);
Assert.IsTrue(result.SchemaValid == expectedResult);
}
}
34 changes: 34 additions & 0 deletions src/YamlValidator.Tests/YamlValidator.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<None Include="_TestData\**\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0-preview.5.24306.7" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\YamlValidator\YamlValidator.csproj" />
</ItemGroup>


<ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Screen2:
Control: Screen
InvalidProperty: "invalid"
Empty file.
4 changes: 4 additions & 0 deletions src/YamlValidator.Tests/_TestData/InvalidYaml/EmptyArray.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-
-
-
-
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test:
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Screen:
ComponentName: "test"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
TestScreen:
"abcd"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:
Control: "bdbd"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
LoginPage:
Control: "Button"
30 changes: 30 additions & 0 deletions src/YamlValidator.Tests/_TestData/ValidYaml/ValidScreen1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Screen2:
Control: Screen
Children:
- ButtonCanvas2:
Control: Button
Properties:
OnSelect: =Navigate(Screen1)
Text: ="Back"
Height: =53
Width: =172
X: =632
Y: =550
- TextCanvas1:
Control: Text
Properties:
Align: ='TextCanvas.Align'.Center
Size: =50
Text: ="Hello"
Height: =91
Width: =368
X: =517
Y: =44
- Image1:
Control: Image
Properties:
Image: ='pexels-pixabay-417173'
Height: =361
Width: =466
X: =447
Y: =135
14 changes: 14 additions & 0 deletions src/YamlValidator/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.PowerPlatform.PowerApps.Persistence.YamlValidator;
public class Constants
{
public const string FileTypeName = "file";
public const string FolderTypeName = "folder";
public const string YamlFileExtension = ".yaml";
public const string YmlFileExtension = ".yml";
public const string JsonFileExtension = ".json";

public const string Verbose = "verbose";
}
109 changes: 109 additions & 0 deletions src/YamlValidator/InputProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.CommandLine;

namespace Microsoft.PowerPlatform.PowerApps.Persistence.YamlValidator;
public class InputProcessor
{

private static void ProcessFiles(string path, string schema, string pathType)
{
// read only records
var filePathInfo = new ValidationRequest(path, schema, pathType);
var verbosityInfo = new VerbosityData(Constants.Verbose);

var validator = new Validator(verbosityInfo.EvalOptions, verbosityInfo.JsonOutputOptions);
var schemaLoader = new SchemaLoader();
var fileLoader = new YamlLoader();
var orchestrator = new Orchestrator(fileLoader, schemaLoader, validator);
orchestrator.RunValidation(filePathInfo);
}
public static RootCommand GetRootCommand()
{

var pathOption = new Option<string>(
name: "--path",
description: "The path to the input yaml file or directory of yaml files"
)
{ IsRequired = true };

pathOption.AddValidator(result =>
{
var inputFilePath = result.GetValueForOption(pathOption);
// either file or folder must be passed
var pathType = string.Empty;
if (string.IsNullOrEmpty(inputFilePath))
{
result.ErrorMessage = "The input is invalid, input must be a filepath to a yaml file \\" +
"or a folder path to a folder of yaml files";
}
else if (!Directory.Exists(inputFilePath) && !File.Exists(inputFilePath))
{
result.ErrorMessage = "The input path does not exist";
}
else if (Directory.Exists(inputFilePath))
{
if (Directory.GetFiles(inputFilePath, $"*{Constants.YamlFileExtension}").Length == 0)
{
result.ErrorMessage = "The input folder does not contain any yaml files";
}
}
else if (File.Exists(inputFilePath))
{
if (Path.GetExtension(inputFilePath) != Constants.YamlFileExtension)
{
result.ErrorMessage = "The input file must be a yaml file";
}
}
});

// assume local schema file exists in nuget package, use relative filepath for now
var schemaOption = new Option<string>(
name: "--schema",
description: "The path to the schema json file",
getDefaultValue: () => @".\schema\pa.yaml-schema.json"
);

schemaOption.AddValidator(result =>
{
var schemaPath = result.GetValueForOption(schemaOption);
if (string.IsNullOrEmpty(schemaPath))
{
result.ErrorMessage = "Schema option selected, but no schema was provided";
}
else if (Path.GetExtension(schemaPath) != Constants.JsonFileExtension)
{
result.ErrorMessage = "The schema file must be a json file";
}
else if (!File.Exists(schemaPath))
{
result.ErrorMessage = "The schema file does not exist";
}
});

// define root
var rootCommand = new RootCommand("YAML validator cli-tool");

// validate command
var validateCommand = new Command("validate", "Validate the input yaml file")
{
pathOption,
schemaOption
};

validateCommand.SetHandler((pathOptionVal, schemaOptionVal) =>
{
var pathType = File.GetAttributes(pathOptionVal).HasFlag(FileAttributes.Directory) ? Constants.FolderTypeName :
Constants.FileTypeName;
ProcessFiles(pathOptionVal, schemaOptionVal, pathType);
}, pathOption, schemaOption);

rootCommand.AddCommand(validateCommand);

return rootCommand;

}
}
42 changes: 42 additions & 0 deletions src/YamlValidator/Orchestrator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.PowerPlatform.PowerApps.Persistence.YamlValidator;
public class Orchestrator
{
private readonly YamlLoader _fileLoader;
private readonly SchemaLoader _schemaLoader;
private readonly Validator _validator;

public Orchestrator(YamlLoader fileLoader, SchemaLoader schemaLoader, Validator validator)
{
_fileLoader = fileLoader;
_schemaLoader = schemaLoader;
_validator = validator;
}

public void RunValidation(ValidationRequest inputData)
{
var schemaPath = inputData.SchemaPath;
var path = inputData.FilePath;
var pathType = inputData.FilePathType;

var yamlData = _fileLoader.Load(path, pathType);
var serializedSchema = _schemaLoader.Load(schemaPath);

foreach (var yamlFileData in yamlData)
{
Console.WriteLine($"Validation for {yamlFileData.Key}");
var result = _validator.Validate(serializedSchema, yamlFileData.Value);
Console.WriteLine($"Validation Result: {result.SchemaValid}");
foreach (var error in result.TraversalResults)
{
Console.WriteLine($"{error}");
}
Console.WriteLine();
}
}



}
15 changes: 15 additions & 0 deletions src/YamlValidator/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.CommandLine;

namespace Microsoft.PowerPlatform.PowerApps.Persistence.YamlValidator;

public class Program
{
private static void Main(string[] args)
{
var inputProcessor = InputProcessor.GetRootCommand();
inputProcessor.Invoke(args);
}
}
Loading

0 comments on commit 197999d

Please sign in to comment.