Question [fonction] Validation des jeux de paramètre

Plus d'informations
il y a 9 ans 9 mois #17557 par Laurent Dardenne
Voici une fonction de validation des jeux de paramètre d'une fonction ou d'un cmdlet.
Powershell ne propose pas de contrôle sur ce type d'erreur, il est donc à la charge du codeur.
[code:1]
#Requires -Version 3
Function Test-ParameterSet{
<#
.SYNOPSIS
Détermine si les jeux de paramètres d'une commande sont valides.
Un jeux de paramètres valide doit contenir au moins un paramètre unique et
les numéros de positions de ses paramètres doivent se suivre et ne pas être dupliqué.
Les noms de paramètres débutant par un chiffre invalideront le test.
#>
param (
#Nom de la commande à tester
[parameter(Mandatory=$True,ValueFromPipeline=$True)]
[string]$Command
)
begin {
[string[]] $CommonParameters=[System.Management.Automation.Internal.CommonParameters].GetProperties().Name

function Test-Sequential{
#Prerequis: la collection doit être triée
param([int[]]$List)
$Count=$List.Count
for ($i = 1; $i -lt $Count; $i++)
{
if ($List[$i] -ne $List[$i - 1] + 1)
{return $false}
}
return $true
}# Test-Sequential

Function New-ParameterSetValidation{
param(
[Parameter(Mandatory=$True,position=0)]
$ModuleName,
[Parameter(Mandatory=$True,position=1)]
$CommandName,
[Parameter(Mandatory=$True,position=2)]
$ParameterSetName,
[Parameter(Mandatory=$True,position=3)]
$isValid,
[Parameter(Mandatory=$True,position=4)]
$Report
)

[pscustomobject]@{
PSTypeName='ParameterSetValidation';
#Nom du module (optionnel)
ModuleName=$ModuleName;
#Nom de la fonction ou du cmdlet testé
CommandName=$CommandName;
#Si aucun jeux n'est déclaré le jeu par défaut se nomme '__AllParameterSets'
#Si aucun jeux n'est déclaré et que l'attribut CmdletBinding déclare
#le paramètre DefaultParameterSetName, alors le jeu par défaut est égale au
# nom indiqué par DefaultParameterSetName
ParameterSetName=$ParameterSetName;
#Le jeux est considéré comme valide selon les régles implémentées.
isValid=$isValid;
#Détails des régles implémentées.
Report=$Report;
}
}# New-ParameterSetValidation

Function New-ParameterSetReport{
#Mémorise les informations.
#Utiles en cas de construction de rapport
param(
[Parameter(Mandatory=$True,position=0)]
#Liste des paramètres du jeu courant
$Params,
[Parameter(Mandatory=$True,position=1)]
#Liste des paramètres n'appartenant pas au jeu courant
$Others,
[Parameter(Mandatory=$True,position=2)]
#Liste de numéros de position des paramètres du jeu courant
$Positions,
[Parameter(Mandatory=$True,position=3)]
#Liste des paramètres commençant par un chiffre
$InvalidParameterName,
[Parameter(Mandatory=$True,position=4)]
#Un jeux de paramètre valide doit avoir un paramètre unique
#n'appartenant à aucun autre jeu
$isHasUniqueParameter,
[Parameter(Mandatory=$True,position=5)]
#les numéros de position ne doivent pas être dupliqués
$isPositionContainsDuplicate,
[Parameter(Mandatory=$True,position=6)]
#les numéros de position doivent se suivre séquentiellement
$isPositionSequential,
[Parameter(Mandatory=$True,position=7)]
#Combinaison des deux champs précédents
$isPositionValid,
[Parameter(Mandatory=$True,position=8)]
#Un nom de paramètre ne doit pas commencer par un chiffre
$isContainsInvalidParameter,
[Parameter(Mandatory=$True,position=9)]
#Les noms de jeux de paramètre doivent être unique
#PS autorise des noms de jeux sensible à la casse
$isParameterSetNameDuplicate
)

[pscustomobject]@{
PSTypeName='ParameterSetReport';
Params=$Params;
Others=$Others;
Positions=$Positions;
InvalidParameterName=$InvalidParameterName;
isHasUniqueParameter=$isHasUniqueParameter;
isPositionContainsDuplicate=$isPositionContainsDuplicate;
isPositionSequential=$isPositionSequential;
isPositionValid=$isPositionValid;
isContainsInvalidParameter=$isContainsInvalidParameter;
isParameterSetNameDuplicate=$isParameterSetNameDuplicate
}
}# New-ParameterSetReport
}#begin

process {
$Cmd=Get-Command $Command
Write-Debug \"Test $Command\"

#bug PS : connect.microsoft.com/PowerShell/feedbac...-an-unknow-data-type
$oldEAP,$ErrorActionPreference=$ErrorActionPreference,'Stop'
$SetCount=$Cmd.get_ParameterSets().count
$ErrorActionPreference=$oldEAP

$_AllNames=@($Cmd.ParameterSets|
Foreach {
$PrmStName=$_.Name
$P=$_.Parameters|Foreach {$_.Name}|Where {$_ -notin $CommonParameters}
Write-Debug \"Build $PrmStName $($P.Count)\"
if (($P.Count) -eq 0)
{ Write-Warning \"[$($Cmd.Name)]: the parameter set '$PrmStName' is empty.\" }
$P
})

$NamesOfParameterSet=$Cmd.ParameterSets.Name
# if ($_AllNames.Count -eq 0 )
# { return $Sets }

#Contient les noms des paramètres de tous les jeux
#Les noms peuvent être dupliqués
$AllNames=new-object System.Collections.ArrayList(,$_AllNames)
$ParametersStartsWithNumber=new-object System.Collections.ArrayList

$Cmd.ParameterSets|
foreach {
$ParameterSetName=$_.Name
Write-Debug \"Validate ParameterSet '$ParameterSetName'.\"

$isParameterSetNameDuplicate =@($NamesOfParameterSet -eq $ParameterSetName).Count -gt 1

#Contient tous les noms de paramètre du jeux courant
$Params=new-object System.Collections.ArrayList
#Contient les positions des paramètres du jeux courant
$Positions=new-object System.Collections.ArrayList
$Others=$AllNames.Clone()

$_.Parameters|
Where {$_.Name -notin $CommonParameters}|
Foreach {
$ParameterName=$_.Name
Write-debug \"Add $ParameterName $($_.Position)\"
$Params.Add($ParameterName) > $null
$Positions.Add($_.Position) > $null
#Les constructions suivantes sont possibles, mais pas testées : ${der[nier}, ${-1}, etc.
if ($ParameterName -match \"^\d\" )
{
Write-debug \"Invalide parameter name '$ParameterName'\"
$ParametersStartsWithNumber.Add($ParameterName) > $null
}
}

#Supprime dans la collection globale
#les noms de paramètres du jeux courant
$Params|
Foreach {
Write-Debug \"Remove $_\"
$Others.Remove($_)
}

#Supprime les valeurs des positions par défaut
$FilterPositions=$Positions|Where {$_ -ge 0}
#Get-Unique attend une collection triée
$SortedPositions=$FilterPositions|Sort-Object
$isDuplicate= -not (@($SortedPositions|Get-Unique).Count -eq $FilterPositions.Count)
$isSequential= Test-Sequential $SortedPositions

$isPositionValid=($isDuplicate -eq $False) -and ($isSequential -eq $true)

$HasParameterUnique= &{
if ($Others.Count -eq 0 )
{
Write-Debug \"Only one parameter set.\"
return $true
}
foreach ($Current in $Params)
{
if ($Current -notin $Others)
{ return $true}
}
return $false
}#$HasParameterUnique

$isContainsInvalidParameter=$ParametersStartsWithNumber.Count -gt 0

$isValid= $HasParameterUnique -and $isPositionValid -and -not $isContainsInvalidParameter -and -not $isParameterSetNameDuplicate
$Report=New-ParameterSetReport $Params `
$Others `
$Positions `
$ParametersStartsWithNumber `
$HasParameterUnique `
$isDuplicate`
$isSequential `
$isPositionValid `
$isContainsInvalidParameter`
$isParameterSetNameDuplicate

New-ParameterSetValidation $Cmd.ModuleName `
$Cmd.Name `
$ParameterSetName `
$isValid `
$Report
}#For ParameterSets
}#process
}#Test-ParameterSet[/code:1]
Deux exemples :
[code:1]
Function ValideParameterSet{
Param (
[Parameter(ParameterSetName=\"Fonctionnalite1\"«»)]
[Switch] $A,
[Parameter(ParameterSetName=\"Fonctionnalite2\"«»)]
[Switch] $B,
[Parameter(ParameterSetName=\"Fonctionnalite3\"«»)]
[Switch] $C,
[Parameter(ParameterSetName=\"Fonctionnalite1\"«»)]
[Parameter(ParameterSetName=\"Fonctionnalite2\"«»)]
[Parameter(ParameterSetName=\"Fonctionnalite3\"«»)]
[Switch] $D,
[Parameter(ParameterSetName=\"Fonctionnalite2\"«»)]
[Switch] $E

)

Write-Host \"Traitement...\"
}

Function inValideParameterSet{
Param (
[Parameter(Position=0,ParameterSetName=\"Fonctionnalite1\")]
[Switch] $A,
[Parameter(Position=0,ParameterSetName=\"Fonctionnalite2\")]
[Switch] $B,
[Parameter(Position=0,ParameterSetName=\"Fonctionnalite3\")]
[Switch] $C,
[Parameter(Position=2,ParameterSetName=\"Fonctionnalite1\")]
[Parameter(Position=1,ParameterSetName=\"Fonctionnalite2\")]
[Parameter(Position=1,ParameterSetName=\"Fonctionnalite3\")]
[Switch] $D,
[Parameter(Position=1,ParameterSetName=\"Fonctionnalite2\")]
[Switch] $E,
[Switch] $All

)

Write-Host \"Traitement...\"
}
[/code:1]
La fonction Test-ParameterSet contrôle tous les jeux de paramètres et renvoie un objet personnalisé pour chaque nom jeu de paramètre.
La propriété Report contient le détail du test. Par exemple isHasUniqueParameter indique si le jeux de paramètre contient au moins un paramètre unique, validant ainsi la règle d'unicité d'un jeux de paramètre.
[code:1]
$Result=Test-ParameterSet ValideParameterSet
$Result[0]
# ModuleName :
# CommandName : ValideParameterSet
# ParameterSetName : Fonctionnalite1
# isValid : True
# Report : ...

$Result[0].report
# Params : {A, D}
# Others : {B, D, E, C, D}
# Positions : {-2147483648, -2147483648}
# InvalidParameterName : {}
# isHasUniqueParameter : True
# isPositionContainsDuplicate : False
#
# isPositionSequential : True
# isPositionValid : True
# isContainsInvalidParameter : False
#
# isParameterSetNameDuplicate : False
[/code:1]

[code:1]
$Result=Test-ParameterSet ValideParameterSet
$Result.CommandName
$Result|
foreach {
if ($_.isValid)
{ Write-host \"Le jeux $($_.ParameterSetName) est valide.\" }
else
{ Write-Error \"Le jeux $($_.ParameterSetName) est invalide.\" }
}

$Result=Test-ParameterSet InValideParameterSet
$Result.CommandName
$Result|
foreach {
if ($_.isValid)
{ Write-host \"Le jeux $($_.ParameterSetName) est valide.\" }
else
{ Write-Error \"Le jeux $($_.ParameterSetName) est invalide.\" }
}

[/code:1]
On peut aussi contrôler les fonctions d'un module
[code:1]
ipmo psionic
$m=Get-Module psionic
#$res=$m.ExportedFunctions.GetEnumerator()|Test-DefaultParameterSetName -Command {$_.Key}
$res=$m.ExportedFunctions.GetEnumerator()|Test-ParameterSet -Command {$_.Key}
$res|ft -proper CommandName,ParameterSetName,isValid
[/code:1]


Message édité par: Laurent Dardenne, à: 4/06/14 15:29

Message édité par: Laurent Dardenne, à: 7/06/14 11:18

Message édité par: Laurent Dardenne, à: 29/07/14 20:31<br><br>Message édité par: Laurent Dardenne, à: 4/08/14 15:07

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 9 ans 9 mois #17566 par Laurent Dardenne
Mise à jour de la function.
Ajout du contrôle du numéro de position des paramètres nommés. Pour chaque jeux, ils doivent constituer une suite et être unique.

La validité est désormais indiqué par la propriété IsValid, les autres permettent de déterminer la cause.

Message édité par: Laurent Dardenne, à: 24/06/14 20:07<br><br>Message édité par: Laurent Dardenne, à: 29/07/14 20:32

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 9 ans 9 mois #17603 par Laurent Dardenne
Correction du numéro de version minimum : 3.0
Et l'accès au jeux de paramètres se fait désormais via :
[code:1]$Cmd.get_ParameterSets()[/code:1]
Les cas d'erreurs citées ici , sont pris en compte lors de l'exécution.
En demandant la réouverture du bug via la mailing list MVP, il m'a été indiqué que lors de l'accès au métadonnées via une propriété ( parameterSets) toutes les exceptions sont masquées, l'usage de l'accesseur permet de remonter la première erreur rencontrée.

Cette fonction de validation doit être exécuté de préférence après les tests unitaires.

Pour du code provenant du net, il faudra corriger les possible erreurs au fur et à mesure, avant de pouvoir valider la cohérence des jeux de paramètre, s'il en existe.<br><br>Message édité par: Laurent Dardenne, à: 7/06/14 11:32

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 9 ans 9 mois #17606 par Laurent Dardenne

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 9 ans 9 mois #17739 par Laurent Dardenne
Une règle restant à implémenter, un nom de paramètre ne doit pas commencer par un chiffre :
[code:1]
Function TestParamName{
[cmdletbinding()]
param(
[int] $first,
[Switch] $32Bit,
$64bits,
$1certain
)
write-host 'suite'
$psboundparameters
}

TestParamName -32
# suite
#
# Key Value
# ---
# first -32
#

TestParamName -64
# suite
#
# Key Value
# ---
# first -64

TestParamName -64 -32
#suite
#
# Key Value
# ---
# first -64
# 64bits -32
[/code:1]
Il existe d'autres cas de nommage de paramètre autorisé, mais inutilisable :
[code:1] #$.0 #parsing erreur MissingExpressionAfterToken
${.0},
#$-1, #parsing erreur
${-1},
[/code:1]
:blink:<br><br>Message édité par: Laurent Dardenne, à: 27/06/14 21:39

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 9 ans 8 mois #17862 par Laurent Dardenne
Sur la mailing list MVP, Michael B. Smith signale que les noms des jeux de paramètres sont sensibles à la casse :
[code:1]
Function Test {
param(
[parameter(parametersetname=\&quot;text\&quot;,mandatory=$false)]
[switch]$mytext,

[parameter(parametersetname=\&quot;Text\&quot;,mandatory=$false)]
[switch]$yourtext,

[parameter(parametersetname=\&quot;TEXT\&quot;,mandatory=$false)]
[switch]$hertext,

[parameter(parametersetname=\&quot;notext\&quot;,mandatory=$false)]
[switch]$notext
)

\&quot;mytext $mytext\&quot;
\&quot;yourtext $yourtext\&quot;
\&quot;hertext $hertext\&quot;
\&quot;notext $notext\&quot;

Write-host \&quot;ParameterSetName = $($PsCmdlet.ParameterSetName)\&quot;
if ($PsCmdlet.ParameterSetName -eq \&quot;Text\&quot;«»)
{Write-host \&quot;ParameterSetName -eq Text: true\&quot;}
if ($PsCmdlet.ParameterSetName -eq \&quot;TEXT\&quot;«»)
{Write-host \&quot;ParameterSetName -eq TEXT: true\&quot;}

if ($PsCmdlet.ParameterSetName -ceq \&quot;Text\&quot;«»)
{Write-host \&quot;ParameterSetName -ceq Text: true\&quot;}
if ($PsCmdlet.ParameterSetName -ceq \&quot;TEXT\&quot;«»)
{Write-host \&quot;ParameterSetName -ceq TEXT: true\&quot;}
}
Test -yourtext
# mytext False
# yourtext True
# hertext False
# notext False
# ParameterSetName = Text
# ParameterSetName -eq Text: true
# ParameterSetName -eq TEXT: true
# ParameterSetName -ceq Text: true
[/code:1]
Un contrôle à ajouter...

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Temps de génération de la page : 0.093 secondes
Propulsé par Kunena