Public/Get-InforcerReportType.ps1
|
<# .SYNOPSIS Lists available report types from the Inforcer Reports API. Required API scope(s): Reports.Read .DESCRIPTION Retrieves the catalog of report types from GET /beta/reports/types. Each catalog entry describes a report's key, supported output formats, collatability, accepted parameters, and tags. The catalog is cached in the module-scoped variable so subsequent calls (and tab-completion lookups) are instant; pass -Force to refetch from the API. Output is shaped for cmdlet consumption: PascalCase property aliases are added and the InforcerCommunity.ReportType PSTypeName is inserted to drive default formatting. .PARAMETER Key Filter to a single report type by key (case-insensitive). Aliases: -Name, -ReportType. .PARAMETER Tag Filter to types containing the specified tag (case-insensitive). .PARAMETER OutputFormat Filter to types that support the specified output format (e.g. csv, json, pdf, html). .PARAMETER Force Bypass the in-memory cache and refetch the catalog from the API. .PARAMETER Format Raw (default). Reserved for future shape changes. .PARAMETER OutputType PowerShellObject (default) or JsonObject. JSON uses Depth 100. .EXAMPLE Get-InforcerReportType Lists every available report type. .EXAMPLE Get-InforcerReportType -Key CopilotAdoption Returns the single CopilotAdoption catalog entry. .EXAMPLE Get-InforcerReportType -OutputFormat pdf Lists every type that supports PDF output. .EXAMPLE Get-InforcerReportType -Tag security Lists every type tagged with 'security'. .EXAMPLE Get-InforcerReportType -Tag security | Invoke-InforcerReport -OutputFormat csv -TenantId 482 Discovers every security-tagged report type and pipes each into Invoke-InforcerReport. Works because Get-InforcerReportType emits a 'Key' alias which binds to Invoke-InforcerReport's -ReportType (ValueFromPipelineByPropertyName). .OUTPUTS PSObject or String .LINK https://github.com/royklo/InforcerCommunity/blob/main/docs/CMDLET-REFERENCE.md#get-inforcerreporttype .LINK Connect-Inforcer .LINK Invoke-InforcerReport #> function Get-InforcerReportType { [CmdletBinding()] [OutputType([PSObject], [string])] param( [Parameter(Mandatory = $false, Position = 0)] [Alias('Name', 'ReportType')] [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) # ArgumentCompleter scriptblocks run in the *caller's* session state, NOT the module's, # so $script:InforcerReportTypeCache / $script:InforcerReportTypeStaticKeys are invisible # from here. Bounce through the module's session via Get-Module so $script:* resolves. # The static-fallback list and live cache both live in Private/Get-InforcerReportTypeStaticKeys.ps1. $module = Get-Module InforcerCommunity $candidates = if ($module) { & $module { if ($script:InforcerReportTypeCache) { $tmp = [System.Collections.Generic.List[string]]::new() foreach ($e in $script:InforcerReportTypeCache) { if ($e -isnot [PSObject]) { continue } $keyProp = $e.PSObject.Properties['key'] if (-not $keyProp) { continue } $v = $keyProp.Value -as [string] if ($v) { [void]$tmp.Add($v) } } # If cache existed but yielded zero keys (malformed entries / unexpected shape), # fall back to the static list rather than letting PS use filesystem completion. if ($tmp.Count -eq 0) { ,$script:InforcerReportTypeStaticKeys } else { ,$tmp.ToArray() } } else { ,$script:InforcerReportTypeStaticKeys } } } else { @() } $matched = [System.Collections.Generic.SortedSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) foreach ($c in $candidates) { if ($c -like "$wordToComplete*") { [void]$matched.Add($c) } } foreach ($c in $matched) { [System.Management.Automation.CompletionResult]::new($c, $c, 'ParameterValue', $c) } })] [string]$Key, [Parameter(Mandatory = $false)] [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) # ArgumentCompleter scriptblocks run in the caller's session state, so $script:* # variables defined in the module are invisible from here — bounce through the module # to read the live tag set from the cached catalog. $known = @('Adoption','Identity','Productivity','Security') $module = Get-Module InforcerCommunity $candidates = if ($module) { & $module { if ($script:InforcerReportTypeCache) { $tmp = [System.Collections.Generic.SortedSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) foreach ($e in $script:InforcerReportTypeCache) { if ($null -eq $e -or $e -isnot [PSObject]) { continue } $tagsProp = $e.PSObject.Properties['tags'] if ($tagsProp) { foreach ($t in @($tagsProp.Value)) { $tStr = $t -as [string] if ($tStr) { [void]$tmp.Add($tStr) } } } } ,$tmp.ToArray() } else { ,@() } } } else { @() } if (-not $candidates -or @($candidates).Count -eq 0) { $candidates = $known } foreach ($c in $candidates) { if ($c -like "$wordToComplete*") { [System.Management.Automation.CompletionResult]::new($c, $c, 'ParameterValue', $c) } } })] [string]$Tag, [Parameter(Mandatory = $false)] [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $formats = @('csv','json','html','pdf','xlsx') foreach ($f in $formats) { if ($f -like "$wordToComplete*") { [System.Management.Automation.CompletionResult]::new($f, $f, 'ParameterValue', $f) } } })] [string]$OutputFormat, [Parameter(Mandatory = $false)] [switch]$Force, [Parameter(Mandatory = $false)] [ValidateSet('Raw')] [string]$Format = 'Raw', [Parameter(Mandatory = $false)] [ValidateSet('PowerShellObject', 'JsonObject')] [string]$OutputType = 'PowerShellObject' ) if (-not (Test-InforcerSession)) { Write-Error -Message 'Not connected yet. Please run Connect-Inforcer first.' ` -ErrorId 'NotConnected' -Category ConnectionError return } # Fetch (or use cache) — JsonObject path always refetches so the response shape isn't # distorted by PSObject mutations applied during alias-adding. if ($OutputType -eq 'JsonObject' -or $Force -or $null -eq $script:InforcerReportTypeCache) { Write-Verbose 'Retrieving report types catalog...' $response = Invoke-InforcerApiRequest -Endpoint '/beta/reports/types' -Method GET -OutputType $OutputType if ($null -eq $response) { return } if ($OutputType -eq 'JsonObject') { # Caller asked for raw JSON — no filtering / aliasing supported in this mode. return $response } $script:InforcerReportTypeCache = @($response) $script:InforcerReportTypeCacheStamp = Get-Date } $catalog = @($script:InforcerReportTypeCache) # Filtering — single pass over the cache applying all 3 predicates short-circuit. $applyKey = $PSBoundParameters.ContainsKey('Key') -and -not [string]::IsNullOrWhiteSpace($Key) $applyTag = $PSBoundParameters.ContainsKey('Tag') -and -not [string]::IsNullOrWhiteSpace($Tag) $applyFormat = $PSBoundParameters.ContainsKey('OutputFormat') -and -not [string]::IsNullOrWhiteSpace($OutputFormat) if ($applyKey -or $applyTag -or $applyFormat) { $filtered = [System.Collections.Generic.List[object]]::new() :outer foreach ($entry in $catalog) { if ($applyKey) { $keyProp = $entry.PSObject.Properties['key'] if (-not $keyProp -or (($keyProp.Value -as [string]) -ine $Key)) { continue outer } } if ($applyTag) { $tagsProp = $entry.PSObject.Properties['tags'] $hit = $false if ($tagsProp) { # Handle both shapes the API may return: an array of tag strings, OR a single # comma-separated string (live observed). Split on commas defensively when we # see a string, trim whitespace, and match case-insensitively against each entry. foreach ($raw in @($tagsProp.Value)) { $rawStr = $raw -as [string] if (-not $rawStr) { continue } foreach ($part in ($rawStr -split ',')) { if ($part.Trim() -ieq $Tag) { $hit = $true; break } } if ($hit) { break } } } if (-not $hit) { continue outer } } if ($applyFormat) { $formats = $null foreach ($candidate in 'supportedOutputFormats','outputFormats','supportedFormats','formats') { if ($entry.PSObject.Properties[$candidate]) { $formats = $entry.PSObject.Properties[$candidate].Value break } } $hit = $false if ($formats) { foreach ($f in @($formats)) { if (($f -as [string]) -ieq $OutputFormat) { $hit = $true; break } } } if (-not $hit) { continue outer } } [void]$filtered.Add($entry) } $catalog = $filtered.ToArray() } # Apply aliases + PSTypeName for clean default display. foreach ($item in $catalog) { if ($item -is [PSObject]) { $null = Add-InforcerPropertyAliases -InputObject $item -ObjectType ReportType if ($item.PSObject.TypeNames[0] -ne 'InforcerCommunity.ReportType') { $item.PSObject.TypeNames.Insert(0, 'InforcerCommunity.ReportType') } } } $catalog } |