Home > Blockchain >  Using ValidateSet with functions or List
Using ValidateSet with functions or List

Time:09-23

I'm trying to do something like this. The Get-SomeOtherParameter returns a system.Array type list from a database.

I don't want to hardcode my ValidateSet in case the list changes overTime in the database

function Get-SomeItems { 
    param (
        [Parameter(Mandatory = $true)]
        [ValidateSet(Get-SomeOtherParameter)] 
        [string]$filter,

        [Parameter(Mandatory = $true)]
        [ValidateSet('abc', 'def', 'ghi')] 
        [String]$filter2
    )
}

CodePudding user response:

There's two aspects to what you're trying to do:

  1. Making sure the parameter validation is correct
  2. Making the PowerShell experience around it "good" (aka supporting tab completion).

Parameter Validation :

As you might have already noticed [ValidateSet] is a hard-coded list. It's not really possible to soft code this (it is possible to dynamically build your script every time using some other modules, lemme know if you want more of an explainer for this).

To make the Validation work without [ValidateSet], I'd suggest [ValidateScript({})]. [ValidateScript] will run whatever script is in ValidateScript to ensure the script is valid. If the [ValidateScript()] throws, the user will see that message when they pass an invalid value in.

Tab-Completion :

To make it feel easy, you'll also want to add support for tab completion.

This is fairly straightforward using the [ArgumentCompleter] attribute.

Here's an example copied / pasted from a module called LightScript

[ArgumentCompleter({
        param ( $commandName,
            $parameterName,
            $wordToComplete,
            $commandAst,
            $fakeBoundParameters )
        $effectNames = @(Get-NanoLeaf -ListEffectName | 
            Select-Object -Unique) 
        if ($wordToComplete) {        
            $toComplete = $wordToComplete -replace "^'" -replace "'$"
            return @($effectNames -like "$toComplete*" -replace '^', "'" -replace '$',"'")
        } else {
            return @($effectNames -replace '^', "'" -replace '$',"'")
        }
    })]

This ArgumentCompleter does a few things:

  1. Calls some other command to get a list of effects
  2. If $wordToComplete was passed, finds all potential completions (while stripping off whitespace and enclosing in quotes)
  3. If $WordToComplete was not passed, puts each potential completion in quotes

Basically, all you should need to change are the command names / variables to make this work.

Hope this Helps

CodePudding user response:

To complement Start-Automating's helpful answer by spelling out the
[ValidateScript({ ... }] and [ArgumentCompleter({ ... }) approaches:

# Function that returns the valid values for the -filter parameter below.
function Get-ValidFilterValues { 
  # Sample, hard-coded values. This is where your database lookup would happen.
  'foo', 'bar'
}

function Get-SomeItems {
  param (
      [Parameter(Mandatory)]
      [ValidateScript({
        $validValues = Get-ValidFilterValues
        if ($_ -in $validValues) { return $true } # OK
        throw "'$_' is not a valid value. Use one of the following: '$($validValues -join ', ')'"
      })]
      [ArgumentCompleter({
        param($cmd, $param, $wordToComplete)
        (Get-ValidFilterValues) -like "$wordToComplete*" 
      })]      
      [string]$filter,

      [Parameter(Mandatory)]
      [ValidateSet('abc', 'def', 'ghi')] 
      [String]$filter2
  )

  $filter, $filter2 # sample output.

}

A simpler PowerShell (Core) 7 alternative is to implement validation via a custom class that implements the System.Management.Automation.IValidateSetValuesGenerator interface, which automatically also provides tab-completion:

# Custom class that implements the IValidateSetValuesGenerator interface 
# in order to return the valid values for the -filter parameter below.
class ValidFilterValues : System.Management.Automation.IValidateSetValuesGenerator { 
  [string[]] GetValidValues() { 
    # Sample, hard-coded values. This is where your database lookup would happen.
    return 'foo', 'bar'
  }
}

function Get-SomeItems {
  param (
      [Parameter(Mandatory)]
      [ValidateSet([ValidFilterValues])] # Pass the custom class defined above.
      [string]$filter,

      [Parameter(Mandatory)]
      [ValidateSet('abc', 'def', 'ghi')] 
      [String]$filter2
  )

  $filter, $filter2 # sample output.

}
  • Related