Skip to content
This repository was archived by the owner on Sep 27, 2019. It is now read-only.

Commit 21671ec

Browse files
authored
Client-side filtering (#393)
Client-side filtering (#393)
1 parent 48b900b commit 21671ec

17 files changed

+1245
-274
lines changed

PSSwagger/Get-ApplicableFilters.ps1

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
Microsoft.PowerShell.Core\Set-StrictMode -Version Latest
2+
3+
<#
4+
.DESCRIPTION
5+
Get all filters that are applicable based on their Value.
6+
7+
.PARAMETER Filters
8+
All filters to check for applicability. Required properties: 'Type', 'Value', 'Property'
9+
Supported types: 'wildcard', 'equalityOperator'
10+
#>
11+
function Get-ApplicableFilters {
12+
[CmdletBinding()]
13+
param(
14+
[Parameter(Mandatory=$true)]
15+
[PSCustomObject[]]
16+
$Filters
17+
)
18+
19+
$ErrorActionPreference = 'Stop'
20+
foreach ($filter in $Filters) {
21+
$res = @{
22+
Filter = $filter
23+
Strict = $false
24+
}
25+
if ($filter.Type -eq 'wildcard') {
26+
if (Test-WildcardFilter -Filter $filter) {
27+
$res['Strict'] = $true
28+
}
29+
}
30+
elseif ($filter.Type -eq 'equalityOperator') {
31+
if (Test-EqualityFilter -Filter $filter) {
32+
$res['Strict'] = $true
33+
}
34+
} elseif ($filter.Type -eq 'powershellWildcard') {
35+
if (Test-PSWildcardFilter -Filter $filter) {
36+
$res['Strict'] = $true
37+
}
38+
}
39+
if ($res['Strict'] -or $filter.Value) {
40+
$res
41+
}
42+
}
43+
}
44+
45+
function Test-WildcardFilter {
46+
[CmdletBinding()]
47+
param(
48+
[Parameter(Mandatory=$true)]
49+
[PSCustomObject]
50+
$Filter
51+
)
52+
53+
# Must contain wildcard character
54+
($Filter) -and ($Filter.Value -is [System.String]) -and ($Filter.Value.Contains($Filter.Character))
55+
}
56+
57+
function Test-EqualityFilter {
58+
[CmdletBinding()]
59+
param(
60+
[Parameter(Mandatory=$true)]
61+
[PSCustomObject]
62+
$Filter
63+
)
64+
65+
# Must be specified
66+
($Filter -and $Filter.Value)
67+
}
68+
69+
function Test-PSWildcardFilter {
70+
[CmdletBinding()]
71+
param(
72+
[Parameter(Mandatory=$true)]
73+
[PSCustomObject]
74+
$Filter
75+
)
76+
77+
($Filter) -and ($Filter.Value -is [System.String]) -and [WildcardPattern]::ContainsWildcardCharacters($Filter.Value)
78+
}

PSSwagger/PSSwagger.Constants.ps1

+59
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ if (Test-Path -Path `$ClrPath -PathType Container) {
6060
6161
. (Join-Path -Path `$PSScriptRoot -ChildPath 'New-ServiceClient.ps1')
6262
. (Join-Path -Path `$PSScriptRoot -ChildPath 'Get-TaskResult.ps1')
63+
. (Join-Path -Path `$PSScriptRoot -ChildPath 'Get-ApplicableFilters.ps1')
64+
. (Join-Path -Path `$PSScriptRoot -ChildPath 'Test-FilteredResult.ps1')
6365
$(if($UseAzureCsharpGenerator) {
6466
". (Join-Path -Path `$PSScriptRoot -ChildPath 'Get-ArmResourceIdParameterValue.ps1')"
6567
})
@@ -242,6 +244,7 @@ if($ResourceIdParamCodeBlock) {
242244
"
243245
}
244246
)
247+
$FilterBlock
245248
$parameterSetBasedMethodStr else {
246249
Write-Verbose -Message 'Failed to map parameter set to operation method.'
247250
throw 'Module failed to find operation to execute.'
@@ -560,3 +563,59 @@ Copyright (c) Microsoft Corporation. All rights reserved.
560563
Licensed under the MIT License. See License.txt in the project root for license information.
561564
'@
562565

566+
$FilterBlockStr = @'
567+
`$filterInfos = @(
568+
$(
569+
$prependComma = $false
570+
foreach ($filter in $clientSideFilter.Filters) {
571+
if ($prependComma) {
572+
", "
573+
} else {
574+
$prependComma = $true
575+
}
576+
577+
"@{
578+
'Type' = '$($filter.Type)'
579+
'Value' = `$$($filter.Parameter)
580+
'Property' = '$($filter.Property)'"
581+
foreach ($property in (Get-Member -InputObject $filter -MemberType NoteProperty)) {
582+
if (($property.Name -eq 'Type') -or ($property.Name -eq 'Parameter') -or ($property.Name -eq 'Property') -or ($property.Name -eq 'AppendParameterInfo')) {
583+
continue
584+
}
585+
586+
"
587+
'$($property.Name)' = '$($filter.($property.Name))'"
588+
}
589+
"
590+
}"
591+
}
592+
))
593+
`$applicableFilters = Get-ApplicableFilters -Filters `$filterInfos
594+
if (`$applicableFilters | Where-Object { `$_.Strict }) {
595+
Write-Verbose -Message 'Performing server-side call ''$(if ($clientSideFilter.ServerSideResultCommand -eq '.') { $commandName } else { $clientSideFilter.ServerSideResultCommand }) -$($matchingParameters -join " -")'''
596+
`$serverSideCall_params = @{
597+
$(
598+
foreach ($matchingParam in $matchingParameters) {
599+
" '$matchingParam' = `$$matchingParam
600+
"
601+
}
602+
)
603+
}
604+
605+
`$serverSideResults = $(if ($clientSideFilter.ServerSideResultCommand -eq '.') { $commandName } else { $clientSideFilter.ServerSideResultCommand }) @serverSideCall_params
606+
foreach (`$serverSideResult in `$serverSideResults) {
607+
`$valid = `$true
608+
foreach (`$applicableFilter in `$applicableFilters) {
609+
if (-not (Test-FilteredResult -Result `$serverSideResult -Filter `$applicableFilter.Filter)) {
610+
`$valid = `$false
611+
break
612+
}
613+
}
614+
615+
if (`$valid) {
616+
`$serverSideResult
617+
}
618+
}
619+
return
620+
}
621+
'@

PSSwagger/PSSwagger.Resources.psd1

+5
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,10 @@ ConvertFrom-StringData @'
9191
InvalidHeaderFilePath=The specified value '{0}' for Header parameter is should be a valid file path.
9292
HeaderContentTwoHyphenWarning=The specified Header content has '--', replacing '--' with '=='.
9393
PSScriptAnalyzerMissing=PSScriptAnalyzer specified as the formatter, but the module is not available on this machine. No formatting will be used. Run 'Install-Module -Name PSScriptAnalyzer -Repository PSGallery -Scope CurrentUser' to install the module.
94+
CouldntFindServerSideResultOperation=Couldn't find server-side result operation: {0}
95+
CouldntFindServerSideResultParameterSet=Couldn't find server-side result parameter set: {0}
96+
CouldntFindClientSideParameterSet=Couldn't find client-side parameter set: {0}
97+
MissingRequiredFilterParameter=Required server-side parameter '{0}' is not required by the client-side, which will cause issues in client-side filtering. Can't include client-side filtering.
98+
FailedToAddAutomaticFilter=Failed to add automatic client-side filter for candidate command '{0}': Mandatory List parameter '{1}' has no matching Get mandatory parameter. It will be impossible to guarantee execution of the List method before client-side filtering occurs.
9499
###PSLOC
95100
'@

PSSwagger/PSSwagger.psm1

+112-9
Original file line numberDiff line numberDiff line change
@@ -299,13 +299,14 @@ function New-PSSwaggerModule {
299299
$ev = $null
300300

301301
$webRequestParams = @{
302-
'Uri' = $SpecificationUri
302+
'Uri' = $SpecificationUri
303303
'OutFile' = $SpecificationPath
304304
}
305305

306-
if($Credential -ne $null) {
306+
if ($Credential -ne $null) {
307307
$webRequestParams['Credential'] = $Credential
308-
} elseif ($UseDefaultCredential) {
308+
}
309+
elseif ($UseDefaultCredential) {
309310
$webRequestParams['UseDefaultCredential'] = $true
310311
}
311312

@@ -452,7 +453,9 @@ function New-PSSwaggerModule {
452453
NoAuthChallenge = $false
453454
NameSpacePrefix = ''
454455
Header = ''
455-
Formatter = ''
456+
Formatter = 'PSScriptAnalyzer'
457+
DefaultWildcardChar = '%'
458+
AzureDefaults = $null
456459
}
457460

458461
# Parse the JSON and populate the dictionary
@@ -482,7 +485,8 @@ function New-PSSwaggerModule {
482485
if (-not $Formatter) {
483486
if ($PowerShellCodeGen['Formatter']) {
484487
$Formatter = $PowerShellCodeGen['Formatter']
485-
} else {
488+
}
489+
else {
486490
$Formatter = 'None'
487491
}
488492
}
@@ -582,6 +586,100 @@ function New-PSSwaggerModule {
582586

583587
}
584588
}
589+
590+
# Add extra metadata based on service type
591+
if (($PowerShellCodeGen['ServiceType'] -eq 'azure') -or ($PowerShellCodeGen['ServiceType'] -eq 'azure_stack') -and
592+
($PowerShellCodeGen.ContainsKey('azureDefaults') -and $PowerShellCodeGen['azureDefaults'] -and
593+
(-not (Get-Member -InputObject $PowerShellCodeGen['azureDefaults'] -Name 'clientSideFiltering')) -or
594+
($PowerShellCodeGen['azureDefaults'].ClientSideFiltering))) {
595+
foreach ($entry in $PathFunctionDetails.GetEnumerator()) {
596+
$hyphenIndex = $entry.Name.IndexOf("-")
597+
if ($hyphenIndex -gt -1) {
598+
$commandVerb = $entry.Name.Substring(0, $hyphenIndex)
599+
# Add client-side filter metadata automatically if:
600+
# 1: If the command is a Get-* command
601+
# 2: A *_List parameter set exists
602+
# 3: A *_Get parameter set exists
603+
# 4: *_List required parameters are a subset of *_Get required parameters
604+
# 5: *_Get has a -Name parameter alias
605+
if ($commandVerb -eq 'Get') {
606+
$getParameters = @()
607+
$listParameters = $null
608+
$listOperationId = $null
609+
$getOperationId = $null
610+
$nameParameterNormalName = $null # This is the one being switched out for -Name later
611+
foreach ($parameterSetDetails in $entry.Value.ParameterSetDetails) {
612+
if ($parameterSetDetails.OperationId.EndsWith("_Get") -and
613+
(-not ($parameterSetDetails.OperationId.StartsWith("InputObject_"))) -and
614+
(-not ($parameterSetDetails.OperationId.StartsWith("ResourceId_")))) {
615+
$getOperationId = $parameterSetDetails.OperationId
616+
foreach ($parametersDetail in $parameterSetDetails.ParameterDetails) {
617+
foreach ($parameterDetailEntry in $parametersDetail.GetEnumerator()) {
618+
$getParameters += $parameterDetailEntry.Value
619+
if ($parameterDetailEntry.Value.ContainsKey('Alias')) {
620+
if ($parameterDetailEntry.Value.Alias -eq 'Name') {
621+
$nameParameterNormalName = $parameterDetailEntry.Value.Name
622+
}
623+
}
624+
elseif ($parameterDetailEntry.Value.Name -eq 'Name') {
625+
# We're currently assuming this is a resource name
626+
$nameParameterNormalName = $parameterDetailEntry.Value.Name
627+
}
628+
}
629+
}
630+
}
631+
elseif ($parameterSetDetails.OperationId.EndsWith("_List")) {
632+
$listOperationId = $parameterSetDetails.OperationId
633+
$listParameters = @()
634+
foreach ($parametersDetail in $parameterSetDetails.ParameterDetails) {
635+
foreach ($parameterDetailEntry in $parametersDetail.GetEnumerator()) {
636+
$listParameters += $parameterDetailEntry.Value
637+
}
638+
}
639+
}
640+
}
641+
642+
if ($getParameters -and $listParameters) {
643+
$valid = $true
644+
foreach ($parameterDetail in $listParameters) {
645+
if ($parameterDetail.Mandatory -eq '$true') {
646+
$matchingGetParameter = $getParameters | Where-Object { ($_.Name -eq $parameterDetail.Name) -and ($_.Mandatory -eq '$true') }
647+
if (-not $matchingGetParameter) {
648+
$valid = $false
649+
Write-Warning -Message ($LocalizedData.FailedToAddAutomaticFilter -f $entry.Name)
650+
}
651+
}
652+
}
653+
654+
if ($valid) {
655+
if (-not $entry.Value.ContainsKey('Metadata')) {
656+
$entry.Value['Metadata'] = New-Object -TypeName PSCustomObject
657+
}
658+
659+
if (-not (Get-Member -InputObject $entry.Value['Metadata'] -Name 'ClientSideFilter')) {
660+
$clientSideFilter = New-Object -TypeName PSCustomObject
661+
# Use the current command for server-side results
662+
Add-Member -InputObject $clientSideFilter -Name 'ServerSideResultCommand' -Value '.' -MemberType NoteProperty
663+
# Use the list operation ID
664+
Add-Member -InputObject $clientSideFilter -Name 'ServerSideResultParameterSet' -Value $listOperationId -MemberType NoteProperty
665+
# Use the get operation ID
666+
Add-Member -InputObject $clientSideFilter -Name 'ClientSideParameterSet' -Value $getOperationId -MemberType NoteProperty
667+
# Create a wildcard filter for the Name parameter
668+
$nameWildcardFilter = New-Object -TypeName PSCustomObject
669+
Add-Member -InputObject $nameWildcardFilter -Name 'Type' -Value 'powershellWildcard' -MemberType NoteProperty
670+
Add-Member -InputObject $nameWildcardFilter -Name 'Parameter' -Value $nameParameterNormalName -MemberType NoteProperty
671+
Add-Member -InputObject $nameWildcardFilter -Name 'Property' -Value 'Name' -MemberType NoteProperty
672+
$filters = @($nameWildcardFilter)
673+
Add-Member -InputObject $clientSideFilter -Name 'Filters' -Value $filters -MemberType NoteProperty
674+
$allClientSideFilters = @($clientSideFilter)
675+
Add-Member -InputObject $entry.Value['Metadata'] -Name 'ClientSideFilters' -Value $allClientSideFilters -MemberType NoteProperty
676+
}
677+
}
678+
}
679+
}
680+
}
681+
}
682+
}
585683
}
586684

587685
$FullClrAssemblyFilePath = $null
@@ -637,7 +735,8 @@ function New-PSSwaggerModule {
637735
-SwaggerDict $swaggerDict `
638736
-DefinitionFunctionsDetails $DefinitionFunctionsDetails `
639737
-PSHeaderComment $PSHeaderComment `
640-
-Formatter $Formatter
738+
-Formatter $Formatter `
739+
-PowerShellCodeGen $PowerShellCodeGen
641740

642741
$FunctionsToExport += New-SwaggerDefinitionCommand -DefinitionFunctionsDetails $DefinitionFunctionsDetails `
643742
-SwaggerMetaDict $swaggerMetaDict `
@@ -676,7 +775,9 @@ function New-PSSwaggerModule {
676775
-PSHeaderComment $PSHeaderComment
677776

678777
$CopyFilesMap = [ordered]@{
679-
'Get-TaskResult.ps1' = 'Get-TaskResult.ps1'
778+
'Get-TaskResult.ps1' = 'Get-TaskResult.ps1'
779+
'Get-ApplicableFilters.ps1' = 'Get-ApplicableFilters.ps1'
780+
'Test-FilteredResult.ps1' = 'Test-FilteredResult.ps1'
680781
}
681782
if ($UseAzureCsharpGenerator) {
682783
$CopyFilesMap['New-ArmServiceClient.ps1'] = 'New-ServiceClient.ps1'
@@ -815,7 +916,8 @@ function ConvertTo-CsharpCode {
815916
$fs = [System.IO.File]::OpenRead($csc.Source)
816917
try {
817918
$null = $fs.Read($data, 0, 4096)
818-
} finally {
919+
}
920+
finally {
819921
$fs.Dispose()
820922
}
821923

@@ -828,7 +930,8 @@ function ConvertTo-CsharpCode {
828930
if ($magic -eq 0x20b) {
829931
# Skip to the end of IMAGE_OPTIONAL_HEADER64 to the first entry in the data directory array
830932
$p_dataDirectory0 = [System.BitConverter]::ToUInt32($data, [int]$p_ioh + 224)
831-
} else {
933+
}
934+
else {
832935
# Same thing, but for IMAGE_OPTIONAL_HEADER32
833936
$p_dataDirectory0 = [System.BitConverter]::ToUInt32($data, [int]$p_ioh + 208)
834937
}

0 commit comments

Comments
 (0)