Modules/businessdev.ALbuild.Apps/Public/Invoke-BcCallGraphQuery.ps1
|
function Invoke-BcCallGraphQuery { <# .SYNOPSIS Answers a reachability question over a call graph with 'al graph query' (who calls X, what does X reach). .DESCRIPTION Selectors follow al graph: 'obj:<object name>', 'app:<app id>', 'ns:<pattern>', 'scope:cloud', 'access:public', and the aliases 'debuggable', 'onprem-surface', 'elevation'; '!' or 'not:' negates, '+' is AND and ',' is OR. With From alone the callees are walked, with To alone the callers; with both and -Paths the concrete paths between them are listed. .PARAMETER GraphPath The call graph folder (or its graph.jsonl). .PARAMETER From Source selector. .PARAMETER To Target selector. .PARAMETER Direction callees, callers or both. Default: callees for From, callers for To. .PARAMETER Depth Maximum traversal depth (0 = unbounded). .PARAMETER EdgeKinds Edge kinds to follow: Direct, Event, Interface, Run, Trigger. .PARAMETER Paths List the concrete From -> To paths. .PARAMETER MaxPaths Cap for -Paths. Default 1000. .PARAMETER ResolvedOnly Follow statically resolved edges only (drops over-approximated interface edges). .PARAMETER Exclude Prune nodes matching this selector, e.g. 'ns:*Test*'. .PARAMETER AlToolPath The 'al' executable. Default: an installed AL Tool with the graph command. .EXAMPLE Invoke-BcCallGraphQuery -GraphPath .albuild/callgraph -To 'obj:bdev.BNK API Client' -Depth 1 .OUTPUTS PSCustomObject: Count, Results (one line per node or path), Output (the raw text). #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter(Mandatory)] [string] $GraphPath, [string] $From, [string] $To, [ValidateSet('', 'callees', 'callers', 'both')] [string] $Direction = '', [ValidateRange(0, [int]::MaxValue)] [int] $Depth = 0, [string[]] $EdgeKinds = @(), [switch] $Paths, [ValidateRange(1, [int]::MaxValue)] [int] $MaxPaths = 1000, [switch] $ResolvedOnly, [string] $Exclude, [string] $AlToolPath ) if (-not $From -and -not $To) { throw 'Pass -From, -To or both.' } $graph = Resolve-BcCallGraphPath -Path $GraphPath -Require $al = $AlToolPath if (-not $al) { $al = (Install-BcAlTool -RuntimeVersion '18.0').Path } $arguments = New-Object System.Collections.Generic.List[string] $arguments.AddRange([string[]]@('graph', 'query', '--graph', $graph.GraphPath)) if ($From) { $arguments.AddRange([string[]]@('--from', $From)) } if ($To) { $arguments.AddRange([string[]]@('--to', $To)) } if ($Direction) { $arguments.AddRange([string[]]@('--direction', $Direction)) } if ($Depth -gt 0) { $arguments.AddRange([string[]]@('--depth', "$Depth")) } if ($EdgeKinds.Count -gt 0) { $arguments.AddRange([string[]]@('--edge-kinds', ($EdgeKinds -join ','))) } if ($Paths) { $arguments.Add('--paths'); $arguments.AddRange([string[]]@('--max-paths', "$MaxPaths")) } if ($ResolvedOnly) { $arguments.Add('--resolved-only') } if ($Exclude) { $arguments.AddRange([string[]]@('--exclude', $Exclude)) } $run = Invoke-ALbuildProcess -FilePath $al -Arguments $arguments.ToArray() -PassThru $text = "$($run.StdOut)" if (-not $run.Success -or $text -match 'Unhandled exception') { $detail = if ("$($run.StdErr)".Trim()) { $run.StdErr } else { $text } throw "al graph query failed: $(("$detail" -split "`r?`n" | Where-Object { $_.Trim() } | Select-Object -First 3) -join ' ')" } $lines = @($text -split "`r?`n" | Where-Object { $_.Trim() }) $count = $null if ($lines.Count -gt 0 -and $lines[0] -match '^(\d+)\s') { $count = [int]$Matches[1] } [PSCustomObject]@{ Count = $count Results = @($lines | Select-Object -Skip 1 | ForEach-Object { $_.Trim() }) Output = $text.TrimEnd() } } |