11$UtilityModulePath = Join-Path - Path $PSScriptRoot - ChildPath " ../../../PowerShell/ScubaGear/Modules/Utility/Utility.psm1" - Resolve
22Import-Module $UtilityModulePath - Function Get-Utf8NoBom , Set-Utf8NoBom
33
4+ function Get-FunctionalTestHeaderValue {
5+ param (
6+ [Parameter (Mandatory = $true )] $Headers ,
7+ [Parameter (Mandatory = $true )] [string ] $Name
8+ )
9+
10+ if ($null -eq $Headers ) {
11+ return $null
12+ }
13+
14+ if (-not $Response.Headers.ContainsKey ($Name )) {
15+ return $null
16+ }
17+
18+ $HeadersValue = $Response.Headers [$Name ]
19+ $HeadersValueType = $Response.Headers [$Name ].GetType()
20+
21+ if ($HeadersValueType -eq [string ]) {
22+ # Write-Information "PS 5 Header '$Name' value: $HeadersValue" -InformationAction Continue
23+ return $HeadersValue
24+ } elseif ($HeadersValueType -eq [string []]) {
25+ # Write-Information "PS 7 Header '$Name' values: $($HeadersValue[0])" -InformationAction Continue
26+ # Return the first item in array
27+ return $HeadersValue [0 ]
28+ } else {
29+ throw " Unexpected HTTP header value type: $HeadersValueType "
30+ }
31+
32+ # if ($Headers -is [System.Collections.IDictionary]) {
33+ # foreach ($Key in $Headers.Keys) {
34+ # if ([string]::Equals([string]$Key, $Name, [System.StringComparison]::OrdinalIgnoreCase)) {
35+ # return [string]$Headers[$Key]
36+ # }
37+ # }
38+ # return $null
39+ # }
40+
41+ # $Property = $Headers.PSObject.Properties | Where-Object {
42+ # [string]::Equals($_.Name, $Name, [System.StringComparison]::OrdinalIgnoreCase)
43+ # } | Select-Object -First 1
44+
45+ # if ($null -eq $Property) {
46+ # return $null
47+ # }
48+
49+ # return [string]$Property.Value
50+ }
51+
52+ function Resolve-FunctionalTestPollingUri {
53+ param (
54+ [Parameter (Mandatory = $true )] [string ] $RequestUri ,
55+ [Parameter (Mandatory = $true )] [string ] $PollingUri
56+ )
57+
58+ return [System.Uri ]::new([System.Uri ]$RequestUri , $PollingUri ).AbsoluteUri
59+ }
60+
61+ function Invoke-FunctionalTestRestRequest {
62+ [CmdletBinding ()]
63+ param (
64+ [Parameter (Mandatory = $true )] [string ] $Uri ,
65+ [Parameter (Mandatory = $true )] [string ] $Method ,
66+ [Parameter (Mandatory = $true )] [hashtable ] $Headers ,
67+ [string ] $Body ,
68+ [string ] $ContentType ,
69+ [int ] $DefaultRetryAfterSeconds = 5 ,
70+ [int ] $MaxPollAttempts = 10
71+ )
72+
73+ $RequestUri = $Uri
74+ $RequestMethod = $Method
75+ $PollAttempts = 0
76+
77+ while ($true ) {
78+ $RequestParams = @ {
79+ Uri = $RequestUri
80+ Method = $RequestMethod
81+ Headers = $Headers
82+ ErrorAction = ' Stop'
83+ }
84+
85+ # PowerShell 5.1 can throw parser null-reference errors for Invoke-WebRequest
86+ # unless -UseBasicParsing is explicitly supplied.
87+ if ($null -ne (Get-Command Invoke-WebRequest ).Parameters[' UseBasicParsing' ]) {
88+ $RequestParams.UseBasicParsing = $true
89+ }
90+
91+ if (-not [string ]::IsNullOrWhiteSpace($Body ) -and $RequestMethod -ne ' GET' ) {
92+ $RequestParams.Body = $Body
93+ }
94+
95+ if (-not [string ]::IsNullOrWhiteSpace($ContentType ) -and $RequestMethod -ne ' GET' ) {
96+ $RequestParams.ContentType = $ContentType
97+ }
98+
99+ try {
100+ $Response = Invoke-WebRequest @RequestParams
101+ }
102+ catch {
103+ $StatusCode = $null
104+ if ($null -ne $_.Exception.Response -and $null -ne $_.Exception.Response.StatusCode ) {
105+ $StatusCode = [int ] $_.Exception.Response.StatusCode
106+ }
107+
108+ if ($null -ne $StatusCode ) {
109+ throw " Request to $RequestUri failed with HTTP status code $StatusCode . $ ( $_.Exception.Message ) "
110+ }
111+
112+ throw
113+ }
114+
115+ $StatusCode = [int ] $Response.StatusCode
116+ if ($StatusCode -eq 202 ) {
117+ $PollAttempts ++
118+ if ($PollAttempts -gt $MaxPollAttempts ) {
119+ throw " Request to $Uri did not complete after $MaxPollAttempts polling attempt(s)."
120+ }
121+
122+ $PollingUri = Get-FunctionalTestHeaderValue - Headers $Response.Headers - Name ' Location'
123+ if ([string ]::IsNullOrWhiteSpace($PollingUri )) {
124+ $PollingUri = Get-FunctionalTestHeaderValue - Headers $Response.Headers - Name ' Operation-Location'
125+ }
126+ if ([string ]::IsNullOrWhiteSpace($PollingUri )) {
127+ $PollingUri = Get-FunctionalTestHeaderValue - Headers $Response.Headers - Name ' Azure-AsyncOperation'
128+ }
129+ if ([string ]::IsNullOrWhiteSpace($PollingUri )) {
130+ throw " Request to $RequestUri returned HTTP 202 without a polling location."
131+ }
132+
133+ $RetryAfter = Get-FunctionalTestHeaderValue - Headers $Response.Headers - Name ' Retry-After'
134+ $RetryAfterSeconds = 0
135+ if (-not [int ]::TryParse([string ] $RetryAfter , [ref ] $RetryAfterSeconds ) -or $RetryAfterSeconds -lt 1 ) {
136+ $RetryAfterSeconds = $DefaultRetryAfterSeconds
137+ }
138+
139+ Write-Information " Request to $RequestUri is still processing." - InformationAction Continue
140+ Write-Information " Polling $PollingUri in $RetryAfterSeconds seconds... (Attempt $PollAttempts of $MaxPollAttempts )" - InformationAction Continue
141+ Start-Sleep - Seconds $RetryAfterSeconds
142+ $RequestUri = Resolve-FunctionalTestPollingUri - RequestUri $RequestUri - PollingUri $PollingUri
143+ $RequestMethod = ' GET'
144+ continue
145+ }
146+
147+ if ($StatusCode -lt 200 -or $StatusCode -ge 300 ) {
148+ throw " Request to $RequestUri returned unexpected HTTP status code $StatusCode ."
149+ }
150+
151+ if ([string ]::IsNullOrWhiteSpace($Response.Content )) {
152+ return $null
153+ }
154+
155+ try {
156+ return $Response.Content | ConvertFrom-Json
157+ }
158+ catch {
159+ return $Response.Content
160+ }
161+ }
162+ }
163+
4164# -----------------------------------------------------------------------
5165# Exchange Online REST wrappers for functional test pre/postconditions.
6166# These replace ExchangeOnlineManagement cmdlets in EXO test plans.
@@ -484,23 +644,23 @@ function Set-ExoOrganizationAuditDisabled {
484644# Products.Tests.ps1 BeforeAll before these functions are called.
485645# -----------------------------------------------------------------------
486646function Get-TenantSettings {
487- $Response = Invoke-RestMethod - Uri " $script :PPBaseUrl /providers/Microsoft.BusinessAppPlatform/listTenantSettings?api-version=2023-06-01" `
488- - Method POST - Headers @ { Authorization = " Bearer $script :PPAccessToken " } - ContentType " application/json"
647+ $Response = Invoke-FunctionalTestRestRequest - Uri " $script :PPBaseUrl /providers/Microsoft.BusinessAppPlatform/listTenantSettings?api-version=2023-06-01" `
648+ - Method ' POST' - Headers @ { Authorization = " Bearer $script :PPAccessToken " } - ContentType ' application/json'
489649 return $Response
490650}
491651
492652function Set-TenantSettings {
493653 param ([Parameter (Position = 0 )][object ]$Settings , [hashtable ]$RequestBody )
494654 if ($RequestBody ) { $Body = $RequestBody | ConvertTo-Json - Depth 10 }
495655 else { $Body = $Settings | ConvertTo-Json - Depth 10 }
496- Invoke-RestMethod - Uri " $script :PPBaseUrl /providers/Microsoft.BusinessAppPlatform/scopes/admin/updateTenantSettings?api-version=2023-06-01" `
497- - Method POST - Headers @ { Authorization = " Bearer $script :PPAccessToken " } - Body $Body - ContentType " application/json" | Out-Null
656+ Invoke-FunctionalTestRestRequest - Uri " $script :PPBaseUrl /providers/Microsoft.BusinessAppPlatform/scopes/admin/updateTenantSettings?api-version=2023-06-01" `
657+ - Method ' POST' - Headers @ { Authorization = " Bearer $script :PPAccessToken " } - Body $Body - ContentType ' application/json' | Out-Null
498658}
499659
500660function Get-AdminPowerAppEnvironment {
501661 param ([switch ]$Default )
502- $Response = Invoke-RestMethod - Uri " $script :PPBaseUrl /providers/Microsoft.BusinessAppPlatform/scopes/admin/environments?api-version=2023-06-01" `
503- - Method GET - Headers @ { Authorization = " Bearer $script :PPAccessToken " }
662+ $Response = Invoke-FunctionalTestRestRequest - Uri " $script :PPBaseUrl /providers/Microsoft.BusinessAppPlatform/scopes/admin/environments?api-version=2023-06-01" `
663+ - Method ' GET' - Headers @ { Authorization = " Bearer $script :PPAccessToken " }
504664 if ($Default ) {
505665 return $Response.value | Where-Object { $_.properties.isDefault -eq $true } | Select-Object - First 1 |
506666 Select-Object @ { Name = " EnvironmentName" ; Expression = { $_.name } }
@@ -509,8 +669,8 @@ function Get-AdminPowerAppEnvironment {
509669}
510670
511671function Get-DlpPolicy {
512- $Response = Invoke-RestMethod - Uri " $script :PPBaseUrl /providers/Microsoft.BusinessAppPlatform/scopes/admin/apiPolicies?api-version=2016-11-01" `
513- - Method GET - Headers @ { Authorization = " Bearer $script :PPAccessToken " }
672+ $Response = Invoke-FunctionalTestRestRequest - Uri " $script :PPBaseUrl /providers/Microsoft.BusinessAppPlatform/scopes/admin/apiPolicies?api-version=2016-11-01" `
673+ - Method ' GET' - Headers @ { Authorization = " Bearer $script :PPAccessToken " }
514674 # Normalize ARM response: hoist properties.displayName and id to root to
515675 # match original Get-DlpPolicy cmdlet output expected by test plan filters.
516676 # id is preserved as the full ARM resource path for use in Remove-DlpPolicy.
@@ -536,8 +696,8 @@ function Remove-DlpPolicy {
536696 } else {
537697 " /providers/Microsoft.BusinessAppPlatform/scopes/admin/apiPolicies/$PolicyName "
538698 }
539- Invoke-RestMethod - Uri " $script :PPBaseUrl${Path} ?api-version=2016-11-01" `
540- - Method DELETE - Headers @ { Authorization = " Bearer $script :PPAccessToken " } | Out-Null
699+ Invoke-FunctionalTestRestRequest - Uri " $script :PPBaseUrl${Path} ?api-version=2016-11-01" `
700+ - Method ' DELETE' - Headers @ { Authorization = " Bearer $script :PPAccessToken " } | Out-Null
541701 }
542702}
543703
@@ -565,17 +725,17 @@ function New-AdminDlpPolicy {
565725 }
566726 }
567727 } | ConvertTo-Json - Depth 10
568- Invoke-RestMethod - Uri " $script :PPBaseUrl /providers/Microsoft.BusinessAppPlatform/scopes/admin/apiPolicies?api-version=2016-11-01" `
569- - Method POST - Headers @ { Authorization = " Bearer $script :PPAccessToken " } - Body $Body - ContentType " application/json" | Out-Null
728+ Invoke-FunctionalTestRestRequest - Uri " $script :PPBaseUrl /providers/Microsoft.BusinessAppPlatform/scopes/admin/apiPolicies?api-version=2016-11-01" `
729+ - Method ' POST' - Headers @ { Authorization = " Bearer $script :PPAccessToken " } - Body $Body - ContentType ' application/json' | Out-Null
570730}
571731
572732function Get-PowerAppTenantIsolationPolicy {
573733 param ([string ]$TenantId )
574734 $MaxAttempts = 3
575735 for ($Attempt = 1 ; $Attempt -le $MaxAttempts ; $Attempt ++ ) {
576736 try {
577- return Invoke-RestMethod - Uri " $script :PPBaseUrl /providers/PowerPlatform.Governance/v1/tenants/$TenantId /tenantIsolationPolicy?api-version=2020-06-01" `
578- - Method GET - Headers @ { Authorization = " Bearer $script :PPAccessToken " }
737+ return Invoke-FunctionalTestRestRequest - Uri " $script :PPBaseUrl /providers/PowerPlatform.Governance/v1/tenants/$TenantId /tenantIsolationPolicy?api-version=2020-06-01" `
738+ - Method ' GET' - Headers @ { Authorization = " Bearer $script :PPAccessToken " }
579739 }
580740 catch {
581741 if ($Attempt -ge $MaxAttempts ) { throw }
@@ -591,8 +751,8 @@ function Set-PowerAppTenantIsolationPolicy {
591751 $MaxAttempts = 3
592752 for ($Attempt = 1 ; $Attempt -le $MaxAttempts ; $Attempt ++ ) {
593753 try {
594- Invoke-RestMethod - Uri " $script :PPBaseUrl /providers/PowerPlatform.Governance/v1/tenants/$TenantId /tenantIsolationPolicy?api-version=2020-06-01" `
595- - Method PUT - Headers @ { Authorization = " Bearer $script :PPAccessToken " } - Body $Body - ContentType " application/json" | Out-Null
754+ Invoke-FunctionalTestRestRequest - Uri " $script :PPBaseUrl /providers/PowerPlatform.Governance/v1/tenants/$TenantId /tenantIsolationPolicy?api-version=2020-06-01" `
755+ - Method ' PUT' - Headers @ { Authorization = " Bearer $script :PPAccessToken " } - Body $Body - ContentType ' application/json' | Out-Null
596756 return
597757 }
598758 catch {
@@ -619,9 +779,9 @@ function Set-PowerBITenantSetting {
619779 $MaxAttempts = 3
620780 for ($Attempt = 1 ; $Attempt -le $MaxAttempts ; $Attempt ++ ) {
621781 try {
622- Invoke-RestMethod - Uri " $script :PBIBaseUrl /v1/admin/tenantsettings/$SettingName /update" `
623- - Method POST - Headers @ { Authorization = " Bearer $script :PBIAccessToken " } `
624- - Body $Body - ContentType " application/json" | Out-Null
782+ Invoke-FunctionalTestRestRequest - Uri " $script :PBIBaseUrl /v1/admin/tenantsettings/$SettingName /update" `
783+ - Method ' POST' - Headers @ { Authorization = " Bearer $script :PBIAccessToken " } `
784+ - Body $Body - ContentType ' application/json' | Out-Null
625785 return
626786 }
627787 catch {
0 commit comments