added boolean conversion helper and replaced code in scripts to use it
This commit is contained in:
+4
-12
@@ -21,18 +21,10 @@ If ( [string]::IsNullOrWhiteSpace([string]$DSAManifestSource) ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Normalize RMM text values without treating every nonempty string as true.
|
# Normalize RMM text values without treating every nonempty string as true.
|
||||||
$DsaOptions = @{ Zip = $Zip; Upload = $Upload; DeleteAfterUpload = $DeleteAfterUpload }
|
$DsaOptions = @{
|
||||||
ForEach ( $DsaOptionName in @('Zip', 'Upload', 'DeleteAfterUpload') ) {
|
Zip = ConvertTo-RmmBoolean -Value $Zip -VariableName 'Zip'
|
||||||
$DsaOptionValue = ([string]$DsaOptions[$DsaOptionName]).Trim().ToLowerInvariant()
|
Upload = ConvertTo-RmmBoolean -Value $Upload -VariableName 'Upload'
|
||||||
If ( $DsaOptionValue -in @('true', '$true', 'yes', 'y', '1') ) {
|
DeleteAfterUpload = ConvertTo-RmmBoolean -Value $DeleteAfterUpload -VariableName 'DeleteAfterUpload'
|
||||||
$DsaOptions[$DsaOptionName] = $true
|
|
||||||
}
|
|
||||||
ElseIf ( $DsaOptionValue -in @('', 'false', '$false', 'no', 'n', '0') ) {
|
|
||||||
$DsaOptions[$DsaOptionName] = $false
|
|
||||||
}
|
|
||||||
Else {
|
|
||||||
Throw ('Invalid ${0} value. Use true, $true, yes, y, 1, false, $false, no, n, or 0.' -f $DsaOptionName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$DsaZipEnabled = $DsaOptions.Zip
|
$DsaZipEnabled = $DsaOptions.Zip
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,25 @@ For download failures, use the standalone [Test-FileDownload.ps1](Test-FileDownl
|
|||||||
|
|
||||||
`Get-DSAManifest.ps1` uses `Download-FileDirectly`, which passes the original URL to BITS without preliminary `Get-AbsoluteURI` or `IsURLValid` requests. It accepts the same URL and optional `-File` arguments as `Download-File`, waits for completion, and throws on BITS errors. If `-File` is omitted, its temporary filename comes from the original URL; supply `-File` for extensionless URLs or URLs with query strings. Existing `Download-File` callers retain their previous behavior. Publish `Tools.ps1` together with `Get-DSAManifest.ps1` so the new helper is available.
|
`Get-DSAManifest.ps1` uses `Download-FileDirectly`, which passes the original URL to BITS without preliminary `Get-AbsoluteURI` or `IsURLValid` requests. It accepts the same URL and optional `-File` arguments as `Download-File`, waits for completion, and throws on BITS errors. If `-File` is omitted, its temporary filename comes from the original URL; supply `-File` for extensionless URLs or URLs with query strings. Existing `Download-File` callers retain their previous behavior. Publish `Tools.ps1` together with `Get-DSAManifest.ps1` so the new helper is available.
|
||||||
|
|
||||||
|
## Converting RMM boolean variables
|
||||||
|
|
||||||
|
After dot-sourcing `Tools.ps1`, use `ConvertTo-RmmBoolean` with any RMM variable:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$RestartEnabled = ConvertTo-RmmBoolean -Value $Restart -VariableName 'Restart'
|
||||||
|
If ( $RestartEnabled ) {
|
||||||
|
# Perform the requested restart.
|
||||||
|
}
|
||||||
|
|
||||||
|
# The variable name is optional; positional and pipeline input also work.
|
||||||
|
$ZipEnabled = ConvertTo-RmmBoolean $Zip
|
||||||
|
$UploadEnabled = $Upload | ConvertTo-RmmBoolean
|
||||||
|
```
|
||||||
|
|
||||||
|
Each input returns a `System.Boolean`: `true`, `$true`, `yes`, `y`, and `1` become `$true`; `false`, `$false`, `no`, `n`, `0`, blank strings, and `$null` become `$false`. Case and surrounding whitespace are ignored, and native boolean values also work. Other values throw an error; `-VariableName` identifies the input in that error. The function returns the converted value without changing the original variable; assign the result wherever needed.
|
||||||
|
|
||||||
|
`Get-DSAManifest.ps1` uses this helper for its three options. Publish it together with the updated `Tools.ps1` so the helper is available to the RMM caller.
|
||||||
|
|
||||||
## Uploading files
|
## Uploading files
|
||||||
|
|
||||||
After dot-sourcing `Tools.ps1`, scripts can upload a completed file to the Emberkom FileDrop:
|
After dot-sourcing `Tools.ps1`, scripts can upload a completed file to the Emberkom FileDrop:
|
||||||
@@ -44,4 +63,5 @@ Offline tests (no uploads or notifications):
|
|||||||
powershell.exe -NoProfile -File .\tests\Upload-File.Tests.ps1
|
powershell.exe -NoProfile -File .\tests\Upload-File.Tests.ps1
|
||||||
powershell.exe -NoProfile -File .\tests\Get-DSAManifest.Tests.ps1
|
powershell.exe -NoProfile -File .\tests\Get-DSAManifest.Tests.ps1
|
||||||
powershell.exe -NoProfile -File .\tests\Download-File.Tests.ps1
|
powershell.exe -NoProfile -File .\tests\Download-File.Tests.ps1
|
||||||
|
powershell.exe -NoProfile -File .\tests\ConvertTo-RmmBoolean.Tests.ps1
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -180,6 +180,45 @@ Function Get-Version {
|
|||||||
Return [version]$Return.Trim('.')
|
Return [version]$Return.Trim('.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Convert an RMM text variable into an actual Boolean value.
|
||||||
|
Function ConvertTo-RmmBoolean {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Converts an RMM text value to a System.Boolean.
|
||||||
|
.DESCRIPTION
|
||||||
|
Accepts true, $true, yes, y, or 1; false, $false, no, n, or 0.
|
||||||
|
Matching ignores case and surrounding whitespace. Null and empty values
|
||||||
|
return false. Unrecognized values throw instead of silently enabling an option.
|
||||||
|
.PARAMETER Value
|
||||||
|
The value to convert. Native Boolean values are also accepted.
|
||||||
|
.PARAMETER VariableName
|
||||||
|
Optional RMM variable name, without the leading $, for error messages.
|
||||||
|
.EXAMPLE
|
||||||
|
$ZipEnabled = ConvertTo-RmmBoolean -Value $Zip -VariableName 'Zip'
|
||||||
|
.EXAMPLE
|
||||||
|
ConvertTo-RmmBoolean ' No '
|
||||||
|
#>
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([bool])]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]
|
||||||
|
[AllowNull()]
|
||||||
|
[AllowEmptyString()]
|
||||||
|
[string]$Value,
|
||||||
|
[string]$VariableName = 'Value'
|
||||||
|
)
|
||||||
|
process {
|
||||||
|
$NormalizedValue = ([string]$Value).Trim().ToLowerInvariant()
|
||||||
|
If ( $NormalizedValue -in @('true', '$true', 'yes', 'y', '1') ) {
|
||||||
|
Return $true
|
||||||
|
}
|
||||||
|
If ( $NormalizedValue -in @('', 'false', '$false', 'no', 'n', '0') ) {
|
||||||
|
Return $false
|
||||||
|
}
|
||||||
|
Throw ('Invalid ${0} value. Use true, $true, yes, y, 1, false, $false, no, n, or 0.' -f $VariableName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Determines whether a URL is valid
|
# Determines whether a URL is valid
|
||||||
Function IsURLValid {
|
Function IsURLValid {
|
||||||
param([Parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$URL)
|
param([Parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$URL)
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#Requires -Version 5.0
|
||||||
|
# Offline tests: load only the helper to avoid Tools.ps1 setup and logging.
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$BooleanToolsPath = Join-Path (Split-Path $PSScriptRoot -Parent) 'Tools.ps1'
|
||||||
|
$BooleanTokens = $null
|
||||||
|
$BooleanParseErrors = $null
|
||||||
|
$BooleanAst = [Management.Automation.Language.Parser]::ParseFile($BooleanToolsPath, [ref]$BooleanTokens, [ref]$BooleanParseErrors)
|
||||||
|
If ($BooleanParseErrors.Count) { Throw ($BooleanParseErrors | Out-String) }
|
||||||
|
$BooleanDefinition = $BooleanAst.Find({
|
||||||
|
param($Node)
|
||||||
|
$Node -is [Management.Automation.Language.FunctionDefinitionAst] -and $Node.Name -eq 'ConvertTo-RmmBoolean'
|
||||||
|
}, $true)
|
||||||
|
If (!$BooleanDefinition) { Throw 'Missing helper: ConvertTo-RmmBoolean' }
|
||||||
|
. ([scriptblock]::Create($BooleanDefinition.Extent.Text))
|
||||||
|
|
||||||
|
# Tools.ps1 overrides Write-Output with a string parameter. Boolean results must
|
||||||
|
# bypass that wrapper so $false remains a Boolean, without log text or side effects.
|
||||||
|
Function Write-Output { Throw 'Conversion must not use the string-based logging wrapper.' }
|
||||||
|
|
||||||
|
$BooleanCases = @(
|
||||||
|
@{Values=@('true', '$true', 'yes', 'y', '1', $true, 1); Expected=$true}
|
||||||
|
@{Values=@('false', '$false', 'no', 'n', '0', '', $null, $false, 0); Expected=$false}
|
||||||
|
)
|
||||||
|
Foreach ($Case in $BooleanCases) {
|
||||||
|
Foreach ($Value in $Case.Values) {
|
||||||
|
$Variants = @($Value)
|
||||||
|
If ($Value -is [string]) { $Variants += " `t$($Value.ToUpperInvariant())`r`n " }
|
||||||
|
Foreach ($Variant in $Variants) {
|
||||||
|
$Result = @(ConvertTo-RmmBoolean -Value $Variant)
|
||||||
|
If ($Result.Count -ne 1 -or $Result[0] -isnot [bool] -or $Result[0] -ne $Case.Expected) {
|
||||||
|
Throw "Input '$Variant' must return exactly one Boolean with value $($Case.Expected)."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host 'PASS: accepted text, case, whitespace, null, native booleans, and numeric 0/1 return one Boolean.'
|
||||||
|
|
||||||
|
$RmmInput = ' no '
|
||||||
|
$Result = ConvertTo-RmmBoolean $RmmInput
|
||||||
|
If ($Result -isnot [bool] -or $Result -ne $false -or $RmmInput -cne ' no ') {
|
||||||
|
Throw 'Positional conversion must return false without changing the original input.'
|
||||||
|
}
|
||||||
|
$Result = @(' yes ', 'false', '', $null, '$TRUE', '0' | ConvertTo-RmmBoolean)
|
||||||
|
$Expected = @($true, $false, $false, $false, $true, $false)
|
||||||
|
If ($Result.Count -ne $Expected.Count) { Throw 'Pipeline conversion must return one value for every input.' }
|
||||||
|
For ($Index = 0; $Index -lt $Expected.Count; $Index++) {
|
||||||
|
If ($Result[$Index] -isnot [bool] -or $Result[$Index] -ne $Expected[$Index]) {
|
||||||
|
Throw "Pipeline conversion returned the wrong type or value at index $Index."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host 'PASS: positional input, preserved caller value, and multiple pipeline inputs.'
|
||||||
|
|
||||||
|
# RMM scripts may use Continue and omit -ErrorAction. Invalid input must still stop.
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
Foreach ($Invalid in @('maybe', 'on', 'off', '2', 'true false', '$null')) {
|
||||||
|
Foreach ($Parameters in @(@{}, @{VariableName='Restart'})) {
|
||||||
|
$Caught = $null
|
||||||
|
$Returned = @()
|
||||||
|
Try { ConvertTo-RmmBoolean -Value $Invalid @Parameters | ForEach-Object { $Returned += $_ } }
|
||||||
|
Catch { $Caught = $_ }
|
||||||
|
$Name = If ($Parameters.ContainsKey('VariableName')) { 'Restart' } Else { 'Value' }
|
||||||
|
If ($null -eq $Caught -or !$Caught.Exception.Message.StartsWith(('Invalid ${0} value.' -f $Name))) {
|
||||||
|
Throw "Invalid input '$Invalid' must throw an error identifying $Name."
|
||||||
|
}
|
||||||
|
If ($Returned.Count -ne 0) { Throw 'Invalid input must not return a value.' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host 'PASS: invalid input throws with default or caller-supplied variable name.'
|
||||||
|
Write-Host "All ConvertTo-RmmBoolean tests passed on PowerShell $($PSVersionTable.PSVersion), $([IntPtr]::Size * 8)-bit."
|
||||||
@@ -8,6 +8,17 @@ $DsaErrors = $null
|
|||||||
$null = [Management.Automation.Language.Parser]::ParseFile($DsaScriptUnderTest, [ref]$DsaTokens, [ref]$DsaErrors)
|
$null = [Management.Automation.Language.Parser]::ParseFile($DsaScriptUnderTest, [ref]$DsaTokens, [ref]$DsaErrors)
|
||||||
If ($DsaErrors.Count) { Throw ($DsaErrors | Out-String) }
|
If ($DsaErrors.Count) { Throw ($DsaErrors | Out-String) }
|
||||||
|
|
||||||
|
# Load only the real conversion helper, without running Tools.ps1 setup.
|
||||||
|
$DsaToolsPath = Join-Path (Split-Path $PSScriptRoot -Parent) 'Tools.ps1'
|
||||||
|
$DsaToolsAst = [Management.Automation.Language.Parser]::ParseFile($DsaToolsPath, [ref]$DsaTokens, [ref]$DsaErrors)
|
||||||
|
If ($DsaErrors.Count) { Throw ($DsaErrors | Out-String) }
|
||||||
|
$DsaBooleanDefinition = $DsaToolsAst.Find({
|
||||||
|
param($Node)
|
||||||
|
$Node -is [Management.Automation.Language.FunctionDefinitionAst] -and $Node.Name -eq 'ConvertTo-RmmBoolean'
|
||||||
|
}, $true)
|
||||||
|
If (!$DsaBooleanDefinition) { Throw 'Missing helper: ConvertTo-RmmBoolean' }
|
||||||
|
. ([scriptblock]::Create($DsaBooleanDefinition.Extent.Text))
|
||||||
|
|
||||||
Function Assert-Dsa { param([bool]$Condition, [string]$Description) If (!$Condition) { Throw $Description } }
|
Function Assert-Dsa { param([bool]$Condition, [string]$Description) If (!$Condition) { Throw $Description } }
|
||||||
Function Download-File { Throw 'DSA must use Download-FileDirectly.' }
|
Function Download-File { Throw 'DSA must use Download-FileDirectly.' }
|
||||||
Function Download-FileDirectly {
|
Function Download-FileDirectly {
|
||||||
|
|||||||
Reference in New Issue
Block a user