Skip to content

Commit 96dcb86

Browse files
committed
Filter schema validation for all Get cmdlets
1 parent 8079f01 commit 96dcb86

File tree

90 files changed

+26102
-337
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+26102
-337
lines changed

Build-LMFilterValidationConfig.ps1

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
<#
2+
.SYNOPSIS
3+
Development tool to generate filter field validation configuration from swagger YAML.
4+
5+
.DESCRIPTION
6+
This script parses the logicmonitor-api.yaml file to extract all API endpoints and their
7+
response schemas. It then maps each endpoint to the properties available in the response
8+
model, creating a validation configuration file that can be used at runtime to validate
9+
filter fields before sending API requests.
10+
11+
.EXAMPLE
12+
.\Build-LMFilterValidationConfig.ps1
13+
14+
This will generate Private/LMFilterValidationConfig.psd1 with all endpoint-to-fields mappings.
15+
16+
.NOTES
17+
This is a development-time tool and should be run whenever the swagger file is updated.
18+
Requires the powershell-yaml module for parsing YAML files.
19+
#>
20+
21+
[CmdletBinding()]
22+
param()
23+
24+
# Check if powershell-yaml module is available
25+
if (-not (Get-Module -ListAvailable -Name powershell-yaml)) {
26+
Write-Warning "The powershell-yaml module is required to parse the swagger YAML file."
27+
Write-Warning "Install it with: Install-Module -Name powershell-yaml"
28+
Write-Warning "Attempting to install now..."
29+
try {
30+
Install-Module -Name powershell-yaml -Scope CurrentUser -Force -AllowClobber
31+
Write-Host "Successfully installed powershell-yaml module" -ForegroundColor Green
32+
}
33+
catch {
34+
Write-Error "Failed to install powershell-yaml module. Please install it manually."
35+
return
36+
}
37+
}
38+
39+
Import-Module powershell-yaml
40+
41+
$SwaggerPath = Join-Path $PSScriptRoot "logicmonitor-api.yaml"
42+
$OutputPath = Join-Path $PSScriptRoot "Private/LMFilterValidationConfig.psd1"
43+
44+
if (-not (Test-Path $SwaggerPath)) {
45+
Write-Error "Swagger file not found at: $SwaggerPath"
46+
return
47+
}
48+
49+
Write-Host "Parsing swagger file: $SwaggerPath" -ForegroundColor Cyan
50+
51+
# Parse the YAML file
52+
$SwaggerContent = Get-Content -Path $SwaggerPath -Raw
53+
$Swagger = ConvertFrom-Yaml -Yaml $SwaggerContent
54+
55+
Write-Host "Extracting API endpoints and schemas..." -ForegroundColor Cyan
56+
57+
# Build endpoint to schema mapping
58+
$EndpointToSchema = @{}
59+
$SchemaProperties = @{}
60+
61+
# First, extract all schema properties
62+
foreach ($schemaName in $Swagger.components.schemas.Keys) {
63+
$schema = $Swagger.components.schemas[$schemaName]
64+
$properties = @()
65+
66+
if ($schema.properties) {
67+
$properties += $schema.properties.Keys
68+
}
69+
70+
# Handle allOf (inheritance)
71+
if ($schema.allOf) {
72+
foreach ($allOfItem in $schema.allOf) {
73+
if ($allOfItem.properties) {
74+
$properties += $allOfItem.properties.Keys
75+
}
76+
# Handle $ref in allOf
77+
if ($allOfItem.'$ref') {
78+
$refSchema = $allOfItem.'$ref' -replace '#/components/schemas/', ''
79+
# We'll resolve these in a second pass
80+
}
81+
}
82+
}
83+
84+
if ($properties.Count -gt 0) {
85+
$SchemaProperties[$schemaName] = $properties | Sort-Object -Unique
86+
}
87+
}
88+
89+
Write-Host "Found $($SchemaProperties.Count) schemas with properties" -ForegroundColor Green
90+
91+
# Now map endpoints to their response schemas
92+
foreach ($path in $Swagger.paths.Keys) {
93+
$pathItem = $Swagger.paths[$path]
94+
95+
# We're primarily interested in GET endpoints for filtering
96+
if ($pathItem.get) {
97+
$getOp = $pathItem.get
98+
99+
# Check if it has a filter parameter
100+
$hasFilter = $false
101+
if ($getOp.parameters) {
102+
foreach ($param in $getOp.parameters) {
103+
if ($param.name -eq 'filter') {
104+
$hasFilter = $true
105+
break
106+
}
107+
}
108+
}
109+
110+
if ($hasFilter) {
111+
# Extract the response schema
112+
if ($getOp.responses.'200'.content.'application/json'.schema.'$ref') {
113+
$responseSchemaRef = $getOp.responses.'200'.content.'application/json'.schema.'$ref'
114+
$responseSchemaName = $responseSchemaRef -replace '#/components/schemas/', ''
115+
116+
# For pagination responses, we need to get the items schema
117+
if ($responseSchemaName -match 'PaginationResponse$') {
118+
$paginationSchema = $Swagger.components.schemas[$responseSchemaName]
119+
if ($paginationSchema.properties.items.items.'$ref') {
120+
$itemsSchemaRef = $paginationSchema.properties.items.items.'$ref'
121+
$itemsSchemaName = $itemsSchemaRef -replace '#/components/schemas/', ''
122+
123+
if ($SchemaProperties[$itemsSchemaName]) {
124+
$EndpointToSchema[$path] = @{
125+
Schema = $itemsSchemaName
126+
Properties = $SchemaProperties[$itemsSchemaName]
127+
}
128+
}
129+
}
130+
}
131+
# For non-pagination responses, use the schema directly
132+
elseif ($SchemaProperties[$responseSchemaName]) {
133+
$EndpointToSchema[$path] = @{
134+
Schema = $responseSchemaName
135+
Properties = $SchemaProperties[$responseSchemaName]
136+
}
137+
}
138+
}
139+
}
140+
}
141+
}
142+
143+
Write-Host "Mapped $($EndpointToSchema.Count) endpoints to schemas" -ForegroundColor Green
144+
145+
# Build the final configuration hashtable
146+
$Config = @{}
147+
148+
foreach ($endpoint in $EndpointToSchema.Keys | Sort-Object) {
149+
$properties = $EndpointToSchema[$endpoint].Properties
150+
$schema = $EndpointToSchema[$endpoint].Schema
151+
152+
# Add special property fields that are always valid for filtering
153+
$specialProperties = @('customProperties', 'systemProperties', 'autoProperties', 'inheritedProperties')
154+
$allProperties = @($properties) + $specialProperties | Sort-Object -Unique
155+
156+
$Config[$endpoint] = $allProperties
157+
158+
Write-Verbose "Endpoint: $endpoint -> Schema: $schema -> Properties: $($properties.Count)"
159+
}
160+
161+
# Generate the PSD1 file
162+
Write-Host "Generating configuration file: $OutputPath" -ForegroundColor Cyan
163+
164+
$PSD1Content = @"
165+
<#
166+
.SYNOPSIS
167+
Filter field validation configuration generated from logicmonitor-api.yaml
168+
169+
.DESCRIPTION
170+
This file contains a mapping of API endpoints to their valid filterable fields.
171+
It is automatically generated by Build-LMFilterValidationConfig.ps1 and should not be manually edited.
172+
173+
Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
174+
Swagger endpoints processed: $($EndpointToSchema.Count)
175+
176+
.NOTES
177+
To regenerate this file, run: .\Build-LMFilterValidationConfig.ps1
178+
#>
179+
180+
@{
181+
"@
182+
183+
foreach ($endpoint in $Config.Keys | Sort-Object) {
184+
$properties = $Config[$endpoint]
185+
$propertiesString = ($properties | ForEach-Object { "'$_'" }) -join ', '
186+
$PSD1Content += "`n '$endpoint' = @($propertiesString)"
187+
}
188+
189+
$PSD1Content += "`n}`n"
190+
191+
# Write the file
192+
Set-Content -Path $OutputPath -Value $PSD1Content -Encoding UTF8
193+
194+
Write-Host "Successfully generated configuration file!" -ForegroundColor Green
195+
Write-Host " Endpoints: $($Config.Keys.Count)" -ForegroundColor Green
196+
Write-Host " Output: $OutputPath" -ForegroundColor Green
197+
198+
# Display some sample mappings
199+
Write-Host "`nSample mappings:" -ForegroundColor Cyan
200+
$sampleEndpoints = @('/device/devices', '/device/groups', '/alert/alerts') | Where-Object { $Config[$_] }
201+
foreach ($endpoint in $sampleEndpoints) {
202+
Write-Host " $endpoint" -ForegroundColor Yellow
203+
Write-Host " Fields: $($Config[$endpoint] -join ', ')" -ForegroundColor Gray
204+
}
205+

Private/Format-LMFilter-v1.ps1

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,38 @@
11
<#
22
.SYNOPSIS
3-
This function formats a filter for Logic Monitor API calls.
3+
This function formats a filter for Logic Monitor API calls (legacy v1 format).
44
55
.DESCRIPTION
6-
The Format-LMFilter-v1 function takes a hashtable of filter properties and an optional array of valid properties.
7-
It checks if the supplied properties are valid, removes any invalid properties, and then formats the remaining properties into a filter string.
6+
The Format-LMFilter-v1 function takes a hashtable of filter properties and formats them into a filter string.
7+
This is the legacy format for hashtable-based filters. Field validation is performed by the parent Format-LMFilter function.
88
99
.PARAMETER Filter
1010
A hashtable of filter properties. This is a mandatory parameter.
1111
12-
.PARAMETER PropList
13-
An array of valid properties. If this parameter is provided, the function checks the properties in the Filter parameter against this list and removes any that are not valid.
14-
1512
.EXAMPLE
16-
Format-LMFilter-v1 -Filter $Filter -PropList $PropList
13+
Format-LMFilter-v1 -Filter @{name='test'; displayName='Test Device'}
1714
18-
This command formats the filter properties represented by the $Filter hashtable into a filter string, removing any properties that are not in the $PropList array.
15+
This command formats the filter properties represented by the $Filter hashtable into a filter string.
1916
2017
.INPUTS
21-
System.Collections.Hashtable, System.String[]. You can pipe a hashtable of filter properties and an array of valid properties to Format-LMFilter-v1.
18+
System.Collections.Hashtable. You can pipe a hashtable of filter properties to Format-LMFilter-v1.
2219
2320
.OUTPUTS
2421
System.String. The function returns a string that represents the formatted filter.
2522
2623
.NOTES
27-
If a property in the Filter parameter is not in the PropList parameter, it is simply removed from the filter.
24+
This function is called internally by Format-LMFilter for hashtable filters.
25+
Field validation is performed before this function is called.
2826
#>
2927
function Format-LMFilter-v1 {
3028
[CmdletBinding()]
3129
param (
32-
[Hashtable]$Filter,
33-
34-
[String[]]$PropList
30+
[Hashtable]$Filter
3531
)
3632

3733
#Initalize variable for final filter string
3834
$FilterString = ""
3935

40-
#Check if supplied properties are valid, if no prop list then just assume valid
41-
if ($PropList) {
42-
foreach ($Key in $($Filter.keys)) {
43-
if ($Key -notin $PropList) {
44-
#Remove key since its not a valid filter property
45-
$filter.remove($Key)
46-
}
47-
}
48-
}
49-
5036
#Create filter string from hash table and url encode
5137
foreach ($Key in $($Filter.keys)) {
5238
$FilterString += $Key + ":" + "`"$($Filter[$Key])`"" + ","

Private/Format-LMFilter.ps1

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
Formats the LogicMonitor filter for API requests.
44
55
.DESCRIPTION
6-
The Format-LMFilter function is used to format the LogicMonitor filter for API requests. It takes an input filter object and an optional list of properties to include in the formatted filter. The function supports both legacy and new filter formats.
6+
The Format-LMFilter function is used to format the LogicMonitor filter for API requests. It takes an input filter object and validates the field names against the API schema before formatting. The function supports both legacy hashtable and new string filter formats.
77
88
.PARAMETER Filter
99
The input filter object. It can be a hashtable or a string.
1010
11-
.PARAMETER PropList
12-
An optional list of properties to include in the formatted filter. The default value is an array containing the following properties: "name", "id", "status", "severity", "startEpoch", "endEpoch", "cleared", "resourceTemplateName", "monitorObjectName", "customProperties", "systemProperties", "autoProperties", "displayName".
11+
.PARAMETER ResourcePath
12+
The API endpoint path (e.g., '/device/devices') used to validate filter fields against the schema.
1313
1414
.OUTPUTS
1515
The formatted filter string.
@@ -19,28 +19,33 @@ $filter = @{
1919
name = "MyMonitor"
2020
status = "active"
2121
}
22-
$formattedFilter = Format-LMFilter -Filter $filter
22+
$formattedFilter = Format-LMFilter -Filter $filter -ResourcePath "/device/devices"
2323
Write-Host $formattedFilter
2424
# Output: name:"MyMonitor",status:"active"
2525
2626
.EXAMPLE
27-
$filter = "name -eq 'MyMonitor' -and status -eq 'active'"
28-
$formattedFilter = Format-LMFilter -Filter $filter
27+
$filter = "name -eq 'MyMonitor' -and displayName -eq 'active'"
28+
$formattedFilter = Format-LMFilter -Filter $filter -ResourcePath "/device/devices"
2929
Write-Host $formattedFilter
30-
# Output: name:"MyMonitor",status:"active"
30+
# Output: name:"MyMonitor",displayName:"active"
3131
#>
3232

3333
function Format-LMFilter {
3434
[CmdletBinding()]
3535
param (
36+
[Parameter(Mandatory)]
3637
[Object]$Filter,
3738

38-
[String[]]$PropList = @("name", "id", "status", "severity", "startEpoch", "endEpoch", "cleared", "resourceTemplateName", "monitorObjectName", "customProperties", "systemProperties", "autoProperties", "displayName")
39+
[Parameter(Mandatory)]
40+
[String]$ResourcePath
3941
)
42+
# Validate filter fields before formatting
43+
Test-LMFilterField -Filter $Filter -ResourcePath $ResourcePath
44+
4045
$FormatedFilter = ""
4146
#Keep legacy filter method for backwards compatability
4247
if ($Filter -is [hashtable]) {
43-
$FormatedFilter = Format-LMFilter-v1 -Filter $Filter -PropList $PropList
48+
$FormatedFilter = Format-LMFilter-v1 -Filter $Filter
4449
Write-Debug "Constructed Filter-v1: $FormatedFilter"
4550
return $FormatedFilter
4651
}

0 commit comments

Comments
 (0)