From b33c6dcd025a2aa3755885ff6be83fbbbb0d9493 Mon Sep 17 00:00:00 2001 From: Caleb Brose <5447118+cmbrose@users.noreply.github.com> Date: Tue, 9 Apr 2024 20:26:58 +0000 Subject: [PATCH 1/3] Add ability to refresh auth headers --- cosmos-db/cosmos-db.psm1 | 85 +++++-- tests/Get-AllCosmosDbRecords.Tests.ps1 | 43 ++-- tests/Get-CommonHeaders.Tests.ps1 | 10 + tests/Get-PartitionKeyRangesOrError.Tests.ps1 | 27 +- ...smosDbApiRequestWithContinuation.Tests.ps1 | 233 +++++++++++++++++- tests/Search-CosmosDbRecords.Tests.ps1 | 77 +++--- 6 files changed, 371 insertions(+), 104 deletions(-) diff --git a/cosmos-db/cosmos-db.psm1 b/cosmos-db/cosmos-db.psm1 index 5d7ee16..8c4b760 100644 --- a/cosmos-db/cosmos-db.psm1 +++ b/cosmos-db/cosmos-db.psm1 @@ -10,6 +10,10 @@ $DELETE_VERB = "delete" $API_VERSION = "2018-12-31" +# Authorization headers are valid for 15 minutes +# https://learn.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key#specifying-the-date-header +$AUTHORIZATION_HEADER_REFRESH_THRESHOLD = [System.TimeSpan]::FromMinutes(10); + $MASTER_KEY_CACHE = @{} $SIGNATURE_HASH_CACHE = @{} $PARTITION_KEY_RANGE_CACHE = @{} @@ -141,13 +145,13 @@ Function Get-AuthorizationHeader([string]$ResourceGroup, [string]$SubscriptionId Function Get-CommonHeaders([string]$now, [string]$encodedAuthString, [string]$contentType = "application/json", [bool]$isQuery = $false, [string]$PartitionKey = $null, [string]$Etag = $null) { $headers = @{ - "x-ms-date" = $now; "x-ms-version" = $API_VERSION; - "Authorization" = $encodedAuthString; "Cache-Control" = "No-Cache"; "Content-Type" = $contentType; } + Set-AuthHeaders -headers $headers -now $now -encodedAuthString $encodedAuthString + if ($isQuery) { $headers["x-ms-documentdb-isquery"] = "true" } @@ -163,6 +167,16 @@ Function Get-CommonHeaders([string]$now, [string]$encodedAuthString, [string]$co $headers } +Function Set-AuthHeaders($headers, [string]$now, [string]$encodedAuthString) { + if ($now) { + $headers["x-ms-date"] = $now + } + + if ($encodedAuthString) { + $headers["Authorization"] = $encodedAuthString + } +} + Function Get-QueryParametersAsNameValuePairs($obj) { if (!$obj) { return @() @@ -239,10 +253,13 @@ Function Get-ContinuationToken($response) { } } -Function Invoke-CosmosDbApiRequestWithContinuation([string]$verb, [string]$url, $headers, $body = $null) { +Function Invoke-CosmosDbApiRequestWithContinuation([string]$verb, [string]$url, $headers, [ScriptBlock]$refreshAuthHeaders, $body = $null) { # Remove in case the headers are reused between multiple calls to this function $headers.Remove("x-ms-continuation"); + $authHeaders = Invoke-Command -ScriptBlock $refreshAuthHeaders + Set-AuthHeaders -headers $headers -now $authHeaders.now -encodedAuthString $authHeaders.encodedAuthString + $response = Invoke-CosmosDbApiRequest -Verb $verb -Url $url -Body $body -Headers $headers $response @@ -250,6 +267,11 @@ Function Invoke-CosmosDbApiRequestWithContinuation([string]$verb, [string]$url, while ($continuationToken) { $headers["x-ms-continuation"] = $continuationToken + if ([System.DateTime]::Parse($authHeaders.now) + $AUTHORIZATION_HEADER_REFRESH_THRESHOLD -lt [System.DateTime]::UtcNow) { + $authHeaders = Invoke-Command -ScriptBlock $refreshAuthHeaders + Set-AuthHeaders -headers $headers -now $authHeaders.now -encodedAuthString $authHeaders.encodedAuthString + } + $response = Invoke-CosmosDbApiRequest -Verb $verb -Url $url -Body $body -Headers $headers $response @@ -278,14 +300,17 @@ Function Get-PartitionKeyRangesOrError return $cacheResult } - $now = Get-Time - - $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $GET_VERB -resourceType $PARTITIONKEYRANGE_TYPE -resourceUrl $collectionsUrl -now $now + $refreshAuthHeaders = { + $now = Get-Time + $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $GET_VERB -resourceType $PARTITIONKEYRANGE_TYPE -resourceUrl $collectionsUrl -now $now - $headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -PartitionKey $requestPartitionKey + return @{ now = $now; encodedAuthString = $encodedAuthString } + } + + $headers = Get-CommonHeaders -PartitionKey $requestPartitionKey $headers["x-ms-documentdb-query-enablecrosspartition"] = "true" - $response = Invoke-CosmosDbApiRequestWithContinuation -Verb $GET_VERB -Url $url -Headers $headers | Get-CosmosDbRecordContent + $response = Invoke-CosmosDbApiRequestWithContinuation -Verb $GET_VERB -Url $url -Headers $headers -RefreshAuthHeaders $refreshAuthHeaders | Get-CosmosDbRecordContent $ranges = $response.partitionKeyRanges @@ -450,16 +475,19 @@ Function Get-AllCosmosDbRecords( $url = "$baseUrl/$docsUrl" - $now = Get-Time + $refreshAuthHeaders = { + $now = Get-Time + $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $GET_VERB -resourceType $DOCS_TYPE -resourceUrl $collectionsUrl -now $now - $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $GET_VERB -resourceType $DOCS_TYPE -resourceUrl $collectionsUrl -now $now + return @{ now = $now; encodedAuthString = $encodedAuthString } + } $tmp = $ProgressPreference $ProgressPreference = 'SilentlyContinue' try { - $headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -isQuery $true + $headers = Get-CommonHeaders -isQuery $true - Invoke-CosmosDbApiRequestWithContinuation -verb $GET_VERB -url $url -Headers $headers + Invoke-CosmosDbApiRequestWithContinuation -verb $GET_VERB -url $url -Headers $headers -RefreshAuthHeaders $refreshAuthHeaders } catch { Get-ExceptionResponseOrThrow $_ @@ -542,18 +570,21 @@ Function Search-CosmosDbRecords( ) { $Parameters = @(Get-QueryParametersAsNameValuePairs $Parameters) + if (!$DisableExtraFeatures) { + return Search-CosmosDbRecordsWithExtraFeatures -ResourceGroup $ResourceGroup -Database $Database -Container $Container -Collection $Collection -Query $Query -Parameters $Parameters -SubscriptionId $SubscriptionId + } + $baseUrl = Get-BaseDatabaseUrl $Database $collectionsUrl = Get-CollectionsUrl $Container $Collection $docsUrl = "$collectionsUrl/$DOCS_TYPE" $url = "$baseUrl/$docsUrl" - $now = Get-Time - - $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $POST_VERB -resourceType $DOCS_TYPE -resourceUrl $collectionsUrl -now $now + $refreshAuthHeaders = { + $now = Get-Time + $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $POST_VERB -resourceType $DOCS_TYPE -resourceUrl $collectionsUrl -now $now - if (!$DisableExtraFeatures) { - return Search-CosmosDbRecordsWithExtraFeatures -ResourceGroup $ResourceGroup -Database $Database -Container $Container -Collection $Collection -Query $Query -Parameters $Parameters -SubscriptionId $SubscriptionId + return @{ now = $now; encodedAuthString = $encodedAuthString } } try { @@ -562,10 +593,10 @@ Function Search-CosmosDbRecords( parameters = $Parameters; } - $headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -isQuery $true -contentType "application/Query+json" + $headers = Get-CommonHeaders -isQuery $true -contentType "application/Query+json" $headers["x-ms-documentdb-query-enablecrosspartition"] = "true" - Invoke-CosmosDbApiRequestWithContinuation -verb $POST_VERB -url $url -Body $body -Headers $headers + Invoke-CosmosDbApiRequestWithContinuation -verb $POST_VERB -url $url -Body $body -Headers $headers -RefreshAuthHeaders $refreshAuthHeaders } catch { Get-ExceptionResponseOrThrow $_ @@ -590,15 +621,18 @@ Function Search-CosmosDbRecordsWithExtraFeatures $url = "$baseUrl/$docsUrl" - $now = Get-Time - - $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $POST_VERB -resourceType $DOCS_TYPE -resourceUrl $collectionsUrl -now $now - $allPartitionKeyRangesOrError = Get-PartitionKeyRangesOrError -ResourceGroup $ResourceGroup -Database $Database -Container $Container -Collection $Collection -SubscriptionId $SubscriptionId if ($allPartitionKeyRangesOrError.ErrorRecord) { return Get-ExceptionResponseOrThrow $allPartitionKeyRangesOrError.ErrorRecord } + + $refreshAuthHeaders = { + $now = Get-Time + $encodedAuthString = Get-AuthorizationHeader -ResourceGroup $ResourceGroup -SubscriptionId $SubscriptionId -Database $Database -verb $POST_VERB -resourceType $DOCS_TYPE -resourceUrl $collectionsUrl -now $now + + return @{ now = $now; encodedAuthString = $encodedAuthString } + } try { $ranges = $allPartitionKeyRangesOrError.Ranges @@ -608,7 +642,8 @@ Function Search-CosmosDbRecordsWithExtraFeatures parameters = $Parameters; } - $headers = Get-CommonHeaders -now $now -encodedAuthString $encodedAuthString -isQuery $true -contentType "application/Query+json" + $authHeaders = Invoke-Command -ScriptBlock $refreshAuthHeaders + $headers = Get-CommonHeaders -now $authHeaders.now -encodedAuthString $authHeaders.encodedAuthString -isQuery $true -contentType "application/Query+json" $headers += @{ "x-ms-documentdb-query-enablecrosspartition" = "true"; "x-ms-cosmos-supported-query-features" = "NonValueAggregate, Aggregate, Distinct, MultipleOrderBy, OffsetAndLimit, OrderBy, Top, CompositeAggregate, GroupBy, MultipleAggregates"; @@ -640,7 +675,7 @@ Function Search-CosmosDbRecordsWithExtraFeatures foreach ($partitionKeyRange in $partitionKeyRanges) { $headers["x-ms-documentdb-partitionkeyrangeid"] = $partitionKeyRange.id - Invoke-CosmosDbApiRequestWithContinuation -verb $POST_VERB -url $url -Body $body -Headers $headers + Invoke-CosmosDbApiRequestWithContinuation -verb $POST_VERB -url $url -Body $body -Headers $headers -RefreshAuthHeaders $refreshAuthHeaders } } catch { diff --git a/tests/Get-AllCosmosDbRecords.Tests.ps1 b/tests/Get-AllCosmosDbRecords.Tests.ps1 index c6e33a5..74982d4 100644 --- a/tests/Get-AllCosmosDbRecords.Tests.ps1 +++ b/tests/Get-AllCosmosDbRecords.Tests.ps1 @@ -18,8 +18,7 @@ InModuleScope cosmos-db { $MOCK_AUTH_HEADER = "MockAuthHeader" - Function VerifyGetAuthHeader($ResourceGroup, $SubscriptionId, $Database, $verb, $resourceType, $resourceUrl, $now) - { + Function VerifyGetAuthHeader($ResourceGroup, $SubscriptionId, $Database, $verb, $resourceType, $resourceUrl, $now) { $ResourceGroup | Should -Be $MOCK_RG $SubscriptionId | Should -Be $MOCK_SUB @@ -28,15 +27,19 @@ InModuleScope cosmos-db { $resourceUrl | Should -Be "dbs/$MOCK_CONTAINER/colls/$MOCK_COLLECTION" } - Function VerifyInvokeCosmosDbApiRequest($verb, $url, $body, $headers, $partitionKey=$MOCK_RECORD_ID) - { + Function VerifyInvokeCosmosDbApiRequest($verb, $url, $body, $headers, $refreshAuthHeaders, $partitionKey = $MOCK_RECORD_ID) { $verb | Should -Be "get" $url | Should -Be "https://$MOCK_DB.documents.azure.com/dbs/$MOCK_CONTAINER/colls/$MOCK_COLLECTION/docs" $body | Should -Be $null + + $authHeaders = Invoke-Command -ScriptBlock $refreshAuthHeaders $global:capturedNow | Should -Not -Be $null - $expectedHeaders = Get-CommonHeaders -now $global:capturedNow -encodedAuthString $MOCK_AUTH_HEADER -isQuery $true + $authHeaders.now | Should -Be $global:capturedNow + $authHeaders.encodedAuthString | Should -Be $MOCK_AUTH_HEADER + + $expectedHeaders = Get-CommonHeaders -isQuery $true AssertHashtablesEqual $expectedHeaders $headers } @@ -55,14 +58,14 @@ InModuleScope cosmos-db { It "Sends correct request" { $response = @{ StatusCode = 200; - Content = "{}"; - Headers = @{}; + Content = "{}"; + Headers = @{}; } Mock Invoke-CosmosDbApiRequestWithContinuation { - param($verb, $url, $body, $headers) + param($verb, $url, $body, $headers, $refreshAuthHeaders) - VerifyInvokeCosmosDbApiRequest $verb $url $body $headers | Out-Null + VerifyInvokeCosmosDbApiRequest $verb $url $body $headers $refreshAuthHeaders | Out-Null $response } @@ -77,26 +80,26 @@ InModuleScope cosmos-db { It "Returns multiple responses" { $response1 = @{ StatusCode = 200; - Content = "1"; - Headers = @{}; + Content = "1"; + Headers = @{}; } $response2 = @{ StatusCode = 200; - Content = "1"; - Headers = @{}; + Content = "1"; + Headers = @{}; } $response3 = @{ StatusCode = 200; - Content = "1"; - Headers = @{}; + Content = "1"; + Headers = @{}; } Mock Invoke-CosmosDbApiRequestWithContinuation { - param($verb, $url, $body, $headers) + param($verb, $url, $body, $headers, $refreshAuthHeaders) - VerifyInvokeCosmosDbApiRequest $verb $url $body $headers | Out-Null + VerifyInvokeCosmosDbApiRequest $verb $url $body $headers $refreshAuthHeaders | Out-Null $response1 $response2 @@ -118,10 +121,10 @@ InModuleScope cosmos-db { $recordResponse = [PSCustomObject]@{} - Mock Invoke-CosmosDbApiRequest { - param($verb, $url, $body, $headers) + Mock Invoke-CosmosDbApiRequestWithContinuation { + param($verb, $url, $body, $headers, $refreshAuthHeaders) - VerifyInvokeCosmosDbApiRequest $verb $url $body $headers | Out-Null + VerifyInvokeCosmosDbApiRequest $verb $url $body $headers $refreshAuthHeaders | Out-Null throw [System.Net.WebException]::new("", $null, [System.Net.WebExceptionStatus]::UnknownError, $response) } diff --git a/tests/Get-CommonHeaders.Tests.ps1 b/tests/Get-CommonHeaders.Tests.ps1 index 0191a5c..53cf47d 100644 --- a/tests/Get-CommonHeaders.Tests.ps1 +++ b/tests/Get-CommonHeaders.Tests.ps1 @@ -34,5 +34,15 @@ InModuleScope cosmos-db { "x-ms-documentdb-partitionkey" = "[`"MOCK_PARTITION_KEY`"]"; } $result } + + It "Doesn't require authorization headers" { + $result = Get-CommonHeaders + + AssertHashtablesEqual @{ + "x-ms-version" = "2018-12-31"; + "Cache-Control" = "No-Cache"; + "Content-Type" = "application/json"; + } $result + } } } \ No newline at end of file diff --git a/tests/Get-PartitionKeyRangesOrError.Tests.ps1 b/tests/Get-PartitionKeyRangesOrError.Tests.ps1 index 4d7b63e..403c1ca 100644 --- a/tests/Get-PartitionKeyRangesOrError.Tests.ps1 +++ b/tests/Get-PartitionKeyRangesOrError.Tests.ps1 @@ -30,14 +30,19 @@ InModuleScope cosmos-db { $resourceUrl | Should -Be "dbs/$MOCK_CONTAINER/colls/$MOCK_COLLECTION" } - Function VerifyInvokeCosmosDbApiRequest($verb, $url, $body, $headers, $partitionKey = $MOCK_RECORD_ID) { + Function VerifyInvokeCosmosDbApiRequest($verb, $url, $body, $headers, $refreshAuthHeaders, $partitionKey = $MOCK_RECORD_ID) { $verb | Should -Be "get" $url | Should -Be "https://$MOCK_DB.documents.azure.com/dbs/$MOCK_CONTAINER/colls/$MOCK_COLLECTION/pkranges" $body | Should -Be $null + + $authHeaders = Invoke-Command -ScriptBlock $refreshAuthHeaders $global:capturedNow | Should -Not -Be $null - $expectedHeaders = Get-CommonHeaders -now $global:capturedNow -encodedAuthString $MOCK_AUTH_HEADER + $authHeaders.now | Should -Be $global:capturedNow + $authHeaders.encodedAuthString | Should -Be $MOCK_AUTH_HEADER + + $expectedHeaders = Get-CommonHeaders $expectedHeaders["x-ms-documentdb-query-enablecrosspartition"] = "true" AssertHashtablesEqual $expectedHeaders $headers @@ -66,9 +71,9 @@ InModuleScope cosmos-db { } Mock Invoke-CosmosDbApiRequestWithContinuation { - param($verb, $url, $body, $headers) + param($verb, $url, $body, $headers, $refreshAuthHeaders) - VerifyInvokeCosmosDbApiRequest $verb $url $body $headers | Out-Null + VerifyInvokeCosmosDbApiRequest $verb $url $body $headers $refreshAuthHeaders | Out-Null $response } @@ -94,9 +99,9 @@ InModuleScope cosmos-db { } Mock Invoke-CosmosDbApiRequestWithContinuation { - param($verb, $url, $body, $headers) + param($verb, $url, $body, $headers, $refreshAuthHeaders) - VerifyInvokeCosmosDbApiRequest $verb $url $body $headers | Out-Null + VerifyInvokeCosmosDbApiRequest $verb $url $body $headers $refreshAuthHeaders | Out-Null $response } @@ -147,9 +152,9 @@ InModuleScope cosmos-db { } Mock Invoke-CosmosDbApiRequestWithContinuation { - param($verb, $url, $body, $headers) + param($verb, $url, $body, $headers, $refreshAuthHeaders) - VerifyInvokeCosmosDbApiRequest $verb $url $body $headers | Out-Null + VerifyInvokeCosmosDbApiRequest $verb $url $body $headers $refreshAuthHeaders | Out-Null $response1 $response2 @@ -176,10 +181,10 @@ InModuleScope cosmos-db { It "Should handle exceptions gracefully" { $exception = [System.Net.WebException]::new("", $null, [System.Net.WebExceptionStatus]::UnknownError, [System.Net.HttpWebResponse]@{}) - Mock Invoke-CosmosDbApiRequest { - param($verb, $url, $body, $headers) + Mock Invoke-CosmosDbApiRequestWithContinuation { + param($verb, $url, $body, $headers, $refreshAuthHeaders) - VerifyInvokeCosmosDbApiRequest $verb $url $body $headers | Out-Null + VerifyInvokeCosmosDbApiRequest $verb $url $body $headers $refreshAuthHeaders | Out-Null throw $exception } diff --git a/tests/Invoke-CosmosDbApiRequestWithContinuation.Tests.ps1 b/tests/Invoke-CosmosDbApiRequestWithContinuation.Tests.ps1 index d41907d..d6d9dba 100644 --- a/tests/Invoke-CosmosDbApiRequestWithContinuation.Tests.ps1 +++ b/tests/Invoke-CosmosDbApiRequestWithContinuation.Tests.ps1 @@ -15,16 +15,28 @@ InModuleScope cosmos-db { } Describe "Invoke-CosmosDbApiRequestWithContinuation" { + BeforeEach { + $ORIG_AUTHORIZATION_HEADER_REFRESH_THRESHOLD = $AUTHORIZATION_HEADER_REFRESH_THRESHOLD + } + + AfterEach { + $AUTHORIZATION_HEADER_REFRESH_THRESHOLD = $ORIG_AUTHORIZATION_HEADER_REFRESH_THRESHOLD + } + It "Handles responses without continuation header" { $PSVersionTable.PSEdition = "Desktop" $MOCK_VERB = "MOCK_VERB" $MOCK_URL = "MOCK_URL" + $MOCK_NOW = Get-Time + $MOCK_AUTH_STRING = "MOCK_AUTH_STRING" $MOCK_BODY = @{ Mock = "Mock" } $MOCK_HEADERS = @{ - Mock = "Mock"; + Mock = "Mock"; + "x-ms-date" = $MOCK_NOW; + Authorization = $MOCK_AUTH_STRING; } $response = @{ @@ -33,6 +45,13 @@ InModuleScope cosmos-db { Headers = @{}; } + $refreshAuthHeaders = { + return @{ + now = $MOCK_NOW; + encodedAuthString = $MOCK_AUTH_STRING; + } + } + Mock Invoke-CosmosDbApiRequest { param($verb, $url, $body, $headers) @@ -46,7 +65,7 @@ InModuleScope cosmos-db { $response } - $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS + $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS -RefreshAuthHeaders $refreshAuthHeaders $result | Should -BeExactly $response @($result).Count | Should -Be 1 @@ -59,11 +78,15 @@ InModuleScope cosmos-db { $MOCK_VERB = "MOCK_VERB" $MOCK_URL = "MOCK_URL" + $MOCK_NOW = Get-Time + $MOCK_AUTH_STRING = "MOCK_AUTH_STRING" $MOCK_BODY = @{ Mock = "Mock" } $MOCK_HEADERS = @{ - Mock = "Mock"; + Mock = "Mock"; + "x-ms-date" = $MOCK_NOW; + Authorization = $MOCK_AUTH_STRING; } $response = @{ @@ -72,6 +95,13 @@ InModuleScope cosmos-db { Headers = @{}; } + $refreshAuthHeaders = { + return @{ + now = $MOCK_NOW; + encodedAuthString = $MOCK_AUTH_STRING; + } + } + Mock Invoke-CosmosDbApiRequest { param($verb, $url, $body, $headers) @@ -85,7 +115,7 @@ InModuleScope cosmos-db { $response } - $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS + $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS -RefreshAuthHeaders $refreshAuthHeaders $result | Should -BeExactly $response @($result).Count | Should -Be 1 @@ -100,11 +130,22 @@ InModuleScope cosmos-db { $MOCK_VERB = "MOCK_VERB" $MOCK_URL = "MOCK_URL" + $MOCK_NOW = Get-Time + $MOCK_AUTH_STRING = "MOCK_AUTH_STRING" $MOCK_BODY = @{ Mock = "Mock" } $MOCK_HEADERS = @{ - Mock = "Mock"; + Mock = "Mock"; + "x-ms-date" = $MOCK_NOW; + Authorization = $MOCK_AUTH_STRING; + } + + $refreshAuthHeaders = { + return @{ + now = $MOCK_NOW; + encodedAuthString = $MOCK_AUTH_STRING; + } } $global:idx = 0 @@ -134,7 +175,7 @@ InModuleScope cosmos-db { $response } - $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS + $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS -RefreshAuthHeaders $refreshAuthHeaders $result | Should -BeExactly $global:expectedResponses @($result).Count | Should -Be $continuationTokens.Count @@ -149,11 +190,22 @@ InModuleScope cosmos-db { $MOCK_VERB = "MOCK_VERB" $MOCK_URL = "MOCK_URL" + $MOCK_NOW = Get-Time + $MOCK_AUTH_STRING = "MOCK_AUTH_STRING" $MOCK_BODY = @{ Mock = "Mock" } $MOCK_HEADERS = @{ - Mock = "Mock"; + Mock = "Mock"; + "x-ms-date" = $MOCK_NOW; + Authorization = $MOCK_AUTH_STRING; + } + + $refreshAuthHeaders = { + return @{ + now = $MOCK_NOW; + encodedAuthString = $MOCK_AUTH_STRING; + } } $global:idx = 0 @@ -185,7 +237,7 @@ InModuleScope cosmos-db { $response } - $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS + $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS -RefreshAuthHeaders $refreshAuthHeaders $result | Should -BeExactly $global:expectedResponses @($result).Count | Should -Be $continuationTokens.Count @@ -200,12 +252,22 @@ InModuleScope cosmos-db { $MOCK_VERB = "MOCK_VERB" $MOCK_URL = "MOCK_URL" + $MOCK_NOW = Get-Time + $MOCK_AUTH_STRING = "MOCK_AUTH_STRING" $MOCK_BODY = @{ Mock = "Mock" } $MOCK_HEADERS = @{ - Mock = "Mock" - "x-ms-continuation" = "BAD_TOKEN" + Mock = "Mock"; + "x-ms-date" = $MOCK_NOW; + Authorization = $MOCK_AUTH_STRING; + } + + $refreshAuthHeaders = { + return @{ + now = $MOCK_NOW; + encodedAuthString = $MOCK_AUTH_STRING; + } } $global:idx = 0 @@ -237,12 +299,161 @@ InModuleScope cosmos-db { $response } - $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS + $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS -RefreshAuthHeaders $refreshAuthHeaders $result | Should -BeExactly $global:expectedResponses @($result).Count | Should -Be $continuationTokens.Count Assert-MockCalled Invoke-CosmosDbApiRequest -Times $continuationTokens.Count } + + It "Refreshes auth headers after passing age threshold" { + # Force each call to refresh + $AUTHORIZATION_HEADER_REFRESH_THRESHOLD = [System.TimeSpan]::Zero + + $continuationTokens = @($null, "100", "200", "300") + + $PSVersionTable.PSEdition = "Core" + + $MOCK_VERB = "MOCK_VERB" + $MOCK_URL = "MOCK_URL" + $MOCK_AUTH_STRING = "MOCK_AUTH_STRING" + $MOCK_BODY = @{ + Mock = "Mock" + } + $MOCK_HEADERS = @{ + Mock = "Mock"; + } + + $global:authRefreshCount = 0 + $global:expectedNow = $null + + $refreshAuthHeaders = { + $global:authRefreshCount += 1 + $global:expectedNow = Get-Time + + return @{ + now = $global:expectedNow; + encodedAuthString = $MOCK_AUTH_STRING + "_" + $global:authRefreshCount; + } + } + + $global:idx = 0 + $global:expectedResponses = @() + + Mock Invoke-CosmosDbApiRequest { + param($verb, $url, $body, $headers) + + $verb | Should -Be $MOCK_VERB | Out-Null + $url | Should -Be $MOCK_URL | Out-Null + $body | Should -Be $MOCK_BODY | Out-Null + + $headers["x-ms-continuation"] | Should -Be $continuationTokens[$global:idx] | Out-Null + $global:idx = $global:idx + 1 + + $headers.Remove("x-ms-continuation") + + $headers["Authorization"] | Should -Be ($MOCK_AUTH_STRING + "_" + $global:authRefreshCount) | Out-Null + $headers.Remove("Authorization") + + $headers["x-ms-date"] | Should -Be $global:expectedNow | Out-Null + $headers.Remove("x-ms-date") + + AssertHashtablesEqual $MOCK_HEADERS $headers + + $response = @{ + StatusCode = 200; + Content = "$global:idx"; + Headers = @{ + # The empty here is to trick powershell into no automatically converting the single item array into just a value + "x-ms-continuation" = @($continuationTokens[$global:idx], "") + }; + } + + $global:expectedResponses += $response + $response + } + + $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS -RefreshAuthHeaders $refreshAuthHeaders + + $result | Should -BeExactly $global:expectedResponses + @($result).Count | Should -Be $continuationTokens.Count + + Assert-MockCalled Invoke-CosmosDbApiRequest -Times $continuationTokens.Count + + $global:authRefreshCount | Should -Be $continuationTokens.Count + } + + It "Does not refresh auth headers before age threshold" { + # Force each call to refresh + $AUTHORIZATION_HEADER_REFRESH_THRESHOLD = [System.TimeSpan]::FromHours(1) + + $continuationTokens = @($null, "100", "200", "300") + + $PSVersionTable.PSEdition = "Core" + + $MOCK_VERB = "MOCK_VERB" + $MOCK_URL = "MOCK_URL" + $MOCK_AUTH_STRING = "MOCK_AUTH_STRING" + $MOCK_BODY = @{ + Mock = "Mock" + } + $MOCK_HEADERS = @{ + Mock = "Mock"; + } + + $global:authRefreshCount = 0 + $global:expectedNow = $null + + $refreshAuthHeaders = { + $global:authRefreshCount += 1 + $global:expectedNow = Get-Time + + return @{ + now = $global:expectedNow; + encodedAuthString = $MOCK_AUTH_STRING + "_" + $global:authRefreshCount; + } + } + + $global:idx = 0 + $global:expectedResponses = @() + + Mock Invoke-CosmosDbApiRequest { + param($verb, $url, $body, $headers) + + $verb | Should -Be $MOCK_VERB | Out-Null + $url | Should -Be $MOCK_URL | Out-Null + $body | Should -Be $MOCK_BODY | Out-Null + + $headers["x-ms-continuation"] | Should -Be $continuationTokens[$global:idx] | Out-Null + $global:idx = $global:idx + 1 + + $headers.Remove("x-ms-continuation") + + $headers["Authorization"] | Should -Be ($MOCK_AUTH_STRING + "_" + $global:authRefreshCount) | Out-Null + $headers["x-ms-date"] | Should -Be $global:expectedNow | Out-Null + + $response = @{ + StatusCode = 200; + Content = "$global:idx"; + Headers = @{ + # The empty here is to trick powershell into no automatically converting the single item array into just a value + "x-ms-continuation" = @($continuationTokens[$global:idx], "") + }; + } + + $global:expectedResponses += $response + $response + } + + $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS -RefreshAuthHeaders $refreshAuthHeaders + + $result | Should -BeExactly $global:expectedResponses + @($result).Count | Should -Be $continuationTokens.Count + + Assert-MockCalled Invoke-CosmosDbApiRequest -Times $continuationTokens.Count + + $global:authRefreshCount | Should -Be 1 + } } } \ No newline at end of file diff --git a/tests/Search-CosmosDbRecords.Tests.ps1 b/tests/Search-CosmosDbRecords.Tests.ps1 index ac23b08..c82eb6a 100644 --- a/tests/Search-CosmosDbRecords.Tests.ps1 +++ b/tests/Search-CosmosDbRecords.Tests.ps1 @@ -19,8 +19,7 @@ InModuleScope cosmos-db { $MOCK_AUTH_HEADER = "MockAuthHeader" - Function VerifyGetAuthHeader($ResourceGroup, $SubscriptionId, $Database, $verb, $resourceType, $resourceUrl, $now) - { + Function VerifyGetAuthHeader($ResourceGroup, $SubscriptionId, $Database, $verb, $resourceType, $resourceUrl, $now) { $ResourceGroup | Should -Be $MOCK_RG $SubscriptionId | Should -Be $MOCK_SUB @@ -29,8 +28,7 @@ InModuleScope cosmos-db { $resourceUrl | Should -Be "dbs/$MOCK_CONTAINER/colls/$MOCK_COLLECTION" } - Function VerifyInvokeCosmosDbApiRequest($verb, $url, $actualBody, $expectedBody, $headers, $partitionKey=$MOCK_RECORD_ID) - { + Function VerifyInvokeCosmosDbApiRequest($verb, $url, $actualBody, $expectedBody, $headers, $refreshAuthHeaders, $partitionKey = $MOCK_RECORD_ID) { $verb | Should -Be "post" $url | Should -Be "https://$MOCK_DB.documents.azure.com/dbs/$MOCK_CONTAINER/colls/$MOCK_COLLECTION/docs" @@ -45,9 +43,14 @@ InModuleScope cosmos-db { $matchedParam.value | Should -Be $e.value -Because ("of the expected query param value for {0}" -f $e.name) } + $authHeaders = Invoke-Command -ScriptBlock $refreshAuthHeaders + $global:capturedNow | Should -Not -Be $null - $expectedHeaders = Get-CommonHeaders -now $global:capturedNow -encodedAuthString $MOCK_AUTH_HEADER -isQuery $true -contentType "application/Query+json" + $authHeaders.now | Should -Be $global:capturedNow + $authHeaders.encodedAuthString | Should -Be $MOCK_AUTH_HEADER + + $expectedHeaders = Get-CommonHeaders -isQuery $true -contentType "application/Query+json" $expectedHeaders["x-ms-documentdb-query-enablecrosspartition"] = "true" AssertHashtablesEqual $expectedHeaders $headers @@ -67,19 +70,19 @@ InModuleScope cosmos-db { It "Sends correct request with no parameters" { $response = @{ StatusCode = 200; - Content = "{}"; - Headers = @{}; + Content = "{}"; + Headers = @{}; } $expectedBody = @{ - query = $MOCK_QUERY; + query = $MOCK_QUERY; parameters = @(); } Mock Invoke-CosmosDbApiRequestWithContinuation { - param($verb, $url, $body, $headers) + param($verb, $url, $body, $headers, $refreshAuthHeaders) - VerifyInvokeCosmosDbApiRequest $verb $url $body $expectedBody $headers | Out-Null + VerifyInvokeCosmosDbApiRequest $verb $url $body $expectedBody $headers $refreshAuthHeaders | Out-Null $response } @@ -94,8 +97,8 @@ InModuleScope cosmos-db { It "Sends correct request with name-value parameters" { $response = @{ StatusCode = 200; - Content = "{}"; - Headers = @{}; + Content = "{}"; + Headers = @{}; } $parameters = @( @@ -104,14 +107,14 @@ InModuleScope cosmos-db { ) $expectedBody = @{ - query = $MOCK_QUERY; + query = $MOCK_QUERY; parameters = $parameters; } Mock Invoke-CosmosDbApiRequestWithContinuation { - param($verb, $url, $body, $headers) + param($verb, $url, $body, $headers, $refreshAuthHeaders) - VerifyInvokeCosmosDbApiRequest $verb $url $body $expectedBody $headers | Out-Null + VerifyInvokeCosmosDbApiRequest $verb $url $body $expectedBody $headers $refreshAuthHeaders | Out-Null $response } @@ -126,8 +129,8 @@ InModuleScope cosmos-db { It "Sends correct request with hashtable parameters" { $response = @{ StatusCode = 200; - Content = "{}"; - Headers = @{}; + Content = "{}"; + Headers = @{}; } $nameValueParams = @( @@ -140,14 +143,14 @@ InModuleScope cosmos-db { } $expectedBody = @{ - query = $MOCK_QUERY; + query = $MOCK_QUERY; parameters = $nameValueParams; } Mock Invoke-CosmosDbApiRequestWithContinuation { - param($verb, $url, $body, $headers) + param($verb, $url, $body, $headers, $refreshAuthHeaders) - VerifyInvokeCosmosDbApiRequest $verb $url $body $expectedBody $headers | Out-Null + VerifyInvokeCosmosDbApiRequest $verb $url $body $expectedBody $headers $refreshAuthHeaders | Out-Null $response } @@ -162,31 +165,31 @@ InModuleScope cosmos-db { It "Returns multiple responses" { $response1 = @{ StatusCode = 200; - Content = "1"; - Headers = @{}; + Content = "1"; + Headers = @{}; } $response2 = @{ StatusCode = 200; - Content = "2"; - Headers = @{}; + Content = "2"; + Headers = @{}; } $response3 = @{ StatusCode = 200; - Content = "3"; - Headers = @{}; + Content = "3"; + Headers = @{}; } $expectedBody = @{ - query = $MOCK_QUERY; + query = $MOCK_QUERY; parameters = @(); } Mock Invoke-CosmosDbApiRequestWithContinuation { - param($verb, $url, $body, $headers) + param($verb, $url, $body, $headers, $refreshAuthHeaders) - VerifyInvokeCosmosDbApiRequest $verb $url $body $expectedBody $headers | Out-Null + VerifyInvokeCosmosDbApiRequest $verb $url $body $expectedBody $headers $refreshAuthHeaders | Out-Null $response1 $response2 @@ -209,14 +212,14 @@ InModuleScope cosmos-db { $recordResponse = [PSCustomObject]@{} $expectedBody = @{ - query = $MOCK_QUERY; + query = $MOCK_QUERY; parameters = @(); } Mock Invoke-CosmosDbApiRequestWithContinuation { - param($verb, $url, $body, $headers) + param($verb, $url, $body, $headers, $refreshAuthHeaders) - VerifyInvokeCosmosDbApiRequest $verb $url $body $expectedBody $headers | Out-Null + VerifyInvokeCosmosDbApiRequest $verb $url $body $expectedBody $headers $refreshAuthHeaders | Out-Null throw [System.Net.WebException]::new("", $null, [System.Net.WebExceptionStatus]::UnknownError, $response) } @@ -238,14 +241,14 @@ InModuleScope cosmos-db { It "Uses extra features by default" { $response = @{ StatusCode = 200; - Content = "{}"; - Headers = @{}; + Content = "{}"; + Headers = @{}; } $mockParameters = @(@{ - name = "Mock"; - value = "MOCK"; - }) + name = "Mock"; + value = "MOCK"; + }) Mock Search-CosmosDbRecordsWithExtraFeatures { param($ResourceGroup, $Database, $Container, $Collection, $Query, $Parameters, $SubscriptionId) From d9ded2af6f88bb9043aff02639333773bec7cd22 Mon Sep 17 00:00:00 2001 From: Caleb Brose <5447118+cmbrose@users.noreply.github.com> Date: Tue, 9 Apr 2024 22:15:11 +0000 Subject: [PATCH 2/3] Add an opt-out flag --- cosmos-db/cosmos-db.psm1 | 12 ++- ...smosDbApiRequestWithContinuation.Tests.ps1 | 79 +++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/cosmos-db/cosmos-db.psm1 b/cosmos-db/cosmos-db.psm1 index 8c4b760..842bc55 100644 --- a/cosmos-db/cosmos-db.psm1 +++ b/cosmos-db/cosmos-db.psm1 @@ -267,7 +267,10 @@ Function Invoke-CosmosDbApiRequestWithContinuation([string]$verb, [string]$url, while ($continuationToken) { $headers["x-ms-continuation"] = $continuationToken - if ([System.DateTime]::Parse($authHeaders.now) + $AUTHORIZATION_HEADER_REFRESH_THRESHOLD -lt [System.DateTime]::UtcNow) { + $authHeaderReuseDisabled = $env:COSMOS_DB_FLAG_ENABLE_AUTH_HEADER_REUSE -eq 0 + $authHeaderExpired = [System.DateTime]::Parse($authHeaders.now) + $AUTHORIZATION_HEADER_REFRESH_THRESHOLD -lt [System.DateTime]::UtcNow + + if ($authHeaderReuseDisabled -or $authHeaderExpired) { $authHeaders = Invoke-Command -ScriptBlock $refreshAuthHeaders Set-AuthHeaders -headers $headers -now $authHeaders.now -encodedAuthString $authHeaders.encodedAuthString } @@ -1001,7 +1004,8 @@ Function Use-CosmosDbInternalFlag ( $enableFiddlerDebugging = $null, $enableCaching = $null, - $enablePartitionKeyRangeSearches = $null + $enablePartitionKeyRangeSearches = $null, + $enableAuthHeaderReuse = $null ) { if ($null -ne $enableFiddlerDebugging) { $env:AZURE_CLI_DISABLE_CONNECTION_VERIFICATION = if ($enableFiddlerDebugging) { 1 } else { 0 } @@ -1014,6 +1018,10 @@ Function Use-CosmosDbInternalFlag if ($null -ne $enablePartitionKeyRangeSearches) { $env:COSMOS_DB_FLAG_ENABLE_PARTITION_KEY_RANGE_SEARCHES = if ($enablePartitionKeyRangeSearches) { 1 } else { 0 } } + + if ($null -ne $enableAuthHeaderReuse) { + $env:COSMOS_DB_FLAG_ENABLE_AUTH_HEADER_REUSE = if ($enableAuthHeaderReuse) { 1 } else { 0 } + } } Function Use-CosmosDbReadonlyKeys diff --git a/tests/Invoke-CosmosDbApiRequestWithContinuation.Tests.ps1 b/tests/Invoke-CosmosDbApiRequestWithContinuation.Tests.ps1 index d6d9dba..7f549e6 100644 --- a/tests/Invoke-CosmosDbApiRequestWithContinuation.Tests.ps1 +++ b/tests/Invoke-CosmosDbApiRequestWithContinuation.Tests.ps1 @@ -17,6 +17,8 @@ InModuleScope cosmos-db { Describe "Invoke-CosmosDbApiRequestWithContinuation" { BeforeEach { $ORIG_AUTHORIZATION_HEADER_REFRESH_THRESHOLD = $AUTHORIZATION_HEADER_REFRESH_THRESHOLD + + Use-CosmosDbInternalFlag -EnableAuthHeaderReuse $true } AfterEach { @@ -455,5 +457,82 @@ InModuleScope cosmos-db { $global:authRefreshCount | Should -Be 1 } + + It "Refreshes auth headers when reuse is disabled" { + Use-CosmosDbInternalFlag -EnableAuthHeaderReuse $false + $AUTHORIZATION_HEADER_REFRESH_THRESHOLD = [System.TimeSpan]::FromHours(1) + + $continuationTokens = @($null, "100", "200", "300") + + $PSVersionTable.PSEdition = "Core" + + $MOCK_VERB = "MOCK_VERB" + $MOCK_URL = "MOCK_URL" + $MOCK_AUTH_STRING = "MOCK_AUTH_STRING" + $MOCK_BODY = @{ + Mock = "Mock" + } + $MOCK_HEADERS = @{ + Mock = "Mock"; + } + + $global:authRefreshCount = 0 + $global:expectedNow = $null + + $refreshAuthHeaders = { + $global:authRefreshCount += 1 + $global:expectedNow = Get-Time + + return @{ + now = $global:expectedNow; + encodedAuthString = $MOCK_AUTH_STRING + "_" + $global:authRefreshCount; + } + } + + $global:idx = 0 + $global:expectedResponses = @() + + Mock Invoke-CosmosDbApiRequest { + param($verb, $url, $body, $headers) + + $verb | Should -Be $MOCK_VERB | Out-Null + $url | Should -Be $MOCK_URL | Out-Null + $body | Should -Be $MOCK_BODY | Out-Null + + $headers["x-ms-continuation"] | Should -Be $continuationTokens[$global:idx] | Out-Null + $global:idx = $global:idx + 1 + + $headers.Remove("x-ms-continuation") + + $headers["Authorization"] | Should -Be ($MOCK_AUTH_STRING + "_" + $global:authRefreshCount) | Out-Null + $headers.Remove("Authorization") + + $headers["x-ms-date"] | Should -Be $global:expectedNow | Out-Null + $headers.Remove("x-ms-date") + + AssertHashtablesEqual $MOCK_HEADERS $headers + + $response = @{ + StatusCode = 200; + Content = "$global:idx"; + Headers = @{ + # The empty here is to trick powershell into no automatically converting the single item array into just a value + "x-ms-continuation" = @($continuationTokens[$global:idx], "") + }; + } + + $global:expectedResponses += $response + $response + } + + $result = Invoke-CosmosDbApiRequestWithContinuation -Verb $MOCK_VERB -Url $MOCK_URL -Body $MOCK_BODY -Headers $MOCK_HEADERS -RefreshAuthHeaders $refreshAuthHeaders + + $result | Should -BeExactly $global:expectedResponses + @($result).Count | Should -Be $continuationTokens.Count + + Assert-MockCalled Invoke-CosmosDbApiRequest -Times $continuationTokens.Count + + $global:authRefreshCount | Should -Be $continuationTokens.Count + } } } \ No newline at end of file From cb69d35fdc20e433abfb5127a78c881ed44b32a6 Mon Sep 17 00:00:00 2001 From: Caleb Brose <5447118+cmbrose@users.noreply.github.com> Date: Tue, 9 Apr 2024 22:22:30 +0000 Subject: [PATCH 3/3] Docs --- CHANGELOG.md | 10 ++++++++++ README.md | 5 +++-- cosmos-db/cosmos-db.psd1 | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 340ae41..8305576 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ # Release Notes All notable changes and release history of the "cosmos-db" module will be documented in this file. +## 1.18 +* Fixes a bug in commands like `Search-CosmosDbRecords` and `Get-AllCosmosDbRecords` which might run for long enough that their auth tokens expire and aren't refreshed. Auth tokens will now be refreshed every 10 min as these commands run. +* Adds a `-enableAuthHeaderReuse` flag to `Use-CosmosDbInternalFlag` which disables the 10 minute refresh period and forces auth header refreshes for every API call. + +## 1.17 +* Fixes `Search-CosmosDbRecords` for partition key range uses where the PK range fetch call didn't use continuation tokens and might miss some results. + +## 1.16 +* Adds support for readonly keys via `Use-CosmosDbReadonlyKeys` + ## 1.15 * Adds support for optimistic concurrency (enabled by default) to `Update-CosmosDbRecord` diff --git a/README.md b/README.md index 0588ddf..4310f67 100644 --- a/README.md +++ b/README.md @@ -263,8 +263,9 @@ Use-CosmosDbInternalFlag -EnableFiddlerDebugging $true | Name | Usage | Required | | - | - | - | | EnableFiddlerDebugging | Sets the `az` flag `env:AZURE_CLI_DISABLE_CONNECTION_VERIFICATION` which enables `az` commands with a Fiddler proxy | No - default is disabled | -| EnableCaching | Enables caching certain values like DB keys, partition ranges, etc. Improves performance of nearly all operations. | No - default is enabled | -| EnablePartitionKeyRangeSearches | **[Experimental]** Enables filtering `Search` queries to relevant partition ranges instead of a full scan. Improves performance of `Search` commands. | No - default is disabled | +| EnableCaching | Enables/disables caching certain values like DB keys, partition ranges, etc. Improves performance of nearly all operations. | No - default is enabled | +| EnablePartitionKeyRangeSearches | **[Experimental]** Enables/disables filtering `Search` queries to relevant partition ranges instead of a full scan. Improves performance of `Search` commands. | No - default is disabled | +| EnableAuthHeaderReuse | Enables/disables reusing auth headers for commands which use continuation tokens, like `Search-CosmosDbRecords` or `Get-AllCosmosDbRecords`. | No - default is enabled | ## Error Handling diff --git a/cosmos-db/cosmos-db.psd1 b/cosmos-db/cosmos-db.psd1 index 667eb02..0099a80 100644 --- a/cosmos-db/cosmos-db.psd1 +++ b/cosmos-db/cosmos-db.psd1 @@ -11,7 +11,7 @@ # RootModule = '' # Version number of this module. - ModuleVersion = '1.17' + ModuleVersion = '1.18' # Supported PSEditions # CompatiblePSEditions = @()