private static void RecordFieldComplexity(INode node, ComplexityResult result, double impact) { result.Complexity += impact; if (result.ComplexityMap.ContainsKey(node)) { result.ComplexityMap[node] += impact; } else { result.ComplexityMap.Add(node, impact); } }
private void TreeIterator(INode node, ComplexityResult result, double avgImpact, double currentSubSelectionImpact, double currentEndNodeImpact) { if (_loopCounter++ > _maxRecursionCount) { _loopCounter = 0; throw new InvalidOperationException("Query is too complex to validate."); } if (node is FragmentDefinition) { return; } if (node.Children != null && node.Children.Any(n => n is Field || n is FragmentSpread || (n is SelectionSet && ((SelectionSet)n).Children.Any()) || n is Operation)) { if (node is Field) { result.TotalQueryDepth++; var impactFromArgs = GetImpactFromArgs(node); RecordFieldComplexity(node, result, currentEndNodeImpact = impactFromArgs / avgImpact * currentSubSelectionImpact ?? currentSubSelectionImpact); foreach (var nodeChild in node.Children.Where(n => n is SelectionSet)) { TreeIterator(nodeChild, result, avgImpact, currentSubSelectionImpact * (impactFromArgs ?? avgImpact), currentEndNodeImpact); } } else { foreach (var nodeChild in node.Children) { TreeIterator(nodeChild, result, avgImpact, currentSubSelectionImpact, currentEndNodeImpact); } } } else if (node is Field) { RecordFieldComplexity(node, result, currentEndNodeImpact); } else if (node is FragmentSpread) { var fragmentComplexity = _fragmentMap[((FragmentSpread)node).Name]; RecordFieldComplexity(node, result, currentSubSelectionImpact / avgImpact * fragmentComplexity.Complexity); result.TotalQueryDepth += fragmentComplexity.Depth; } }
/// <summary> /// Analyzes the complexity of a document. /// </summary> internal ComplexityResult Analyze(Document doc, double avgImpact = 2.0d) { if (avgImpact <= 1) { throw new ArgumentOutOfRangeException(nameof(avgImpact)); } doc.Children.Where(node => node is FragmentDefinition).Apply(node => { var fragResult = new FragmentComplexity(); FragmentIterator(node, fragResult, avgImpact, avgImpact, 1d); _fragmentMap[(node as FragmentDefinition)?.Name] = fragResult; }); TreeIterator(doc, _result, avgImpact, avgImpact, 1d); // Cleanup in case Analyze is called again _loopCounter = 0; var retVal = _result; _result = new ComplexityResult(); return(retVal); }
protected virtual void Analyzed(Document document, ComplexityConfiguration complexityParameters, ComplexityResult complexityResult) { #if DEBUG Debug.WriteLine($"Complexity: {complexityResult.Complexity}"); Debug.WriteLine($"Sum(Query depth across all subqueries) : {complexityResult.TotalQueryDepth}"); foreach (var node in complexityResult.ComplexityMap) { Debug.WriteLine($"{node.Key} : {node.Value}"); } #endif }