Merge pull request #152 from Azure/nonik/miscfixes
Fixes for issues #137 and #141
This commit is contained in:
Коммит
1a10c0bc60
|
@ -1,159 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions;
|
||||
using Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Operators;
|
||||
using Microsoft.Azure.Templates.Analyzer.Types;
|
||||
using Microsoft.Azure.Templates.Analyzer.Utilities;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using static Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.UnitTests.TestUtilities;
|
||||
|
||||
namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.UnitTests
|
||||
{
|
||||
[TestClass]
|
||||
public class AnyOfExpressionTests
|
||||
{
|
||||
[DataTestMethod]
|
||||
[DataRow(true, true, DisplayName = "AnyOf evaluates to true (true || true)")]
|
||||
[DataRow(true, false, DisplayName = "AnyOf evaluates to true (true || false)")]
|
||||
[DataRow(false, true, DisplayName = "AnyOf evaluates to true (false || true)")]
|
||||
[DataRow(false, false, DisplayName = "AnyOf evaluates to false (false || false)")]
|
||||
[DataRow(false, false, "ResourceProvider/resource", DisplayName = "AnyOf - scoped to resourceType - evaluates to false (false || false)")]
|
||||
[DataRow(false, false, "ResourceProvider/resource", "some.path", DisplayName = "AnyOf - scoped to resourceType and path - evaluates to false (false || false)")]
|
||||
public void Evaluate_TwoLeafExpressions_ExpectedResultIsReturned(bool evaluation1, bool evaluation2, string resourceType = null, string path = null)
|
||||
{
|
||||
// Arrange
|
||||
var mockJsonPathResolver = new Mock<IJsonPathResolver>();
|
||||
var mockLineResolver = new Mock<ILineNumberResolver>().Object;
|
||||
|
||||
// This AnyOf will have 2 expressions
|
||||
var mockOperator1 = new Mock<LeafExpressionOperator>().Object;
|
||||
var mockOperator2 = new Mock<LeafExpressionOperator>().Object;
|
||||
|
||||
var mockLeafExpression1 = new Mock<LeafExpression>(mockLineResolver, mockOperator1, new ExpressionCommonProperties { ResourceType = "ResourceProvider/resource", Path = "some.path" });
|
||||
var mockLeafExpression2 = new Mock<LeafExpression>(mockLineResolver, mockOperator2, new ExpressionCommonProperties { ResourceType = "ResourceProvider/resource", Path = "some.path" });
|
||||
|
||||
var jsonRuleResult1 = new JsonRuleResult
|
||||
{
|
||||
Passed = evaluation1
|
||||
};
|
||||
|
||||
var jsonRuleResult2 = new JsonRuleResult
|
||||
{
|
||||
Passed = evaluation2
|
||||
};
|
||||
|
||||
mockJsonPathResolver
|
||||
.Setup(s => s.Resolve(It.IsAny<string>()))
|
||||
.Returns(new List<IJsonPathResolver> { mockJsonPathResolver.Object });
|
||||
|
||||
if (!string.IsNullOrEmpty(resourceType))
|
||||
{
|
||||
mockJsonPathResolver
|
||||
.Setup(s => s.ResolveResourceType(It.Is<string>(type => type == resourceType)))
|
||||
.Returns(new List<IJsonPathResolver> { mockJsonPathResolver.Object });
|
||||
}
|
||||
|
||||
var results1 = new JsonRuleResult[] { jsonRuleResult1 };
|
||||
var results2 = new JsonRuleResult[] { jsonRuleResult2 };
|
||||
|
||||
mockLeafExpression1
|
||||
.Setup(s => s.Evaluate(mockJsonPathResolver.Object))
|
||||
.Returns(new JsonRuleEvaluation(mockLeafExpression1.Object, evaluation1, results1));
|
||||
|
||||
mockLeafExpression2
|
||||
.Setup(s => s.Evaluate(mockJsonPathResolver.Object))
|
||||
.Returns(new JsonRuleEvaluation(mockLeafExpression2.Object, evaluation2, results2));
|
||||
|
||||
var expressionArray = new Expression[] { mockLeafExpression1.Object, mockLeafExpression2.Object };
|
||||
|
||||
var anyOfExpression = new AnyOfExpression(expressionArray, new ExpressionCommonProperties { ResourceType = resourceType, Path = path });
|
||||
|
||||
// Act
|
||||
var anyOfEvaluation = anyOfExpression.Evaluate(mockJsonPathResolver.Object);
|
||||
|
||||
// Assert
|
||||
bool expectedAnyOfEvaluation = evaluation1 || evaluation2;
|
||||
Assert.AreEqual(expectedAnyOfEvaluation, anyOfEvaluation.Passed);
|
||||
Assert.AreEqual(2, anyOfEvaluation.Evaluations.Count());
|
||||
Assert.IsTrue(anyOfEvaluation.HasResults);
|
||||
|
||||
int expectedTrue = new[] { evaluation1, evaluation2 }.Count(e => e);
|
||||
int expectedFalse = 2 - expectedTrue;
|
||||
|
||||
Assert.AreEqual(expectedTrue, anyOfEvaluation.EvaluationsEvaluatedTrue.Count());
|
||||
Assert.AreEqual(expectedFalse, anyOfEvaluation.EvaluationsEvaluatedFalse.Count());
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(null, true, true, DisplayName = "First expression not evaluated, second expression passes, overall result is pass")]
|
||||
[DataRow(null, false, false, DisplayName = "First expression not evaluated, second expression fails, overall result is fail")]
|
||||
[DataRow(true, null, true, DisplayName = "First expression passes, second expression not evaluated, overall result is pass")]
|
||||
[DataRow(null, null, true, DisplayName = "All expressions not evaluated, overall result is pass")]
|
||||
public void Evaluate_SubResourceScopeNotFound_ExpectedResultIsReturned(bool? firstExpressionPass, bool? secondExpressionPass, bool overallPass)
|
||||
{
|
||||
string evaluatedPath = "some.evaluated.path";
|
||||
string notEvaluatedPath = "path.not.evaluated";
|
||||
|
||||
// Arrange
|
||||
var mockJsonPathResolver = new Mock<IJsonPathResolver>();
|
||||
mockJsonPathResolver
|
||||
.Setup(r => r.Resolve(It.Is<string>(path => evaluatedPath.Equals(path))))
|
||||
.Returns(() => new[] { mockJsonPathResolver.Object });
|
||||
|
||||
var mockLineResolver = new Mock<ILineNumberResolver>().Object;
|
||||
|
||||
// Create 2 expressions for the AnyOf.
|
||||
// Whether each is evaluated or not is determined by the path passed to each.
|
||||
|
||||
var mockLeafExpression1 = new MockExpression(new ExpressionCommonProperties { Path = firstExpressionPass.HasValue ? evaluatedPath : notEvaluatedPath })
|
||||
{
|
||||
EvaluationCallback = pathResolver => firstExpressionPass.HasValue
|
||||
? new JsonRuleEvaluation(null, firstExpressionPass.Value, new[] { new JsonRuleResult { Passed = firstExpressionPass.Value } })
|
||||
: null
|
||||
};
|
||||
|
||||
var mockLeafExpression2 = new MockExpression(new ExpressionCommonProperties { Path = secondExpressionPass.HasValue ? evaluatedPath : notEvaluatedPath })
|
||||
{
|
||||
EvaluationCallback = pathResolver => secondExpressionPass.HasValue
|
||||
? new JsonRuleEvaluation(null, secondExpressionPass.Value, new[] { new JsonRuleResult { Passed = secondExpressionPass.Value } })
|
||||
: null
|
||||
};
|
||||
|
||||
var anyOfExpression = new AnyOfExpression(
|
||||
new Expression[] { mockLeafExpression1, mockLeafExpression2 },
|
||||
new ExpressionCommonProperties());
|
||||
|
||||
var expectedResults = new[] { firstExpressionPass, secondExpressionPass };
|
||||
|
||||
// Act
|
||||
var anyOfEvaluation = anyOfExpression.Evaluate(mockJsonPathResolver.Object);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(overallPass, anyOfEvaluation.Passed);
|
||||
Assert.AreEqual(expectedResults.Count(r => r.HasValue), anyOfEvaluation.Evaluations.Count());
|
||||
Assert.AreEqual(expectedResults.Any(r => r.HasValue), anyOfEvaluation.HasResults);
|
||||
|
||||
Assert.AreEqual(expectedResults.Count(r => r.HasValue && r.Value), anyOfEvaluation.EvaluationsEvaluatedTrue.Count());
|
||||
Assert.AreEqual(expectedResults.Count(r => r.HasValue && !r.Value), anyOfEvaluation.EvaluationsEvaluatedFalse.Count());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Evaluate_NullScope_ThrowsException()
|
||||
{
|
||||
new AnyOfExpression(new Expression[0], new ExpressionCommonProperties()).Evaluate(jsonScope: null);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Constructor_NullExpressions_ThrowsException()
|
||||
{
|
||||
new AnyOfExpression(null, new ExpressionCommonProperties());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,16 +15,37 @@ using static Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.UnitTests
|
|||
namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.UnitTests
|
||||
{
|
||||
[TestClass]
|
||||
public class AllOfExpressionTests
|
||||
public class StructuredExpressionTests
|
||||
{
|
||||
private readonly Func<bool, bool, bool> DummyOperation = (x, y) => x ^ y;
|
||||
|
||||
// tests functionality of AllOfExpression
|
||||
[DataTestMethod]
|
||||
[DataRow(true, true, DisplayName = "AllOf evaluates to true (true && true)")]
|
||||
[DataRow(true, false, DisplayName = "AllOf evaluates to false (true && false)")]
|
||||
[DataRow(false, true, DisplayName = "AllOf evaluates to false (false && true)")]
|
||||
[DataRow(false, false, DisplayName = "AllOf evaluates to false (false && false)")]
|
||||
[DataRow(false, false, "ResourceProvider/resource", DisplayName = "AllOf - scoped to resourceType - evaluates to false (false && false)")]
|
||||
[DataRow(false, false, "ResourceProvider/resource", "some.path", DisplayName = "AllOf - scoped to resourceType and path - evaluates to false (false && false)")]
|
||||
public void Evaluate_TwoLeafExpressions_ExpectedResultIsReturned(bool evaluation1, bool evaluation2, string resourceType = null, string path = null)
|
||||
[DataRow(true, true, DisplayName = "Evaluates to true (true && true)")]
|
||||
[DataRow(true, false, DisplayName = "Evaluates to false (true && false)")]
|
||||
[DataRow(false, true, DisplayName = "Evaluates to false (false && true)")]
|
||||
[DataRow(false, false, DisplayName = "Evaluates to false (false && false)")]
|
||||
[DataRow(false, false, "ResourceProvider/resource", DisplayName = "Scoped to resourceType - Evaluates to false (false && false)")]
|
||||
[DataRow(false, false, "ResourceProvider/resource", "some.path", DisplayName = "Scoped to resourceType and path - Evaluates to false (false && false)")]
|
||||
public void Evaluate_TwoLeafExpressions_ExpectedResultIsReturnedForAndOperations(bool evaluation1, bool evaluation2, string resourceType = null, string path = null)
|
||||
{
|
||||
Evaluate_TwoLeafExpressions(evaluation1, evaluation2, (x, y) => x && y, resourceType, path);
|
||||
}
|
||||
|
||||
// tests functionality of AnyOfExpression
|
||||
[DataTestMethod]
|
||||
[DataRow(true, true, DisplayName = "Evaluates to true (true || true)")]
|
||||
[DataRow(true, false, DisplayName = "Evaluates to true (true || false)")]
|
||||
[DataRow(false, true, DisplayName = "Evaluates to true (false || true)")]
|
||||
[DataRow(false, false, DisplayName = "Evaluates to false (false || false)")]
|
||||
[DataRow(false, false, "ResourceProvider/resource", DisplayName = "Scoped to resourceType - Evaluates to false (false || false)")]
|
||||
[DataRow(false, false, "ResourceProvider/resource", "some.path", DisplayName = "Scoped to resourceType and path - Evaluates to false (false || false)")]
|
||||
public void Evaluate_TwoLeafExpressions_ExpectedResultIsReturnedForOrOperations(bool evaluation1, bool evaluation2, string resourceType = null, string path = null)
|
||||
{
|
||||
Evaluate_TwoLeafExpressions(evaluation1, evaluation2, (x, y) => x || y, resourceType, path);
|
||||
}
|
||||
|
||||
private static void Evaluate_TwoLeafExpressions(bool evaluation1, bool evaluation2, Func<bool, bool, bool> operation, string resourceType = null, string path = null)
|
||||
{
|
||||
// Arrange
|
||||
var mockJsonPathResolver = new Mock<IJsonPathResolver>();
|
||||
|
@ -71,24 +92,24 @@ namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.UnitTests
|
|||
|
||||
var expressionArray = new Expression[] { mockLeafExpression1.Object, mockLeafExpression2.Object };
|
||||
|
||||
var allOfExpression = new AllOfExpression(expressionArray, new ExpressionCommonProperties { ResourceType = resourceType, Path = path });
|
||||
var structuredExpression = new StructuredExpression(expressionArray, operation, new ExpressionCommonProperties { ResourceType = resourceType, Path = path });
|
||||
|
||||
// Act
|
||||
var allOfEvaluation = allOfExpression.Evaluate(mockJsonPathResolver.Object);
|
||||
var structuredEvaluation = structuredExpression.Evaluate(mockJsonPathResolver.Object);
|
||||
|
||||
// Assert
|
||||
bool expectedAllOfEvaluation = evaluation1 && evaluation2;
|
||||
Assert.AreEqual(expectedAllOfEvaluation, allOfEvaluation.Passed);
|
||||
Assert.AreEqual(2, allOfEvaluation.Evaluations.Count());
|
||||
Assert.IsTrue(allOfEvaluation.HasResults);
|
||||
bool expectedCompoundEvaluation = operation(evaluation1, evaluation2);
|
||||
Assert.AreEqual(expectedCompoundEvaluation, structuredEvaluation.Passed);
|
||||
Assert.AreEqual(2, structuredEvaluation.Evaluations.Count());
|
||||
Assert.IsTrue(structuredEvaluation.HasResults);
|
||||
|
||||
int expectedTrue = new[] { evaluation1, evaluation2 }.Count(e => e);
|
||||
int expectedFalse = 2 - expectedTrue;
|
||||
|
||||
Assert.AreEqual(expectedTrue, allOfEvaluation.EvaluationsEvaluatedTrue.Count());
|
||||
Assert.AreEqual(expectedFalse, allOfEvaluation.EvaluationsEvaluatedFalse.Count());
|
||||
Assert.AreEqual(expectedTrue, structuredEvaluation.EvaluationsEvaluatedTrue.Count());
|
||||
Assert.AreEqual(expectedFalse, structuredEvaluation.EvaluationsEvaluatedFalse.Count());
|
||||
|
||||
foreach (var evaluation in allOfEvaluation.Evaluations)
|
||||
foreach (var evaluation in structuredEvaluation.Evaluations)
|
||||
{
|
||||
// Assert all leaf expressions have results and no evaluations
|
||||
Assert.IsTrue(evaluation.HasResults);
|
||||
|
@ -132,36 +153,37 @@ namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.UnitTests
|
|||
: null
|
||||
};
|
||||
|
||||
var allOfExpression = new AllOfExpression(
|
||||
var structuredExpression = new StructuredExpression(
|
||||
new Expression[] { mockLeafExpression1, mockLeafExpression2 },
|
||||
DummyOperation,
|
||||
new ExpressionCommonProperties());
|
||||
|
||||
var expectedResults = new[] { firstExpressionPass, secondExpressionPass };
|
||||
|
||||
// Act
|
||||
var allOfEvaluation = allOfExpression.Evaluate(mockJsonPathResolver.Object);
|
||||
var structuredEvaluation = structuredExpression.Evaluate(mockJsonPathResolver.Object);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(overallPass, allOfEvaluation.Passed);
|
||||
Assert.AreEqual(expectedResults.Count(r => r.HasValue), allOfEvaluation.Evaluations.Count());
|
||||
Assert.AreEqual(expectedResults.Any(r => r.HasValue), allOfEvaluation.HasResults);
|
||||
Assert.AreEqual(overallPass, structuredEvaluation.Passed);
|
||||
Assert.AreEqual(expectedResults.Count(r => r.HasValue), structuredEvaluation.Evaluations.Count());
|
||||
Assert.AreEqual(expectedResults.Any(r => r.HasValue), structuredEvaluation.HasResults);
|
||||
|
||||
Assert.AreEqual(expectedResults.Count(r => r.HasValue && r.Value), allOfEvaluation.EvaluationsEvaluatedTrue.Count());
|
||||
Assert.AreEqual(expectedResults.Count(r => r.HasValue && !r.Value), allOfEvaluation.EvaluationsEvaluatedFalse.Count());
|
||||
Assert.AreEqual(expectedResults.Count(r => r.HasValue && r.Value), structuredEvaluation.EvaluationsEvaluatedTrue.Count());
|
||||
Assert.AreEqual(expectedResults.Count(r => r.HasValue && !r.Value), structuredEvaluation.EvaluationsEvaluatedFalse.Count());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Evaluate_NullScope_ThrowsException()
|
||||
{
|
||||
new AllOfExpression(new Expression[0], new ExpressionCommonProperties()).Evaluate(jsonScope: null);
|
||||
new StructuredExpression(Array.Empty<Expression>(), DummyOperation, new ExpressionCommonProperties()).Evaluate(jsonScope: null);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void Constructor_NullExpressions_ThrowsException()
|
||||
{
|
||||
new AllOfExpression(null, new ExpressionCommonProperties());
|
||||
new StructuredExpression(null, DummyOperation, new ExpressionCommonProperties());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Azure.Templates.Analyzer.Types;
|
||||
|
||||
namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an allOf expression in a JSON rule.
|
||||
/// </summary>
|
||||
internal class AllOfExpression : Expression
|
||||
internal class AllOfExpression : StructuredExpression
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the expressions to be evaluated.
|
||||
/// </summary>
|
||||
public Expression[] AllOf { get; private set; }
|
||||
public Expression[] AllOf
|
||||
{
|
||||
get => Expressions;
|
||||
private set => Expressions = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="AllOfExpression"/>.
|
||||
|
@ -23,37 +23,8 @@ namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions
|
|||
/// <param name="expressions">List of expressions to perform a logical AND against.</param>
|
||||
/// <param name="commonProperties">The properties common across all <see cref="Expression"/> types.</param>
|
||||
public AllOfExpression(Expression[] expressions, ExpressionCommonProperties commonProperties)
|
||||
: base(commonProperties)
|
||||
: base(expressions, (x, y) => x && y, commonProperties)
|
||||
{
|
||||
this.AllOf = expressions ?? throw new ArgumentNullException(nameof(expressions));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates all expressions provided and aggregates them in a final <see cref="JsonRuleEvaluation"/>.
|
||||
/// </summary>
|
||||
/// <param name="jsonScope">The json to evaluate.</param>
|
||||
/// <returns>A <see cref="JsonRuleEvaluation"/> with the results of the evaluation.</returns>
|
||||
public override JsonRuleEvaluation Evaluate(IJsonPathResolver jsonScope)
|
||||
{
|
||||
return EvaluateInternal(jsonScope, scope =>
|
||||
{
|
||||
List<JsonRuleEvaluation> jsonRuleEvaluations = new List<JsonRuleEvaluation>();
|
||||
bool evaluationPassed = true;
|
||||
|
||||
foreach (var expression in AllOf)
|
||||
{
|
||||
var evaluation = expression.Evaluate(scope);
|
||||
|
||||
// Add evaluations if scopes were found to evaluate
|
||||
if (evaluation.HasResults)
|
||||
{
|
||||
evaluationPassed &= evaluation.Passed;
|
||||
jsonRuleEvaluations.Add(evaluation);
|
||||
}
|
||||
}
|
||||
|
||||
return new JsonRuleEvaluation(this, evaluationPassed, jsonRuleEvaluations);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Azure.Templates.Analyzer.Types;
|
||||
|
||||
namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an anyOf expression in a JSON rule.
|
||||
/// Represents an AnyOf expression in a JSON rule.
|
||||
/// </summary>
|
||||
internal class AnyOfExpression : Expression
|
||||
internal class AnyOfExpression : StructuredExpression
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the expressions to be evaluated.
|
||||
/// Gets the expressions to be evaluated.
|
||||
/// </summary>
|
||||
public Expression[] AnyOf { get; private set; }
|
||||
public Expression[] AnyOf
|
||||
{
|
||||
get => Expressions;
|
||||
private set => Expressions = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="AnyOfExpression"/>.
|
||||
|
@ -23,36 +23,8 @@ namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions
|
|||
/// <param name="expressions">List of expressions to perform a logical OR against.</param>
|
||||
/// <param name="commonProperties">The properties common across all <see cref="Expression"/> types.</param>
|
||||
public AnyOfExpression(Expression[] expressions, ExpressionCommonProperties commonProperties)
|
||||
: base(commonProperties)
|
||||
: base(expressions, (x, y) => x || y, commonProperties)
|
||||
{
|
||||
this.AnyOf = expressions ?? throw new ArgumentNullException(nameof(expressions));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates all expressions provided and aggregates them in a final <see cref="JsonRuleEvaluation"/>
|
||||
/// </summary>
|
||||
/// <param name="jsonScope">The json to evaluate.</param>
|
||||
/// <returns>A <see cref="JsonRuleEvaluation"/> with zero or more results of the evaluation, depending on whether there are any/multiple resources of the given type,
|
||||
/// and if the path contains any wildcards.</returns>
|
||||
public override JsonRuleEvaluation Evaluate(IJsonPathResolver jsonScope)
|
||||
{
|
||||
return EvaluateInternal(jsonScope, scope =>
|
||||
{
|
||||
List<JsonRuleEvaluation> jsonRuleEvaluations = new List<JsonRuleEvaluation>();
|
||||
|
||||
foreach (var expression in AnyOf)
|
||||
{
|
||||
var evaluation = expression.Evaluate(scope);
|
||||
|
||||
// Add evaluations if scopes were found to evaluate
|
||||
if (evaluation.HasResults)
|
||||
{
|
||||
jsonRuleEvaluations.Add(evaluation);
|
||||
}
|
||||
}
|
||||
bool evaluationPassed = jsonRuleEvaluations.Count == 0 || jsonRuleEvaluations.Exists(e => e.Passed);
|
||||
return new JsonRuleEvaluation(this, evaluationPassed, jsonRuleEvaluations);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Azure.Templates.Analyzer.Types;
|
||||
|
||||
namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a structured expression in a JSON rule.
|
||||
/// </summary>
|
||||
internal class StructuredExpression : Expression
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines the boolean operation to use when evaluating the expressions.
|
||||
/// </summary>
|
||||
private readonly Func<bool, bool, bool> Operation;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the expressions to be evaluated.
|
||||
/// </summary>
|
||||
internal Expression[] Expressions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="StructuredExpression"/>.
|
||||
/// </summary>
|
||||
/// <param name="expressions">List of expressions to perform a logical operation against.</param>
|
||||
/// <param name="operation">The boolean operation to perform to calculate the overall expression result.</param>
|
||||
/// <param name="commonProperties">The properties common across all <see cref="Expression"/> types.</param>
|
||||
public StructuredExpression(Expression[] expressions, Func<bool,bool, bool> operation, ExpressionCommonProperties commonProperties)
|
||||
: base(commonProperties)
|
||||
{
|
||||
Expressions = expressions ?? throw new ArgumentNullException(nameof(expressions));
|
||||
Operation = operation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates all expressions provided and aggregates them in a final <see cref="JsonRuleEvaluation"/>
|
||||
/// </summary>
|
||||
/// <param name="jsonScope">The json to evaluate.</param>
|
||||
/// <returns>A <see cref="JsonRuleEvaluation"/> with zero or more results of the evaluation, depending on whether there are any/multiple resources of the given type,
|
||||
/// and if the path contains any wildcards.</returns>
|
||||
public override JsonRuleEvaluation Evaluate(IJsonPathResolver jsonScope)
|
||||
{
|
||||
return EvaluateInternal(jsonScope, scope =>
|
||||
{
|
||||
List<JsonRuleEvaluation> jsonRuleEvaluations = new List<JsonRuleEvaluation>();
|
||||
bool? evaluationPassed = null;
|
||||
|
||||
foreach (var expression in Expressions)
|
||||
{
|
||||
var evaluation = expression.Evaluate(scope);
|
||||
|
||||
// Add evaluations if scopes were found to evaluate
|
||||
if (evaluation.HasResults)
|
||||
{
|
||||
// if no value, this is the first expression evaluated so set the inital value of result
|
||||
if (!evaluationPassed.HasValue)
|
||||
{
|
||||
evaluationPassed = evaluation.Passed;
|
||||
}
|
||||
// otherwise use defined operation to calculate intermediate expression result
|
||||
else
|
||||
{
|
||||
evaluationPassed = Operation(evaluationPassed.Value, evaluation.Passed);
|
||||
}
|
||||
jsonRuleEvaluations.Add(evaluation);
|
||||
}
|
||||
}
|
||||
|
||||
evaluationPassed |= jsonRuleEvaluations.Count == 0;
|
||||
|
||||
return new JsonRuleEvaluation(this, evaluationPassed.Value, jsonRuleEvaluations);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -157,7 +157,7 @@ namespace Microsoft.Azure.Templates.Analyzer.TemplateProcessor.UnitTests
|
|||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessResourcesAndOutputs_ValidTemplateWithExpressionInResourceProperties_ProcessResourceProperyLanguageExpressions()
|
||||
public void ProcessResourcesAndOutputs_ValidTemplateWithExpressionInResourceProperties_ProcessResourcePropertyLanguageExpressions()
|
||||
{
|
||||
string templateJson = @"{
|
||||
""$schema"": ""https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#"",
|
||||
|
@ -665,6 +665,67 @@ namespace Microsoft.Azure.Templates.Analyzer.TemplateProcessor.UnitTests
|
|||
AssertDictionariesAreEqual(expectedMapping, armTemplateProcessor.ResourceMappings);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CopyResourceDependants_DependsOnSpecifiedInNestedChildResource()
|
||||
{
|
||||
// Arrange
|
||||
string resourceJson = @"{
|
||||
""type"": ""Microsoft.Sql/servers"",
|
||||
""apiVersion"": ""2020-02-02-preview"",
|
||||
""name"": ""sqlServerName"",
|
||||
""location"": ""location"",
|
||||
""properties"": {},
|
||||
""resources"": [
|
||||
{
|
||||
""type"": ""databases"",
|
||||
""apiVersion"": ""2020-02-02-preview"",
|
||||
""name"": ""dataWarehouseName"",
|
||||
""location"": ""location'"",
|
||||
""kind"": ""v12.0,user,datawarehouse"",
|
||||
""dependsOn"": [
|
||||
""sqlServerName""
|
||||
],
|
||||
""properties"": {},
|
||||
""resources"": [
|
||||
{
|
||||
""type"": ""transparentDataEncryption"",
|
||||
""apiVersion"": ""2017-03-01-preview"",
|
||||
""name"": ""current"",
|
||||
""dependsOn"": [
|
||||
""dataWarehouseName""
|
||||
],
|
||||
""properties"": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}";
|
||||
|
||||
Dictionary<string, string> expectedMapping = new Dictionary<string, string> {
|
||||
{ "resources[0]", "resources[0]" },
|
||||
{ "resources[0].resources[0]", "resources[0].resources[0]" },
|
||||
{ "resources[0].resources[0].resources[0]", "resources[0].resources[0].resources[0]" }
|
||||
};
|
||||
|
||||
TemplateResource templateResource = JObject.Parse(resourceJson).ToObject<TemplateResource>(SerializerSettings.SerializerWithObjectTypeSettings);
|
||||
|
||||
ArmTemplateProcessor armTemplateProcessor = new ArmTemplateProcessor(GenerateTemplateWithResources(new TemplateResource[] { templateResource }));
|
||||
|
||||
// Act
|
||||
var template = armTemplateProcessor.ProcessTemplate();
|
||||
|
||||
// Assert
|
||||
string expectedResourceArray = $@"[ {resourceJson} ]";
|
||||
var expectedResourceJArray = JArray.Parse(expectedResourceArray);
|
||||
(expectedResourceJArray[0] as JObject).Add("dependsOn", new JArray());
|
||||
|
||||
var actualResourceArray = template.ToJToken().InsensitiveToken("resources");
|
||||
|
||||
Assert.IsTrue(JToken.DeepEquals(expectedResourceJArray, actualResourceArray));
|
||||
|
||||
AssertDictionariesAreEqual(expectedMapping, armTemplateProcessor.ResourceMappings);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CopyResourceDependants_DependsOnIsChained_CopiesToCorrectResource()
|
||||
{
|
||||
|
|
|
@ -234,7 +234,10 @@ namespace Microsoft.Azure.Templates.Analyzer.TemplateProcessor
|
|||
|
||||
AddResourceMapping($"{parentResource.Path}.resources[0]", templateResource.Path);
|
||||
}
|
||||
else
|
||||
// check if resource is already a child of parent resource
|
||||
else if (!parentResource.Resources.Any(res =>
|
||||
res.Name.Value == templateResource.Name.Value &&
|
||||
res.Type.Value == templateResource.Type.Value))
|
||||
{
|
||||
var childResources = parentResource.Resources;
|
||||
parentResource.Resources = childResources.ConcatArray(new TemplateResource[] { templateResource });
|
||||
|
|
Загрузка…
Ссылка в новой задаче