src/Analyzer.JsonRuleEngine/Expressions/Expression.cs (51 lines of code) (raw):

// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.Linq; using Microsoft.Azure.Templates.Analyzer.Types; using Microsoft.Azure.Templates.Analyzer.Utilities; namespace Microsoft.Azure.Templates.Analyzer.RuleEngines.JsonEngine.Expressions { /// <summary> /// The base class for all Expressions in JSON rules. /// </summary> internal abstract class Expression { /// <summary> /// Gets the type of resource to evaluate. /// </summary> public string ResourceType { get; private set; } /// <summary> /// Gets the JSON path to evaluate. /// </summary> public string Path { get; private set; } /// <summary> /// Gets the Where condition of this <see cref="Expression"/>. /// </summary> public Expression Where { get; private set; } /// <summary> /// Initialization for the base Expression. /// </summary> /// <param name="commonProperties">The properties common across all <see cref="Expression"/> types.</param> internal Expression(ExpressionCommonProperties commonProperties) { (this.ResourceType, this.Path, this.Where) = (commonProperties?.ResourceType, commonProperties?.Path, commonProperties?.Where); } /// <summary> /// Executes this <see cref="Expression"/> against a template. /// </summary> /// <param name="jsonScope">The specific scope to evaluate.</param> /// <param name="jsonLineNumberResolver">An <see cref="ISourceLocationResolver"/> to /// map JSON paths in the returned evaluation to the line number in the JSON evaluated.</param> /// <returns>An <see cref="IEnumerable{JsonRuleEvaluation}"/> with the results of the evaluation.</returns> public abstract IEnumerable<JsonRuleEvaluation> Evaluate(IJsonPathResolver jsonScope, ISourceLocationResolver jsonLineNumberResolver); /// <summary> /// Performs tasks common across <see cref="Expression"/> implementations, such as /// determining all paths to run against. /// </summary> /// <param name="jsonScope">The scope being evaluated.</param> /// <param name="doEvaluation">A delegate for logic specific to the child <see cref="Expression"/>, which /// generates a <see cref="JsonRuleEvaluation"/> for the specified <paramref name="jsonScope"/>.</param> /// <returns>The results of the evaluation.</returns> protected IEnumerable<JsonRuleEvaluation> EvaluateInternal(IJsonPathResolver jsonScope, Func<IJsonPathResolver, JsonRuleEvaluation> doEvaluation) => EvaluateInternal(jsonScope, scope => new[] { doEvaluation(scope) }); /// <summary> /// Performs tasks common across <see cref="Expression"/> implementations, such as /// determining all paths to run against. /// </summary> /// <param name="jsonScope">The scope being evaluated.</param> /// <param name="doEvaluation">A delegate for logic specific to the child <see cref="Expression"/>, which /// generates an enumerable of <see cref="JsonRuleEvaluation"/>s for the specified <paramref name="jsonScope"/>.</param> /// <returns>The results of the evaluation.</returns> protected IEnumerable<JsonRuleEvaluation> EvaluateInternal(IJsonPathResolver jsonScope, Func<IJsonPathResolver, IEnumerable<JsonRuleEvaluation>> doEvaluation) { if (jsonScope == null) throw new ArgumentNullException(nameof(jsonScope)); // Select resources of given type, if specified IEnumerable<IJsonPathResolver> scopesToEvaluate; if (!string.IsNullOrEmpty(ResourceType)) { scopesToEvaluate = jsonScope.ResolveResourceType(ResourceType); } else { scopesToEvaluate = new[] { jsonScope }; } List<JsonRuleEvaluation> evaluations = new List<JsonRuleEvaluation>(); foreach (var initialScope in scopesToEvaluate) { // Expand with path if specified IEnumerable<IJsonPathResolver> expandedScopes = Path == null ? new[] { initialScope }.AsEnumerable() : initialScope?.Resolve(Path); foreach (var propertyToEvaluate in expandedScopes) { // Evaluate this path if either (a) there is no Where condition to evaluate, or (b) the Where expression passed for this path. // Do not pass a line number resolver to Where because line numbers in these evaluations do not matter. var whereEvaluation = Where?.Evaluate(propertyToEvaluate, jsonLineNumberResolver: null); if (whereEvaluation == null || whereEvaluation.Any(w => w.Passed && w.HasResults)) { // Perform the evaluation on this scope/path evaluations.AddRange(doEvaluation(propertyToEvaluate)); } } } return evaluations; } } }