diff --git a/Forge.TreeWalker.UnitTests/test/TreeSchemaValidatorTests.cs b/Forge.TreeWalker.UnitTests/test/TreeSchemaValidatorTests.cs
index bc93a59..acb818d 100644
--- a/Forge.TreeWalker.UnitTests/test/TreeSchemaValidatorTests.cs
+++ b/Forge.TreeWalker.UnitTests/test/TreeSchemaValidatorTests.cs
@@ -3,7 +3,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
-// Tests the TreeSchemaValidator class.
+// Tests for the TreeSchemaValidator class.
//
//-----------------------------------------------------------------------
@@ -18,7 +18,6 @@ namespace Microsoft.Forge.TreeWalker.UnitTests
using Newtonsoft.Json;
using Newtonsoft.Json.Schema;
using Microsoft.Forge.TreeWalker;
- using System.Threading.Tasks;
[TestClass]
public class TreeSchemaValidatorTests
@@ -53,59 +52,67 @@ namespace Microsoft.Forge.TreeWalker.UnitTests
invalidSchemaWithErrorContent = File.ReadAllText(Path.Combine(Environment.CurrentDirectory, "test\\InvalidTestSchemas\\InvalidTestSchemaErrorContent.json"));
invalidSchemaDirectoryPath = "test\\ExampleSchemas\\TardigradeSchema.json";
}
+
[TestMethod]
public void Test_ValidateSchemaInForgeTreeWithCustomRulesInString()
{
- var res = ForgeSchemaValidator.ValidateSchema(treeSchema, stringRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateSchema(treeSchema, stringRules, out List errorList);
Assert.AreEqual(true, res);
Assert.AreEqual(0, errorList.Count);
}
+
[TestMethod]
public void Test_ValidateSchemaInForgeTreeWithCustomRulesInJSchema()
{
- var res = ForgeSchemaValidator.ValidateSchema(treeSchema, jschemaRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateSchema(treeSchema, jschemaRules, out List errorList);
Assert.AreEqual(true, res);
Assert.AreEqual(0, errorList.Count);
}
+
[TestMethod]
public void Test_ValidateSchemaInForgeTreeListWithCustomRulesInString()
{
- var res = ForgeSchemaValidator.ValidateSchema(treeSchema, stringRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateSchema(treeSchema, stringRules, out List errorList);
Assert.AreEqual(true, res);
Assert.AreEqual(0, errorList.Count);
}
+
[TestMethod]
public void Test_ValidateSchemaInForgeTreeListWithCustomRulesInJSchema()
{
- var res = ForgeSchemaValidator.ValidateSchema(treeSchema, jschemaRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateSchema(treeSchema, jschemaRules, out List errorList);
Assert.AreEqual(true, res);
Assert.AreEqual(0, errorList.Count);
}
+
[TestMethod]
- public void Test_ValidateSchemaInStringWithCustomRulesInString()
+ public void Test_ValidateSchemaInStringWithCustomRules()
{
- var res = ForgeSchemaValidator.ValidateSchemaString(stringSchema, stringRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateSchemaString(stringSchema, stringRules, false, out List errorList);
Assert.AreEqual(true, res);
Assert.AreEqual(0, errorList.Count);
}
+
[TestMethod]
public void Test_ValidateSchemaInStringWithCustomRulesInJSchema()
{
- var res = ForgeSchemaValidator.ValidateSchemaString(stringSchema, jschemaRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateSchemaString(stringSchema, jschemaRules, false, out List errorList);
Assert.AreEqual(true, res);
Assert.AreEqual(0, errorList.Count);
}
+
[TestMethod]
public void Test_ValidateSchemasInStringListWithCustomRulesInString()
{
- var res = ForgeSchemaValidator.ValidateSchemaString(stringSchemaList, stringRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateSchemaString(stringSchemaList, stringRules, false, out List errorList);
Assert.AreEqual(true, res);
Assert.AreEqual(0, errorList.Count);
}
+
[TestMethod]
public void Test_ValidateSchemasInStringListWithCustomRulesInJSchema()
{
- var res = ForgeSchemaValidator.ValidateSchemaString(stringSchemaList, jschemaRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateSchemaString(stringSchemaList, jschemaRules, false, out List errorList);
Assert.AreEqual(true, res);
Assert.AreEqual(0, errorList.Count);
}
@@ -113,59 +120,96 @@ namespace Microsoft.Forge.TreeWalker.UnitTests
[TestMethod]
public void Test_ValidateSchemaFromPathWithCustomRulesInString()
{
- var res = ForgeSchemaValidator.ValidateSchemaInPath(schemaPath, stringRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateSchemaInPath(schemaPath, stringRules, false, out List errorList);
Assert.AreEqual(true, res);
Assert.AreEqual(0, errorList.Count);
}
+
[TestMethod]
public void Test_ValidateSchemaFromPathWithCustomRulesInJSchema()
{
- var res = ForgeSchemaValidator.ValidateSchemaInPath(schemaPath, jschemaRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateSchemaInPath(schemaPath, jschemaRules, false, out List errorList);
Assert.AreEqual(true, res);
Assert.AreEqual(0, errorList.Count);
}
+
[TestMethod]
public void Test_ValidateSchemasFromDirectoryListWithCustomRulesInString()
{
- var res = ForgeSchemaValidator.ValidateMultipleSchemasInPath(schemaDirectoryPath, stringRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateMultipleSchemasInPath(schemaDirectoryPath, stringRules, false, out List errorList);
Assert.AreEqual(true, res);
Assert.AreEqual(0, errorList.Count);
}
+
[TestMethod]
public void Test_ValidateSchemasFromDirectoryListWithCustomRulesInJSchema()
{
- var res = ForgeSchemaValidator.ValidateMultipleSchemasInPath(schemaDirectoryPath, jschemaRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateMultipleSchemasInPath(schemaDirectoryPath, jschemaRules, false, out List errorList);
Assert.AreEqual(true, res);
Assert.AreEqual(0, errorList.Count);
}
+
[TestMethod]
public void Test_ValidateInvalidSchemaInString()
{
- var res = ForgeSchemaValidator.ValidateSchemaString(invalidSchemaNotTree, jschemaRules, out List errorList);
- Assert.AreEqual(false, res);
- Assert.AreEqual("Required property 'Type' not found in JSON. Path 'Tree.Root', line 5, position 9.", errorList.First());
+ bool res = ForgeSchemaValidator.ValidateSchemaString(stringSchema, jschemaRules, false, out List errorList);
+ Assert.AreEqual(true, res);
+ Assert.AreEqual(0, errorList.Count);
}
+
[TestMethod]
public void Test_ValidateInvalidSchemaDirectoryPath()
{
- var res = ForgeSchemaValidator.ValidateMultipleSchemasInPath(invalidSchemaDirectoryPath, jschemaRules,out List errorList);
+ bool res = ForgeSchemaValidator.ValidateMultipleSchemasInPath(invalidSchemaDirectoryPath, jschemaRules, false, out List errorList);
Assert.AreEqual(false, res);
Assert.AreEqual("The directory name is invalid.\r\n", errorList.First());
}
+
[TestMethod]
public void Test_ValidateSchemaWithErrorContent()
{
- var res = ForgeSchemaValidator.ValidateSchemaString(invalidSchemaWithErrorContent, jschemaRules, out List errorList);
+ bool res = ForgeSchemaValidator.ValidateSchemaString(invalidSchemaWithErrorContent, jschemaRules, false, out List errorList);
Assert.AreEqual(false, res);
Assert.AreEqual("JSON is valid against no schemas from 'oneOf'. line: 3, position: 17", errorList.First());
}
+
+ [TestMethod]
+ public void Test_ValidateInvalidSchemaAsDictionary()
+ {
+ bool res = ForgeSchemaValidator.ValidateSchemaString(invalidSchemaWithErrorContent, jschemaRules, true, out List errorList);
+ Assert.AreEqual(false, res);
+ Assert.AreEqual("JSON is valid against no schemas from 'oneOf'. line: 3, position: 17", errorList.First());
+ }
+
+ [TestMethod]
+ public void Test_ValidateValidSchemaAsDictionary()
+ {
+ bool res = ForgeSchemaValidator.ValidateSchemaString(invalidSchemaWithErrorContent, jschemaRules, true, out List errorList);
+ Assert.AreEqual(false, res);
+ Assert.AreEqual("JSON is valid against no schemas from 'oneOf'. line: 3, position: 17", errorList.First());
+ }
+
+ [TestMethod]
+ public void Test_ValidateSchemasFromDirectoryListAsDictionaryWithCustomRulesInString()
+ {
+ bool res = ForgeSchemaValidator.ValidateMultipleSchemasInPath(schemaDirectoryPath, stringRules, true, out List errorList);
+ Assert.AreEqual(false, res);
+ Assert.AreEqual("An item with the same key has already been added.", errorList.First());
+ }
+
[TestMethod]
public void Test_GetLinkedJSchemaRules()
{
- var linkedRules = ForgeSchemaValidator.GetLinkedJSchemaRules(stringRules, stringRules, "//ForgeSchemaValidationRules.json", out string error);
- Assert.AreEqual("", error);
- ForgeSchemaValidator.ValidateSchema(treeSchema, linkedRules, out List errorList);
- Assert.AreEqual(0, errorList.Count);
+ try
+ {
+ JSchema linkedRules = ForgeSchemaValidator.GetLinkedJSchemaRules(stringRules, stringRules, "//ForgeSchemaValidationRules.json");
+ ForgeSchemaValidator.ValidateSchema(treeSchema, linkedRules, out List errorList);
+ Assert.AreEqual(0, errorList.Count);
+ }
+ catch (Exception ex)
+ {
+ Assert.Fail("Expected no exception, but got: " + ex.Message);
+ }
}
}
}
diff --git a/Forge.TreeWalker/src/ForgeSchemaValidator.cs b/Forge.TreeWalker/src/ForgeSchemaValidator.cs
index 7211cf9..5673ba0 100644
--- a/Forge.TreeWalker/src/ForgeSchemaValidator.cs
+++ b/Forge.TreeWalker/src/ForgeSchemaValidator.cs
@@ -3,200 +3,170 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
-// The ForgeSchemaValidator class implements the ITreeSchemaValidator interface.
+// The ForgeSchemaValidator class.
//
//-----------------------------------------------------------------------
+
namespace Microsoft.Forge.TreeWalker
{
using System;
using System.Collections.Generic;
using System.IO;
- using System.Threading.Tasks;
using Microsoft.Forge.DataContracts;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
+
///
- /// The ForgeSchemaValidator class implements the validation method that tests input schemas with custom rules from input.
+ /// The ForgeSchemaValidator class implements the validation method that tests input schemas with input custom rules.
///
public static class ForgeSchemaValidator
{
+
///
- /// The GetLinkedJSchema method that creates the linked rules in JSchema.
+ /// Linked rules in JSchema type.
///
/// The rules to be included in the parent rules
/// The parent rules to absorb the child rules
/// The address of childRules
- /// The result of schema combination. ErrorMessage would be set if it throw exceptions
- public static JSchema GetLinkedJSchemaRules(string childRules, string parentRules, string referenceUri, out string errorMessage)
+ /// The result of schema combination.
+ public static JSchema GetLinkedJSchemaRules(string childRules, string parentRules, string referenceUri)
{
- try
- {
- JSchemaPreloadedResolver resolver = new JSchemaPreloadedResolver();
- resolver.Add(new Uri(referenceUri), childRules);
- errorMessage = "";
- return JSchema.Parse(parentRules, resolver);
- }
- catch(Exception e)
- {
- errorMessage = e.Message;
- return null;
- }
+ JSchemaPreloadedResolver resolver = new JSchemaPreloadedResolver();
+ resolver.Add(new Uri(referenceUri), childRules);
+
+ return JSchema.Parse(parentRules, resolver);
}
///
- /// The validate task that validate the input schema with custom rules in string.
+ /// Validates the ForgeTree schema with the given rules.
///
/// The schema to be validated
- /// The rules used to validate input schemas
- /// The result of schema validation. The errorList would contain error message if validation fails
- public static bool ValidateSchema(ForgeTree schema, string rules, out List errorList)
+ /// The rules used to validate input schemas is only allowed in string or JSchema type
+ /// /// The result of schema validation. The errorList would contain error message if validation fails
+ public static bool ValidateSchema(ForgeTree schema, object rules, out List errorList)
{
- return Validate(new List { SerializeForgeTree(schema) }, JSchema.Parse(rules), out errorList);
+ return Validate(new List { SerializeToJObject(schema) }, rules, out errorList);
}
+
///
- /// The validate task that validate the input schema with custom rules in string.
- ///
- /// The schema to be validated
- /// The rules used to validate input schemas
- /// The result of schema validation. The errorList would contain error message if validation fails
- public static bool ValidateSchema(ForgeTree schema, JSchema rules, out List errorList)
- {
- return Validate(new List { SerializeForgeTree(schema) }, rules, out errorList);
- }
- ///
- /// The validate task that validate multiple input schemas with custom rules in string.
+ /// Validates single or multiple schemas in Dictionary with the given rules.
///
/// The schemas to be validated
- /// The rules used to validate input schemas
+ /// The rules used to validate input schemas is only allowed in string or JSchema type
+ /// True if the custom rules is to handle the whole dictionary
/// The result of schema validation. The errorList would contain error message if validation fails
- public static bool ValidateSchemas(Dictionary schemas, string rules, out List errorList)
+ public static bool ValidateSchemas(Dictionary schemas, object rules, bool validateAsDictionary, out List errorList)
{
- return Validate(ConvertDictionaryToForgeTreeList(schemas), JSchema.Parse(rules), out errorList);
+ return Validate(ConvertDictionaryToJObjectList(schemas, validateAsDictionary), rules, out errorList);
}
+
///
- /// The validate task that check the input schema in string with custom rules in string.
+ /// Validates single or multiple schemas in string with the given rules.
///
/// The schema to be validated
- /// The rules used to validate input schemas
+ /// The rules used to validate input schemas is only allowed in string or JSchema type
+ /// True if the custom rules is to handle the whole dictionary
/// The result of schema validation. The errorList would contain error message if validation fails
- public static bool ValidateSchemaString(string schema, string rules, out List errorList)
+ public static bool ValidateSchemaString(string schema, object rules, bool validateAsDictionary, out List errorList)
{
- var schemaList = ConvertStringToJObjectList(schema, out errorList);
- return CheckConvertErrorAndValidate(JSchema.Parse(rules), ref errorList, schemaList);
+ List schemaList = ConvertStringToJObjectList(schema, out errorList, validateAsDictionary);
+
+ return CheckConvertErrorAndValidate(rules, ref errorList, schemaList);
}
///
- /// The validate task that validate the schema in the input file path with custom rules in string.
+ /// Validates single or multiple schemas from input path with the given rules.
///
/// The path that contains a schema file
- /// The rules used to validate input schemas
+ /// The rules used to validate input schemas is only allowed in string or JSchema type
+ /// True if the custom rules is to handle the whole dictionary
/// The result of schema validation. The errorList would contain error message if validation fails
- public static bool ValidateSchemaInPath(string path, string rules, out List errorList)
+ public static bool ValidateSchemaInPath(string path, object rules, bool validateAsDictionary, out List errorList)
{
- var schemas = GetSchemaFromPath(path, out errorList);
- return CheckConvertErrorAndValidate(JSchema.Parse(rules), ref errorList, schemas);
- }
+ List schemas = GetSchemaFromPath(path, out errorList, validateAsDictionary);
- ///
- /// The validate task that validate all schemas in a directory with custom rules in string.
- ///
- /// The path that contains a schemas directory
- /// The rules used to validate input schemas
- /// The result of schema validation. The errorList would contain error message if validation fails
- public static bool ValidateMultipleSchemasInPath(string path, string rules, out List errorList)
- {
- var schemas = GetAllSchemasInDirectory(path, out errorList);
- return CheckConvertErrorAndValidate(JSchema.Parse(rules), ref errorList, schemas);
- }
- ///
- /// The validate task that validate multiple input schemas with custom rules in JSchema.
- ///
- /// The schemas to be validated
- /// The rules used to validate input schemas
- /// The result of schema validation. The errorList would contain error message if validation fails
- public static bool ValidateSchemas(Dictionary schemas, JSchema rules, out List errorList)
- {
- return Validate(ConvertDictionaryToForgeTreeList(schemas), rules, out errorList);
- }
- ///
- /// The validate task that validate the schema in the input file path with custom rules in JSchema.
- ///
- /// The path that contains a schema file
- /// The rules used to validate input schemas
- /// The result of schema validation. The errorList would contain error message if validation fails
- public static bool ValidateSchemaInPath(string path, JSchema rules, out List errorList)
- {
- var schema = GetSchemaFromPath(path, out errorList);
- return CheckConvertErrorAndValidate(rules, ref errorList, schema);
- }
- ///
- /// The validate task that validate all schemas in a directory with custom rules in JSchema.
- ///
- /// The path that contains a schemas directory
- /// The rules used to validate input schemas
- /// The result of schema validation. The errorList would contain error message if validation fails
- public static bool ValidateMultipleSchemasInPath(string path, JSchema rules, out List errorList)
- {
- var schemas = GetAllSchemasInDirectory(path, out errorList);
return CheckConvertErrorAndValidate(rules, ref errorList, schemas);
}
///
- /// The validate task that check the input schema in string with custom rules in JSchema.
+ /// Validates single or multiple schemas from input path with the given rules.
///
- /// The schema to be validated
- /// The rules used to validate input schemas
+ /// The path that contains a schemas directory
+ /// The rules used to validate input schemas is only allowed in string or JSchema type
+ /// True if the custom rules is to handle the whole dictionary
/// The result of schema validation. The errorList would contain error message if validation fails
- public static bool ValidateSchemaString(string schema, JSchema rules, out List errorList)
+ public static bool ValidateMultipleSchemasInPath(string path, object rules, bool validateAsDictionary, out List errorList)
{
- var schemaList = ConvertStringToJObjectList(schema, out errorList);
- if (errorList.Count > 0)
- {
- return false;
- }
- var res = Validate(schemaList, rules, out errorList);
- return res;
+ List schemas = GetAllSchemasInDirectory(path, out errorList, validateAsDictionary);
+
+ return CheckConvertErrorAndValidate(rules, ref errorList, schemas);
}
- private static List ConvertDictionaryToForgeTreeList(Dictionary schemas)
+
+ private static List ConvertDictionaryToJObjectList(Dictionary schemas, bool validateAsDictionary)
{
- var schemaList = new List();
- foreach (var item in schemas.Values)
- schemaList.Add(SerializeForgeTree(item));
+ List schemaList = new List();
+
+ if (validateAsDictionary)
+ {
+ schemaList.Add(SerializeToJObject(schemas));
+ }
+ else
+ {
+ foreach (ForgeTree item in schemas.Values)
+ {
+ schemaList.Add(SerializeToJObject(item));
+ }
+ }
+
return schemaList;
}
- private static List ConvertStringToJObjectList(string schema, out List errorList)
+ private static List ConvertStringToJObjectList(string schema, out List errorList, bool validateAsDictionary)
{
- var schemaList = new List();
+ List schemaList = new List();
errorList = new List();
+
try
{
+ //There could be three possible cases:
+ //1. TreeName mapped to the ForgeTree in the dictionary and custom rules handle dictionary.
+ //2. There are only ForgeTree without matching forge tree name custom rules handle ForgeTree list.
Dictionary forgeTrees = JsonConvert.DeserializeObject>(schema);
- foreach (var kvp in forgeTrees)
+
+ if (validateAsDictionary)
{
- ForgeTree forgeTree = kvp.Value;
- if (forgeTree.Tree == null)
+ schemaList.Add(JObject.Parse(schema));
+ }
+ else
+ {
+ foreach (KeyValuePair kvp in forgeTrees)
{
- // Deserialize into Dictionary does not throw exception but will have null "Tree" property if schema is just a ForgeTree.
- // try to deserialize string to forge tree directly
- JsonConvert.DeserializeObject(schema);
- JObject res = JObject.Parse(schema);
- schemaList.Add(res);
- break;
+ ForgeTree forgeTree = kvp.Value;
+
+ if (forgeTree.Tree == null)
+ {
+ // Deserialize into Dictionary does not throw exception but will have null "Tree" property if schema is just a ForgeTree.
+ // try to deserialize string to forge tree directly
+ JsonConvert.DeserializeObject(schema);
+ schemaList.Add(JObject.Parse(schema));
+ break;
+ }
+
+ schemaList.Add(SerializeToJObject(forgeTree));
}
- schemaList.Add(SerializeForgeTree(forgeTree));
}
}
catch (Exception e)
{
errorList.Add(e.Message);
}
+
return schemaList;
}
- private static JObject SerializeForgeTree(ForgeTree forgeTree)
+ private static JObject SerializeToJObject(object forgeTree)
{
string stringSchema = JsonConvert.SerializeObject(
forgeTree,
@@ -205,79 +175,130 @@ namespace Microsoft.Forge.TreeWalker
DefaultValueHandling = DefaultValueHandling.Ignore, // Prevent default values from getting added to serialized json schema.
Converters = new List { new Newtonsoft.Json.Converters.StringEnumConverter() } // Use string enum values instead of numerical.
});
+
return JObject.Parse(stringSchema);
}
- private static List GetSchemaFromPath(string path, out List errorList)
+ private static List GetSchemaFromPath(string path, out List errorList, bool validateAsDictionary)
{
errorList = new List();
+
try
{
- var schema = File.ReadAllText(path);
- var res = ConvertStringToJObjectList(schema, out List convertError);
+ string schema = File.ReadAllText(path);
+
+ List res = ConvertStringToJObjectList(schema, out List convertError, validateAsDictionary);
errorList = convertError;
+
return res;
}
catch (Exception e)
{
errorList.Add(e.Message);
+
return new List();
}
}
- private static List GetAllSchemasInDirectory(string path, out List errorList)
+ private static List GetAllSchemasInDirectory(string path, out List errorList, bool validateAsDictionary)
{
- var schemaList = new List();
+ List schemaList = new List();
errorList = new List();
+
try
{
string[] Files = Directory.GetFiles(path);
- var schemalist = new List